Review audit · firmware & RF
17 September 2026
DM-1701 · HR-C6000 · AT1846S · STM32F405 · P25

DeepSeek V4 Pro’s P25 review, graded

DeepSeek V4 Pro 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, and it finished in eight minutes, the fastest run on this task. I checked its claims against the code, the manual and the repository’s notes, compiled the parts it questioned, and used the firmware-in-the-loop model and CPU emulation from the earlier clean-room audits.

Overall grade
D
53 / 100 weighted

Right pivot. Wrong arithmetic.

DeepSeek V4 Pro gets the shape of the problem right. The sample source is the blocker, the manual and the stock analysis give I2S to an external codec, and the honest fix is the M17 discriminator tap with the C6000 left to drive the speaker. It is also the only audit to read the demodulator’s control loops: no frequency tracking, ad hoc timing gains of 0.5 and 0.001, no lock detection. Those quotes are exact. Then it is the only audit to find the repository’s own vocoder measurement and the only one to misread it.

4.36M instructions per 20 ms frame is 303% of what a 72 MHz core can issue in 20 ms, not the ~60% it reports, and the decoder it calls “at least as expensive” needs 11–16×. That slip turns the CPU wall into a margin and sends the plan to a 168 MHz clock and a polyphase resampler instead. The delivered file also carries a first draft, four pages of reasoning, and a retracted single most important code bug that was never a bug. 16 of 26 claims hold, which puts it at D.

Claims that check out
16 / 26
5 overstated, miscounted or miscited · 5 wrong
Decode-critical issues found
1 of 4
CPU miscounted; filters and the 8 kHz rule untouched
Its own CPU citation
303%
of the 20 ms budget; it read it as 60%
What the file contains
2 reviews
a draft, its reasoning, and a broken tool call

The run, not graded: 8 min · 16 agent steps · 41 tool calls · 17K output tokens (3K reasoning) · 1.3M tokens re-read · Alibaba Cloud Token Plan, interactive

Headline findings

Six things to know about this review

Holds up

It names the blocker and the honest fix

The manual gives I2S to an external codec or vocoder and ends FM receive at the speaker; the stock firmware uses 0xE0=0xC9 in the speaker path; the firmware itself prints source=UNVERIFIED. So the samples are most likely microphone audio, and a stable clock is not the right signal. Its answer is the M17 tap from AT1846S pin 9 to an MCU pin, with the C6000 left to drive the speaker, run only after the carrier-keyed acceptance test confirms the source.

manual §4.5, §6 · analysis/stock-p25/REPORT.md:175–176 · p25mon.c:519 · STAGE3-BRINGUP.md:134

New reading

It is the only audit to read the control loops

Nobody else opened the timing recovery. It quotes the loop exactly — first = next_sample + 0.5·delta, period += 0.001·delta — and notes there is no proportional-integral design and no out-of-lock detection, so a phase wrap past half a symbol tracks silently. It also finds there is no frequency tracking at all: one DC estimate per window against PLLs that drift. Both check out in the source, along with its list of the float operations in the hot path.

p25_4fsk.c:162, 187–210, 236–241

Critical error

It misreads its own CPU measurement by five times

It is the only audit to find the repository’s measured vocoder number: the IMBE encoder at 4.36M instructions per 20 ms frame. A 72 MHz core issues about 1.44M in that time, so the figure is 303% of the budget. The review calls it “~60% of the 72 MHz budget” and concludes the budget is “marginal”. Emulated, the decoder it calls “at least as expensive” needs 11–16× the CPU. The arithmetic it had in hand was the wall it missed.

STAGE4-TX-FEASIBILITY.md:36 · mbelib.c:306 · Core/Src/main.c:269–274

Critical error

Two premises that send the work sideways

“No documented raw 4FSK/C4FM modem mode” is how it rules the chip out. Manual §5.4.6 documents exactly that: one-layer mode demodulates 4FSK and stores 36 bytes per frame in receive RAM, which is 4,800 symbols per second, P25 Phase 1’s own rate. And it calls the linear resampler unfit for production. Rerun through the firmware’s own resampler, 24 kHz decodes the same frames as a direct 38.4 kHz path, with equal or fewer symbol errors and images 41 dB down.

manual 2552–2638 · p25_resample.c · resampler_check.py

Wrong

A retracted “most important bug” ships in the file

