ADS-B (Automatic Dependent Surveillance-Broadcast) is a surveillance protocol where aircraft broadcast their identity, position, altitude, and velocity on 1090 MHz. The signal is unencrypted — any receiver within line-of-sight (~100-200 nautical miles depending on altitude) can hear every aircraft in the sky.
The signal uses Pulse Position Modulation (PPM) at 1 Megabit/second. Messages are either 56 bits (short, Mode S) or 112 bits (long, ADS-B extended squitter). Every message starts with an 8-microsecond preamble that receivers use to detect the frame.
The RTL-SDR dongle is a software-defined radio that samples the 1090 MHz band and produces IQ (In-phase/Quadrature) samples — pairs of 8-bit unsigned integers representing signal amplitude and phase.
| Parameter | Value |
|---|---|
| Sample rate | 2 MHz (2 million sample pairs/sec) |
| Sample format | Interleaved uint8 pairs [I₀, Q₀, I₁, Q₁, ...] |
| Center frequency | 1090 MHz |
We also support pre-demodulated hex frame input from tools like rtl_adsb or dump1090 --raw for testing without raw IQ processing.
Raw IQ samples are converted to magnitude: mag = sqrt(I² + Q²). In practice we use squared magnitude to avoid the sqrt — relative comparisons still work. The Rust implementation uses a compile-time 256×256 lookup table.
A sliding window searches for the preamble pattern: pulses at sample positions 0, 2, 7, 9 (at 2 MHz) with gaps between them. When found, the next 112 samples are extracted as the message data.
Bit recovery: each bit occupies 2 samples. If the first sample is stronger than the second, the bit is '1'. Otherwise '0'. This produces a raw bitstream of 56 or 112 bits.
Every Mode S message includes a 24-bit CRC using the ICAO polynomial (0xFFF409). For ADS-B (DF17) messages, valid frames produce remainder 0x000000. For all-call (DF11) messages, the remainder is XORed with the ICAO address.
Both implementations include syndrome-table error correction for 1-2 bit errors. The Rust CRC lookup table is built at compile time via const fn. Safety rule: never correct bits 0-4 (the Downlink Format field) to prevent turning one message type into another.
| DF | Bits | Content |
|---|---|---|
| 0, 4 | 56 | Altitude replies |
| 5 | 56 | Squawk code (identity) |
| 11 | 56 | All-call (ICAO acquisition) |
| 17 | 112 | ADS-B extended squitter — the main event |
| 20, 21 | 112 | Comm-B data (BDS) |
The 56-bit ME (Message Extended) field in DF17 frames carries the payload. The first 5 bits are the Type Code (TC):
| TC | Content | Details |
|---|---|---|
| 1-4 | Identification | Callsign as 8 chars from 64-char alphabet (6 bits each) |
| 9-18 | Airborne position | Gillham-coded altitude + CPR-encoded lat/lon |
| 19 | Velocity | Ground speed (N/S + E/W components) or airspeed + heading |
Altitude uses Gillham coding: either 25-ft increments (modern) or 100-ft Gray code (legacy). Special squawk codes 7500 (hijack), 7600 (radio failure), and 7700 (emergency) trigger immediate alerts.
Compact Position Reporting is the trickiest part of ADS-B. It compresses latitude and longitude into 17-bit values using a zone system with 15 latitude zones per hemisphere.
Aircraft alternate between even and odd frames (different zone counts: 15 vs 14). By combining one even and one odd frame, the receiver determines which zone the aircraft is in and computes position to ~5.1 meter precision.
Global decode requires two frames; local decode uses a single frame plus a known reference position (e.g., the receiver's location). Both methods are implemented and tested.
Decoded messages feed into a tracker that maintains state per aircraft: position history, altitude profile, velocity trend, and identification. The tracker detects military transponders, emergency squawks, and unusual altitude patterns.
Aircraft data is enriched via external APIs (hexdb.io for type/operator, Planespotters for photos) and persisted to a TimescaleDB time-series database for historical queries, heatmaps, and 4D replay.
Multiple receivers can feed the same server. Frames are deduplicated by ICAO + timestamp, and each receiver is tracked independently for coverage analysis.