pyModeS 2.21__tar.gz → 2.22.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.
Files changed (113) hide show
  1. {pymodes-2.21 → pymodes-2.22.0}/.github/workflows/build-publish.yml +15 -4
  2. {pymodes-2.21 → pymodes-2.22.0}/.gitignore +5 -0
  3. {pymodes-2.21 → pymodes-2.22.0}/PKG-INFO +8 -8
  4. {pymodes-2.21 → pymodes-2.22.0}/README.rst +7 -7
  5. {pymodes-2.21 → pymodes-2.22.0}/pyproject.toml +3 -3
  6. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/__init__.py +7 -13
  7. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/c_common.pxd +3 -3
  8. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/c_common.pyx +20 -23
  9. pymodes-2.21/src/pyModeS/c_common.pyi → pymodes-2.22.0/src/pyModeS/common.pyi +8 -4
  10. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/__init__.py +4 -7
  11. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/adsb.py +54 -37
  12. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/allcall.py +2 -6
  13. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/__init__.py +1 -3
  14. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds05.py +2 -7
  15. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds06.py +19 -3
  16. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds09.py +23 -20
  17. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds44.py +5 -5
  18. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds45.py +2 -3
  19. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds60.py +3 -3
  20. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds61.py +2 -6
  21. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds62.py +2 -3
  22. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/commb.py +35 -35
  23. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/ehs.py +17 -17
  24. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/flarm/__init__.py +1 -0
  25. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/flarm/decode.pyi +0 -1
  26. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/surv.py +1 -0
  27. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/uncertainty.py +1 -3
  28. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/uplink.py +8 -10
  29. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/extra/rtlreader.py +4 -6
  30. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/extra/tcpclient.py +12 -10
  31. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/py_common.py +16 -14
  32. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/streamer/decode.py +19 -12
  33. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/streamer/modeslive.py +11 -8
  34. {pymodes-2.21 → pymodes-2.22.0}/tests/benchmark.py +2 -0
  35. {pymodes-2.21 → pymodes-2.22.0}/tests/sample_run_adsb.py +0 -1
  36. {pymodes-2.21 → pymodes-2.22.0}/tests/sample_run_commb.py +1 -1
  37. pymodes-2.22.0/tests/test_adsb.py +266 -0
  38. {pymodes-2.21 → pymodes-2.22.0}/tests/test_bds_inference.py +25 -0
  39. pymodes-2.22.0/tests/test_c_common.py +106 -0
  40. {pymodes-2.21 → pymodes-2.22.0}/tests/test_commb.py +2 -1
  41. pymodes-2.22.0/tests/test_common_contract.py +84 -0
  42. pymodes-2.22.0/tests/test_full.py +17 -0
  43. pymodes-2.22.0/tests/test_py_common.py +148 -0
  44. {pymodes-2.21 → pymodes-2.22.0}/uv.lock +435 -431
  45. pymodes-2.21/.github/dependabot.yml +0 -11
  46. pymodes-2.21/tests/test_adsb.py +0 -132
  47. pymodes-2.21/tests/test_c_common.py +0 -60
  48. pymodes-2.21/tests/test_py_common.py +0 -64
  49. {pymodes-2.21 → pymodes-2.22.0}/.coveragerc +0 -0
  50. {pymodes-2.21 → pymodes-2.22.0}/LICENSE +0 -0
  51. {pymodes-2.21 → pymodes-2.22.0}/doc/Makefile +0 -0
  52. {pymodes-2.21 → pymodes-2.22.0}/doc/README.rst +0 -0
  53. {pymodes-2.21 → pymodes-2.22.0}/doc/modeslive-screenshot.png +0 -0
  54. {pymodes-2.21 → pymodes-2.22.0}/doc/source/_templates/layout.html +0 -0
  55. {pymodes-2.21 → pymodes-2.22.0}/doc/source/conf.py +0 -0
  56. {pymodes-2.21 → pymodes-2.22.0}/doc/source/index.rst +0 -0
  57. {pymodes-2.21 → pymodes-2.22.0}/doc/source/modules.rst +0 -0
  58. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.c_common.rst +0 -0
  59. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.common.rst +0 -0
  60. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.acas.rst +0 -0
  61. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.adsb.rst +0 -0
  62. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.allcall.rst +0 -0
  63. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds05.rst +0 -0
  64. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds06.rst +0 -0
  65. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds08.rst +0 -0
  66. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds09.rst +0 -0
  67. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds10.rst +0 -0
  68. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds17.rst +0 -0
  69. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds20.rst +0 -0
  70. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds30.rst +0 -0
  71. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds40.rst +0 -0
  72. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds44.rst +0 -0
  73. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds45.rst +0 -0
  74. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds50.rst +0 -0
  75. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds53.rst +0 -0
  76. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.bds60.rst +0 -0
  77. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.bds.rst +0 -0
  78. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.commb.rst +0 -0
  79. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.rst +0 -0
  80. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.surv.rst +0 -0
  81. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.uncertainty.rst +0 -0
  82. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.decoder.uplink.rst +0 -0
  83. {pymodes-2.21 → pymodes-2.22.0}/doc/source/pyModeS.rst +0 -0
  84. {pymodes-2.21 → pymodes-2.22.0}/doc/warnings +0 -0
  85. {pymodes-2.21 → pymodes-2.22.0}/hatch_build.py +0 -0
  86. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/.gitignore +0 -0
  87. pymodes-2.21/src/pyModeS/common.pyi → pymodes-2.22.0/src/pyModeS/c_common.pyi +0 -0
  88. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/acas.py +0 -0
  89. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds08.py +0 -0
  90. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds10.py +0 -0
  91. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds17.py +0 -0
  92. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds20.py +0 -0
  93. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds30.py +0 -0
  94. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds40.py +0 -0
  95. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds50.py +0 -0
  96. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/bds/bds53.py +0 -0
  97. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/els.py +3 -3
  98. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/flarm/core.c +0 -0
  99. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/flarm/core.h +0 -0
  100. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/flarm/core.pxd +0 -0
  101. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/decoder/flarm/decode.pyx +0 -0
  102. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/extra/__init__.py +0 -0
  103. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/extra/aero.py +0 -0
  104. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/py.typed +0 -0
  105. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/streamer/__init__.py +0 -0
  106. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/streamer/screen.py +3 -3
  107. {pymodes-2.21 → pymodes-2.22.0}/src/pyModeS/streamer/source.py +1 -1
  108. {pymodes-2.21 → pymodes-2.22.0}/tests/data/sample_data_adsb.csv +0 -0
  109. {pymodes-2.21 → pymodes-2.22.0}/tests/data/sample_data_commb_df20.csv +0 -0
  110. {pymodes-2.21 → pymodes-2.22.0}/tests/data/sample_data_commb_df21.csv +0 -0
  111. {pymodes-2.21 → pymodes-2.22.0}/tests/test_allcall.py +0 -0
  112. {pymodes-2.21 → pymodes-2.22.0}/tests/test_surv.py +0 -0
  113. {pymodes-2.21 → pymodes-2.22.0}/tests/test_tell.py +0 -0