Four pages of the delivered review argue that the NID parser collects only 22 of the 32 dibits the BCH decoder needs, and call it “the single most important code bug found”. The parser packs 32 of 33 dibits and skips only the status symbol, so the full codeword arrives. The review works this out itself and retracts it — in the same file, after the reader has read it, along with an unterminated tool-call block wrapping the final review.

p25rx.c NID parser · DEEPSEEKV4PRO_REVIEW.md lines 288–512

Major gap

The front end and the instruments go unexamined

It never opens the analog chain: no 0x34, no de-emphasis, no AT1846S filter set, though de-emphasis alone leaves 0 of 180 frames in the model. It never opens the diagnostics either: the capture export that ships the wrong halfwords, the parser that passes incomplete streams, the starvation counter that never counts. It also cites P25-HARDWARE-REVIEW.md as agreeing with it. That file is referenced by the README but is not in the tree.

HR-C6000.c:2851 · AT1846S.c:191–217 · p25mon.c:123, 460–466 · README.md:157

Scorecard

How the 53 breaks down

Same rubric, weights and scale as every P25 audit on bench, so scores compare directly. DeepSeek V4 Pro scores mid-table on root cause and last on clarity, where the delivered file works against it.

Dimension scores

Hover or focus a bar for the reasoning. Weighted total 53, grade D.

0255075100Score out of 100Accuracy & evidenceweight 30%60Coverage of decode problemsweight 25%33Root cause & prioritisationweight 15%64Fix plan & acceptance gatesweight 15%60Originality & attributionweight 10%62Clarity & calibrationweight 5%48
Rubric table
DimensionWeightScorePointsWhy
Accuracy & evidence30%6018.016 of 26 claims hold, and its reading of the demodulator’s loops is exact. Five are wrong, including the arithmetic on its own CPU citation, a “no raw 4FSK modem mode” that the manual contradicts, and a NID bug it retracts four pages later inside the same file.
Coverage of decode problems25%338.2Finds the microphone question, the discriminator tap, the clock contention and the demodulator’s missing frequency tracking, and reaches for a measured vocoder figure. It never looks at the inherited filters, the capture export, the parser, starvation or the 8 kHz rule.
Root cause & prioritisation15%649.6Puts the sample source first and the hardware tap as the answer, which is the right pivot. But it rests that on a wrong claim about the chip’s modem, never touches the analog filtering, and downgrades the vocoder to “marginal”.
Fix plan & acceptance gates15%609.0The first two steps, the acceptance test then the tap, are the right ones. The five that follow spend the effort on a 168 MHz clock, a polyphase resampler, fixed-point conversion and a Gardner loop, while the de-emphasis, the broken capture export and the vocoder go unfixed.
Originality & attribution10%626.2The control-loop critique is new: no other audit read the timing loop or noticed the missing AFC. Most of the rest restates the manual, the stock report and the bring-up log, and one of its own citations points at a document that isn’t in the tree.
Clarity & calibration5%482.4The final review is well organised, but it is delivered inside a file that also holds an earlier draft, four pages of “wait, I misread” reasoning, and an unterminated tool-call block. A reader meets a retracted critical bug before the real review.
Weighted total100%53Grade 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

DeepSeek V4 Pro’s findings sit at the two ends of the chain: the sample source at the front and the demodulator’s control loops in the middle. The red tags cover the whole analog conditioning stage, the diagnostics branch and the vocoder it measured and then discounted.

RADIO · ANALOG FRONT ENDSTM32F405 · 72 MHz · -OsMicrophoneLineIn1 · enabled by E0 bit 6P25 C4FM carrier12.5 kHz channel±1.8 kHz peak deviationAT1846S receiveranalog FM register setHPF + LPF on · LF audio off · 25 kHzHR-C6000 codec ADCE0 = 0xC9 · 0x34 de-emphasis onno documented RF input to I2SI2S to the MCULRCK 24 kHz (manual: 8 kHz)left slot, 16-bitRFaudio?ADCDMA half-buffer ISRp25monI2sHalfBuffer160 frames per callbackSample ring512 samples ≈ 21 ms≤128 drained per tickResampler ×1.624 → 38.4 kHz, nominalhealth gate ±10 %RRC FIR + timing81 taps · 768/512 windowperiod clamp ±1 %p25rx framingsync · NID · LDU · FECfail-closed mutembelib IMBE≈16 M instructions/framecosf slow pathPCM ring → I2S TXradioSetAudioPath() per frame→ speaker amplifierCapture export ‘c’ISR stages [j][0..79]framed hex over USBUSB CDC consoleshared with 1 Hz reportp25_capture.pyparser on the hostdiagnostic tapSKTN1N2N3N4N5N6N7
S raised by DeepSeek V4 ProN1 missed or misreadDashed edge: no documented connection
SThe I2S stream is most likely the microphone; take the discriminator tap
N1mbelib needs 11–16× the 72 MHz CPU (called “marginal”)
KSlot engine and sync detector gate the I2S clock
N2HR-C6000 de-emphasis on the capture path
TNo frequency tracking; ad hoc timing gains
N3AT1846S voice filters and low-frequency bit
N4The capture stages the wrong halfwords
N5The parser certifies incomplete captures
N6pcm_starve never counts
N7Calls the resampler unfit for production
The CPU budget

