pyModeS 3.5.0__tar.gz → 3.6.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.5.0 → pymodes-3.6.0}/CHANGELOG.md +12 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/PKG-INFO +6 -1
- {pymodes-3.5.0 → pymodes-3.6.0}/README.md +5 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/pyproject.toml +1 -1
- {pymodes-3.5.0 → pymodes-3.6.0}/scripts/README.md +8 -4
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/__init__.py +4 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/_pipe.py +21 -5
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/cli/_args.py +10 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/cli/decode.py +2 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/cli/live.py +5 -1
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/core.py +15 -1
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/commb.py +7 -2
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/message.py +17 -2
- {pymodes-3.5.0 → pymodes-3.6.0}/.gitignore +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/LICENSE +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/scripts/data/README.md +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/_aero.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/_altcode.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/_bits.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/_callsign.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/_idcode.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/_pipe_validation.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/_schema.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/_uncertainty.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/_v2_removed.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/cli/__init__.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/cli/__main__.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/cli/_parse.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/cli/_sink.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/cli/_source.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/cli/_tui.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/data/__init__.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/data/airports.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/__init__.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/_base.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/acas.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/adsb.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/allcall.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/__init__.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/_helpers.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/_infer.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds05.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds06.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds08.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds09.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds10.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds17.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds20.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds30.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds40.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds44.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds45.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds50.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds60.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds61.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds62.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/bds/bds65.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/decoder/surv.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/errors.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/position/__init__.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/position/_airports.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/position/_cpr.py +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/py.typed +0 -0
- {pymodes-3.5.0 → pymodes-3.6.0}/src/pyModeS/util.py +0 -0
|
@@ -5,6 +5,18 @@ 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.6.0] — 2026-07-16
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `decode()`, `Message.decode()`, and `PipeDecoder` accept
|
|
13
|
+
`include_meteo=True` to opt into heuristic Comm-B BDS 4,4 routine
|
|
14
|
+
meteorological reports and BDS 4,5 meteorological hazard reports. The
|
|
15
|
+
default remains `False` because these payload patterns can overlap other
|
|
16
|
+
Comm-B registers.
|
|
17
|
+
- The `modes decode`, `modes live`, and `scripts/stream_filtered.py` interfaces
|
|
18
|
+
expose the same behavior through `--include-meteo`.
|
|
19
|
+
|
|
8
20
|
## [3.5.0] — 2026-07-15
|
|
9
21
|
|
|
10
22
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyModeS
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.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
|
|
@@ -199,6 +199,7 @@ the full command reference.
|
|
|
199
199
|
- `full_dict=True` populates every key in the canonical 123-field
|
|
200
200
|
schema for pandas / parquet workflows
|
|
201
201
|
- `known=` aircraft state disambiguates Comm-B BDS 5,0/6,0 ambiguity
|
|
202
|
+
- `include_meteo=True` opts into heuristic Comm-B BDS 4,4/4,5 inference
|
|
202
203
|
- Airport ICAO database for surface CPR resolution (`surface_ref="EHAM"`)
|
|
203
204
|
- Type-checked under mypy strict across all source files
|
|
204
205
|
- Golden-file oracle regression test against `pyModeS 2.21.1`
|
|
@@ -228,6 +229,10 @@ the full command reference.
|
|
|
228
229
|
- BDS 5,0: track and turn report
|
|
229
230
|
- BDS 6,0: heading and speed report
|
|
230
231
|
|
|
232
|
+
BDS 4,4 and 4,5 are heuristic and disabled by default. Enable them with
|
|
233
|
+
`decode(..., include_meteo=True)` or `PipeDecoder(include_meteo=True)` when
|
|
234
|
+
these meteorological registers are expected on the input feed.
|
|
235
|
+
|
|
231
236
|
## Migrating from pyModeS 2.x
|
|
232
237
|
|
|
233
238
|
pyModeS 3 is **not backwards-compatible** with pyModeS 2.x. The
|
|
@@ -172,6 +172,7 @@ the full command reference.
|
|
|
172
172
|
- `full_dict=True` populates every key in the canonical 123-field
|
|
173
173
|
schema for pandas / parquet workflows
|
|
174
174
|
- `known=` aircraft state disambiguates Comm-B BDS 5,0/6,0 ambiguity
|
|
175
|
+
- `include_meteo=True` opts into heuristic Comm-B BDS 4,4/4,5 inference
|
|
175
176
|
- Airport ICAO database for surface CPR resolution (`surface_ref="EHAM"`)
|
|
176
177
|
- Type-checked under mypy strict across all source files
|
|
177
178
|
- Golden-file oracle regression test against `pyModeS 2.21.1`
|
|
@@ -201,6 +202,10 @@ the full command reference.
|
|
|
201
202
|
- BDS 5,0: track and turn report
|
|
202
203
|
- BDS 6,0: heading and speed report
|
|
203
204
|
|
|
205
|
+
BDS 4,4 and 4,5 are heuristic and disabled by default. Enable them with
|
|
206
|
+
`decode(..., include_meteo=True)` or `PipeDecoder(include_meteo=True)` when
|
|
207
|
+
these meteorological registers are expected on the input feed.
|
|
208
|
+
|
|
204
209
|
## Migrating from pyModeS 2.x
|
|
205
210
|
|
|
206
211
|
pyModeS 3 is **not backwards-compatible** with pyModeS 2.x. The
|
|
@@ -13,16 +13,20 @@ through their names; generated benchmark reports live in
|
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
uv run python scripts/stream_filtered.py \
|
|
16
|
-
--network airsquitter.lr.tudelft.nl:10006
|
|
16
|
+
--network airsquitter.lr.tudelft.nl:10006 \
|
|
17
|
+
--include-meteo > decoded.jsonl
|
|
17
18
|
```
|
|
18
19
|
|
|
20
|
+
`--include-meteo` is optional and enables heuristic Comm-B BDS 4,4/4,5
|
|
21
|
+
reports when the feed is expected to contain meteorological data.
|
|
22
|
+
|
|
19
23
|
## Benchmarks
|
|
20
24
|
|
|
21
|
-
- `benchmark_mixed_traffic.py` — isolated v2.21.1, released v3.
|
|
22
|
-
and prefiltered v3.
|
|
25
|
+
- `benchmark_mixed_traffic.py` — isolated v2.21.1, released v3.5.0, v3.6.0,
|
|
26
|
+
and prefiltered v3.6.0 comparison on mixed captured traffic. It measures
|
|
23
27
|
throughput, immediate streaming position yield, and coordinate agreement.
|
|
24
28
|
By default it fails below 80% of v2 position yield, below 99% agreement
|
|
25
|
-
within 100 m, or when v3.
|
|
29
|
+
within 100 m, or when v3.6.0 yields fewer positions than v3.5.0. The two
|
|
26
30
|
percentage floors are configurable through command-line options. Writes
|
|
27
31
|
`benchmark_results/mixed_traffic.md` by default.
|
|
28
32
|
- `benchmark_pipe.py` — synthetic high-cardinality PipeDecoder comparison.
|
|
@@ -21,6 +21,10 @@ For streaming workflows where references aren't known ahead of time,
|
|
|
21
21
|
use :class:`PipeDecoder`. It establishes airborne positions from even/odd CPR
|
|
22
22
|
pairs, then resolves later frames locally from the last validated airborne
|
|
23
23
|
position. The optional ``surface_ref`` applies only to surface messages.
|
|
24
|
+
|
|
25
|
+
Comm-B BDS 4,4/4,5 meteorological inference is heuristic and disabled by
|
|
26
|
+
default. Pass ``include_meteo=True`` to :func:`decode` or
|
|
27
|
+
:class:`PipeDecoder` when these reports are expected on the input feed.
|
|
24
28
|
"""
|
|
25
29
|
|
|
26
30
|
from importlib.metadata import version as _version
|
|
@@ -104,6 +104,8 @@ class PipeDecoder(ValidationMixin):
|
|
|
104
104
|
surface_ref: Optional surface-position CPR reference (ICAO airport
|
|
105
105
|
code or (lat, lon) tuple). Used only for BDS 0,6 surface messages.
|
|
106
106
|
See pyModeS.decode for details.
|
|
107
|
+
include_meteo: When True, Comm-B inference also considers the
|
|
108
|
+
heuristic BDS 4,4 and 4,5 meteorological registers. Default False.
|
|
107
109
|
full_dict: When True, every decoded result is populated with
|
|
108
110
|
every key from _FULL_SCHEMA.
|
|
109
111
|
pair_window: Maximum age difference (seconds) between an even
|
|
@@ -129,6 +131,7 @@ class PipeDecoder(ValidationMixin):
|
|
|
129
131
|
"_eviction_interval",
|
|
130
132
|
"_eviction_ttl",
|
|
131
133
|
"_full_dict",
|
|
134
|
+
"_include_meteo",
|
|
132
135
|
"_local_ref_window",
|
|
133
136
|
"_max_speed_kmps",
|
|
134
137
|
"_motion_margin_km",
|
|
@@ -147,6 +150,7 @@ class PipeDecoder(ValidationMixin):
|
|
|
147
150
|
self,
|
|
148
151
|
*,
|
|
149
152
|
surface_ref: str | tuple[float, float] | None = None,
|
|
153
|
+
include_meteo: bool = False,
|
|
150
154
|
full_dict: bool = False,
|
|
151
155
|
pair_window: float = 10.0,
|
|
152
156
|
local_ref_window: float = 30.0,
|
|
@@ -156,6 +160,7 @@ class PipeDecoder(ValidationMixin):
|
|
|
156
160
|
motion_margin_km: float = 2.0,
|
|
157
161
|
) -> None:
|
|
158
162
|
self._surface_ref = surface_ref
|
|
163
|
+
self._include_meteo = include_meteo
|
|
159
164
|
self._full_dict = full_dict
|
|
160
165
|
self._pair_window = pair_window
|
|
161
166
|
if local_ref_window < 0:
|
|
@@ -307,11 +312,22 @@ class PipeDecoder(ValidationMixin):
|
|
|
307
312
|
else:
|
|
308
313
|
known = None
|
|
309
314
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
+
# Avoid forwarding a false opt-in flag on every message. Keyword
|
|
316
|
+
# dispatch is measurable in this hot path; only enabled streams need
|
|
317
|
+
# the additional Comm-B inference branch.
|
|
318
|
+
if self._include_meteo:
|
|
319
|
+
result = message.decode(
|
|
320
|
+
surface_ref=self._surface_ref,
|
|
321
|
+
known=known,
|
|
322
|
+
include_meteo=True,
|
|
323
|
+
full_dict=self._full_dict,
|
|
324
|
+
)
|
|
325
|
+
else:
|
|
326
|
+
result = message.decode(
|
|
327
|
+
surface_ref=self._surface_ref,
|
|
328
|
+
known=known,
|
|
329
|
+
full_dict=self._full_dict,
|
|
330
|
+
)
|
|
315
331
|
|
|
316
332
|
self._stats["decoded"] += 1
|
|
317
333
|
if result.get("crc_valid") is False:
|
|
@@ -76,6 +76,11 @@ def _add_decode_parser(
|
|
|
76
76
|
action="store_true",
|
|
77
77
|
help="Populate every key in the canonical schema (missing = null).",
|
|
78
78
|
)
|
|
79
|
+
decode_p.add_argument(
|
|
80
|
+
"--include-meteo",
|
|
81
|
+
action="store_true",
|
|
82
|
+
help="Include heuristic Comm-B BDS 4,4/4,5 meteorological inference.",
|
|
83
|
+
)
|
|
79
84
|
decode_p.add_argument(
|
|
80
85
|
"--reference",
|
|
81
86
|
nargs=2,
|
|
@@ -132,6 +137,11 @@ def _add_live_parser(
|
|
|
132
137
|
action="store_true",
|
|
133
138
|
help="Emit every key in the canonical schema (missing = null).",
|
|
134
139
|
)
|
|
140
|
+
live_p.add_argument(
|
|
141
|
+
"--include-meteo",
|
|
142
|
+
action="store_true",
|
|
143
|
+
help="Include heuristic Comm-B BDS 4,4/4,5 meteorological inference.",
|
|
144
|
+
)
|
|
135
145
|
live_p.add_argument(
|
|
136
146
|
"--dump-to",
|
|
137
147
|
metavar="FILE",
|
|
@@ -50,6 +50,7 @@ def _run_single(args: argparse.Namespace) -> int:
|
|
|
50
50
|
args.message,
|
|
51
51
|
reference=reference,
|
|
52
52
|
surface_ref=surface_ref,
|
|
53
|
+
include_meteo=args.include_meteo,
|
|
53
54
|
full_dict=args.full_dict,
|
|
54
55
|
)
|
|
55
56
|
except Exception as e:
|
|
@@ -131,6 +132,7 @@ def _emit_batch(
|
|
|
131
132
|
hexes,
|
|
132
133
|
timestamps=timestamps,
|
|
133
134
|
surface_ref=surface_ref,
|
|
135
|
+
include_meteo=args.include_meteo,
|
|
134
136
|
full_dict=args.full_dict,
|
|
135
137
|
)
|
|
136
138
|
except Exception as error:
|
|
@@ -55,7 +55,11 @@ def run(args: argparse.Namespace) -> int:
|
|
|
55
55
|
)
|
|
56
56
|
return 2
|
|
57
57
|
|
|
58
|
-
pipe = PipeDecoder(
|
|
58
|
+
pipe = PipeDecoder(
|
|
59
|
+
surface_ref=surface_ref,
|
|
60
|
+
include_meteo=args.include_meteo,
|
|
61
|
+
full_dict=args.full_dict,
|
|
62
|
+
)
|
|
59
63
|
|
|
60
64
|
# TUI path takes its own branch — textual owns the event loop
|
|
61
65
|
# and drives the source + pipe itself, so the sink pipeline
|
|
@@ -25,6 +25,7 @@ def decode(
|
|
|
25
25
|
reference: tuple[float, float] | None = None,
|
|
26
26
|
surface_ref: str | tuple[float, float] | None = None,
|
|
27
27
|
known: dict[str, Any] | None = None,
|
|
28
|
+
include_meteo: bool = False,
|
|
28
29
|
full_dict: bool = False,
|
|
29
30
|
) -> Decoded: ...
|
|
30
31
|
|
|
@@ -35,6 +36,7 @@ def decode(
|
|
|
35
36
|
*,
|
|
36
37
|
timestamps: list[float] | None = None,
|
|
37
38
|
surface_ref: str | tuple[float, float] | None = None,
|
|
39
|
+
include_meteo: bool = False,
|
|
38
40
|
full_dict: bool = False,
|
|
39
41
|
) -> list[Decoded]: ...
|
|
40
42
|
|
|
@@ -48,6 +50,7 @@ def decode(
|
|
|
48
50
|
reference: Any = None,
|
|
49
51
|
surface_ref: Any = None,
|
|
50
52
|
known: Any = None,
|
|
53
|
+
include_meteo: bool = False,
|
|
51
54
|
full_dict: bool = False,
|
|
52
55
|
timestamps: Any = None,
|
|
53
56
|
) -> Any:
|
|
@@ -79,6 +82,9 @@ def decode(
|
|
|
79
82
|
Comm-B BDS inference to disambiguate BDS 5,0 vs 6,0
|
|
80
83
|
when both heuristic validators pass. Ignored for
|
|
81
84
|
non-Comm-B downlink formats.
|
|
85
|
+
include_meteo: When True, Comm-B inference also considers BDS 4,4
|
|
86
|
+
and 4,5 meteorological registers. Disabled by default because
|
|
87
|
+
their heuristic payload patterns can overlap other registers.
|
|
82
88
|
full_dict: When True, the result dict is augmented with
|
|
83
89
|
every key from `_FULL_SCHEMA`, defaulting missing keys
|
|
84
90
|
to `None`. Useful for pandas/parquet workflows that
|
|
@@ -102,6 +108,7 @@ def decode(
|
|
|
102
108
|
WARNING is logged — pair matching will still function
|
|
103
109
|
but the synthesized values are not wall-clock times.
|
|
104
110
|
surface_ref: Same as single-message mode.
|
|
111
|
+
include_meteo: Same as single-message mode.
|
|
105
112
|
full_dict: Same as single-message mode.
|
|
106
113
|
|
|
107
114
|
Batch mode returns a ``list[Decoded]`` of the same length as
|
|
@@ -140,6 +147,7 @@ def decode(
|
|
|
140
147
|
msg,
|
|
141
148
|
timestamps=timestamps,
|
|
142
149
|
surface_ref=surface_ref,
|
|
150
|
+
include_meteo=include_meteo,
|
|
143
151
|
full_dict=full_dict,
|
|
144
152
|
)
|
|
145
153
|
|
|
@@ -164,6 +172,7 @@ def decode(
|
|
|
164
172
|
reference=reference,
|
|
165
173
|
surface_ref=surface_ref,
|
|
166
174
|
known=known,
|
|
175
|
+
include_meteo=include_meteo,
|
|
167
176
|
full_dict=full_dict,
|
|
168
177
|
)
|
|
169
178
|
|
|
@@ -173,6 +182,7 @@ def _decode_batch(
|
|
|
173
182
|
*,
|
|
174
183
|
timestamps: list[float] | None,
|
|
175
184
|
surface_ref: str | tuple[float, float] | None,
|
|
185
|
+
include_meteo: bool,
|
|
176
186
|
full_dict: bool,
|
|
177
187
|
) -> list[Decoded]:
|
|
178
188
|
"""Run ``msgs`` through a transient PipeDecoder and return results."""
|
|
@@ -193,7 +203,11 @@ def _decode_batch(
|
|
|
193
203
|
f"messages length {len(msgs)}"
|
|
194
204
|
)
|
|
195
205
|
|
|
196
|
-
pipe = PipeDecoder(
|
|
206
|
+
pipe = PipeDecoder(
|
|
207
|
+
surface_ref=surface_ref,
|
|
208
|
+
include_meteo=include_meteo,
|
|
209
|
+
full_dict=full_dict,
|
|
210
|
+
)
|
|
197
211
|
results = [
|
|
198
212
|
pipe.decode(m, timestamp=t) for m, t in zip(msgs, timestamps, strict=True)
|
|
199
213
|
]
|
|
@@ -59,7 +59,12 @@ _COMMB_DISPATCH: dict[str, Callable[[int], dict[str, Any]]] = {
|
|
|
59
59
|
class CommB(DecoderBase):
|
|
60
60
|
"""Decoder for DF20 Comm-B altitude replies and DF21 Comm-B identity replies."""
|
|
61
61
|
|
|
62
|
-
def decode(
|
|
62
|
+
def decode(
|
|
63
|
+
self,
|
|
64
|
+
*,
|
|
65
|
+
known: dict[str, Any] | None = None,
|
|
66
|
+
include_meteo: bool = False,
|
|
67
|
+
) -> Decoded:
|
|
63
68
|
result: Decoded = Decoded()
|
|
64
69
|
|
|
65
70
|
# Header field: altitude for DF20, squawk for DF21.
|
|
@@ -75,7 +80,7 @@ class CommB(DecoderBase):
|
|
|
75
80
|
# disambiguation of BDS 5,0 / 6,0 when the caller supplies
|
|
76
81
|
# aircraft state.
|
|
77
82
|
candidates = _infer.infer(
|
|
78
|
-
self._payload, self._df, include_meteo=
|
|
83
|
+
self._payload, self._df, include_meteo=include_meteo, known=known
|
|
79
84
|
)
|
|
80
85
|
if not candidates:
|
|
81
86
|
return result
|
|
@@ -17,11 +17,14 @@ This module defines two public types:
|
|
|
17
17
|
|
|
18
18
|
import json
|
|
19
19
|
from functools import cached_property
|
|
20
|
-
from typing import Any, Self
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Self, cast
|
|
21
21
|
|
|
22
22
|
from pyModeS._bits import crc_remainder, extract_unsigned
|
|
23
23
|
from pyModeS.errors import InvalidHexError, InvalidLengthError, UnknownDFError
|
|
24
24
|
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from pyModeS.decoder.commb import CommB
|
|
27
|
+
|
|
25
28
|
_HEX_CHARS = frozenset("0123456789abcdefABCDEF")
|
|
26
29
|
_VALID_LENGTHS = (56, 112)
|
|
27
30
|
_HEX_LENGTHS = (14, 28)
|
|
@@ -242,6 +245,7 @@ class Message:
|
|
|
242
245
|
reference: tuple[float, float] | None = None,
|
|
243
246
|
surface_ref: str | tuple[float, float] | None = None,
|
|
244
247
|
known: dict[str, Any] | None = None,
|
|
248
|
+
include_meteo: bool = False,
|
|
245
249
|
full_dict: bool = False,
|
|
246
250
|
) -> Decoded:
|
|
247
251
|
"""Decode every field of this message.
|
|
@@ -270,6 +274,10 @@ class Message:
|
|
|
270
274
|
Comm-B BDS inference to disambiguate BDS 5,0 vs 6,0
|
|
271
275
|
when both heuristic validators pass. Ignored for
|
|
272
276
|
non-Comm-B downlink formats.
|
|
277
|
+
include_meteo: When True, Comm-B inference also considers the
|
|
278
|
+
heuristic BDS 4,4 and 4,5 meteorological registers. Disabled
|
|
279
|
+
by default because their payloads can overlap other Comm-B
|
|
280
|
+
formats.
|
|
273
281
|
full_dict: When True, the result dict is augmented with
|
|
274
282
|
every key from `_FULL_SCHEMA`, defaulting missing
|
|
275
283
|
keys to `None`. Useful for pandas/parquet workflows
|
|
@@ -291,7 +299,14 @@ class Message:
|
|
|
291
299
|
decoder = decoder_cls(
|
|
292
300
|
self._n, df=self.df, icao=self.icao, length=self._length
|
|
293
301
|
)
|
|
294
|
-
|
|
302
|
+
# Keep the common non-meteorological path free of an extra keyword
|
|
303
|
+
# dispatch. This matters for high-rate ADS-B streams, while the
|
|
304
|
+
# opt-in branch is relevant only to DF20/21 Comm-B decoders.
|
|
305
|
+
if include_meteo and self.df in (20, 21):
|
|
306
|
+
commb_decoder = cast("CommB", decoder)
|
|
307
|
+
result.update(commb_decoder.decode(known=known, include_meteo=True))
|
|
308
|
+
else:
|
|
309
|
+
result.update(decoder.decode(known=known))
|
|
295
310
|
|
|
296
311
|
self._resolve_position(result, reference=reference, surface_ref=surface_ref)
|
|
297
312
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|