Skip to content

v0.8.0

Choose a tag to compare

@matthewrankin matthewrankin released this 31 Aug 17:01
· 26 commits to master since this release
f183e2a

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)               # after

matplotlib 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 optional minimum_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 in smooth() 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(), and plot_peak_hold(), the three
    functions that still had none (#4). The suite has grown from 53 to 154 tests.
  • WINDOW_FUNCTIONS, the windows smooth() accepts, is exported alongside
    STFT_WINDOWS.
  • siganalysis.__version__ reports the installed version. It was only
    reachable as siganalysis.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 while freq_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.