The number it found, and the number it needed

The review is the only one to quote the project’s measured vocoder cost. A 72 MHz core issues about 1.44M instructions in a 20 ms frame, so the 4.36M it cites is three times the budget for the encoder alone. The emulated decoder is worse.

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.

0%200%400%600%800%1000%1200%Share of one CPU second at 72 MHz (-Os, 1.0 cycles per instruction)Resampler ×1.61.4 M instructions/s1.9%RRC FIR, timing and level fit28.0 M instructions/s39%Framing, NID and FEC0.7 M instructions/s1.0%mbelib IMBE decode and synthesis781.9 M instructions/s1,086%whole CPU
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/sShare at 1.0 · 1.5 c/i-O2, M instr/s
Resampler ×1.61.401.9% · 2.9%1.32
RRC FIR, timing and level fit28.0038.9% · 58.3%27.70
Framing, NID and FEC0.741.0% · 1.5%0.73
mbelib IMBE decode and synthesis781.901,086.0% · 1,629.0%781.60
Total812.11,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.

0 ms100 ms200 ms300 ms400 ms500 msCPU time per decoded 20 ms frame at 72 MHz (mean, 1.0 cycles per instruction)L=20, all bands unvoiced3.0 M instructions42 msL=20, typical (15 of 20 voiced)9.7 M instructions134 msL=30, typical (21 of 30 voiced)14.0 M instructions194 msRandom pitch and voicing15.9 M instructions220 msL=56, all bands voiced31.2 M instructions434 ms20 ms deadline
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 typeMean, M instrWorst, M instrMean ms at 1.0 · 1.5 c/i
L=20, all bands unvoiced3.005.0242 · 62
L=20, typical (15 of 20 voiced)9.6810.23134 · 202
L=30, typical (21 of 30 voiced)13.9714.76194 · 291
Random pitch and voicing15.8626.17220 · 330
L=56, all bands voiced31.2532.97434 · 651
The resampler it would replace

Polyphase for nothing

Its step four replaces the linear resampler. The model says what that would buy.

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
0100200300400500Symbol errors per 100,000 symbolsNoise-free0016 dB SNR4413 dB SNR996811 dB SNR448372
Both paths decode the same frames in every trial, and the resampled path never has more symbol errors. A polyphase replacement would buy nothing the decoder can use, at a cost in flash, RAM and CPU.
Data table
NoiseDirect: frames per trialDirect: symbol errorsVia 24 kHz: frames per trialVia 24 kHz: symbol errors
Noise-free1800 / 17,2001800 / 17,200
16 dB SNR180, 180, 1802 / 51,600180, 180, 1802 / 51,600
13 dB SNR180, 180, 18051 / 51,600180, 180, 18035 / 51,600
11 dB SNR180, 180, 171231 / 51,600180, 180, 171192 / 51,600

180 IMBE frames possible per trial. Three noise seeds per SNR; one noise-free trial.

The front end it never opened

Where the frames actually go

The review’s analog section stops at the chip’s block diagram. The registers the monitor leaves in place are the ones that decide whether a C4FM eye survives.

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.

RegisterMonitor today (analog FM)OpenGD77 DMR modeEffect on C4FM
AT1846S 0x400x0030, 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 0x580xBC85, “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 0x440x06CC, AF gain 80%0x07FF, 100%; the DM-1701 “needs higher AF gain” to decode 4FSKLower discriminator level into the next stage
AT1846S bandwidth25 kHz12.5 kHz, forced for digitalWider noise bandwidth and deviation scaling set for ±5 kHz FM
HR-C6000 0x340x3C, “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.

