Grok 4.6 got the same brief as every P25 audit on bench: find why this firmware struggles to decode P25 Phase 1 and Phase 2 voice on the radio’s HR-C6000, and what to do about it.
It worked in a clean room, with a fresh copy of the repository, no earlier reviews and no network. I checked its claims against the code, the HR-C6000 manual and the repository’s own notes, and put two of them through the firmware’s demodulator.
Overall grade
C
70 / 100 weighted
FDCBA
Finds what erases the signal. Misses what stalls the voice.
Grok 4.6 reads the radio front end better than any clean-room audit so far. It traces what analog mode leaves switched on: the AT1846S voice filters with the low-frequency bit cleared, and HR-C6000 register 0x34 with de-emphasis on, which alone leaves zero decodable voice frames in my model.
It builds the microphone case from the driver itself: analog setup writes 0xE0=0x89, “Turn off Microphone input”, and the monitor overwrites it with the mic-enable value. It also finds the manual’s 8 kHz frame-clock rule and the capture staging bug, and its plan starts with the right experiment.
It stops where the samples become voice. It calls the sample ring “fine on a 1 ms tick” and mbelib “the right vocoder”, yet emulated instruction counts put IMBE synthesis at 11–16 times the radio’s 72 MHz CPU.
It also misses the circular test oracle and both muting policies, and places an AMBE+2 vocoder on a chip that has none. 21 of 30 claims hold and nothing is fabricated. That puts it at the top of the clean-room table, just into a C: two points ahead of Muse Spark 1.3 Contributor and five ahead of DeepSeek V4.1 Flash.
Claims that check out
21 / 30
7 overstated, miscited or doubtful · 2 wrong
Decode-critical issues found
3 of 4
misses the vocoder CPU wall
Voice frames with de-emphasis on
0 / 180
the 0x34 setting it flags, in my model
CPU the vocoder needs
11–16×
of 72 MHz, emulated; it calls the budget fine
Headline findings
Six things to know about this review
Holds up
It names the settings that erase C4FM
Analog mode leaves the AT1846S in its FM register set: high- and low-pass filters on (0x58=0xBC85), the low-frequency audio bit cleared (0x40=0x0030), 25 kHz. OpenGD77’s DMR set for the same 4800-baud 4FSK does the opposite.
It adds the HR-C6000 side: 0x34=0x3C keeps de-emphasis on, and the monitor never clears it. Both go into its fix. In my model de-emphasis alone leaves 0 of 180 voice frames, and a high-pass stops every frame from 75 Hz.
AT1846S.c:191–217 · HR-C6000.c:2846–2854 · manual 2806 · model: standard C4FM through the firmware demodulator
Holds up
It builds the microphone case from the driver itself
trxSetModeAndBandwidth(RADIO_MODE_ANALOG) runs HRC6000SetFMRx(), which writes 0xE0=0x89, commented “Turn off Microphone input”. The monitor then writes 0xC9, the bit HRC6000SetMic() sets.
So Grok 4.6 assumes I2S carries the mic until proven otherwise, predicts that its first gate will show exactly that, and stops tuning the demodulator if it does. It also sets the manual’s “must be 8KHz” frame clock against the monitor’s 24 kHz.
It waves through a vocoder that needs 11–16× the CPU
It calls the 512-sample ring “fine on a 1 ms tick; UI stalls will overrun” and software mbelib “the right vocoder”, and leaves CPU watermarks for after RF works.
The 1 ms loop is a minimum, and mbelib runs inside the same pass. Emulated, one voiced 20 ms frame takes 134–458 ms of CPU at 72 MHz, so the 21 ms ring overruns on every voiced frame. Fixing the front end alone would still produce no real-time audio.
GROK46_REVIEW.md §5 and Gate C · p25mon.c:93–95, 583–610 · mbelib.c:306
Major gap
Past the front end, the review thins out
It misses the circular test oracle: the project’s channel model shapes the test transmitter with the receiver’s own RRC, so standard C4FM needs about 2 dB more than the tests show.
It also misses that one failed signalling decode mutes a clear call, that a non-standard MFID never plays, the ±10% rate gate against the ±1% timing clamp, two unverified SPI writes per decoded frame, and four of the six tooling issues.
Gate A fixes the capture to export the decoder’s own samples, then compares a keyed carrier with a mic tap. Gate B flattens the front end with the AT1846S DMR set and de-emphasis off, and judges it by the four-level histogram, not sync counts. The fallbacks are the M17 discriminator tap, DC-coupled, and the chip’s one-layer 4FSK test mode.
Two values need fixing: Table 5.6 gives 0x41=0x41, not 0x01, and 0x34=0x00 may select the narrower 2.55 kHz filter. Clearing bit 5 alone is safer.
“Linear upsample aliases”: through the firmware’s own resampler, 24 kHz C4FM decodes exactly the frames the direct path does, with no extra symbol errors. “The on-chip AMBE+2 vocoder”: the HR-C6000 docks external vocoders and has none; DMR voice runs as DVSI software on the STM32, as the stock report it cites says.
“I/Q would be required” for Phase 2 is too strong, and a quiet channel during bring-up says nothing about the sample path.
Same rubric, weights and scale as every P25 audit on bench, so scores compare directly. Grok 4.6 leads the clean-room audits on coverage and fix plan and ties Muse Spark 1.3 Contributor on accuracy; Muse Spark edges it on root cause and clarity.
Dimension scores
Hover or focus a bar for the reasoning. Weighted total 70, grade C.
Rubric table
Dimension
Weight
Score
Points
Why
Accuracy & evidence
30%
74
22.2
21 of 30 claims hold, with register values exact across HR-C6000.c, AT1846S.c and p25mon.c and nothing fabricated. Two claims are wrong, one of them misdirecting (“fine on a 1 ms tick” for a loop that overruns on every voiced frame), seven are qualified, and it gives no line numbers.
Coverage of decode problems
25%
59
14.8
The only clean-room audit to find three of the four decode-critical issues, with the fullest analog-chain finding (AT1846S filters and low-frequency bit plus the 0x34 de-emphasis bit), and adds the one-layer mode, the M17 tap and the capture staging bug. It misses the vocoder CPU wall, the circular RRC oracle, both muting policies, the rate-gate mismatch and most tooling issues.
Root cause & prioritisation
15%
70
10.5
Puts the receive chain first, in the right order: prove microphone versus RF, flatten the filters, then fix the rate, with nothing unreachable first. Held back because it waves the vocoder through and leaves CPU watermarks for last.
Fix plan & acceptance gates
15%
75
11.2
Gates A–E are ordered, with expected outcomes, stop rules and fallbacks (the M17 ADC tap, one-layer mode), and include a complete flat receive path. It has no real-time vocoder work and no independent C4FM oracle, and writes 0x34=0x00 where clearing bit 5 alone is safer.
Originality & attribution
10%
72
7.2
From a clean room it adds what no repository document states: the AT1846S and 0x34 register culprits, the manual’s 8 kHz clock rule, the capture staging mismatch and one-layer mode as a symbol tap. It credits the stock-firmware report and bring-up notes, but repeats STAGE4’s 5-samples-per-symbol line and the repository’s M17 tap without credit.
Clarity & calibration
5%
78
3.9
Crisp and decisive, with expected outcomes and falsification tests at every gate. Held back by confident overreach (“I/Q would be required”, “linear upsample aliases”, “the on-chip AMBE+2 vocoder”) and by 16 citations without a single line number.
Weighted total
100%
70
Grade scale: A ≥ 90 · B 80–89 · C+ 75–79 · C 70–74 · C− 65–69 · D 50–64 · F < 50
Receive chain
Where its findings sit
Grok 4.6’s findings sit in the radio front end: the AT1846S, the HR-C6000, the microphone input and the I2S clock all carry amber tags. The dashed amber route is its optional one-layer path, where the HR-C6000 demodulates the 4FSK and the MCU reads bits over SPI. Everything it missed sits on the MCU side, where the samples become voice.
§1 raised by Grok 4.6M1 missed by Grok 4.6path its proposed one-layer routeGrey dashed edge: no documented connection
§1I2S most likely carries the microphone: analog setup turns the mic off, the monitor turns it back on
M1IMBE synthesis needs 11–16× the 72 MHz CPU; it calls mbelib “the right vocoder”
§2AT1846S FM filters, low-frequency bit and 25 kHz; HR-C6000 0x34 de-emphasis
M2The 21 ms ring overruns on every voiced frame; it calls the tick budget fine
§3Manual: the I2S frame clock must be 8 kHz; the monitor runs 24 kHz
M3The test waveform shares the receiver’s RRC filter
§4One-layer 4FSK test mode as a symbol tap (Gate D)
M4Fail-closed muting and non-standard MFID muting
§1Capture export stages the wrong halfwords
M5±10% health gate versus ±1% timing clamp
M6Two unverified SPI writes per decoded frame
The front end, checked
Its strongest ground holds up
Grok 4.6’s register-by-register reading of the analog chain matches the code and the manual. My model shows why it matters: the de-emphasis it flags is fatal on its own, and so is any audio high-pass above a few tens of hertz, which is also why its advice to DC-couple a direct discriminator tap is right.
Register state for a P25 session
What trxSetModeAndBandwidth(RADIO_MODE_ANALOG, true) leaves in place, next to OpenGD77’s own settings for 4800-baud 4FSK.
Register
Monitor today (analog FM)
OpenGD77 DMR mode
Effect on C4FM
AT1846S 0x40
0x0030, low-frequency bit cleared: “so it should be cleared to receive FM”
0x0031, “allows low freq audio”
Removes the near-DC content that runs of equal symbols depend on
AT1846S 0x58
0xBC85, “enable some filters for FM e.g. high and low pass filters”
0x9CDD, “disable all filters in DMR mode”
High- and low-pass shaping of the symbol stream
AT1846S 0x44
0x06CC, AF gain 80%
0x07FF, 100%; the DM-1701 “needs higher AF gain” to decode 4FSK
Lower discriminator level into the next stage
AT1846S bandwidth
25 kHz
12.5 kHz, forced for digital
Wider noise bandwidth and deviation scaling set for ±5 kHz FM
HR-C6000 0x34
0x3C, “compressor off, de-emph on, 3 kHz audio filter”
not written; the FM value persists
−6 dB/octave from 300 Hz and a 3 kHz low-pass on the FM receive path (manual §6.2)
Sources: AT1846S.c:191–217, HR-C6000.c:2846–2854 and 2858–2875, trx.c:228–279, p25mon.c:724. Register comments are OpenGD77’s; the manual translation documents 0x34.
What the inherited 0x34=0x3C does to C4FM
Standard C4FM through each HR-C6000 receive stage alone, then the firmware’s live demodulator and framer. 20 LDUs: 180 IMBE frames possible.
The 3 kHz low-pass is harmless because C4FM sits below 2.88 kHz. De-emphasis, −6 dB per octave from 300 Hz as the manual describes, is fatal on its own. Grok 4.6 names both stages; whether de-emphasis sits on the I2S capture path is still unmeasured.Data table
Audio stage
Noise-free frames · NIDs
Symbol errors
20 dB frames · NIDs
no audio filtering
180/180 · 20/20
0.0%
180/180 · 20/20
3 kHz low-pass only
180/180 · 20/20
0.0%
180/180 · 20/20
de-emphasis only
0/180 · 0/20
51.3%
0/180 · 0/20
de-emphasis + 3 kHz low-pass (0x34=0x3C)
0/180 · 0/20
49.8%
0/180 · 0/20
Voice frames decoded versus audio high-pass corner
Standard C4FM through a 2nd-order Butterworth high-pass, then the firmware’s live demodulator and framer. 20 back-to-back LDUs: 180 IMBE frames and 20 NIDs possible.
Noise-free
20 dB SNR
Typical voice high-pass range
Decoding holds up to 20 Hz, loses a quarter of its frames at 35 Hz, and fails completely from 75 Hz. Filters that reject CTCSS tones (67–254 Hz) sit well past that point, so the exact AT1846S corner, which is undocumented, doesn’t change the conclusion.Data table
Corner (Hz)
Noise-free frames · NIDs
Noise-free SER
20 dB frames · NIDs
20 dB SER
off
100% · 20/20
0.00%
100% · 20/20
0.00%
10
100% · 20/20
0.00%
100% · 20/20
0.01%
20
100% · 20/20
0.13%
100% · 20/20
0.23%
35
75% · 15/20
2.31%
60% · 12/20
2.80%
50
10% · 2/20
7.04%
20% · 4/20
7.45%
75
0% · 0/20
15.17%
0% · 0/20
15.37%
100
0% · 0/20
21.29%
0% · 0/20
21.70%
150
0% · 0/20
30.21%
0% · 0/20
30.58%
200
0% · 0/20
36.59%
0% · 0/20
36.19%
300
0% · 0/20
44.46%
0% · 0/20
44.67%
Two claims, rerun
A harmless resampler, and a risky register value
Grok 4.6 ran nothing, so there was no harness of its own to rerun. Instead I put two of its claims through the firmware’s demodulator and framer: that the 24 → 38.4 kHz linear resampler aliases, and that writing 0x34=0x00 simply removes the voice low-pass.
What the linear resampler costs
Standard C4FM into the firmware’s demodulator and framer, once directly at 38.4 kHz and once band-limited to the codec’s 24 kHz, then upsampled by the firmware’s own p25_resample() in 32-sample chunks, as the monitor does.
Direct at 38.4 kHz
24 kHz through p25_resample
Both paths decode the same frames in every trial, and the resampled path never has more symbol errors. Its images sit 41 dB below the signal, far outside the band the matched filter passes. Dropping the resampler would save 2–3% of the CPU, not any symbols.Data table
Noise
Direct: frames per trial
Direct: symbol errors
Via 24 kHz: frames per trial
Via 24 kHz: symbol errors
Noise-free
180
0 / 17,200
180
0 / 17,200
16 dB SNR
180, 180, 180
2 / 51,600
180, 180, 180
2 / 51,600
13 dB SNR
180, 180, 180
51 / 51,600
180, 180, 180
35 / 51,600
11 dB SNR
180, 180, 171
231 / 51,600
180, 180, 171
192 / 51,600
180 IMBE frames possible per trial. Three noise seeds per SNR; one noise-free trial.
What writing 0x34=0x00 could cost
The manual lets 0x34 choose a 2.55 kHz or a 3 kHz low-pass on the FM receive path and documents no bypass. Standard C4FM through each 4th-order Butterworth low-pass, then the firmware’s demodulator and framer. Corners and orders are assumptions.
Noise-free
13 dB SNR
The 3 kHz filter that today’s 0x3C selects costs no frames. If clearing the register selects the 2.55 kHz filter, the eye starts to close: 0.6% symbol errors with no noise, and 45 of 540 frames lost at 13 dB. Clearing bit 5 alone turns de-emphasis off without that risk.Data table
Low-pass
Noise-free frames
Noise-free symbol errors
13 dB frames per trial
13 dB symbol errors
No low-pass
180
0 / 17,200
180, 180, 180
51 / 51,600
3 kHz low-pass
180
0 / 17,200
180, 180, 180
222 / 51,600
2.55 kHz low-pass
180
100 / 17,200
162, 180, 153
1360 / 51,600
The CPU budget
The wall it waved through
Grok 4.6 does think about CPU, but only about the demodulator, which fits. The emulated budget shows where the time actually goes once voice starts.
Where one second of decoding goes
The firmware’s streaming loop, compiled with its own GCC flags and newlib and run in an emulated Cortex-M4F over 2.2 s of input (12 LDUs, 108 IMBE frames, all decoded). Instruction counts become time by assuming one clock cycle per instruction, the optimistic end.
The demodulator fits: about 40% of the core, 60% at 1.5 cycles per instruction. The vocoder needs roughly 11 times the whole CPU, 16 times at 1.5. Building with -O2 changes these by under 1%, because the time is spent inside newlib’s prebuilt libm.Per-stage table
Stage
-Os, M instr/s
Share at 1.0 · 1.5 c/i
-O2, M instr/s
Resampler ×1.6
1.40
1.9% · 2.9%
1.32
RRC FIR, timing and level fit
28.00
38.9% · 58.3%
27.70
Framing, NID and FEC
0.74
1.0% · 1.5%
0.73
mbelib IMBE decode and synthesis
781.90
1,086.0% · 1,629.0%
781.60
Total
812.1
1,128% · 1,692%
811.4
CPU time for each 20 ms of speech
One call to mbelib’s IMBE decoder per frame, uvquality 1, FEC-valid codewords with chosen pitch and voicing, mean over 100 consecutive frames.
Every frame type misses its 20 ms deadline, from about 2× for the simplest frames to more than 20× for loud voiced ones. The cause is mbelib’s per-harmonic phase, which accumulates without wrapping (mbelib.c:306). Once a call has run for about half a second, nearly every voiced-band cosf argument exceeds about 201 radians, and newlib’s cosf takes its __kernel_rem_pio2f path at roughly 1,700 instructions per call instead of 40 to 100. The real firmware links that same path.Per-frame table
Frame type
Mean, M instr
Worst, M instr
Mean ms at 1.0 · 1.5 c/i
L=20, all bands unvoiced
3.00
5.02
42 · 62
L=20, typical (15 of 20 voiced)
9.68
10.23
134 · 202
L=30, typical (21 of 30 voiced)
13.97
14.76
194 · 291
Random pitch and voicing
15.86
26.17
220 · 330
L=56, all bands voiced
31.25
32.97
434 · 651
Claim check
30 claims, checked one by one
Twenty-one hold, three of them with gaps that matter. Seven are overstated, miscited or doubtful. Two are wrong, and one of those steers the reader away from the vocoder, the blocker it misses.
Item
What Grok 4.6 claims
Where
Result
Notes
Verdict
The HR-C6000 is a DMR baseband/codec chip with no P25 engine, no IMBE vocoder and no CQPSK/H-DQPSK modem
manual 43–49, 1181–1200
Holds
The manual never mentions P25. It docks external vocoders.
Verdict
Bring-up measured a stable 24 kHz clock and found no validated NIDs on air, which “matches a broken RF-to-sample path”
STAGE3-BRINGUP.md:328–331
Overstated
The clock result is right: 300/300 reports at exactly 24,000 Hz. But the same note says no P25 sync had been caught while it waited for sporadic traffic. That silence says nothing about the sample path.
Chip
Manual Figure 6.1: FM receive runs discriminator → audio filters → de-emphasis and decompression → Audio_out, with no route to I2S
manual 2638–2676, 2794–2806
Holds
analysis/stock-p25/REPORT.md §4 reads it the same way.
Chip
LRCK defaults to 8 kHz, and the vocoder frame clock I2S_FS_M “must be 8 kHz”
manual 1127, 1217–1219
Holds
Set against the monitor’s 24 kHz configuration in §3. No repo document mentions it.
Chip
Register 0xE0 is undocumented in the translation; HRC6000SetMic() treats bit 6 as the mic / LineIn1 enable
manual; HR-C6000.c:2962–2964
Holds
Chip
Stock V02.03 has no discriminator→I2S setup; 0xE0=0xC9 sits in the speaker path, not a capture path
analysis/stock-p25/REPORT.md:164–177
Holds
Credited to the stock report.
§1
Analog mode runs HRC6000SetFMRx (0xE0=0x89, 0x10=0x80, 0x34=0x3C, 0x26=0xFD); the monitor then writes 0xE0=0xC9, the bit HRC6000SetMic sets, plus 0x36=0x00 and 0x40/0x41/0x5F=0, and skips 0x10=0x80 because it “starves the codec clock”
Exact values. The driver’s own comment on 0x89 is “Turn off Microphone input”, so the monitor undoes the mic-off setting.
§1
“FM mode is not refreshed”
p25mon.c:490, 810; HR-C6000.c:2967–2977
Incomplete
True as far as it goes, but radioSetAudioPath(false) writes 0x10=0x6E, a DMR value, at start and on every decoded frame. The chip ends in a hybrid state the review never names.
§1
The monitor keeps HRC6000SetFmAudio(true) (0x36=0x02 + 0x10=0x80) off “to mute FM noise”
p25mon.c:644–646, 711–716
Miscited
The code gives a different reason: that pair “poisons the hybrid I2S state”, and 0x10=0x80 starves the codec clock. Nothing mentions noise.
§1
Until a keyed-RF versus mic-tap contrast is done, assume I2S carries codec/mic PCM, not C4FM
The stock mic path reads the same [j][i*2] slot as “Mic Audio” (sound.c:444). The contrast test is STAGE3-BRINGUP.md’s, and the review credits it.
§1
The capture stages the first 80 interleaved halfwords of each half-buffer, while the decoder takes [j][i*2]
p25mon.c:443, 460–466
Holds
158 of 160 staged positions differ from what the decoder consumed.
§2
AT1846FMSettings turns on the high- and low-pass filters (0x58=0xBC85) and clears the low-frequency bit (0x40=0x0030); the DMR set (0x58=0x9CDD, 0x40=0x0031) is the front end 4FSK needs; the 25 kHz IF is not the fatal part
AT1846S.c:191–217; p25mon.c:724
Holds
In the firmware-in-the-loop model, a high-pass alone stops every frame from 75 Hz. IF width isn’t modelled.
§2
0x34=0x3C leaves bit 5, de-emphasis (−6 dB/oct over 300–3000 Hz), on, and the monitor never clears it
HR-C6000.c:2851; manual 2806; p25mon.c
Holds
Model: de-emphasis alone leaves 0 of 180 voice frames.
§3
Stock 8 kHz gives 1.67 samples per symbol and is gated off (nominal >= 12000); config 2 (0x2F=0x03, LRCK 0x00FF) measures 24 kHz, exactly 5 samples per symbol, the rate MMDVM’s P25 modulator uses
“Linear upsample aliases”, listed as a problem for the eye
p25_resample.c; rerun resampler_check.py
Overstated
Rerun: standard C4FM band-limited to 24 kHz, then the firmware’s own resampler in 32-sample chunks, decodes exactly the frames the direct 38.4 kHz path does, with equal or fewer symbol errors from 11 to 16 dB SNR. Its images sit 41 dB below the signal.
§3
Extra CPU at 72 MHz: 38.4 kS/s through 81 taps plus a 4-level fit every 512-sample hop
p25_4fsk.c; p25mon.c:597–615
Holds
Emulated, the filter, timing search and level fit take 28 M instructions per second of input, 39–58% of the CPU. IMBE synthesis takes 28 times that, and the review never looks at it.
§4
The monitor turns off the chip’s DMR engine (0x40=0x00, 0x5F=0x00) because slot timing gates the I2S clock (~54% duty)
Table 5.6 gives 0x41 = 2’b0100 0001 (0x41). 0x41=0x01 is the stock firmware’s BER-test value (analysis/stock-p25/REPORT.md:88). The recipe also leaves out 0x01 and the 0x07–0x09 IF word.
§4
One-layer mode is “the documented way to get modem symbols without DMR framing”, and the firmware never implements it
manual 2552–2586
Overstated
The firmware never writes 0x10=0x02, and the mode is a real, untried opening. But the manual documents 36-byte frames typed by DMR sync class in DMR burst geometry (Figure 5.15), not raw symbols. Whether it emits anything without DMR sync is the open question.
§5
Audio plays only in P25_CALL_CLEAR; late entry mutes the first seven IMBE frames until signalling completes
p25rx.c:121–123; STAGE3-BRINGUP.md:129–130
Holds
It doesn’t mention that one failed signalling decode demotes a clear call (p25rx.c:177–181).
§5
512-sample ring (~21 ms), 4×32 samples per p25monTick: “Fine on a 1 ms tick; UI stalls will overrun”
p25mon.c:93–95, 570–591; applicationMain.c:1451
Wrong
The arithmetic holds only if the loop runs every millisecond. IMBE synthesis runs inside the same pass and takes 134–458 ms of CPU per voiced frame in emulation, so the ring overruns on every voiced frame, not just on UI stalls.
§5
“Software mbelib is the right vocoder”; don’t feed IMBE into the DMR AMBE path
p25rx.c:105–108; mbelib.c:306
Incomplete
mbelib is the only IMBE decoder in the tree, but as shipped it needs 11–16× the 72 MHz CPU (unwrapped harmonic phase sends newlib’s cosf down its slow path). It must be reworked before any voice plays in real time.
Phase 2
Phase 2 is two-slot TDMA at 6000 sym/s, H-DQPSK outbound and H-CPM inbound, AMBE+2 3600 voice, 2V/4V bursts with ISCH
Phase 2 section
Holds
Phase 2
The tree has no Phase 2 demodulator or 2V/4V parser; p25rx knows only Phase 1 DUIDs; the trunk follower rejects TDMA identifiers
p25_tables.h:85–92; p25rx.c:105; p25_trunk.c:26
Holds
Phase 2
Discriminator audio of CQPSK is not a 4-level eye, so I/Q sampling or a CQPSK-capable modem “would be required”
Phase 2 section
Overstated
A C4FM slicer won’t work, but a discriminator can recover H-DQPSK phase steps with less margin. DSD-FME in ref-sources carries CQPSK options for FM-demodulated input. It is harder, not impossible.
Phase 2
“The on-chip AMBE+2 vocoder” matches Phase 2 voice; use “mbelib AMBE+2 or the C6000 vocoder SPI”
The HR-C6000 has no vocoder. It docks external ones such as the HR_V3000, and this radio has none. DMR voice runs through DVSI AMBE+2 software on the STM32, as the stock report the review cites says. mbelib’s AMBE+2 path shares the synthesis routine that is 11–16× too slow.
Not the bug
Already handled: DUIDs, NID status dibits and LDU length, the DMA stop flag, CCM zeroing, eco power-cycling, DMR slot gating, SPI0 silent failures, the 24 kHz latch sequence
Each holds, but the SPI retry covers only p25monWriteReg. radioSetAudioPath(false) still writes 0x36 and 0x10 with no readback on every decoded frame (p25mon.c:490).
Gate B
0x34=0x00 turns off the band-pass filter, compressor, de-emphasis and the “3 kHz voice LPF”
The manual documents bit 7 (band-pass), bit 6 (compressor), bit 5 (de-emphasis) and a choice between 2.55 and 3 kHz low-pass filters, not a bypass. If 0x00 selects the 2.55 kHz filter, the model shows 0.6% symbol errors with no noise and loses 45 of 540 frames at 13 dB. Clearing bit 5 alone is the safe first step.
Gate B
The M17 DM-1701 precedent (AT1846S discriminator pin → STM32 ADC) is the honest fallback; sample ≥24 kHz, DC-coupled, no de-emphasis
DC coupling matters: in the model a high-pass costs frames from about 35 Hz. The repo already proposes the tap; not credited here.
Provenance
What was already known, and what it added
Grok 4.6 worked in a clean room: no earlier reviews, only the repository’s own notes, code comments and the HR-C6000 manual. The first column shows what those already say, so the second shows what it added. It credits the stock-firmware report and the bring-up notes where it uses them, but repeats two repository points without credit. This is the first clean-room audit, so there are no earlier columns.
Grok 4.6’s gates run in the right order: prove what the I2S stream carries, flatten the front end, then demodulate. Most steps survive checking. Two register values need correcting, and the plan stops before the part that decides whether anyone hears voice.
Keep
Worth doing as written
Gate A first: export the decoder’s own [j][i*2] samples, then compare a keyed carrier with a mic tap. Its prediction, that the stream follows the mic, and its stop rule are right.
The flat front end: the AT1846S DMR register set at 12.5 kHz instead of analog FM, de-emphasis off, and 0xE0 back to the driver’s mic-off value. Judge it by a four-level histogram, not sync counts.
The fallbacks: the M17 discriminator tap into an STM32 ADC, DC-coupled with no de-emphasis, and the one-layer 4FSK test mode as a symbol source.
Leave the parser alone until nid_ok moves on a known C4FM source, and treat Phase 2 as its own project.
Change
Would cause new problems
0x34=0x00: the manual offers a 2.55 or 3 kHz low-pass, not a bypass, and the narrower one costs symbols. Clear bit 5 alone, change one register at a time, and watch the codec clock.
Table 5.6 values:0x41 is 0x41, not 0x01, and the recipe also needs 0x01 and the 0x07–0x09 IF word. Test first whether the mode emits frames without DMR sync.
CPU watermarks last: measure max_process_cycles against 20 ms on the first decoded frame, not after RF works. Emulation says it will miss by 11–16×.
Add
Missing from its plan
A real-time vocoder: a per-harmonic phase accumulator with wrapping and a cosine table instead of per-sample cosf. Without it, even a perfect front end yields no audio.
Standard C4FM test vectors (raised cosine × inverse sinc) with de-emphasis and high-pass regressions, instead of a generator that shares the receiver’s RRC.
Set the audio path once at start, not with two unverified SPI writes on every decoded frame.
Call handling: latch a clear call through one failed signalling decode, and handle non-standard MFID link control instead of muting.
The one-layer path, and what it would take
Manual Table 5.6 (verified line by line), with the questions that decide whether it can carry P25.
Register
Value
Manual description
0x01
xxxx 0000
IF receive mode
0x07 0x08 0x09
0x0B 0xD9 0x54
24-bit IF word, 455 kHz by default (OpenGD77 boots at 450 kHz)
0x10
0x02
One-layer mode, continuous receive; bit 5 for slot receive
0x40
0x40
Receive enable, test mode
0x41
0x41
Receive test enable
Output: 36 demodulated bytes per frame in RX RAM from 0x30, frame type in 0x51, system interrupt bit 0 when ready.
Rate fits exactly: 36 bytes every 30 ms is 9,600 bit/s, which is 4,800 symbols per second. Contiguous frames would lose nothing.
Open: Figure 5.15 is DMR burst geometry, so the mode may only emit frames after DMR sync. That’s the first thing to test.
Also needed: the AT1846S DMR register set, since the demodulator is fed by the AT1846S. And the vocoder still has to run in real time.
Phase 2
Right on the air interface, wrong about the vocoder
Its Phase 2 architecture holds: two-slot TDMA at 6,000 symbols per second, H-DQPSK outbound and H-CPM inbound, AMBE+2 half-rate voice, and 2V and 4V bursts with ISCH. It is right that the tree has no Phase 2 demodulator or burst parser, that the trunk follower rejects TDMA identifiers (p25_trunk.c:26), and that Phase 2 should wait until Phase 1 speaks.
Two details are off. It places “the on-chip AMBE+2 vocoder” on the HR-C6000, which docks external vocoders and has none. On this radio DMR voice runs through DVSI AMBE+2 software on the STM32, as the stock-firmware report it cites says, and that decoder already runs in real time; its other option, mbelib’s AMBE+2 path, shares the synthesis routine that is 11–16 times too slow. And “I/Q sampling would be required” is too strong: DSD-FME, bundled in ref-sources/, carries CQPSK options for FM-demodulated input, at a cost in margin.
Against the other clean-room audits
The most decode-critical findings of the three
All three clean-room audits find the microphone route and the AT1846S filters, and none finds the vocoder CPU wall. Grok 4.6 is the only one to find three of the four decode-critical issues, adding the 8 kHz rule and the capture staging bug. DeepSeek V4.1 Flash runs more code and derives the clock path but clears de-emphasis; Muse Spark 1.3 Contributor also finds de-emphasis and writes the best-calibrated prose, but trusts the broken instruments.
Same rubric and weights. Hover or focus a bar for the score.
DeepSeek V4.1 Flash
Muse Spark 1.3 Contributor
Grok 4.6
Method and limits
How this was checked
I read GROK46_REVIEW.md, the only file the run added, and checked each claim against the firmware sources, the OpenGD77 radio drivers, the translated HR-C6000 manual, the stock-firmware analysis and the repository’s bring-up notes. Before grading I audited the run itself: one model throughout, no network, no skills or memory, and nothing read outside its copy of the repository.
The review executed nothing, so there was no harness to rerun. I put two of its claims through the firmware-in-the-loop model instead, and reused the model’s de-emphasis, high-pass and CPU results built for earlier audits of this firmware.
The AT1846S and HR-C6000 filter responses are modelled, not measured, and the manual doesn’t say which 0x34 bits select the 2.55 kHz filter.
CPU figures come from instruction counts in an emulator, not timing on the radio.
No firmware was edited or flashed, and no radio was touched.
DM1701_REPO=/path/to/DM-1701 python3 resampler_check.py
# via 24 kHz + p25_resample: same frames as direct, no extra symbol errors
# 2.55 kHz low-pass: 100/17,200 symbol errors noise-free, 495/540 frames at 13 dB
DM1701_REPO=/path/to/DM-1701 python3 deemph_check.py
# 3 kHz low-pass alone 180/180 · de-emphasis alone 0/180
grep -n "Turn off Microphone input\|Mic Input to Line in 1" \
opengd77-rt3s-experiments/MDUV380_firmware/application/source/hardware/HR-C6000.c