ADS-B Flight Tracker

Listen to the reports that airliners broadcast about themselves and watch them appear on a globe.

Download
Blocks 4
Category Receivers
Version 1.0.0
License MIT
Updated Sep 28, 2026

Blocks used

Soapy SDR
ADS-B Decoder
Spectrum Analyzer
Note

Overview

Airliners tell anyone who listens who they are, where they are, and how fast they are moving. The system is called Automatic Dependent Surveillance-Broadcast, or ADS-B, and it lets an aircraft broadcast its flight state without being asked.[1] Position and velocity go out twice a second and the flight identification every five seconds, on 1090 MHz.[2] Outside the United States almost all ADS-B equipment uses that frequency, and inside it aircraft below 18,000 ft may use a separate link on 978 MHz instead.[3]

This flowgraph captures 2 MHz around 1090 MHz and passes it to a native ADS-B Decoder block, which finds the message bursts, checks them, keeps a table of every aircraft it has heard, and plots those with a known position on a globe. A spectrum analyzer fed directly by the radio shows the band. Running it needs a radio supported by SoapySDR that can tune 1090 MHz and sample at 2 MS/s, such as an RTL-SDR, an antenna with a view of the sky, and aircraft within line of sight.

How it works

Soapy SDRADS-B Decoder

Each message is a Mode S burst that opens with an 8 µs preamble of four pulses and then carries one bit per microsecond. A 1 is a 0.5 µs pulse followed by 0.5 µs of silence, and a 0 is the reverse.[1] At 2 MS/s every half bit gets exactly one sample, which is why the decoder rejects any other sample rate. A 112-bit ADS-B message with its preamble lasts 120 µs, or 240 samples.

The block turns each update of 65,536 samples, about 32.8 ms, into magnitudes and hands them to libmodes,[4] a C library refactored from Salvatore Sanfilippo's dump1090.[5] It looks for the preamble's pulse pattern, reads each bit by comparing the two halves of its microsecond, and checks the 24-bit parity at the end of the message.[1] A single wrong bit can be repaired.

Every message that passes adds its aircraft's 24-bit ICAO address to the table. Three kinds of ADS-B message fill in the rest.[2]

Message Type codes Table columns Broadcast rate in flight
Identification 1 to 4 Callsign 0.2 Hz
Airborne position 9 to 18 Alt (ft), Lat, Lon 2 Hz
Airborne velocity 19 Speed (kt), Hdg 2 Hz

Position takes two messages. Compact position reporting packs latitude and longitude into 17 bits each, which give the position within a zone but not which zone it is. Aircraft alternate between an even grid with 6° latitude zones and an odd grid with slightly larger ones.[6] The block keeps the latest report of each kind per aircraft, and when the two arrive within 10 s of each other and fall in the same longitude zone, it solves for the global position.[6]

What to look for

The table has one row per address, a dash marks a field that has not arrived, and position usually fills in last. Speed is ground speed and Hdg is the direction of travel over the ground.[7] Rows are never removed. A row with nothing but an address comes from messages the block does not decode, such as the short acquisition squitters that carry only the address,[8] or surface position reports from aircraft on the ground.[2]

Each globe label shows the callsign or address, then altitude in hundreds of feet and speed in tens of knots, so 350 45 means 35,000 ft and 450 kt. An arrow points along the track once speed is known, and dots mark recent positions. A target turns orange and reads STALE 15 s after its last position and disappears after two minutes. The view zooms to fit the first aircraft unless you click or scroll first.

The spectrum shows no station. Each trace averages the eight 4.1 ms frames of an update, so a 120 µs burst barely lifts the floor, while the waterfall shows bursts as short ticks across the band. A steady line is interference, not an aircraft.

Reception is line of sight, so the number of rows depends on antenna placement more than on any setting in the graph. For the message formats and the position equations, see the sources below.

Going further

The farthest aircraft located is a direct measure of range. The great-circle distance from your antenna at latitude φ0\varphi_0 and longitude λ0\lambda_0 to an aircraft at φ\varphi and λ\lambda is

d=2Rarcsin⁡sin⁡2φ−φ02+cos⁡φ0cos⁡φ sin⁡2λ−λ02,d = 2R \arcsin \sqrt{\sin^2 \frac{\varphi - \varphi_0}{2} + \cos \varphi_0 \cos \varphi \, \sin^2 \frac{\lambda - \lambda_0}{2}},

