FAILURE MAP
← Case archive

FA-91721 / Digital signal filters / Open access

Goertzel power adds the cross term · case 01

Bin powers are wildly high for bins whose states have the same sign.

Verified by executionVariant 1 · 7 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The final power uses + c s1 s2.

VERIFIED REPAIR

Use s1^2 + s2^2 - c s1 s2.

Unsuccessful approach: The attempted repair uses 2 cos(w/2) as the cross-term coefficient.

Case contract

Input [N, k, samples]; samples must have length N ("length-mismatch"). Goertzel recursion s = x + c s1 - s2 with c = 2 cos(2 pi k / N) from zero state; power = s1^2 + s2^2 - c s1 s2, rounded to 6 decimals (equals |X[k]|^2 of the DFT).

Why this case matters

Goertzel filters detect single tones cheaply (DTMF, pilot tones); recursion or coefficient slips misreport bin energy.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    N, k, xs = x
    if len(xs) != N:
        return 'length-mismatch'
    w = 2 * math.pi * k / N
    coeff = 2 * math.cos(w)
    s1 = s2 = 0.0
    for v in xs:
        s = v + coeff * s1 - s2
        s2 = s1
        s1 = s
    power = s1 * s1 + s2 * s2 + coeff * s1 * s2
    return round(power, 6) + 0.0
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: dc bin', [4, 0, [1, 1, 1, 1]], 16.0], ['regression: nyquist bin', [4, 2, [1, -1, 1, -1]], 16.0], ['regression: random goertzel 0', [5, 3, [1.783, -0.103, 1.735, 0.172, 0.47]], 7.631659], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 1', [16, 7, [1.67, -0.361, -1.179, -1.404, -0.191, 0.823, -1.353, 1.734, 0.486, 0.989, 1.205, -0.229, 1.256, -0.62, 1.422, -1.514]], 81.860198], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049]], [['regression: random goertzel 1', [16, 7, [1.67, -0.361, -1.179, -1.404, -0.191, 0.823, -1.353, 1.734, 0.486, 0.989, 1.205, -0.229, 1.256, -0.62, 1.422, -1.514]], 81.860198], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 0', [5, 3, [1.783, -0.103, 1.735, 0.172, 0.47]], 7.631659], ['regression: random goertzel 4', [5, 1, [0.277, 1.508, 1.644, 1.371, 0.902]], 2.552062]], [['regression: random goertzel 4', [5, 1, [0.277, 1.508, 1.644, 1.371, 0.902]], 2.552062], ['regression: random goertzel 6', [16, 3, [-0.301, 1.167, -1.088, 1.455, 1.212, 0.703, -1.679, -0.745, -1.885, -0.092, 1.441, 0.517, 1.787, 1.007, 1.333, 0.125]], 11.236712], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 1', [16, 7, [1.67, -0.361, -1.179, -1.404, -0.191, 0.823, -1.353, 1.734, 0.486, 0.989, 1.205, -0.229, 1.256, -0.62, 1.422, -1.514]], 81.860198], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049]], [['regression: random goertzel 12', [8, 5, [1.095, -0.847, 1.178, 0.214, 0.875, -0.711, 1.404, 0.206]], 0.121928], ['regression: random goertzel 14', [8, 7, [-1.442, 0.598, 0.015, 1.269, 0.654, 1.166, 1.867, 1.215]], 11.338617], ['repair check: random goertzel 5', [16, 12, [0.484, 1.263, -0.839, 0.847, 1.38, 0.512, -1.644, -1.061, -1.041, 1.886, 1.201, 1.603, -1.82, -1.609, -1.768, -0.588]], 5.77981], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002]], [['regression: random goertzel 16', [4, 2, [0.184, -0.187, 1.09, -0.658]], 4.490161], ['regression: random goertzel 17', [16, 3, [0.915, -1.946, -1.09, 0.373, 0.39, -1.357, -0.289, -0.785, -0.369, 1.592, 1.602, -0.177, 0.892, -1.412, 0.72, -0.583]], 34.209051], ['repair check: random goertzel 7', [16, 12, [0.634, -0.987, 0.86, -1.732, -1.556, 1.652, 1.567, 1.68, 0.143, -1.093, 0.974, -0.392, 0.657, -0.89, -0.677, -0.216]], 8.53268], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002], ['regression: random goertzel 4', [5, 1, [0.277, 1.508, 1.644, 1.371, 0.902]], 2.552062]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: dc bin256.016.0Failed
regression: nyquist bin256.016.0Failed
regression: random goertzel 053.6564587.631659Failed
control: tone on bin 116.00000516.000005Passed
control: length mismatchlength-mismatchlength-mismatchPassed
regression: random goertzel 1547.26989981.860198Failed
regression: random goertzel 2270.76702514.115049Failed

