FIR filter designer (windowed sinc)

Design a linear-phase FIR low-pass, high-pass or band-pass filter with the window method. The coefficients match scipy.signal.firwin; copy them as Python or as a C array for CMSIS-DSP.

Coefficients: Python (SciPy) and C (CMSIS-DSP)

Tips

  • More taps give a sharper transition but more delay ((N−1)/2 samples).
  • The window, not the number of taps, sets how deep the stopband goes (see the table below).
  • High-pass filters need an odd number of taps; the tool adds one if needed.

How the window method works

An ideal low-pass filter with cutoff ωc (in radians per sample) has an impulse response that is a sinc function stretching forever in both directions. The window method keeps N samples of it, centred, and tapers them with a window w[n]:

\[h[n] = w[n]\,\frac{\sin\!\big(\omega_c\,(n - M)\big)}{\pi\,(n - M)}, \qquad M = \frac{N-1}{2}, \quad n = 0, \dots, N-1\]

(At n = M the fraction is replaced by its limit, ωc/π.) High-pass and band-pass filters are built the same way from differences of sincs. Like SciPy, the tool then scales the taps so the gain is exactly 1 at the centre of the passband (0 Hz for low-pass, fs/2 for high-pass). Because h[n] is symmetric, the filter has linear phase: every frequency is delayed by the same M samples, so waveshapes are preserved.

Choosing the window

Cutting the sinc off abruptly causes ripple near the band edges (the Gibbs phenomenon). A smoother window lowers that ripple but widens the transition band. We measured the minimum stopband attenuation of SciPy designs at 51 and 101 taps:

WindowMinimum stopband attenuationTransition width
Rectangularabout 21 dBnarrowest
Hannabout 44 dBwider
Hammingabout 53 dBwider (similar to Hann)
Blackmanabout 75 dBwidest (about 1.6× Hamming)

These match the classic figures in Oppenheim & Schafer. Adding taps narrows the transition roughly in proportion to 1/N but leaves the attenuation almost unchanged, so pick the window for the stopband depth you need, then add taps until the transition is narrow enough. If you need a specific attenuation, the Kaiser window (scipy.signal.kaiserord) lets you set it directly.

Check a design in Python

The coefficients from this page are identical to SciPy’s, so you can verify a design like this (here 51 taps, 1 kHz cutoff, 8 kHz sample rate):

from scipy import signal
import numpy as np

h = signal.firwin(51, 1000, fs=8000, window="hamming")
w, H = signal.freqz(h, worN=4096, fs=8000)
print("gain at 0 Hz:", abs(H[0]))          # 1.0
print("delay (ms):", (len(h) - 1) / 2 / 8000 * 1000)  # 3.125

Using the taps with CMSIS-DSP

Paste the C array, then set up the filter with arm_fir_init_f32(&S, numTaps, coeffs, state, blockSize). The state buffer must hold numTaps + blockSize − 1 samples. CMSIS-DSP expects coefficients in time-reversed order, which makes no difference here because linear-phase taps are symmetric.

FAQ

FIR or IIR: which should I use?

FIR filters are always stable and can have exactly linear phase, but need more taps (and delay) for a sharp cutoff. IIR filters reach a sharp cutoff with far fewer operations, but their phase is not linear and they can become unstable with rounding. Use FIR when phase or robustness matters; IIR when computing budget is tight.

Why does a high-pass filter need an odd number of taps?

A symmetric FIR filter with an even number of taps always has a zero at fs/2, so it cannot pass the highest frequencies. With an odd number of taps there is no such forced zero.

How much delay does the filter add?

Exactly (N − 1)/2 samples for every frequency. For 51 taps at 8 kHz that is 25 samples, or 3.125 ms.

Why is my stopband not as deep as the table says?

The table gives the window method’s typical minimum. Very short filters, cutoffs close to 0 or fs/2, and fixed-point rounding of the coefficients (for example q15) can all reduce it. Check the response the tool plots, or use freqz as shown above.

Related lessons: impulse response and DSP on a Cortex-M55.

References

  • Oppenheim, A. V. & Schafer, R. W., Discrete-Time Signal Processing, 3rd ed., Pearson, 2010, chapter on filter design techniques (FIR design by windowing).
  • Lyons, R. G., Understanding Digital Signal Processing, 3rd ed., Prentice Hall, 2011, chapter on finite impulse response filters.
  • SciPy documentation: scipy.signal.firwin.
  • Arm CMSIS-DSP documentation: FIR filters.
Esc