ex/iir.py uses a = 0.85, b = 0.25: |z| = 0.89, so the
poles sit just inside the unit circle and the resonance picks out the
low-frequency image while the copies further out fall at 40
dB/decade. Push |a + jb| past 1 and the filter is unstable.
A second order infinite impulse response filter is one line of code:
y[n] = b*x[n-1] + 2a*y[n-1] - (a²+b²)*y[n-2]. Each
output feeds back the two previous outputs, and it is that feedback that
makes the impulse response infinite and puts a complex conjugate pole
pair in the transfer function
H(z) = b·z / (z² − 2a·z + a²+b²)
with a zero at the origin and poles at z = a ± jb — the same
complex frequency the lecture's z-plane discussion uses. On the z-plane
panel the circle is |z| = 1; the filter is stable as long as
|a + jb| < 1.
The bottom middle panel is the measured output spectrum, and the yellow
trace is |H(f)| evaluated straight from b and a — they
should lie on top of each other, which is a good way to convince yourself
that the loop and the algebra agree.
#- Second-order IIR filter, poles at z = a +/- jb
b = 0.25
a = 0.85
z = a + 1j*b
z_abs = np.abs(z)
y = np.zeros(N)
for i in range(2,N):
y[i] = b*x_sn[i-1] + 2*a*y[i-1] - (a*a + b*b)*y[i-2]
Source: ex/iir.py.
This page makes the a coefficient a slider so you can watch
the pole move.
There are faster ways to do this in Python; see
scipy.signal.iirfilter.