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

Muse Spark 1.3 Contributor’s P25 review, graded

Muse Spark 1.3 Contributor 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 finished in six minutes. I checked its claims against the code, the manual and the repository’s notes, and used the firmware-in-the-loop model and CPU emulation from the earlier clean-room audits to test the ones that matter.

Overall grade
C−
68 / 100 weighted

Finds both halves of the filter problem. Points the CPU fix the wrong way.

Muse Spark 1.3 Contributor names both halves of the analog chain, as only Grok 4.6 does among the clean-room audits: the inherited 0x34=0x3C that leaves HR-C6000 de-emphasis on, and the AT1846S voice filters. Its fix turns de-emphasis off while keeping the widest low-pass, the safe register change. It also puts the microphone question first, finds the ±10% gate against the ±1% timing clamp, and keeps CPU on the list as unmeasured, with the sharp observation that an overrun would look like poor RF sensitivity.

Its CPU advice runs the wrong way: raise the clock, trim the filter, “only then touch the decoder”, when emulated counts put mbelib at 11–16 times the 72 MHz CPU and the filter under one. It trusts instruments that don’t work, misses the manual’s 8 kHz rule and dismisses the one-layer route. 22 of 30 claims hold. That puts it at C−, between Grok 4.6 and DeepSeek V4.1 Flash on the same clean-room protocol.

Claims that check out
22 / 30
6 overstated or doubtful · 2 wrong
Decode-critical issues found
2 of 4
CPU flagged, not found; misses the 8 kHz rule
Voice frames with de-emphasis on
0 / 180
the 0x34 setting it names, in my model
CPU the vocoder needs
11–16×
of 72 MHz; 168 MHz adds 2.3×
Headline findings

Six things to know about this review

Holds up

It finds both halves of the analog chain

Analog mode runs HRC6000SetFMRx, which writes 0x34=0x3C: de-emphasis on with a 3 kHz low-pass, never cleared by the monitor. The AT1846S FM table adds its own high- and low-pass filters, which the DMR table switches off. It names both. Its fix is the careful one: de-emphasis off, compressor off, widest low-pass, AT1846S filters bypassed, then measure the transfer function. In my model de-emphasis alone leaves 0 of 180 frames, while the 3 kHz low-pass costs none.

HR-C6000.c:2851 · AT1846S.c:191–217 · manual 2798–2824 · model: deemph_check.py

Holds up

It keeps the right questions first

It leans on the stock analysis to say the I2S stream most likely carries the microphone, and makes the carrier-keyed test the first step, with the M17 tap as the fallback. It finds the ±10% decode gate against a ±1% timing clamp. On CPU it doesn’t clear anything: it estimates about 4 million filter multiply-adds per second, says the real budget is unmeasured, and notes that an overrun resets the stream, so a CPU shortfall would look like poor RF sensitivity. That last point is exactly right.

analysis/stock-p25/REPORT.md:58–61 · STAGE3-BRINGUP.md:153–176 · p25mon.c:575–580 · p25_4fsk.c:240–241

Critical error

Its CPU fix points away from the vocoder

If CPU is tight, it says: raise the clock toward 168 MHz, shrink the filter, and “only then touch the decoder”. Emulated instruction counts put the filter, timing search and level fit at 39–58% of the 72 MHz CPU, and mbelib’s IMBE synthesis at 11–16 times the whole CPU. 168 MHz buys 2.3×, and trimming the filter saves less than one CPU. The decoder, with its unwrapped harmonic phase driving newlib’s slow cosf path, is the part that has to change first.

MUSESPARK13_REVIEW.md P1-5 · mbelib.c:306 · model: Unicorn emulation of the firmware’s own build

Major gap

It trusts instruments that don’t work

Its first step is the capture acceptance procedure, but the capture stages the first 80 interleaved halfwords rather than the decoder’s samples (158 of 160 positions differ), and the parser certifies streams missing their first or last chunk. It expects pcm_starve to reveal audio underruns; that counter is never incremented. And it calls fail-closed muting an asset, though one failed signalling decode mutes a clear call for up to 9 frames.

p25mon.c:123, 443, 460–466, 517 · tools/p25_capture.py:104–222 · p25rx.c:159–181

Overstated

