Filtering and Convolution

Filtering: Sculpting Signals in Frequency

A digital filter is a system that modifies a signal's frequency content. Lowpass filters remove high frequencies (noise), highpass filters extract fast variations, and bandpass filters isolate specific frequency bands (e.g., a single radio channel). SciPy provides a complete toolkit for designing and applying both FIR and IIR filters.

Definition:

Linear Convolution

The linear convolution of two sequences x[n]x[n] (length NN) and h[n]h[n] (length MM) produces a sequence of length N+Mβˆ’1N + M - 1:

(xβˆ—h)[n]=βˆ‘m=0Mβˆ’1h[m] x[nβˆ’m](x * h)[n] = \sum_{m=0}^{M-1} h[m]\, x[n - m]

In SciPy:

from scipy.signal import convolve, fftconvolve
y = convolve(x, h, mode='full')       # direct method
y = fftconvolve(x, h, mode='full')    # FFT-based (faster for large arrays)

convolve uses direct summation (O(NM)O(NM)); fftconvolve uses the FFT-based overlap-add approach (O((N+M)log⁑(N+M))O((N+M)\log(N+M))) and is faster when M>∼50M > \sim 50.

Definition:

FIR Filter (Finite Impulse Response)

An FIR filter computes the output as a weighted sum of the current and past input samples:

y[n]=βˆ‘k=0Mβˆ’1bk x[nβˆ’k]y[n] = \sum_{k=0}^{M-1} b_k\, x[n-k]

where {bk}\{b_k\} are the filter coefficients. The output is simply the convolution y=xβˆ—by = x * b. FIR filters are always stable and can have exactly linear phase (constant group delay).

from scipy.signal import firwin
b = firwin(numtaps=51, cutoff=0.3)  # lowpass, cutoff = 0.3 * Nyquist

Definition:

IIR Filter (Infinite Impulse Response)

An IIR filter uses feedback from past outputs, implementing a rational transfer function:

H(z)=βˆ‘k=0Mbkzβˆ’k1+βˆ‘k=1Nakzβˆ’kH(z) = \frac{\sum_{k=0}^{M} b_k z^{-k}}{1 + \sum_{k=1}^{N} a_k z^{-k}}

IIR filters achieve steep rolloff with fewer coefficients than FIR filters but can be unstable and have nonlinear phase.

from scipy.signal import butter, cheby1, sosfilt
sos = butter(N=5, Wn=0.3, btype='low', output='sos')
y = sosfilt(sos, x)

Always use output='sos' (second-order sections) instead of 'ba' (transfer function) for numerical stability. The 'ba' form suffers from severe coefficient quantization issues for orders above ~8.

Definition:

Second-Order Sections (SOS)

A second-order section (biquad) is a 2nd-order IIR filter:

Hi(z)=bi0+bi1zβˆ’1+bi2zβˆ’21+ai1zβˆ’1+ai2zβˆ’2H_i(z) = \frac{b_{i0} + b_{i1}z^{-1} + b_{i2}z^{-2}}{1 + a_{i1}z^{-1} + a_{i2}z^{-2}}

Any IIR filter of order NN can be factored into ⌈N/2βŒ‰\lceil N/2 \rceil cascaded second-order sections. This representation avoids the numerical instability of high-order polynomials:

sos = butter(10, 0.2, output='sos')   # 5 second-order sections
y = sosfilt(sos, x)                    # cascade filtering

Definition:

Matched Filter

The matched filter for a known signal s[n]s[n] in additive white Gaussian noise is the filter whose impulse response is the time-reversed conjugate of ss:

hMF[n]=sβˆ—[Mβˆ’1βˆ’n]h_\text{MF}[n] = s^*[M - 1 - n]

The matched filter maximizes the output signal-to-noise ratio (SNR). In practice, it is implemented as cross-correlation:

from scipy.signal import correlate
y = correlate(received, template, mode='full')

The matched filter is the optimal linear detector in AWGN. It appears in radar (pulse compression), wireless (RAKE receiver), and spread-spectrum systems.

Definition:

Optimal FIR Design with the Remez Algorithm