where RR is the Earth's mean radius of 6,371 km. A Python block can apply it to the decoder's table through block metrics. Give the block one input and no outputs, connect the input to the Soapy SDR output so it runs once per update, set HOME_LAT and HOME_LON to your antenna's position in degrees, and paste the code below. About every five seconds it prints how many aircraft have a position and the farthest one. Compare the result after moving the antenna or collapsing a telescoping whip to a quarter wavelength, 6.9 cm at 1090 MHz.

PYTHON
import numpy as np

HOME_LAT = 52.3100
HOME_LON = 4.7600
EARTH_RADIUS_KM = 6371.0

_CALLS = 0


def compute(ctx):
    global _CALLS
    _CALLS += 1
    table = ctx.metrics.get_value("adsb", "aircraftTable", default={})
    if _CALLS % 150 != 0 or not table:
        return
    columns = list(table["columns"])
    icao, callsign = columns.index("ICAO"), columns.index("Callsign")
    lat_col, lon_col = columns.index("Lat"), columns.index("Lon")
    rows = [r for r in table["rows"] if r[lat_col] != "-"]
    if not rows:
        print("No positions yet")
        return
    lat = np.radians([float(r[lat_col]) for r in rows])
    lon = np.radians([float(r[lon_col]) for r in rows])
    lat0, lon0 = np.radians(HOME_LAT), np.radians(HOME_LON)
    a = np.sin((lat - lat0) / 2) ** 2 + np.cos(lat0) * np.cos(lat) * np.sin((lon - lon0) / 2) ** 2
    d = 2 * EARTH_RADIUS_KM * np.arcsin(np.sqrt(a))
    far = rows[int(np.argmax(d))]
    name = far[callsign] if far[callsign] not in ("-", "") else far[icao]
    print(f"{len(rows)} of {len(table['rows'])} aircraft located, farthest {name} at {d.max():.0f} km")

The result is the farthest of the last known positions of every aircraft heard since the flowgraph started, and the Python block reference explains how blocks read metrics.

The frequency must stay at 1090 MHz and the sample rate at 2 MS/s, while the batch count and length can change. Bias-T powers an external amplifier through the antenna cable and should stay off without one.

The block reads only airborne ADS-B from transponders. It skips surface positions, positions reported with GNSS height, and broadcasts from equipment that is not a transponder, which uses a different downlink format.[2] Every position needs a fresh even and odd pair, since the block does not decode a single message against a previous position,[6] so a faint aircraft may show altitude long before a location.

References

References

  1. J. Sun, "Introduction," in The 1090 Megahertz Riddle: A Guide to Decoding Mode S and ADS-B Signals, 2nd ed. Delft, Netherlands: TU Delft OPEN Publishing, 2021, doi:10.34641/mg.11. ↩ ↩2 ↩3

  2. J. Sun, "ADS-B basics," in The 1090 Megahertz Riddle: A Guide to Decoding Mode S and ADS-B Signals, 2nd ed. Delft, Netherlands: TU Delft OPEN Publishing, 2021, doi:10.34641/mg.11. ↩ ↩2 ↩3 ↩4

  3. U.S. Federal Aviation Administration, "Installation," Equip ADS-B, 2023. ↩

  4. T. Watson, libmodes, GitHub. github.com/watson/libmodes ↩

  5. S. Sanfilippo, dump1090, GitHub. github.com/antirez/dump1090 ↩

  6. J. Sun, "Airborne position," in The 1090 Megahertz Riddle: A Guide to Decoding Mode S and ADS-B Signals, 2nd ed. Delft, Netherlands: TU Delft OPEN Publishing, 2021, doi:10.34641/mg.11. ↩ ↩2 ↩3

  7. J. Sun, "Airborne velocity," in The 1090 Megahertz Riddle: A Guide to Decoding Mode S and ADS-B Signals, 2nd ed. Delft, Netherlands: TU Delft OPEN Publishing, 2021, doi:10.34641/mg.11. ↩

  8. J. Sun, "All-call reply," in The 1090 Megahertz Riddle: A Guide to Decoding Mode S and ADS-B Signals, 2nd ed. Delft, Netherlands: TU Delft OPEN Publishing, 2021, doi:10.34641/mg.11. ↩

CyberEther

High-performance GPU-accelerated signal processing and visualization framework that runs anywhere.

Star on GitHub

© 2026 CyberEther. All rights reserved.

The final frontier!