It closes the one-layer door too early

It rejects the HR-C6000’s one-layer BER-test mode because the frames are DMR-structured and “the samples never reach the MCU as a continuous symbol stream”. The DMR framing is a real risk. But the manual documents continuous one-layer reception, and 36 bytes every 30 ms is exactly 4,800 symbols per second. Whether the mode needs DMR sync is untested, and it is the cheapest experiment on the chip.

manual 2552–2637 · analysis/stock-p25/REPORT.md:77, 88

Careless

The manual rule it walked past

It cites the manual’s I2S and LRCK divider formulas but not the sentence beside them: the I2S frame clock “must be 8KHz”, against the monitor’s 24 kHz. It also says C4FM needs a flat response to about 6 kHz with “no 3 kHz brick wall”, though C4FM sits below 2.88 kHz. And it lists trellis and CRC among the codes “cross-checked against independent reference encoders”; the trellis test re-implements the production table.

manual 1126–1133, 1217–1219 · tests/p25/signalling.py:84

Scorecard

How the 68 breaks down

Same rubric, weights and scale as every P25 audit on bench, so scores compare directly. Muse Spark 1.3 Contributor ties Grok 4.6 on accuracy and leads the clean-room audits on root cause and clarity, but covers less ground than Grok.

Dimension scores

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

0255075100Score out of 100Accuracy & evidenceweight 30%74Coverage of decode problemsweight 25%54Root cause & prioritisationweight 15%72Fix plan & acceptance gatesweight 15%71Originality & attributionweight 10%70Clarity & calibrationweight 5%80
Rubric table
DimensionWeightScorePointsWhy
Accuracy & evidence30%7422.222 of 30 claims hold, and the few line citations it gives are exact (0x34=0x3C, the gate and clamp, the TDMA rejection). Two are wrong, one of them misdirecting: it would raise the clock and trim the FIR before touching the 11–16× vocoder.
Coverage of decode problems25%5413.5Finds both halves of the analog chain (0x34 de-emphasis and the AT1846S filters), as only Grok 4.6 also does, as well as the microphone route, the gate/clamp mismatch and the M17 tap, and it flags CPU as unmeasured. It misses the 8 kHz rule, dismisses one-layer mode, and trusts the capture, the parser, pcm_starve and fail-closed muting.
Root cause & prioritisation15%7210.8Puts the microphone question and the flattened analog path first, and keeps CPU on the list as unmeasured rather than clearing it. Held back by a CPU fix order that points away from the vocoder and by dismissing the one-layer route.
Fix plan & acceptance gates15%7110.7Ordered steps with measurable checks: carrier-keyed captures, a four-level histogram, a transfer function, real recordings as regression vectors, and 0x34 changed without narrowing the low-pass. It starts with a capture that stages the wrong samples and would steer the resampler from a quantised rate estimate.
Originality & attribution10%707.0Adds the 0x34 de-emphasis and AT1846S register culprits, the gate/clamp arithmetic, a FIR cost estimate and the overrun-as-poor-sensitivity diagnosis, and credits the stock report, bring-up notes and Stage-4 note throughout. The M17 tap, simulcast scope and band limits restate the repository.
Clarity & calibration5%804.0Concise and well calibrated: blocker, bug, qualification gap and unmeasured are kept apart, and scope limits are marked “do not mistake for bugs”. A few overreaches remain: “cannot be made to”, “no 3 kHz brick wall”, “all cross-checked against independent reference encoders”.
Weighted total100%68Grade 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

Muse Spark 1.3 Contributor’s findings cover the analog front end on both chips, the microphone input, the sample-rate gate and the overrun behaviour of the sample ring. Its misses sit in the instruments it trusts, the 8 kHz clock rule, the call-muting policy and the vocoder, where its CPU advice points the other way.

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 tapF1F2F2F3F5N1N2N3N4N5N6
F1 raised by Muse Spark 1.3 ContributorN1 missed or misdirectedDashed edge: no documented connection
F1I2S most likely carries the microphone; settle it first (P1-1)
N1Manual: the I2S frame clock must be 8 kHz
F2HR-C6000 0x34 de-emphasis and the AT1846S voice filters (P1-2)
N2mbelib needs 11–16× the CPU; its fix order starts with the clock and the filter
F3±10% decode gate versus ±1% timing clamp (P1-3)
N3The capture stages the wrong halfwords
F5Overruns reset the stream and look like weak RF (P1-5)
N4The parser certifies incomplete captures
N5pcm_starve never counts; two unverified SPI writes per frame
N6Fail-closed and non-standard MFID muting
The front end, checked

