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.
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: dc bin | 256.0 | 16.0 | Failed |
| regression: nyquist bin | 256.0 | 16.0 | Failed |
| regression: random goertzel 0 | 53.656458 | 7.631659 | Failed |
| control: tone on bin 1 | 16.000005 | 16.000005 | Passed |
| control: length mismatch | length-mismatch | length-mismatch | Passed |
| regression: random goertzel 1 | 547.269899 | 81.860198 | Failed |
| regression: random goertzel 2 | 270.767025 | 14.115049 | Failed |
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: dc bin | 16.0 | 16.0 | Passed |
| regression: nyquist bin | 136.0 | 16.0 | Failed |
| regression: random goertzel 0 | 21.854104 | 7.631659 | Failed |
| control: tone on bin 1 | 16.000005 | 16.000005 | Passed |
| control: length mismatch | length-mismatch | length-mismatch | Passed |
| regression: random goertzel 1 | 363.703992 | 81.860198 | Failed |
| regression: random goertzel 2 | 142.441037 | 14.115049 | Failed |
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: dc bin | 16.0 | 16.0 | Passed |
| regression: nyquist bin | 16.0 | 16.0 | Passed |
| regression: random goertzel 0 | 7.631659 | 7.631659 | Passed |
| control: tone on bin 1 | 16.000005 | 16.000005 | Passed |
| control: length mismatch | length-mismatch | length-mismatch | Passed |
| regression: random goertzel 1 | 81.860198 | 81.860198 | Passed |
| regression: random goertzel 2 | 14.115049 | 14.115049 | Passed |
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