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

DeepSeek V4.1 Flash’s P25 review, graded

DeepSeek V4.1 Flash got the same brief as UNIONALPHA: 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. I checked its claims against the code, the HR-C6000 manual and its own evidence files, and reran its executed tests.

Overall grade
C−
68 / 100 weighted

Right about the chip, wrong about the CPU.

DeepSeek goes where UNIONALPHA didn’t. It makes the HR-C6000 path the central question and flips the burden of proof: the I2S stream the decoder reads is most likely microphone audio. It finds the monitor leaves the chip in an undocumented hybrid state with de-emphasis switched on. On its own, that setting leaves zero decodable voice frames in my model. It also proposes the best experiment in either review: the chip’s documented one-layer 4FSK test mode, which hands demodulated bits to the MCU over SPI.

The trouble is precision. It calls the mbelib vocoder “not the blocker” and marks the sample-drain budget “verified adequate”, yet emulated instruction counts put IMBE synthesis at 11–16 times the radio’s 72 MHz CPU. Neither of its executed MCU findings holds as stated, and 5 of the 30 claims I checked are wrong. That leaves it three points ahead of UNIONALPHA, not a letter grade.

Claims that check out
17 / 30
8 overstated, miscounted or doubtful · 5 wrong
Decode-critical issues found
3 of 4
misses the vocoder CPU wall
Ideas no earlier review had
4
one-layer tap · hybrid state · 8 kHz limit · executed knee
Voice frames with de-emphasis on
0 / 180
3 kHz low-pass alone: 180 / 180 (model)
Headline findings

Six things to know about this review

Holds up

It puts the chip path at the centre, and it’s right to

The MCU reads the HR-C6000’s C_SDO/I2S_TX pin. The manual gives that pin two personalities, vocoder PCM and CodecADC voice, neither of them discriminator audio. OpenGD77 reads the same slot and calls it “Mic Audio”. Where UNIONALPHA called the route unproven “either direction”, DeepSeek flips the presumption, adds the manual’s “must be 8KHz” frame-clock limit, and names a falsification test.

manual 496–506, 1217–1221 · main.c:464 · sound.c:444 (it cites i2s.c:444)

Validated

Its de-emphasis finding is decisive

The monitor never writes HR-C6000 register 0x34, so it inherits 0x3C from the FM receive setup: de-emphasis plus a 3 kHz low-pass. DeepSeek calls that plausibly destructive. My model makes it concrete: through the firmware’s demodulator, de-emphasis alone leaves 0 of 180 frames, while the 3 kHz low-pass alone costs nothing.

HR-C6000.c:2846–2854 · manual 2803–2808 · model: de-emphasis −6 dB/oct from 300 Hz

New idea

It found a documented bit-level path nobody had tried

Manual §5.4.6: in one-layer test mode the HR-C6000 demodulates 4FSK and leaves 36-byte frames in RX RAM at 0x30 for the MCU to read over SPI. That bypasses the codec, I2S, de-emphasis and the resampler. 36 bytes every 30 ms is exactly 4,800 symbols per second, so contiguous frames would carry every P25 symbol. Whether the mode waits for DMR sync is the open question, and the review says so.

manual 2553–2635, Table 5.6 · 1455–1461

Critical error

It clears the vocoder that cannot run in real time

The executive verdict calls the mbelib path “the correct architecture and not the blocker”, and its evidence marks the drain budget “verified adequate” because 128 samples per 1 ms tick is a 5× margin. That tick is a minimum, not a guarantee. Emulated counts put mbelib at 11–16 times the 72 MHz CPU, about 430 ms of CPU for a loud 20 ms frame, which overruns the 21 ms sample ring on every frame.

DEEPSEEK41_REVIEW.md §0 item 1 · MCU_CHAIN_FINDINGS.md N6 · mbelib.c:306

Overstated

Its executed MCU findings don’t hold as stated

