Fourteen audits argued about where the samples come from. This one settled it. HRC6000SetFMRx writes 0xE0 = 0x89 under the comment “Turn off Microphone input”; the monitor writes 0xC9, which is that value with the microphone bit added back. Then it found the stock driver reading the identical expression the monitor reads — i2s_Rx_Buffer[bufNum][j][i*2] — commented “only use the Left Channel of the Mic Audio”. Nobody else found either line.
It also wrote an emulator profiler and measured the thing every other audit missed: the IMBE vocoder at 9.44M instructions per frame, 655% of a 72 MHz core. Its profiler reruns here bit-for-bit. And it explains the symptom nobody had connected — one frame stalls the monitor tick for 55–190 ms, the capture ring holds 21 ms, and an overrun resets the parser, so even with a perfect input the call dies after the first voice frame of every LDU. All four decode-critical issues, a first. What it never opens is the instrument layer: no capture defect, no starvation counter, no squelch conflict.
Claims that check out
20 / 21
1 overstated · 0 wrong
Decode-critical issues found
4 of 4
the only audit of the sixteen
The vocoder, measured
655%
of a 72 MHz core; its profiler reruns exactly
Ranking
Unranked
different harness, no step cap
ConditionsClean room: a fresh copy of the repository (46eebda with its uncommitted changes, its own docs and the HR-C6000 manual) with no earlier reviews, reviewed by one model with no network tools, no skills, no plugins, no MCP servers and no subagents.
The run, not graded25 min · 120 agent steps · 54 tool calls · 185K output tokens · Claude Code CLI, subscription · one-shot
Read the full review audit
Every claim, every rerun and the modelling behind this grade, as published.
Weights reflect the question asked: find what stops P25 decoding and say how to fix it. Accuracy carries the most weight because a wrong review does harm; coverage and root cause together outweigh it because an accurate review of the wrong things does not help.
Where the points went
The 100-point grade split into the rubric’s weighted slots: filled is earned, empty is lost, with the other models on this task for scale.
1Accuracy2Coverage3Root cause4Fix plan56
Claude Opus 5
92/ 100
GPT-6 Astra
89/ 100
GPT-5.6 Sol
84/ 100
Claude Fable 5.1
79/ 100
HY4 Preview
77/ 100
GPT-5.6 Luna
75/ 100
UNIONALPHA
74/ 100
Grok 4.6
70/ 100
Muse Spark 1.3 Contributor
68/ 100
DeepSeek V4.1 Flash
66/ 100
GLM 5.3
65/ 100
Gemini 3.8 Flash
64/ 100
Qwen3.8 Max
63/ 100
Qwen3.8 Flash
59/ 100
GLM 5.3 Flash
56/ 100
DeepSeek V4 Pro
54/ 100
1Accuracy & evidence30 pts
2Coverage of decode problems25 pts
3Root cause & prioritisation15 pts
4Fix plan & acceptance gates15 pts
5Originality & attribution10 pts
6Clarity & calibration5 pts
Accuracy & evidence30% of the grade
9428.2 pts
20 of 21 claims hold and none is wrong. Both experiments reproduce bit-for-bit, and three citations nobody else found are exact to the line. One judgement is overstated: it calls the test oracle’s shared filter harmless when its own numbers show a 2× symbol-error penalty.
Coverage of decode problems25% of the grade
8621.5 pts
All four decode-critical issues — the first audit to manage it — plus the MFID muting, the memory ceiling with a concrete overlay, and the chip’s one-layer mode developed into a route. It finds no instrument defect at all: not the capture export, the starvation counter, the squelch conflict or the health gate.
Root cause & prioritisation15% of the grade
9614.4 pts
Three independent blockers, each stated as sufficient on its own, and the only audit to join the CPU cost to the observed field symptom through the ring overrun and the parser reset. It also says plainly which earlier work was correct but aimed at the wrong signal.
Fix plan & acceptance gates15% of the grade
9314.0 pts
A ten-minute bench test before anything else, then two routes: the chip’s own demodulator with a falsifiable acceptance test, or the hardware tap. The RAM overlay names the three buffers to reuse. It does not fix the instruments its own bench test would run through.
Originality & attribution10% of the grade
959.5 pts
The microphone proof, the vocoder measurement, the filter-corner sweep, the one-layer route and the memory overlay are five things no other audit produced, and two of them are runnable programs left in the tree.
Clarity & calibration5% of the grade
924.6 pts
Ranked findings with an explicit “verified correct, no action needed” list, tables of measured numbers, and a closing statement of what the experiments do not establish. The register-flow diagram at the top is the clearest orientation any of the sixteen gave.
Weighted total 92, grade A. Grade scale: A ≥ 90 · B 80–89 · C+ 75–79 · C 70–74 · C− 65–69 · D 50–64 · F < 50
Headline findings
Six things to know about this review
Validated
It proves the microphone instead of inferring it
Every audit before this one argued from absence: the manual documents no route from the discriminator to I2S, so the samples are probably the microphone. This one found the positive evidence twice over. HRC6000SetFMRx writes 0xE0 = 0x89 with the driver’s own comment, “Turn off Microphone input”. The monitor writes 0xC9 — that same value with bit 0x40 set, the bit the driver calls “Mic Input to Line in 1”. And the stock code reads the very expression the monitor reads, i2s_Rx_Buffer[bufNum][j][i*2], commented “only use the Left Channel of the Mic Audio”.
It measured the vocoder, and the measurement reruns
It wrote a Unicorn profiler, ran the demo firmware’s self-test inside it and counted instructions per call: mbe_processImbe7200x4400Frame at a mean of 9.44M instructions per frame, max 13.8M — 655% of what a 72 MHz core can issue at 50 frames per second, worst frame 955%. The demodulator, by contrast, is 39%. I reran its profiler in a clean copy of the repository and got the same numbers to the instruction. Sixteen audits have now looked at this firmware; this is the first to put a number on the vocoder.
analysis/review/emu_profile.py · mbelib.c:306
New
It connects the CPU cost to the symptom on the radio
The number alone would be an optimisation note. What makes it a decode blocker is the chain it traces: mbelib runs inline inside p25monTick(), one frame stalls the tick for 55–190 ms, the capture ring holds 512 samples — 21 ms at 24 kHz — so it overruns, and the next pass sees the overrun counter move and calls p25rx_reset_stream(). So the parser returns to hunt mode after the first IMBE frame of every LDU: link control never completes, late entry never unmutes, and the call is dropped. Even with a perfect input.
p25mon.c:93, 578 · p25rx.c:105–123
Validated
It swept the filter corner rather than asserting the direction
Through the unmodified firmware demodulator, on standards-shaped C4FM: 0% symbol errors on a flat discriminator, 0.14% with a 20 Hz high-pass, 5.6% at 50 Hz, 17.5% at 100 Hz, 42.5% at 300 Hz — the typical CTCSS-reject corner — and 48.9% for de-emphasis plus a 3 kHz low-pass. Rebuilt and rerun here; every row reproduces. It then names the fix as the firmware’s own DMR profile, where the “magic register which allows low freq audio” is set, and adds the point the numbers imply: the demodulator fits DC once per window, so it cannot follow baseline wander.
Its first step is a bench capture using the c command and tools/p25_capture.py — and it never checks either. The export stages the contiguous first 80 halfwords of each row while the decoder reads [j][i*2], so 158 of 160 positions differ; the parser certifies captures with missing chunks; pcm_starve is printed but never incremented. Nor does it find the stock squelch rewriting the codec mid-session, the ±10% health gate against a ±1% timing clamp, or the unknown-talkgroup audio bypass. Four other audits found those. This one measured the physics and skipped the plumbing.
On the circular test oracle it did what nobody else did — it tested whether the shared filter matters, generating true C4FM shaping and running it through the production receiver. Its conclusion: the mismatch is harmless and the matched filter needs no change. The second half holds. The first understates its own table: at σ = 500 Hz the true-C4FM waveform gives 0.064% symbol errors against 0.034% for the self-shaped one. That is a 2× penalty, small in absolute terms and real.
analysis/review/results_c4fm.txt
Claim check
21 claims, checked one by one
20 hold, 1 overstated, 0 wrong. Each claim was checked at the lines it cites.
The audits track 4 issues that stop this radio decoding P25. This review: the first audit to find all four. How the review treated each, from the provenance table: