'''
provide a simple python3 interface to the gsl_fft_real_transform function
'''
import sys
import itertools
from gsl_setup import *
def grouper(n, iterable, fillvalue=None):
# http://docs.python.org/dev/3.0/library/itertools.html#module-itertools
"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return itertools.zip_longest(fillvalue=fillvalue, *args)
real_workspace_alloc = setup(
gsl.gsl_fft_real_workspace_alloc,[c_ulong,],c_void_p)
real_wavetable_alloc = setup(
gsl.gsl_fft_real_wavetable_alloc,[c_ulong,],c_void_p)
real_workspace_free =setup(gsl.gsl_fft_real_workspace_free ,[c_void_p,])
real_wavetable_free =setup(gsl.gsl_fft_real_wavetable_free ,[c_void_p,])
real_transform = setup(gsl.gsl_fft_real_transform,
[c_void_p,c_ulong,c_ulong,c_void_p,c_void_p],)
class Real_FFT:
'''
returns the complex values of the real transform of the real data.
return value[0] describes the offset,
[1] is amplitude of term for wavelength = data length
etceteras
[-1] amp of wavelength = twice sample distance
'''
def __init__(self):
self.n = 0
def __call__(self,data):
if len(data) < 2:
if 1 == len(data):
return data[:]
return []
if len(data) != self.n:
self.__del__()
self.n = len(data)
size = c_ulong(self.n)
self.workspace = real_workspace_alloc(size)
self.wavetable = real_wavetable_alloc(size)
a = array('d',data) # need a copy of the data
real_transform(ADDRESS(a),1,self.n,self.wavetable,self.workspace)
rv = [complex(a[0]),]
rv.extend(itertools.starmap(complex,grouper(2,a[1:],fillvalue=0)))
return rv
def __del__(self):
if self.n:
try:
real_workspace_free(self.workspace)
real_wavetable_free(self.wavetable)
except AttributeError:
print('Attribute error while freeing FFT auxiliary storage',
file=sys.stderr)
except:
print('error freeing FFT auxiliary storage',
file=sys.stderr)
def produce_frequency(self,*,samples=None,sample_interval=None,sample_rate=None,total_length=None):
'''
return the frequency grid based on actual sizes (default sample_interval=1).
'''
n = samples or self.n
if not n:
return array('d')
args_specified = 3 - ((not sample_interval)+(not sample_rate)+(not total_length))
if 1 < args_specified:
raise TypeError('specify at most one of [sample_rate, total_length, sample_interval]')
if 0 == args_specified:
L = n
elif sample_interval:
L = n*sample_interval
elif sample_rate:
L = n/sample_rate
else:
L = total_length
return as_array(waves/L for waves in range(1+n//2))
def produce_period(self,*args,**kwargs):
'''
return the period grid based on actual sizes.
frequency of zero --> period 0. what else to do?
'''
f2T = self.produce_frequency(*args,**kwargs)
for i in range(1,len(f2T)):
f2T[i] = 1/f2T[i]
return f2T
real_fft = Real_FFT()
def magnitude(a):
return [abs(b) for b in a]
def phase(a):
return [phase(b) for b in a]