04590135180IMBE frames decoded, noise-free (of 180)no audio filtering180 / 1803 kHz low-pass only180 / 180de-emphasis only0 / 180de-emphasis + 3 kHz low-pass (0x34=0x3C)0 / 180
De-emphasis, −6 dB per octave from 300 Hz, leaves no frames on its own. No part of this review mentions 0x34, the AT1846S filter set or the low-frequency bit.
Data table
Audio stageNoise-free frames · NIDsSymbol errors20 dB frames · NIDs
no audio filtering180/180 · 20/200.0%180/180 · 20/20
3 kHz low-pass only180/180 · 20/200.0%180/180 · 20/20
de-emphasis only0/180 · 0/2051.3%0/180 · 0/20
de-emphasis + 3 kHz low-pass (0x34=0x3C)0/180 · 0/2049.8%0/180 · 0/20
Claim check

26 claims, checked one by one

Sixteen hold, including every quotation from the demodulator and the bring-up log. Five are overstated, miscounted or miscited. Five are wrong, and three of those shape the plan: the CPU percentage, the chip’s modem and the resampler.

ItemWhat DeepSeek V4 Pro claimsWhereResultNotes
ScopeThe host suite passes: “PASS all native P25 regression tests”, selftest 111111, 77 PASS linestests/p25/run.pyMiscountedThe suite passes and the selftest line is right; the log has 78 PASS lines, not 77.
§1The HR-C6000 has “no documented raw 4FSK/C4FM modem mode, no continuous-stream discriminator output capability”manual characteristics sectionWrongManual §5.4.6 and Table 5.6 document one-layer mode: the chip demodulates 4FSK itself and stores 36 bytes per frame in receive RAM from 0x30, which is 4,800 symbols per second, P25 Phase 1’s own rate. Whether it emits frames without DMR sync is untested, not absent.
§1The manual documents I2S for external-codec or vocoder PCM exchange, and the FM receive path ends at Audio_out with no discriminator route to I2Smanual §4.5, §4.7, §4.8, §6Holds
§1Stock V02.03 uses 0xE0=0xC9 in helpers that drive the speaker audio path, and the 0xE0 bit semantics are community reverse-engineeringanalysis/stock-p25/REPORT.mdHoldsCredited to the stock report.
§2The sampled stream is most likely microphone or LineIn1 audio rather than RF discriminator audio, the firmware tags every line source=UNVERIFIED, and a stable clock is not the right signalp25mon.c:519, 648, 733HoldsThe central question, stated clearly.
§2“The manual, the stock firmware analysis, and the P25-HARDWARE-REVIEW.md all agree” on the microphone readingP25-HARDWARE-REVIEW.mdWrong fileThat document isn’t in the tree. README.md and CAPABILITY-REPORT.md link to it, but the file was never committed, as DeepSeek V4.1 Flash noticed in its own clean-room run.
§1The fix is the M17-Project tap, AT1846S pin 9 to an MCU pin, leaving the C6000 for speaker audioanalysis/stock-p25/REPORT.md:175–176; CAPABILITY-REPORT.mdHoldsCredited, and the most likely real answer if the capture turns out to be the microphone.
§3Phase 2 uses H-CPM/H-DQPSK, two-slot TDMA with 30 ms slots and AMBE+2 3600×2450, and every layer of the Phase 1 decoder is Phase 1 specific; mbelib has the AMBE+2 decoder but it isn’t integratedp25rx.c:105; mbelib/ambe3600x2450.cHoldsAccurate, though it never gives Phase 2’s 6,000 symbols per second, the number that rules the chip’s modem out.
§4The DMR slot engine gates the I2S clock (about 56% duty at 8 kHz, 54% at 24 kHz), the sync detector locks onto any 4FSK carrier and re-arms it, and only rxPowerSavingSetLevel(0) fully disables the eco power-cycleSTAGE3-BRINGUP.md:250, 274; p25mon.c:650–652HoldsA faithful summary of the bring-up log.
§4hrc6000Task’s register writes race the monitor because SPI0 is locked and the IRQ handler wins, leaving silent failuresinterfaces/spi.c:53–73; p25mon.c:622–623Holds
§4Suspend hrc6000Task while the monitor is active and feed its AliveCount from the monitor tickSTAGE3-BRINGUP.md:359–363HoldsThe bring-up notes’ own next step; still not implemented.
§5.1Linear interpolation “has no stopband rejection” and is “suitable only for validation, not production”; replace it with a polyphase FIR resamplerp25_resample.cWrongRerun: standard C4FM band-limited to 24 kHz through the firmware’s own p25_resample decodes exactly the frames the direct 38.4 kHz path does, with equal or fewer symbol errors from 11 to 16 dB, and its images sit 41 dB down. The resampler costs about 2% of the CPU and no symbols.
§5.1The suite exercises a 24 kHz → 38.4 kHz → demodulator round trip and passes at 20, 12 and 8 dB SNR on an ideal RRC-shaped signaltests/p25/run.py; p25_selftest.c:344–347HoldsThe cases are 20, 12 and 8 dB, with the 8 dB case informational. The generator shapes with the receiver’s own RRC, which the review notes as “ideal”.
§5.2The demodulator uses sqrtf per phase candidate, floorf and fmodf for fractional interpolation, and three decision-directed level-fit iterations, which is expensive on a 72 MHz M4Fp25_4fsk.c:162, 235, 245HoldsThe float usage is exactly as described. Emulated, the filter, timing and fit together take 39–58% of the CPU, so the observation is real but secondary.
§5.3Timing recovery uses ad hoc gains (first = next_sample + 0.5·delta, period += 0.001·delta) with no PI design and no out-of-lock detectionp25_4fsk.c:236–241HoldsQuoted exactly, including the ±1% period clamp around it. New: no other audit read the loop.
§5.4There is no frequency tracking: the demodulator takes one DC estimate per window, so drift during a 180 ms LDU shifts the slicer levelsp25_4fsk.c:187–210HoldsNew, and a fair engineering point, though unquantified.
§7STAGE4 measured the IMBE encoder at 4.36M instructions per 20 ms frame, “~60% of the 72 MHz budget”STAGE4-TX-FEASIBILITY.md:36WrongThe figure is right, the percentage is not: 72 MHz gives 1.44M instructions per 20 ms, so 4.36M is 303% of the budget. Its own citation says the vocoder can’t fit, and the arithmetic error is what let it call the budget “marginal”.
§7The mbelib decoder is “at least as expensive” as the encoder, so the per-20 ms voice budget is marginal at 72 MHz§7UnderstatedThe direction is right and it is the only audit to reach for a measured vocoder number. Emulated, IMBE synthesis needs 11–16× the 72 MHz CPU: 134–458 ms per voiced 20 ms frame, which is a wall, not a margin.
§7Moving to 168 MHz (the OpenRTX precedent on this hardware) is the single largest CPU improvement available, about 2.3× headroom§7 resolutionOverstated2.3× does not close an 11–16× gap, and the demodulator work it pairs this with saves under 1×. The order is backwards: the vocoder is where the time goes.
§8SRAM and CCM are both near their limits, with the capture hardening pushing the link to about zero free bytes; p25monRx is about 3.7 KB and p25monWin about 1.8 KBCAPABILITY-REPORT.md; analysis/capture-milestone-REPORT.md; p25rx.hMiscountedSRAM really is at 0 bytes free and the window buffer is 1.8 KB, but p25rx_t compiles to 4,136 bytes, not 3.7 KB. The numbers come from the docs rather than the map.
§9A sync-detector re-lock between the 5 s refreshes leaves a window of gated clockingp25mon.c:642–656, 1115–1120Holds
§10Working correctly: BCH(63,16,23) NID decode, Golay, RS over GF(64), the trellis decoder and CRCs, the IMBE interleave tables matching DSD, sync detection with two-error tolerance, and the parser state machinep25_nid.c; p25_fec.c; p25_tables.h; p25rx.cHoldsThe interleave tables do match DSD, and the parser geometry checks out.
§10“Hamming(15,11) FEC: correct single-error correction”p25_fec.c:29–44WrongThe firmware implements Hamming(10,6,3) for P25 signalling, as its own encode and decode show. Hamming(15,11) is DMR’s code.
§10The capture module is working correctly: ISR-safe queue, gap preservation, backpressurep25mon_capture.cIncompleteTrue of the module, but its caller stages the first 80 interleaved halfwords instead of the decoder’s left-channel samples, so the export is wrong in exactly the experiment this review makes step one. Four other audits found it.
NID“The NID decoder is fed only 44 of the needed 64 wire bits … this is the single most important code bug found”p25rx.c NID parserWrongThe parser packs 32 of the 33 dibits and skips only the status symbol, so the full 64-bit codeword reaches the BCH decoder. The review reaches the same conclusion four pages later and retracts it, but both the claim and the retraction ship in the delivered file.
PlanRun the RF-versus-microphone acceptance procedure first, then implement the direct ADC tap, then raise the clock, upgrade the resampler, add AFC, convert to fixed point and redesign timing recoveryRecommended action planOverstatedThe first two steps are right. The next five spend the project’s effort on DSP polish the evidence doesn’t call for, while the inherited de-emphasis, the broken capture export and the vocoder go unaddressed.
Provenance

