Skip to main content

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 in seconds. 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 seen recently in clean DF17/18 plaintext.

Pass timestamps in seconds from a consistent clock.

from pyModeS import PipeDecoder

pipe = PipeDecoder(surface_ref="EHAM")
for msg, timestamp in stream:
    decoded = pipe.decode(msg, timestamp=timestamp)
    if decoded.get("latitude") is not None:
        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:10006

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

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

Mode-S Beast binary format is supported (dump1090 and compatible feeds). 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, global and local CPR position decoding, 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
  • include_meteo=True opts into heuristic Comm-B BDS 4,4/4,5 inference
  • 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

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

BDS 4,4 and 4,5 are heuristic and disabled by default. Enable them with decode(..., include_meteo=True) or PipeDecoder(include_meteo=True) when these meteorological registers are expected on the input feed.

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

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},
}

Metadata

Release files for pyModeS 3.6.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyModeS 3.6.1
File Size Uploaded
pymodes-3.6.1.tar.gz 176.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyModeS 3.6.1
File Interpreter ABI Platform
pymodes-3.6.1-py3-none-any.whl Python 3 none any Details

Total release size: 368.0 kB

Release files / pymodes-3.6.1.tar.gz

Download URL pymodes-3.6.1.tar.gz
Size 176.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7da700d5344be03c59cb5a966a63feeea74caed1a44078d2056ea5f0072edf07
BLAKE2b-256 checksum
How to use checksums
44a316e9e2f4c41e0589ad7cf55abe61f6df333e04d2d147dec8b464c547578e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / pymodes-3.6.1-py3-none-any.whl

Download URL pymodes-3.6.1-py3-none-any.whl
Size 191.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9884d4ee12d858d8d4d4a16f86790d0f1fa6e2d2556d595dec727d9c9959033c
BLAKE2b-256 checksum
How to use checksums
dd0357f26086ce82b3a738d13526041b841deb7aaf7054f9eb66c46cdbcf32e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

3.6.1 This release

2 release files

3.6.0

2 release files

3.5.1

2 release files

3.5.0

2 release files

3.4.0

2 release files

3.3.0

2 release files

3.2.0

2 release files

3.1.0

2 release files

3.0.0

2 release files

2.21

25 release files

2.20

25 release files

2.19

2 release files

2.18

2 release files

2.17

13 release files

2.16

13 release files

2.15

13 release files

2.14

5 release files

2.11

2 release files

2.9

1 release file

2.8

1 release file

2.5

2 release files

2.4

2 release files

2.2

2 release files

2.1

2 release files

2.0

2 release files

1.2.2

2 release files

1.2.0

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.9

2 release files

1.0.8

2 release files

1.0.7

2 release files

1.0.5

2 release files

1.0.4

1 release file

1.0.3

1 release file

1.0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page