Short background: I want to implement a lowpass butterworth filter in C/C++. The end goal is to use this in a low-latency Python program, for which of course scipy.signal
exists, but I want to be able to change the cutoff frequency (smoothly) at each time step.
Where I am right now: I found a really helpful article on the discretization of a butterworth filter here. It ends with a helpful section on Second Order Sections
, where they include the following formula:
And then, to get an n-th order filter, we do $$H_n(s') = \prod_{k=0}^{n/2-1}H_{n,k}(s').$$
So I figured I can just translate the H_s,k
into discrete time:
y_k[i] + 2 y_k[i-1] + y_k[i-2] = b0_k x_k[i] + b1_k x_k[i-1] + b2_k x_k[i-2], (Eq. 1)
where b0_k = gamma^2 - alpha_k gamma + 1,
b1_k = 2 - 2\gamma^2
b2_k = gamma^2 + alpha_k gamma + 1,
gamma, alpha_k as in the above post.
Now comes the tricky part. I actually am a DSP noob, and I have no real clue whether it makes sense to iteratively apply the above. However, I did just that, implementing:
... bunch of setup code omitted ...
for (size_t i = 0; i < num_samples; i++) {
const float x = input_signal[i];
const float cutoff_i = cutoffs[i];
const float gamma = 1/(std::tan(pi*cutoff_i/sampling_rate));
// We set xk[t] = x[t] for i = {t, t-1, t-2}.
xk0 = x0; // x0 is input_signal[i], xk0 is x_k[i].
xk1 = x1; // x1 is input_signal[i-1], xk1 is x_k[i-1].
xk2 = x2; // ...
// Iterate `k` from 0 to `n/2-1`.
for (size_t k=0; k <= (n/2-1); k++){
const float alpha_k = 2 * std::cos(2*pi * (2.*k + n + 1)/(4.*n));
const float b0k = gamma * gamma - alpha_k * gamma + 1.;
const float b1k = 2. - 2. * gamma*gamma;
const float b2k = gamma * gamma + alpha_k * gamma + 1.;
// y0[k] is y_k[i],
// y1[k] is y_k[i-1],
// y2[k] is y_k[i-2],
// Here we calculate y_k[i] according to Eq 1.
y0[k] = -2.*y1[k] - y2[k] + b0k * xk0 + b1k * xk1 + b2k * xk2;
// Shift xk.
xk0 = y0[k]; xk1 = y1[k]; xk2 = y2[k];
// Shift ys.
y2[k] = y1[k]; y1[k] = y0[k];
}
x2 = x1; x1 = x0;
output[i] = y0[n/2-1];
Sadly, this does not work at all. Outputs blow up to -infinity
within a few time steps.
I see a few potential problems: a) The formula for H_2,k above is wrong b) My assumption that I can just go through the sections is wrong c) My code is wrong