What was already known, and what it added

Every clean-room audit could see only the repository: its notes, code comments and the HR-C6000 manual, summarised in the first column. DeepSeek V4 Pro raises few of these rows, and adds the last one: the demodulator’s control loops, which no earlier audit had opened.

IssueProject docs
in the repository
Grok 4.6
Sep 17
DeepSeek V4.1 Flash
Sep 17
Muse Spark 1.3 Contributor
Sep 17
GLM 5.3 Flash
Sep 17
UNIONALPHA
Sep 17
Qwen3.8 Flash
Sep 17
Qwen3.8 Max
Sep 17
GLM 5.3
Sep 17
DeepSeek V4 Pro
Sep 17
Capture export stages the wrong I2S halfwordsabsentraised
§1, first code change
absent
its first experiment uses that capture
absent
trusts the capture
absent
relies on the capture
raised
F3, fixed and verified
raised
P1-2, tests miss the adapter
absent
calls the capture validated
raised
§2, proven with a reproducer
absent
calls the capture module sound
Capture parser certifies an incomplete streamabsentabsentabsentabsent
trusts the parser
absent
relies on the parser
absentabsentabsent
relies on the parser
absentabsent
pcm_starve never incrementsabsent
documented as working
absentabsentabsent
relies on it
absentraised
F7
raised
P1-7
absentraised
§2
absent
No static RAM marginpartly
margins still to measure
absentraised
M6, 0 bytes free
partly
“nearly full”, from the docs
raised
F5, byte-exact map audit
raised
F6, byte-exact
partly
calls the reservation headroom
absentpartly
“~zero headroom”
partly
from the docs, not the map
The I2S stream is most likely microphone audiopartly
open, leaning sceptical
raised
§1, 0x89 versus 0xC9
raised
B1, four-value test
raised
P1-1
partly
open, leans towards RF
partly
blocking, but never says microphone
raised
P1-4, 0xE0 mic bit
raised
A, the clearest case yet
raised
§1, three sources
raised
§1–§2, with the tap as the fix
HR-C6000 de-emphasis on the capture path missedabsentraised
§2, bit 5 of 0x34
absent
calls it benign
raised
P1-2, 0x34=0x3C
raised
F2, closes the eye
partly
cited, called unmeasured
raised
P1-3, 0x34=0x1C
raised
C, with the 0x34 bit 5 fix
raised
§1, tilts the eye
absent
AT1846S FM filters, low-frequency bit, 25 kHz missedpartly
“require characterization”
raised
§2, register level
raised
B2, filter register
raised
P1-2, 0x58 filters
partly
“voice filtering”; wrong bandwidth premise
partly
cited, called unmeasured
raised
P1-3, DMR 0x58 probe
absentabsentabsent
0x10=0x6E hybrid state; 0x36 dual rolepartly
bring-up clock rules
partly
misses 0x6E and the 0x36 clock gate
partly
“undocumented hybrid state”
partly
quiet-chip registers
raised
F4, 0x10=0x80 kills the clock
partly
F1, 0x6E against 0x80
partly
“hybrid I2S state”
partly
slot engine and eco only
partly
§4.3, 0x10=0x80 hazard
partly
“inconsistent hybrid state”
Manual: I2S frame clock “must be 8KHz” missedabsentraised
§3
absent
quotes the paragraph, not the rule
absentabsentraised
F4
absent
quotes the formulas, not the rule
absent
cites the section, not the rule
absentabsent
One-layer 4FSK test mode as a P25 tappartly
stock BER-test block only
raised
Gate D
raised
B5, exact recipe
absent
dismissed
absentpartly
worth a bounded test
absent
ruled out at “9600 Bd”
raised
step 2, a symbol source
absent
“not a P25 symbol source”
absent
“no raw modem mode”
±10% health gate versus ±1% timing clampabsentabsentraised
B3, impact overstated
raised
P1-3
raised
F3
absentabsentabsentabsentabsent
Fail-closed muting at LDU cadenceabsentabsent
late-entry mute only
absentabsent
calls it an asset
absentabsentpartly
“keep it”
absentabsent
calls it tested
absent
Non-standard MFID mutes clear callsabsentabsentabsentabsentabsentabsentabsentabsentabsentabsent
Test waveform shares the receiver’s RRC filterpartly
“synthetic RRC/AWGN” caveat
absentpartly
tested it, says not to fix
partly
synthetic only, wants recordings
absent
would extend that model
raised
F5, unquantified
absent
would extend that model
partly
synthetic only, not the circularity
partly
synthetic only
partly
“ideal RRC-shaped signal”
MCU runs at 72 MHzraisedraised
in passing
absentraised
P1-5
absentraised
F7
absentabsentraised
§5
raised
§7, with the OpenRTX precedent
Vocoder needs 11–16× the 72 MHz CPU missedpartly
decode timing unmeasured
absent
“fine on a 1 ms tick”
absent
“vocoder question settled”
partly
unmeasured; fix order backwards
absent
“in good shape”
partly
deadlines “unproven”
absent
“not the problem”
absent
“not the problem”
partly
inline, unmeasured
partly
cites 4.36M, calls it 60%
Direct discriminator tap (M17 mod)raisedraised
uncredited
raised
pins, timer ADC, 48 kS/s
raised
fallback, pin 9
raised
fallback
raised
fallback
raised
fallback
raised
step 5, the likely answer
raised
§1 pivot
raised
its central recommendation
Phase 2 architecture and scopepartly
not implemented
partly
misplaces the AMBE+2 decoder
partly
voice via mbelib AMBE+2
raised
with RF band limits
partly
says mbelib has no AMBE+2
raised
most accurate section
partly
DMR and X2-TDMA parts
raised
AMBE+2 present but uncalled
raised
with a reference map
partly
no symbol rate or sync
Two unverified SPI writes per decoded 20 ms frameabsentabsentabsentabsentabsent
treats them as protection
absentabsentabsentabsentabsent
Capture sessions lack epochsabsentabsentpartly
measured=0 only
absentabsentraised
F9, 8 kHz under a 24 kHz header
absentabsentabsentabsent
Ring and tick real-time budgetpartly
deadlines unproven
partly
calls it fine
absentpartly
overruns look like weak RF
absentraised
F7, 1 ms is a minimum
partly
register stalls against the ring
absentpartly
1 ms tick as a constraint
absent
Clock config 3 assumes 12,288 Hz; the codec formula gives 12,000absentabsentraised
B3, clock model
absentpartly
“guessed semantics”
absentraised
P1-6, for a different reason
absentpartly
“unvalidated on hardware”
absent
Clock-config writes bypass the verified SPI writerpartly
SPI retry note
absentraised
M1
absentabsentraised
F9
absentabsentraised
§4.2
absent
Stock squelch re-arms FM audio (0x10=0x80) during monitoringabsent
assumes it can’t re-arm
absentabsentabsentpartly
names squelch logic as a risk
raised
F1, new
absent
“fixed” by forcing squelch open
partly
names the squelch path as a writer
partly
hazard flagged, “unlikely”
absent
Stale clear-call state releases a new call’s first framesabsentabsentabsentabsentabsentraised
F8, probe
absentabsentabsentabsent
No frequency tracking; ad-hoc timing loop gainspartly
a code comment calls the DC estimate biased
absentabsentabsentabsentabsentabsentabsentabsentraised
§4, new
Unknown talkgroup opens audio (fail-open gating)absentabsentabsentabsentabsentabsentabsentabsentabsentabsent

