pyModeS 3.2.0__tar.gz → 3.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pymodes-3.2.0 → pymodes-3.4.0}/CHANGELOG.md +25 -1
- {pymodes-3.2.0 → pymodes-3.4.0}/PKG-INFO +16 -16
- {pymodes-3.2.0 → pymodes-3.4.0}/README.md +18 -18
- {pymodes-3.2.0 → pymodes-3.4.0}/pyproject.toml +2 -2
- pymodes-3.4.0/scripts/README.md +58 -0
- pymodes-3.4.0/scripts/data/README.md +37 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/_pipe.py +573 -75
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/data/airports.py +1 -1
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/_infer.py +2 -5
- {pymodes-3.2.0 → pymodes-3.4.0}/.gitignore +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/LICENSE +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/__init__.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/_aero.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/_altcode.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/_bits.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/_callsign.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/_idcode.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/_schema.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/_uncertainty.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/_v2_removed.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/cli/__init__.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/cli/__main__.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/cli/_args.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/cli/_sink.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/cli/_source.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/cli/_tui.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/cli/decode.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/cli/live.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/core.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/data/__init__.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/__init__.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/_base.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/acas.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/adsb.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/allcall.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/__init__.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/_helpers.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds05.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds06.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds08.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds09.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds10.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds17.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds20.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds30.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds40.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds44.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds45.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds50.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds60.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds61.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds62.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/bds/bds65.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/commb.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/decoder/surv.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/errors.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/message.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/position/__init__.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/position/_airports.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/position/_cpr.py +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/py.typed +0 -0
- {pymodes-3.2.0 → pymodes-3.4.0}/src/pyModeS/util.py +0 -0
|
@@ -5,6 +5,30 @@ All notable changes to pyModeS are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [3.4.0] — 2026-07-13
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- `PipeDecoder` no longer scans every per-ICAO cache on every timestamped
|
|
13
|
+
message. Full TTL eviction is throttled to a configurable one-second
|
|
14
|
+
interval by default, capped at the TTL itself. A bounded per-ICAO check
|
|
15
|
+
still expires state exactly before it is used, so sweep throttling cannot
|
|
16
|
+
change BDS inference at the TTL boundary. Set `eviction_interval=0` for
|
|
17
|
+
the former sweep-per-message memory-reclamation behavior.
|
|
18
|
+
- Timestamped messages that contain no fields used for stateful
|
|
19
|
+
disambiguation no longer create empty per-ICAO state entries. Existing
|
|
20
|
+
useful state is still refreshed when such a message arrives.
|
|
21
|
+
- State updates skip field extraction for downlink formats that cannot
|
|
22
|
+
contribute to stateful inference.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `scripts/stream_filtered.py`, a configurable live/replay Beast example
|
|
27
|
+
demonstrating high-volume DF/typecode prefiltering before `PipeDecoder`.
|
|
28
|
+
- Reproducible version-isolated benchmarks for mixed live traffic and
|
|
29
|
+
high-cardinality streaming, including checked-in reports and the public
|
|
30
|
+
Beast capture used by the mixed-traffic benchmark.
|
|
31
|
+
|
|
8
32
|
## [3.1.0] — 2026-04-14
|
|
9
33
|
|
|
10
34
|
Streaming-robustness release. Adds defences against phantom positions
|
|
@@ -98,7 +122,7 @@ the migration guide.
|
|
|
98
122
|
`bin2hex`, `crc`, `df`, `icao`, `typecode`, `altcode`, `idcode`,
|
|
99
123
|
`cprNL`. Thin wrappers over `_bits.py`, `_altcode.py`, `_idcode.py`
|
|
100
124
|
and `position/_cpr.py` with no logic duplication.
|
|
101
|
-
- `scripts/
|
|
125
|
+
- `scripts/smoke_test_wheel.sh` installs the freshly built wheel
|
|
102
126
|
into a clean Python 3.12 venv and verifies the public API end to
|
|
103
127
|
end (single-message decode, PipeDecoder baseline, batch CPR pair
|
|
104
128
|
resolution, error-dict on malformed input).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyModeS
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.4.0
|
|
4
4
|
Summary: Python Mode-S and ADS-B Decoder
|
|
5
5
|
Project-URL: homepage, https://mode-s.org
|
|
6
6
|
Project-URL: repository, https://github.com/junzis/pyModeS
|
|
@@ -205,24 +205,24 @@ the full command reference.
|
|
|
205
205
|
|
|
206
206
|
## Performance
|
|
207
207
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
The committed benchmarks run each released/working-tree version in an
|
|
209
|
+
isolated environment and reject output mismatches before reporting timings.
|
|
210
|
+
On the captured 176,612-frame mixed Beast feed, the header-prefiltered v3 path
|
|
211
|
+
processes 85,963 incoming messages/s:
|
|
212
212
|
|
|
213
|
-
| Decoder
|
|
214
|
-
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
218
|
-
|
|
|
213
|
+
| Decoder path | Throughput |
|
|
214
|
+
|---|---:|
|
|
215
|
+
| pyModeS 2.21.1 selective helpers | 52,998 msg/s |
|
|
216
|
+
| released v3.3.0 `PipeDecoder` | 3,897 msg/s |
|
|
217
|
+
| v3.4.0 `PipeDecoder` | 49,243 msg/s |
|
|
218
|
+
| v3.4.0 + header prefilter | **85,963 msg/s** |
|
|
219
219
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
**2.71× faster** than rs1090's single-core Rust — all while remaining
|
|
223
|
-
pure Python with no C/Cython build.
|
|
220
|
+
On the 2,000-aircraft synthetic PipeDecoder workload, v3.4.0 processes
|
|
221
|
+
38,838 msg/s versus v2's 39,604 msg/s, with identical normalized output.
|
|
224
222
|
|
|
225
|
-
|
|
223
|
+
See [`scripts/README.md`](./scripts/README.md) for the reproducible commands
|
|
224
|
+
and [`scripts/benchmark_results/`](./scripts/benchmark_results/) for the full
|
|
225
|
+
methodology, timings, and output digests.
|
|
226
226
|
|
|
227
227
|
## Supported messages
|
|
228
228
|
|
|
@@ -178,24 +178,24 @@ the full command reference.
|
|
|
178
178
|
|
|
179
179
|
## Performance
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
| Decoder
|
|
187
|
-
|
|
188
|
-
|
|
|
189
|
-
|
|
|
190
|
-
|
|
|
191
|
-
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
181
|
+
The committed benchmarks run each released/working-tree version in an
|
|
182
|
+
isolated environment and reject output mismatches before reporting timings.
|
|
183
|
+
On the captured 176,612-frame mixed Beast feed, the header-prefiltered v3 path
|
|
184
|
+
processes 85,963 incoming messages/s:
|
|
185
|
+
|
|
186
|
+
| Decoder path | Throughput |
|
|
187
|
+
|---|---:|
|
|
188
|
+
| pyModeS 2.21.1 selective helpers | 52,998 msg/s |
|
|
189
|
+
| released v3.3.0 `PipeDecoder` | 3,897 msg/s |
|
|
190
|
+
| v3.4.0 `PipeDecoder` | 49,243 msg/s |
|
|
191
|
+
| v3.4.0 + header prefilter | **85,963 msg/s** |
|
|
192
|
+
|
|
193
|
+
On the 2,000-aircraft synthetic PipeDecoder workload, v3.4.0 processes
|
|
194
|
+
38,838 msg/s versus v2's 39,604 msg/s, with identical normalized output.
|
|
195
|
+
|
|
196
|
+
See [`scripts/README.md`](./scripts/README.md) for the reproducible commands
|
|
197
|
+
and [`scripts/benchmark_results/`](./scripts/benchmark_results/) for the full
|
|
198
|
+
methodology, timings, and output digests.
|
|
199
199
|
|
|
200
200
|
## Supported messages
|
|
201
201
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pyModeS"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.4.0"
|
|
4
4
|
description = "Python Mode-S and ADS-B Decoder"
|
|
5
5
|
authors = [{ name = "Junzi Sun", email = "git@junzis.com" }]
|
|
6
6
|
license = { text = "GNU GPL v3" }
|
|
@@ -44,6 +44,7 @@ issues = "https://github.com/junzis/pyModeS/issues"
|
|
|
44
44
|
[dependency-groups]
|
|
45
45
|
dev = [
|
|
46
46
|
"mypy>=1.13",
|
|
47
|
+
"pre-commit>=4.5.1",
|
|
47
48
|
"pytest>=7.2.0",
|
|
48
49
|
"pytest-cov>=4.0.0",
|
|
49
50
|
"ruff>=0.15.8",
|
|
@@ -56,7 +57,6 @@ dev = [
|
|
|
56
57
|
[tool.ruff]
|
|
57
58
|
target-version = "py311"
|
|
58
59
|
line-length = 88
|
|
59
|
-
extend-exclude = ["scripts/bench_pms_v2.py"]
|
|
60
60
|
|
|
61
61
|
[tool.ruff.lint]
|
|
62
62
|
select = [
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Maintainer scripts
|
|
2
|
+
|
|
3
|
+
Run these commands from the repository root. Scripts are grouped by purpose
|
|
4
|
+
through their names; generated benchmark reports live in
|
|
5
|
+
`scripts/benchmark_results/`, and committed input data lives in
|
|
6
|
+
`scripts/data/`.
|
|
7
|
+
|
|
8
|
+
## Streaming example
|
|
9
|
+
|
|
10
|
+
- `stream_filtered.py` — configurable high-throughput example using a live
|
|
11
|
+
Mode-S Beast feed or the committed replay capture. Defaults to DF17/20/21
|
|
12
|
+
while excluding ADS-B typecodes 28/29/31.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
uv run python scripts/stream_filtered.py \
|
|
16
|
+
--network airsquitter.lr.tudelft.nl:10006 > decoded.jsonl
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Benchmarks
|
|
20
|
+
|
|
21
|
+
- `benchmark_mixed_traffic.py` — isolated v2, released-v3, updated-v3, and
|
|
22
|
+
prefiltered-v3 comparison on mixed captured traffic. Writes
|
|
23
|
+
`benchmark_results/mixed_traffic.md` by default.
|
|
24
|
+
- `benchmark_pipe.py` — synthetic high-cardinality PipeDecoder comparison.
|
|
25
|
+
Writes `benchmark_results/pipe.md` by default.
|
|
26
|
+
- `benchmark_cpr.py` — focused CPR primitive comparison against the v2 NumPy
|
|
27
|
+
implementation.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv run python scripts/benchmark_mixed_traffic.py
|
|
31
|
+
uv run python scripts/benchmark_pipe.py
|
|
32
|
+
uv run --with numpy python scripts/benchmark_cpr.py
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The version-comparison benchmarks build the working tree and resolve released
|
|
36
|
+
versions from PyPI in isolated temporary environments. They require network
|
|
37
|
+
access unless those packages are already cached.
|
|
38
|
+
|
|
39
|
+
## Generated data
|
|
40
|
+
|
|
41
|
+
- `generate_airport_db.py` — refreshes `src/pyModeS/data/airports.py` from
|
|
42
|
+
OurAirports.
|
|
43
|
+
- `generate_v2_fixture.py` — regenerates the committed v2.21.1 golden oracle.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv run python scripts/generate_airport_db.py
|
|
47
|
+
uv run --no-project --with pyModeS==2.21.1 \
|
|
48
|
+
python scripts/generate_v2_fixture.py
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Release smoke tests
|
|
52
|
+
|
|
53
|
+
- `smoke_test_wheel.sh [wheel]` — installs a built wheel in a clean virtual
|
|
54
|
+
environment and checks the public API.
|
|
55
|
+
- `smoke_test_cli.sh` — checks CLI decoding and a short live Beast connection.
|
|
56
|
+
|
|
57
|
+
The CLI smoke test requires network access. The wheel smoke test uses the most
|
|
58
|
+
recent `dist/pymodes-*.whl` when no path is supplied.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Benchmark capture data
|
|
2
|
+
|
|
3
|
+
## `airsquitter_2026-07-13_120s.csv.gz`
|
|
4
|
+
|
|
5
|
+
Two-minute capture from the public TU Delft AirSquitter Mode-S Beast feed:
|
|
6
|
+
|
|
7
|
+
- Source: `airsquitter.lr.tudelft.nl:10006` (TCP, Beast binary)
|
|
8
|
+
- Capture date: 2026-07-13
|
|
9
|
+
- Frames: 176,612
|
|
10
|
+
- Uncompressed format: `timestamp,hex_message`, one frame per line
|
|
11
|
+
- Compressed size: approximately 2 MiB
|
|
12
|
+
- SHA-256: `fa433eb8dc2ec9cf6ebd40c1bf0a095ae753960d5e8c42ae6da15cc28eb3baea`
|
|
13
|
+
|
|
14
|
+
The timestamps are reconstructed by `pyModeS.cli._source.NetworkSource`
|
|
15
|
+
from the Beast MLAT counter, anchored to wall-clock time when the TCP data
|
|
16
|
+
is received. Mode A/C and Beast status frames are omitted because
|
|
17
|
+
`NetworkSource` yields only Mode-S short and long frames.
|
|
18
|
+
|
|
19
|
+
Capture and compact conversion:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
timeout --signal=INT 120 \
|
|
23
|
+
modes live \
|
|
24
|
+
--network airsquitter.lr.tudelft.nl:10006 \
|
|
25
|
+
--quiet \
|
|
26
|
+
--dump-to /tmp/pymodes-live-mixed.jsonl
|
|
27
|
+
|
|
28
|
+
jq -r '"\(.timestamp),\(.raw_msg)"' \
|
|
29
|
+
/tmp/pymodes-live-mixed.jsonl \
|
|
30
|
+
| gzip -9 > scripts/data/airsquitter_2026-07-13_120s.csv.gz
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Run the associated isolated three-version benchmark with:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python scripts/benchmark_mixed_traffic.py
|
|
37
|
+
```
|