B4: the “lost first LDU with zero impairments” comes from starting the synthetic stream exactly on the frame sync. Add a 40-dibit lead-in and its own harness decodes 36/36. B3: the clock-error knee is real, but the radio’s 24 kHz clock is a crystal-derived integer divide measured at exactly 24,000 Hz, so it is not the defect to fix first. Its ±0.5% gate fix would flap on 1 Hz counts quantised to ±0.67%.

adv_chain.py rerun · STAGE3-BRINGUP.md (300/300 reports at 24,000 Hz) · p25mon.c:1151–1156

Careless

Several factual slips a reviewer should have caught

DSD does skip the non-standard c0 pattern (p25p1_ldu.c:126–141). The Phase 2 sync is 20 symbols, not 12. The “1,674 references” into the codec blob are mostly the blob’s own lines; 27 lines actually reference it. “Mic Audio” is cited in the wrong file, and “108+ PASS” doesn’t match the 78 PASS lines in its own log.

ref-sources/dsd-master · ref-sources/dsd-fme dsd.h:1027 · stock-v0203.dis · run_py.log

Scorecard

How the 68 breaks down

Same rubric, weights and scale as the UNIONALPHA grade, so the two scores compare directly. DeepSeek gains on coverage, root cause and originality, and gives most of it back on accuracy.

Dimension scores

Hover or focus a bar for the reasoning. Weighted total 68, grade C−.

0255075100Score out of 100Accuracy & evidenceweight 30%68Coverage of decode problemsweight 25%66Root cause & prioritisationweight 15%64Fix plan & acceptance gatesweight 15%74Originality & attributionweight 10%70Clarity & calibrationweight 5%68
Rubric table
DimensionWeightScorePointsWhy
Accuracy & evidence30%6820.4Most citations and its reading of the manual hold, but 5 of 30 checked claims are wrong, including that mbelib is not a blocker and that the drain budget is adequate, and its “executed” first-LDU loss is a harness artifact.
Coverage of decode problems25%6616.5Finds the microphone route, the de-emphasis hybrid state and the 8 kHz frame-clock limit that UNIONALPHA missed. Misses the vocoder CPU wall, the circular RRC test oracle and, in its final synthesis, the AT1846S filter set.
Root cause & prioritisation15%649.6Rightly makes the chip path the central question and flips the burden of proof to the microphone. Then says to fix an unreachable clock-error knee first, and clears the vocoder that cannot run in real time.
Fix plan & acceptance gates15%7411.1The one-layer 4FSK experiment, a scoped I2S test with mic excitation and the M17 tap with pins are concrete and decisive. Its ±0.5% rate gate, reset suppression and restoring 0x10=0x80 would each create new failures.
Originality & attribution10%707.0Credits UNIONALPHA and the Sep 8 review and adds genuinely new work: one-layer mode, the register-level hybrid state, the 8 kHz limit. Omits the Sep 10 review, which already had two of the deltas it claims.
Clarity & calibration5%683.4Clear certainty labels and a falsification test, undercut by overclaims: “by anyone, ever”, a “confirmed defect that silently destroys voice”, “verified adequate”.
Weighted total100%68Same rubric and scale as the UNIONALPHA grade: 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’s findings reach the radio front end and the chip configuration, where UNIONALPHA’s didn’t. The dashed amber route is its proposed one-layer path: the HR-C6000 demodulates the 4FSK itself and the MCU reads bits over SPI. What it missed sits on the MCU side and in the AT1846S.

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 ADC0x10=0x6E hybrid · 0x34 de-emphasisE0 = 0xC9 (mic)I2S to the MCULRCK 24 kHz (manual: 8 kHz)left slot, 16-bitHR-C6000 4FSK demodulatorone-layer test mode → RX RAM 0x30RFaudio?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 amplifierSPI RX-RAM reader36 B per 30 ms = 4,800 sym/sproposed, not builtCapture export ‘c’ISR stages [j][0..79]→ USB → p25_capture.pydiagnostic tapdemodulated bits over SPIdibits straight to p25rxB2B1B2B7B3B6B8B9B9B5M1M2M3M4
B1 raised by DeepSeekM1 missed by DeepSeekpath its proposed one-layer routeGrey dashed edge: no documented connection
B1I2S source is most likely the microphone
M1AT1846S FM filter set, low-frequency bit, 25 kHz (evidence file only)
B2Hybrid 0x10=0x6E state, inherited 0x34 de-emphasis, 8 kHz frame-clock limit
M2IMBE vocoder needs 11–16× the 72 MHz CPU; it calls mbelib “not the blocker”
B3±10% gate versus ±1% clamp (overstated impact)
M3RRC test waveform matches the receiver filter; its own tests reuse it
B5Capture export stages the wrong halfwords
M4Two SPI writes per decoded voice frame
B6Memory: CCM nearly full, SRAM reserved
B7One-layer 4FSK test mode as a bit-level tap
B8Fail-closed muting at the LDU cadence
B9pcm_starve never increments; MFID mutes clear calls
The chip evidence, checked