Project docs: README.md, CAPABILITY-REPORT.md, STAGE3-BRINGUP.md, STAGE4-TX-FEASIBILITY.md, analysis/stock-p25/REPORT.md, analysis/capture-milestone-REPORT.md, dm1701-p25-demo/README.md and source comments. The clock-config rows were first raised by DeepSeek V4.1 Flash, the next two by UNIONALPHA, and the last row is new in this audit.

Its plan

What to keep, change and add

Its eight steps open with the two that matter and then turn into a DSP wish list. Nothing in the evidence asks for a new resampler or a new timing detector; the things the evidence does ask for are missing.

Keep

Worth doing as written

  • The carrier-keyed acceptance test first, before any more code is written on an unproven source.
  • The M17 discriminator tap if the capture turns out to be the microphone, with the C6000 kept for speaker audio.
  • Suspend hrc6000Task while the monitor owns the codec, fed by the monitor tick.
Change

Would cause new problems

  • “Move to 168 MHz” as the largest CPU win: 2.3× doesn’t close an 11–16× gap. Rework the vocoder first.
  • The polyphase resampler: the linear one costs no frames and about 2% of the CPU.
  • Fixed-point conversion and a Gardner loop: real engineering, wrong order. The filter and timing together are under 60% of the CPU; the vocoder is 11–16×.
  • Ruling out the chip’s modem: one-layer mode is documented and untried, at exactly P25 Phase 1’s symbol rate.
