Beyond Voice is a 14-part deep dive into everything GopherTrunk decodes that is not a trunked voice call. Every earlier series on this blog followed a voice call from carrier to WAV; this one follows the rest of the band — the AFSK/FFSK signalling that rides under analog fleets (MDC1200, Kenwood FleetSync), two-tone fire paging, POCSAG and FLEX pager networks, APRS and the location layer that feeds the map, ADS-B aircraft squitters, AIS ship reports, marine DSC, a pure-Go LoRa chirp receiver, the open M17 link layer, and the amateur digital-voice family (D-STAR, System Fusion, dPMR).
The running thread is a single shape. Each decoder is the same eleven-place
pattern — front end, framer, bus event, storage table, REST route, web panel,
config builder, field help, doctor preflight, config.example.yaml, and the
tests that police them — so the series teaches the pattern once (with the
FleetSync landing as the worked example) and then walks it through twelve
different physical layers. Along the way it collects the traps that cost real
rounds: a WAV reader that assumed 16-bit PCM, a carrier estimator that only
looked at the first few milliseconds, and a slicer threshold that drifted
toward a run of space tone.
Every post reads three ways: a TL;DR + cheat-sheet for skimmers, bold headers, tables, and diagrams for the medium read, and full prose with real code for the deep read.
New here? Start with Other signals in the RF & SDR module for what else lives on the band, then come back for how each one is decoded.<ol class="post-list series-list"><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 1: Why a Scanner Decodes Data — The Eleven-Place Pattern</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 2: AFSK & FFSK — Two Tones, One Bit</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 3: MDC1200 — The Template Decoder</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 4: FleetSync — Cloning the Template & the WAV That Lied</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 5: Two-Tone Paging — Goertzel, Profiles & Tone-Out</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 6: POCSAG & FLEX — Paging Networks</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 7: APRS, AX.25 & the Location Layer</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 8: ADS-B — Mode S Squitters & CRC-24</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 9: AIS — GMSK, NRZI & Bit Stuffing</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 10: DSC — Marine Digital Selective Calling</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 11: LoRa — A Pure-Go Chirp Receiver</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 12: M17 — The Open Digital Voice Link Layer</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 13: D-STAR, System Fusion & dPMR</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">Three AMBE-era digital modes at three distances from on-air verification: D-STAR’s GMSK header behind a 660-bit FEC shell, System Fusion’s FICH trellis codec that is built but not yet on the hot path, and dPMR Mode 3’s CSBK trunking — plus the two voice chains whose interleave tables are honest placeholders.</p></li><li class="post-card"> <h2 class="post-card__title">Beyond Voice, Part 14: Adding a Decoder in a Day — The Checklist</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">The finale — the eleven places a new GopherTrunk decoder touches, from front end to config.example.yaml, with the real FleetSync file for each; the three tests that police the pattern and the CI job vitest cannot replace; a decision table for message decoders versus trunked protocols; and the honest list of what this series leaves open.</p></li></ol><p class="blog-feed-link"> See all deep dives or subscribe via RSS. </p>