Announcements
Project news, milestones, and community updates.
-
The $4 Million Side Project: What GopherTrunk Would Cost Without AI
We audited every line of GopherTrunk — 425k lines of code, 2.7 million words of docs — and priced what it would cost a human team to build. The answer says something bigger about what AI makes viable.
-
Welcome to the GopherTrunk blog
A new home for project updates, release notes, deep dives on protocols and DSP, and step-by-step tutorials.
Deep dives
Technical articles on protocols, DSP, and architecture.
-
DMR End to End, Part 12: Enhanced Privacy — RC4 & the IV That Names the Next Superframe
How GopherTrunk decrypts DMR Enhanced Privacy with an operator-held key: the Privacy Indicator header, the RC4 key‖MI keystream with its 256-byte warm-up, the 32-bit LFSR that advances the Message Indicator, the embedded IV that names the NEXT superframe, and the capture that settled it.
-
Beyond Voice, Part 12: M17 — The Open Digital Voice Link Layer
How GopherTrunk decodes M17's link layer without touching the voice: the 4FSK stream frame, the Golay-coded LICH that spreads a Link Setup Frame across six frames, base-40 callsigns, the CRC-16 with polynomial 0x5935 — and exactly where the decoder stops today, Codec 2 payload and web panel included.
-
DMR End to End, Part 11: AMBE+2 Voice & the Silence-Frame Bug
How a frame-by-frame diff against two mbelib lineages refuted the 'AMBE+2 2450 high-band deficit' theory, found the real DMR voice bug in the silence frames that reset the gain predictor 24 dB low at every utterance onset, and calibrated the unvoiced band and radio tilt against dsd-neo — measured, not tuned by ear.
-
Beyond Voice, Part 11: LoRa — A Pure-Go Chirp Receiver
How GopherTrunk decodes LoRa without Semtech silicon — chirp spread spectrum as cyclic shifts, the dechirp-and-fold FFT that reads a symbol as a bin, preamble and SFD sync that separates carrier offset from timing, the Gray, interleave, Hamming and whitening chain, LDRO, and LoRaWAN MIC verification and decryption with operator-held keys.
-
DMR End to End, Part 10: Wideband DMR — Bin Edges & the Deaf Heal
Two IPSC repeaters on one X310 channelizer, one tap deaf for minutes at a time — how the polyphase bin edge folded half a DMR channel back on itself, why the fix was a 2x-oversampled channelizer, and how the wideband engine now heals a receiver deaf at its own decoding level and rejects a coarse offset decode evidence contradicts.
-
Beyond Voice, Part 10: DSC — Marine Digital Selective Calling
How GopherTrunk decodes DSC on marine channel 70 — 1200-baud FFSK at 1300/2100 Hz, ten-bit characters with a detect-only check code, the phasing hunt that locks a 20-bit DX grid, the format and category tables, MMSI and position codecs, and what stays unverified without a capture.
-
DMR End to End, Part 9: Direct Mode — The Gaps That Blinded the Receiver
Why a DMR handheld on a simplex frequency never decoded for the whole life of issue 836 — the 32.5 ms gaps between bursts fed receiver noise to every slow tracker — and the three fixes that make it decode from the first header: a variance carrier gate, feed-forward timing acquisition and an acquirer that keeps its lock.
-
Beyond Voice, Part 9: AIS — GMSK, NRZI & Bit Stuffing
How GopherTrunk decodes marine AIS in pure Go — a 9600-baud GMSK front end behind an FM discriminator, the NRZI line code where the bit is the transition, the HDLC framer borrowed from AX.25 with its bit-stuffing rule, six-bit ASCII, and the message types it really parses.
-
DMR End to End, Part 8: Tier III Trunking — C_ALOHA, Grants & LCNs
How GopherTrunk reads a DMR Tier III control channel — the 96-bit CSBK and the 0x5A5A CRC mask that once rejected every real one, locking on C_ALOHA, topology from C_BCAST, voice grants that carry a 12-bit LPCN instead of a frequency, vendor feature IDs, multi-block control, and the −56 dBFS WARN that contradicted live decodes.
-
Beyond Voice, Part 8: ADS-B — Mode S Squitters & CRC-24
How GopherTrunk decodes 1090 MHz aircraft transponders: a 2 Msps magnitude-domain preamble detector and PPM slicer, a CRC-24 that verifies extended squitters and doubles as the address on interrogation replies, the even/odd Compact Position Reporting pair, and a BEAST client that lets a running dump1090 feed the same aircraft table.
-
DMR End to End, Part 7: The Idle Beacon & the CSBK Train
What a keyed-but-idle IPSC repeater puts on the air, how GopherTrunk pinned the ETSI Idle burst against MMDVMHost's constant and turned it into a site-alive lock, how CSBK beacons are gated by CRC — and the cc=7 CSBK train on a cc=12 system that is still, honestly, unresolved.
-
Beyond Voice, Part 7: APRS, AX.25 & the Location Layer
How GopherTrunk turns 1200-baud Bell 202 tones into APRS positions: Mueller-Müller timing on the FFSK discriminator, NRZI, an HDLC framer that reverses bit-stuffing, an AX.25 parser with a reflected CRC-16, the Mic-E format that hides latitude in the destination address, and the location tables that feed the map.
-
DMR End to End, Part 6: Conventional IPSC — Two Slots as Two Calls
How GopherTrunk's conventional DMR state machine turned one IPSC repeater's two timeslots into two concurrent calls — a per-destination call map, a synthetic slot token the engine keys on, two same-carrier voice taps routed by embedded LC, and the re-key rule that recovered replies lost inside hangtime.
-
Beyond Voice, Part 6: POCSAG & FLEX — Paging Networks
How GopherTrunk decodes the two paging protocols that still carry fire, EMS and hospital dispatch: POCSAG's polarity-agnostic sync, BCH(31,21) codewords and frame-slot RIC arithmetic, the FLEX decoder that reuses the same BCH primitive bit-reversed for its one supported mode, and the wideband DDC bank that puts both on one dongle.
-
DMR End to End, Part 5: Link Control, Embedded LC & Late Entry
How a DMR call names itself three times over — the 72-bit Full Link Control in the Voice LC Header, the Terminator with LC, and the fragments embedded in voice bursts B–E — and how GopherTrunk turns the embedded copy into late entry, a re-key anchor, and a talker alias.
-
Beyond Voice, Part 5: Two-Tone Paging — Goertzel, Profiles & Tone-Out
How GopherTrunk's tone-out detector turns decoded PCM into fire-paging alerts: a block Goertzel resonator per configured tone, a per-device sequence matcher with duration, gap and cooldown gates, and the two places where this decoder deliberately skips the eleven-place pattern because it listens to audio, not RF.
-
DMR End to End, Part 4: The FEC Stack & the Forged Terminator
The error-correcting codes a DMR burst passes through in GopherTrunk — BPTC(196,96) with its stride-181 interleave, the (20,8,7) slot-type code, RS(12,9) with three context seeds, the two CRC masks whose convention hid a bug — and how a voice burst's AMBE bits once Golay-decoded to "Terminator with LC" and ended a live call mid-sentence.
-
Beyond Voice, Part 4: FleetSync — Cloning the Template & the WAV That Lied
How Kenwood FleetSync was cloned from the MDC1200 template — a preamble-plus-sync framer, a bit-serial block check, the FleetSync II single-error-correcting code — and how the reporter's two SDR# captures were decoded only after three traps fell: a WAV reader that read 32-bit float as int16, a baseband recording whose carrier sat 424.5 kHz off centre, and a carrier estimator that looked before the radio keyed up.
-
DMR End to End, Part 3: Two Slots, One Carrier — Repeater vs Simplex Cadence
How GopherTrunk follows a DMR voice superframe across six bursts when only the first carries a sync — the 132, 264 and 288-dibit cadences, embedded-LC and AMBE-FEC cadence detection, the relative Phase label separating two interleaved calls, and the simplex handheld whose empty slot a single-slot decoder sliced as voice.
-
Beyond Voice, Part 3: MDC1200 — The Template Decoder
The Motorola MDC1200 burst from sync word to web panel — the 40-bit sync hunted with five errors of tolerance and its complement for inverted polarity, the 16×7 column interleave, the reflected CRC-16, the opcode table, double packets, the receiver → bus → mdc1200_log → REST → panel chain later decoders were cloned from, and what its tests do not yet prove.
-
DMR End to End, Part 2: Bursts, Sync Words & Polarity
How GopherTrunk turns a raw DMR dibit stream into bursts — the 132-dibit layout with its central sync, the nine 48-bit sync words and the polarity flip that maps every data sync onto a voice sync, the Golay-protected slot type that routes each burst, and the one FEC-valid burst that fixes a stream's polarity.
-
Beyond Voice, Part 2: AFSK & FFSK — Two Tones, One Bit
How GopherTrunk turns an FM voice channel into a bit stream for MDC1200, FleetSync and APRS — the FM discriminator, the resample to 9600 Hz, demod.FFSK's mix-filter-discriminate tone detector, Mueller-Müller timing at eight samples per bit, and the slicer lesson that made FleetSync slice at a fixed zero while APRS tracks DC.
-
DMR End to End, Part 1: The 4FSK Carrier & the Tier Family
What a DMR carrier actually is — 4800 symbols per second of four-level FSK in a 12.5 kHz channel, dibits riding on ±648 and ±1944 Hz deviations, two 30 ms timeslots sharing one frequency, three tiers on one wire format, and the 48 kHz FM-discriminator chain GopherTrunk sizes around all of it.
-
Beyond Voice, Part 1: Why a Scanner Decodes Data — The Eleven-Place Pattern
Why a trunking scanner decodes in-band data at all, and the one wiring shape every non-voice decoder in GopherTrunk shares — front end, framer, bus event, SQLite log, REST route, web panel, config editors, doctor preflight and example config — eleven places and three policing tests, walked through FleetSync's landing.
-
P25 End to End, Part 14: The P25 Playbook
The finale — the whole P25 stack folded into one layer map from antenna to WAV: which symptom points at which layer, the instrument that confirms it, the failure signatures operators actually report, the twin-path ledger, and the honest list of what's still open.
-
From Spec to Shipping, Part 14: The Definition of Verified
The finale — closing an issue is a claim the problem is gone, and GopherTrunk defines exactly when that claim is earned. The issue-closing policy, the guard hook that asks a human before any close, Refs versus Closes, and the full shipping checklist from spec to verified.
-
P25 End to End, Part 13: Testing P25 Without a Tower
How GopherTrunk tests a P25 decoder with no transmitter in reach — literal byte vectors that catch what round-trips cannot, impairment-swept synthetic carriers with a hard CI loss budget, committed real-air capture fixtures, and rate-invariance tests that prove where a bug is not.
-
From Spec to Shipping, Part 13: Instruments, Not Logs
How GopherTrunk designs decoder diagnostics as instruments — counters on every branch including the failing ones, verdict lines written for operators, WARNs gated on persistence instead of one sampled blip, and health checks that never judge a channel by absolute dBFS.
-
P25 End to End, Part 12: The Weak-Signal Gap — P1 Voice's Missing Levers
Why P25 Phase 1 C4FM voice is GopherTrunk's least-equipped decode path — an FM-discriminator chain with no channel equalizer and hard-decision IMBE FEC, a measured ~24 dB gap to the coherent reference, and the weak-signal voice capture that gates the fix.
-
From Spec to Shipping, Part 12: Failing First — The Regression Rule
Why every GopherTrunk bug fix is one narrow commit plus a regression test that fails without the fix — with three worked examples where writing the failing test was the diagnosis, from the DMO colour-0 descramble to the SmartNet rebuild to the noise-grant trio.
-
P25 End to End, Part 11: Wideband P25 — Watching the Whole System at Once
One wide capture, many control channels — the tuner-bank architecture behind role wideband, per-call voice taps carved from the same IQ stream, and the twin down-converter paths (the wideband DDCBank vs the single-channel Downconverter) whose drift let the fix for issue 764 miss the replay symptom in issue 771.
-
From Spec to Shipping, Part 11: Capture-Driven Development
How GopherTrunk turns operator IQ recordings into first-class test fixtures — the samples/ conventions, .metadata.json sidecars whose bounds turn log lines into pass/fail gates, env-gated replay harnesses that skip in CI and become field instruments, and the discipline of baselining before fixing.
-
P25 End to End, Part 10: Sites, WACNs & Roaming a Multi-Site System
The P25 identity ladder — a 20-bit WACN, 12-bit System ID, RFSS and Site — and the status broadcasts that carry it in TSBK and multi-block AMBT forms. How GopherTrunk votes a system's identity out of noisy frames, accumulates a neighbour map, and roams by hunting control channels.
-
From Spec to Shipping, Part 10: The On-Air Gate — Green Synthetics Prove Nothing
Why every decoder claim in GopherTrunk carries a verification status — synthetic-green, capture-verified, or on-air-verified — and how the TETRA DMO "encrypted" misdiagnosis was overturned three times by three successive operator captures before the real causes surfaced.
-
P25 End to End, Part 9: Encryption Signalling — Flags, Metadata & Policy
Where a P25 call's encryption actually lives on the air — the grant's protected bit, the LDU2 Encryption Sync carrying the Message Indicator, Algorithm ID and Key ID, and Phase 2's MAC_PTT — and how GopherTrunk turns those signals into per-system tuner and recording policy.
-
From Spec to Shipping, Part 9: Wire Protocols Without Schemas
How GopherTrunk shipped a SoapyRemote client that asked for HAS_DC_OFFSET_MODE when it meant SET_ANTENNA — opcode 600 instead of 501 — and the rules that catch schemaless wire drift: pin constants against upstream literals, validate through introspection, read back what you set.
-
P25 End to End, Part 8: IMBE Voice — From LDU to WAV
How P25 Phase 1 voice travels — the 1728-bit LDU carrying nine IMBE frames woven between link-control words, the Golay and Hamming FEC that turns 144 channel bits into 88, and the composer chain in GopherTrunk that gates, decodes and records a call.
-
From Spec to Shipping, Part 8: Case Study — Rebuilding SmartNet From Proven Decoders
The full #1143 arc as a method case study: a Motorola SmartNet decoder green on every synthetic test yet unable to lock any real system, diagnosed as fabricated framing and rebuilt from OP25 and trunk-recorder — 8-bit sync 0xAC, stride-19 deinterleave, inverted data, CRC-10.
-
P25 End to End, Part 7: Phase 2 TDMA — Two Voices per Carrier
How P25 Phase 2 fits two voice calls in one 12.5 kHz channel — H-DQPSK at 6000 symbols per second, the 360 ms superframe and ISCH slot typing, MAC PDUs in place of TSBKs, and the FEC hand-off that silently defaulted to zero on one pipeline.
-
From Spec to Shipping, Part 7: Tests That Can Disagree With You
Four ways GopherTrunk engineers the self-consistent trap out of its test suites: fixture transmitters that behave like real radios, independent-path controls, fake servers that enforce the real protocol's strictness, and synthetic streams laid out on the real slot grid.
-
P25 End to End, Part 6: CQPSK & LSM — The Linear Twin Path
P25 Phase 1's second physical layer — the opt-in CQPSK/LSM demodulator GopherTrunk runs when an FM discriminator produces near-random dibits, its T/2 fractionally-spaced CMA equalizer and carrier-recovery chain, and the hard rule that simulcast does not imply LSM.
-
From Spec to Shipping, Part 6: When References Disagree, the Capture Referees
Two trusted decoders gave two different DMO burst geometries, and a single capture could not pin the colour-code bit offset at all. How GopherTrunk designs referee measurements where the right answer wins by a wide margin — and refuses to answer when it doesn't.
-
P25 End to End, Part 5: Channel Identifiers & Band Plans — From Channel Number to Hertz
How a P25 grant's 16-bit channel field becomes a frequency — the IDEN_UP band-plan broadcasts in their 0x3D, 0x34 and 0x33 dialects, the base-plus-spacing arithmetic in BandPlan.Frequency, and what happens when a grant arrives before the table that decodes it.
-
From Spec to Shipping, Part 5: Clean-Room Rules — Reading Without Copying
How a pure-Go, Apache-2.0 decoder uses GPL decoders as validation oracles without copying them: where the line sits between wire-format facts and copyrightable expression, the vocoder patent aisle, and the licensing hygiene that CI enforces on every build.
-
P25 End to End, Part 4: Multi-Block Trunking — The PDU That Isn't Noise
Why a PDU on a P25 control channel is Multi-Block Trunking, not garbage — the AMBT header and data blocks with their two different CRCs, the neighbour sites and WACN that only arrive this way, and why a "MBT data CRC failed" line next to a clean decode is two frames, not a contradiction.
-
From Spec to Shipping, Part 4: The Conformance Harness — Bit-Identical or Bust
Building a conformance harness against the ETSI EN 300 395-2 reference codec: one 137-bit bitstream into both decoders, zero PCM mismatches allowed, the LP64 Word32 gotcha that makes the reference itself lie, and why conformance at two layers brackets everything in between.
-
P25 End to End, Part 3: TSBKs — The Control Channel's Workhorse
Inside the P25 Trunking Signaling Block — 12 bytes through a 1/2-rate trellis and interleaver into 98 channel dibits, the CRC variant many references get wrong, the opcode families that run a trunked system, and the SCCB byte-offset bug a round-trip test blessed.
-
From Spec to Shipping, Part 3: Literal Vectors, Not Round-Trips
Why round-trip tests let parser bugs live — the P25 SCCB opcode that read channel B one byte early while its test stayed green — and the fix as method: pin every parser with literal byte vectors cross-checked against an independent decoder, kept as bytes in the test, never generated.
-
P25 End to End, Part 2: Frame Sync, the NID & What 'Locked' Means
How GopherTrunk turns a raw P25 dibit stream into frames — the 48-bit frame sync word, the BCH(63,16)-protected NID carrying NAC and DUID, the status symbols hiding every 36th dibit, and the tiered alignment search that decides when a control channel is really locked.
-
From Spec to Shipping, Part 2: Choosing Reference Implementations You Can Trust
The reference stable a protocol decoder is built against — OP25, trunk-recorder, SDRTrunk, osmo-tetra, the ETSI reference codec, mbelib — what each is authoritative for, why proven-on-air beats popular, and how to know what a reference does not decide.
-
P25 End to End, Part 1: C4FM & the Shape of a P25 Carrier
What a P25 Phase 1 carrier actually is — 4800 symbols per second of four-level FSK in a 12.5 kHz channel, dibits riding on ±600 and ±1800 Hz deviations, and the FM-discriminator → matched-filter → slicer chain GopherTrunk sizes around a 48 kHz channel rate.
-
From Spec to Shipping, Part 1: How to Read a Radio Standard
How ETSI EN and TIA-102 standards families are organized, the order a decoder author should read them — physical layer up to PDU layouts — and the constants to extract on day one: sync words, CRC definitions, and the exact bit offsets everything else leans on.
-
Weak-Signal Engineering, Part 14: The Weak-Signal Playbook
The finale — the whole method folded into one decision tree from symptom to lever: measure in-channel SNR and yield, classify the channel as ISI-limited, reliability-limited, fading-limited, or signal-limited, apply the matching lever, and hold every change to the failing-first, capture-A/B, yield-verdict discipline.
-
TETRA End to End, Part 14: Testing TETRA Without a Network — & What's Still Open
The finale — the four-layer test lattice that lets a TETRA stack be developed with no network in range, from synthetic round-trips through skip-guarded capture harnesses to full-daemon integration, and an unvarnished list of what is verified, what is staged, and what stays open until on-air evidence lands.
-
Weak-Signal Engineering, Part 13: The Odd Path Out — P25 Phase 1 C4FM
A diagnosis, not a fix — why P25 Phase 1 C4FM voice is the one decode path in GopherTrunk with neither an equalizer nor soft-decision FEC, what the Astro Spectra report showed, and the baseline harness waiting on the one thing that lets a fix land: a real weak-signal capture.
-
TETRA End to End, Part 13: DMO III — A Production Pipeline: Grid Votes, Grants & Noise
Wiring DMO into the daemon as a first-class protocol — streaming burst extraction, sticky locks, edge-triggered grants — and the first on-air run's humbling lesson: a loose correlator false-alarms eighteen times a second, and only a learned slot-grid vote separates a real transmission from an idle channel.
-
Weak-Signal Engineering, Part 12: Proving It's the Signal — Rate Invariance & Independent Resamplers
Experimental design as an engineering skill, with issue #764 as the worked example — how an independent 4:1 resampler proved a ten-dB decode deficit was baked into the captured samples, how clipping was ruled out, and how a unit test now pins the invariant the experiment relied on.
-
TETRA End to End, Part 12: DMO II — The 'Encrypted' Verdict That Was a Descramble Skip
The honest retelling of issue #1003: how a colour-0 descramble shortcut inherited from trunked mode made clear DMO voice look encrypted, why the synthetic round-trips couldn't catch it, and how the colour code the voice actually scrambles with turned out not to be the one the signalling advertises.
-
Weak-Signal Engineering, Part 11: Diversity II — Tracking Without Breaking the Differential
Why independent-PLL front ends make a frozen diversity calibration decay, and how TrackingCalibrator re-estimates the branch gain continuously yet stays safe ahead of a differential decoder — the anchored phase, the hold-don't-fallback rule, the step clamp, and the four-arm capture A/B.
-
TETRA End to End, Part 11: DMO I — Direct Mode & the DSB/DNB Geometry
TETRA without a network: how Direct Mode reuses the trunked physical layer with different burst layouts, how GopherTrunk derived the DSB and DNB block geometry from the spec's bit tables, and why one pair of offsets was confirmed by a sharp CRC optimum rather than trusted from another project.
-
Weak-Signal Engineering, Part 10: Diversity I — MRC & Coherence-Gated Calibration
Maximal-ratio combining from two receive branches, the wideband-scalar caveat that bounds what one complex gain can deliver, and the scale-invariant coherence gate — |rho| = γ/(1+γ), a noise floor you can compute, and the DC-removal detail that turns out to be load-bearing.
-
TETRA End to End, Part 10: The Control Channel Under Stress — Sync Loss & the CC Equalizer
Reading a one-hour field session's eleven hard control-channel sync losses: why the compute theory was wrong, how a signal-time resync budget survives CPU starvation, and the one TETRA control path that wasn't running the equalizer — lifting a marginal capture from twelve percent to full BSCH yield.
-
Weak-Signal Engineering, Part 9: Parallel Buffers — SymbolSink, SoftSink & Opt-In Soft Paths
The architecture pattern that let equalizers and soft-decision FEC land safely — LLR differentials and raw symbols carried strictly parallel to the hard dibit buffer, a stash bridge keyed by base index, and a byte-identical opt-out pinned by failing-first tests.
-
TETRA End to End, Part 9: The Equalizer on the Voice Path
How a blind CMA equalizer with frozen-snapshot taps, inserted between symbol timing and the differential decoder, roughly doubled CRC-valid TCH/S yield on real concurrent-load TETRA captures — and why the naive way of wiring it drives the decode to exactly zero.
-
Weak-Signal Engineering, Part 8: Soft Decisions — LLRs Through Depuncture & Viterbi
Why hard-slicing a marginal symbol throws away the information the FEC needs most, how GopherTrunk carries per-bit log-likelihood ratios through descramble, deinterleave, depuncture, and a correlation-metric Viterbi, and the measured TETRA outcome that justified the whole soft path.
-
TETRA End to End, Part 8: Going Soft — Soft-Decision TCH/S
Why hard-decision decoding threw away roughly seventy percent of a marginal same-carrier TETRA call's traffic bursts, and how the soft path — receiver differentials carried in lockstep through the traffic extractor into a soft Viterbi — recovers them without touching the hard contract.
-
Weak-Signal Engineering, Part 7: Trained Equalization — LMS on the Midamble
When the burst carries known symbols, stop guessing — how GopherTrunk trains a SnapshotLMS on each TETRA burst's midamble in the raw-symbol domain, freezes the taps, equalizes the payload with a FIR warm-up, and re-derives the soft LLRs, taking a synthetic multipath burst from 13% payload bit-error to zero while staying byte-identical when switched off.
-
TETRA End to End, Part 7: Conformance — Bit-Identical Against the ETSI Reference
How GopherTrunk proves its TETRA voice path — two independent conformance passes against the ETSI EN 300 395-2 reference codec, one demanding sample-for-sample PCM equality on a shared bitstream and one replaying real IQ through the whole chain, plus the 64-bit Word32 build trap that makes the reference tools lie.
-
Weak-Signal Engineering, Part 6: Normalisation & Divergence Guards
The unglamorous half of a working blind equalizer — why the CMA update's cubic scaling in input amplitude makes normalisation part of the algorithm, how an EMA tracking a TDMA downlink's slot-to-slot power swings turned the full equalizer win into CRC zero, and why the divergence guard that re-seeds blown-up taps deserves a test of its own.
-
TETRA End to End, Part 6: A Clean-Room ACELP Vocoder in Pure Go
Inside GopherTrunk's pure-Go TETRA speech decoder — how a 137-bit ACELP frame becomes 240 samples of 8 kHz audio through LSP dequantisation, adaptive and algebraic codebooks, and a bit-exact fixed-point synthesis filter, and what clean-room means when the target is sample-for-sample agreement with a reference codec.
-
Weak-Signal Engineering, Part 5: The Snapshot Trick — Frozen Taps & Differential Decoders
Why a continuously-adapting CMA in front of a differential decoder scores exactly zero CRC-valid frames, why permanently frozen taps only ever match the baseline, and how SnapshotCMA's adapt-continuously-apply-frozen design resolves the dilemma — doubling TETRA voice yield and lifting the thread capture from twelve percent to one hundred.
-
TETRA End to End, Part 5: TCH/S — From Traffic Burst to Speech Frame
Decoding TETRA's full-rate speech channel — two 137-bit frames per slot, the class-0/1/2 sensitivity split with its unequal error protection, the AACH usage marker that routes concurrent same-carrier calls, and the replay harness that correlated decoded timeslots against the control channel's grants on real air.
-
Weak-Signal Engineering, Part 4: Blind Equalization — CMA From First Principles
The Constant Modulus Algorithm built from its cost function up — how the Godard gradient becomes a five-line tap update in GopherTrunk's cma.go, why constant-envelope PSK is the perfect prey, why the algorithm needs a noise floor to be well-posed, and where the spurious minima behind Part 2's EVM trap come from.
-
TETRA End to End, Part 4: Scrambling & Colour Codes — Why Colour 0 Is Not a No-Op
Inside TETRA's scrambler — the 32-tap LFSR, the 30-bit extended colour code that seeds it, why colour 0 still produces a real scrambling sequence, and the two seed bugs that let a control channel lock perfectly while decoding nothing: a story that sets up the DMO 'encryption' misdiagnosis.
-
Weak-Signal Engineering, Part 3: ISI & the Linear Channel — What an Equalizer Can & Can't Fix
The convolution model y = h∗x + n that every equalizer in this series inverts — what multipath and band-edge group delay do to π/4-DQPSK and C4FM symbols, which impairments are linear and therefore recoverable, which are not, and the raw-symbol-domain fact that dictates the architecture of Parts 5 and 7.
-
TETRA End to End, Part 3: Channel Coding — RCPC, Viterbi & the CRC That Isn't an LFSR
Walking TETRA's type-5 bits back to information — two K=5 RCPC mother codes, depuncturing into a shared 16-state Viterbi, the block interleavers, and the class-2 speech CRC that is a fixed parity-check matrix, whose LFSR misimplementation silently dropped every on-air voice burst while synthetic round-trips stayed green.
-
Weak-Signal Engineering, Part 2: Metrics That Lie — EVM vs CRC Yield
Why error vector magnitude, wideband carrier SNR, and constellation beauty can all improve while a decoder recovers nothing — the equalizer whose EVM collapsed 34% to 8% with CRC stuck at zero, the
-
TETRA End to End, Part 2: The Burst Zoo & the Slot Grid
How GopherTrunk finds structure in the 18000-dibit-per-second TETRA firehose — the synchronisation and normal downlink bursts, the three training sequences that mark them, the 255-dibit slot grid, and the one-slot anchor shift that silently misfiles every traffic burst when you get it wrong.
-
Weak-Signal Engineering, Part 1: The Marginal Regime
Between full quieting and no signal lies the marginal regime — where a receiver locks but decodes only a fraction of what it hears — and this opener defines that regime with a real TETRA capture, previews the four levers that roughly doubled GopherTrunk's yield there, and sets the one verdict rule the whole series lives by.
-
TETRA End to End, Part 1: π/4-DQPSK & the Shape of a TETRA Carrier
Why TETRA is not "European P25" — a 25 kHz carrier at 18000 symbols per second, dibits riding in phase transitions instead of amplitudes, a four-slot TDMA downlink that never stops transmitting, and the 144 kHz channel rate GopherTrunk's whole TETRA path is sized around.
-
The Hunt, Part 14: The Hunt Cockpit & Testing Discovery Without Radios
The finale — how the interactive hunt cockpit drives the whole sweep-identify-map-export pipeline live over REST from a browser or terminal, and how the entire discovery path is tested end to end with synthesized IQ and canned sources so a find is proven reproducible with no radio attached.
-
Running It For Real, Part 14: Staying Up — Health, Watchdogs & the Ops Payoff
The finale — the health endpoint that tells a probe the daemon is doing work not just running, the heartbeat that makes a stop never silent, the soft memory limit that dodges the OOM killer, and the USB watchdog that reacquires a dropped dongle without a restart.
-
The Operator's Cockpit, Part 14: Testing Browser & Terminal UIs
The finale — how GopherTrunk tests a browser and a terminal operator UI without a live radio, standing the whole cockpit up in memory — httptest API contract tests, a byte-for-byte WAV header pinned on both the Go server and the TypeScript client, Bubbletea model tests that feed synthetic messages into the reducer, and pure SSE-parser and audio-framer unit tests.
-
The Hunt, Part 13: Exporting Your Finds — RadioReference, TrunkRecorder, SigMF
How GopherTrunk exports a discovered system in every format that matters — a round-tripping CSV import bundle, a TrunkRecorder JSON stanza, a RadioReference submission package with a live cross-reference diff, and a SigMF-tagged capture bundle — one DiscoveredSystem, one Write call, many destinations.
-
Running It For Real, Part 13: systemd Hardening & the Windows Installer
A hardened systemd unit that sandboxes the daemon down to one USB device and a state directory, and the Inno Setup Windows installer that separates program from data, seeds a config, bundles the Zadig driver, and preserves your captures on uninstall.
-
The Operator's Cockpit, Part 13: The Reflect-Driven Config Form
How one reflection walk over config.Config plus one shared field-metadata registry generates the whole config editor for both the terminal Bubbletea form and the web Config Builder — the TUI reading the registry directly, the web reading it over a fieldmeta endpoint, and two drift-guard tests keeping both editors honest to the struct.
-
The Hunt, Part 12: Alias Harvesting — Following Traffic for Talker Aliases
How GopherTrunk harvests P25 Phase 2 talker aliases off a traffic channel's signalling stream without recording voice — a signalling-only DDC tap per in-window grant, the shared MAC dispatcher the voice composer also uses, RS-parity gating, and harvesting the Motorola alias ciphertext for cryptanalysis.
-
Running It For Real, Part 12: Docker & RTL-SDR USB Pass-Through
Containerizing the pure-Go daemon and passing a real RTL-SDR through the container boundary — the multi-stage zero-CGO build, the three things a dongle needs inside a container (host udev, DVB blacklist, device mapping), and why least-privilege beats --privileged.
-
The Operator's Cockpit, Part 12: The TUI Cockpit — The Same API in Your Terminal
How GopherTrunk's Bubbletea TUI drives the identical REST + SSE API as the browser — an Elm-model root reducer fanning out polling commands plus a long-lived SSE pump into one SharedState, thirteen panels as pure renderers over it, and a tab strip that hides the same web.tabs panels the web SPA does.
-
The Hunt, Part 11: Naming the Unknown
How GopherTrunk turns a blind discovery's bare numbers into names — a synthesized system handle from the protocol and identity, a Name/Service/Purpose triple from the frequency allocation and reference catalog, and the honest line between what a decoder can name and what only an operator can.
-
Running It For Real, Part 11: Grant Webhooks & External Integrations
Firing an HTTP webhook the instant a control-channel grant is decoded — the push form of GET /api/v1/grants for home automation, dashboards, and alerting, with a bounded queue that drops before it ever stalls the decoder, a shared JSON schema, and per-grant retry/backoff.
-
Recording, Composition & Streaming, Part 14: The Aggregator Backends — Broadcastify, RdioScanner, OpenMHz, Icecast & Webhook
A closing tour of every outbound destination's real wire protocol — Broadcastify's two-step metadata-then-PUT upload, the multipart POSTs for RdioScanner and OpenMHz, Icecast's paced silence-topped source connection, and the generic JSON webhook — capped by the pure-Go, zero-CGO testing story that verifies them all.
-
The Operator's Cockpit, Part 11: The Mobile Shell, Command Palette & PWA
How one React shell serves a desktop sidebar and a phone's bottom nav from a single nav registry, adds a ⌘K command palette as the fastest path to any panel, and installs as an offline-first PWA on a phone — with a service-worker denylist so the console never shadows its sibling apps.
-
The Hunt, Part 10: Offline vs Live Surveys — Hunting a Recording
How GopherTrunk replays a recorded IQ capture through the exact same sweep, classify, identify, and accumulate pipeline it runs on the air — offline carrier detection with signed offsets, a shared per-candidate routing body, and why a find reproduces from a .cfile byte-for-byte.
-
Running It For Real, Part 10: Broadcast Backends II — RdioScanner, OpenMHz, Icecast
Operating the other three call destinations on a 24/7 daemon — RdioScanner and OpenMHz credentials and endpoints riding the shared retry loop, and why Icecast is the odd one out that drops instead of retrying, reconnects on its own, and needs a source password kept off disk.
-
Recording, Composition & Streaming, Part 13: Outbound Streaming — The Broadcast Manager
How GopherTrunk ships a finished call to the outside world — one bus subscriber that gates on stream/duration, encodes MP3 exactly once, and fans out through a bounded worker pool with drop-on-full and exponential-backoff retry so a wedged aggregator can never back-pressure the decode path.
-
The Operator's Cockpit, Part 10: Write Mode — Mutating Config Safely
How GopherTrunk lets a browser or terminal change a running daemon without lying about what took effect — the mutation-capability gate, pointer-field PATCH semantics that preserve leave-alone, the hot-reload vs restart-required split, an mtime conflict guard, and the validate-then-activate config swap.
-
The Hunt, Part 9: Wideband Multi-Site P25 — Watching Many Channels at Once
How GopherTrunk pins one wideband SDR to a band centre, splits its IQ into a narrowband stream per control channel with a tuner bank, and runs a per-channel P25 (or DMR) state machine on each — so a whole multi-site system's control channels are watched simultaneously off a single dongle.
-
Running It For Real, Part 9: Broadcast Backends I — Broadcastify
Operating a Broadcastify Calls feed on a 24/7 daemon — where the API key and system ID live, how the two-leg upload retries with exponential backoff, how a rejection surfaces, and the counters that tell you a feed went quiet before your listeners do.
-
Recording, Composition & Streaming, Part 12: Live Listening — The Local Audio Path
How GopherTrunk lets you hear a call the instant it decodes — a lock-free AudioPublisher fan-out, an open-ended WAV HTTP stream that even Safari will play, a gRPC StreamAudio path, and a host speaker player that deliberately refuses digital PCM to avoid an echo.
-
The Operator's Cockpit, Part 9: The Map — Plotting Sites & Emitters
How GopherTrunk plots P25 sites and position-bearing emitters — APRS, AIS, ADS-B, DSC — on one shared Leaflet map, patching markers in place from the daemon's locations and sites REST endpoints as fixes arrive, with an auto-fitting camera and a merged discovered-plus-configured site table.
-
The Hunt, Part 8: DMR LCN Correlation — Rebuilding a Channel Map
How GopherTrunk learns a DMR Tier III band plan that is never broadcast — correlating granted logical channel numbers to the RF carriers that key up in response, confirming each pairing by decoding a DMR sync, and fitting a base-plus-spacing grid it hot-swaps into the live control channel.
-
Running It For Real, Part 8: The Opt-In Feature Matrix
How GopherTrunk decides what runs by default versus what stays off until you ask — the three kinds of feature gate, why headless-safe defaults matter, and the one config rule that keeps optional subsystems from surprising a 24/7 daemon.
-
Recording, Composition & Streaming, Part 11: Retention & Housekeeping
How GopherTrunk bounds disk and database growth with one idempotent background sweeper — call-row age, an allow-listed set of decoder-log tables, and mtime-based deletion of only .wav and .raw artifacts under an opt-in files root, each lane gated independently by config.
-
The Operator's Cockpit, Part 8: Constellation, Eye Diagram & Symbol Scope
How one WebSocket symbol stream off the daemon feeds three browser DSP scopes — a complex constellation, a folded C4FM eye diagram, and a rolling symbol oscilloscope — with server-side offset tuning to dodge the DC spike and a soft-vs-dibit frame shape shared across all three canvases.
-
The Hunt, Part 7: Locking a P25 System — Candidate to Confirmed
How GopherTrunk turns a locked P25 control channel into a confirmed DiscoveredSystem — folding WACN and System ID into an identity, recovering the IDEN_UP band plan, resolving neighbour frequencies, and honestly reporting when the NSB that carries the WACN never decoded.
-
Running It For Real, Part 7: SDR Doctor & Preflight
How GopherTrunk catches a bad dongle, wrong USB driver, or broken config before it costs a call — the read-only sdr doctor that inspects per-device driver binding, the preflight that validates directories and TLS files before the listener binds, and the gain-sanity warnings.
-
Recording, Composition & Streaming, Part 10: The Call Log — SQLite Persistence & Never-Downgrade Updates
How GopherTrunk indexes every call into a CGO-free SQLite call_log by subscribing to the bus — an INSERT on start, an UPDATE on end keyed by device and timestamp, and a COALESCE/NULLIF discipline that backfills late RID and encryption without ever clobbering a value it already knew.
-
The Operator's Cockpit, Part 7: Live Spectrum & Waterfall in the Browser
How GopherTrunk streams FFT frames from the SDR broker over a WebSocket and paints a scrolling waterfall plus analyzer trace on a canvas — a per-request FFT producer, a rate-clamped frame protocol, ImageData row rendering, and an FFT-shifted x-to-frequency mapping that powers hover readout and click-to-tune.
-
The Hunt, Part 6: Control-Channel Hunting — The Supervisor
How GopherTrunk's cchunt supervisor multiplexes per-system control-channel hunts over one shared SDR, dwelling on candidates, parking on locks, backing off failures exponentially, and publishing HuntProgress so the cockpit can watch every system at once.
-
Running It For Real, Part 6: The Diagnostics Reporter
How GopherTrunk prepends a host/SDR snapshot to every error surface — the boxed boot banner, the memoized Collector that enumerates dongles at most once, the cheap-versus-costly split in SysInfo, the full error-chain verbose trace, and the non-TTY-safe reporter.
-
Recording, Composition & Streaming, Part 9: CallComplete — The Seam to Everything Downstream
How GopherTrunk's single CallComplete event decouples recording from streaming and persistence — what finalizeLocked builds, the identity and file metadata it carries, the per-call VoiceStats quality log emitted alongside it, and why the demod signal, EVM, and SNR figures ride the earlier CallEnd instead.
-
The Operator's Cockpit, Part 6: Client-Side Resampling
How GopherTrunk plays 8 kHz voice on a 48 kHz browser AudioContext — a windowed-sinc polyphase resampler that carries a history tail and fractional cursor across network chunks, per-phase unity-gain normalized, band-limiting the images that linear interpolation leaks, with a split-feed-equals-whole-feed test as its keystone.
-
The Hunt, Part 5: Autogain & Autotune — Settling the Front End
How GopherTrunk sweeps front-end gain to minimize the decode error rate before committing to a control channel, and how a running-average autotune corrects each dongle's crystal offset so a decoder's carrier loop starts already near lock.
-
Running It For Real, Part 5: Structured Logs — Event, Message & Power
How GopherTrunk writes three purpose-built logs off one event bus — the JSONL event log for machines, the human-readable decoded-message log, and the decode-gated power log — plus the panic-recovery guard that turns a crashing goroutine into a logged, survivable event.
-
Recording, Composition & Streaming, Part 8: Loudness at the Output Stage
Where and when GopherTrunk applies loudness leveling to a finished call — an atomic per-call rewrite of the on-disk WAV, an independent in-memory gain on the distributed MP3 copy, and an optional real-time AGC on the live browser stream — each a separate config switch, none re-deriving the BS.1770 math.
-
The Operator's Cockpit, Part 5: The Live Audio Cockpit
How GopherTrunk streams live decoded voice to the browser — a publisher that fans PCM to bounded per-subscriber channels, a continuous open-ended WAV body served over fetch so the auth header rides along, and an AudioWorklet ring buffer that emits silence on underrun for gapless playback.
-
The Hunt, Part 4: Classifying a Signal — Analog, Digital, Encrypted
How GopherTrunk cheaply classifies a bare carrier into analog, digital, paging, or encrypted using envelope, discriminator, and cyclostationary features before spending an expensive protocol identify, and how the encryption triage tells structured traffic from random.
-
Running It For Real, Part 4: Metrics That Matter — Prometheus & SDR Tiles
How GopherTrunk's Prometheus registry is built — bus-driven counters versus scrape-time snapshot collectors, the SDR tiles worth alerting on (IQ power, clip ratio, gain, lock state), why calls_started beats calls_total, and the opt-in FEC histograms.
-
Recording, Composition & Streaming, Part 7: Correctness Guards — Cross-Call Fencing, Encryption Skip & Runtime Toggles
The subtle safety machinery in GopherTrunk's recorder and audio publisher — a CallID fence that stops a reused voice-tap serial bleeding one call's tail into the next, mid-stream encryption abort-and-delete, grant backfill for late-resolving IDs, and runtime record on/off that never drops an in-flight call.
-
The Operator's Cockpit, Part 4: The Event Stream — SSE to React
How GopherTrunk fans every internal bus event to clients as one JSON envelope — Server-Sent Events for the terminal and curl, a WebSocket twin for the browser that can't set an auth header — with jittered reconnect backoff, frame coalescing, and interval poll hooks that only re-render on real change.
-
The Hunt, Part 3: Peaks & Occupancy — Finding Carriers in the Noise
How GopherTrunk estimates a robust noise floor, picks true carrier peaks without triple-counting one emitter, snaps them onto the land-mobile channel raster, and separates narrow carriers from wide OFDM plateaus in one spectrum frame.
-
Running It For Real, Part 3: TLS & Sitting Behind a Reverse Proxy
How GopherTrunk terminates TLS on its HTTP and gRPC listeners, why plain TCP stays the default, the both-or-neither cert/key rule, the permissive-by-default CORS allow-list, and the loopback-upstream reverse-proxy pattern with nginx or Caddy.
-
Recording, Composition & Streaming, Part 6: Segmentation, Naming & Raw Sidecars
How a trunked call maps to the filesystem in GopherTrunk — the system/talkgroup directory scheme, timezone-aware timestamped filenames tagged with frequency, source and timeslot, per-transmission file rolls, and the .raw sidecar written for DMR, ProVoice and TETRA.
-
The Operator's Cockpit, Part 3: The Connect Screen & Auth Handshake
How a browser pairs with a GopherTrunk daemon — the connect screen that probes health before saving, the three-mode bearer-token policy with loopback trust, the mutation gate, and the capability probe that lets a front-end grey out write controls before it ever tries them.
-
The Hunt, Part 2: The Wideband Sweep Engine
How GopherTrunk steps a wide SDR receiver across megahertz of spectrum, estimates a power spectrum from short IQ grabs, and stitches overlapping tiles into one occupancy picture without dropping a carrier at a step boundary.
-
Running It For Real, Part 2: Auth Posture — Closed-LAN, Auto, Required
How GopherTrunk's mutation-endpoint auth works — the disabled/auto/required modes, why the default flipped to disabled for closed-LAN deployments, the loopback and trusted-network bypasses, constant-time token comparison, and hot token rotation without a restart.
-
Recording, Composition & Streaming, Part 5: WAV on Disk — Crash-Safety, the Tolerant Reader & Clean Cuts
How GopherTrunk's recorder turns decoded PCM into a WAV that survives a crash — placeholder-then-patch RIFF headers, a chunk-walking reader that clamps a truncated final chunk, an end-of-call fade tail, and dead-key suppression that removes empty files.
-
The Operator's Cockpit, Part 2: A React SPA Inside a Go Binary
How GopherTrunk embeds its built Vite/React bundle into the daemon with go:embed all:dist, serves it at the root with a client-side-routing SPA fallback, degrades to a helpful message when the embed is empty, and stays a plain API client so browser and standalone deployments are identical.
-
The Hunt, Part 1: What Discovery Means — From a Blank Band to a Known System
How GopherTrunk turns an empty stretch of spectrum into a named, mapped trunked system — the sweep, identify, and map pipeline behind the hunt engine, and the one discovery contract that the CLI, the daemon, and the web cockpit all drive.
-
Running It For Real, Part 1: From a Laptop Demo to a 24/7 Service
What actually changes when GopherTrunk stops being a laptop demo and becomes an unattended 24/7 service — the daemon lifecycle, dependency-ordered construction and teardown, graceful shutdown, the supervised spawn model, and the operator mindset behind the whole series.
-
Recording, Composition & Streaming, Part 4: The Recording Session
How GopherTrunk's per-call recording session opens files lazily on the first write so dead-keys leave nothing on disk, carries identity forward through dormant post-segment parks, and runs decode-only when no recordings directory is configured.
-
The Operator's Cockpit, Part 1: One Engine, Many Front-Ends
How one running GopherTrunk daemon exposes a single REST + Server-Sent-Events contract that both a React SPA in the browser and a Bubbletea terminal TUI render, kept honest by an api package that talks only to interfaces and never imports the SDR pool.
-
Recording, Composition & Streaming, Part 3: Assembling a Call
How GopherTrunk turns a burst of separate overs into one continuous call — hangtime, talkgroup gating and per-transmission splitting surfacing as segment events that roll the recorder's files, from the output side rather than the DSP.
-
Recording, Composition & Streaming, Part 2: The Composition Contract
How composed audio crosses from GopherTrunk's demod composer into its recorder through three consumer-owned interfaces, why the recorder is the one place a call is decoded, and how the PCM path and the raw-vocoder-frame path differ.
-
Recording, Composition & Streaming, Part 1: The Output Half
How GopherTrunk turns a decoded call into files, feeds, and uploads — four independent, config-gated subsystems that subscribe to a handful of bus events instead of calling each other, so recording, persistence, live audio, and streaming never block one another.
-
Voice Coding, Part 12: Calibrating & Testing Vocoders Against References
How GopherTrunk proves its pure-Go IMBE and AMBE+2 decoders are correct — the voice-calibrate harness, golden-frame regression, streaming decode, and cross-correlation against DSD-FME and OP25.
-
Trunking Engine, Part 12: Control-Channel Hunting, the Call Watchdog & Testing With a Fake Bus
How GopherTrunk finds a control channel and backs off when it can't, how a 500 ms watchdog reaps calls that go silent, and how the whole engine is tested by publishing synthetic grants to a fake bus with no radio attached.
-
Protocol Decoders, Part 12: Testing Decoders Without Radios
How GopherTrunk tests every protocol decoder with no SDR and no live system — synthesized control channels modulated through the real TX chain, a decoder registry, golden fixtures, and the regression discipline that makes 'verified' mean something.
-
Voice Coding, Part 11: Recording & Encoding — WAV, MP3, Tone-Out & Hardware
How GopherTrunk writes crash-safe WAVs, encodes MP3 in pure Go with a Xing/LAME header, detects paging tones with Goertzel, and offers an optional DVSI hardware vocoder behind a build tag.
-
Trunking Engine, Part 11: Encrypted-Mode Handling — Follow, Metadata, or Ignore
How GopherTrunk decides what to do with an encrypted trunked call — hold the tuner, grab metadata and release, or ignore it — with a per-system policy, a configured-key exemption, and a metadata-follow window that frees scarce voice SDRs.
-
Protocol Decoders, Part 11: The Alias Hunt II — The Keystream, Dead Ends & Ethics
The clean-room cryptanalysis of the Motorola P25 talker-alias cipher — the per-character affine keystream, every ruled-out hypothesis, the low-byte observability floor that keeps it uncracked, and the ethics that gate it all.
-
Voice Coding, Part 10: Enhancement & Loudness — Making It Sound Right
The post-synthesis audio chain in GopherTrunk's MBE decoders — spectral enhancement, DC blocking, AGC, tone tilt, an opt-in voice enhancer, and per-call EBU R128 loudness normalization.
-
Trunking Engine, Part 10: Sites, Topology & Multi-Site Roaming
How GopherTrunk accumulates a P25 system's site map from control-channel status broadcasts, tracks a radio roaming between sites, and renders an SDRtrunk-style network configuration report — all derived from the event stream.
-
Protocol Decoders, Part 10: The Alias Hunt I — Framing the Unsolved Cipher
The Motorola P25 talker-alias field carries an encrypted display name. Part 1 of the capstone frames it correctly — the SUID header, the critical strip-the-CRC lesson, the verified per-byte decode model, and the recovered 256-entry LUT.
-
Voice Coding, Part 9: The Composer — Wiring Frames to the Right Vocoder
How GopherTrunk's composer turns a CallStart event into PCM — per-protocol demod chains, front-end decimation, boundary handling, and how the recorder maps each protocol to its vocoder.
-
Trunking Engine, Part 9: Patches, Supergroups & Physical-Channel RID Recovery
How GopherTrunk tracks P25 patches and dynamic-regroup supergroups, and how folding a source RID by physical channel recovers the transmitter and kills a phantom duplicate call from a mis-aliased compressed grant.
-
Protocol Decoders, Part 9: Conventional, Wideband & the Symbol Scope
How GopherTrunk decodes what isn't a trunked control channel — fixed-frequency conventional scanning with tone squelch, wideband multi-carrier Phase 2, DMR LCN band-plan learning, and the symbol-scope diagnostic.
-
Voice Coding, Part 8: AMBE+2 FEC & the Knox Path
Where AMBE+2 error correction actually lives, how GopherTrunk conceals bad frames with replay and adaptive smoothing, and what the vendor-specific knox tone path really is — and isn't.
-
Trunking Engine, Part 8: Affiliation Tracking — Building the Roster From the Event Stream
How GopherTrunk builds a live "which radio is on which talkgroup" roster entirely from the trunking event stream — grants, affiliation responses, and unit registrations — with no extra radio I/O.
-
Protocol Decoders, Part 8: The Legacy Family — EDACS, LTR & MPT-1327
How GopherTrunk follows the pre-digital trunking systems — EDACS 9600-baud CCWs, LTR's central-control-free per-repeater status words, and MPT-1327 FFSK codewords — and why they decode nothing like a modern digital control channel.
-
Voice Coding, Part 7: AMBE+2 — One Decoder, Two Rates
How GopherTrunk's pure-Go AMBE+2 decoder unpacks 49 information bits into shared MBE parameters, and why P25 Phase 2, DMR, and NXDN all run through one Go decoder with two rate tables.
-
Trunking Engine, Part 7: Recovering the Source Radio ID (Issue #915)
A current-work deep dive into how GopherTrunk backfills the source radio ID onto calls that bound from a source-less P25 Phase 2 grant, republishing it through the source-update path so nearly every call reports who keyed up.
-
Protocol Decoders, Part 7: TETRA — π/4-DQPSK, Channel Coding & the Sub-Target-Rate Fix
How GopherTrunk decodes TETRA — π/4-DQPSK framing, the type-1 to type-5 channel-coding chain, CMCE grant PDUs, and the recent fix that interpolates a narrowband capture up to the 144 kHz channel rate so the receiver locks.
-
Voice Coding, Part 6: The Call-Startup Acquisition Squelch
A real bug in GopherTrunk — fresh P25 recordings opened with a full-scale noise burst while the receiver acquired lock, because the FEC resolved marginal dibits to valid-but-wrong IMBE frames. Here's the heuristic that mutes it.
-
Trunking Engine, Part 6: Talkgroups, Aliases & Scan Modes
How GopherTrunk's talkgroup database gives numeric IDs names and policy, how hold and lockout gate a call, and how the engine flips between scan-all and scan-list at runtime from the cockpit without a restart.
-
Protocol Decoders, Part 6: NXDN & dPMR — The Narrowband FDMA Family
How GopherTrunk decodes NXDN and dPMR — the 6.25 kHz FDMA narrowband modes — their 80 ms frames, LICH and CAC signalling, CSBK trunking blocks, and convolutional FEC, and how they contrast with the TDMA modes.
-
Voice Coding, Part 5: IMBE FEC & De-Interleave — Recovering the Bit Soup
How GopherTrunk turns 144 noisy on-air IMBE channel bits into 88 clean information bits — the Golay and Hamming FEC on the eight u-vectors, the channel layout, and why de-interleave must run before descramble.
-
Trunking Engine, Part 5: Priority & Preemption When Calls Outnumber Radios
How GopherTrunk decides which call to drop when active calls exceed voice SDRs — the priority ranking, strict-higher preemption rule, emergency override, and the thrash and starvation it is tuned to avoid.
-
Protocol Decoders, Part 5: DMR Tier 2/3 — Bursts, EMB & FLC
How GopherTrunk decodes DMR's two-slot TDMA — the 132-dibit burst, the sync words closed under polarity flip, the slot-type Hamming code, embedded signalling and Full Link Control, and the reverse channel.
-
Voice Coding, Part 4: IMBE — Decoding P25 Phase 1 Voice
How GopherTrunk turns 88 IMBE information bits into MBE parameters — the b_0 pitch codeword, the PRBS scrambler that whitens the spectrum, and the table-driven PRBA and DCT quantization that reconstruct the spectral envelope.
-
Trunking Engine, Part 4: The Voice Pool — Allocating Scarce Radios
How GopherTrunk's voice pool allocates a handful of Voice-role SDRs across many talkgroups, binds a call, retunes for handoffs, and detects when a wideband rig's tuning window doesn't cover the granted repeater.
-
Protocol Decoders, Part 4: P25 Phase 2 TDMA — The MAC Layer
How GopherTrunk decodes P25 Phase 2's two-slot TDMA — the ISCH slot-type field, the 360 ms superframe, the MAC PDU layer, and the compressed source-less grant that forces a source-RID backfill downstream.
-
Voice Coding, Part 3: MBE Synthesis — Voiced Sinusoids + Unvoiced Noise
How GopherTrunk grows 160 samples of speech from an MBE parameter set — summed sinusoids for the voiced bands, FFT-shaped noise for the unvoiced bands, and the cross-frame phase and overlap-add smoothing that kills clicks.
-
Trunking Engine, Part 3: Grants — The Engine's Only Input
A field-by-field tour of GopherTrunk's protocol-agnostic Grant struct, source-bearing versus source-less grants, and the duplicate-grant guard that stops a repeated control-channel TSBK from binding a second radio.
-
Protocol Decoders, Part 3: P25 Phase 1 TSBKs & Link Control
How GopherTrunk parses P25 Phase 1 Trunking Signaling Blocks — the CRC-checked 12-byte TSBK, its OSP opcode taxonomy, vendor Motorola extensions, TDULC and Link Control — and turns a grant PDU into the Grant the trunking engine records.
-
Voice Coding, Part 2: The MBE Model — Pitch, Voicing & Spectral Envelope
The Multi-Band Excitation parameter set at the heart of IMBE and AMBE+2 — fundamental frequency, per-band voiced/unvoiced decisions, and spectral amplitudes, as the Go structs both codecs synthesize from.
-
Trunking Engine, Part 2: The Event Bus — Typed Pub/Sub That Decouples Everything
How GopherTrunk's in-process event bus fans typed events from the trunking engine to every subscriber, with async delivery and overflow protection so one slow consumer can never stall the decode path.
-
Protocol Decoders, Part 2: P25 Phase 1 — NID, Sync & the FEC That Gates Lock
A field-level tour of P25 Phase 1's physical layer in GopherTrunk — the 48-bit frame sync, the BCH-protected NID with its NAC and DUID, C4FM 4-level symbols, and the trellis plus Hamming FEC that decide whether a control channel locks.
-
Voice Coding, Part 1: What a Vocoder Is & the Pluggable Registry
How GopherTrunk turns 2400-bit-per-second radio data back into speech — what a vocoder actually models, and the Go Vocoder interface + registry that lets IMBE and AMBE+2 plug in behind one seam.
-
Trunking Engine, Part 1: From Grant to Recorded Call
A tour of GopherTrunk's trunking engine — the single-goroutine state machine that subscribes to control-channel grants, allocates a voice SDR, starts a call, and publishes events, without ever importing the code that records or displays them.
-
Protocol Decoders, Part 1: Anatomy of a Control-Channel Decoder
How every GopherTrunk control-channel decoder shares one shape — symbols to framing to FEC to PDU to opcode to bus event — behind a single pipeline interface and a protocol-keyed factory registry.
-
RF Front End, Part 14: Diagnostics, Metrics & the Pure-Go Payoff
The finale of RF Front End. How GopherTrunk makes the front end observable — sdr list --probe, the RTLSDR_DEBUG_USB control-transfer trace, and the iq_underruns_total metric that turned silent IQ loss into something an operator can see — and then the payoff the whole series argued for: one static binary, CGO_ENABLED=0, no librtlsdr, no libusb, cross-compiled everywhere.
-
RF Front End, Part 13: Testing Radios Without Radios
How GopherTrunk's USB driver code is tested in CI with no hardware attached — replaying captured control-transfer sequences through a scripted mock transport, pinning the pure-Go sample conversion bit-for-bit against librtlsdr with a golden-master test, and an opt-in real-hardware tier gated behind environment flags with bias-tee safety resets.
-
RF Front End, Part 12: The SDR Pool & USB Hotplug Watchdog
How GopherTrunk manages a fleet of opened dongles behind one pool — assigning control, voice, and wideband roles, running a USB watchdog that re-enumerates every 30 seconds to catch hotplug, and reacquiring a device after the kernel hands it a new bus address. Plus the re-open race that taught us to serialize retune behind stream teardown.
-
RF Front End, Part 11: HackRF One — Signed 8-Bit IQ End to End
The HackRF One driver end to end — the simplest IQ format of the three, signed 8-bit interleaved I,Q scaled to complex64; enumeration across One/Jawbreaker/Rad1o PIDs; and the open-time board-ID readback that fixed empty probe gains by establishing identity and the gain ladder before streaming.
-
RF Front End, Part 10: Airspy R2/Mini & HF+ — Real Samples to Complex Baseband
How GopherTrunk turns the Airspy R2/Mini's bare real ADC stream into complex baseband entirely on the host — a leaky DC blocker, a multiplier-free Fs/4 mix, and a stateful half-band Hilbert pair — then folds in the Airspy HF+, whose native int16 IQ needs none of it.
-
RF Front End, Part 9: RTL-SDR III — IQ Streaming & the GC-Churn Bug
Sustained IQ streaming from the RTL2832U in pure Go — 32 async USB buffers, a deep consumer channel, and a bit-identical U8-to-complex64 lookup table. Plus the headline bug: per-chunk allocations whose GC churn shed a quarter of the control channel's live IQ, and the zero-allocation reuse ring that fixed it.
-
RF Front End, Part 8: RTL-SDR II — The R82xx Tuner & the Blog V4 Deafness
GopherTrunk's pure-Go R820T/R828D tuner driver — PLL tuning, a shadow-register cache that makes read-modify-write free, and tuner auto-detection. Plus the headline bug: why the RTL-SDR Blog V4 came up deaf and mistuned by 1.8×, and how a manual override plus per-band input switching fixed it.
-
RF Front End, Part 7: RTL-SDR I — Bringing Up the RTL2832U
How GopherTrunk brings up the RTL2832U demodulator in pure Go — the order-sensitive init sequence captured verbatim from librtlsdr, the I2C bridge to the tuner, GPIO and bias-tee, and the USB register transport underneath it all.
-
RF Front End, Part 6: USB on macOS & Windows
How GopherTrunk reaches the same Transport contract through macOS IOKit (via purego, with OS-thread-pinned reader goroutines) and Windows WinUSB (with overlapped I/O), and why macOS forced us to own the calling thread.
-
RF Front End, Part 5: USB on Linux — USBDEVFS & Async URBs
How GopherTrunk's Linux USB backend hand-rolls USBDEVFS ioctl encoding, submits a ring of async URBs, and reaps them in a single goroutine for sustained IQ throughput — all in pure Go behind a safe Transport API.
-
RF Front End, Part 4: Talking to USB Without libusb
How GopherTrunk speaks to RTL-SDR hardware over raw kernel USB interfaces instead of linking libusb, and the single Transport contract that lets one driver run on three wildly different OS USB stacks.
-
Rebuilding GopherTrunk from Source: Update to the Latest Version with Git and GitHub
A copy-paste guide to rebuilding GopherTrunk from source with the Git CLI and GitHub — clone the repo, check out the latest version or a specific branch, build the pure-Go binary, verify it, and keep an existing clone up to date.
-
RF Front End, Part 3: The Driver Registry & Enumeration
How GopherTrunk's self-registering driver registry keeps the core hardware-agnostic — drivers register at init(), the binary's import set chooses the hardware, and EnumerateAll walks every backend to list attached dongles while staying resilient when one driver fails.
-
RF Front End, Part 2: The Device Contract
A method-by-method tour of GopherTrunk's Device interface — the one abstraction four very different radios hide behind — and the optional, type-asserted extension interfaces that expose RTL-SDR-only quirks without polluting the universal contract.
-
RF Front End, Part 1: Why Drive Radios in Pure Go?
The opening post of RF Front End, a 14-part deep dive into GopherTrunk's RF source layer — the pure-Go USB drivers that turn RTL-SDR, Airspy, and HackRF hardware into IQ samples without libusb, librtlsdr, or any C dependency.
-
SDR in Pure Go, Part 14: APIs, Testing & the Pure-Go Story
How GopherTrunk exposes the engine through gRPC, REST, WebSocket and a TUI, observes it with Prometheus and SQLite, and proves it works with synthesized-IQ integration tests — a ports-and-adapters finale.
-
SDR in Pure Go, Part 13: Recording, Composition & Streaming
How GopherTrunk records calls to WAV, drives a per-call demodulation chain with the composer, and streams audio to Broadcastify and RdioScanner — all from optional, config-driven subsystems.
-
SDR in Pure Go, Part 12: Voice Coding — IMBE, AMBE+2 & MBE
How GopherTrunk turns digital voice frames into audio with pure-Go IMBE and AMBE+2 vocoders sharing one MBE synthesis core, all behind a pluggable Vocoder registry.
-
SDR in Pure Go, Part 11: The Trunking Engine & Event Bus
How GopherTrunk's trunking engine turns channel grants into recorded calls, and how an in-process pub/sub event bus decouples the core from the API, storage, and streaming subsystems.
-
SDR in Pure Go, Part 10: Protocol Decoders as State Machines
How GopherTrunk decodes P25, DMR, NXDN, TETRA and a dozen more trunking protocols in pure Go — each a stateful receiver behind a uniform Grant contract, an adapter pattern that keeps the engine protocol-agnostic.
-
SDR in Pure Go, Part 9: Framing & Forward Error Correction
How GopherTrunk recovers clean data from noisy symbols using pure-Go forward error correction — Golay, BCH, Hamming, Reed-Solomon, BPTC, trellis, and Viterbi — built as deterministic, table-driven codecs.
-
SDR in Pure Go, Part 8: Equalization, Diversity & the FFT
How GopherTrunk fights simulcast distortion with adaptive equalizers, combines multiple receivers with diversity, and drives the live waterfall with a rate-limited FFT — all in pure Go.
-
SDR in Pure Go, Part 7: Symbol Timing & Sync Recovery
How GopherTrunk recovers the symbol clock from oversampled signals with a Mueller-Muller timing loop and finds frame boundaries with a sync correlator — feedback state machines implemented in pure Go.
-
SDR in Pure Go, Part 6: Demodulation — FM, C4FM, GFSK & More
How GopherTrunk turns baseband IQ into symbols with pure-Go demodulators for FM, C4FM, GFSK, FFSK, and π/4-DQPSK — each a focused, single-responsibility type composed from DSP primitives.
-
SDR in Pure Go, Part 5: Tuning & Channelization — DDC vs. Polyphase
How GopherTrunk extracts many narrowband channels from one wideband SDR capture using a digital down-converter or a polyphase channelizer, selected at runtime through a Go Strategy interface.
-
SDR in Pure Go, Part 4: DSP Foundations — Filters, NCO & AGC
The building blocks of GopherTrunk's signal chain in Go — FIR/CIC filters, a numerically-controlled oscillator, automatic gain control, and resamplers — and the zero-allocation streaming design behind them.
-
SDR in Pure Go, Part 3: The SDR Pool & Streaming Concurrency
How GopherTrunk streams IQ samples through Go channels and goroutines, manages a fleet of SDR dongles with a device pool, and fans one stream to many consumers without blocking the decode path.
-
SDR in Pure Go, Part 2: SDR Devices & the Driver Registry
How GopherTrunk talks to RTL-SDR, HackRF, and Airspy hardware in pure Go behind one Device interface, and how a self-registering driver registry keeps the core hardware-agnostic.
-
SDR in Pure Go, Part 1: What Is Software-Defined Radio?
A pure-Go tour of software-defined radio — what SDR is, the GopherTrunk signal pipeline from RF to audio, and the layered architecture behind a single static binary.
Tutorials
Step-by-step user-facing guides.
-
The Field Notebook, Part 12: Web Symptoms That Are Really Log Stories
Five GopherTrunk web-console complaints — History hours behind, an empty table, a reconnect storm, a permanent symbol: poor badge, a silent 0.1-second recording row — traced through the daemon log to the seam that broke: the UTC render, the r.rows key, the pre-upgrade 404, symbol_proto, and a voice chain that decoded nothing.
-
The Field Notebook, Part 11: Voice-Chain Lines — Frames, Colours & Teardown Reasons
How to read GopherTrunk's composer voice-chain log — the voice-follow start and end lines, speech_frames and tch_frames, the bfi_count bad-frame counter, the TETRA DMO seed lines, and the undecoded_drops and concurrency_suppressed counters that look alarming but work exactly as designed.
-
The Field Notebook, Part 10: Recorder Lines — Spans, Segments & Dead Keys
How to read GopherTrunk's recorder log — call started and call ended with its reason field, the audio_pct that catches a recording shorter than its call span, the dead-key drops that leave nothing on disk, the per-transmission segment rolls, and the retention sweeper lines that tidy up behind them.
-
The Field Notebook, Part 9: Capture Lines — Aligning a Capture to the Log
How to read GopherTrunk's siglab capture-started, capture-ended and capture-aborted log lines, line a recorded IQ file up with the daemon's decode log by timestamp, and recognise the two capture failures — a centre carved at the wrong frequency and a FLAC rate refusal — before they cost you an afternoon.
-
The Field Notebook, Part 8: The MRC Health Line
How to read GopherTrunk's soapyremote MRC diversity branches health line — branch_dbfs, coherence, branch_gain_db, branch_phase_deg, updates and holds, lock_gate — the anchor and inter-branch delay lines around it, the four WARNs and how their text history changed, and which Weak-Signal or Analog Edge post to open when a two-antenna rig stops earning its keep.
-
The Field Notebook, Part 7: Overruns & host_drops — Reading a Downstream Signal
How to read GopherTrunk's overrun family — the soapyremote SDR overruns WARN with device_overflows and host_drops, the ccdecoder decode can't keep up WARN, the same-carrier voice tap dropped_chunks line and the runtime heartbeat — why each one points downstream at the decoder rather than at the radio, and the CPU story behind the 10 Sep dual-TETRA rig.
-
The Field Notebook, Part 6: The DMO Status Line — Noise Meters & Traffic Counters
How to read GopherTrunk's tetra dmo decode status line — dsb_total against dsb_schs_crc, the dnb_total noise meter against dnb_qualified, tch_crc, distinct_fn and the per-transmission scramble-seed fields — plus the seed recovered, seed changed and voice-follow lines around it, what healthy DMO prints, and when a counter means a defect.
-
The Field Notebook, Part 5: The DMR Activity Line
How to read GopherTrunk's widebandt2 channel decode activity line for conventional DMR — dibits, sync_hits, fec_pass, beacons, late_entries, rekeys, csbk_crc_fail and deaf_heals — what a healthy IPSC tap prints, what the deaf-tap heal WARN's receiver internals mean, and which DMR deep dive to open when a counter goes wrong.
-
The Field Notebook, Part 4: The Carrier-Offset WARN — Alias Buckets & Persistence
An operator's reading of GopherTrunk's issue
-
The Field Notebook, Part 3: The TETRA Decode-Status Line
An operator's field-by-field reading of GopherTrunk's tetra decode status line — sb_bursts, bsch_ok and bsch_fail, sch_pdus and sch_pdus_fail, frag_abandons — the "locked but deaf" signature where bsch_ok climbs while sch_pdus stays zero, and the signal-time resync and payload-drought watchdogs that recover it.
-
The Field Notebook, Part 2: Lock Lines — Locked, Lost & Transitions
An operator's reading of GopherTrunk's control-channel lock family — cc-hunt trying and locked, the per-protocol cc locked lines, the cc.lost events behind a re-hunt, cchunt hunt failed with its IQ diagnosis, the camped idle state, and the transition counter that turns "it drops sometimes" into a number.
-
The Field Notebook, Part 1: Startup — The Lines Before the First Lock
An operator's reading of GopherTrunk's startup log block — the two "gophertrunk starting" lines, device opened and gain set, and the config WARNs the daemon prints before its first lock, including the gain-in-tenths trap, two systems on one control tuner, and the pool gate that used to log nothing.
-
The Operator's Cookbook, Part 14: The Kitchen-Sink Config, Annotated
The series finale: one full GopherTrunk config.yaml walked section by section — sdr to systems to recordings to broadcast to api — each block annotated with the cookbook part that owns it, a decision table from goal to recipe, and the config hygiene that keeps a growing rig maintainable.
-
The Operator's Cookbook, Part 13: Naming Everything — Aliases, Labels & Exports
Turn every talkgroup and radio ID on your GopherTrunk rig into a name — the real talkgroup_file and rid_alias_file CSV columns, naming things live from the web console with the labels layer, harvesting talker aliases off the air, and exporting the merged catalogue back to CSV.
-
The Operator's Cookbook, Part 12: Two Antennas, One Signal — A Diversity Build
A complete GopherTrunk diversity build — a two-channel SDR over SoapyRemote with diversity mrc, reading the MRC health line (coherence, branch_phase_deg), choosing mrc vs mrc-static by hardware class, and the pre-combine capture A/B that proves whether combining actually helps.
-
The Operator's Cookbook, Part 11: The Closet Appliance — Pi, systemd & Docker
Turn a GopherTrunk rig into a headless 24/7 appliance — a Raspberry Pi or mini-PC under a hardened systemd unit, Docker with USB pass-through, honest LAN auth posture, sdr doctor preflight, runtime heartbeats and USB watchdogs, and the graceful shutdown that finally takes milliseconds.
-
The Operator's Cookbook, Part 10: The Archival Rig — FLAC, Retention & the Call Log
The keep-everything GopherTrunk build — lossless FLAC voice recordings at roughly half the size of WAV, FLAC IQ taps, the SQLite call log, retention sweeps that actually run, DSD-FME-playable vocoder sidecars, and disk-budget math with real byte rates.
-
The Operator's Cookbook, Part 9: Sharing the Feed — Broadcastify, OpenMHz & Friends
Turn a working GopherTrunk rig into a public feed — copy-paste broadcast config for Broadcastify Calls, RdioScanner, OpenMHz, Icecast and webhooks, the MP3 transcode and loudness settings that make uploads sound right, and a troubleshooting table for uploads that fail or vanish.
-
The Operator's Cookbook, Part 8: Radios Far Away — SoapyRemote, rtl_tcp & ka9q
A complete GopherTrunk recipe for putting the antenna where reception is and the decoder where the CPU is: sdr.soapy_remote, rtl_tcp and ka9q_radio config, the network bandwidth math per format, validated antenna-port selection, and why host_drops means your decoder stalled — not your network.
-
The Operator's Cookbook, Part 7: Many Systems, One Box — The SDR Pool
A complete GopherTrunk recipe for monitoring several trunked systems from one daemon: pinning dongles by serial, control vs voice vs wideband roles, sizing the shared voice pool against concurrent calls, and the priority, scan-list and preemption rules that decide who gets a radio.
-
The Operator's Cookbook, Part 6: Analog FM & Tone-Out Paging
A complete GopherTrunk recipe for conventional analog FM — a real scan list with squelch, CTCSS/DCS tone gating and per-channel hangtime, plus Quick Call II two-tone fire paging alerts, built on the scanner.conventional and tone_out config sections with every key verified.
-
The Operator's Cookbook, Part 5: TETRA Direct Mode — Scanning DMO
A complete GopherTrunk recipe for TETRA DMO — radio-to-radio direct mode with no infrastructure. One dongle camped on a simplex frequency, the tetra-dmo protocol, colour-code auto-recovery, the tetra_mcc/tetra_mnc keys an MNI≠0 network requires, and an honest account of what is still on-air-gated.
-
The Operator's Cookbook, Part 4: A TETRA TMO Rig
A complete GopherTrunk build for a TETRA trunked-mode network — one dongle camped on a 25 kHz control carrier, the CC equalizer that now ships enabled, MCC/MNC identity, four-slot same-carrier voice through the clean-room ACELP vocoder, and the sync-loss troubleshooting that came from real field logs.
-
The Operator's Cookbook, Part 3: One Repeater, Two Conversations — Conventional DMR
A single-dongle GopherTrunk build for a conventional DMR Tier II repeater — two independent talkgroups on TS1 and TS2 decoded as two simultaneous calls, with the interleaved two-slot voice path, embedded-LC routing, colour-code pinning, and the honest edge cases.
-
The Operator's Cookbook, Part 2: A DMR Tier III Network, End to End
A complete GopherTrunk build for trunked DMR — one wideband dongle on a Tier III control channel, the LCN-to-frequency band plan problem (and letting GopherTrunk learn the plan off the air), plus the log lines, colour codes and timeslots that tell you it's working.
-
The Operator's Cookbook, Part 1: The $40 P25 Starter Rig
A complete first GopherTrunk build — one RTL-SDR dongle, the stock whip, and a laptop decoding a local P25 Phase 1 system, with a copy-paste config.yaml, the exact log lines a healthy first run prints, and a troubleshooting table for when it won't lock.
-
The Analog Edge, Part 14: The Field Checklist — Is It RF or Is It Software?
The whole series folded into one triage flow — dBFS, clipping, gain sweep, rate A/B, capture, coherence — a decision tree for splitting radio problems from software problems, the order to spend hardware money in, and the send-off for the marginal system that started it all.
-
The Analog Edge, Part 13: Coherence, Not dBFS — Scale-Invariant Health
The story of an operator who raised front-end gain seventeen decibels to push a number past a software constant, and the scale-invariant cross-correlation that replaced the gate — why coherence answers the question a calibrator actually asks, and why DC removal inside it is load-bearing.
-
The Analog Edge, Part 12: Front-End Classes — Shared LO vs Independent PLLs
Why two receive channels on one chip keep a constant relative phase while two daughterboards with their own synthesizers lock at a random phase and walk afterwards, how that hardware class decides between mrc and mrc-static, and how the branch_phase_deg log field identifies your class without recording anything.
-
The Analog Edge, Part 11: Two Antennas — Diversity & MRC From the Operator's Seat
What a second antenna and maximal-ratio combining actually buy on a fading trunking band, how to configure GopherTrunk's diversity mode and read its health log line, and the coherence signature that tells you when two antennas cannot be combined with one wideband gain.
-
The Analog Edge, Part 10: Capture Discipline — cfile, cs16, SigMF & Metadata
Why "get the raw capture" is the most repeated sentence in the GopherTrunk issue tracker, the IQ formats and sidecar metadata that make a capture usable, the tap points that decide what a recording can prove, and how a good capture turns a complaint into a regression test.
-
The Analog Edge, Part 9: Filters & LNAs — Adding Gain Without Adding Garbage
When a low-noise amplifier actually helps, why its position in the chain matters more than its gain figure, how bandpass and FM-notch filters buy back headroom on a hot band, and the decision rules for ordering filter, LNA, and coax so you don't amplify your own problems.
-
The Analog Edge, Part 8: Feedline & Connectors — Where dB Go to Die
Why the coax between your antenna and your SDR quietly costs more decode margin than any software setting — published loss figures per cable class at 800 MHz, the real price of every adapter, and why receive-side loss is worse than the half-your-power framing suggests.
-
The Analog Edge, Part 7: Antennas for Trunking Bands
Antenna craft for trunked-radio monitoring — which bands the systems actually live in, why antenna gain is a radiation shape rather than free signal, polarization, when height beats gain, and how to choose between a discone, a tuned vertical, and a yagi for a fixed system.
-
The Analog Edge, Part 6: Sample Rate — The Decode Path Doesn't Care; the Front End Does
Why GopherTrunk's decoders are deliberately rate-invariant — every capture rate is normalized to one per-protocol channel rate before the receiver ever runs — so a symptom that tracks the sample rate indicts the capture, plus what higher rates genuinely buy and the two warnings that mean the consumer fell behind.
-
The Analog Edge, Part 5: Phase Noise & Reciprocal Mixing — The Ten-Megasamples Lesson
The front-end failure no level meter can see: how oscillator phase noise smears modulation while leaving the carrier looking clean, retold for operators through the #764 verdict — same antenna, same signal, 10 dB of demod SNR gone at one sample rate and not the other.
-
The Analog Edge, Part 4: Clipping, Overload & Intermod
What front-end overload actually looks like from the samples up — rail-pinned IQ, the clip ratio as the authoritative verdict, intermodulation products that manufacture phantom signals as gain rises, out-of-band overload from FM broadcast, and why a clean clip ratio still doesn't clear the front end.
-
The Analog Edge, Part 3: Gain Staging — Never Chase a Software Threshold
How to set SDR front-end gain the measured way — the operator who raised gain 17 dB to clear a software constant by 0.2 dB, the ladder method scored by decode quality, autogain's lock-then-error-rate-then-lower-gain tie-break, and the tenths-of-a-dB config trap.
-
The Analog Edge, Part 2: dBFS — What the Number Means (& What It Doesn't)
A precise definition of dBFS for SDR operators — full scale, peak versus RMS, the readings GopherTrunk exports, and why two captures peaking at the same −48 dBFS can differ by 10 dB of usable signal, which makes dBFS a headroom meter and never a quality meter.
-
The Analog Edge, Part 1: Where the Software Ends
The signal chain from antenna to ADC and why no code change can fix a problem on the analog side of it — an inventory of GopherTrunk issue-tracker bugs that turned out to live in the samples, and the map of a 14-part field guide to the front end.
-
Signal Lab, Part 10: The Demod Bench — siglab sweep, Regression & Export
Signal Lab's demod benchmark, gophertrunk siglab sweep, synthesizes P25 Phase 1 across an SNR ladder on both the c4fm and cqpsk/lsm demod paths, decodes through the production pipeline, and prints measured demod quality against a theoretical symbol-error-rate reference with CSV export.
-
RF Scope, Part 10: Advanced — Tuning Segmentation, Selective Analyzers & Pipelines
The advanced rfscope guide — run a subset of analyzers with dependencies auto-pulled, tune segmentation for unusual bands, emit machine formats (json/jsonl/yaml/csv) for pipelines, and close the reverse-engineering loop from -frames-out into cryptolab, with a note on how rfscope's downconverter relates to GopherTrunk's DSP paths.
-
Crypto Lab, Part 10: The Subject Framework, alias & the Mercury Reveal
Crypto Lab's pluggable subject framework and its alias tool recover length-seeded, keyless byte obfuscators through four incremental, resumable modes — gauge, structure, cells, and fromseed — and finally reveal that the Mercury signal was never encryption at all, graded BROKEN, with the moral that obfuscation is not encryption.
-
Signal Lab, Part 9: Dissecting P25 — TSBK PDUs, Receiver States & the Sync Landscape
Signal Lab's deepest P25 view — collect_pdus surfaces every TSBK signaling block (decoded and CRC-failed) as a field-level dissection with a csv-pdus export, plus the C4FM and CQPSK receiver-state series, the soft-eye verdict, and the sync-landscape heatmap.
-
RF Scope, Part 9: The Scene Cockpit — Live TUI & Web Console
rfscope cockpit is a full-screen Bubbletea terminal dashboard and rfscope serve is a browser console — both render the same live Scene (header, hierarchy tree, channel I/O sparklines, top talkers, conversations, severity-colored expert info) over a capture or a live SDR, with a Crypto Lab hand-off built in.
-
Crypto Lab, Part 9: The Web Console, Live-Capture Bridge & External Ciphers
Crypto Lab's serve command launches a schema-driven web console that auto-renders a form for every tool, mounts inside the daemon when built with the cryptolab tag, feeds ks and assess from a live decoder bridge, and drives operator-supplied external ciphers as CLI-only subprocesses behind a strict safety boundary.
-
Signal Lab, Part 8: Naming the Unknown — Blind Signal-ID & the Wideband Survey
Signal Lab identifies an unknown capture by ranking protocol candidates, names undecodable carriers from a blind symbol-rate and modulation estimate against an offline signal-ID reference database, and surveys a wide capture in the Wideband tab — where the Mercury signal finally gets a best guess but no lock.
-
RF Scope, Part 8: Expert Info — Anomalies & What They Mean
rfscope's expert analyzer is the RF analog of Wireshark's Expert Information — rule-based anomaly flags at note, warn, and alert severity for frequency hoppers, intermittent emitters, abnormally wide or narrow carriers, noise-like spectra, and the encrypted or obfuscated findings the entropy triage produced.
-
Crypto Lab, Part 8: Analog Voice Descrambling — Inversion, Split-Band, Rolling Code
Crypto Lab's descramble tool undoes analog voice privacy — frequency inversion, split-band inversion, and rolling-code hopping — operating on raw 16-bit PCM audio, and shows why analog scrambling is fundamentally weaker than digital encryption.
-
Signal Lab, Part 7: VSA — Lab-Grade Modulation Quality
Signal Lab's vector signal analyzer measures modulation quality like bench gear — carrier-frequency error, RMS and peak EVM split into magnitude and phase error, I/Q gain imbalance and quadrature skew, origin offset, an EVM-vs-symbol trace, and an error-vector spectrum.
-
RF Scope, Part 7: Entropy & Encryption Triage — The Crypto Lab Bridge
rfscope's entropy analyzer triages unidentified digital emitters — blind-demodulating a representative payload, running the cryptolab randomness battery and classify-auto measurements, and returning a verdict from plaintext to strong-encrypted with a recommended cryptolab command, plus a -frames-out file that hands the bytes to Crypto Lab.
-
Crypto Lab, Part 7: CRC Recovery & the Recipe Pipeline
Crypto Lab's crc recover reconstructs a protocol's CRC parameters — width, polynomial, init, reflection, and xorout — from sample frames, while recipe run chains transform and analysis operations into a CyberChef-style reusable pipeline for de-obfuscating RF payloads.
-
Signal Lab, Part 6: Synthesize — Building Ideal & Impaired References
Signal Lab's synthesis engine generates an idealized IQ capture and then injects controlled impairments — SNR, carrier offset and drift, multipath, DC, and I/Q imbalance — so you can build a clean reference or a known-bad regression fixture and overlay them in the Compare tab.
-
RF Scope, Part 6: Topology — Emitters, Frequency Hoppers & Conversations
rfscope's topology analyzer is the RF analog of Wireshark's Conversations and Endpoints — it clusters bursts into emitters by RF fingerprint, collapses a frequency hopper's scattered bursts into one logical source, links emitters into conversations, and defers to hunt's authoritative map when a trunking control channel decodes.
-
Crypto Lab, Part 6: The assess Battery — Attacking P25/DMR/TETRA
Crypto Lab's assess crypto harness runs every applicable attack against captured P25, DMR, and TETRA frames and grades each — cipher-strength, a published-weakness knowledge base, IV reuse, known-plaintext, weak/default keys against the real ciphers, and reduced-keyspace brute — landing on a RESISTANT, PARTIAL, or BROKEN verdict.
-
Signal Lab, Part 5: PSD, Spectrogram & Spectral Occupancy
Signal Lab's frequency-domain views — the power spectral density, the spectrogram/waterfall, and the spectral-occupancy metrics computed off the PSD: 99% occupied bandwidth, channel power, adjacent-channel power ratio (ACPR), and spectral flatness.
-
RF Scope, Part 5: Timing & Periodicity — Recovering the TDMA Frame
rfscope's timing analyzer builds burst-length and inter-arrival histograms per channel and recovers a TDMA or frame period by autocorrelating each channel's occupancy series, keeping only peaks above a minimum confidence — the time-domain counterpart to cryptolab's byte-period detection.
-
Crypto Lab, Part 5: Keystream Reuse — Many-Time-Pad & Crib-Dragging
Crypto Lab's ks tool exploits keystream reuse — the core real-world break against additive stream ciphers like P25 DES-OFB, ADP/RC4, and TETRA AIE — detecting IV/MI collisions with ks reuse and recovering plaintext with ks mtp via known-plaintext or crib-dragging, no key recovery required.
-
Signal Lab, Part 4: Constellations & Eye Diagrams — Seeing Modulation Quality
Signal Lab's constellation, eye diagram, and symbol scope — how to read clean versus noisy C4FM (four rails) and CQPSK modulation, what opening and closing eyes mean, and how these plots make EVM and SNR visible.
-
RF Scope, Part 4: The I/O Graph — Per-Channel Activity Over Time
rfscope's timeline analyzer is the RF analog of Wireshark's I/O Graphs — it groups bursts by frequency into channels and fills each with a 100-bin occupancy and power series plus duty cycle, occupancy percentage, burst rate, dominant class, and the sparkline the cockpit renders.
-
Crypto Lab, Part 4: Classical Ciphers — Brute XOR, Caesar, Vigenère, Substitution
Crypto Lab's brute tool recovers classical ciphers — repeating XOR, Caesar shift, Vigenère, and monoalphabetic substitution — using an embedded English trigram language model and optional cribs, with lfsr recovering the keystream of a linear scrambler.
-
Signal Lab, Part 3: The Browser Console — siglab serve and Live Decode
Signal Lab's browser console, gophertrunk siglab serve — a fully offline web app at 127.0.0.1:8099 with Captures, Synthesize, Identify, Wideband, and Compare tabs, upload-configure-run workflow, and a live SSE decode event stream.
-
RF Scope, Part 3: Protocol Hierarchy — What's on the Air
rfscope's hierarchy analyzer is the RF analog of Wireshark's Protocol Hierarchy — it groups bursts by modulation class, then occupied-bandwidth bucket, then names the protocol of the longest digital burst with siglab, attaching burst counts, airtime, spectrum share, and symbol volume to every node.
-
Crypto Lab, Part 3: Randomness — NIST SP 800-22 & the LFSR Signature
Crypto Lab's randomness battery runs a NIST SP 800-22 subset over a keystream bitstream — monobit, block frequency, runs, longest run, binary-matrix rank, spectral DFT, approximate entropy, serial, cumulative sums, and linear complexity — to tell strong keyed encryption from an exploitable LFSR scrambler.
-
Signal Lab, Part 2: Reading the Dashboard — Symbols, Baud, Lock & the 2% Rule
Signal Lab's terminal dashboard, field by field — protocol, symbols, effective baud versus expected, lock status and latency, symbol histogram, IQ imbalance, decode-error rate, EVM, and SNR — plus the ~2% baud-deviation rule that catches a wrong sample rate.
-
RF Scope, Part 2: From IQ to Bursts — The Segmentation Pre-Pass
rfscope's segmentation pre-pass turns a wideband IQ span into bursts — discovering carriers with a wideband FFT, estimating a robust noise floor, downconverting each carrier to a narrow baseband, and slicing onset→offset bursts with per-burst spectral metrics and a blind modulation class.
-
Crypto Lab, Part 2: First Triage — classify auto and stats scan
Crypto Lab's classify auto emits a ranked verdict for an unknown RF payload — plaintext, substitution, repeating-XOR, periodic scrambler, LFSR, or strong-encrypted — each with the exact next command, backed by the stats scan and stats period statistical triage.
-
Signal Lab, Part 1: Your First Capture — No Radio Required
Signal Lab is GopherTrunk's offline signal-analysis workbench — replay, identify, synthesize, and grade recorded IQ captures through the exact production decode pipeline with no SDR, no daemon, and no network attached.
-
RF Scope, Part 1: Wireshark for the RF Physical Layer
rfscope is GopherTrunk's protocol-agnostic RF network analyzer — point it at any band, recorded IQ or a live SDR, with no prior knowledge of the technology, modulation, framing, or encryption, and get a structured Scene of what is on the air and how it behaves.
-
Crypto Lab, Part 1: Breaking It Is the Test
Crypto Lab is GopherTrunk's byte-oriented cryptographic-research toolkit for security-testing RF encryption — shipped inside the binary but excluded from the default install behind a build tag, it attempts decryption by every applicable method and grades how far each one got.
-
Build in the Open, Part 14: The CLI Workflow & Claude Code as Your Daily Driver
Tie the whole idea-to-release journey into one repeatable loop using the gh CLI and Claude Code — CLAUDE.md, slash commands, hooks, MCP servers, and web sessions.
-
Build in the Open, Part 13: Advanced Git & GitHub Features
Level up beyond commit and push — interactive rebase, cherry-pick, bisect, reflog, worktrees, conflict resolution, plus Discussions, Codespaces, GHCR, and Environments.
-
Build in the Open, Part 12: Optimizing & Securing Your Repository
How to make your GitHub repo discoverable and safe — About box, badges, branch protection, secret scanning, Dependabot, SECURITY.md, and least-privilege tokens.
-
Build in the Open, Part 11: Releases — Cadence, Pre-Release vs. Release, SemVer & Changelogs
How to ship software releases the right way — Semantic Versioning, pre-releases, git tags, Keep a Changelog notes, and automated, checksummed builds.
-
Build in the Open, Part 10: Websites, Support Pages & GitHub Pages
How to give a project a free website — enabling GitHub Pages, a Jekyll static site, a custom domain via CNAME, support and sponsor pages, and a drip-released blog.
-
Build in the Open, Part 9: Documentation Done Right — What Lives Where
How to organize project documentation — README, CONTRIBUTING, SECURITY, CHANGELOG, in-repo docs — using the Diátaxis framework so docs are findable and don't rot.
-
Build in the Open, Part 8: Testing — How to Build and Write Tests
How to test software well — the testing pyramid, table-driven tests, golden files, race detection, coverage limits, and writing tests with Claude Code.
-
Build in the Open, Part 7: GitHub Actions — Which Workflows to Create and Why
Which GitHub Actions workflows most projects need — CI, security scanning, release automation, docs build, scheduled jobs — and how to gate merges with them.
-
Build in the Open, Part 6: Planning & Tracking Work — and Inviting Contributors
How to plan work with GitHub Issues, labels, milestones, and Projects, link issues to PRs, and onboard contributors with CONTRIBUTING, templates, and roles.
-
Build in the Open, Part 5: Branching & the Three Ways to Merge to Main
How to use Git branches and pick the right GitHub merge strategy — merge commit, squash & merge, or rebase & merge — with a clear "use this when" for each.
-
Build in the Open, Part 4: Git & GitHub Fundamentals (Using the Web Interface)
A beginner's Git mental model — commits, branches, remotes, the staging area — and how to create a repo, edit files, and open your first pull request entirely in the GitHub web UI.
-
Build in the Open, Part 3: Brainstorming Features with Claude & Writing the README as Your Roadmap
How to research a domain and brainstorm features with Claude Code, then write a README that doubles as a living roadmap — plus what CLAUDE.md is for.
-
Build in the Open, Part 2: Choosing Your Language, Platforms & Tech Stack
How to choose a programming language and tech stack — let your constraints (platforms, performance, distribution, team skills) decide, with GopherTrunk as a worked example.
-
Build in the Open, Part 1: Picking What to Build — How Pros Decide
How experienced developers choose what software to build — validating demand, scoping a first version, and avoiding the build-trap, with GopherTrunk as a worked example.
Solution Postmortem
Post-incident write-ups — what broke, how it was diagnosed, and the fix.
-
From the Issue Tracker, Part 22: Two Pipelines, One Symptom — When Parallel Code Paths Drift
GopherTrunk grew parallel implementations of the same contract — daemon vs replay, single-channel vs wideband, C4FM vs CQPSK — and five issues trace to one path getting a fix, a warning, or a config knob the other never received. The series finale, with the rules that keep twins honest.
-
From the Issue Tracker, Part 21: Census Everything — The Silence of a Success-Only Log Line Carries No Information
Five GopherTrunk investigations stalled or went sideways because the logs only spoke on success — zero lines from a dead decoder looks identical at every failure stage. The fix each time was a census — an unconditional per-unit count with denominators — and this post distills when and how to add one.
-
From the Issue Tracker, Part 20: The Self-Consistent Trap — Round-Trip Tests That Validate Their Own Bugs
Seven GopherTrunk bugs shipped behind green test suites because the encoder and decoder shared the same wrong constant, table, or convention — a wrong sync word, a wrong BCH polynomial, a skipped descramble — and a round-trip test cannot see a mistake it makes twice.
-
From the Issue Tracker, Part 19: One Render Loop — A Blank UI, a Host-less URL, and React Error #185
A blank web UI, a WebSocket URL missing its host, sockets closing before the handshake, and React's maximum-update-depth error all traced to a single unstable selector in an effect dependency array — plus a separate go:embed trap that shipped a binary whose web assets were one .gitkeep file.
-
From the Issue Tracker, Part 18: The Stall That Wasn't — A Dongle Off the Bus and an Opcode Off the Books
A decoder that "stalled" after a clean control-channel lock turned out to be a dongle leaving the USB bus, a retry loop that matched only one error sentinel and left the daemon half-dead, and — found by running p25_survey as an oracle — a TDMA IDEN_UP opcode the dispatcher never handled.
-
From the Issue Tracker, Part 17: Placeholder Constants — The TETRA Sync That Never Existed
TETRA never locked because the training-sequence constants were placeholders — 76 bits declared into a 64-bit variable, bits conflated with dibits, and a linear dibit mapping where the air uses Gray — and every test passed because the fixtures were built from the same wrong constants.
-
From the Issue Tracker, Part 16: The Channel That Was Its Own Voice Channel — Conventional FM and the IQ Broker
Conventional analog FM channels lit up with activity and recorded nothing. Fixing the composer's digital-only gate revealed a second bug hiding behind it — in conventional scanning the monitored channel is the recorded channel, and a single-consumer device API can't serve both.
-
From the Issue Tracker, Part 15: The Silent MP3 — Three Encoder Bugs and a Test That Checked One Frame
Rdio Scanner accepted every upload and played silence. Behind it sat three independent MP3 encoder bugs — an illegal bitrate corrupting headers via putBits(-1), bit-reservoir stuffing desync, and a mono stride that skipped half the audio — none caught by a test that only checked the first frame's sync word.
-
From the Issue Tracker, Part 14: The Recorder Is the Decoder — Perfect Recordings, Silent Speakers
Live audio in the web UI did nothing while recordings came out flawless. Two plausible fixes shipped before a 44-byte curl probe exposed the real cause — only the recorder ever decoded vocoder frames to PCM, so every live path was silently skipped.
-
From the Issue Tracker, Part 13: The SoapyRemote Handshake — Three Wrong Root Causes and a Server That Says Nothing First
A SoapySDRServer fronting a USRP X310 segfaulted the moment GopherTrunk connected. The reporter filed three confident root-cause analyses; all three were disproved by the code. The real bugs were a two-phase, two-socket SETUP_STREAM handshake the client half-implemented — and a TCP flow-control ACK without which the server sends zero samples, forever.
-
From the Issue Tracker, Part 12: Seventy-Eight Degrees — The Phase Angle That Named the Bug
An Airspy R2 that refused to open because its opcode table was systematically shifted, then opened and decoded nothing at all. One diagnostic triplet — EVM 22.7%, phase imbalance +78.1°, image rejection 3.3 dB — read as a sentence, named the bug: a real-sampling stream misread as interleaved I/Q.
-
From the Issue Tracker, Part 11: Detected but Not Present — One Hex Code from a Fix That Already Existed
macOS enumerated the dongle, then the daemon insisted the configured SDR wasn't on the bus. One probe line surfaced IOKit kern_return 0xe000404f — a pipe stall GopherTrunk already knew how to recover from on Linux and Windows, except the macOS backend never translated the error.
-
From the Issue Tracker, Part 10: Faster Than libusb — When the Second Write Outruns the First
A Windows 10 dongle accepted a control write and then rejected its byte-identical twin with ERROR_GEN_FAILURE. The retry-envelope fix couldn't work by construction — the real bug was that a pure-Go WinUSB path is thinner than libusb's stack, and libusb's overhead had been hiding a firmware race.
-
From the Issue Tracker, Part 9: Broken Pipe — Six Rounds of Traces for One USB Write
Two NESDR SMArt v5 dongles that worked in every other SDR app failed GopherTrunk's tuner init with one EPIPE. Five parity fixes didn't take, one of them shipped a regression of our own making, and paired USB traces ruled out everything until a 5 ms sleep closed it.
-
From the Issue Tracker, Part 8: Nineteen Dibits — A Perfect Hypothesis Meets a Rail-Pinned ADC
A meticulously argued bug report proved that GopherTrunk's wideband DDC starved the P25 demodulator with chunks too short to hold a frame sync word — except the sync detector buffers across chunks, the incriminating log line only fires while unlocked, and the raw capture was 50% pinned to the ADC rails. The fix was turning the gain down.
-
From the Issue Tracker, Part 7: The LSM Myth — When Your Own Docs Are the Bug
GopherTrunk's docs, config comments, and UI labels all taught that simulcast P25 sites need the CQPSK demodulator. A user proved a genuine three-tower simulcast system decodes fine in C4FM — and that forcing CQPSK kills it. The real fault was a gain value, and the real fix was rewriting our own guidance across every surface it had leaked into.
-
From the Issue Tracker, Part 6: CQPSK in Four Acts — Fixing the Linear Path One Layer at a Time
A P25 control channel that decoded perfectly in C4FM produced almost nothing in CQPSK mode. The fix took four rounds — a missing carrier-recovery stage, a multipath-poisoned frequency seed, a fractionally-spaced equalizer, and a BCH hot path that only appeared once locking finally worked — and the most convincing piece of evidence in the thread was a broken instrument.
-
From the Issue Tracker, Part 5: Ten Megasamples — When the Bug Is in the Samples Themselves
Raising an Airspy R2 from 2.5 to 10 MS/s made every wideband tap go dark, including the strong one — two real bugs got fixed, an alarming AGC number turned out to be normal, and an independent-resampler A/B proved the remaining 10 dB deficit was recorded into the capture itself.
-
From the Issue Tracker, Part 4: The Dongle That Heard Nothing — One Line in a Register Table
An RTL-SDR Blog V4 that only ever received noise survived four confident wrong diagnoses, a crystal set wrong and then wrong again, and a missing input-routing block — until the reporter read the register tables and found the one value that had to be 1.
-
From the Issue Tracker, Part 3: Encrypted, Says Who — Four Layers Between a Flag and Its Metadata
A P25 Phase 2 system flagged every encrypted call correctly but never reported which algorithm or key — because a fictional MAC opcode, a missing carrier loop, a 48-bit sync constant in a 40-bit field, and a swapped dibit map were stacked on top of each other.
-
From the Issue Tracker, Part 2: The Talker-Alias Hunt — Three Wrong Transports and an Architectural Gate
A month of chasing blank P25 talker aliases through three wrong transport theories, until a per-opcode census and SDRTrunk ground truth revealed the aliases were riding traffic-channel signalling — and GopherTrunk's decoder was architecturally gated behind winning a voice tuner.
-
From the Issue Tracker, Part 1: The First P25 Lock — Eleven Fixes Between 'Trying' and 'Locked'
A P25 control channel that SDRTrunk and OP25 decoded in five seconds took GopherTrunk eleven fixes over five days to lock — and the last two bugs were a wrong BCH generator polynomial and a wrong CRC variant that every synthetic round-trip test had happily agreed with.
Subscribe via RSS.