v0.8.0
A choice of STFT window, matplotlib made optional, and a correction to the
amplitude of every windowed STFT.
Upgrading
Two changes need attention before this drops into an existing project.
stft() takes window in place of use_hamming_window. The argument
names a window, or is None for no window. Passing the old argument raises a
TypeError, so nothing fails quietly:
stft(data, fs, frame, hop, use_hamming_window=True) # before
stft(data, fs, frame, hop, window="hamming") # after, and the default
stft(data, fs, frame, hop, use_hamming_window=False) # before
stft(data, fs, frame, hop, window=None) # aftermatplotlib is no longer installed by default. It is now an optional
dependency, so it is only installed for those who plot:
pip install siganalysis[plotting]
siganalysis.plot_spectrogram() and siganalysis.plot_peak_hold() still
resolve, so no import has to change. The package imports the plotting module
on the first use of either name, which keeps matplotlib out of a plain
import siganalysis. Plotting without it installed raises an ImportError
naming the extra.
Your numbers will change
stft() had scaled a Hamming windowed frame by 2 rather than by the gain of
the window, leaving every windowed amplitude 7.08% (0.594 dB) high. That is
corrected here, so the same code returns different amplitudes: about 7%
lower, and now equal to what the same tone reads with the window off. A 1.0
amplitude tone on the center of a bin reads 1.0 rather than 1.0708.
Nothing raises to tell you this happened. If you are comparing new output
against stored spectrograms, or against a limit line, expect a 0.594 dB shift
that is the fix working rather than a regression.
plot_spectrogram() also draws a slightly different range for a plot range
that does not land on a bin: the bin holding a value is now the one nearest
to it, rather than the one found by truncating (#11).
A choice of window
stft() accepts any of the windows named in STFT_WINDOWS: hamming,
hann (also spelled hanning, as smooth() spells it), blackman,
blackmanharris, and flattop. This closes #6, which asked for the Hann
window that the Agilent 35670A applies.
flattop is worth knowing about: it is the window a spectrum analyzer offers
for accurate amplitude. For a tone falling exactly between two bins, the worst
case, it reports 0.999 of a 1.0 amplitude, against 0.821 for hamming and
0.650 for no window, at the cost of resolving the neighboring bins.
Better failures
The functions taking STFT data now check it against the vectors describing its
axes and say what does not line up, rather than failing further down with an
error about numpy indexing (#12). plot_spectrogram() had accepted a time or
frequency vector that did not match the data at all and plotted it against
mislabelled axes. smooth2() gained the checks smooth() already had, having
quietly returned an empty array for a window longer than the signal.
plot_peak_hold() checks that a limit_array carries the fields it reads.
time_slice_zip() rejects fewer than one sample per time slice, which had put
it into an endless loop.
Also new
time_slice_zip()takes an optionalminimum_samples_in_last_slice. A last
slice shorter than that is folded into the slice before it, so a sample count
just past a multiple of the slice size no longer leaves a last slice too
short to process. The samples are kept, so the last slice grows (#20).- The docstring examples run as doctests under pytest, so an example that stops
working fails the suite (#10). The one insmooth()had been broken for some
time:np.linspace(-2, 2, 0.1)raises a TypeError, since the third argument
is a count rather than a step. - Tests for
smooth(),smooth2(), andplot_peak_hold(), the three
functions that still had none (#4). The suite has grown from 53 to 154 tests. WINDOW_FUNCTIONS, the windowssmooth()accepts, is exported alongside
STFT_WINDOWS.siganalysis.__version__reports the installed version. It was only
reachable assiganalysis.siganalysis.__version__before.
Fixed
stft()scaled a Hamming windowed frame by 2 rather than by the coherent
gain of the window. Applying a window scales every amplitude by the mean of
its samples, 0.5354 for a Hamming window, so the correction needed is
1/0.5354 = 1.8678 rather than 2._bin_holding()truncated whilefreq_bin()rounded to nearest, so the
module held two different answers to which bin holds a value. For 10 Hz bins,
6 Hz gave the 0 Hz bin one way and the 10 Hz bin the other. Both round to
nearest now, which is also exactly the set of bins overlapping a requested
plot range.smooth()returned a signal one sample short of the one given when the
window length was even and equal to the length of the signal. The window is
made odd before the length is checked now, so such a call raises IndexError
rather than quietly returning the wrong length.
Upgrading from v0.5.1 also picks up the v0.6.0 and v0.7.0 changes, which
include two further corrections that change results silently: stft() built
its time vector from the requested hop rather than the hop actually taken, and
plot_spectrogram() sliced its plot ranges exclusively while documenting them
as inclusive. See the CHANGELOG for the full history.