On the question that matters it is exhaustive. It does not just show that the monitor enables the microphone bit; it shows that soundReceiveRefillData is the only stock consumer of that buffer, that the DMR transmit path is what arms it, and that DMR receive audio never touches I2S at all. Then it reads the map at object granularity and names the three buffers a P25 build could reclaim — uiNotification.o at 38,400 bytes, and two smaller ones — all three exact.
It also measured the vocoder, wrapping libm to count 11,812 cosf calls per frame, which is close to what the grading emulator counts. Then it priced them at 100–200 cycles each and concluded the synthesis needs “60–120% of a 72 MHz core”. With newlib’s range reduction the true cost is nearer a thousand cycles a call: 655% on the same build another audit profiled, and 1,100–1,600% on the live one. The finding is right and the margin is out by about six times — which is what makes its “168 MHz and a cheaper oscillator” plan look sufficient when it is not.
Claims that check out
15 / 20
4 overstated or unverified · 1 wrong
Decode-critical issues found
2 of 4
plus a vocoder it measured and under-costed
Trig calls per frame
11,812
counted correctly, priced at a sixth
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 graded12 min · 71 agent steps · 44 tool calls · 249K 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
8525.5 pts
15 of 20 claims hold, six of them validated by rerunning what it ran. Its register, map and test citations are exact to the byte. One is wrong — the cycle cost of the trig it counted — and three are overstated, including a denial of the chip mode the other run planned around.
Coverage of decode problems25% of the grade
6215.5 pts
Both hardware causes, the two SPI writes per voice frame, the memory ceiling with named objects, and three demodulator defects nobody else reached. It never opens the capture export, the starvation counter, the squelch path, the health gate or the identity gating.
Root cause & prioritisation15% of the grade
8412.6 pts
It separates “architectural, not bugs” cleanly and draws the right conclusion for the bring-up work: the clock machinery solved a real problem on the wrong signal. Its CPU mis-pricing is what keeps this from being the strongest causal account.
Fix plan & acceptance gates15% of the grade
8612.9 pts
Six steps from a fifteen-minute source test to a validation ladder that starts by injecting known baseband from a sound card — the most practical first bench step any audit proposed. The register and profile changes are specific and correct.
Originality & attribution10% of the grade
848.4 pts
The provenance chain for the I2S buffer, object-level memory reclamation, the libm call-counting harness, the in-frame resync defect and the exact control-channel opcode that carries the Phase 2 descrambler seed are all its own.
Clarity & calibration5% of the grade
884.4 pts
A verified-checks table before any argument, evidence blocks under each claim, and a Phase 2 table that states component, status and source file. It is the easiest of the sixteen to audit, which is how its one wrong number was found.
Weighted total 79, grade C+. 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
The most complete case on the board for the microphone
Other audits show that the monitor sets the microphone bit. This one shows what that buffer is for: soundReceiveRefillData is the only stock consumer of i2s_Rx_Buffer, it is armed by soundReceiveData() from the DMR transmit path, and its comment reads “only use the Left Channel of the Mic Audio”. DMR receive audio never touches I2S receive at all — the C6000 hands AMBE frames over SPI and PCM goes out over I2S TX. Then the registers: HRC6000SetMic() toggles bit 0x40 of 0xE0, and HRC6000SetFMRx() writes 0xE0 = 0x89, “Turn off Microphone input”. The monitor writes 0xC9.
Every audit that reached the memory ceiling reported the same totals. This one goes further and names what to reclaim: uiNotification.o holds 38,400 bytes of CCM, SPI_Flash.o 4,096 and voicePrompts.o 3,788. I checked all three against the map — 0x9600, 0x1000, 0xecc, exact. It then sizes what a new design would need against that: an ADC DMA double buffer at 24 kHz is under a kilobyte, so the overlay pays for itself several times over.
MDUV380_FW.map · p25_4fsk.c
Wrong
It counts the trig calls, then prices them at a sixth
It wrapped libm and counted: 11,812cosf calls per 20 ms frame, 14,865 at worst, with uvquality already at its cheapest. That matches the grading emulator’s 13,072 on a different frame set, so the measurement is sound. Then it assumes 100–200 cycles per call and concludes the synthesis needs “roughly 60–120% of a 72 MHz core”. Newlib’s cosf does full range reduction — the emulator counts a matching ~13,000 __ieee754_rem_pio2f calls per frame — and mbelib totals 9.4M instructions per frame on the demo build, 655% of the core, or 11–16× on the live build. Its own conclusion survives; its margin does not, and the plan it builds on that margin would not close the gap.
A real ±1.1 kHz carrier offset at 450 MHz is comparable to the 1.2 kHz level spacing, so fitting gain and DC over an unbalanced 64-symbol window can converge on the wrong labelling — calibrate from the 24 known sync symbols instead. Sync detection compares hard dibits with ≤2 mismatches, so a +3↔+1 slip counts as a full error where soft correlation would be more sensitive and calibrate the levels for free. And after a lost frame the parser stays in RX_LDU for the full 784 payload dibits, so the next frame’s sync is swallowed too: one corrupted frame costs two.
p25_4fsk.c:186–210 · p25rx.c:409–433
Overstated
It rules out the chip mode the other run planned around
It says the one-layer mode is still DMR-framed — 264 or 288 bit data after DMR sync and slot timing, with the interrupt “typically tested in bit error rate” — so there is no continuous dibit stream and no route without a hardware modification. Those lines are real. But the same section describes one-layer mode as solving “the channel filtering of the baseband or low-IF signal and the modulation and demodulation process”, leaving “the channel codec and the processing of all communication protocol” to the user. Whether the chip emits frames without DMR sync is untested, and the Opus 5 run built a falsifiable acceptance test for exactly that question.
manual 1346–1360, 2384–2386 · manual 2553–2632
Overstated
A real gap, attributed to the wrong cause
It found something no other audit did: in the project’s own suite, the direct 38.4 kHz path recovers 7 of 9 clean frames at 8 dB while the 24 kHz-plus-resampler path recovers 2 of 9. I reran it — exact. But the two tests differ in more than the resampler: test 4 generates at 24 kHz, so samples per symbol and noise bandwidth change too. Isolating the resampler alone — band-limit, resample, decode — gives equal or fewer symbol errors at every SNR from 16 dB down to 8. The gap deserves investigating; blaming linear interpolation for it is not established.
tests/p25/run.py test 3, test 4 · p25_resample.c
Claim check
20 claims, checked one by one
15 hold, 4 overstated or unverified, 1 wrong. Each claim was checked at the lines it cites.
The audits track 4 issues that stop this radio decoding P25. This review: the vocoder found but under-costed; no 8 kHz rule. How the review treated each, from the provenance table: