Elliptic Filter (Cauer Filter): Design Principles and Python Implementation

A comprehensive guide to elliptic (Cauer) filter design: mathematical foundations, equiripple characteristics, comparison with Butterworth and Chebyshev filters, and practical Python implementation using SciPy for lowpass, highpass, and bandpass filters.

Introduction

The elliptic filter (also known as the Cauer filter) is an IIR filter that exhibits equiripple behavior in both the passband and the stopband. Among all IIR filters of the same order, it achieves the steepest transition band:

\[\text{Elliptic} > \text{Chebyshev (Type I \& II)} > \text{Butterworth}\]

The trade-off is that both the passband and stopband contain ripples. This article covers the mathematical background and practical design using SciPy.

Frequency Response

The squared magnitude response of an elliptic filter is:

\[|H(j\Omega)|^2 = \frac{1}{1 + \varepsilon^2 R_n^2(\xi, \Omega/\Omega_p)} \tag{1}\]

where:

  • \(\varepsilon\): passband ripple parameter (\(\varepsilon^2 = 10^{R_p/10} - 1\))
  • \(R_n(\xi, x)\): Jacobi elliptic rational function of order \(n\)
  • \(\xi\): selectivity parameter (\(\xi = \Omega_p / \Omega_s > 1\))
  • \(n\): filter order

Jacobi Elliptic Rational Function

The function \(R_n(\xi, x)\) is constructed from Jacobi elliptic functions (\(\text{sn}\), \(\text{cn}\), \(\text{dn}\)) and satisfies:

  • \(|x| \le 1 \Rightarrow |R_n(\xi, x)| \le 1\) (passband)
  • \(|x| \ge \xi \Rightarrow |R_n(\xi, x)| \ge \xi\) (stopband)

This property ensures equiripple behavior in both bands simultaneously.

Order Determination

The minimum order required to meet specifications (\(\Omega_p\), \(\Omega_s\), \(R_p\), \(R_s\)) is:

\[n \ge \frac{K(1/\xi) \cdot K(\sqrt{1-1/\xi_s^2})}{K(\xi_s) \cdot K(\sqrt{1-1/\xi^2})} \tag{2}\]

where \(K(\cdot)\) is the complete elliptic integral of the first kind. In practice, scipy.signal.ellipord computes this automatically.

Comparison with Butterworth and Chebyshev

FilterPassbandStopbandTransition Band (same order)Phase Response
ButterworthMaximally flatMonotoneWidestBest
Chebyshev Type IEquirippleMonotoneModerateModerate
Chebyshev Type IIMaximally flatEquirippleModerateModerate
EllipticEquirippleEquirippleNarrowestWorst

The elliptic filter maximizes transition band steepness at the cost of nonlinear phase response (poor group delay flatness).

SciPy Implementation

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

# ===== Design Parameters =====
fs = 1000          # Sampling frequency [Hz]
f_pass = 100       # Passband edge [Hz]
f_stop = 150       # Stopband edge [Hz]
rp = 1.0           # Passband ripple [dB]
rs = 60.0          # Stopband attenuation [dB]

# Normalize to Nyquist frequency
nyq = fs / 2
wp = f_pass / nyq
ws = f_stop / nyq

# ===== Compute Minimum Order =====
n, wn = signal.ellipord(wp, ws, rp, rs)
print(f"Minimum elliptic filter order: {n}")

# ===== Design Lowpass Elliptic Filter =====
b, a = signal.ellip(n, rp, rs, wn, btype='low')

# ===== Compute Frequency Response =====
w, h = signal.freqz(b, a, worN=2048, fs=fs)

# ===== Plot =====
fig, axes = plt.subplots(2, 1, figsize=(10, 8))