The Remez exchange algorithm (Parks-McClellan) designs equiripple FIR filters that minimize the maximum error in specified frequency bands:

from scipy.signal import remez
b = remez(
    numtaps=65,
    bands=[0, 0.2, 0.3, 0.5],   # passband: 0-0.2, stopband: 0.3-0.5
    desired=[1, 0],               # gain in each band
    fs=1.0,
)

The result has equiripple behavior: the approximation error oscillates with equal amplitude across each band (Chebyshev optimality).

Theorem: Matched Filter Maximizes Output SNR

Among all linear filters h[n]h[n], the matched filter hMF[n]=sβˆ—[Mβˆ’1βˆ’n]h_\text{MF}[n] = s^*[M-1-n] maximizes the output signal-to-noise ratio at the sampling instant:

SNRout=∣y[n0]∣2E{∣noiseΒ output∣2}≀2EsN0\text{SNR}_\text{out} = \frac{|y[n_0]|^2}{E\{|\text{noise output}|^2\}} \le \frac{2E_s}{N_0}

where Es=βˆ‘βˆ£s[n]∣2E_s = \sum|s[n]|^2 is the signal energy and N0/2N_0/2 is the noise power spectral density. Equality holds for the matched filter.

The matched filter correlates the received signal with a template of what we expect. By the Cauchy-Schwarz inequality, this correlation is maximized when the filter is a scaled, conjugated, time-reversed copy of the template signal.

Example: Butterworth Lowpass Filter Design

Design a 5th-order Butterworth lowpass filter with a cutoff frequency of 100 Hz for a signal sampled at 1000 Hz. Apply it to a noisy signal and compare the result.

Example: FIR vs IIR: Same Cutoff, Different Behavior

Compare a 51-tap FIR filter (windowed sinc) and a 5th-order Butterworth IIR filter with the same cutoff frequency. Examine the magnitude response, phase response, and group delay.

Example: Matched Filter for Pulse Detection in Noise

A chirp pulse s(t)=sin⁑(2Ο€(f0+Ξ²t/2)t)s(t) = \sin(2\pi(f_0 + \beta t/2)t) is transmitted and received with additive noise. Use a matched filter to detect the pulse arrival time.

Interactive Filter Designer

Design and visualize FIR and IIR filters with adjustable parameters. Compare magnitude response, phase response, and impulse response.

Parameters

Classical Analog Filter Prototypes

Classical Analog Filter Prototypes
Comparison of Butterworth (maximally flat), Chebyshev Type I (equiripple passband), and Elliptic (equiripple both bands) filter magnitude responses. All are 5th-order with the same cutoff frequency.

Filter Design and Application

python
Complete examples: FIR design (firwin, remez), IIR design (butter, cheby1), sosfilt, fftconvolve, and matched filtering.
# Code from: ch07/python/filtering.py
# Load from backend supplements endpoint

Quick Check

Why should you use output='sos' instead of output='ba' when designing IIR filters with scipy.signal.butter?

SOS is faster to compute

SOS form avoids numerical instability from high-order polynomials

SOS produces linear phase filters

SOS allows higher sampling rates

Common Mistake: Using ba Form for High-Order IIR Filters

Mistake:

Designing a 10th-order Butterworth filter with output='ba' and applying it with lfilter(b, a, x). For orders above ~8, the polynomial coefficients lose precision due to floating-point errors.

Correction:

Always use SOS form:

sos = butter(10, 0.2, output='sos')
y = sosfilt(sos, x)

Key Takeaway

FIR filters are always stable and can have linear phase; IIR filters achieve steeper rolloff with fewer coefficients. Always use the SOS representation for IIR filters. For long convolutions, fftconvolve is faster than direct convolve. The matched filter maximizes SNR and is the foundation of radar and communication receivers.

FIR Filter

A filter whose output depends only on the current and past input samples. Always stable, and can be designed with exactly linear phase.

Related: IIR Filter, Linear Convolution

IIR Filter

A filter that uses feedback from past outputs, implementing a rational transfer function. Achieves steep rolloff with fewer coefficients than FIR, but can be unstable.

Related: FIR Filter, Poles, QR Is More Stable Than Normal Equations