# Why Does This Sliding Window Goertzel Filter Trick Work?

I'm reading an analog signal at a constant interval on an embedded device, and I want to extract a specific frequency component. Since the signal is continuously being read I want to constantly scan the most recent $$N$$ samples for this frequency component. For efficiency sake preferably without recomputing the FFT every time a new sample is added, since $$N-1$$ measurements overlap.

As I only care about one specific frequency component, the Goertzel filter is the natural choice. I implemented this naively, but since the derivation only works with exactly $$N$$ samples I have to reapply it over the entire window each time a new sample is added.

On a whim, I decided to ask ChatGPT to try to convert it to use a sliding window. It suggested replacing the first stage filter (see the Wikipedia page for notation) of $$s[n]=x[n]+2\cos(\omega_0)s[n-1]-s[n-2]$$ with $$s[n]=x[n]-x[n-N]+2\cos(\omega_0)s[n-1]-s[n-2]$$ where $$x[n]$$ is now being continuously added to instead of length exactly $$N$$.

Much to my surprise, this extremely basic trick worked perfectly, and I now have a functional sliding window Goertzel filter that works at least for my application. However when asked to explain it ChatGPT gives a fairly cop-out answer (it "reverses" the effect of the oldest sample, ignoring its compounding effect on the $$s[n-1]$$ and $$s[n-2]$$ terms that make the filter IIR), and I can't see a theoretical reason this should work. I've also tried looking for textbook references although none of them contain this exact trick. If the C++ code would be helpful I can post it, although the only difference from Goertzel filter definition is the first stage filter described above. Can someone please explain the theoretical reason that this trick works?

• You should also ask why it won't work for arbitrarily large $n$ (hint: because the Goertzel filter is metastable, and subject to unbounded error when fed numerical noise), and alternatives to a Goertzel for continuous band-pass filtering. Commented Jun 20 at 2:53

One way to conceptually think about it is that it is computing the difference between two first stage filters, one of which is delayed by $$N$$ samples. By doing so, it isolates the change caused by the last $$N$$ samples.
There are probably multiple ways you could mathematically arrive at the same result. Here is a z-transform approach: $$S[z] = \frac{X[z]}{1-2\cos[\omega_0]z^{-1}+z^{-2}} - \frac{z^{-N}X[z]}{1-2\cos[\omega_0]z^{-1}+z^{-2}}=\frac{\left(1-z^{-N}\right)X[z]}{1-2\cos[\omega_0]z^{-1}+z^{-2}}$$ Here is a difference equation approach, where the first expression $$s_0$$ is the first stage filter, and $$s_N$$ is the first stage filter whose output lags by $$N$$ samples: $$s_0[n]=x[n] + 2\cos[\omega_0]s_0[n-1]-s_0[n-2]$$ $$s_N[n]=x[n-N] + 2\cos[\omega_0]s_N[n-1]-s_N[n-2]$$ Taking the difference: $$s[n]=s_0[n]-s_N[n]\\=x[n] - x[n-N] + 2\cos[\omega_0]\left(s_0[n-1]-s_N[n-1]\right)-\left(s_0[n-2]-s_N[n-2]\right)\\=x[n]-x[n-N]+2\cos[\omega_0]s[n-1]-s[n-2]$$ To make it explicit, in the final expression I have used: $$s[n]=s_0[n]-s_N[n]$$ So: $$s[n-1]=s_0[n-1]-s_N[n-1]$$ $$s[n-2]=s_0[n-2]-s_N[n-2]$$