# Magnitude response
axes[0].plot(w, 20 * np.log10(np.abs(h)), label=f'Elliptic (n={n})', color='royalblue')
axes[0].axvline(f_pass, color='green', linestyle='--', alpha=0.7, label=f'Passband edge {f_pass}Hz')
axes[0].axvline(f_stop, color='red', linestyle='--', alpha=0.7, label=f'Stopband edge {f_stop}Hz')
axes[0].axhline(-rp, color='orange', linestyle=':', alpha=0.7, label=f'Ripple -{rp}dB')
axes[0].axhline(-rs, color='purple', linestyle=':', alpha=0.7, label=f'Attenuation -{rs}dB')
axes[0].set_ylim(-80, 5)
axes[0].set_xlabel('Frequency [Hz]')
axes[0].set_ylabel('Gain [dB]')
axes[0].set_title('Elliptic Filter: Magnitude Response')
axes[0].legend()
axes[0].grid(True)

# Phase response
angles = np.unwrap(np.angle(h))
axes[1].plot(w, np.degrees(angles), color='royalblue')
axes[1].set_xlabel('Frequency [Hz]')
axes[1].set_ylabel('Phase [degrees]')
axes[1].set_title('Elliptic Filter: Phase Response')
axes[1].grid(True)

plt.tight_layout()
plt.show()

Order Comparison Across Filter Types

Using the same specification (\(f_p=100\) Hz, \(f_s=150\) Hz, \(R_p=1\) dB, \(R_s=60\) dB):

from scipy import signal

fs = 1000
nyq = fs / 2
wp = 100 / nyq
ws = 150 / nyq
rp, rs = 1.0, 60.0

n_butter, _ = signal.buttord(wp, ws, rp, rs)
n_cheby1, _ = signal.cheb1ord(wp, ws, rp, rs)
n_cheby2, _ = signal.cheb2ord(wp, ws, rp, rs)
n_ellip, _ = signal.ellipord(wp, ws, rp, rs)

print(f"Butterworth  : order {n_butter}")
print(f"Chebyshev I  : order {n_cheby1}")
print(f"Chebyshev II : order {n_cheby2}")
print(f"Elliptic     : order {n_ellip}")

Typical output:

Butterworth  : order 11
Chebyshev I  : order 6
Chebyshev II : order 6
Elliptic     : order 4

The elliptic filter achieves the same specification with roughly 1/3 the order of a Butterworth filter.

Highpass, Bandpass, and Bandstop Design

from scipy import signal

fs = 1000
nyq = fs / 2
rp, rs = 1.0, 60.0

# ===== Highpass Filter =====
n_hp, wn_hp = signal.ellipord(200/nyq, 100/nyq, rp, rs)
b_hp, a_hp = signal.ellip(n_hp, rp, rs, wn_hp, btype='high')

# ===== Bandpass Filter =====
n_bp, wn_bp = signal.ellipord(
    [100/nyq, 300/nyq], [50/nyq, 400/nyq], rp, rs
)
b_bp, a_bp = signal.ellip(n_bp, rp, rs, wn_bp, btype='bandpass')

# ===== Bandstop Filter =====
n_bs, wn_bs = signal.ellipord(
    [50/nyq, 400/nyq], [100/nyq, 300/nyq], rp, rs
)
b_bs, a_bs = signal.ellip(n_bs, rp, rs, wn_bs, btype='bandstop')

Numerical Stability: Use SOS Format

For higher-order filters (roughly n > 6), direct-form coefficients (b, a) can suffer from numerical precision issues. Use Second-Order Sections (SOS) format instead:

# SOS format: more numerically stable
sos = signal.ellip(n, rp, rs, wn, btype='low', output='sos')

# Zero-phase filtering (offline use)
y = signal.sosfiltfilt(sos, x)

# Causal filtering (real-time use)
y = signal.sosfilt(sos, x)
FunctionPhase delayReal-timeRecommended when
signal.lfilterYesYesReal-time, low-order filters
signal.filtfiltNone (zero-phase)NoOffline analysis
signal.sosfiltYesYesHigher-order, real-time
signal.sosfiltfiltNoneNoHigher-order, offline

When to Choose Elliptic Filters

ScenarioRecommended Filter
Steepest possible transition bandElliptic
Minimize filter order (computation cost)Elliptic
Passband ripple must be zeroChebyshev Type II or Butterworth
Flat group delay (linear phase)Butterworth or Bessel
Low latency real-time processingButterworth (low order)