SHA-256 / b332ae8a2ef09eff7d1a3c36c0e506df785c7e5f53ff953b89da129efd00aa9d

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    N, k, xs = x
    if len(xs) != N:
        return 'length-mismatch'
    w = 2 * math.pi * k / N
    coeff = 2 * math.cos(w)
    s1 = s2 = 0.0
    for v in xs:
        s = v + coeff * s1 - s2
        s2 = s1
        s1 = s
    power = s1 * s1 + s2 * s2 - 2 * math.cos(w / 2) * s1 * s2
    return round(power, 6) + 0.0
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: dc bin', [4, 0, [1, 1, 1, 1]], 16.0], ['regression: nyquist bin', [4, 2, [1, -1, 1, -1]], 16.0], ['regression: random goertzel 0', [5, 3, [1.783, -0.103, 1.735, 0.172, 0.47]], 7.631659], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 1', [16, 7, [1.67, -0.361, -1.179, -1.404, -0.191, 0.823, -1.353, 1.734, 0.486, 0.989, 1.205, -0.229, 1.256, -0.62, 1.422, -1.514]], 81.860198], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049]], [['regression: random goertzel 1', [16, 7, [1.67, -0.361, -1.179, -1.404, -0.191, 0.823, -1.353, 1.734, 0.486, 0.989, 1.205, -0.229, 1.256, -0.62, 1.422, -1.514]], 81.860198], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 0', [5, 3, [1.783, -0.103, 1.735, 0.172, 0.47]], 7.631659], ['regression: random goertzel 4', [5, 1, [0.277, 1.508, 1.644, 1.371, 0.902]], 2.552062]], [['regression: random goertzel 4', [5, 1, [0.277, 1.508, 1.644, 1.371, 0.902]], 2.552062], ['regression: random goertzel 6', [16, 3, [-0.301, 1.167, -1.088, 1.455, 1.212, 0.703, -1.679, -0.745, -1.885, -0.092, 1.441, 0.517, 1.787, 1.007, 1.333, 0.125]], 11.236712], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 1', [16, 7, [1.67, -0.361, -1.179, -1.404, -0.191, 0.823, -1.353, 1.734, 0.486, 0.989, 1.205, -0.229, 1.256, -0.62, 1.422, -1.514]], 81.860198], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049]], [['regression: random goertzel 12', [8, 5, [1.095, -0.847, 1.178, 0.214, 0.875, -0.711, 1.404, 0.206]], 0.121928], ['regression: random goertzel 14', [8, 7, [-1.442, 0.598, 0.015, 1.269, 0.654, 1.166, 1.867, 1.215]], 11.338617], ['repair check: random goertzel 5', [16, 12, [0.484, 1.263, -0.839, 0.847, 1.38, 0.512, -1.644, -1.061, -1.041, 1.886, 1.201, 1.603, -1.82, -1.609, -1.768, -0.588]], 5.77981], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002]], [['regression: random goertzel 16', [4, 2, [0.184, -0.187, 1.09, -0.658]], 4.490161], ['regression: random goertzel 17', [16, 3, [0.915, -1.946, -1.09, 0.373, 0.39, -1.357, -0.289, -0.785, -0.369, 1.592, 1.602, -0.177, 0.892, -1.412, 0.72, -0.583]], 34.209051], ['repair check: random goertzel 7', [16, 12, [0.634, -0.987, 0.86, -1.732, -1.556, 1.652, 1.567, 1.68, 0.143, -1.093, 0.974, -0.392, 0.657, -0.89, -0.677, -0.216]], 8.53268], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002], ['regression: random goertzel 4', [5, 1, [0.277, 1.508, 1.644, 1.371, 0.902]], 2.552062]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: dc bin16.016.0Passed
regression: nyquist bin136.016.0Failed
regression: random goertzel 021.8541047.631659Failed
control: tone on bin 116.00000516.000005Passed
control: length mismatchlength-mismatchlength-mismatchPassed
regression: random goertzel 1363.70399281.860198Failed
regression: random goertzel 2142.44103714.115049Failed

