Introduction
The Chebyshev filter achieves a sharper transition band than a Butterworth filter of the same order by allowing equiripple in the passband (Type I) or stopband (Type II).
Where the Butterworth filter maximizes flatness in the passband, the Chebyshev filter trades that flatness for steeper rolloff. This article covers the mathematical foundations of both types and their practical design with SciPy.
Chebyshev Polynomials
Chebyshev filter frequency responses are based on the Chebyshev polynomial \(T_N(x)\):
\[T_N(x) = \begin{cases} \cos(N \arccos x) & |x| \leq 1 \\ \cosh(N \operatorname{arccosh} x) & |x| > 1 \end{cases} \tag{1}\]The first few Chebyshev polynomials are:
| Order \(N\) | \(T_N(x)\) |
|---|---|
| 0 | \(1\) |
| 1 | \(x\) |
| 2 | \(2x^2 - 1\) |
| 3 | \(4x^3 - 3x\) |
| 4 | \(8x^4 - 8x^2 + 1\) |
Within \(|x| \leq 1\), the Chebyshev polynomial oscillates between \(-1\) and \(+1\) with equal amplitude — this property produces the equiripple characteristic in the filter.
Chebyshev Type I Filter
Magnitude Response
The magnitude squared function of an \(N\)-th order Chebyshev Type I lowpass filter is:
\[|H(j\Omega)|^2 = \frac{1}{1 + \varepsilon^2 T_N^2\!\left(\dfrac{\Omega}{\Omega_p}\right)} \tag{2}\]where:
- \(\varepsilon\): ripple factor (\(\varepsilon > 0\))
- \(\Omega_p\): passband edge angular frequency
- \(T_N\): Chebyshev polynomial of order \(N\)
In the passband \(\Omega \leq \Omega_p\), we have \(T_N(\Omega/\Omega_p) \in [-1, 1]\), so the magnitude ripples between \(1/\sqrt{1+\varepsilon^2}\) and \(1\) with equal amplitude. The relationship between passband ripple \(R_p\) [dB] and the ripple factor is:
\[\varepsilon = \sqrt{10^{R_p/10} - 1} \tag{3}\]Pole Locations
The poles of a Chebyshev Type I filter lie on an ellipse. For \(k = 1, 2, \ldots, N\):
\[\sigma_k = -\sinh\!\left(\frac{\operatorname{arcsinh}(1/\varepsilon)}{N}\right)\sin\theta_k \tag{4}\]\[\omega_k = \cosh\!\left(\frac{\operatorname{arcsinh}(1/\varepsilon)}{N}\right)\cos\theta_k \tag{5}\]where \(\theta_k = \dfrac{\pi(2k-1)}{2N}\). Only the left half-plane poles \(s_k = \sigma_k + j\omega_k\) are used for stability.
Chebyshev Type II Filter
The Chebyshev Type II (inverse Chebyshev) filter places equiripple in the stopband while maintaining a monotonic passband. Its magnitude squared function is:
\[|H(j\Omega)|^2 = \frac{1}{1 + \left[\varepsilon^2 T_N^2\!\left(\dfrac{\Omega_s}{\Omega}\right)\right]^{-1}} \tag{6}\]where \(\Omega_s\) is the stopband edge angular frequency. With a monotonic passband, the phase response is smoother than Type I.
Type I vs Type II Comparison
| Property | Chebyshev Type I | Chebyshev Type II |
|---|---|---|
| Passband | Equiripple | Monotone (flat) |
| Stopband | Monotone | Equiripple |
| Transition | Sharper than Butterworth | Sharper than Butterworth |
| Phase | Nonlinear | Relatively smooth |
| Use case | Sharp cutoff, ripple allowed | Flat passband, sharp cutoff |
Filter Order Determination
Given specifications (passband ripple \(R_p\), stopband attenuation \(R_s\), passband edge \(\Omega_p\), stopband edge \(\Omega_s\)), the minimum required order is:
\[N \geq \frac{\operatorname{arccosh}\!\left(\sqrt{\dfrac{10^{R_s/10}-1}{10^{R_p/10}-1}}\right)}{\operatorname{arccosh}(\Omega_s/\Omega_p)} \tag{7}\]For the same specification, a Chebyshev filter requires a lower order than a Butterworth filter.
Python Implementation
Chebyshev Type I Filter with SciPy
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# --- Filter specification ---
fs = 1000 # Sampling frequency [Hz]
fp = 100 # Passband edge [Hz]
rp = 1.0 # Passband ripple [dB]
orders = [2, 4, 6]
fig, axes = plt.subplots(2, 1, figsize=(10, 8))
for N in orders:
sos = signal.cheby1(N, rp, fp, btype='low', fs=fs, output='sos')
w, h = signal.sosfreqz(sos, worN=4096, fs=fs)
axes[0].plot(w, 20 * np.log10(np.abs(h) + 1e-12), label=f'N={N}')
axes[1].plot(w, np.degrees(np.unwrap(np.angle(h))), label=f'N={N}')
axes[0].set_xlabel('Frequency [Hz]')
axes[0].set_ylabel('Magnitude [dB]')
axes[0].set_title('Chebyshev Type I - Magnitude Response (rp=1dB)')
axes[0].set_xlim(0, 500)
axes[0].set_ylim(-80, 5)
axes[0].axvline(fp, color='gray', linestyle='--', alpha=0.5, label=f'fp={fp}Hz')
axes[0].axhline(-rp, color='r', linestyle=':', alpha=0.5, label=f'-{rp}dB ripple')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[1].set_xlabel('Frequency [Hz]')
axes[1].set_ylabel('Phase [degrees]')
axes[1].set_title('Chebyshev Type I - Phase Response')
axes[1].set_xlim(0, 500)
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Chebyshev Type II Filter
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
fs = 1000
fs_stop = 150 # Stopband edge [Hz]
rs = 40.0 # Stopband attenuation [dB]
orders = [2, 4, 6]
fig, axes = plt.subplots(2, 1, figsize=(10, 8))
for N in orders:
sos = signal.cheby2(N, rs, fs_stop, btype='low', fs=fs, output='sos')
w, h = signal.sosfreqz(sos, worN=4096, fs=fs)
axes[0].plot(w, 20 * np.log10(np.abs(h) + 1e-12), label=f'N={N}')
axes[1].plot(w, np.degrees(np.unwrap(np.angle(h))), label=f'N={N}')
axes[0].set_xlabel('Frequency [Hz]')
axes[0].set_ylabel('Magnitude [dB]')
axes[0].set_title('Chebyshev Type II - Magnitude Response (rs=40dB)')
axes[0].set_xlim(0, 500)
axes[0].set_ylim(-80, 5)
axes[0].axvline(fs_stop, color='gray', linestyle='--', alpha=0.5, label=f'fs={fs_stop}Hz')
axes[0].axhline(-rs, color='r', linestyle=':', alpha=0.5, label=f'-{rs}dB')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[1].set_xlabel('Frequency [Hz]')
axes[1].set_ylabel('Phase [degrees]')
axes[1].set_title('Chebyshev Type II - Phase Response')
axes[1].set_xlim(0, 500)
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Order Comparison with Butterworth
from scipy.signal import buttord, cheb1ord
fp = 100 # Passband edge [Hz]
fs_stop = 150 # Stopband edge [Hz]
rp = 1.0 # Passband ripple [dB]
rs = 40.0 # Stopband attenuation [dB]
fs = 1000 # Sampling frequency
N_butter, _ = buttord(fp, fs_stop, rp, rs, fs=fs)
N_cheby1, _ = cheb1ord(fp, fs_stop, rp, rs, fs=fs)
print(f"Butterworth : order {N_butter}")
print(f"Chebyshev I : order {N_cheby1}")
For the same specification, Chebyshev Type I typically achieves a lower order than Butterworth.
Noise Reduction Application
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
fs = 1000
t = np.arange(0, 1, 1/fs)
clean = np.sin(2 * np.pi * 10 * t) + 0.5 * np.sin(2 * np.pi * 30 * t)
noisy = clean + 0.8 * np.random.randn(len(t))
# Butterworth (order 4)
sos_butter = signal.butter(4, 100, fs=fs, output='sos')
y_butter = signal.sosfiltfilt(sos_butter, noisy)
# Chebyshev Type I (order 4, 1 dB ripple)
sos_cheby1 = signal.cheby1(4, 1, 100, fs=fs, output='sos')
y_cheby1 = signal.sosfiltfilt(sos_cheby1, noisy)
# Chebyshev Type II (order 4, 40 dB attenuation)
sos_cheby2 = signal.cheby2(4, 40, 150, fs=fs, output='sos')
y_cheby2 = signal.sosfiltfilt(sos_cheby2, noisy)
fig, axes = plt.subplots(4, 1, figsize=(10, 12), sharex=True)
axes[0].plot(t, noisy, alpha=0.5, label='Noisy'); axes[0].plot(t, clean, 'k', label='Original')
axes[0].set_title('Input Signal'); axes[0].legend(); axes[0].grid(True, alpha=0.3)
axes[1].plot(t, y_butter, label='Butterworth N=4'); axes[1].plot(t, clean, 'k', lw=1.5)
axes[1].set_title('Butterworth (N=4)'); axes[1].legend(); axes[1].grid(True, alpha=0.3)
axes[2].plot(t, y_cheby1, label='Chebyshev I N=4, rp=1dB'); axes[2].plot(t, clean, 'k', lw=1.5)
axes[2].set_title('Chebyshev Type I (N=4, rp=1dB)'); axes[2].legend(); axes[2].grid(True, alpha=0.3)
axes[3].plot(t, y_cheby2, label='Chebyshev II N=4, rs=40dB'); axes[3].plot(t, clean, 'k', lw=1.5)
axes[3].set_title('Chebyshev Type II (N=4, rs=40dB)')
axes[3].set_xlabel('Time [s]'); axes[3].legend(); axes[3].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Comparison with Other IIR Filters
| Filter | Passband | Stopband | Transition | Phase |
|---|---|---|---|---|
| Butterworth | Maximally flat | Monotone | Wide | Relatively smooth |
| Chebyshev Type I | Equiripple | Monotone | Narrower | Nonlinear |
| Chebyshev Type II | Monotone | Equiripple | Narrower | Relatively smooth |
| Elliptic (Cauer) | Equiripple | Equiripple | Narrowest | Most nonlinear |
Use Type I when a sharp cutoff is needed and passband ripple is acceptable. Use Type II when passband flatness is required but improved stopband attenuation is desired.
Summary
- Chebyshev filters exploit the equal-amplitude oscillation property of Chebyshev polynomials to produce equiripple IIR filters
- Type I has equiripple in the passband; Type II has equiripple in the stopband
- Both types achieve sharper transition bands than Butterworth at the same order
- In SciPy, use
signal.cheby1()/signal.cheby2()for design, andsignal.cheb1ord()/signal.cheb2ord()to compute the required order automatically
Related Articles
- Lowpass Filter Design: Moving Average, Butterworth, and Chebyshev - Compares all three filter types by frequency response, group delay, and step response.
- Butterworth Filter Design: Theory and Python Implementation - The maximally flat IIR filter, a close relative of the Chebyshev.
- FIR vs IIR Filters - Design philosophy differences between Chebyshev (IIR) and FIR filters.
- Notch Filter Design and Python Implementation - Applying IIR filter design to remove a specific frequency.
- Exponential Moving Average (EMA) Filter: Frequency Characteristics - The simplest 1st-order IIR filter as a stepping stone to Chebyshev.
- Fast Fourier Transform (FFT): Theory and Python Implementation - Analyzing filter frequency responses with FFT.
References
- Proakis, J. G., & Manolakis, D. G. (2007). Digital Signal Processing (4th ed.). Pearson.
- Oppenheim, A. V., & Schafer, R. W. (2009). Discrete-Time Signal Processing (3rd ed.). Prentice Hall.
- SciPy scipy.signal.cheby1 documentation
- SciPy scipy.signal.cheby2 documentation