Add

Missing from its plan

  • Clear 0x34 bit 5 and load the AT1846S DMR register set before any capture is trusted.
  • Fix the capture export and the parser, or step one measures the wrong signal.
  • A real-time vocoder: wrapped per-harmonic phase and a cosine table in mbelib.

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.

RegisterValueManual description
0x01xxxx 0000IF receive mode
0x07 0x08 0x090x0B 0xD9 0x5424-bit IF word, 455 kHz by default (OpenGD77 boots at 450 kHz)
0x100x02One-layer mode, continuous receive; bit 5 for slot receive
0x400x40Receive enable, test mode
0x410x41Receive 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

Correct, and thin

Its Phase 2 section is four paragraphs: H-CPM and H-DQPSK rather than C4FM, two-slot TDMA with 30 ms slots, AMBE+2 3600×2450 rather than IMBE, and every layer of the Phase 1 decoder specific to Phase 1. All true, and it correctly notes mbelib carries the AMBE+2 decoder without it being wired in.

What it leaves out is the number that decides the architecture: Phase 2 runs at 6,000 symbols per second, which is why the chip’s 4,800-baud modem cannot track it even in the one-layer mode this review believes does not exist. It also doesn’t mention the 20-symbol sync, the descrambler keyed by WACN, System ID and NAC, or that the radio already runs a licensed AMBE+2 decoder for DMR in real time.

