I recently implemented a digital state filter based on the recommendation here. I've tested this filter's performance as a very low cutoff low-pass filter with limited coefficient quantization precision and it's working entirely as expected. However, I've only tested it in the time domain. I'd additionally like to characterize it in terms of its frequency response. I've used Mason's gain formula to arrive at the following transfer function:
$ H(z) = \frac{f^2z^{-1}}{1-z^{-1}(2-qf-f^2)+z^{-2}(1-qf)}, $
where $f=2\sin(\pi f_c/f_s)$ and $q=1/Q$ are chosen constants (see this link). According to that link, $f_c=10\,\text{Hz}$ is the cutoff frequency, $f_s=500\,\text{kHz}$ is the sampling rate, and $Q=1/\sqrt{2}$. Here's a block diagram of the filter (taken from Musical Applications of Microprocessors) for reference:
However, when I plot the response ($H(e^{j\omega})$) it doesn't quite look as I expect. Here's the Python code for plotting:
import numpy as np
import matplotlib.pyplot as plt
fc = 1e1
fsample = 500e3
fnyquist = fsample / 2
q = 1 / np.sqrt(2)
Fc = 2 * np.sin(np.pi * fc / fsample)
Q = 1 / q
def tf(f):
w = 2 * np.pi * f
z = np.exp(-1 * 1j * w)
return (
Fc ** 2
* z
/ (1 - z * (2 - Q * Fc - Fc ** 2) + z ** 2 * (1 - Q * Fc))
)
freq = np.logspace(-10, np.log10(fnyquist), int(1e5))
resp = [20 * np.log10(abs(tf(f))) for f in freq]
_, ax = plt.subplots()
ax.plot(freq, resp)
ax.grid(b=True, which="major")
ax.set_ylim(-120, 10)
ax.set_xscale("log")
plt.show()
Here's the plotted frequency response
The shape is as I would have expected (low-pass and low q-value with 12dB/oct. rolloff). However, the cutoff frequency, which is roughly $2\times 10^{-5}\,\text{Hz}$ is much lower than the $10\,\text{Hz}$ I set. Additionally, I'm somewhat perturbed by the spikes in the frequency response, which I didn't expect. Have I set up this filter incorrectly, or calculated the frequency response incorrectly? This is my first time using Mason's gain formula, so it's possible I've done that incorrectly. Why do I not see a gain of $-3\,\text{dB}$ at $10\,\text{Hz}$? How can I achieve the correct cutoff frequency? Are those "spikes" a cause for concern? Why are they present and how can I remove them?