fixed taps/space, update documentation

This commit is contained in:
Markus Rosenstihl 2014-11-26 15:46:27 +00:00
parent 7aeb010a83
commit d656daef5b

View File

@ -2,33 +2,38 @@ import numpy
import sys
import autophase
class DamarisFFT:
"""
Class for Fourier transforming data.
Provides several helper and apodization functions
"""
def clip( self, start=None, stop=None ):
"""
:param float start: beginning of clipping
:param float stop: end of clipping
Method for clipping data, returns only the timesignal
between start and stop
is returned.
Method for clipping data, returns only the data between start and stop
start and stop can be either time or frequency.
The unit is automatically determined
The unit is automatically determined (Hz or s).
:param float start: beginning of clipping in s
:param float stop: end of clipping in s
"""
# check if start/stop order is properly
if start > stop:
# I could swap start/stop actually
# TODO swap values?
raise
# if one uses clip as a "placeholder"
start, stop = stop, start
# do nothing if one uses clip as a "placeholder"
if start == None and stop == None:
return self
if start == None:
start = 0
start = self.x[ 0 ]
if stop == None:
stop = -1
stop = self.x[ -1 ]
# check if data is fft which changes the start/stop units
# TODO should get nicer(failsafe), i.e. flags in the object?
if self.xlabel == "Frequency / Hz":
isfft = True
start = self.x.size * (0.5 + start / self.sampling_rate)
@ -40,13 +45,11 @@ class DamarisFFT:
stop *= self.sampling_rate
# check if boundaries make sense, raise exception otherwise
if numpy.abs( int( start ) - int( stop ) ) <= 0:
raise ValueError("start stop too close: There are no values in the given boundaries!")
for ch in xrange(len(self.y)):
raise ValueError( "start stop too close: There are no samples in the given boundaries!" )
# clip the data for each channel
# TODO multi records
for ch in xrange( len( self.y ) ):
self.y[ ch ] = self.y[ ch ][ int( start ):int( stop ) ]
# TODO what to do with x? Should it start from 0 or from start?
# self.x = self.x[:int(stop)-int(start)]
self.x = self.x[ int( start ):int( stop ) ]
return self
@ -61,35 +64,16 @@ class DamarisFFT:
"""
# TODO baseline correction for spectra after:
# Heuer, A; Haeberlen, U.: J. Mag. Res.(1989) 85, Is 1, 79-94
# Should I create an empty object?
# I deided to do NOT a copy, but
# rather modify the object
n = int( self.x.size * last_part )
for ch in xrange( len( self.y ) ):
self.y[ ch ] -= self.y[ ch ][ -n: ].mean( )
# Skip the following due to design reasons
# new_object.was_copied = True
return self
"""
Apodization functions:
* exp_window and gauss_window are S/N enhancing,
* dexp_window and traf_window are resolution enhancing
* standard windows [hamming, hanning, bartlett, blackman, kaiser-bessel]
are also available
self.x = time points
elf.aquisition_time = aquisition time (no. samples / sampling_rate)
line_broadening = line broadening factor (standard = 10 Hz)
gaussian_multiplicator = Gaussian Multiplication Factor for
the double exponential apodization
function (standard = 0.3)
"""
def exp_window( self, line_broadening=10 ):
"""
Exponential window function
:param float line_broadening: Applies apodization to time signal
:param float line_broadening: default 10, line broadening factor in Hz
.. math::
@ -102,6 +86,14 @@ class DamarisFFT:
return self
def gauss_window( self, line_broadening=10 ):
"""
Gaussian window function
:param float line_broadening: default 10, line broadening factor in Hz
.. math:: \\exp\\left(- (\\textsf{line_broadening} \\cdot t)^2\\right)
"""
apod = numpy.exp( -(self.x * line_broadening) ** 2 )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
@ -115,36 +107,56 @@ class DamarisFFT:
def traf_window( self, line_broadening=10 ):
apod = (numpy.exp( -self.x * line_broadening )) ** 2 / ( (numpy.exp( -self.x * line_broadening )) ** 3
+ (numpy.exp(-self.x.max()*line_broadening))**3 )
+ (
numpy.exp( -self.x.max( ) * line_broadening )) ** 3 )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def hanning_window( self ):
"""
Symmetric centered window (hanning)
"""
apod = numpy.hanning( self.x.size )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def hamming_window( self ):
"""
Symmetric centered window (hamming)
"""
apod = numpy.hamming( self.x.size )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def blackman_window( self ):
"""
Symmetric centered window (blackmann)
"""
apod = numpy.blackman( self.x.size )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def bartlett_window( self ):
"""
Symmetric centered window (bartlett)
"""
apod = numpy.bartlett( self.x.size )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def kaiser_window( self, beta=4, use_scipy=None ):
"""
Symmetric centered window (kaiser)
"""
if use_scipy == None:
# modified Bessel function of zero kind order from somewhere
def I_0( x ):
@ -161,6 +173,7 @@ class DamarisFFT:
else:
# alternative method using scipy
import scipy
apod = scipy.kaiser( self.x.size, beta )
for i in range( 2 ):
@ -169,29 +182,23 @@ class DamarisFFT:
def autophase( self ):
"""
works nice with a SNR above 20 dB
10 V signal height to 1V noise width
Automatically phases the data to maximize real part.
Works nice with a SNR above 20 dB, i.e.
10 V signal to 0.1 V noise amplitude.
"""
autophase.get_phase( self )
return self
def fft( self, samples=None ):
"""
Fouriertransform the timesignal inplace.
For "zerofilling" set "samples" to a value higher than your data length.
Shorten "samples" to truncate your data.
samples takes only integer values
Calculate the Fourier transform of the data inplace.
For zero filling set **samples** to a value higher than your data length,
smaller values will truncate your data.
:param int samples: default=None, if given, number of samples returned
"""
# Is this smart performance wise? Should I create an empty object?
# Tests showed that this try except block performed 3.78ms
# timesignal.baseline().fft()
# with out this it needed 4.41 ms, thus this is justified :-)
#try:
# if self.was_copied:
# new_object = self
#except:
# new_object = self+0
fft_of_signal = numpy.fft.fft( self.y[ 0 ] + 1j * self.y[ 1 ], n=samples )
fft_of_signal = numpy.fft.fftshift( fft_of_signal )
dwell = 1.0 / self.sampling_rate
@ -204,6 +211,16 @@ class DamarisFFT:
return self
def magnitude( self ):
"""
Return absolute signal, i.e.:
.. math::
y[0] &= \\sqrt{y[0]^2 + y[1]^2} \\\\
y[1] &= 0
"""
# this should calculate the absolute value, and set the imag channel to zero
self.y[ 0 ] = numpy.sqrt( self.y[ 0 ] ** 2 + self.y[ 1 ] ** 2 )
self.y[ 1 ] *= 0 # self.y[0].copy()