← All posts

The Uplink Insight: Why We Listen to the Officer, Not the Tower

A foundational design decision crystallized today: the scanner should measure uplink frequencies, not downlink. This single choice defines the entire geolocation approach and what the platform can ultimately deliver.

P25 trunked radio operates in paired frequency bands. When a tower transmits to a portable radio, it uses the downlink frequency — typically in the 851–869 MHz range for 800 MHz systems. When the officer's radio transmits back, it uses the uplink frequency, offset by exactly 45 MHz lower. OP25 decodes the control channel and reports voice grants on downlink frequencies. But the downlink signal comes from a fixed tower whose location I already know. The uplink signal comes from the officer's radio — and that's the position users actually want to know.

I rewrote scanner.py to compute the uplink frequency from every voice grant (downlink minus 45 MHz for the 800 MHz band, plus 30 MHz for 700 MHz, minus 39 MHz for 900 MHz) and tune the second SDR to capture signal strength there. The RSSI threshold was lowered from -25 to -40 dBFS to accommodate the weaker uplink signal — portable radios transmit at 1–3 watts compared to the tower's 50–100 watts.

The architecture was formally documented today in a comprehensive system document covering all three nodes, every WebSocket message type, the data flow from RF capture through GPS stamping to cloud geolocation, and the design trade-offs. The document also maps out the planned geolocation algorithm: a Bayesian grid search that fuses six complementary techniques — RSSI range estimation, Doppler bearing, rate-of-change analysis, road network masking, talkgroup geographic priors, and terrain-adjusted propagation. Each technique contributes a log-likelihood term per grid cell, and the search finds the maximum a posteriori position estimate.

Field testing resumed with the new uplink capture mode. OP25 locked the Frederick County control channel cleanly, but a PLL lock failure on one of the SDR dongles forced a hardware serial number swap — putting the less stable dongle on RSSI duty where brief captures are more tolerant of oscillator instability, and the reliable dongle on continuous control channel tracking. A serial resolution function was added to scanner.py so that USB re-enumeration order changes between sessions don't accidentally swap the SDR roles. The scanner now resolves devices by serial number, not by index.

← First Light: Connecting the Scanner to t...Designing the Overwatch Scanner: From Co... →