Math errors - o3 to the rescue

May 31, 2025

How o3 saved me hours of debugging with a math problem

Something’s afoot

So I wanted to implement a frequency detector - easy enough, for a single frequency in a signal I chose the Goertzel algorithm. A simple resonant IIR filter which outputs a complex number which is equal to a Discrete Fourier Transform bin. That’s what I came up with:

samplerate = 1000
N = 1000 # 1 second
f = 50

s = np.cos(2 * np.pi * f * np.arange(0, N)/samplerate) + \
    np.cos(2 * np.pi * f*2 * np.arange(0, N)/samplerate)

def goertzel(x, f, samplerate):
    w = 2 * np.pi * f/ samplerate
    c = 2 * np.cos(w)

    # Second order resonant IIR filter
    s = [0,0,0]
    for n in range(len(x)):
        s[0] = x[n] + c * s[1] - s[2]
        s[2] = s[1]
        s[1] = s[0]

    # Use internal filter state to calculate DFT bin
    real = s[1] - s[2] * np.cos(w)
    imag = s[2] * np.sin(w)
    return (real + 1j * imag) / (N/2)

result = goertzel(s, f, samplerate)
np.abs(result), np.angle(result)
> 1.0000000000000004, -36.00000000000132

Magnitude is \(1\), it works! But wait, the phase angle is, uh, -36 degrees? That can’t be right. It should be close to \(0\), because I’m measuring a cosine.

o3 to the rescue

o4 is my goto because of the speed. It handles the usual small queries quite well.

I think by now I can tell if o4 doesn’t know what it’s talking about. It meanders around in the beginning of the response, repeating everything about the problem, and then taking a turn into random-guess land. By contrast when it knows the issue, it loses less time chit-chatting and goes straight at it. But telling it to cut out the bs does not help.

So, this time I switched to o3, and after about 4 minutes, it came back to me with a result, which was spot on: The calculation of the DFT bin coefficients that depend on the internal IIR filter state were switched:

real = s[1] - s[2] * np.cos(w)
imag = s[2] * np.sin(w)

should be

real = s[1] * np.cos(w) - s[2]
imag = s[1] * np.sin(w)

The fun thing about this, this was not clear to o3 at all - it took some time to debug this. It did:

It closes in on the error step by step, until it realizes there’s a index shift error. Those 4 minutes could’ve been one hour of debugging.

Math errors - o3 to the rescue - May 31, 2025 - Arne Elster