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
Linear Convolution
The linear convolution of two sequences (length ) and (length ) produces a sequence of length :
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 (); fftconvolve uses the
FFT-based overlap-add approach () and is faster
when .
Definition: FIR Filter (Finite Impulse Response)
FIR Filter (Finite Impulse Response)
An FIR filter computes the output as a weighted sum of the current and past input samples:
where are the filter coefficients. The output is simply the convolution . 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)
IIR Filter (Infinite Impulse Response)
An IIR filter uses feedback from past outputs, implementing a rational transfer function:
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)
Second-Order Sections (SOS)
A second-order section (biquad) is a 2nd-order IIR filter:
Any IIR filter of order can be factored into 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
Matched Filter
The matched filter for a known signal in additive white Gaussian noise is the filter whose impulse response is the time-reversed conjugate of :
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
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 , the matched filter maximizes the output signal-to-noise ratio at the sampling instant:
where is the signal energy and 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.
Design and apply the filter
from scipy.signal import butter, sosfilt, sosfreqz
import numpy as np
fs = 1000
fc = 100
sos = butter(N=5, Wn=fc, btype='low', fs=fs, output='sos')
# Create noisy signal: 30 Hz sine + noise
t = np.arange(0, 1, 1/fs)
x = np.sin(2*np.pi*30*t) + 0.5*np.random.randn(len(t))
y = sosfilt(sos, x)
The filter passes the 30 Hz sinusoid while suppressing high-frequency noise. The Butterworth response is maximally flat in the passband.
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.
Design both filters
from scipy.signal import firwin, butter, freqz, sosfreqz, group_delay
# FIR: 51 taps, cutoff at 0.3 * Nyquist
b_fir = firwin(51, 0.3)
w_fir, H_fir = freqz(b_fir, worN=1024)
# IIR: 5th-order Butterworth, same cutoff
sos = butter(5, 0.3, output='sos')
w_iir, H_iir = sosfreqz(sos, worN=1024)
The IIR filter achieves steeper rolloff with fewer coefficients, but the FIR filter has exactly linear phase (constant group delay of 25 samples).
Example: Matched Filter for Pulse Detection in Noise
A chirp pulse is transmitted and received with additive noise. Use a matched filter to detect the pulse arrival time.
Generate chirp and apply matched filter
from scipy.signal import chirp, correlate
import numpy as np
fs = 1000
T_pulse = 0.05
t_pulse = np.arange(0, T_pulse, 1/fs)
s = chirp(t_pulse, f0=50, f1=200, t1=T_pulse)
# Received signal: delayed pulse + noise
t = np.arange(0, 0.3, 1/fs)
received = np.zeros(len(t))
delay_idx = 100
received[delay_idx:delay_idx+len(s)] = s
received += 2 * np.random.randn(len(t))
# Matched filter output
mf_output = correlate(received, s, mode='full')
peak_idx = np.argmax(np.abs(mf_output))
The matched filter output shows a sharp peak at the pulse arrival time, even when the pulse is buried in noise.
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
Filter Design and Application
# Code from: ch07/python/filtering.py
# Load from backend supplements endpointQuick 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
The ba form represents a single high-order polynomial whose roots are sensitive to coefficient quantization. SOS cascades second-order stages, each numerically well-conditioned.
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