Both chips, and the right register change

Muse Spark 1.3 Contributor reads the AT1846S and HR-C6000 settings correctly and proposes the change that removes the damage without adding new risk: de-emphasis off, the widest low-pass kept. The model shows why the de-emphasis bit is the one that matters.

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
The 3 kHz low-pass is harmless because C4FM sits below 2.88 kHz, so “no 3 kHz brick wall” overstates it. De-emphasis, −6 dB per octave from 300 Hz, is fatal on its own, and that is the bit Muse Spark 1.3 Contributor tells you to clear.
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 CPU budget

Where the time actually goes

Muse Spark 1.3 Contributor estimates the filter cost correctly and calls the rest unmeasured. The emulated budget answers the question it left open, and shows why its order of fixes would not help.

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

Twenty-two hold, one of them with a gap that matters. Six are overstated or doubtful. Two are wrong, and one of those would send CPU work to the clock and the filter instead of the vocoder.

ItemWhat Muse Spark 1.3 Contributor claimsWhereResultNotes
Bottom lineThe HR-C6000 has no P25 mode, C4FM slicer, P25 FEC or IMBE/AMBE+2 vocoder; vocoders attach externallymanual 43–49, 1181–1200Holds
§1The one-layer BER-test mode “does not solve P25”: frames stay DMR-structured and “the samples never reach the MCU as a continuous symbol stream”; the chip “cannot be made to” helpmanual 2552–2637OverstatedThe DMR frame layout is a real risk, but the manual documents continuous one-layer reception at 36 bytes per 30 ms, which is the full 4,800 symbols per second. Whether it needs DMR sync is untested, not settled.
§1Stock firmware has a BER-test block with 0x10=0x02, 0x40=0xA0, 0x41=0x01analysis/stock-p25/REPORT.md:77, 88HoldsCorrectly attributed to the stock firmware, not to Table 5.6.
§1The Stage-4 TX note finds no raw-4FSK transmit modeSTAGE4-TX-FEASIBILITY.md:14–17Holds
P1-1The stock 0xE0 table shows 0xC9 selects “CPU Controls Codec, Line in 1” and 0x8B a different input; the bit meanings are community reverse-engineering, and LineIn1 is the microphone pathanalysis/stock-p25/REPORT.md:58–61; HR-C6000.c:2964Holds
P1-1The firmware itself reports source=UNVERIFIED; STAGE3’s carrier-keyed RF-versus-mic test comes first, and the fallback is the M17 tap from AT1846S pin 9 to an MCU ADCp25mon.c:519; STAGE3-BRINGUP.md:153–176; REPORT.md:175–176HoldsCredited to the repository’s notes.
P1-1Run the capture acceptance procedure (c plus tools/p25_capture.py) before any other P25 workp25mon.c; tools/p25_capture.pyIncompleteThe right first step, but the capture stages the first 80 interleaved halfwords instead of the decoder’s [j][i*2] samples, and the parser certifies streams missing their first or last chunk. Both need fixing before the test means anything.
P1-2HRC6000SetFMRx programs 0x34=0x3C, de-emphasis on with a 3 kHz low-pass, and p25mon.c never writes 0x34HR-C6000.c:2851HoldsExact line. In my model de-emphasis alone leaves 0 of 180 frames.
P1-2The AT1846S FM table turns on high- and low-pass filters through 0x58; the DMR table disables themAT1846S.c:191–217Holds
P1-2C4FM needs a flat response to about 6 kHz and “no 3 kHz brick wall”P1-2OverstatedC4FM occupies less than 2.88 kHz: the 3 kHz low-pass alone costs no frames in my model. De-emphasis is the stage that erases the eye.
P1-2Fix: program 0x34 with de-emphasis off, compressor off and the widest low-pass, bypass the AT1846S voice filters, then measure the transfer function with a known C4FM sourceP1-2, §5 step 2HoldsThe safe register change: keeping the 3 kHz choice avoids the narrower 2.55 kHz filter.
P1-3Decoding runs whenever the rate is within ±10% of nominal, the resampler uses the nominal ratio, and the timing loop clamps the symbol period to 7.92–8.08 samplesp25mon.c:575–577, 1066; p25_4fsk.c:240–241HoldsExact lines.
P1-3A 5% rate error slips a symbol every 20 symbols and decodes as garbage instead of reporting a clock faultP1-3HoldsTrue, with low practical impact: the default 24 kHz clock is an exact crystal divide measured at 24,000 Hz.
P1-3Fix: tighten the gate to ±1–2% or, better, drive the resampler from a continuously estimated input rateP1-3DoubtfulA ±2% gate is fine, but the 1 Hz rate report is quantised to about ±0.67%, and the code notes that telemetry is not a symbol-clock estimate (p25mon.c:396–398). Steering the ratio from it adds error to a rate that is already exact.
P1-4Validation is RRC plus AWGN, symbol-clock drift to ±1,000 ppm and carrier offsets to ±400 Hz: synthetic onlyp25_selftest.c:5–11, 266–268HoldsIt doesn’t say the generator uses the receiver’s own RRC, which hides about 2 dB against standard C4FM.
P1-4CQPSK/LSM simulcast isn’t addressed; a scalar discriminator path can’t handle itP1-4HoldsCAPABILITY-REPORT.md:30 says the same.
P1-5The 81-tap Q12 FIR costs about 55,000 multiply-adds per 768-sample window at 75 windows per second, about 4 M per second at 72 MHz; the DWT counter exists but nothing has been measuredMakefile:49; p25mon.c:97, 535HoldsArithmetic right.
P1-5The demo sets SystemCoreClock = 72000000dm1701-p25-demo/src/main.c:34, 90Holds
P1-5Overruns reset the stream, so a CPU shortfall shows up as repeated sync loss that looks like poor RF sensitivityp25mon.c:578–580HoldsA sharp and correct diagnosis of how the CPU wall would present.
P1-5If CPU is tight: raise the clock toward 168 MHz, then shrink the FIR, and “only then touch the decoder”P1-5 fixWrongBackwards. Emulated, mbelib IMBE synthesis needs 11–16× the 72 MHz CPU while the FIR, timing and level fit take 39–58%. 168 MHz is 2.3× and trimming the FIR saves under 1×; the decoder is where the time goes.
P1-6Playback upsamples mbelib’s 8 kHz PCM to the codec rate by sample hold, with a phase accumulator against the nominal ratep25mon.c:394–422Holds
P1-6The pcm_drop and pcm_starve counters will show whether the audio ring is sized rightp25mon.c:517Wrongpcm_starve is declared and printed but never incremented, so an underrunning ring reads as healthy.
P1-7Documented scope limits, not bugs: late entry mutes until signalling validates; single-block signalling; scan camps after two BCH-valid NIDs with a 600 ms dwell and −95 dBm thresholdSTAGE3-BRINGUP.md:46–47, 121–130; p25mon.c:70–71Holds
§3No Phase 2 demodulator or TDMA framing; the trunk follower invalidates TDMA identifiers; the AMBE half-rate sources are vendored but never called, and p25rx.c calls only the IMBE decoderp25_trunk.c:26; p25rx.c:105; mbelib/ambe3600x2450.cHoldsExact lines.
§3“Wiring the half-rate decoder in is the easy part”§3Doubtfulmbelib’s AMBE+2 path shares the synthesis routine that puts IMBE at 11–16× the CPU; the radio’s DVSI decoder already runs AMBE+2 in real time.
§3The hardware covers 136–174 and 400–470 MHz, so 700/800 MHz Phase 2 systems are out of reach regardless of firmwareCAPABILITY-REPORT.md:16; STAGE3-BRINGUP.md:42–44HoldsRestates the capability report.
§4NID BCH: all 65,536 data words verified, multi-bit patterns to 11 errors, status dibit at wire position 35 handledtests/p25/run.py:117, 125; p25rx.cHolds
§4The Golay search’s i ^ (i>>1) is the correct inverse of its ctz-stepped Gray enumerationp25_fec.c:65–70HoldsA verified non-issue, stated precisely.
§4Hamming, Golay, Reed–Solomon, trellis and CRC16/32 are all cross-checked against independent reference encoderstests/p25/signalling.pyOverstatedGolay, Hamming and RS use DSD’s reference classes, but the trellis test re-implements the production transition table (signalling.py:84), and trunk tests live in trunk.py.
§4Fail-closed call-state discipline and the capture-export module are assets to build on, not to revisit§4OverstatedOne failed signalling decode mutes a clear call for up to 9 frames, a non-standard MFID never plays, the capture stages the wrong halfwords and the parser certifies incomplete streams.
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. All three find the microphone route and the AT1846S filters, and all three miss the vocoder CPU wall. Muse Spark 1.3 Contributor shares the de-emphasis finding with Grok 4.6 and the gate mismatch with DeepSeek V4.1 Flash, but alone among them dismisses one-layer mode.

