From Spec to Shipping is a 14-part series on how a protocol decoder actually gets written — the method GopherTrunk follows to go from an ETSI or TIA PDF to code that decodes real signals on air. From the Issue Tracker taught these lessons as postmortems: the round-trip test that validates its own bug, the placeholder constant that became a fabricated protocol, the RPC opcode nothing ever checked. This series teaches the same discipline forward — reading standards, choosing reference implementations you can trust, pinning parsers with literal byte vectors, building bit-identical conformance harnesses, staying clean-room, letting operator captures referee when references disagree, and gating every claim behind on-air verification.
The recurring villain is the test that passes because both sides share the same assumption; the recurring hero is the independent reference — another implementation, a reference codec, a reporter’s capture. Every part closes with rules that transfer to any wire format, not just radio.
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? The Testing module covers the fundamentals this series builds on.<ol class="post-list series-list"><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 1: How to Read a Radio Standard</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 2: Choosing Reference Implementations You Can Trust</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 3: Literal Vectors, Not Round-Trips</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 4: The Conformance Harness — Bit-Identical or Bust</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 5: Clean-Room Rules — Reading Without Copying</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 6: When References Disagree, the Capture Referees</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 7: Tests That Can Disagree With You</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 8: Case Study — Rebuilding SmartNet From Proven Decoders</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 9: Wire Protocols Without Schemas</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 10: The On-Air Gate — Green Synthetics Prove Nothing</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 11: Capture-Driven Development</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 12: Failing First — The Regression Rule</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">From Spec to Shipping, Part 13: Instruments, Not Logs</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li></ol><p class="blog-feed-link"> See all deep dives or subscribe via RSS. </p>