Trunking Engine is a 12-part deep dive into the “brain” of GopherTrunk — the component that turns a stream of control-channel grants into tuned, recorded calls. The SDR Internals series gave this engine a single survey episode and flagged it as “worth its own series.” This is that series.
Each part explains what the engine does with one class of event — a grant, a
priority conflict, an affiliation, a patch, an encrypted call — how it’s built
in pure Go around a single-writer select loop and an in-process event bus, and
the software-design principle that keeps the core testable without a radio.
Several parts carry a “problem we hit” war story from real field captures
(the source-RID recovery work behind issue #915, encrypted-mode tuner
starvation).
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 to trunked radio first? Start with the Digital Trunking path, then come back for the implementation.<ol class="post-list series-list"><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 1: From Grant to Recorded Call</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 2: The Event Bus — Typed Pub/Sub That Decouples Everything</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 3: Grants — The Engine’s Only Input</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 4: The Voice Pool — Allocating Scarce Radios</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 5: Priority & Preemption When Calls Outnumber Radios</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 6: Talkgroups, Aliases & Scan Modes</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 7: Recovering the Source Radio ID (Issue #915)</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 8: Affiliation Tracking — Building the Roster From the Event Stream</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 9: Patches, Supergroups & Physical-Channel RID Recovery</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 10: Sites, Topology & Multi-Site Roaming</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 11: Encrypted-Mode Handling — Follow, Metadata, or Ignore</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li><li class="post-card"> <h2 class="post-card__title">Trunking Engine, Part 12: Control-Channel Hunting, the Call Watchdog & Testing With a Fake Bus</h2> <p class="post-card__meta"> Deep dives </p><p class="post-card__desc">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.</p></li></ol><p class="blog-feed-link"> See all deep dives or subscribe via RSS. </p>