A guitar amplifier picks up mains hum, and its designer wants a filter that removes exactly that frequency. The neat way to design one is to say where the transfer function should be zero and where it should blow up, and let algebra produce the coefficients.
A filter with zeros q1, q2, ... and poles p1, p2, ... (complex numbers) has
H(z) = k * (1 - q1 * z^-1) * (1 - q2 * z^-1) * ... / ((1 - p1 * z^-1) * (1 - p2 * z^-1) * ...)
Each factor (1 - r * z^-1) is the coefficient list [1, -r], so b and a are products of such lists (multiply them as in "Two Echo Units"). A zero on the unit circle at angle w (q = exp(1j * w)) removes the frequency f = w * fs / (2 * pi) completely; a pole just inside the circle at the same angle makes the notch narrow. Real filters have real coefficients, so every non-real zero or pole comes with its complex conjugate (r.conjugate(), the mirror image below the real axis); multiplying the pair gives a real quadratic, so after the products, keep only the real parts (.real).
Choose the factor k so that the filter passes a constant unchanged, H(1) = 1: compute b and a with k = 1, then multiply every b coefficient by A(1) / B(1) (sums of the lists, as in "Bass and Treble at a Glance"). If abs(B(1)) < 1e-9 (a zero at z = 1: the filter blocks constants by design) leave k = 1.
Write design(zeros, poles) that returns the pair (b, a) of lists of real numbers, with len(zeros) + 1 and len(poles) + 1 entries.
Examples
Input: zeros = [1j, -1j], poles = [0.9j, -0.9j]
Output: ([0.905, 0.0, 0.905], [1.0, 0.0, 0.81])
Explanation: (1 - 1j z^-1)(1 + 1j z^-1) = 1 + z^-2 and (1 - 0.9j z^-1)(1 + 0.9j z^-1) = 1 + 0.81 z^-2.
B(1) = 2, A(1) = 1.81, so b is scaled by 0.905. This notch removes fs / 4 (the zeros at angle pi / 2).
Input: zeros = [1], poles = [0.95]
Output: ([1.0, -1.0], [1.0, -0.95])
Explanation: a zero at z = 1 removes any constant offset (a "DC blocker"), so k stays 1.
Input: zeros = [-1], poles = [0.5]
Output: ([0.25, 0.25], [1.0, -0.5])
Constraints
- at most 12 zeros and 12 poles; non-real ones come in conjugate pairs; no pole at
z = 1 - answers are compared with a tolerance of
1e-6; a list may be given as a tuple
Goals
- Build the coefficient lists of a filter from its zeros and poles
- See that conjugate pairs of roots give real coefficients
- Scale the numerator so the filter passes a constant unchanged