IssueProject docs
in the repository
Grok 4.6
Sep 17
DeepSeek V4.1 Flash
Sep 17
Muse Spark 1.3 Contributor
Sep 17
Capture export stages the wrong I2S halfwordsabsentraised
§1, first code change
absent
its first experiment uses that capture
absent
trusts the capture
Capture parser certifies an incomplete streamabsentabsentabsentabsent
trusts the parser
pcm_starve never incrementsabsent
documented as working
absentabsentabsent
relies on it
No static RAM marginpartly
margins still to measure
absentraised
M6, 0 bytes free
partly
“nearly full”, from the docs
The I2S stream is most likely microphone audiopartly
open, leaning sceptical
raised
§1, 0x89 versus 0xC9
raised
B1, four-value test
raised
P1-1
HR-C6000 de-emphasis on the capture pathabsentraised
§2, bit 5 of 0x34
absent
calls it benign
raised
P1-2, 0x34=0x3C
AT1846S FM filters, low-frequency bit, 25 kHzpartly
“require characterization”
raised
§2, register level
raised
B2, filter register
raised
P1-2, 0x58 filters
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
Manual: I2S frame clock “must be 8KHz” missedabsentraised
§3
absent
quotes the paragraph, not the rule
absent
One-layer 4FSK test mode as a P25 tappartly
stock BER-test block only
raised
Gate D
raised
B5, exact recipe
absent
dismissed
±10% health gate versus ±1% timing clampabsentabsentraised
B3, impact overstated
raised
P1-3
Fail-closed muting at LDU cadenceabsentabsent
late-entry mute only
absentabsent
calls it an asset
Non-standard MFID mutes clear callsabsentabsentabsentabsent
Test waveform shares the receiver’s RRC filterpartly
“synthetic RRC/AWGN” caveat
absentpartly
tested it, says not to fix
partly
synthetic only, wants recordings
MCU runs at 72 MHzraisedraised
in passing
absentraised
P1-5
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
Direct discriminator tap (M17 mod)raisedraised
uncredited
raised
pins, timer ADC, 48 kS/s
raised
fallback, pin 9
Phase 2 architecture and scopepartly
not implemented
partly
misplaces the AMBE+2 decoder
partly
voice via mbelib AMBE+2
raised
with RF band limits
Two unverified SPI writes per decoded 20 ms frameabsentabsentabsentabsent
Capture sessions lack epochsabsentabsentpartly
measured=0 only
absent
Ring and tick real-time budgetpartly
deadlines unproven
partly
calls it fine
absentpartly
overruns look like weak RF
Clock config 3 assumes 12,288 Hz; the codec formula gives 12,000absentabsentraised
B3, clock model
absent
Clock-config writes bypass the verified SPI writerpartly
SPI retry note
absentraised
M1
absent

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 last two rows were first raised by DeepSeek V4.1 Flash.

