Skip to main content

Python Mode-S and ADS-B Decoder

Project description

pyModeS

Fast decoder for Mode-S and ADS-B messages in Python. Ground-up v3 rewrite of pyModeS with a single unified decode() function.

license

Install

pip install "pyModeS>=3"

Python 3.11+ required.

Usage examples

Single-message decode

The decode() function returns a Decoded dict with every decodable field populated in one pass.

from pyModeS import decode

result = decode("8D406B902015A678D4D220AA4BDA")
print(result)
# {
#     'df': 17,
#     'icao': '406B90',
#     'crc_valid': True,
#     'typecode': 4,
#     'bds': '0,8',
#     'callsign': 'EZY85MH',
#     'category': 0,
#     'wake_vortex': 'No category information',
# }

Batch decode (mixed message types)

Pass a list of hex strings and parallel timestamps. Any mix of downlink formats, typecodes, and Comm-B registers is fine — the dispatcher routes each message to the right decoder and uses the timestamps to resolve CPR pairs and disambiguate ambiguous Comm-B registers.

from pyModeS import decode

results = decode(
    [
        "8D406B902015A678D4D220AA4BDA",  # DF17 BDS 0,8 identification
        "8D485020994409940838175B284F",  # DF17 BDS 0,9 airborne velocity
        "8D40058B58C901375147EFD09357",  # DF17 BDS 0,5 airborne pos (even)
        "8D40058B58C904A87F402D3B8C59",  # DF17 BDS 0,5 airborne pos (odd)
        "A000178D10010080F50000D5893C",  # DF20 BDS 1,0 data link capability
        "A8000D9FA55A032DBFFC000D8123",  # DF21 BDS 6,0 heading & speed
    ],
    timestamps=[1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
)
assert results[0]["callsign"] == "EZY85MH"
assert results[1]["groundspeed"] == 159
assert results[3]["latitude"] is not None  # CPR pair resolved

Surface position with airport reference

Surface CPR needs a reference within ~45 NM. Pass an ICAO airport code (looked up in the shipped airport database) or an explicit (lat, lon) tuple.

from pyModeS import decode

# Real DF18 surface movement on LFBO (Toulouse-Blagnac).
r = decode("903a23ff426a4e65f7487a775d17", surface_ref="LFBO")
print(r["latitude"], r["longitude"])  # 43.6264..., 1.3747...

Streaming decoder

PipeDecoder is stateful — it holds per-ICAO state across calls, matches CPR pairs automatically, evicts stale aircraft after a TTL, and flags DF20/21 messages as icao_verified when their CRC-derived ICAO was already seen in a clean DF17/18 plaintext.

from pyModeS import PipeDecoder

pipe = PipeDecoder(surface_ref="EHAM")
for msg, timestamp in stream:
    decoded = pipe.decode(msg, timestamp=timestamp)
    if "latitude" in decoded:
        print(decoded["icao"], decoded["latitude"], decoded["longitude"])

See docs/quickstart.md for the full tour (full-dict mode, error handling, attribute access).

Low-level helpers

For ad-hoc message inspection that doesn't need a full decode, pyModeS.util exposes thin wrappers around the bit/hex/CRC primitives: hex2bin, bin2int, hex2int, bin2hex, crc, df, icao, typecode, altcode, idcode, cprNL.

from pyModeS.util import hex2bin, crc, icao, typecode

msg = "8D406B902015A678D4D220AA4BDA"
hex2bin(msg)[:16]   # '1000110101000000'
crc(msg)            # 0 — valid DF17
icao(msg)           # '406B90'
typecode(msg)       # 4 — ADS-B identification

CLI

pyModeS ships with a modes command-line tool for ad-hoc decoding and live streaming.

modes decode — one-shot and file mode

# Decode one hex message (pretty-printed JSON)
modes decode 8D406B902015A678D4D220AA4BDA

# Decode several messages inline — comma-separated, emits JSON lines
modes decode 8D40058B58C901375147EFD09357,8D40058B58C904A87F402D3B8C59,8D406B902015A678D4D220AA4BDA

# With airborne CPR reference (single message only)
modes decode 8D40058B58C901375147EFD09357 --reference 49.0 6.0

# Compact JSON piped to jq
modes decode 8D406B902015A678D4D220AA4BDA --compact | jq .

# Decode a file of hex messages (one per line OR timestamp,hex CSV)
modes decode --file captures/flight.log

# Stdin + surface CPR
cat taxi.log | modes decode --file - --surface-ref LFBO

modes live — streaming TCP source

# Stream decoded JSON lines from a dump1090-style beast feed
modes live --network localhost:30005

# Tee output to a file
modes live --network host:30005 --dump-to flight.jsonl

# Stream from the TU Delft public feed (live aircraft over Europe)
modes live --network airsquitter.lr.tudelft.nl:10006

# Interactive live aircraft table (requires pyModeS[tui] extra)
pip install "pyModeS[tui]"
modes live --network host:30005 --tui

Mode-S Beast binary format is supported (dump1090 port 30005 and equivalents). See docs/quickstart.md for the full command reference.

Features

  • Unified decode() returns every decodable field in one dict
  • Batch mode preserves list length (errors become error-dicts, not exceptions)
  • PipeDecoder for streams: per-ICAO state, CPR pair accumulation, TTL eviction, DF20/21 ICAO verification via trusted-set promotion
  • full_dict=True populates every key in the canonical 123-field schema for pandas / parquet workflows
  • known= aircraft state disambiguates Comm-B BDS 5,0/6,0 ambiguity
  • Airport ICAO database for surface CPR resolution (surface_ref="EHAM")
  • Type-checked under mypy strict across all source files
  • Golden-file oracle regression test against pyModeS 2.21.1

Performance

Measured on jet1090's long_flight.csv (172,432 Beast-format messages, 7 runs × 1 loop, mean timings, single-core only):

Decoder Wall time Throughput vs pyModeS v3
pyModeS v3 (pure Python) 2.06s ± 0.01 83,549 msg/s 1.00×
pyModeS 2.21.1 (Python with compiled C) 5.03s ± 0.01 34,303 msg/s 0.41×
rs1090 (Rust) 5.60s ± 0.01 30,798 msg/s 0.37×
pyModeS 2.21.1 (Python) 9.09s ± 0.02 18,959 msg/s 0.23×

pyModeS v3 is 2.44× faster than pyModeS 2.21.1's compiled C extension, 4.41× faster than pyModeS 2.21.1 pure-Python, and 2.71× faster than rs1090's single-core Rust — all while remaining pure Python with no C/Cython build.

Reproduce with scripts/benchmark_decode.py.

Supported messages

  • DF4 / DF20: altitude code (surveillance altitude reply)
  • DF5 / DF21: identity code (squawk)
  • DF11: all-call reply (partial — II/SI decoding deferred)
  • DF17 / DF18 ADS-B:
    • TC 1-4 (BDS 0,8): identification + category
    • TC 5-8 (BDS 0,6): surface position
    • TC 9-18 (BDS 0,5): airborne position (barometric altitude)
    • TC 19 (BDS 0,9): airborne velocity (all 4 subtypes)
    • TC 20-22 (BDS 0,5): airborne position (GNSS altitude)
    • TC 28 (BDS 6,1): aircraft status
    • TC 29 (BDS 6,2): target state and status
    • TC 31 (BDS 6,5): operational status
  • DF20 / DF21 Comm-B:
    • BDS 1,0: data link capability
    • BDS 1,7: common-usage GICB capability
    • BDS 2,0: aircraft identification
    • BDS 3,0: ACAS active resolution advisory
    • BDS 4,0: selected vertical intention
    • BDS 4,4: meteorological routine air report
    • BDS 4,5: meteorological hazard report
    • BDS 5,0: track and turn report
    • BDS 6,0: heading and speed report

Migrating from pyModeS 2.x

pyModeS 3 is not backwards-compatible with pyModeS 2.x. The function-per-field API (pms.adsb.callsign(msg), ...) is replaced by a single decode() returning a dict. See the migration guide for the full equivalence table.

If you aren't ready to migrate:

pip install "pyModeS<3"

v3 replaces v2 on the same PyPI slot and import name, so pip install -U pyModeS upgrades existing v2 users to v3 (and triggers the v2-API removal shims — see the migration guide). Pinning <3 keeps you on the 2.x line.

Documentation

The full docs live under docs/ and are published via MkDocs + Material. To rebuild locally:

# One-shot build (strict mode fails on warnings). Output → site/
uv run --with mkdocs-material --with mkdocs-include-markdown-plugin --with "mkdocstrings[python]" mkdocs build --clean --strict

# Live-reload dev server at http://127.0.0.1:8000
uv run --with mkdocs-material --with mkdocs-include-markdown-plugin --with "mkdocstrings[python]" mkdocs serve

Links

Attribution

pyModeS is a project created by Junzi Sun, who works at TU Delft, Aerospace Engineering Faculty. It is supported by many contributors from different institutions.

If you use pyModeS in academic work, please cite:

@article{sun2019pyModeS,
    author={J. {Sun} and H. {V\^u} and J. {Ellerbroek} and J. M. {Hoekstra}},
    journal={IEEE Transactions on Intelligent Transportation Systems},
    title={pyModeS: Decoding Mode-S Surveillance Data for Open Air Transportation Research},
    year={2019},
    doi={10.1109/TITS.2019.2914770},
    ISSN={1524-9050},
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pymodes-3.3.0.tar.gz (168.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pymodes-3.3.0-py3-none-any.whl (187.0 kB view details)

Uploaded Python 3

File details

Details for the file pymodes-3.3.0.tar.gz.

File metadata

  • Download URL: pymodes-3.3.0.tar.gz
  • Upload date:
  • Size: 168.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pymodes-3.3.0.tar.gz
Algorithm Hash digest
SHA256 c09975fa63624447acae3c9859a1f542353a73577e879944507d7f48e50eac27
MD5 c53e5bb1106208ae4557a0724853ddd8
BLAKE2b-256 99f7bec1d2e6b18a2cfae2c01f8194fb873bcacf0ea0dbb7bdc5216e230c98ab

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymodes-3.3.0.tar.gz:

Publisher: build-publish.yml on junzis/pyModeS

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pymodes-3.3.0-py3-none-any.whl.

File metadata

  • Download URL: pymodes-3.3.0-py3-none-any.whl
  • Upload date:
  • Size: 187.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pymodes-3.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 35517122c7353640b235ddf48287cd219415cacdc6341a9e3561d86aa529c7f0
MD5 340211487b86ea515c15ad8df1cb7d93
BLAKE2b-256 9233c6327f5288381d383b8a1b3df4890e9dfb70aecbf128ca5480ebbf2b9956

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymodes-3.3.0-py3-none-any.whl:

Publisher: build-publish.yml on junzis/pyModeS

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page