Against the other clean-room audits

The fastest run, and the thinnest coverage

DeepSeek V4 Pro finished in eight minutes, against five hours for Qwen3.8 Max and three for GLM 5.3, and it reaches the same pivot they do: the source is probably the microphone, so tap the discriminator. It is also the only audit whose delivered file contains its own draft and reasoning, and the only one to find the repository’s vocoder measurement and then misread it.

GLM 5.3 clean-room review, graded C− (65) →

Scores by dimension

Same rubric and weights. Hover or focus a bar for the score.

  • GLM 5.3
  • Qwen3.8 Max
  • DeepSeek V4.1 Flash
  • DeepSeek V4 Pro
0255075100Score out of 100Accuracy & evidence68707360Coverage of decode problems52394933Root cause & prioritisation74716664Fix plan & acceptance gates68717060Originality & attribution70667462Clarity & calibration72787448Weighted total65636653
Method and limits

How this was checked

I read DEEPSEEKV4PRO_REVIEW.md in full, including the draft and reasoning it ships ahead of the final review, and checked each claim against the firmware sources, the OpenGD77 drivers, the translated HR-C6000 manual and the repository’s notes. Where it quoted code I compared the lines; where it gave numbers I recomputed them, including compiling the parser state structure and re-deriving the CPU budget from its own citation.

Before grading I audited the run: one model for all 16 steps, no network, no skills or memory, and nothing read outside its copy of the repository. It ran as an interactive session on the Alibaba Token Plan, with the same prompt, tools and sandbox as every other run. The resampler, de-emphasis and CPU results come from the model and emulator used for the Grok 4.6 audit.

  • Filter responses are modelled, not measured.
  • CPU figures come from instruction counts in an emulator, not timing on the radio.
  • Interactive runs write no usage file; its run facts come from the session history.
  • No firmware was flashed, and no radio was touched.
python3 -c "print(72e6*0.02)"
# 1,440,000 instructions per 20 ms at 72 MHz — its citation is 4.36M

gcc -I…/p25 -o sz sz.c && ./sz
# sizeof(p25rx_t) = 4136 bytes, not ~3.7 KB

DM1701_REPO=/path/to/DM-1701 python3 resampler_check.py
# 24 kHz through p25_resample decodes the same frames as 38.4 kHz direct

Scripts: resampler_check.py, deemph_check.py, c4fm_sim.py and metrics.json. They need numpy and gcc.