Its strongest ground, and the part it left out

DeepSeek’s register-by-register reading holds up against the code and the manual. The one gap is on the other chip: the AT1846S settings appear in its evidence file but never reach the review or the plan. They matter for its own one-layer experiment, because on this board the HR-C6000’s 4FSK demodulator is fed by the AT1846S.

What the P25 monitor leaves in place

Final register state against the stock analog-FM path and OpenGD77’s DMR receive path. The first five rows are DeepSeek’s B2; the last is from its evidence file.

RegisterMonitor todayStock FM RX (V02.03)OpenGD77 DMR RXWhat it means for P25
C6000 0x100x6E, DMR slot mode, behind an analog-FM front end0x80, FM mode0x6EUndocumented hybrid, kept because 0x80 stopped the codec clock during bring-up
C6000 0x340x3C, inherited: de-emphasis on, 3 kHz low-pass0x98: band-pass on, de-emphasis offnot writtenDe-emphasis alone leaves 0/180 frames in the model
C6000 0x360x00: feedthrough off, I2S clocks on0x02: feedthrough onnot writtenDual role; the manual documents only the clock gate
C6000 0xE00xC9: mic into LineIn10xC90xC9Same value as stock receive; consistent with microphone audio
C6000 0x40 / 0x410x00 / 0x00: slot engine offnot written0xC3 / 0x50Keeps I2S clocking continuous; forfeits the chip’s own 4FSK receive path
AT1846S 0x40, 0x58, bandwidth0x0030, 0xBC85, 25 kHz: filters on, low-frequency audio offnot analysed0x0031, 0x9CDD, 12.5 kHzMissing from the review and its plan; needed for any P25 route, including one-layer mode
Sources: p25mon.c:642–656, 724, 810 · HR-C6000.c:2846–2878, 2967–2978 · AT1846S.c:191–217 · analysis/stock-p25/REPORT.md §2–§5 · manual 1222–1223, 2803–2824.

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
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. The model can’t tell whether the stage sits on the I2S capture path; DeepSeek says the same.
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

The one-layer path, and what it would take