SHA-256 / 4880b97766f6d7e2a848b39033c1dabd3ba522f87175c50bc3c6f9cbc0980094

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    N, k, xs = x
    if len(xs) != N:
        return 'length-mismatch'
    w = 2 * math.pi * k / N
    coeff = 2 * math.cos(w)
    s1 = s2 = 0.0
    for v in xs:
        s = v + coeff * s1 - s2
        s2 = s1
        s1 = s
    power = s1 * s1 + s2 * s2 - coeff * s1 * s2
    return round(power, 6) + 0.0
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: dc bin', [4, 0, [1, 1, 1, 1]], 16.0], ['regression: nyquist bin', [4, 2, [1, -1, 1, -1]], 16.0], ['regression: random goertzel 0', [5, 3, [1.783, -0.103, 1.735, 0.172, 0.47]], 7.631659], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 1', [16, 7, [1.67, -0.361, -1.179, -1.404, -0.191, 0.823, -1.353, 1.734, 0.486, 0.989, 1.205, -0.229, 1.256, -0.62, 1.422, -1.514]], 81.860198], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049]], [['regression: random goertzel 1', [16, 7, [1.67, -0.361, -1.179, -1.404, -0.191, 0.823, -1.353, 1.734, 0.486, 0.989, 1.205, -0.229, 1.256, -0.62, 1.422, -1.514]], 81.860198], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 0', [5, 3, [1.783, -0.103, 1.735, 0.172, 0.47]], 7.631659], ['regression: random goertzel 4', [5, 1, [0.277, 1.508, 1.644, 1.371, 0.902]], 2.552062]], [['regression: random goertzel 4', [5, 1, [0.277, 1.508, 1.644, 1.371, 0.902]], 2.552062], ['regression: random goertzel 6', [16, 3, [-0.301, 1.167, -1.088, 1.455, 1.212, 0.703, -1.679, -0.745, -1.885, -0.092, 1.441, 0.517, 1.787, 1.007, 1.333, 0.125]], 11.236712], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 1', [16, 7, [1.67, -0.361, -1.179, -1.404, -0.191, 0.823, -1.353, 1.734, 0.486, 0.989, 1.205, -0.229, 1.256, -0.62, 1.422, -1.514]], 81.860198], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049]], [['regression: random goertzel 12', [8, 5, [1.095, -0.847, 1.178, 0.214, 0.875, -0.711, 1.404, 0.206]], 0.121928], ['regression: random goertzel 14', [8, 7, [-1.442, 0.598, 0.015, 1.269, 0.654, 1.166, 1.867, 1.215]], 11.338617], ['repair check: random goertzel 5', [16, 12, [0.484, 1.263, -0.839, 0.847, 1.38, 0.512, -1.644, -1.061, -1.041, 1.886, 1.201, 1.603, -1.82, -1.609, -1.768, -0.588]], 5.77981], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 2', [4, 2, [1.006, -1.378, 0.575, -0.798]], 14.115049], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002]], [['regression: random goertzel 16', [4, 2, [0.184, -0.187, 1.09, -0.658]], 4.490161], ['regression: random goertzel 17', [16, 3, [0.915, -1.946, -1.09, 0.373, 0.39, -1.357, -0.289, -0.785, -0.369, 1.592, 1.602, -0.177, 0.892, -1.412, 0.72, -0.583]], 34.209051], ['repair check: random goertzel 7', [16, 12, [0.634, -0.987, 0.86, -1.732, -1.556, 1.652, 1.567, 1.68, 0.143, -1.093, 0.974, -0.392, 0.657, -0.89, -0.677, -0.216]], 8.53268], ['control: tone on bin 1', [8, 1, [1, 0.707107, 0, -0.707107, -1, -0.707107, 0, 0.707107]], 16.000005], ['control: length mismatch', [4, 1, [1, 2, 3]], 'length-mismatch'], ['regression: random goertzel 3', [5, 2, [0.006, 0.503, -1.021, -1.836, 1.378]], 7.416002], ['regression: random goertzel 4', [5, 1, [0.277, 1.508, 1.644, 1.371, 0.902]], 2.552062]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: dc bin16.016.0Passed
regression: nyquist bin16.016.0Passed
regression: random goertzel 07.6316597.631659Passed
control: tone on bin 116.00000516.000005Passed
control: length mismatchlength-mismatchlength-mismatchPassed
regression: random goertzel 181.86019881.860198Passed
regression: random goertzel 214.11504914.115049Passed

SHA-256 / d822e21b51bca75ef5cd49274ea870da0244a859936ac67c8f4d7c88204e5e65

Verification & scope

A deterministic bounded teaching model with a stipulated toy contract; exact rational arithmetic or fixed-decimal rounding keeps outputs strict JSON. It is not a production DSP library and claims no standards conformance. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:51:38.762140+00:00.

Case digest / 020b1338f03f3ff7b90a32a1eb2e14ed68494a8519c68686c59884747be58c26