@@ -1,10 +1,14 @@
1
1
  name: Build and publish pyModeS
2
2
 
3
3
  on:
4
- release:
5
- types: [published]
4
+ push:
5
+ tags:
6
+ - "v*"
6
7
  workflow_dispatch:
7
8
 
9
+ permissions:
10
+ contents: write
11
+
8
12
  env:
9
13
  CIBW_BUILD: cp310* cp311* cp312* cp313*
10
14
  CIBW_ARCHS_WINDOWS: auto64
@@ -59,7 +63,8 @@ jobs:
59
63
  environment: pypi
60
64
  permissions:
61
65
  id-token: write
62
- if: github.event_name == 'release' && github.event.action == 'published'
66
+ contents: write
67
+ if: startsWith(github.ref, 'refs/tags/v')
63
68
  steps:
64
69
  - uses: actions/download-artifact@v4
65
70
  with:
@@ -71,4 +76,10 @@ jobs:
71
76
  - uses: pypa/gh-action-pypi-publish@release/v1
72
77
  with:
73
78
  user: __token__
74
- password: ${{ secrets.PYPI_API_TOKEN_PYMODES }}
79
+ password: ${{ secrets.PYPI_API_TOKEN_PYMODES }}
80
+
81
+ - name: Create GitHub Release
82
+ uses: softprops/action-gh-release@v2
83
+ with:
84
+ generate_release_notes: true
85
+ files: dist/*
@@ -1,3 +1,5 @@
1
+ tmp/
2
+
1
3
  .DS_Store
2
4
 
3
5
  # Byte-compiled / optimized / DLL files
@@ -53,6 +55,9 @@ coverage.xml
53
55
  # Sphinx documentation
54
56
  docs/_build/
55
57
 
58
+ # Local planning / brainstorming artifacts (not for publication)
59
+ docs/superpowers/
60
+
56
61
  # PyBuilder
57
62
  target/
58
63
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyModeS
3
- Version: 2.21
3
+ Version: 2.22.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
@@ -123,7 +123,7 @@ If you want to make use of the (faster) c module, install ``pyModeS`` as follows
123
123
  # development version
124
124
  git clone https://github.com/junzis/pyModeS
125
125
  cd pyModeS
126
- poetry install -E rtlsdr
126
+ uv sync --dev --all-extras
127
127
 
128
128
 
129
129
  View live traffic (modeslive)
@@ -208,7 +208,6 @@ Core functions for ADS-B decoding
208
208
  pms.adsb.position(msg_even, msg_odd, t_even, t_odd, lat_ref=None, lon_ref=None)
209
209
  pms.adsb.airborne_position(msg_even, msg_odd, t_even, t_odd)
210
210
  pms.adsb.surface_position(msg_even, msg_odd, t_even, t_odd, lat_ref, lon_ref)
211
- pms.adsb.surface_velocity(msg)
212
211
 
213
212
  pms.adsb.position_with_ref(msg, lat_ref, lon_ref)
214
213
  pms.adsb.airborne_position_with_ref(msg, lat_ref, lon_ref)
@@ -219,17 +218,18 @@ Core functions for ADS-B decoding
219
218
  # Typecode: 19
220
219
  pms.adsb.velocity(msg) # Handles both surface & airborne messages
221
220
  pms.adsb.speed_heading(msg) # Handles both surface & airborne messages
222
- pms.adsb.airborne_velocity(msg)
221
+ pms.adsb.surface_velocity(msg, source)
222
+ pms.adsb.airborne_velocity(msg, source)
223
223
 
224
224
 
225
225
  Note: When you have a fix position of the aircraft, it is convenient to use `position_with_ref()` method to decode with only one position message (either odd or even). This works with both airborne and surface position messages. But the reference position shall be within 180NM (airborne) or 45NM (surface) of the true position.
226
226
 
227
227
 
228
- Decode altitude replies in DF4 / DF20
229
- **************************************
228
+ Decode altitude replies in DF0 / DF4 / DF16 /DF20
229
+ **************************************************
230
230
  .. code:: python
231
231
 
232
- pms.common.altcode(msg) # Downlink format must be 4 or 20
232
+ pms.common.altcode(msg) # Downlink format must be 0, 4, 16 or 20
233
233
 
234
234
 
235
235
  Decode identity replies in DF5 / DF21
@@ -305,7 +305,7 @@ To identify BDS 4,4 and 4,5 codes, you must set ``mrar`` argument to ``True`` in
305
305
 
306
306
  .. code:: python
307
307
 
308
- pms.bds.infer(msg. mrar=True)
308
+ pms.bds.infer(msg, mrar=True)
309
309
 
310
310
  Once the correct MRAR and MHR messages are identified, decode them as follows:
311
311
 
@@ -100,7 +100,7 @@ If you want to make use of the (faster) c module, install ``pyModeS`` as follows
100
100
  # development version
101
101
  git clone https://github.com/junzis/pyModeS
102
102
  cd pyModeS
103
- poetry install -E rtlsdr
103
+ uv sync --dev --all-extras
104
104
 
105
105
 
106
106
  View live traffic (modeslive)
@@ -185,7 +185,6 @@ Core functions for ADS-B decoding
185
185
  pms.adsb.position(msg_even, msg_odd, t_even, t_odd, lat_ref=None, lon_ref=None)
186
186
  pms.adsb.airborne_position(msg_even, msg_odd, t_even, t_odd)
187
187
  pms.adsb.surface_position(msg_even, msg_odd, t_even, t_odd, lat_ref, lon_ref)
188
- pms.adsb.surface_velocity(msg)
189
188
 
190
189
  pms.adsb.position_with_ref(msg, lat_ref, lon_ref)
191
190
  pms.adsb.airborne_position_with_ref(msg, lat_ref, lon_ref)
@@ -196,17 +195,18 @@ Core functions for ADS-B decoding
196
195
  # Typecode: 19
197
196
  pms.adsb.velocity(msg) # Handles both surface & airborne messages
198
197
  pms.adsb.speed_heading(msg) # Handles both surface & airborne messages
199
- pms.adsb.airborne_velocity(msg)
198
+ pms.adsb.surface_velocity(msg, source)
199
+ pms.adsb.airborne_velocity(msg, source)
200
200
 
201
201
 
202
202
  Note: When you have a fix position of the aircraft, it is convenient to use `position_with_ref()` method to decode with only one position message (either odd or even). This works with both airborne and surface position messages. But the reference position shall be within 180NM (airborne) or 45NM (surface) of the true position.
203
203
 
204
204
 
205
- Decode altitude replies in DF4 / DF20
206
- **************************************
205
+ Decode altitude replies in DF0 / DF4 / DF16 /DF20
206
+ **************************************************
207
207
  .. code:: python
208
208
 
209
- pms.common.altcode(msg) # Downlink format must be 4 or 20
209
+ pms.common.altcode(msg) # Downlink format must be 0, 4, 16 or 20
210
210
 
211
211
 
212
212
  Decode identity replies in DF5 / DF21
@@ -282,7 +282,7 @@ To identify BDS 4,4 and 4,5 codes, you must set ``mrar`` argument to ``True`` in
282
282
 
283
283
  .. code:: python
284
284
 
285
- pms.bds.infer(msg. mrar=True)
285
+ pms.bds.infer(msg, mrar=True)
286
286
 
287
287
  Once the correct MRAR and MHR messages are identified, decode them as follows:
288
288
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyModeS"
3
- version = "2.21"
3
+ version = "2.22.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" }
@@ -27,8 +27,8 @@ homepage = "https://mode-s.org"
27
27
  repository = "https://github.com/junzis/pyModeS"
28
28
  issues = "https://github.com/junzis/pyModeS/issues"
29
29
 
30
- [tool.uv]
31
- dev-dependencies = [
30
+ [dependency-groups]
31
+ dev = [
32
32
  "mypy>=0.991",
33
33
  "flake8>=5.0.0",
34
34
  "black>=22.12.0",
@@ -8,25 +8,19 @@ except Exception:
8
8
  from . import py_common as common # type: ignore
9
9
  from .py_common import * # type: ignore
10
10
 
11
- from .decoder import tell
12
- from .decoder import adsb
13
- from .decoder import commb
14
- from .decoder import allcall
15
- from .decoder import surv
16
- from .decoder import bds
17
- from .extra import aero
18
- from .extra import tcpclient
11
+ from .decoder import adsb, allcall, bds, commb, surv, tell
12
+ from .extra import aero, tcpclient
19
13
 
20
14
  __all__ = [
21
- "common",
22
- "tell",
23
15
  "adsb",
24
- "commb",
16
+ "aero",
25
17
  "allcall",
26
- "surv",
27
18
  "bds",
28
- "aero",
19
+ "commb",
20
+ "common",
21
+ "surv",
29
22
  "tcpclient",
23
+ "tell",
30
24
  ]
31
25
 
32
26
 
@@ -15,14 +15,14 @@ cpdef long floor(double x)
15
15
  cpdef str icao(str msg)
16
16
  cpdef bint is_icao_assigned(str icao)
17
17
 
18
- cpdef int typecode(str msg)
18
+ cpdef object typecode(str msg)
19
19
  cpdef int cprNL(double lat)
20
20
 
21
21
  cpdef str idcode(str msg)
22
22
  cpdef str squawk(str binstr)
23
23
 
24
- cpdef int altcode(str msg)
25
- cpdef int altitude(str binstr)
24
+ cpdef object altcode(str msg)
25
+ cpdef object altitude(str binstr)
26
26
 
27
27
  cpdef str data(str msg)
28
28
  cpdef bint allzeros(str msg)
@@ -97,7 +97,7 @@ cpdef long crc(str msg, bint encode=False):
97
97
  msg (string): 28 bytes hexadecimal message string
98
98
  encode (bool): True to encode the date only and return the checksum
99
99
  Returns:
100
- int: message checksum, or partity bits (encoder)
100
+ int: message checksum, or parity bits (encoder)
101
101
 
102
102
  """
103
103
  # the CRC generator
@@ -183,34 +183,33 @@ cpdef bint is_icao_assigned(str icao):
183
183
 
184
184
  cdef long icaoint = hex2int(icao)
185
185
 
186
- if 0x200000 < icaoint < 0x27FFFF:
186
+ if 0x200000 <= icaoint <= 0x27FFFF:
187
187
  return False # AFI
188
- if 0x280000 < icaoint < 0x28FFFF:
188
+ if 0x280000 <= icaoint <= 0x28FFFF:
189
189
  return False # SAM
190
- if 0x500000 < icaoint < 0x5FFFFF:
190
+ if 0x500000 <= icaoint <= 0x5FFFFF:
191
191
  return False # EUR, NAT
192
- if 0x600000 < icaoint < 0x67FFFF:
192
+ if 0x600000 <= icaoint <= 0x67FFFF:
193
193
  return False # MID
194
- if 0x680000 < icaoint < 0x6F0000:
194
+ if 0x680000 <= icaoint <= 0x6F0000:
195
195
  return False # ASIA
196
- if 0x900000 < icaoint < 0x9FFFFF:
196
+ if 0x900000 <= icaoint <= 0x9FFFFF:
197
197
  return False # NAM, PAC
198
- if 0xB00000 < icaoint < 0xBFFFFF:
198
+ if 0xB00000 <= icaoint <= 0xBFFFFF:
199
199
  return False # CAR
200
- if 0xD00000 < icaoint < 0xDFFFFF:
200
+ if 0xD00000 <= icaoint <= 0xDFFFFF:
201
201
  return False # future
202
- if 0xF00000 < icaoint < 0xFFFFFF:
202
+ if 0xF00000 <= icaoint <= 0xFFFFFF:
203
203
  return False # future
204
204
 
205
205
  return True
206
206
 
207
207
  @cython.boundscheck(False)
208
208
  @cython.wraparound(False)
209
- cpdef int typecode(str msg):
209
+ cpdef object typecode(str msg):
210
210
  """Type code of ADS-B message"""
211
211
  if df(msg) not in (17, 18):
212
- return -1
213
- # return None
212
+ return None
214
213
 
215
214
  cdef str tcbin = hex2bin(msg[8:10])
216
215
  return bin2int(tcbin[0:5])
@@ -282,18 +281,17 @@ cpdef str squawk(str binstr):
282
281
 
283
282
  @cython.boundscheck(False)
284
283
  @cython.wraparound(False)
285
- cpdef int altcode(str msg):
284
+ cpdef object altcode(str msg):
286
285
  """Compute the altitude."""
287
286
  if df(msg) not in [0, 4, 16, 20]:
288
287
  raise RuntimeError("Message must be Downlink Format 0, 4, 16, or 20.")
289
288
 
290
- alt = altitude(hex2bin(msg)[19:32])
291
- return alt
289
+ return altitude(hex2bin(msg)[19:32])
292
290
 
293
291
 
294
292
  @cython.boundscheck(False)
295
293
  @cython.wraparound(False)
296
- cpdef int altitude(str binstr):
294
+ cpdef object altitude(str binstr):
297
295
 
298
296
  if len(binstr) != 13 or not set(binstr).issubset(set("01")):
299
297
  raise RuntimeError("Input must be 13 bits binary string")
@@ -304,20 +302,20 @@ cpdef int altitude(str binstr):
304
302
  cdef char Mbit = binstr[6]
305
303
  cdef char Qbit = binstr[8]
306
304
 
307
- cdef int alt = 0
305
+ alt = None
308
306
  cdef bytearray vbin
309
307
  cdef bytearray _graybytes = bytearray(11)
310
308
  cdef unsigned char[:] graybytes = _graybytes
311
309
 
312
310
  if bin2int(binstr) == 0:
313
311
  # altitude unknown or invalid
314
- alt = -999999
312
+ return None
315
313
 
316
314
  elif Mbit == 48: # unit in ft, "0" -> 48
317
315
  if Qbit == 49: # 25ft interval, "1" -> 49
318
316
  vbin = _mbin[:6] + _mbin[7:8] + _mbin[9:]
319
317
  alt = bin2int(vbin.decode()) * 25 - 1000
320
- if Qbit == 48: # 100ft interval, above 50175ft, "0" -> 48
318
+ if Qbit == 48: # 100ft interval, above 50187.5ft, "0" -> 48
321
319
  graybytes[8] = mbin[0]
322
320
  graybytes[2] = mbin[1]
323
321
  graybytes[9] = mbin[2]
@@ -341,7 +339,7 @@ cpdef int altitude(str binstr):
341
339
  return alt
342
340
 
343
341
 
344
- cpdef int gray2alt(str codestr):
342
+ cpdef object gray2alt(str codestr):
345
343
  cdef str gc500 = codestr[:8]
346
344
  cdef int n500 = gray2int(gc500)
347
345
 
@@ -350,8 +348,7 @@ cpdef int gray2alt(str codestr):
350
348
  cdef int n100 = gray2int(gc100)
351
349
 
352
350
  if n100 in [0, 5, 6]:
353
- return -1
354
- #return None
351
+ return None
355
352
 
356
353
  if n100 == 7:
357
354
  n100 = 5
@@ -1,3 +1,5 @@
1
+ from typing import Optional
2
+
1
3
  def hex2bin(hexstr: str) -> str: ...
2
4
  def bin2int(binstr: str) -> int: ...
3
5
  def hex2int(hexstr: str) -> int: ...
@@ -5,14 +7,16 @@ def bin2hex(binstr: str) -> str: ...
5
7
  def df(msg: str) -> int: ...
6
8
  def crc(msg: str, encode: bool = False) -> int: ...
7
9
  def floor(x: float) -> float: ...
8
- def icao(msg: str) -> str: ...
10
+ def icao(msg: str) -> Optional[str]: ...
9
11
  def is_icao_assigned(icao: str) -> bool: ...
10
- def typecode(msg: str) -> int: ...
12
+ def typecode(msg: str) -> Optional[int]: ...
11
13
  def cprNL(lat: float) -> int: ...
12
14
  def idcode(msg: str) -> str: ...
13
15
  def squawk(binstr: str) -> str: ...
14
- def altcode(msg: str) -> int: ...
15
- def altitude(binstr: str) -> int: ...
16
+ def altcode(msg: str) -> Optional[int]: ...
17
+ def altitude(binstr: str) -> Optional[int]: ...
18
+ def gray2alt(binstr: str) -> Optional[int]: ...
19
+ def gray2int(binstr: str) -> int: ...
16
20
  def data(msg: str) -> str: ...
17
21
  def allzeros(msg: str) -> bool: ...
18
22
  def wrongstatus(data: str, sb: int, msb: int, lsb: int) -> bool: ...
@@ -1,5 +1,5 @@
1
1
  def tell(msg: str) -> None:
2
- from .. import common, adsb, commb, bds
2
+ from .. import adsb, bds, commb, common
3
3
 
4
4
  def _print(label, value, unit=None):
5
5
  print("%28s: " % label, end="")
@@ -132,7 +132,7 @@ def tell(msg: str) -> None:
132
132
  _print("TCAS/ACAS RA", tcas_ra_types[tcas_ra])
133
133
  _print("Emergency status", emergency_types[emergency_status])
134
134
  else:
135
- alt, alt_source = adsb.selected_altitude(msg) # type: ignore
135
+ alt, alt_source = adsb.selected_altitude(msg)
136
136
  baro = adsb.baro_pressure_setting(msg)
137
137
  hdg = adsb.selected_heading(msg)
138
138
  autopilot = adsb.autopilot(msg)
@@ -149,9 +149,7 @@ def tell(msg: str) -> None:
149
149
  )
150
150
  _print("Selected Heading", hdg, "°")
151
151
  if not (common.bin2int((common.hex2bin(msg)[32:])[46]) == 0):
152
- _print(
153
- "Autopilot", types_29[autopilot] if autopilot else None
154
- )
152
+ _print("Autopilot", types_29[autopilot] if autopilot else None)
155
153
  _print("VNAV mode", types_29[vnav] if vnav else None)
156
154
  _print(
157
155
  "Altitude hold mode",
@@ -220,8 +218,7 @@ def tell(msg: str) -> None:
220
218
  if BDS == "BDS44":
221
219
  _print("Wind speed", commb.wind44(msg)[0], "knots")
222
220
  _print("Wind direction", commb.wind44(msg)[1], "degrees")
223
- _print("Temperature 1", commb.temp44(msg)[0], "Celsius")
224
- _print("Temperature 2", commb.temp44(msg)[1], "Celsius")
221
+ _print("Temperature", commb.temp44(msg), "Celsius")
225
222
  _print("Pressure", commb.p44(msg), "hPa")
226
223
  _print("Humidity", commb.hum44(msg), "%")
227
224
  _print("Turbulence", commb.turb44(msg))
@@ -14,6 +14,7 @@ The ADS-B module also imports functions from the following modules:
14
14
  from __future__ import annotations
15
15
 
16
16
  from datetime import datetime
17
+ from typing import Literal, overload
17
18
 
18
19
  from .. import common
19
20
  from . import uncertainty
@@ -48,52 +49,52 @@ from .bds.bds62 import (
48
49
  __all__ = [
49
50
  "airborne_position",
50
51
  "airborne_position_with_ref",
52
+ "airborne_velocity",
53
+ "altitude",
51
54
  "altitude05",
52
- "surface_position",
53
- "surface_position_with_ref",
54
- "surface_velocity",
55
+ "altitude_diff",
56
+ "altitude_hold_mode",
57
+ "approach_mode",
58
+ "autopilot",
59
+ "baro_pressure_setting",
55
60
  "callsign",
56
61
  "category",
57
- "airborne_velocity",
58
- "altitude_diff",
62
+ "df",
59
63
  "emergency_squawk",
60
64
  "emergency_state",
61
- "is_emergency",
62
- "df",
65
+ "emergency_status",
66
+ "horizontal_mode",
63
67
  "icao",
64
- "typecode",
65
- "position",
66
- "position_with_ref",
67
- "altitude",
68
- "velocity",
69
- "speed_heading",
70
- "oe_flag",
71
- "version",
72
- "nuc_p",
73
- "nuc_v",
74
- "nic_v1",
75
- "nic_v2",
76
- "nic_s",
77
- "nic_a_c",
78
- "nic_b",
68
+ "is_emergency",
69
+ "lnav_mode",
79
70
  "nac_p",
80
71
  "nac_v",
81
- "sil",
72
+ "nic_a_c",
73
+ "nic_b",
74
+ "nic_s",
75
+ "nic_v1",
76
+ "nic_v2",
77
+ "nuc_p",
78
+ "nuc_v",
79
+ "oe_flag",
80
+ "position",
81
+ "position_with_ref",
82
82
  "selected_altitude",
83
- "target_altitude",
84
- "vertical_mode",
85
- "horizontal_mode",
86
83
  "selected_heading",
84
+ "sil",
85
+ "speed_heading",
86
+ "surface_position",
87
+ "surface_position_with_ref",
88
+ "surface_velocity",
89
+ "target_altitude",
87
90
  "target_angle",
88
- "baro_pressure_setting",
89
- "autopilot",
90
- "vnav_mode",
91
- "altitude_hold_mode",
92
- "approach_mode",
93
- "lnav_mode",
94
91
  "tcas_operational",
95
92
  "tcas_ra",
96
- "emergency_status",
93
+ "typecode",
94
+ "velocity",
95
+ "version",
96
+ "vertical_mode",
97
+ "vnav_mode",
97
98
  ]
98
99
 
99
100
 
@@ -195,7 +196,7 @@ def position_with_ref(msg: str, lat_ref: float, lon_ref: float) -> tuple[float,
195
196
  raise RuntimeError("incorrect or inconsistent message types")
196
197
 
197
198
 
198
- def altitude(msg: str) -> None | float:
199
+ def altitude(msg: str) -> None | int:
199
200
  """Decode aircraft altitude.
200
201
 
201
202
  Args:
@@ -219,9 +220,25 @@ def altitude(msg: str) -> None | float:
219
220
  return altitude05(msg)
220
221
 
221
222
 
223
+ @overload
224
+ def velocity(
225
+ msg: str, source: Literal[False] = False
226
+ ) -> None | tuple[None | float, None | float, None | int, str]: ...
227
+
228
+
229
+ @overload
230
+ def velocity(
231
+ msg: str, source: Literal[True]
232
+ ) -> None | tuple[None | float, None | float, None | int, str, str, str | None]: ...
233
+
234
+
222
235
  def velocity(
223
236
  msg: str, source: bool = False
224
- ) -> None | tuple[None | float, None | float, None | int, str]:
237
+ ) -> (
238
+ None
239
+ | tuple[None | float, None | float, None | int, str]
240
+ | tuple[None | float, None | float, None | int, str, str, str | None]
241
+ ):
225
242
  """Calculate the speed, heading, and vertical rate
226
243
  (handles both airborne or surface message).
227
244
 
@@ -232,7 +249,7 @@ def velocity(
232
249
  If set to True, the function will return six value instead of four.
233
250
 
234
251
  Returns:
235
- int, float, int, string, [string], [string]:
252
+ float, float, int, string, [string], [string]:
236
253
  - Speed (kt)
237
254
  - Angle (degree), either ground track or heading
238
255
  - Vertical rate (ft/min)
@@ -325,7 +342,7 @@ def nuc_p(msg: str) -> tuple[int, None | float, None | int, None | int]:
325
342
  """
326
343
  tc = typecode(msg)
327
344
 
328
- if tc is None or tc < 5 or tc is None or tc > 22:
345
+ if tc is None or tc < 5 or tc == 19 or tc > 22:
329
346
  raise RuntimeError(
330
347
  "%s: Not a surface position message (5<TC<8), \
331
348
  airborne position message (8<TC<19), \
@@ -2,8 +2,8 @@
2
2
  Decode all-call reply messages, with downlink format 11
3
3
  """
4
4
 
5
-
6
5
  from __future__ import annotations
6
+
7
7
  from typing import Callable, TypeVar
8
8
 
9
9
  from .. import common
@@ -13,7 +13,6 @@ F = Callable[[str], T]
13
13
 
14
14
 
15
15
  def _checkdf(func: F[T]) -> F[T]:
16
-
17
16
  """Ensure downlink format is 11."""
18
17
 
19
18
  def wrapper(msg: str) -> T:
@@ -82,10 +81,7 @@ def capability(msg: str) -> tuple[int, None | str]:
82
81
  elif ca == 5:
83
82
  text = "level 2 transponder, ability to set CA to 7, airborne"
84
83
  elif ca == 6:
85
- text = (
86
- "evel 2 transponder, ability to set CA to 7, "
87
- "either airborne or ground"
88
- )
84
+ text = "evel 2 transponder, ability to set CA to 7, either airborne or ground"
89
85
  elif ca == 7:
90
86
  text = (
91
87
  "Downlink Request value is not 0, "
@@ -39,9 +39,7 @@ from . import ( # noqa: F401
39
39
  )
40
40
 
41
41
 
42
- def is50or60(
43
- msg: str, spd_ref: float, trk_ref: float, alt_ref: float
44
- ) -> Optional[str]:
42
+ def is50or60(msg: str, spd_ref: float, trk_ref: float, alt_ref: float) -> Optional[str]:
45
43
  """Use reference ground speed and trk to determine BDS50 and DBS60.
46
44
 
47
45
  Args:
@@ -149,11 +149,6 @@ def altitude(msg: str) -> None | int:
149
149
 
150
150
  if tc < 19:
151
151
  altcode = altbin[0:6] + "0" + altbin[6:]
152
- alt = common.altitude(altcode)
153
- if alt != -999999:
154
- return alt
155
- else:
156
- # return None if altitude is invalid
157
- return None
152
+ return common.altitude(altcode)
158
153
  else:
159
- return common.bin2int(altbin) * 3.28084 # type: ignore
154
+ return int(common.bin2int(altbin) * 3.28084)
@@ -7,6 +7,7 @@
7
7
  from __future__ import annotations
8
8
 
9
9
  from datetime import datetime
10
+ from typing import Literal, overload
10
11
 
11
12
  from ... import common
12
13
 
@@ -138,9 +139,24 @@ def surface_position_with_ref(
138
139
  return lat, lon
139
140
 
140
141
 
142
+ @overload
143
+ def surface_velocity(
144
+ msg: str, source: Literal[False] = False
145
+ ) -> tuple[None | float, None | float, int, str]: ...
146
+
147
+
148
+ @overload
149
+ def surface_velocity(
150
+ msg: str, source: Literal[True]
151
+ ) -> tuple[None | float, None | float, int, str, str, str | None]: ...
152
+
153
+
141
154
  def surface_velocity(
142
155
  msg: str, source: bool = False
143
- ) -> tuple[None | float, None | float, int, str]:
156
+ ) -> (
157
+ tuple[None | float, None | float, int, str]
158
+ | tuple[None | float, None | float, int, str, str, str | None]
159
+ ):
144
160
  """Decode surface velocity from a surface position message
145
161
 
146
162
  Args:
@@ -150,7 +166,7 @@ def surface_velocity(
150
166
  If set to True, the function will return six value instead of four.
151
167
 
152
168
  Returns:
153
- int, float, int, string, [string], [string]:
169
+ float, float, int, string, [string], [string]:
154
170
  - Speed (kt)
155
171
  - Angle (degree), ground track
156
172
  - Vertical rate, always 0
@@ -189,6 +205,6 @@ def surface_velocity(
189
205
  spd = kts_lb[i - 1] + (mov - mov_lb[i - 1]) * step[i - 1]
190
206
 
191
207
  if source:
192
- return spd, trk, 0, "GS", "TRUE_NORTH", None # type: ignore
208
+ return spd, trk, 0, "GS", "TRUE_NORTH", None
193
209
  else:
194
210
  return spd, trk, 0, "GS"