Its plan

What to keep, change and add

Muse Spark 1.3 Contributor’s resolution order is sound for the RF side: settle the sample source, flatten the analog path, fix the gate, then graduate to real recordings. It needs working instruments before step one, and its CPU step needs turning around.

Keep

Worth doing as written

  • Settle the sample source first: carrier-keyed captures against mic excitation, then decide stock I2S or a hardware tap.
  • Flatten the analog path: 0x34 de-emphasis off, compressor off, widest low-pass; AT1846S filters bypassed; measure the transfer function with a known C4FM source.
  • Real single-site C4FM recordings as regression vectors, with simulcast explicitly out of scope.
  • Clear-voice acceptance before Phase 2, limited to the bands the radio covers.
Change

Would cause new problems

  • The CPU order: rework mbelib’s synthesis first (a wrapped per-harmonic phase and a cosine table), then measure the worst voiced frame. 168 MHz is headroom, not a fix.
  • A resampler steered by the measured rate: the 1 Hz report is quantised to about ±0.67% and the 24 kHz clock is exact. A ±2% gate is fine; keep the nominal ratio.
  • “Fail-closed discipline is an asset”: latch an established clear call through one failed decode, and handle non-standard MFIDs.
  • Half-rate AMBE via mbelib: it shares the slow synthesis routine; assess the DVSI decoder the radio already runs.
