Dev Patel

v0.1.0 · synthetic-IQ verified · Oct 2026 → Present

Spectrum

A $40 radio.
Every plane, ship and satellite overhead.

Plug a $40 USB radio into a laptop and see the invisible world: planes and ships on a live map from my own ADS-B and AIS decoders, the next weather satellites overhead, and plain-English answers about mystery signals.

The live map on the built-in simulated sky over San Francisco: eight aircraft and six ships decoded by Spectrum's own receivers from generated radio samples, with the AIS waterfall below.

Highlights

203 / 2041ADS-B frames decoded, synthetic
±7 kHzAIS tuning error tolerated
101tests

The problem.

The air around any laptop is full of broadcasts anyone may receive — aircraft position reports at 1090 MHz, ship transponders at 162 MHz, weather satellites at 137 MHz. A $40 RTL-SDR dongle can hear all of it.

Turning that into something a curious person can use takes a pile of single-purpose tools, each with its own driver setup and jargon. And a waterfall full of unexplained blips is the moment most people give up.

What I built.

One streaming DSP core — decimator, mixer, FM and AM demodulators, squelch, waterfall — feeds small stateful receivers that take any chunk size, so a radio, a file and a simulation are interchangeable. The ADS-B receiver is my own Mode S codec and PPM demodulator with CRC-24 one-bit repair and global-then-local CPR positions, so aircraft need no station location. The AIS receiver demodulates both channels from one capture with per-burst carrier-offset removal, HDLC framing and NMEA output.

Satellite passes come from SGP4 on Celestrak elements. Spectrum decides when to record, records SigMF cleanly, and hands LRPT decoding to SatDump, detecting whether it is talking to the 1.2 or the 2.0 command line. The identifier measures bandwidth, burst timing, keying and symbol rate, checks the band plan, and scores 11 classes of weighted rules, showing the reasons that fired. Claude can give a second opinion on the spectrogram; if it fails, the rule result stands.

I had no radio, so the tests decode signals rather than bits. A generator models the physical layer — pulse timing with sub-sample offsets, Gaussian-filtered GMSK, carrier offsets, noise — and a simulated sky with ground truth drives both the demo and every end-to-end test.

Screens.

Satellite passes from real Celestrak elements, with a Meteor-M2 ground track and the captures worth recording on a dipole.
Satellite passes from real Celestrak elements, with a Meteor-M2 ground track and the captures worth recording on a dipole.
The identifier on a 433 MHz mystery signal: measured bandwidth, bursts and on-off keying, then a plain-English answer and the rules that fired.
The identifier on a 433 MHz mystery signal: measured bandwidth, bursts and on-off keying, then a plain-English answer and the rules that fired.

What I considered.

Rejected

Build on an existing ADS-B decoding library

The obvious one, pyModeS, is GPL-3 and this repo is MIT. The codec is my own, and its tests use published reference frames with known decoded values.

Rejected

Decode the weather-satellite images in-house

Viterbi, Reed-Solomon and JPEG for LRPT is SatDump's job, and it does it well. The NOAA APT satellites retired in 2025, so the targets are Meteor-M2 3 and 4.

Chosen

Test decoders on physically modelled signals, not on bit streams

A decoder that passes only on clean bits tells you nothing about a weak frame landing between samples. The cost is that every number is still synthetic until someone plugs in a real dongle.

Results.

  • On the demo's synthetic sky, 203 of 204 transmitted ADS-B frames decode with a valid CRC (3 repaired by one-bit correction), all 8 aircraft are located with a median position error of 58 m, and all 6 ships decode from 60 AIS packets.
  • AIS decodes with up to ±7 kHz of tuning error; ADS-B positions tested at four locations on four continents, with the codec checked against published Mode S reference frames.
  • The identifier names a 433.92 MHz on-off-keyed sensor with high confidence and explains it in plain English; its 11 classes are validated on synthetic signals only.
  • 101 tests and a clean fresh-clone install; CI green on Python 3.10–3.13 and macOS on the first run.

Architecture

How it fits together.

RTL-SDRor SigMF fileSimulated skyground truthDSP coredecimate · mixdemodulateADS-BPPM · CRC · CPRAISGMSK · HDLC · NMEASatellitesSGP4 → SatDumpIdentifier11 rule classesWeb UImap · waterfallpasses · scanner
  1. 01

    RTL-SDRor SigMF file

    Simulated skyground truth

  2. 02

    DSP coredecimate · mix · demodulate

  3. 03

    ADS-BPPM · CRC · CPR

    AISGMSK · HDLC · NMEA

  4. 04

    SatellitesSGP4 → SatDump

    Identifier11 rule classes

  5. 05

    Web UImap · waterfall · passes · scanner

Any source, one DSP core, one receiver per mode. The synthetic sky and a real dongle enter at the same place.

In hindsight

Nothing here has been run against a real radio. The RTL-SDR and SoapySDR paths are thin wrappers that have never seen a V4, the SatDump hand-off is tested against stand-ins that mimic its two command lines, and the identifier's real-world accuracy is unmeasured. The bundled satellite elements are real but age, so the demo pins its clock near their epoch and says so. The decision I'd make again is testing on modelled signals: it is what caught the classic 2 MS/s weakness for frames whose pulses land between samples, which a bit-level test never would have.

Tech specs.

Type
Project
Where
Personal
When
Oct 2026 → Present
Duration
1 mo
Status
v0.1.0 · synthetic-IQ verified
Stack
PythonNumPySciPySkyfieldFastAPIWebSocketsLeafletClaudeGitHub Actions
Caveats
All decoding is verified on physically modelled synthetic IQ and published reference frames. Not yet run on a real RTL-SDR or a real SatDump install.