Manual Table 5.6 as DeepSeek quotes it (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.
  • Missing from its recipe: the AT1846S DMR register set, since the demodulator is fed by the AT1846S. And the vocoder still has to run in real time.
The MCU evidence, rerun

Two executed findings, one artifact, one missed wall

DeepSeek ran its own harness against the production sources, which neither earlier review did. I reran it unchanged except for one variable: whether the synthetic stream starts exactly on the first frame sync, or 40 dibits before it, as the repository’s own timing test does.

Frames decoded versus codec rate error

Its adv_chain.py harness: RRC/AWGN channel at 20 dB, decimated at the true codec rate, then the firmware’s 32-sample streaming resampler, 768/512 demodulator and framer. 4 LDUs, 36 frames possible.

  • Its harness: stream starts on the frame sync
  • Same harness with a 40-dibit lead-in
  • The radio’s measured clock
RADIO: 24,000 Hz, ppm-level09182736-2.0%-1.5%-1.0%-0.5%0+0.5%+1.0%+1.5%+2.0%Codec sample-rate error against the 24 kHz nominalIMBE frames decoded (of 36)with lead-instarts on the sync
With a lead-in, every frame decodes within ±0.4% and the knee sits between 0.6% and 1%, so B3’s mechanism is real. Without it, the first LDU is lost at every rate, which is all B4’s “zero impairments” result shows. The radio’s clock error is ppm-level, hundreds of times inside the working range.
Data table
Codec errorStarts on the sync: frames · NIDs40-dibit lead-in: frames · NIDs
-2.0%0/36 · 0/40/36 · 0/4
-1.5%0/36 · 0/49/36 · 1/4
-1.0%0/36 · 0/49/36 · 1/4
-0.8%0/36 · 0/417/36 · 2/4
-0.6%9/36 · 1/427/36 · 3/4
-0.4%27/36 · 3/436/36 · 4/4
-0.2%27/36 · 3/436/36 · 4/4
+0.0%27/36 · 3/436/36 · 4/4
+0.2%27/36 · 3/436/36 · 4/4
+0.4%27/36 · 3/436/36 · 4/4
+0.6%9/36 · 1/427/36 · 3/4
+0.8%9/36 · 1/49/36 · 1/4
+1.0%0/36 · 0/40/36 · 0/4
+1.5%0/36 · 0/40/36 · 0/4
+2.0%0/36 · 0/40/36 · 0/4

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
Claim check

30 claims, checked one by one

Seventeen hold. Eight are overstated, miscounted, miscited or doubtful. Five are wrong, and two of those steer the reader away from a real blocker.

ItemWhat DeepSeek V4.1 Flash claimsWhereResultNotes
§1The HR-C6000 docks external vocoders (AMBE3000/1000, HR_V3000, SELP, AVDS) and has no IMBE, AMBE+2 or P25 blockmanual 43–49, 1181–1213HoldsThe manual never mentions P25, IMBE or DQPSK.
§1Digital voice on this radio is a DVSI AMBE+2 blob on the STM32 at 0x0807537Ccodec.h:38, 51; codec_interface.c:63, 90HoldsExact lines.
§1The stock disassembly has 1,674 references into 0x08075xxxstock-v0203.disMiscounted1,639 of the 1,666 matching lines are the blob’s own code; 27 lines reference it from outside. The conclusion stands.
ExecIMBE cannot be decoded “on the HR-C6000 — by anyone, ever”manualHoldsTrue of the die, though the wording is stronger than the point needs.
ExecThe MCU-side mbelib path “is the correct architecture and is not the blocker”p25rx.c:105WrongEmulated instruction counts put mbelib IMBE synthesis at 11–16× the 72 MHz CPU.
B1The MCU reads the C6000’s C_SDO/I2S_TX pin, whose two documented personalities carry vocoder PCM or CodecADC voicemanual 496–506; main.c:464HoldsThe clearest statement of the microphone evidence in any review of this tree.
B1ADCDAT is an input from an external codec; the built-in codec is mic in and line out; FM receive ends at the speaker DACmanual 337–341, 1108–1112, 2641–2650Holds
B1The stock consumer labels the same stream “Mic Audio”cited as interfaces/i2s.c:444Wrong fileThe comment is at functions/sound.c:444; i2s.c has 139 lines. B5 cites it correctly.
B2The final chip state is a hybrid: 0x10=0x6E (DMR) with an analog-FM front end, 0x34=0x3C inherited, 0x36=0x00p25mon.c:642–656, 732–734, 810; HR-C6000.c:2846–2854, 2976–2977HoldsNew at register level.
B2The inherited 0x34=0x3C enables de-emphasis and a 3 kHz low-pass, plausibly destructive for C4FMmanual 2803–2808, 2822–2824HoldsModel: de-emphasis alone leaves 0/180 frames; the 3 kHz low-pass alone is harmless.
B20x36[6]=0 keeps the I2S master clocks on, in tension with the codebase’s “FM feedthrough off” meaningmanual 1222–1223; HR-C6000.c:2971, 2976Holds
B2The manual says the I2S frame clock “must be 8KHz”; the 24 kHz divider math is otherwise legalmanual 1217–1221HoldsMissed by UNIONALPHA.
B3The resampler uses the nominal rate, the tracker clamps at ±1% and the health gate admits ±10%p25mon.c:574–577; p25_4fsk.c:240–241HoldsSame as UNIONALPHA’s C2.
B3Executed: around 1% codec error gives zero framesadv_chain.py; adv_clock_bisect.logHoldsRerun with a lead-in: full decode within ±0.4%, collapse by +1%.
B3So drift, a mis-latched LRCK-only config or an unchanged codec cause silent loss that looks healthy; fix this firstp25mon.c:83, 150OverstatedThe 24 kHz clock is a crystal-derived integer divide, measured at exactly 24,000 Hz, so drift is ppm-level. 8 kHz and LRCK-only configs fall below the 12 kHz decode gate and are rejected, not accepted.
B4Executed: LDU #1 is lost even with zero impairments, so every reset costs at least 180 msadv_chain.log A1ArtifactThe harness starts the stream exactly on the frame sync. With a 40-dibit lead-in the same code decodes 36/36 frames.
B5The capture stages the first 40 L/R pairs of each row, and the second half of every row is missingp25mon.c:462–467HoldsAlready described in the Sep 10 review.
B6CCM has 1,160 bytes free; .bss ends at 0x2001F9FC; defaultTask stack is 8,192 bytesMDUV380_FW.elf; main.c:74–78HoldsIts evidence table lists the 0x600 heap and stack reservation as “free”, which it is not.
B7One-layer 4FSK mode stores 36-byte demodulated frames at RX RAM 0x30 for SPI reads; Table 5.6 gives the register recipemanual 2553–2635, 1455–1461HoldsDocumented and untried. The frame geometry is DMR-shaped, as the review says.
B8One failed signalling decode demotes a clear call and mutes audio until the next LDU2 or HDUp25rx.c:42–49, 177–181, 261–265, 274–277Holds
B9A non-standard-MFID group LC never establishes a clear callp25rx.c:159–163Holds
B9The non-standard c0 skip is project-local; DSD has no equivalentp25rx.c:91–104; dsd_mbe.c:94WrongDSD skips the identical 23-bit pattern in p25p1_ldu.c:126–141.
B9No end-to-end voice test exists, so a future table or mbelib-layout regression would pass every framing testp25_selftest.c:442–448OverstatedThe suite rebuilds payload dibits through DSD’s independent interleave tables, so a table regression fails. Only audio-level regressions slip through.
B10Phase 2 grants are not followed because 0x33 invalidates TDMA identifiersp25_trunk.c:10–26Holds
B10Phase 2 has a 12-symbol syncPHASE2_FINDINGS.mdWrongThe Phase 2 sync is 20 symbols (DSD-FME P25P2_SYNC).
B10H-DQPSK needs coherent IQ and is not discriminator-compatibleB10; PHASE2_FINDINGS.mdOverstatedDSD-FME, bundled in ref-sources, carries Phase 2 CQPSK options for FM-demodulated input. Harder than C4FM, not impossible.
B10Use the vendored mbelib AMBE+2 decoder for Phase 2 voice rather than the DVSI blobmbelib/ambe3600x2450.cDoubtfulmbelib’s AMBE+2 path shares the synthesis routine that puts IMBE at 11–16× the CPU; the DVSI decoder already runs DMR voice in real time on this MCU.
N6Drain budget “verified adequate”: 128 samples per 1 ms tick is a 5× marginMCU_CHAIN_FINDINGS.md N6WrongThe 1 ms loop is a minimum period. One mbelib frame takes 134–458 ms of CPU, far past the 21 ms ring.
§4The host suite gives “108+ PASS”, exit 0run_py.logMiscountedIts own log has 78 PASS lines out of 104; exit 0 is right.
§5New in this review: the 0x34 de-emphasis discovery and the half-row capture lossDeltas sectionOverstatedBoth earlier reviews raised C6000 de-emphasis, and the Sep 10 review described the half-row loss. The register-level 0x34 and 0x10 analysis is new.
Provenance

What was already known, and what it added

DeepSeek was handed UNIONALPHA’s findings as a baseline and says so, which is the right way to build on earlier work. It doesn’t mention the Sep 10 review, which already had two of the deltas it claims. Its new material is real and chip-side.

IssueP25-HARDWARE-REVIEW
Sep 8
P25-PROJECT-REVIEW
Sep 10
UNIONALPHA
Sep 16
DeepSeek V4.1 Flash
Sep 16–17
Capture export stages the wrong I2S halfwordsabsentraised
F1
raised
F1
raised
B5
Capture parser certifies an incomplete streamabsentraised
F2
raised
F2
partly
in its plan
pcm_starve never incrementspartly
asks for underrun counts
raised
F3
raised
F3
raised
B9
No static RAM marginpartly
648 bytes then
raised
F4
raised
F4
raised
B6
The I2S stream is most likely microphone audioraised
cites sound.c
raisedpartly
“either direction”
raised
B1
HR-C6000 de-emphasis on the capture pathpartly
code comment
partly
code comment
absentraised
B2, register 0x34
AT1846S FM filters, low-frequency bit, 25 kHzraisedraisedabsentpartly
evidence file only
0x10=0x6E hybrid state; 0x36 dual roleabsentabsentabsentraised
new
Manual: I2S frame clock “must be 8KHz”absentabsentabsentraised
new
One-layer 4FSK test mode as a P25 tapabsentabsentabsentraised
new
±10% health gate versus ±1% timing clampabsentabsentraised
C2
raised
B3, executed
Fail-closed muting at LDU cadenceabsentpartly
late entry
absent
“keep fail-closed”
raised
B8
Non-standard MFID mutes clear callsabsentabsentabsentraised
B9
Test waveform shares the receiver’s RRC filter missedraisedraised
F6
absentabsent
reuses it
MCU runs at 72 MHzraisedraisedabsentpartly
Phase 2 gate only
Vocoder needs 11–16× the 72 MHz CPU missedabsentpartly
cycles per frame
partly
“to measure”
absent
“not the blocker”
Direct discriminator tap (M17 mod)raised
with pins
raisedpartly
no pins
raised
with pins
Phase 2 architecture and scopepartlypartlyraisedraised
with errors
Its plan

What to keep, change and add

DeepSeek’s ordering is sound: trust the instruments, settle the tap question with the documented path, then harden the MCU chain. Most of its steps survive checking; three would cause new problems, and two things any P25 route needs are missing.

Keep

Worth doing as written

  • Phase 0 instrument fixes: capture stride through the real DMA adapter, a strict parser, a live pcm_starve counter.
  • The one-layer experiment: Table 5.6 values, reads of RX RAM 0x30, 0x51 and interrupt bit 0. Gate: bits follow RF and not the microphone.
  • Scope the I2S pins with the speaker muted and the mic excited, and abandon the I2S route if samples follow the mic.
  • 0x34=0x00 as the first register test; the model says de-emphasis alone is fatal.
  • The M17 tap fallback with pins (AT1846S pin 9 to MCU pin 18), time-boxed.
  • Latch a clear call through one failed signalling decode, fix MFID handling, add a known-answer IMBE audio vector.
Change

Would cause new problems

  • B3’s ±0.5% gate: the rate report is quantised to ±0.67% per second, so this gate would flap and reset the stream. Keep the validated nominal ratio, average over tens of seconds, and report the deviation.
  • B4’s reset suppression: retunes, clock changes and overruns are real discontinuities. Count and show resets instead of joining unrelated samples.
  • Restoring 0x10=0x80 or trying 0x40=0x43: the bring-up log shows both stop or gate the codec clock. Change 0x34 alone first.
  • Phase 2 voice via mbelib: it shares the synthesis routine that is 11–16× too slow here. Assess the real-time DVSI decoder’s entry points and licensing first.
Add

Missing from its plan

  • The AT1846S DMR register set for any P25 session: 0x40 low-frequency bit, 0x58=0x9CDD, 0x44=0x07FF, 12.5 kHz. The one-layer path and the M17 tap need it as much as I2S does.
  • A real-time vocoder: a per-harmonic phase accumulator with a cosine table. Without it, even a perfect tap yields no audio.
  • Standard C4FM test vectors (raised cosine × inverse sinc) with de-emphasis and high-pass regressions, instead of the RRC model its own harness reuses.
  • Set the audio path once at start, not on every decoded 20 ms frame.
Phase 2

Right on architecture, wrong on the details that decide the route

DeepSeek’s central Phase 2 points hold. The HR-C6000 decodes neither IMBE nor AMBE+2, its TDMA engine is DMR-shaped and runs at 4,800 rather than 6,000 symbols per second, and p25_trunk.c:26 means Phase 2 grants are never followed. Its work breakdown, 2–4 thousand lines plus test vectors, is a reasonable order of magnitude.

Three details are off. The Phase 2 sync is 20 symbols, not 12. “Coherent IQ only” is too strong: DSD-FME, bundled in ref-sources/, carries Phase 2 CQPSK options for FM-demodulated input, at a cost in margin. And its preferred voice route, the vendored mbelib AMBE+2 decoder, uses the same synthesis routine that puts IMBE at 11–16 times this CPU. The licensed DVSI decoder already decodes AMBE+2 in real time on this radio for DMR. Its entry points and licensing are fair open questions, but it is the route that could keep up.

Against UNIONALPHA

More useful, less careful

UNIONALPHA is the more careful review: almost every claim holds, but it spends them on tooling. DeepSeek finds the chip-side problems UNIONALPHA missed and proposes the best experiment either offers, but makes more mistakes, and one of them clears a real blocker. Neither found the vocoder CPU wall or the circular RRC test oracle.

UNIONALPHA review, graded C− (65) →

Scores by dimension

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

  • UNIONALPHA
  • DeepSeek V4.1 Flash
0255075100Score out of 100Accuracy & evidence9068Coverage of decode problems4566Root cause & prioritisation5564Fix plan & acceptance gates7474Originality & attribution4070Clarity & calibration7668Weighted total6568
Method and limits

How this was checked

I read DEEPSEEK41_REVIEW.md and its evidence in analysis/deepseek41-review/, then checked each claim against the firmware sources, the OpenGD77 radio drivers, the translated HR-C6000 manual, the stock-firmware analysis, DSD, DSD-FME and the earlier reviews. The review’s build had touched the P25 source timestamps; the contents were confirmed unchanged.

I reran its adv_chain.py harness unchanged, varying only the lead-in before the first frame sync. The de-emphasis check and CPU figures use the same firmware-in-the-loop model and emulator measurement as the UNIONALPHA grade.

  • The AT1846S and HR-C6000 filter responses are modelled, not measured. Whether de-emphasis sits on the I2S capture path is unknown.
  • The one-layer mode is documented but untested here; nothing was flashed and no radio was touched.
  • CPU figures come from instruction counts in an emulator, not cycle-accurate timing on the radio.
DM1701_REPO=/path/to/DM-1701 python3 knee_rerun.py
# lead-in 0: at most 27/36 frames, even at 0% error
# lead-in 40: 36/36 within ±0.4%, 0/36 at +1.0%

DM1701_REPO=/path/to/DM-1701 python3 deemph_check.py
# 3 kHz low-pass alone 180/180 · de-emphasis alone 0/180

grep -n "non-standard c0" -A16 ref-sources/dsd-master/src/p25p1_ldu.c
grep -n P25P2_SYNC ref-sources/dsd-fme/include/dsd.h

Scripts: knee_rerun.py (its harness with a lead-in option), deemph_check.py, c4fm_sim.py and metrics.json. They need numpy and gcc.