Open source · Chicago Offline
Directories go stale. The air doesn't lie.
Radio reference data mostly comes from aging directories and club pages — listings that outlive the systems they describe. rf-survey turns cheap SDRs into a disciplined surveying rig that checks the record against the spectrum itself: sweep, dwell, decode, and file location-tagged reception reports that validate or extend SSRF-Lite — the same data that builds codeplugs.
How it works
A declarative scan plan drives everything. Devices are claimed by serial, observations land in SQLite, and every record carries where and how it was heard.
- Sweepwide-band power scan; SNR against each bin's own long-term median — never raw dB
- Dwellbounded-lifetime listen on anything that clears the threshold — FM or digital
- Decodecolor codes, talkgroups, radio IDs — trusted only when the energy gate agrees
- ReportSSRF-Lite candidate stubs with cited observations; a human reviews before merge
The validation ladder
Every system/channel pair earns a verification tier per station — the higher the rung, the more a codeplug can trust it.
A distributed survey network
Fixed stations around Chicagoland — plus mobile rigs — scan continuously and submit signed observation batches over MQTT. A central aggregator verifies signatures, stores append-only evidence, and promotes records: verified when multiple stations agree across days, flagged when a decode contradicts the record, stale when the air goes quiet. Stations keep themselves honest by measuring shared beacons — NWS 162.55, ATSC pilots — so hardware drift is caught automatically. The payoff lands in codeplugger: program only channels somebody actually heard, recently, near you.
Receive-only, always. Surveyed systems are licensed to other people. Full design in NETWORK.md.
How the data flows
One pipeline, antenna to programmed radio: measured reception reports keep the database honest, profiles keep the radios yours.
flowchart LR
rf["📡 rf-survey"] -- "reception reports" --> ssrf["🗃 ssrf-lite"]
ov["overlays · shared + private"] --> ssrf
ssrf -- "validated RF data" --> cp["⚙ codeplugger"]
prof["👤 profiles"] --> cp
cp -- "CHIRP · qdmr · dmrconf" --> radios(["📻 radios"])
click rf "https://chicago-offline.github.io/rf-survey/" _blank
click ssrf "https://chicago-offline.github.io/ssrf-lite/" _blank
click cp "https://chicago-offline.github.io/codeplugger/" _blank
click prof "https://chicago-offline.github.io/chioff-codeplugger-profiles-shared/" _blank
Ecosystem
rf-survey is the measurement layer. The rest of the stack turns its evidence into working radios.
SSRF-Lite
Authoritative, human-readable YAML for channel plans, repeaters, stations, and talkgroups. Where survey evidence ends up.
codeplugger
Compiles SSRF data and a profile into validated codeplugs. Verification tiers from survey data raise its confidence.
DSD-FME
The digital decoder behind DMR/P25/NXDN dwell. rf-survey orchestrates and gates it; it does the demodulation.
RTL-SDR
The hardware that makes a survey station cost less than a decent antenna. Enumerated by serial, claimed with verified release.
Chicago Offline
Resilient, community-owned communications for Chicagoland: amateur radio, GMRS/MURS, DMR, and LoRa mesh.
Quick start
Python 3.9+, rtl-sdr tools on the path, optional dsd-fme for digital dwell.
Install and list devices
pip install git+https://github.com/Chicago-Offline/rf-survey
survey devices
# 0: SN=BENCH Realtek RTL2838UHIDIR digital
# 1: SN=SONDE Realtek RTL2838UHIDIR analog
Run a scan plan
survey run examples/plan-uhf-dmr.yml --serial BENCH
survey report
survey candidates # ssrf-lite stubs, cited evidence