This module has tools for generating spectral data from various sources.
Examples of FFT work using fft() and ifft().
Null sequence is OK
>>> fft([])
()
Singleton is trivial
>>> fft([1+1j])
((1+1j),)
Even/odd pair gives pure real/imaginary results
>>> fft([1-1j, 1+1j])
((2+0j), -2j)
Some other simple examples:
>>> fft([1, -1] * 4)
(0, 0j, 0j, 0j, 8, 0j, 0j, 0j)
>>> fft([1] * 2 + [0] * 2)
(2, (1-1j), 0, (1+1j))
Tranform of impulse is uniform in frequency
>>> fft([1] * 1 + [0] * (8 - 1))
(1, (1+0j), (1+0j), (1+0j), 1, (1+0j), (1+0j), (1+0j))
Now, helper rounding and scaling functions to make the following results readable, and to hide small floating-point differences.
>>> def cint(c): return complex(int(round(c.real)), int(round(c.imag))) if isinstance(c, complex) else int(round(c))
>>> def scale_it(array): return tuple(cint(item * 1000) for item in array)
Delayed impulses get phase shift, but have same magnitudes
>>> scale_it(fft([0] * 1 + [1] * 1 + [0] * (8 - 2)))
(1000, (707-707j), -1000j, (-707-707j), -1000, (-707+707j), 1000j, (707+707j))
>>> scale_it(fft([0] * 2 + [1j] * 1 + [0] * (8 - 3)))
(1000j, (1000+0j), -1000j, (-1000+0j), 1000j, (1000+0j), -1000j, (-1000+0j))
>>> scale_it(fft([0] * 3 + [(1+1j)*sqrt(1/2)] * 1 + [0] * (8 - 4)))
((707+707j), -1000j, (-707+707j), (1000+0j), (-707-707j), 1000j, (707-707j), (-1000+0j))
Demonstrate superposition
>>> x = [1] * 2 + [0j] * 2 + [0] * (16 - 4)
>>> y = [0] * 2 + [1j] * 2 + [0] * (16 - 4)
>>> z = list(a+b for a,b in izip(x,y))
>>> scale_it(a+b for a,b in izip(fft(x), fft(y))) == scale_it(fft(z))
True
Create a 1/4 duty-cycle pulse
>>> pulse = [1] * 4 + [0] * (16 - 4)
>>> scale_it(pulse)
(1000, 1000, 1000, 1000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
Look at sinx/x pattern with zeroes every 4th frequency
>>> scale_it(fft(pulse))
(4000, (3014-2014j), (1000-2414j), (-248-1248j), 0j, (834+166j), (1000-414j), (401-599j), 0, (401+599j), (1000+414j), (834-166j), 0j, (-248+1248j), (1000+2414j), (3014+2014j))
Show recovery of original signal with inverse DFT and vice versa. Third example shows that equality in the first example is relying on the rounding/truncating in scale_it.
>>> scale_it(ifft(fft(pulse))) == scale_it(pulse)
True
>>> scale_it(fft(ifft(pulse))) == scale_it(pulse)
True
>>> ifft(fft(pulse)) == pulse
False
Odd real sinusoid gives pure imaginary impulse:
>>> scale_it(fft(sin(3 * (2 * PI * n / 32)) for n in xrange(32)))
(0, 0j, 0j, -16000j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 16000j, 0j, 0j)
More superposition. Note that the odd imaginary sine at 5 gives the pure real part of the result:
>>> scale_it(fft(complex(sin(3 * (2 * PI * n / 32)), sin(5 * (2 * PI * n / 32))) for n in xrange(32)))
(0j, 0j, 0j, -16000j, 0j, (16000+0j), 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, 0j, (-16000+0j), 0j, 16000j, 0j, 0j)
Regression test, showing that the twiddle cache works when you drop back from 32 to 16 samples....
>>> scale_it(fft([-1] * 2 + [-1j] * 2 + [0] * (16 - 4)))
((-2000-2000j), (-3555-707j), (-3414+1414j), (-1707+2555j), 2000j, (472+707j), 0j, (-293+58j), 0j, (141-707j), (-586-1414j), (-1707-1141j), (-2000+0j), (-1058+707j), 0j, (-293-1472j))
Show that there are no significant differences this implementation and the reference
>>> tuple((i, int(abs(100000 * (ref-can)))) for i in xrange(11) for ref, can in izip(reference_fft(range(1 << i)), fft(range(1 << i))) if int(abs(100000 * (ref-can))))
()
Bases: onyx.signalprocessing.sigprocbase
Updates self.init_options dictionary with option values, creating the dictionary if it doesn’t exist.
Process each of the elements of ‘items’. Return a list of results.
Return a serialized version of this object as a tuple of strings. The form of the tuple is: (module_name, factory_name, version, arg0, arg1, ...) where the version and the args will be passed to the factory to construct the object.
Bases: onyx.signalprocessing.sigprocbase
Updates self.init_options dictionary with option values, creating the dictionary if it doesn’t exist.
Process each of the elements of ‘items’. Return a list of results.
Return a serialized version of this object as a tuple of strings. The form of the tuple is: (module_name, factory_name, version, arg0, arg1, ...) where the version and the args will be passed to the factory to construct the object.
Bases: object
A callable object that returns the magnitudes of the DFT of a sequence of values. It pads the sequence to a power of two, calculates the DFT, and returns the magnitudes. If the optional constructor argument truncate is ‘half’ the returned sequence will be truncated to one half the length of the padded sequence. If truncate is ‘half+’ the returned sequence will be truncated to one half the length of the padded sequence plus one sample.
Examples:
Set up the three flavors of FftMag and a helper function
>>> mag = FftMag()
>>> maghalf = FftMag('half')
>>> maghalfp = FftMag('half+')
>>> def intify(iterable): return tuple(int(x) for x in iterable)
Some simple examples
>>> mag([])
(0,)
>>> mag([1])
(1,)
>>> mag([1] * 16)
(16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
>>> cycle3 = tuple(sin(3 * (2*PI*n/32)) for n in xrange(32))
>>> intify(mag(cycle3))
(0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0)
>>> intify(maghalf(cycle3))
(0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
Showing why ‘half+’ is important
>>> nyquist = tuple(item for item in [1, -1] * 16)
>>> intify(maghalf(nyquist))
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
>>> intify(maghalfp(nyquist))
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32)
>>> dd = [1, -1, 1, -1] + [0] * 16
>>> intify(maghalf(dd))
(0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 2, 3, 3, 3)
>>> intify(maghalfp(dd))
(0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 2, 3, 3, 3, 4)
>>> def energy(iterable): return sum(abs(x)*abs(x) for x in iterable)
>>> energy(dd)
4
>>> magdd = mag(dd)
>>> round(energy(x/sqrt(len(magdd)) for x in magdd), 5)
4.0
>>> intify(maghalfp(sin(3 * (2*PI*n/33)) for n in xrange(32)))
(0, 0, 1, 15, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
Bases: onyx.signalprocessing.sigprocbase
Updates self.init_options dictionary with option values, creating the dictionary if it doesn’t exist.
Process each of the elements of ‘items’. Return a list of results.
Return a serialized version of this object as a tuple of strings. The form of the tuple is: (module_name, factory_name, version, arg0, arg1, ...) where the version and the args will be passed to the factory to construct the object.
Bases: onyx.signalprocessing.filter.type2stageDevelop
Pre-emphasis of wave data. Boost higher frequencies.
>>> pe = PreEmphasis()
>>> pe
PreEmphasis((0, 0), (1, -0.98046875), 1)
>>> res = list()
>>> pe.set_recipient(res.append)
>>> pe.send(1)
>>> res
[1.0]
>>> pe.send(0)
>>> pe.send(0)
>>> pe.send(0)
>>> res
[1.0, 1.0, -0.98046875, 0.0]
>>> pe.send_many(xrange(5))
>>> res
[1.0, 1.0, -0.98046875, 0.0, 0.0, 1.0, 3.0, 4.01953125, 5.0390625]
Returns an opaque object representing the internal state of the filter. This object can be used subsequently as an argument to set_state() to return the filter to its state when get_state() was called.
Process each of the elements of ‘items’. Return a list of results.
Resets the filter to its zero state.
Set the state of the filter according to state, a value returned by the get_state() method.
Bases: onyx.signalprocessing.sigprocbase
Pre-emphasis of wave data. Boost higher frequencies with a one-zero filter.
>>> pe = PreEmphasis_draft('3dB=2500*Hz*Hz/sec/usec*usec')
>>> pe.configure(sample_rate=8000)
* Hz
* Hz
/ sec
/ usec
* usec
defaultdict(<type 'int'>, {'Hz': 2, 'usec': 0, 'sec': -1})
Process each of the elements of ‘items’. Return a list of results.
Return a serialized version of this object as a tuple of strings. The form of the tuple is: (module_name, factory_name, version, arg0, arg1, ...) where the version and the args will be passed to the factory to construct the object.
Bases: object
A class with a very simple recursive implementation of the decimate in time FFT algorithm for the DFT. Each instance caches twiddle factors, so a single instance can be used for all power-of-two DFT work. Instance is callable with a sequence or iterable of (possibly complex) numbers.
The module functions fft() and ifft() use a shared instance of SimpleFftMgr.
Calculates the Discrete Fourier Transform of the sequence of (possibly complex) values in the iterable argument. Returns a tuple of the resulting (likely complex) values.
Calculates the inverse Discrete Fourier Transform of the sequence of (possibly complex) values in the iterable argument. Returns a tuple of the resulting (likely complex) values.
Bases: onyx.builtin.attrdict
an auto dict of zero-paddings needed for power-of-two lengths
D.clear() -> None. Remove all items from D.
dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v. v defaults to None.
D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.
D.has_key(k) -> True if D has a key k, else False
D.items() -> list of D’s (key, value) pairs, as 2-tuples
D.iteritems() -> an iterator over the (key, value) items of D
D.iterkeys() -> an iterator over the keys of D
D.itervalues() -> an iterator over the values of D
D.keys() -> list of D’s keys
D.pop(k[,d]) -> v, remove specified key and return the corresponding value. If key is not found, d is returned if given, otherwise KeyError is raised
D.popitem() -> (k, v), remove and return some (key, value) pair as a 2-tuple; but raise KeyError if D is empty.
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
D.update(E, **F) -> None. Update D from dict/iterable E and F. If E has a .keys() method, does: for k in E: D[k] = E[k] If E lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]
D.values() -> list of D’s values
D.viewitems() -> a set-like object providing a view on D’s items
D.viewkeys() -> a set-like object providing a view on D’s keys
D.viewvalues() -> an object providing a view on D’s values
A very simple refernce implementation of the decimate in time FFT algorithm for the DFT.
The length of the (possibly complex) array must be a power of two.
>>> def scale_it(array): return tuple(int(round(abs(item) * 1000)) for item in array)
>>> reference_fft([])
()
>>> reference_fft([1])
(1,)
>>> scale_it(reference_fft([1] * 2))
(2000, 0)
>>> scale_it(reference_fft([1] * 2 + [0] * 2))
(2000, 1414, 0, 1414)
>>> scale_it(reference_fft([1] * 2 + [complex(0,1)] * 2 + [0] * (32 - 4)))
(2828, 3310, 3625, 3754, 3696, 3460, 3073, 2571, 2000, 1410, 850, 368, 0, 227, 299, 218, 0, 326, 721, 1139, 1531, 1849, 2053, 2110, 2000, 1718, 1273, 688, 0, 747, 1501, 2212)
>>> scale_it(reference_fft([1] * 1 + [0] * (16 - 1)))
(1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000)
>>> scale_it(reference_fft([1] * 8 + [0] * (32 - 8)))
(8000, 7214, 5126, 2436, 0, 1500, 1800, 1115, 0, 915, 1203, 802, 0, 739, 1020, 711, 0, 711, 1020, 739, 0, 802, 1203, 915, 0, 1115, 1800, 1500, 0, 2436, 5126, 7214)
>>> scale_it(reference_fft(sin(2 * PI * n * 3 / 32) for n in xrange(32)))
(0, 0, 0, 16000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16000, 0, 0)
>>> scale_it(reference_fft([1] * 2 + [complex(0,1)] * 2 + [0] * (16 - 4)))
(2828, 3625, 3696, 3073, 2000, 850, 0, 299, 0, 721, 1531, 2053, 2000, 1273, 0, 1501)
>>> tuple(complex(int(round(x.real)), int(round(x.imag))) for x in fft([-1000] * 2 + [-1000j] * 2 + [0] * (16 - 4)))
((-2000-2000j), (-3555-707j), (-3414+1414j), (-1707+2555j), 2000j, (472+707j), 0j, (-293+58j), 0j, (141-707j), (-586-1414j), (-1707-1141j), (-2000+0j), (-1058+707j), 0j, (-293-1472j))
>>> for i in xrange(8):
... scale_it(reference_fft(range(1 << i)))
(0,)
(1000, 1000)
(6000, 2828, 2000, 2828)
(28000, 10453, 5657, 4330, 4000, 4330, 5657, 10453)
(120000, 41007, 20905, 14400, 11314, 9622, 8659, 8157, 8000, 8157, 8659, 9622, 11314, 14400, 20905, 41007)
(496000, 163237, 82013, 55118, 41810, 33942, 28799, 25221, 22627, 20698, 19243, 18142, 17318, 16720, 16313, 16077, 16000, 16077, 16313, 16720, 17318, 18142, 19243, 20698, 22627, 25221, 28799, 33942, 41810, 55118, 82013, 163237)
(2016000, 652161, 326474, 218087, 164027, 131698, 110237, 94987, 83620, 74844, 67883, 62244, 57598, 53718, 50442, 47650, 45255, 43188, 41397, 39840, 38486, 37308, 36284, 35399, 34637, 33987, 33440, 32989, 32627, 32350, 32155, 32039, 32000, 32039, 32155, 32350, 32627, 32989, 33440, 33987, 34637, 35399, 36284, 37308, 38486, 39840, 41397, 43188, 45255, 47650, 50442, 53718, 57598, 62244, 67883, 74844, 83620, 94987, 110237, 131698, 164027, 218087, 326474, 652161)
(8128000, 2607856, 1304321, 869984, 652947, 522830, 436174, 374352, 328053, 292102, 263396, 239959, 220473, 204028, 189973, 177830, 167240, 157931, 149688, 142345, 135767, 129844, 124489, 119627, 115197, 111148, 107437, 104026, 100884, 97983, 95301, 92815, 90510, 88368, 86375, 84521, 82793, 81183, 79681, 78279, 76972, 75753, 74616, 73556, 72569, 71651, 70797, 70006, 69273, 68596, 67973, 67402, 66880, 66405, 65977, 65594, 65254, 64956, 64700, 64485, 64310, 64174, 64077, 64019, 64000, 64019, 64077, 64174, 64310, 64485, 64700, 64956, 65254, 65594, 65977, 66405, 66880, 67402, 67973, 68596, 69273, 70006, 70797, 71651, 72569, 73556, 74616, 75753, 76972, 78279, 79681, 81183, 82793, 84521, 86375, 88368, 90510, 92815, 95301, 97983, 100884, 104026, 107437, 111148, 115197, 119627, 124489, 129844, 135767, 142345, 149688, 157931, 167240, 177830, 189973, 204028, 220473, 239959, 263396, 292102, 328053, 374352, 436174, 522830, 652947, 869984, 1304321, 2607856)
A very simple refernce implementation of the decimate in time FFT algorithm for the DFT.
The length of the (possibly complex) array must be a power of two.
>>> def scale_it(array): return tuple(int(round(abs(item) * 1000)) for item in array)
>>> reference_splitradix_fft([])
()
>>> reference_splitradix_fft([1])
(1,)
>>> scale_it(reference_splitradix_fft([1] * 2))
(2000, 0)
>>> scale_it(reference_splitradix_fft([1] * 2 + [0] * 2))
(2000, 1414, 0, 1414)
>>> scale_it(reference_splitradix_fft([1] * 2 + [complex(0,1)] * 2 + [0] * (32 - 4)))
(2828, 3310, 3625, 3754, 3696, 3460, 3073, 2571, 2000, 1410, 850, 368, 0, 227, 299, 218, 0, 326, 721, 1139, 1531, 1849, 2053, 2110, 2000, 1718, 1273, 688, 0, 747, 1501, 2212)
>>> scale_it(reference_splitradix_fft([1] * 1 + [0] * (16 - 1)))
(1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000)
>>> scale_it(reference_splitradix_fft([1] * 8 + [0] * (32 - 8)))
(8000, 7214, 5126, 2436, 0, 1500, 1800, 1115, 0, 915, 1203, 802, 0, 739, 1020, 711, 0, 711, 1020, 739, 0, 802, 1203, 915, 0, 1115, 1800, 1500, 0, 2436, 5126, 7214)
>>> scale_it(reference_splitradix_fft(sin(2 * PI * n * 3 / 32) for n in xrange(32)))
(0, 0, 0, 16000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16000, 0, 0)
>>> scale_it(reference_splitradix_fft([1] * 2 + [complex(0,1)] * 2 + [0] * (16 - 4)))
(2828, 3625, 3696, 3073, 2000, 850, 0, 299, 0, 721, 1531, 2053, 2000, 1273, 0, 1501)
>>> tuple(complex(int(round(x.real)), int(round(x.imag))) for x in fft([-1000] * 2 + [-1000j] * 2 + [0] * (16 - 4)))
((-2000-2000j), (-3555-707j), (-3414+1414j), (-1707+2555j), 2000j, (472+707j), 0j, (-293+58j), 0j, (141-707j), (-586-1414j), (-1707-1141j), (-2000+0j), (-1058+707j), 0j, (-293-1472j))
>>> for i in xrange(8):
... scale_it(reference_splitradix_fft(range(1 << i)))
(0,)
(1000, 1000)
(6000, 2828, 2000, 2828)
(28000, 10453, 5657, 4330, 4000, 4330, 5657, 10453)
(120000, 41007, 20905, 14400, 11314, 9622, 8659, 8157, 8000, 8157, 8659, 9622, 11314, 14400, 20905, 41007)
(496000, 163237, 82013, 55118, 41810, 33942, 28799, 25221, 22627, 20698, 19243, 18142, 17318, 16720, 16313, 16077, 16000, 16077, 16313, 16720, 17318, 18142, 19243, 20698, 22627, 25221, 28799, 33942, 41810, 55118, 82013, 163237)
(2016000, 652161, 326474, 218087, 164027, 131698, 110237, 94987, 83620, 74844, 67883, 62244, 57598, 53718, 50442, 47650, 45255, 43188, 41397, 39840, 38486, 37308, 36284, 35399, 34637, 33987, 33440, 32989, 32627, 32350, 32155, 32039, 32000, 32039, 32155, 32350, 32627, 32989, 33440, 33987, 34637, 35399, 36284, 37308, 38486, 39840, 41397, 43188, 45255, 47650, 50442, 53718, 57598, 62244, 67883, 74844, 83620, 94987, 110237, 131698, 164027, 218087, 326474, 652161)
(8128000, 2607856, 1304321, 869984, 652947, 522830, 436174, 374352, 328053, 292102, 263396, 239959, 220473, 204028, 189973, 177830, 167240, 157931, 149688, 142345, 135767, 129844, 124489, 119627, 115197, 111148, 107437, 104026, 100884, 97983, 95301, 92815, 90510, 88368, 86375, 84521, 82793, 81183, 79681, 78279, 76972, 75753, 74616, 73556, 72569, 71651, 70797, 70006, 69273, 68596, 67973, 67402, 66880, 66405, 65977, 65594, 65254, 64956, 64700, 64485, 64310, 64174, 64077, 64019, 64000, 64019, 64077, 64174, 64310, 64485, 64700, 64956, 65254, 65594, 65977, 66405, 66880, 67402, 67973, 68596, 69273, 70006, 70797, 71651, 72569, 73556, 74616, 75753, 76972, 78279, 79681, 81183, 82793, 84521, 86375, 88368, 90510, 92815, 95301, 97983, 100884, 104026, 107437, 111148, 115197, 119627, 124489, 129844, 135767, 142345, 149688, 157931, 167240, 177830, 189973, 204028, 220473, 239959, 263396, 292102, 328053, 374352, 436174, 522830, 652947, 869984, 1304321, 2607856)
Bases: object
Slice-based views of a sequence.
>>> def printit(x):
... print len(x), ':',
... for i in x:
... print i,
... print
>>> r = xrange(32)
>>> s0 = sliceview(r, 0, 2)
>>> printit(s0)
16 : 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
>>> s1 = sliceview(r, 1, 2)
>>> printit(s1)
16 : 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31
>>> sx = sliceview(s1, 1, 2)
>>> printit(sx)
8 : 3 7 11 15 19 23 27 31
>>> sy = sliceview(sx, 0, 2)
>>> printit(sy)
4 : 3 11 19 27
>>> sz = sliceview(sx, 1, 2)
>>> printit(sz)
4 : 7 15 23 31