Add

Missing from its plan

  • Working instruments before step one: stage the decoder’s [j][i*2] samples, a strict capture parser, a live pcm_starve counter.
  • The one-layer probe: one register sequence and an RX RAM read decide whether the chip will hand over 4,800 symbols per second.
  • The manual’s 8 kHz rule in the step-one evidence: 24 kHz runs, but outside the documented operating point.
  • Standard C4FM test vectors (raised cosine × inverse sinc): today’s generator shares the receiver’s RRC and hides about 2 dB.

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

The most practical Phase 2 section yet, with one slip

Its Phase 2 inventory is specific and correct: no 6,000 sym/s H-DQPSK demodulator, no slot sync or SACCH/FACCH, TDMA grants discarded on purpose (p25_trunk.c:26), half-rate AMBE sources vendored but never called (p25rx.c:105 calls only IMBE). It adds the point the other audits skipped: this radio covers 136–174 and 400–470 MHz, so the many Phase 2 systems on 700/800 MHz are out of reach whatever the firmware does.

The slip is calling the vocoder “the easy part”. mbelib’s AMBE+2 decoder shares the synthesis routine that puts IMBE at 11–16 times this CPU, while the radio already runs DVSI’s AMBE+2 decoder for DMR in real time. That decoder’s entry points and licensing are the real vocoder question.

Against the other clean-room audits

Four audits, four different blind spots

None of the four clean-room audits finds the vocoder CPU wall. Grok 4.6 adds de-emphasis, the 8 kHz rule and the capture bug; DeepSeek V4.1 Flash adds the clock model but clears de-emphasis; Muse Spark 1.3 Contributor adds de-emphasis and the gate mismatch, flags CPU as unmeasured, and has the best-calibrated prose of the three, but trusts the broken instruments and points its CPU fix at the wrong stage. GLM 5.3 Flash audits memory to the byte but misreads the receive path.

Grok 4.6 clean-room review, graded C (70) →

Scores by dimension

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

  • Grok 4.6
  • DeepSeek V4.1 Flash
  • GLM 5.3 Flash
  • Muse Spark 1.3 Contributor
0255075100Score out of 100Accuracy & evidence74736474Coverage of decode problems59493454Root cause & prioritisation70666272Fix plan & acceptance gates75706271Originality & attribution72745870Clarity & calibration78746880Weighted total70665668
Method and limits

How this was checked

I read MUSESPARK13_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 and the repository’s notes. Before grading I audited the run: one model for all 27 calls, no network, no skills or memory, and nothing read outside its copy of the repository. The contributor tier was used with the user’s explicit acceptance.

The review executed only the host suite. The de-emphasis, low-pass and CPU results come from the firmware-in-the-loop model and emulator used for the Grok 4.6 audit.

  • Filter responses are modelled, not measured; which 0x34 bits select the 2.55 kHz filter is not documented.
  • 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 deemph_check.py
# 3 kHz low-pass alone 180/180 · de-emphasis alone 0/180

DM1701_REPO=/path/to/DM-1701 python3 resampler_check.py
# 3 kHz low-pass costs no frames · 2.55 kHz loses frames at 13 dB

grep -n "pcm_starve\|p25monAudioUnderruns" opengd77-rt3s-experiments/MDUV380_firmware/application/source/p25/p25mon.c

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