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 sys
import autophase import autophase
class DamarisFFT: class DamarisFFT:
"""
Class for Fourier transforming data.
Provides several helper and apodization functions
"""
def clip( self, start=None, stop=None ): def clip( self, start=None, stop=None ):
""" """
:param float start: beginning of clipping Method for clipping data, returns only the data between start and stop
:param float stop: end of clipping
Method for clipping data, returns only the timesignal
between start and stop
is returned.
start and stop can be either time or frequency. 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 # check if start/stop order is properly
if start > stop: if start > stop:
# I could swap start/stop actually start, stop = stop, start
# TODO swap values?
raise # do nothing if one uses clip as a "placeholder"
# if one uses clip as a "placeholder"
if start == None and stop == None: if start == None and stop == None:
return self return self
if start == None: if start == None:
start = 0 start = self.x[ 0 ]
if stop == None: if stop == None:
stop = -1 stop = self.x[ -1 ]
# check if data is fft which changes the start/stop units # 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": if self.xlabel == "Frequency / Hz":
isfft = True isfft = True
start = self.x.size * (0.5 + start / self.sampling_rate) start = self.x.size * (0.5 + start / self.sampling_rate)
@ -40,13 +45,11 @@ class DamarisFFT:
stop *= self.sampling_rate stop *= self.sampling_rate
# check if boundaries make sense, raise exception otherwise # check if boundaries make sense, raise exception otherwise
if numpy.abs( int( start ) - int( stop ) ) <= 0: if numpy.abs( int( start ) - int( stop ) ) <= 0:
raise ValueError("start stop too close: There are no values in the given boundaries!") raise ValueError( "start stop too close: There are no samples in the given boundaries!" )
for ch in xrange(len(self.y)):
# clip the data for each channel # 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 ) ] 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 ) ] self.x = self.x[ int( start ):int( stop ) ]
return self return self
@ -61,35 +64,16 @@ class DamarisFFT:
""" """
# TODO baseline correction for spectra after: # TODO baseline correction for spectra after:
# Heuer, A; Haeberlen, U.: J. Mag. Res.(1989) 85, Is 1, 79-94 # 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 ) n = int( self.x.size * last_part )
for ch in xrange( len( self.y ) ): for ch in xrange( len( self.y ) ):
self.y[ ch ] -= self.y[ ch ][ -n: ].mean( ) self.y[ ch ] -= self.y[ ch ][ -n: ].mean( )
# Skip the following due to design reasons
# new_object.was_copied = True
return self 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 ): def exp_window( self, line_broadening=10 ):
""" """
Exponential window function Exponential window function
:param float line_broadening: Applies apodization to time signal :param float line_broadening: default 10, line broadening factor in Hz
.. math:: .. math::
@ -102,6 +86,14 @@ class DamarisFFT:
return self return self
def gauss_window( self, line_broadening=10 ): 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 ) apod = numpy.exp( -(self.x * line_broadening) ** 2 )
for i in range( 2 ): for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod self.y[ i ] = self.y[ i ] * apod
@ -115,36 +107,56 @@ class DamarisFFT:
def traf_window( self, line_broadening=10 ): def traf_window( self, line_broadening=10 ):
apod = (numpy.exp( -self.x * line_broadening )) ** 2 / ( (numpy.exp( -self.x * line_broadening )) ** 3 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 ): for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod self.y[ i ] = self.y[ i ] * apod
return self return self
def hanning_window( self ): def hanning_window( self ):
"""
Symmetric centered window (hanning)
"""
apod = numpy.hanning( self.x.size ) apod = numpy.hanning( self.x.size )
for i in range( 2 ): for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod self.y[ i ] = self.y[ i ] * apod
return self return self
def hamming_window( self ): def hamming_window( self ):
"""
Symmetric centered window (hamming)
"""
apod = numpy.hamming( self.x.size ) apod = numpy.hamming( self.x.size )
for i in range( 2 ): for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod self.y[ i ] = self.y[ i ] * apod
return self return self
def blackman_window( self ): def blackman_window( self ):
"""
Symmetric centered window (blackmann)
"""
apod = numpy.blackman( self.x.size ) apod = numpy.blackman( self.x.size )
for i in range( 2 ): for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod self.y[ i ] = self.y[ i ] * apod
return self return self
def bartlett_window( self ): def bartlett_window( self ):
"""
Symmetric centered window (bartlett)
"""
apod = numpy.bartlett( self.x.size ) apod = numpy.bartlett( self.x.size )
for i in range( 2 ): for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod self.y[ i ] = self.y[ i ] * apod
return self return self
def kaiser_window( self, beta=4, use_scipy=None ): def kaiser_window( self, beta=4, use_scipy=None ):
"""
Symmetric centered window (kaiser)
"""
if use_scipy == None: if use_scipy == None:
# modified Bessel function of zero kind order from somewhere # modified Bessel function of zero kind order from somewhere
def I_0( x ): def I_0( x ):
@ -161,6 +173,7 @@ class DamarisFFT:
else: else:
# alternative method using scipy # alternative method using scipy
import scipy import scipy
apod = scipy.kaiser( self.x.size, beta ) apod = scipy.kaiser( self.x.size, beta )
for i in range( 2 ): for i in range( 2 ):
@ -169,29 +182,23 @@ class DamarisFFT:
def autophase( self ): def autophase( self ):
""" """
works nice with a SNR above 20 dB Automatically phases the data to maximize real part.
10 V signal height to 1V noise width
Works nice with a SNR above 20 dB, i.e.
10 V signal to 0.1 V noise amplitude.
""" """
autophase.get_phase( self ) autophase.get_phase( self )
return self return self
def fft( self, samples=None ): def fft( self, samples=None ):
""" """
Fouriertransform the timesignal inplace. Calculate the Fourier transform of the data inplace.
For "zerofilling" set "samples" to a value higher than your data length. For zero filling set **samples** to a value higher than your data length,
Shorten "samples" to truncate your data. smaller values will truncate your data.
samples takes only integer values
: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.fft( self.y[ 0 ] + 1j * self.y[ 1 ], n=samples )
fft_of_signal = numpy.fft.fftshift( fft_of_signal ) fft_of_signal = numpy.fft.fftshift( fft_of_signal )
dwell = 1.0 / self.sampling_rate dwell = 1.0 / self.sampling_rate
@ -204,6 +211,16 @@ class DamarisFFT:
return self return self
def magnitude( 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 # 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[ 0 ] = numpy.sqrt( self.y[ 0 ] ** 2 + self.y[ 1 ] ** 2 )
self.y[ 1 ] *= 0 # self.y[0].copy() self.y[ 1 ] *= 0 # self.y[0].copy()