Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
555b2eea40 | ||
|
|
dea7cde317 | ||
|
|
e16d34bc06 | ||
|
|
9bb87b00be | ||
|
|
3dae0438bf | ||
|
|
02c5117de5 | ||
|
|
3f24f78d3a | ||
|
|
cf3828d2a0 | ||
|
|
f70d1f2f1f | ||
|
|
dfeb65fbd7 | ||
|
|
13b283666a | ||
|
|
6144b88188 | ||
|
|
b503beb3fd | ||
|
|
c804cd876c | ||
|
|
d48caed7e6 | ||
|
|
eb675d5ca3 | ||
|
|
b04a1bd49c |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,7 +6,7 @@ __pycache__/
|
|||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|
||||||
#cython
|
#cython
|
||||||
*.c
|
.c
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
|||||||
13
Makefile
13
Makefile
@@ -8,18 +8,11 @@ ext:
|
|||||||
python setup.py build_ext --inplace
|
python setup.py build_ext --inplace
|
||||||
|
|
||||||
test:
|
test:
|
||||||
make clean
|
python -m pytest
|
||||||
@echo ""
|
|
||||||
@echo "[Test with py_common]"
|
|
||||||
python -m pytest tests
|
|
||||||
@echo ""
|
|
||||||
@echo "[Test with c_common]"
|
|
||||||
python setup.py build_ext --inplace
|
|
||||||
python -m pytest tests
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
find pyModeS -type f -name '*.c' -delete
|
find pyModeS/decoder -type f -name '*.c' -delete
|
||||||
find pyModeS -type f -name '*.so' -delete
|
find pyModeS/decoder -type f -name '*.so' -delete
|
||||||
find . | grep -E "(__pycache__|\.pyc|\.pyo$$)" | xargs rm -rf
|
find . | grep -E "(__pycache__|\.pyc|\.pyo$$)" | xargs rm -rf
|
||||||
rm -rf *.egg-info
|
rm -rf *.egg-info
|
||||||
rm -rf .pytest_cache
|
rm -rf .pytest_cache
|
||||||
|
|||||||
130
README.rst
130
README.rst
@@ -1,42 +1,6 @@
|
|||||||
The Python ADS-B/Mode-S Decoder
|
The Python ADS-B/Mode-S Decoder
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
PyModeS is a Python library designed to decode Mode-S (including ADS-B) message. It can be imported to your python project or used as a standalone tool to view and save live traffic data.
|
|
||||||
|
|
||||||
This is a project created by Junzi Sun, who works at `TU Delft <https://www.tudelft.nl/en/>`_, `Aerospace Engineering Faculty <https://www.tudelft.nl/en/ae/>`_, `CNS/ATM research group <http://cs.lr.tudelft.nl/atm/>`_. It is supported by many `contributors <https://github.com/junzis/pyModeS/graphs/contributors>`_ from different institutions.
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
|
|
||||||
pyModeS supports the decoding of following types of messages:
|
|
||||||
|
|
||||||
- DF4 / DF20: Altitude code
|
|
||||||
- DF5 / DF21: Identity code (squawk code)
|
|
||||||
|
|
||||||
- DF17 / DF18: Automatic Dependent Surveillance-Broadcast (ADS-B)
|
|
||||||
|
|
||||||
- TC=1-4 / BDS 0,8: Aircraft identification and category
|
|
||||||
- TC=5-8 / BDS 0,6: Surface position
|
|
||||||
- TC=9-18 / BDS 0,5: Airborne position
|
|
||||||
- TC=19 / BDS 0,9: Airborne velocity
|
|
||||||
- TC=28 / BDS 6,1: Airborne status [to be implemented]
|
|
||||||
- TC=29 / BDS 6,2: Target state and status information [to be implemented]
|
|
||||||
- TC=31 / BDS 6,5: Aircraft operational status [to be implemented]
|
|
||||||
|
|
||||||
- DF20 / DF21: Mode-S Comm-B messages
|
|
||||||
|
|
||||||
- BDS 1,0: Data link capability report
|
|
||||||
- BDS 1,7: Common usage GICB capability report
|
|
||||||
- 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 (experimental)
|
|
||||||
- BDS 4,5: Meteorological hazard report (experimental)
|
|
||||||
- BDS 5,0: Track and turn report
|
|
||||||
- BDS 6,0: Heading and speed report
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
If you find this project useful for your research, please considering cite this tool as::
|
If you find this project useful for your research, please considering cite this tool as::
|
||||||
|
|
||||||
@article{sun2019pymodes,
|
@article{sun2019pymodes,
|
||||||
@@ -50,6 +14,40 @@ If you find this project useful for your research, please considering cite this
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
---------------------
|
||||||
|
PyModeS is a Python library designed to decode Mode-S (including ADS-B) message. It can be imported to your python project or be used as a standalone tool to view and save live traffic data.
|
||||||
|
|
||||||
|
Messages with following Downlink Formats (DF) are supported:
|
||||||
|
|
||||||
|
**DF17 / DF18: Automatic Dependent Surveillance-Broadcast (ADS-B)**
|
||||||
|
|
||||||
|
- TC=1-4 / BDS 0,8: Aircraft identification and category
|
||||||
|
- TC=5-8 / BDS 0,6: Surface position
|
||||||
|
- TC=9-18 / BDS 0,5: Airborne position
|
||||||
|
- TC=19 / BDS 0,9: Airborne velocity
|
||||||
|
- TC=28 / BDS 6,1: Airborne status [to be implemented]
|
||||||
|
- TC=29 / BDS 6,2: Target state and status information [to be implemented]
|
||||||
|
- TC=31 / BDS 6,5: Aircraft operational status [to be implemented]
|
||||||
|
|
||||||
|
|
||||||
|
**DF20 / DF21: Mode-S Comm-B replies**
|
||||||
|
|
||||||
|
- BDS 1,0: Data link capability report
|
||||||
|
- BDS 1,7: Common usage GICB capability report
|
||||||
|
- 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 (experimental)
|
||||||
|
- BDS 4,5: Meteorological hazard report (experimental)
|
||||||
|
- BDS 5,0: Track and turn report
|
||||||
|
- BDS 6,0: Heading and speed report
|
||||||
|
|
||||||
|
|
||||||
|
**DF4 / DF20: Altitude code**
|
||||||
|
|
||||||
|
**DF5 / DF21: Identity code (squawk code)**
|
||||||
|
|
||||||
|
|
||||||
Resources
|
Resources
|
||||||
-----------
|
-----------
|
||||||
@@ -57,50 +55,26 @@ Check out and contribute to this open-source project at:
|
|||||||
https://github.com/junzis/pyModeS
|
https://github.com/junzis/pyModeS
|
||||||
|
|
||||||
Detailed manual on Mode-S decoding is published at:
|
Detailed manual on Mode-S decoding is published at:
|
||||||
https://mode-s.org/decode
|
https://mode-s.org/decode.
|
||||||
|
|
||||||
The API documentation of pyModeS is at:
|
The API documentation of pyModeS is at:
|
||||||
https://mode-s.org/api
|
http://pymodes.readthedocs.io
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Basic installation
|
Install
|
||||||
-------------------
|
-------
|
||||||
|
|
||||||
Installation examples::
|
Installation examples::
|
||||||
|
|
||||||
# stable version
|
# stable version
|
||||||
pip install pyModeS
|
pip install pyModeS
|
||||||
|
|
||||||
# conda (compiled) version
|
|
||||||
conda install -c conda-forge pymodes
|
|
||||||
|
|
||||||
# development version
|
# development version
|
||||||
pip install git+https://github.com/junzis/pyModeS
|
pip install git+https://github.com/junzis/pyModeS
|
||||||
|
|
||||||
|
|
||||||
Dependencies ``numpy``, and ``pyzmq`` are installed automatically during previous installations processes.
|
Dependencies ``numpy``, ``pyzmq`` and ``pyrtlsdr`` are installed automatically during previous installations processes.
|
||||||
|
|
||||||
If you need to connect pyModeS to a RTL-SDR receiver, ``pyrtlsdr`` need to be installed manually::
|
|
||||||
|
|
||||||
pip install pyrtlsdr
|
|
||||||
|
|
||||||
|
|
||||||
Advanced installation (using c modules)
|
|
||||||
------------------------------------------
|
|
||||||
|
|
||||||
If you want to make use of the (faster) c module, install ``pyModeS`` as follows::
|
|
||||||
|
|
||||||
# conda (compiled) version
|
|
||||||
conda install -c conda-forge pymodes
|
|
||||||
|
|
||||||
# stable version (to be compiled on your side)
|
|
||||||
pip install pyModeS[fast]
|
|
||||||
|
|
||||||
# development version
|
|
||||||
git clone https://github.com/junzis/pyModeS
|
|
||||||
cd pyModeS
|
|
||||||
pip install .[fast]
|
|
||||||
|
|
||||||
|
|
||||||
View live traffic (modeslive)
|
View live traffic (modeslive)
|
||||||
@@ -126,7 +100,7 @@ General usage::
|
|||||||
Live with RTL-SDR
|
Live with RTL-SDR
|
||||||
*******************
|
*******************
|
||||||
|
|
||||||
If you have an RTL-SDR receiver connected to your computer, you can use the ``rtlsdr`` source switch (require ``pyrtlsdr`` package), with command::
|
If you have an RTL-SDR receiver plugged to the computer, you can connect it with ``rtlsdr`` source switch, shown as follows::
|
||||||
|
|
||||||
$ modeslive --source rtlsdr
|
$ modeslive --source rtlsdr
|
||||||
|
|
||||||
@@ -167,7 +141,7 @@ Common functions
|
|||||||
pms.hex2bin(str) # Convert hexadecimal string to binary string
|
pms.hex2bin(str) # Convert hexadecimal string to binary string
|
||||||
pms.bin2int(str) # Convert binary string to integer
|
pms.bin2int(str) # Convert binary string to integer
|
||||||
pms.hex2int(str) # Convert hexadecimal string to integer
|
pms.hex2int(str) # Convert hexadecimal string to integer
|
||||||
pms.gray2int(str) # Convert grey code to integer
|
pms.gray2int(str) # Convert grey code to interger
|
||||||
|
|
||||||
|
|
||||||
Core functions for ADS-B decoding
|
Core functions for ADS-B decoding
|
||||||
@@ -181,7 +155,7 @@ Core functions for ADS-B decoding
|
|||||||
# Typecode 1-4
|
# Typecode 1-4
|
||||||
pms.adsb.callsign(msg)
|
pms.adsb.callsign(msg)
|
||||||
|
|
||||||
# Typecode 5-8 (surface), 9-18 (airborne, barometric height), and 20-22 (airborne, GNSS height)
|
# Typecode 5-8 (surface), 9-18 (airborne, barometric height), and 9-18 (airborne, GNSS height)
|
||||||
pms.adsb.position(msg_even, msg_odd, t_even, t_odd, lat_ref=None, lon_ref=None)
|
pms.adsb.position(msg_even, msg_odd, t_even, t_odd, lat_ref=None, lon_ref=None)
|
||||||
pms.adsb.airborne_position(msg_even, msg_odd, t_even, t_odd)
|
pms.adsb.airborne_position(msg_even, msg_odd, t_even, t_odd)
|
||||||
pms.adsb.surface_position(msg_even, msg_odd, t_even, t_odd, lat_ref, lon_ref)
|
pms.adsb.surface_position(msg_even, msg_odd, t_even, t_odd, lat_ref, lon_ref)
|
||||||
@@ -275,20 +249,8 @@ Mode-S Enhanced Surveillance (EHS)
|
|||||||
pms.commb.vr60ins(msg) # Inertial vertical speed (ft/min)
|
pms.commb.vr60ins(msg) # Inertial vertical speed (ft/min)
|
||||||
|
|
||||||
|
|
||||||
Meteorological reports [Experimental]
|
Meteorological routine air report (MRAR) [Experimental]
|
||||||
**************************************
|
********************************************************
|
||||||
|
|
||||||
To identify BDS 4,4 and 4,5 codes, you must set ``mrar`` argument to ``True`` in the ``infer()`` function:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
pms.bds.infer(msg. mrar=True)
|
|
||||||
|
|
||||||
Once the correct MRAR and MHR messages are identified, decode them as follows:
|
|
||||||
|
|
||||||
|
|
||||||
Meteorological routine air report (MRAR)
|
|
||||||
+++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
@@ -299,8 +261,8 @@ Meteorological routine air report (MRAR)
|
|||||||
pms.commb.hum44(msg) # Humidity (%)
|
pms.commb.hum44(msg) # Humidity (%)
|
||||||
|
|
||||||
|
|
||||||
Meteorological hazard air report (MHR)
|
Meteorological hazard air report (MHR) [Experimental]
|
||||||
+++++++++++++++++++++++++++++++++++++++++
|
*******************************************************
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ help:
|
|||||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
%: Makefile
|
%: Makefile
|
||||||
rm -f source/pyModeS*.rst source/modules.rst
|
rm -f source/pyModeS*.rst source/modules.rst
|
||||||
sphinx-apidoc -f -e -M -o source/ ../pyModeS ../pyModeS/decoder/ehs.py ../pyModeS/decoder/els.py ../pyModeS/streamer ../pyModeS/extra
|
sphinx-apidoc -f -e -M -o source/ ../pyModeS
|
||||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
{% extends "!layout.html" %}
|
|
||||||
|
|
||||||
{% block footer %}
|
|
||||||
{{ super() }}
|
|
||||||
|
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
|
||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-74700456-1"></script>
|
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
function gtag(){dataLayer.push(arguments);}
|
|
||||||
gtag('js', new Date());
|
|
||||||
|
|
||||||
gtag('config', 'UA-74700456-1');
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -14,20 +14,19 @@
|
|||||||
#
|
#
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
sys.path.insert(0, os.path.abspath('../..'))
|
||||||
sys.path.insert(0, os.path.abspath("../.."))
|
|
||||||
|
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = "pyModeS"
|
project = 'pyModeS'
|
||||||
copyright = "2019, Junzi Sun"
|
copyright = '2019, Junzi Sun'
|
||||||
author = "Junzi Sun"
|
author = 'Junzi Sun'
|
||||||
|
|
||||||
# The short X.Y version
|
# The short X.Y version
|
||||||
version = ""
|
version = ''
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = ""
|
release = ''
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
@@ -40,24 +39,26 @@ release = ""
|
|||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
# ones.
|
# ones.
|
||||||
extensions = [
|
extensions = [
|
||||||
"sphinx.ext.autodoc",
|
'sphinx.ext.autodoc',
|
||||||
"sphinx.ext.mathjax",
|
'sphinx.ext.todo',
|
||||||
"sphinx.ext.viewcode",
|
'sphinx.ext.coverage',
|
||||||
"sphinx.ext.githubpages",
|
'sphinx.ext.mathjax',
|
||||||
"sphinx.ext.napoleon",
|
'sphinx.ext.viewcode',
|
||||||
|
'sphinx.ext.githubpages',
|
||||||
|
'sphinx.ext.napoleon',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ["_templates"]
|
# templates_path = ['']
|
||||||
|
|
||||||
# The suffix(es) of source filenames.
|
# The suffix(es) of source filenames.
|
||||||
# You can specify multiple suffix as a list of string:
|
# You can specify multiple suffix as a list of string:
|
||||||
#
|
#
|
||||||
# source_suffix = ['.rst', '.md']
|
# source_suffix = ['.rst', '.md']
|
||||||
source_suffix = ".rst"
|
source_suffix = '.rst'
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = "index"
|
master_doc = 'index'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
@@ -81,10 +82,6 @@ pygments_style = None
|
|||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
#
|
#
|
||||||
# html_theme = 'alabaster'
|
# html_theme = 'alabaster'
|
||||||
html_theme = "neo_rtd_theme"
|
|
||||||
import sphinx_theme
|
|
||||||
|
|
||||||
html_theme_path = [sphinx_theme.get_html_theme_path()]
|
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
# further. For a list of options available for each theme, see the
|
# further. For a list of options available for each theme, see the
|
||||||
@@ -111,7 +108,7 @@ html_theme_path = [sphinx_theme.get_html_theme_path()]
|
|||||||
# -- Options for HTMLHelp output ---------------------------------------------
|
# -- Options for HTMLHelp output ---------------------------------------------
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = "pyModeSdoc"
|
htmlhelp_basename = 'pyModeSdoc'
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output ------------------------------------------------
|
# -- Options for LaTeX output ------------------------------------------------
|
||||||
@@ -120,12 +117,15 @@ latex_elements = {
|
|||||||
# The paper size ('letterpaper' or 'a4paper').
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
#
|
#
|
||||||
# 'papersize': 'letterpaper',
|
# 'papersize': 'letterpaper',
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
#
|
#
|
||||||
# 'pointsize': '10pt',
|
# 'pointsize': '10pt',
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
# Additional stuff for the LaTeX preamble.
|
||||||
#
|
#
|
||||||
# 'preamble': '',
|
# 'preamble': '',
|
||||||
|
|
||||||
# Latex figure (float) alignment
|
# Latex figure (float) alignment
|
||||||
#
|
#
|
||||||
# 'figure_align': 'htbp',
|
# 'figure_align': 'htbp',
|
||||||
@@ -135,7 +135,8 @@ latex_elements = {
|
|||||||
# (source start file, target name, title,
|
# (source start file, target name, title,
|
||||||
# author, documentclass [howto, manual, or own class]).
|
# author, documentclass [howto, manual, or own class]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
(master_doc, "pyModeS.tex", "pyModeS Documentation", "Junzi Sun", "manual")
|
(master_doc, 'pyModeS.tex', 'pyModeS Documentation',
|
||||||
|
'Junzi Sun', 'manual'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -143,7 +144,10 @@ latex_documents = [
|
|||||||
|
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [(master_doc, "pymodes", "pyModeS Documentation", [author], 1)]
|
man_pages = [
|
||||||
|
(master_doc, 'pymodes', 'pyModeS Documentation',
|
||||||
|
[author], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output ----------------------------------------------
|
# -- Options for Texinfo output ----------------------------------------------
|
||||||
@@ -152,15 +156,9 @@ man_pages = [(master_doc, "pymodes", "pyModeS Documentation", [author], 1)]
|
|||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
(
|
(master_doc, 'pyModeS', 'pyModeS Documentation',
|
||||||
master_doc,
|
author, 'pyModeS', 'One line description of project.',
|
||||||
"pyModeS",
|
'Miscellaneous'),
|
||||||
"pyModeS Documentation",
|
|
||||||
author,
|
|
||||||
"pyModeS",
|
|
||||||
"One line description of project.",
|
|
||||||
"Miscellaneous",
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -179,7 +177,7 @@ epub_title = project
|
|||||||
# epub_uid = ''
|
# epub_uid = ''
|
||||||
|
|
||||||
# A list of files that should not be packed into the epub file.
|
# A list of files that should not be packed into the epub file.
|
||||||
epub_exclude_files = ["search.html"]
|
epub_exclude_files = ['search.html']
|
||||||
|
|
||||||
|
|
||||||
# -- Extension configuration -------------------------------------------------
|
# -- Extension configuration -------------------------------------------------
|
||||||
|
|||||||
@@ -9,49 +9,11 @@ Welcome to pyModeS documentation!
|
|||||||
The source code can be found at: https://github.com/junzis/pyModeS
|
The source code can be found at: https://github.com/junzis/pyModeS
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:caption: Core modules
|
:maxdepth: 3
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
pyModeS.decoder.adsb
|
|
||||||
pyModeS.decoder.commb
|
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:caption: ADS-B messages
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
pyModeS.decoder.bds.bds05
|
|
||||||
pyModeS.decoder.bds.bds06
|
|
||||||
pyModeS.decoder.bds.bds08
|
|
||||||
pyModeS.decoder.bds.bds09
|
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:caption: ELS - elementary surveillance
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
pyModeS.decoder.bds.bds10
|
|
||||||
pyModeS.decoder.bds.bds17
|
|
||||||
pyModeS.decoder.bds.bds20
|
|
||||||
pyModeS.decoder.bds.bds30
|
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:caption: EHS - enhanced surveillance
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
pyModeS.decoder.bds.bds40
|
|
||||||
pyModeS.decoder.bds.bds50
|
|
||||||
pyModeS.decoder.bds.bds60
|
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:caption: MRAR / MHR
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
pyModeS.decoder.bds.bds44
|
|
||||||
pyModeS.decoder.bds.bds45
|
|
||||||
|
|
||||||
|
pyModeS.decoder
|
||||||
|
pyModeS.streamer
|
||||||
|
pyModeS.extra
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|||||||
35
doc/source/make.bat
Normal file
35
doc/source/make.bat
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set SOURCEDIR=.
|
||||||
|
set BUILDDIR=_build
|
||||||
|
|
||||||
|
if "%1" == "" goto help
|
||||||
|
|
||||||
|
%SPHINXBUILD% >NUL 2>NUL
|
||||||
|
if errorlevel 9009 (
|
||||||
|
echo.
|
||||||
|
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||||
|
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||||
|
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||||
|
echo.may add the Sphinx directory to PATH.
|
||||||
|
echo.
|
||||||
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
|
echo.http://sphinx-doc.org/
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:help
|
||||||
|
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
popd
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
pyModeS.c\_common module
|
|
||||||
========================
|
|
||||||
|
|
||||||
.. automodule:: pyModeS.c_common
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
pyModeS.common module
|
|
||||||
=====================
|
|
||||||
|
|
||||||
.. automodule:: pyModeS.common
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.acas module
|
|||||||
===========================
|
===========================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.acas
|
.. automodule:: pyModeS.decoder.acas
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.adsb module
|
|||||||
===========================
|
===========================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.adsb
|
.. automodule:: pyModeS.decoder.adsb
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.allcall module
|
|||||||
==============================
|
==============================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.allcall
|
.. automodule:: pyModeS.decoder.allcall
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds05 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds05
|
.. automodule:: pyModeS.decoder.bds.bds05
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds06 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds06
|
.. automodule:: pyModeS.decoder.bds.bds06
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds08 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds08
|
.. automodule:: pyModeS.decoder.bds.bds08
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds09 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds09
|
.. automodule:: pyModeS.decoder.bds.bds09
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds10 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds10
|
.. automodule:: pyModeS.decoder.bds.bds10
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds17 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds17
|
.. automodule:: pyModeS.decoder.bds.bds17
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds20 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds20
|
.. automodule:: pyModeS.decoder.bds.bds20
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds30 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds30
|
.. automodule:: pyModeS.decoder.bds.bds30
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds40 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds40
|
.. automodule:: pyModeS.decoder.bds.bds40
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds44 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds44
|
.. automodule:: pyModeS.decoder.bds.bds44
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds45 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds45
|
.. automodule:: pyModeS.decoder.bds.bds45
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds50 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds50
|
.. automodule:: pyModeS.decoder.bds.bds50
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds53 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds53
|
.. automodule:: pyModeS.decoder.bds.bds53
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds60 module
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds.bds60
|
.. automodule:: pyModeS.decoder.bds.bds60
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ pyModeS.decoder.bds package
|
|||||||
===========================
|
===========================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.bds
|
.. automodule:: pyModeS.decoder.bds
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
Submodules
|
Submodules
|
||||||
----------
|
----------
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 4
|
|
||||||
|
|
||||||
pyModeS.decoder.bds.bds05
|
pyModeS.decoder.bds.bds05
|
||||||
pyModeS.decoder.bds.bds06
|
pyModeS.decoder.bds.bds06
|
||||||
@@ -26,3 +25,4 @@ Submodules
|
|||||||
pyModeS.decoder.bds.bds50
|
pyModeS.decoder.bds.bds50
|
||||||
pyModeS.decoder.bds.bds53
|
pyModeS.decoder.bds.bds53
|
||||||
pyModeS.decoder.bds.bds60
|
pyModeS.decoder.bds.bds60
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.commb module
|
|||||||
============================
|
============================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.commb
|
.. automodule:: pyModeS.decoder.commb
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
7
doc/source/pyModeS.decoder.common.rst
Normal file
7
doc/source/pyModeS.decoder.common.rst
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pyModeS.decoder.common module
|
||||||
|
=============================
|
||||||
|
|
||||||
|
.. automodule:: pyModeS.decoder.common
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
7
doc/source/pyModeS.decoder.ehs.rst
Normal file
7
doc/source/pyModeS.decoder.ehs.rst
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pyModeS.decoder.ehs module
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. automodule:: pyModeS.decoder.ehs
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
7
doc/source/pyModeS.decoder.els.rst
Normal file
7
doc/source/pyModeS.decoder.els.rst
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pyModeS.decoder.els module
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. automodule:: pyModeS.decoder.els
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@@ -2,28 +2,29 @@ pyModeS.decoder package
|
|||||||
=======================
|
=======================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder
|
.. automodule:: pyModeS.decoder
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
Subpackages
|
Subpackages
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 4
|
|
||||||
|
|
||||||
pyModeS.decoder.bds
|
pyModeS.decoder.bds
|
||||||
|
|
||||||
Submodules
|
Submodules
|
||||||
----------
|
----------
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 4
|
|
||||||
|
|
||||||
pyModeS.decoder.acas
|
pyModeS.decoder.acas
|
||||||
pyModeS.decoder.adsb
|
pyModeS.decoder.adsb
|
||||||
pyModeS.decoder.allcall
|
pyModeS.decoder.allcall
|
||||||
pyModeS.decoder.commb
|
pyModeS.decoder.commb
|
||||||
|
pyModeS.decoder.common
|
||||||
|
pyModeS.decoder.ehs
|
||||||
|
pyModeS.decoder.els
|
||||||
pyModeS.decoder.surv
|
pyModeS.decoder.surv
|
||||||
pyModeS.decoder.uncertainty
|
pyModeS.decoder.uncertainty
|
||||||
pyModeS.decoder.uplink
|
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.surv module
|
|||||||
===========================
|
===========================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.surv
|
.. automodule:: pyModeS.decoder.surv
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ pyModeS.decoder.uncertainty module
|
|||||||
==================================
|
==================================
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.uncertainty
|
.. automodule:: pyModeS.decoder.uncertainty
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
pyModeS.decoder.uplink module
|
|
||||||
=============================
|
|
||||||
|
|
||||||
.. automodule:: pyModeS.decoder.uplink
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
7
doc/source/pyModeS.extra.aero.rst
Normal file
7
doc/source/pyModeS.extra.aero.rst
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pyModeS.extra.aero module
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. automodule:: pyModeS.extra.aero
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
16
doc/source/pyModeS.extra.rst
Normal file
16
doc/source/pyModeS.extra.rst
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
pyModeS.extra package
|
||||||
|
=====================
|
||||||
|
|
||||||
|
.. automodule:: pyModeS.extra
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
|
||||||
|
pyModeS.extra.aero
|
||||||
|
pyModeS.extra.tcpclient
|
||||||
|
|
||||||
7
doc/source/pyModeS.extra.tcpclient.rst
Normal file
7
doc/source/pyModeS.extra.tcpclient.rst
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pyModeS.extra.tcpclient module
|
||||||
|
==============================
|
||||||
|
|
||||||
|
.. automodule:: pyModeS.extra.tcpclient
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@@ -2,23 +2,16 @@ pyModeS package
|
|||||||
===============
|
===============
|
||||||
|
|
||||||
.. automodule:: pyModeS
|
.. automodule:: pyModeS
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
Subpackages
|
Subpackages
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 4
|
|
||||||
|
|
||||||
pyModeS.decoder
|
pyModeS.decoder
|
||||||
|
pyModeS.extra
|
||||||
|
pyModeS.streamer
|
||||||
|
|
||||||
Submodules
|
|
||||||
----------
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 4
|
|
||||||
|
|
||||||
pyModeS.c_common
|
|
||||||
pyModeS.common
|
|
||||||
|
|||||||
16
doc/source/pyModeS.streamer.rst
Normal file
16
doc/source/pyModeS.streamer.rst
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
pyModeS.streamer package
|
||||||
|
========================
|
||||||
|
|
||||||
|
.. automodule:: pyModeS.streamer
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
|
||||||
|
pyModeS.streamer.screen
|
||||||
|
pyModeS.streamer.stream
|
||||||
|
|
||||||
7
doc/source/pyModeS.streamer.screen.rst
Normal file
7
doc/source/pyModeS.streamer.screen.rst
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pyModeS.streamer.screen module
|
||||||
|
==============================
|
||||||
|
|
||||||
|
.. automodule:: pyModeS.streamer.screen
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
7
doc/source/pyModeS.streamer.stream.rst
Normal file
7
doc/source/pyModeS.streamer.stream.rst
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pyModeS.streamer.stream module
|
||||||
|
==============================
|
||||||
|
|
||||||
|
.. automodule:: pyModeS.streamer.stream
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@@ -1,23 +1,22 @@
|
|||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from . import c_common as common
|
from .decoder import c_common as common
|
||||||
from .c_common import *
|
from .decoder.c_common import *
|
||||||
except:
|
except:
|
||||||
from . import py_common as common
|
from .decoder import common
|
||||||
from .py_common import *
|
from .decoder.common import *
|
||||||
|
|
||||||
from .decoder import tell
|
from .decoder import tell
|
||||||
from .decoder import adsb
|
from .decoder import adsb
|
||||||
from .decoder import commb
|
from .decoder import commb
|
||||||
from .decoder import allcall
|
|
||||||
from .decoder import surv
|
|
||||||
from .decoder import bds
|
from .decoder import bds
|
||||||
from .extra import aero
|
from .extra import aero
|
||||||
from .extra import tcpclient
|
from .extra import tcpclient
|
||||||
|
|
||||||
|
|
||||||
warnings.simplefilter("once", DeprecationWarning)
|
warnings.simplefilter("once", DeprecationWarning)
|
||||||
|
|
||||||
dirpath = os.path.dirname(os.path.realpath(__file__))
|
dirpath = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
def tell(msg: str) -> None:
|
from __future__ import absolute_import, print_function, division
|
||||||
from pyModeS import common, adsb, commb, bds
|
|
||||||
|
|
||||||
|
from pyModeS.decoder import adsb, commb, common, bds
|
||||||
|
|
||||||
|
|
||||||
|
def tell(msg):
|
||||||
def _print(label, value, unit=None):
|
def _print(label, value, unit=None):
|
||||||
print("%20s: " % label, end="")
|
print("%20s: " % label, end="")
|
||||||
print("%s " % value, end="")
|
print("%s " % value, end="")
|
||||||
@@ -17,7 +20,7 @@ def tell(msg: str) -> None:
|
|||||||
_print("Downlink Format", df)
|
_print("Downlink Format", df)
|
||||||
|
|
||||||
if df == 17:
|
if df == 17:
|
||||||
_print("Protocol", "Mode-S Extended Squitter (ADS-B)")
|
_print("Protocal", "Mode-S Extended Squitter (ADS-B)")
|
||||||
|
|
||||||
tc = common.typecode(msg)
|
tc = common.typecode(msg)
|
||||||
if 1 <= tc <= 4: # callsign
|
if 1 <= tc <= 4: # callsign
|
||||||
@@ -26,7 +29,7 @@ def tell(msg: str) -> None:
|
|||||||
_print("Callsign:", callsign)
|
_print("Callsign:", callsign)
|
||||||
|
|
||||||
if 5 <= tc <= 8: # surface position
|
if 5 <= tc <= 8: # surface position
|
||||||
_print("Type", "Surface position")
|
_print("Type", "Surface postition")
|
||||||
oe = adsb.oe_flag(msg)
|
oe = adsb.oe_flag(msg)
|
||||||
msgbin = common.hex2bin(msg)
|
msgbin = common.hex2bin(msg)
|
||||||
cprlat = common.bin2int(msgbin[54:71]) / 131072.0
|
cprlat = common.bin2int(msgbin[54:71]) / 131072.0
|
||||||
@@ -72,11 +75,11 @@ def tell(msg: str) -> None:
|
|||||||
_print("Altitude", alt, "feet")
|
_print("Altitude", alt, "feet")
|
||||||
|
|
||||||
if df == 20:
|
if df == 20:
|
||||||
_print("Protocol", "Mode-S Comm-B altitude reply")
|
_print("Protocal", "Mode-S Comm-B altitude reply")
|
||||||
_print("Altitude", common.altcode(msg), "feet")
|
_print("Altitude", common.altcode(msg), "feet")
|
||||||
|
|
||||||
if df == 21:
|
if df == 21:
|
||||||
_print("Protocol", "Mode-S Comm-B identity reply")
|
_print("Protocal", "Mode-S Comm-B identity reply")
|
||||||
_print("Squawk code", common.idcode(msg))
|
_print("Squawk code", common.idcode(msg))
|
||||||
|
|
||||||
if df == 20 or df == 21:
|
if df == 20 or df == 21:
|
||||||
|
|||||||
@@ -3,3 +3,6 @@ Decoding Air-Air Surveillance (ACAS) DF=0/16
|
|||||||
|
|
||||||
[To be implemented]
|
[To be implemented]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder import common
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
"""ADS-B module.
|
"""ADS-B Wrapper.
|
||||||
|
|
||||||
The ADS-B module also imports functions from the following modules:
|
The ADS-B wrapper also imports functions from the following modules:
|
||||||
|
|
||||||
- pyModeS.decoder.bds.bds05: ``airborne_position()``, ``airborne_position_with_ref()``, ``altitude()``
|
- pyModeS.decoder.bds.bds05
|
||||||
- pyModeS.decoder.bds.bds06: ``surface_position()``, ``surface_position_with_ref()``, ``surface_velocity()``
|
Functions: ``airborne_position``, ``airborne_position_with_ref``, ``altitude``
|
||||||
- pyModeS.decoder.bds.bds08: ``category()``, ``callsign()``
|
- pyModeS.decoder.bds.bds06
|
||||||
- pyModeS.decoder.bds.bds09: ``airborne_velocity()``, ``altitude_diff()``
|
Functions: ``surface_position``, ``surface_position_with_ref``, ``surface_velocity``
|
||||||
|
- pyModeS.decoder.bds.bds08
|
||||||
|
Functions: ``category``, ``callsign``
|
||||||
|
- pyModeS.decoder.bds.bds09
|
||||||
|
Functions: ``airborne_velocity``, ``altitude_diff``
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
|
|
||||||
import pyModeS as pms
|
import pyModeS as pms
|
||||||
|
from pyModeS.decoder import common
|
||||||
from pyModeS import common
|
|
||||||
|
|
||||||
from pyModeS.decoder import uncertainty
|
from pyModeS.decoder import uncertainty
|
||||||
|
|
||||||
# from pyModeS.decoder.bds import bds05, bds06, bds09
|
# from pyModeS.decoder.bds import bds05, bds06, bds09
|
||||||
@@ -28,7 +32,6 @@ from pyModeS.decoder.bds.bds06 import (
|
|||||||
)
|
)
|
||||||
from pyModeS.decoder.bds.bds08 import category, callsign
|
from pyModeS.decoder.bds.bds08 import category, callsign
|
||||||
from pyModeS.decoder.bds.bds09 import airborne_velocity, altitude_diff
|
from pyModeS.decoder.bds.bds09 import airborne_velocity, altitude_diff
|
||||||
from pyModeS.decoder.bds.bds61 import is_emergency, emergency_state, emergency_squawk
|
|
||||||
|
|
||||||
|
|
||||||
def df(msg):
|
def df(msg):
|
||||||
@@ -44,32 +47,27 @@ def typecode(msg):
|
|||||||
|
|
||||||
|
|
||||||
def position(msg0, msg1, t0, t1, lat_ref=None, lon_ref=None):
|
def position(msg0, msg1, t0, t1, lat_ref=None, lon_ref=None):
|
||||||
"""Decode surface or airborne position from a pair of even and odd
|
"""Decode position from a pair of even and odd position message
|
||||||
position messages.
|
(works with both airborne and surface position messages)
|
||||||
|
|
||||||
Note, that to decode surface position using the position message pair,
|
|
||||||
the reference position has to be provided.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg0 (string): even message (28 hexdigits)
|
msg0 (string): even message (28 bytes hexadecimal string)
|
||||||
msg1 (string): odd message (28 hexdigits)
|
msg1 (string): odd message (28 bytes hexadecimal string)
|
||||||
t0 (int): timestamps for the even message
|
t0 (int): timestamps for the even message
|
||||||
t1 (int): timestamps for the odd message
|
t1 (int): timestamps for the odd message
|
||||||
lat_ref (float): latitude of reference position
|
|
||||||
lon_ref (float): longitude of reference position
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(float, float): (latitude, longitude) of the aircraft
|
(float, float): (latitude, longitude) of the aircraft
|
||||||
|
|
||||||
"""
|
"""
|
||||||
tc0 = typecode(msg0)
|
tc0 = typecode(msg0)
|
||||||
tc1 = typecode(msg1)
|
tc1 = typecode(msg1)
|
||||||
|
|
||||||
if 5 <= tc0 <= 8 and 5 <= tc1 <= 8:
|
if 5 <= tc0 <= 8 and 5 <= tc1 <= 8:
|
||||||
if lat_ref is None or lon_ref is None:
|
if (not lat_ref) or (not lon_ref):
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Surface position encountered, a reference position"
|
"Surface position encountered, a reference \
|
||||||
" lat/lon required. Location of receiver can be used."
|
position lat/lon required. Location of \
|
||||||
|
receiver can be used."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return surface_position(msg0, msg1, t0, t1, lat_ref, lon_ref)
|
return surface_position(msg0, msg1, t0, t1, lat_ref, lon_ref)
|
||||||
@@ -83,20 +81,19 @@ def position(msg0, msg1, t0, t1, lat_ref=None, lon_ref=None):
|
|||||||
return airborne_position(msg0, msg1, t0, t1)
|
return airborne_position(msg0, msg1, t0, t1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Incorrect or inconsistent message types")
|
raise RuntimeError("incorrect or inconsistant message types")
|
||||||
|
|
||||||
|
|
||||||
def position_with_ref(msg, lat_ref, lon_ref):
|
def position_with_ref(msg, lat_ref, lon_ref):
|
||||||
"""Decode position with only one message.
|
"""Decode position with only one message,
|
||||||
|
knowing reference nearby location, such as previously
|
||||||
A reference position is required, which can be previously
|
calculated location, ground station, or airport location, etc.
|
||||||
calculated location, ground station, or airport location.
|
Works with both airborne and surface position messages.
|
||||||
The function works with both airborne and surface position messages.
|
|
||||||
The reference position shall be with in 180NM (airborne) or 45NM (surface)
|
The reference position shall be with in 180NM (airborne) or 45NM (surface)
|
||||||
of the true position.
|
of the true position.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): even message (28 hexdigits)
|
msg (string): even message (28 bytes hexadecimal string)
|
||||||
lat_ref: previous known latitude
|
lat_ref: previous known latitude
|
||||||
lon_ref: previous known longitude
|
lon_ref: previous known longitude
|
||||||
|
|
||||||
@@ -113,19 +110,19 @@ def position_with_ref(msg, lat_ref, lon_ref):
|
|||||||
return airborne_position_with_ref(msg, lat_ref, lon_ref)
|
return airborne_position_with_ref(msg, lat_ref, lon_ref)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("incorrect or inconsistent message types")
|
raise RuntimeError("incorrect or inconsistant message types")
|
||||||
|
|
||||||
|
|
||||||
def altitude(msg):
|
def altitude(msg):
|
||||||
"""Decode aircraft altitude.
|
"""Decode aircraft altitude
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: altitude in feet
|
int: altitude in feet
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tc = typecode(msg)
|
tc = typecode(msg)
|
||||||
|
|
||||||
if tc < 5 or tc == 19 or tc > 22:
|
if tc < 5 or tc == 19 or tc > 22:
|
||||||
@@ -140,35 +137,40 @@ def altitude(msg):
|
|||||||
return altitude05(msg)
|
return altitude05(msg)
|
||||||
|
|
||||||
|
|
||||||
def velocity(msg, source=False):
|
def velocity(msg, rtn_sources=False):
|
||||||
"""Calculate the speed, heading, and vertical rate (handles both airborne or surface message).
|
"""Calculate the speed, heading, and vertical rate
|
||||||
|
(handles both airborne or surface message)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
source (boolean): Include direction and vertical rate sources in return. Default to False.
|
rtn_source (boolean): If the function will return
|
||||||
If set to True, the function will return six value instead of four.
|
the sources for direction of travel and vertical
|
||||||
|
rate. This will change the return value from a four
|
||||||
|
element array to a six element array.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int, float, int, string, [string], [string]: Four or six parameters, including:
|
(int, float, int, string, string, string): speed (kt),
|
||||||
- Speed (kt)
|
ground track or heading (degree),
|
||||||
- Angle (degree), either ground track or heading
|
rate of climb/descent (ft/min), speed type
|
||||||
- Vertical rate (ft/min)
|
('GS' for ground speed, 'AS' for airspeed),
|
||||||
- Speed type ('GS' for ground speed, 'AS' for airspeed)
|
direction source ('true_north' for ground track / true north
|
||||||
- [Optional] Direction source ('TRUE_NORTH' or 'MAGENTIC_NORTH')
|
as refrence, 'mag_north' for magnetic north as reference),
|
||||||
- [Optional] Vertical rate source ('BARO' or 'GNSS')
|
rate of climb/descent source ('Baro' for barometer, 'GNSS'
|
||||||
|
for GNSS constellation).
|
||||||
For surface messages, vertical rate and its respective sources are set to None.
|
|
||||||
|
|
||||||
|
In the case of surface messages, None will be put in place
|
||||||
|
for vertical rate and its respective sources.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if 5 <= typecode(msg) <= 8:
|
if 5 <= typecode(msg) <= 8:
|
||||||
return surface_velocity(msg, source)
|
return surface_velocity(msg, rtn_sources)
|
||||||
|
|
||||||
elif typecode(msg) == 19:
|
elif typecode(msg) == 19:
|
||||||
return airborne_velocity(msg, source)
|
return airborne_velocity(msg, rtn_sources)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"incorrect or inconsistent message types, expecting 4<TC<9 or TC=19"
|
"incorrect or inconsistant message types, expecting 4<TC<9 or TC=19"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -177,7 +179,7 @@ def speed_heading(msg):
|
|||||||
(handles both airborne or surface message)
|
(handles both airborne or surface message)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(int, float): speed (kt), ground track or heading (degree)
|
(int, float): speed (kt), ground track or heading (degree)
|
||||||
@@ -189,7 +191,7 @@ def speed_heading(msg):
|
|||||||
def oe_flag(msg):
|
def oe_flag(msg):
|
||||||
"""Check the odd/even flag. Bit 54, 0 for even, 1 for odd.
|
"""Check the odd/even flag. Bit 54, 0 for even, 1 for odd.
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
Returns:
|
Returns:
|
||||||
int: 0 or 1, for even or odd frame
|
int: 0 or 1, for even or odd frame
|
||||||
"""
|
"""
|
||||||
@@ -201,7 +203,7 @@ def version(msg):
|
|||||||
"""ADS-B Version
|
"""ADS-B Version
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string, TC = 31
|
msg (string): 28 bytes hexadecimal message string, TC = 31
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: version number
|
int: version number
|
||||||
@@ -223,7 +225,7 @@ def nuc_p(msg):
|
|||||||
"""Calculate NUCp, Navigation Uncertainty Category - Position (ADS-B version 1)
|
"""Calculate NUCp, Navigation Uncertainty Category - Position (ADS-B version 1)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string,
|
msg (string): 28 bytes hexadecimal message string,
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: Horizontal Protection Limit
|
int: Horizontal Protection Limit
|
||||||
@@ -259,7 +261,7 @@ def nuc_v(msg):
|
|||||||
"""Calculate NUCv, Navigation Uncertainty Category - Velocity (ADS-B version 1)
|
"""Calculate NUCv, Navigation Uncertainty Category - Velocity (ADS-B version 1)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string,
|
msg (string): 28 bytes hexadecimal message string,
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int or string: 95% Horizontal Velocity Error
|
int or string: 95% Horizontal Velocity Error
|
||||||
@@ -288,7 +290,7 @@ def nic_v1(msg, NICs):
|
|||||||
"""Calculate NIC, navigation integrity category, for ADS-B version 1
|
"""Calculate NIC, navigation integrity category, for ADS-B version 1
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
NICs (int or string): NIC supplement
|
NICs (int or string): NIC supplement
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -322,7 +324,7 @@ def nic_v2(msg, NICa, NICbc):
|
|||||||
"""Calculate NIC, navigation integrity category, for ADS-B version 2
|
"""Calculate NIC, navigation integrity category, for ADS-B version 2
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
NICa (int or string): NIC supplement - A
|
NICa (int or string): NIC supplement - A
|
||||||
NICbc (int or srting): NIC supplement - B or C
|
NICbc (int or srting): NIC supplement - B or C
|
||||||
|
|
||||||
@@ -360,7 +362,7 @@ def nic_s(msg):
|
|||||||
"""Obtain NIC supplement bit, TC=31 message
|
"""Obtain NIC supplement bit, TC=31 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: NICs number (0 or 1)
|
int: NICs number (0 or 1)
|
||||||
@@ -382,7 +384,7 @@ def nic_a_c(msg):
|
|||||||
"""Obtain NICa/c, navigation integrity category supplements a and c
|
"""Obtain NICa/c, navigation integrity category supplements a and c
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(int, int): NICa and NICc number (0 or 1)
|
(int, int): NICa and NICc number (0 or 1)
|
||||||
@@ -405,7 +407,7 @@ def nic_b(msg):
|
|||||||
"""Obtain NICb, navigation integrity category supplement-b
|
"""Obtain NICb, navigation integrity category supplement-b
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: NICb number (0 or 1)
|
int: NICb number (0 or 1)
|
||||||
@@ -427,7 +429,7 @@ def nac_p(msg):
|
|||||||
"""Calculate NACp, Navigation Accuracy Category - Position
|
"""Calculate NACp, Navigation Accuracy Category - Position
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string, TC = 29 or 31
|
msg (string): 28 bytes hexadecimal message string, TC = 29 or 31
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int or string: 95% horizontal accuracy bounds, Estimated Position Uncertainty
|
int or string: 95% horizontal accuracy bounds, Estimated Position Uncertainty
|
||||||
@@ -462,7 +464,7 @@ def nac_v(msg):
|
|||||||
"""Calculate NACv, Navigation Accuracy Category - Velocity
|
"""Calculate NACv, Navigation Accuracy Category - Velocity
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string, TC = 19
|
msg (string): 28 bytes hexadecimal message string, TC = 19
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int or string: 95% horizontal accuracy bounds for velocity, Horizontal Figure of Merit
|
int or string: 95% horizontal accuracy bounds for velocity, Horizontal Figure of Merit
|
||||||
@@ -491,7 +493,7 @@ def sil(msg, version):
|
|||||||
"""Calculate SIL, Surveillance Integrity Level
|
"""Calculate SIL, Surveillance Integrity Level
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string with TC = 29, 31
|
msg (string): 28 bytes hexadecimal message string with TC = 29, 31
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int or string: Probability of exceeding Horizontal Radius of Containment RCu
|
int or string: Probability of exceeding Horizontal Radius of Containment RCu
|
||||||
@@ -502,7 +504,7 @@ def sil(msg, version):
|
|||||||
|
|
||||||
if tc not in [29, 31]:
|
if tc not in [29, 31]:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"%s: Not a target state and status message, \
|
"%s: Not a target state and status messag, \
|
||||||
or operation status message, expecting TC = 29 or 31"
|
or operation status message, expecting TC = 29 or 31"
|
||||||
% msg
|
% msg
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,82 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
Decode all-call reply messages, with dowlink format 11
|
Decoding all call replies DF=11
|
||||||
|
|
||||||
|
[To be implemented]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder import common
|
||||||
|
|
||||||
def _checkdf(func):
|
|
||||||
"""Ensure downlink format is 11."""
|
|
||||||
|
|
||||||
def wrapper(msg):
|
|
||||||
df = common.df(msg)
|
|
||||||
if df != 11:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Incorrect downlink format, expect 11, got {}".format(df)
|
|
||||||
)
|
|
||||||
return func(msg)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
@_checkdf
|
|
||||||
def icao(msg):
|
|
||||||
"""Decode transponder code (ICAO address).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (str): 14 hexdigits string
|
|
||||||
Returns:
|
|
||||||
string: ICAO address
|
|
||||||
|
|
||||||
"""
|
|
||||||
return common.icao(msg)
|
|
||||||
|
|
||||||
|
|
||||||
@_checkdf
|
|
||||||
def interrogator(msg):
|
|
||||||
"""Decode interrogator identifier code.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (str): 14 hexdigits string
|
|
||||||
Returns:
|
|
||||||
int: interrogator identifier code
|
|
||||||
|
|
||||||
"""
|
|
||||||
# the CRC remainder contains the CL and IC field. top three bits are CL field and last four bits are IC field.
|
|
||||||
remainder = common.crc(msg)
|
|
||||||
if remainder > 79:
|
|
||||||
IC = "corrupt IC"
|
|
||||||
elif remainder < 16:
|
|
||||||
IC="II"+str(remainder)
|
|
||||||
else:
|
|
||||||
IC="SI"+str(remainder-16)
|
|
||||||
return IC
|
|
||||||
|
|
||||||
|
|
||||||
@_checkdf
|
|
||||||
def capability(msg):
|
|
||||||
"""Decode transponder capability.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (str): 14 hexdigits string
|
|
||||||
Returns:
|
|
||||||
int, str: transponder capability, description
|
|
||||||
|
|
||||||
"""
|
|
||||||
msgbin = common.hex2bin(msg)
|
|
||||||
ca = common.bin2int(msgbin[5:8])
|
|
||||||
|
|
||||||
if ca == 0:
|
|
||||||
text = "level 1 transponder"
|
|
||||||
elif ca == 4:
|
|
||||||
text = "level 2 transponder, ability to set CA to 7, on ground"
|
|
||||||
elif ca == 5:
|
|
||||||
text = "level 2 transponder, ability to set CA to 7, airborne"
|
|
||||||
elif ca == 6:
|
|
||||||
text = "evel 2 transponder, ability to set CA to 7, either airborne or ground"
|
|
||||||
elif ca == 7:
|
|
||||||
text = "Downlink Request value is 0,or the Flight Status is 2, 3, 4 or 5, either airborne or on the ground"
|
|
||||||
else:
|
|
||||||
text = None
|
|
||||||
|
|
||||||
return ca, text
|
|
||||||
|
|||||||
@@ -18,11 +18,11 @@
|
|||||||
Common functions for Mode-S decoding
|
Common functions for Mode-S decoding
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from pyModeS.extra import aero
|
from pyModeS.extra import aero
|
||||||
from pyModeS import common
|
from pyModeS.decoder import common
|
||||||
|
|
||||||
from pyModeS.decoder.bds import (
|
from pyModeS.decoder.bds import (
|
||||||
bds05,
|
bds05,
|
||||||
bds06,
|
bds06,
|
||||||
@@ -45,7 +45,7 @@ def is50or60(msg, spd_ref, trk_ref, alt_ref):
|
|||||||
"""Use reference ground speed and trk to determine BDS50 and DBS60.
|
"""Use reference ground speed and trk to determine BDS50 and DBS60.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
spd_ref (float): reference speed (ADS-B ground speed), kts
|
spd_ref (float): reference speed (ADS-B ground speed), kts
|
||||||
trk_ref (float): reference track (ADS-B track angle), deg
|
trk_ref (float): reference track (ADS-B track angle), deg
|
||||||
alt_ref (float): reference altitude (ADS-B altitude), ft
|
alt_ref (float): reference altitude (ADS-B altitude), ft
|
||||||
@@ -60,34 +60,25 @@ def is50or60(msg, spd_ref, trk_ref, alt_ref):
|
|||||||
vy = v * np.cos(np.radians(angle))
|
vy = v * np.cos(np.radians(angle))
|
||||||
return vx, vy
|
return vx, vy
|
||||||
|
|
||||||
# message must be both BDS 50 and 60 before processing
|
|
||||||
if not (bds50.is50(msg) and bds60.is60(msg)):
|
if not (bds50.is50(msg) and bds60.is60(msg)):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# --- assuming BDS60 ---
|
h50 = bds50.trk50(msg)
|
||||||
|
v50 = bds50.gs50(msg)
|
||||||
|
|
||||||
|
if h50 is None or v50 is None:
|
||||||
|
return "BDS50,BDS60"
|
||||||
|
|
||||||
h60 = bds60.hdg60(msg)
|
h60 = bds60.hdg60(msg)
|
||||||
m60 = bds60.mach60(msg)
|
m60 = bds60.mach60(msg)
|
||||||
i60 = bds60.ias60(msg)
|
i60 = bds60.ias60(msg)
|
||||||
|
|
||||||
# additional check now knowing the altitude
|
|
||||||
if (m60 is not None) and (i60 is not None):
|
|
||||||
ias_ = aero.mach2cas(m60, alt_ref * aero.ft) / aero.kts
|
|
||||||
if abs(i60 - ias_) > 20:
|
|
||||||
return "BDS50"
|
|
||||||
|
|
||||||
if h60 is None or (m60 is None and i60 is None):
|
if h60 is None or (m60 is None and i60 is None):
|
||||||
return "BDS50,BDS60"
|
return "BDS50,BDS60"
|
||||||
|
|
||||||
m60 = np.nan if m60 is None else m60
|
m60 = np.nan if m60 is None else m60
|
||||||
i60 = np.nan if i60 is None else i60
|
i60 = np.nan if i60 is None else i60
|
||||||
|
|
||||||
# --- assuming BDS50 ---
|
|
||||||
h50 = bds50.trk50(msg)
|
|
||||||
v50 = bds50.gs50(msg)
|
|
||||||
|
|
||||||
if h50 is None or v50 is None:
|
|
||||||
return "BDS50,BDS60"
|
|
||||||
|
|
||||||
XY5 = vxy(v50 * aero.kts, h50)
|
XY5 = vxy(v50 * aero.kts, h50)
|
||||||
XY6m = vxy(aero.mach2tas(m60, alt_ref * aero.ft), h60)
|
XY6m = vxy(aero.mach2tas(m60, alt_ref * aero.ft), h60)
|
||||||
XY6i = vxy(aero.cas2tas(i60 * aero.kts, alt_ref * aero.ft), h60)
|
XY6i = vxy(aero.cas2tas(i60 * aero.kts, alt_ref * aero.ft), h60)
|
||||||
@@ -117,7 +108,7 @@ def infer(msg, mrar=False):
|
|||||||
"""Estimate the most likely BDS code of an message.
|
"""Estimate the most likely BDS code of an message.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
mrar (bool): Also infer MRAR (BDS 44) and MHR (BDS 45). Defaults to False.
|
mrar (bool): Also infer MRAR (BDS 44) and MHR (BDS 45). Defaults to False.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -134,7 +125,7 @@ def infer(msg, mrar=False):
|
|||||||
tc = common.typecode(msg)
|
tc = common.typecode(msg)
|
||||||
|
|
||||||
if 1 <= tc <= 4:
|
if 1 <= tc <= 4:
|
||||||
return "BDS08" # identification and category
|
return "BDS08" # indentification and category
|
||||||
if 5 <= tc <= 8:
|
if 5 <= tc <= 8:
|
||||||
return "BDS06" # surface movement
|
return "BDS06" # surface movement
|
||||||
if 9 <= tc <= 18:
|
if 9 <= tc <= 18:
|
||||||
|
|||||||
@@ -4,15 +4,17 @@
|
|||||||
# Airborn position
|
# Airborn position
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
|
||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder import common
|
||||||
|
|
||||||
|
|
||||||
def airborne_position(msg0, msg1, t0, t1):
|
def airborne_position(msg0, msg1, t0, t1):
|
||||||
"""Decode airborn position from a pair of even and odd position message
|
"""Decode airborn position from a pair of even and odd position message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg0 (string): even message (28 hexdigits)
|
msg0 (string): even message (28 bytes hexadecimal string)
|
||||||
msg1 (string): odd message (28 hexdigits)
|
msg1 (string): odd message (28 bytes hexadecimal string)
|
||||||
t0 (int): timestamps for the even message
|
t0 (int): timestamps for the even message
|
||||||
t1 (int): timestamps for the odd message
|
t1 (int): timestamps for the odd message
|
||||||
|
|
||||||
@@ -85,7 +87,7 @@ def airborne_position_with_ref(msg, lat_ref, lon_ref):
|
|||||||
be with in 180NM of the true position.
|
be with in 180NM of the true position.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): even message (28 hexdigits)
|
msg (string): even message (28 bytes hexadecimal string)
|
||||||
lat_ref: previous known latitude
|
lat_ref: previous known latitude
|
||||||
lon_ref: previous known longitude
|
lon_ref: previous known longitude
|
||||||
|
|
||||||
@@ -127,7 +129,7 @@ def altitude(msg):
|
|||||||
"""Decode aircraft altitude
|
"""Decode aircraft altitude
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: altitude in feet
|
int: altitude in feet
|
||||||
@@ -139,13 +141,17 @@ def altitude(msg):
|
|||||||
raise RuntimeError("%s: Not a airborn position message" % msg)
|
raise RuntimeError("%s: Not a airborn position message" % msg)
|
||||||
|
|
||||||
mb = common.hex2bin(msg)[32:]
|
mb = common.hex2bin(msg)[32:]
|
||||||
altbin = mb[8:20]
|
|
||||||
|
|
||||||
if tc < 19:
|
if tc < 19:
|
||||||
altcode = altbin[0:6] + "0" + altbin[6:]
|
# barometric altitude
|
||||||
|
q = mb[15]
|
||||||
|
if q:
|
||||||
|
n = common.bin2int(mb[8:15] + mb[16:20])
|
||||||
|
alt = n * 25 - 1000
|
||||||
|
else:
|
||||||
|
alt = None
|
||||||
else:
|
else:
|
||||||
altcode = altbin[0:6] + "0" + altbin[6:]
|
# GNSS altitude, meters -> feet
|
||||||
|
alt = common.bin2int(mb[8:20]) * 3.28084
|
||||||
alt = common.altitude(altcode)
|
|
||||||
|
|
||||||
return alt
|
return alt
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
# BDS 0,6
|
# BDS 0,6
|
||||||
# ADS-B TC=5-8
|
# ADS-B TC=5-8
|
||||||
# Surface movment
|
# Surface position
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder import common
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
def surface_position(msg0, msg1, t0, t1, lat_ref, lon_ref):
|
def surface_position(msg0, msg1, t0, t1, lat_ref, lon_ref):
|
||||||
@@ -12,8 +14,8 @@ def surface_position(msg0, msg1, t0, t1, lat_ref, lon_ref):
|
|||||||
the lat/lon of receiver must be provided to yield the correct solution.
|
the lat/lon of receiver must be provided to yield the correct solution.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg0 (string): even message (28 hexdigits)
|
msg0 (string): even message (28 bytes hexadecimal string)
|
||||||
msg1 (string): odd message (28 hexdigits)
|
msg1 (string): odd message (28 bytes hexadecimal string)
|
||||||
t0 (int): timestamps for the even message
|
t0 (int): timestamps for the even message
|
||||||
t1 (int): timestamps for the odd message
|
t1 (int): timestamps for the odd message
|
||||||
lat_ref (float): latitude of the receiver
|
lat_ref (float): latitude of the receiver
|
||||||
@@ -89,7 +91,7 @@ def surface_position_with_ref(msg, lat_ref, lon_ref):
|
|||||||
be with in 45NM of the true position.
|
be with in 45NM of the true position.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): even message (28 hexdigits)
|
msg (string): even message (28 bytes hexadecimal string)
|
||||||
lat_ref: previous known latitude
|
lat_ref: previous known latitude
|
||||||
lon_ref: previous known longitude
|
lon_ref: previous known longitude
|
||||||
|
|
||||||
@@ -127,24 +129,23 @@ def surface_position_with_ref(msg, lat_ref, lon_ref):
|
|||||||
return round(lat, 5), round(lon, 5)
|
return round(lat, 5), round(lon, 5)
|
||||||
|
|
||||||
|
|
||||||
def surface_velocity(msg, source=False):
|
def surface_velocity(msg, rtn_sources=False):
|
||||||
"""Decode surface velocity from a surface position message
|
"""Decode surface velocity from from a surface position message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
source (boolean): Include direction and vertical rate sources in return. Default to False.
|
rtn_source (boolean): If the function will return
|
||||||
If set to True, the function will return six value instead of four.
|
the sources for direction of travel and vertical
|
||||||
|
rate. This will change the return value from a four
|
||||||
|
element array to a six element array.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int, float, int, string, [string], [string]: Four or six parameters, including:
|
(int, float, int, string, string, None): speed (kt),
|
||||||
- Speed (kt)
|
ground track (degree), None for rate of climb/descend (ft/min),
|
||||||
- Angle (degree), ground track
|
and speed type ('GS' for ground speed), direction source
|
||||||
- Vertical rate, always 0
|
('true_north' for ground track / true north as reference),
|
||||||
- Speed type ('GS' for ground speed, 'AS' for airspeed)
|
None rate of climb/descent source.
|
||||||
- [Optional] Direction source ('TRUE_NORTH')
|
|
||||||
- [Optional] Vertical rate source (None)
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if common.typecode(msg) < 5 or common.typecode(msg) > 8:
|
if common.typecode(msg) < 5 or common.typecode(msg) > 8:
|
||||||
raise RuntimeError("%s: Not a surface message, expecting 5<TC<8" % msg)
|
raise RuntimeError("%s: Not a surface message, expecting 5<TC<8" % msg)
|
||||||
|
|
||||||
@@ -158,7 +159,7 @@ def surface_velocity(msg, source=False):
|
|||||||
else:
|
else:
|
||||||
trk = None
|
trk = None
|
||||||
|
|
||||||
# ground movement / speed
|
# ground movment / speed
|
||||||
mov = common.bin2int(mb[5:12])
|
mov = common.bin2int(mb[5:12])
|
||||||
|
|
||||||
if mov == 0 or mov > 124:
|
if mov == 0 or mov > 124:
|
||||||
@@ -168,12 +169,14 @@ def surface_velocity(msg, source=False):
|
|||||||
elif mov == 124:
|
elif mov == 124:
|
||||||
spd = 175
|
spd = 175
|
||||||
else:
|
else:
|
||||||
mov_lb = [2, 9, 13, 39, 94, 109, 124]
|
movs = [2, 9, 13, 39, 94, 109, 124]
|
||||||
kts_lb = [0.125, 1, 2, 15, 70, 100, 175]
|
kts = [0.125, 1, 2, 15, 70, 100, 175]
|
||||||
step = [0.125, 0.25, 0.5, 1, 2, 5]
|
i = next(m[0] for m in enumerate(movs) if m[1] > mov)
|
||||||
i = next(m[0] for m in enumerate(mov_lb) if m[1] > mov)
|
step = (kts[i] - kts[i - 1]) * 1.0 / (movs[i] - movs[i - 1])
|
||||||
spd = kts_lb[i - 1] + (mov - mov_lb[i - 1]) * step[i - 1]
|
spd = kts[i - 1] + (mov - movs[i - 1]) * step
|
||||||
if source:
|
spd = round(spd, 2)
|
||||||
return spd, trk, 0, "GS", "TRUE_NORTH", None
|
|
||||||
|
if rtn_sources:
|
||||||
|
return spd, trk, 0, "GS", "true_north", None
|
||||||
else:
|
else:
|
||||||
return spd, trk, 0, "GS"
|
return spd, trk, 0, "GS"
|
||||||
|
|||||||
@@ -4,14 +4,15 @@
|
|||||||
# Aircraft identitification and category
|
# Aircraft identitification and category
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder import common
|
||||||
|
|
||||||
|
|
||||||
def category(msg):
|
def category(msg):
|
||||||
"""Aircraft category number
|
"""Aircraft category number
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: category number
|
int: category number
|
||||||
@@ -29,7 +30,7 @@ def callsign(msg):
|
|||||||
"""Aircraft callsign
|
"""Aircraft callsign
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
string: callsign
|
string: callsign
|
||||||
|
|||||||
@@ -4,30 +4,32 @@
|
|||||||
# Aircraft Airborn velocity
|
# Aircraft Airborn velocity
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder import common
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
|
||||||
def airborne_velocity(msg, source=False):
|
def airborne_velocity(msg, rtn_sources=False):
|
||||||
"""Decode airborne velocity.
|
"""Calculate the speed, track (or heading), and vertical rate
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
source (boolean): Include direction and vertical rate sources in return. Default to False.
|
rtn_source (boolean): If the function will return
|
||||||
If set to True, the function will return six value instead of four.
|
the sources for direction of travel and vertical
|
||||||
|
rate. This will change the return value from a four
|
||||||
|
element array to a six element array.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int, float, int, string, [string], [string]: Four or six parameters, including:
|
(int, float, int, string, string, string): speed (kt),
|
||||||
- Speed (kt)
|
ground track or heading (degree),
|
||||||
- Angle (degree), either ground track or heading
|
rate of climb/descent (ft/min), speed type
|
||||||
- Vertical rate (ft/min)
|
('GS' for ground speed, 'AS' for airspeed),
|
||||||
- Speed type ('GS' for ground speed, 'AS' for airspeed)
|
direction source ('true_north' for ground track / true north
|
||||||
- [Optional] Direction source ('TRUE_NORTH' or 'MAGENTIC_NORTH')
|
as refrence, 'mag_north' for magnetic north as reference),
|
||||||
- [Optional] Vertical rate source ('BARO' or 'GNSS')
|
rate of climb/descent source ('Baro' for barometer, 'GNSS'
|
||||||
|
for GNSS constellation).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if common.typecode(msg) != 19:
|
if common.typecode(msg) != 19:
|
||||||
raise RuntimeError("%s: Not a airborne velocity message, expecting TC=19" % msg)
|
raise RuntimeError("%s: Not a airborne velocity message, expecting TC=19" % msg)
|
||||||
|
|
||||||
@@ -59,9 +61,9 @@ def airborne_velocity(msg, source=False):
|
|||||||
trk = math.degrees(trk) # convert to degrees
|
trk = math.degrees(trk) # convert to degrees
|
||||||
trk = trk if trk >= 0 else trk + 360 # no negative val
|
trk = trk if trk >= 0 else trk + 360 # no negative val
|
||||||
|
|
||||||
spd_type = "GS"
|
tag = "GS"
|
||||||
trk_or_hdg = round(trk, 2)
|
trk_or_hdg = round(trk, 2)
|
||||||
dir_type = "TRUE_NORTH"
|
dir_type = "true_north"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if mb[13] == "0":
|
if mb[13] == "0":
|
||||||
@@ -78,33 +80,32 @@ def airborne_velocity(msg, source=False):
|
|||||||
spd *= 4
|
spd *= 4
|
||||||
|
|
||||||
if mb[24] == "0":
|
if mb[24] == "0":
|
||||||
spd_type = "IAS"
|
tag = "IAS"
|
||||||
else:
|
else:
|
||||||
spd_type = "TAS"
|
tag = "TAS"
|
||||||
|
|
||||||
dir_type = "MAGENTIC_NORTH"
|
dir_type = "mag_north"
|
||||||
|
|
||||||
vr_source = "GNSS" if mb[35] == "0" else "BARO"
|
vr_source = "GNSS" if mb[35] == "0" else "Baro"
|
||||||
vr_sign = -1 if mb[36] == "1" else 1
|
vr_sign = -1 if mb[36] == "1" else 1
|
||||||
vr = common.bin2int(mb[37:46])
|
vr = common.bin2int(mb[37:46])
|
||||||
vs = None if vr == 0 else int(vr_sign * (vr - 1) * 64)
|
rocd = None if vr == 0 else int(vr_sign * (vr - 1) * 64)
|
||||||
|
|
||||||
if source:
|
if rtn_sources:
|
||||||
return spd, trk_or_hdg, vs, spd_type, dir_type, vr_source
|
return spd, trk_or_hdg, rocd, tag, dir_type, vr_source
|
||||||
else:
|
else:
|
||||||
return spd, trk_or_hdg, vs, spd_type
|
return spd, trk_or_hdg, rocd, tag
|
||||||
|
|
||||||
|
|
||||||
def altitude_diff(msg):
|
def altitude_diff(msg):
|
||||||
"""Decode the differece between GNSS and barometric altitude.
|
"""Decode the differece between GNSS and barometric altitude
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string, TC=19
|
msg (string): 28 bytes hexadecimal message string, TC=19
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: Altitude difference in feet. Negative value indicates GNSS altitude
|
int: Altitude difference in ft. Negative value indicates GNSS altitude
|
||||||
below barometric altitude.
|
below barometric altitude.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
tc = common.typecode(msg)
|
tc = common.typecode(msg)
|
||||||
|
|
||||||
|
|||||||
@@ -3,36 +3,37 @@
|
|||||||
# Data link capability report
|
# Data link capability report
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros
|
||||||
|
|
||||||
|
|
||||||
def is10(msg):
|
def is10(msg):
|
||||||
"""Check if a message is likely to be BDS code 1,0
|
"""Check if a message is likely to be BDS code 1,0
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True or False
|
bool: True or False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if common.allzeros(msg):
|
if allzeros(msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
# first 8 bits must be 0x10
|
# first 8 bits must be 0x10
|
||||||
if d[0:8] != "00010000":
|
if d[0:8] != "00010000":
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# bit 10 to 14 are reserved
|
# bit 10 to 14 are reserved
|
||||||
if common.bin2int(d[9:14]) != 0:
|
if bin2int(d[9:14]) != 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# overlay capability conflict
|
# overlay capabilty conflict
|
||||||
if d[14] == "1" and common.bin2int(d[16:23]) < 5:
|
if d[14] == "1" and bin2int(d[16:23]) < 5:
|
||||||
return False
|
return False
|
||||||
if d[14] == "0" and common.bin2int(d[16:23]) > 4:
|
if d[14] == "0" and bin2int(d[16:23]) > 4:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -42,11 +43,11 @@ def ovc10(msg):
|
|||||||
"""Return the overlay control capability
|
"""Return the overlay control capability
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: Whether the transponder is OVC capable
|
int: Whether the transponder is OVC capable
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
return int(d[14])
|
return int(d[14])
|
||||||
|
|||||||
@@ -3,25 +3,27 @@
|
|||||||
# Common usage GICB capability report
|
# Common usage GICB capability report
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
|
||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros
|
||||||
|
|
||||||
|
|
||||||
def is17(msg):
|
def is17(msg):
|
||||||
"""Check if a message is likely to be BDS code 1,7
|
"""Check if a message is likely to be BDS code 1,7
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True or False
|
bool: True or False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if common.allzeros(msg):
|
if allzeros(msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if common.bin2int(d[24:56]) != 0:
|
if bin2int(d[28:56]) != 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
caps = cap17(msg)
|
caps = cap17(msg)
|
||||||
@@ -42,10 +44,10 @@ def cap17(msg):
|
|||||||
"""Extract capacities from BDS 1,7 message
|
"""Extract capacities from BDS 1,7 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: list of support BDS codes
|
list: list of suport BDS codes
|
||||||
"""
|
"""
|
||||||
allbds = [
|
allbds = [
|
||||||
"05",
|
"05",
|
||||||
@@ -72,10 +74,14 @@ def cap17(msg):
|
|||||||
"56",
|
"56",
|
||||||
"5F",
|
"5F",
|
||||||
"60",
|
"60",
|
||||||
|
"NA",
|
||||||
|
"NA",
|
||||||
|
"E1",
|
||||||
|
"E2",
|
||||||
]
|
]
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
idx = [i for i, v in enumerate(d[:24]) if v == "1"]
|
idx = [i for i, v in enumerate(d[:28]) if v == "1"]
|
||||||
capacity = ["BDS" + allbds[i] for i in idx]
|
capacity = ["BDS" + allbds[i] for i in idx if allbds[i] is not "NA"]
|
||||||
|
|
||||||
return capacity
|
return capacity
|
||||||
|
|||||||
@@ -3,23 +3,24 @@
|
|||||||
# Aircraft identification
|
# Aircraft identification
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros
|
||||||
|
|
||||||
|
|
||||||
def is20(msg):
|
def is20(msg):
|
||||||
"""Check if a message is likely to be BDS code 2,0
|
"""Check if a message is likely to be BDS code 2,0
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True or False
|
bool: True or False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if common.allzeros(msg):
|
if allzeros(msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[0:8] != "00100000":
|
if d[0:8] != "00100000":
|
||||||
return False
|
return False
|
||||||
@@ -36,23 +37,23 @@ def cs20(msg):
|
|||||||
"""Aircraft callsign
|
"""Aircraft callsign
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS40) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
string: callsign, max. 8 chars
|
string: callsign, max. 8 chars
|
||||||
"""
|
"""
|
||||||
chars = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######"
|
chars = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######"
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
cs = ""
|
cs = ""
|
||||||
cs += chars[common.bin2int(d[8:14])]
|
cs += chars[bin2int(d[8:14])]
|
||||||
cs += chars[common.bin2int(d[14:20])]
|
cs += chars[bin2int(d[14:20])]
|
||||||
cs += chars[common.bin2int(d[20:26])]
|
cs += chars[bin2int(d[20:26])]
|
||||||
cs += chars[common.bin2int(d[26:32])]
|
cs += chars[bin2int(d[26:32])]
|
||||||
cs += chars[common.bin2int(d[32:38])]
|
cs += chars[bin2int(d[32:38])]
|
||||||
cs += chars[common.bin2int(d[38:44])]
|
cs += chars[bin2int(d[38:44])]
|
||||||
cs += chars[common.bin2int(d[44:50])]
|
cs += chars[bin2int(d[44:50])]
|
||||||
cs += chars[common.bin2int(d[50:56])]
|
cs += chars[bin2int(d[50:56])]
|
||||||
|
|
||||||
return cs
|
return cs
|
||||||
|
|||||||
@@ -3,23 +3,24 @@
|
|||||||
# ACAS active resolution advisory
|
# ACAS active resolution advisory
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros
|
||||||
|
|
||||||
|
|
||||||
def is30(msg):
|
def is30(msg):
|
||||||
"""Check if a message is likely to be BDS code 2,0
|
"""Check if a message is likely to be BDS code 2,0
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True or False
|
bool: True or False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if common.allzeros(msg):
|
if allzeros(msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[0:8] != "00110000":
|
if d[0:8] != "00110000":
|
||||||
return False
|
return False
|
||||||
@@ -29,7 +30,7 @@ def is30(msg):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# reserved for ACAS III, in far future
|
# reserved for ACAS III, in far future
|
||||||
if common.bin2int(d[15:22]) >= 48:
|
if bin2int(d[15:22]) >= 48:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -3,48 +3,49 @@
|
|||||||
# Selected vertical intention
|
# Selected vertical intention
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
import warnings
|
import warnings
|
||||||
from pyModeS import common
|
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||||
|
|
||||||
|
|
||||||
def is40(msg):
|
def is40(msg):
|
||||||
"""Check if a message is likely to be BDS code 4,0
|
"""Check if a message is likely to be BDS code 4,0
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True or False
|
bool: True or False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if common.allzeros(msg):
|
if allzeros(msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
# status bit 1, 14, and 27
|
# status bit 1, 14, and 27
|
||||||
|
|
||||||
if common.wrongstatus(d, 1, 2, 13):
|
if wrongstatus(d, 1, 2, 13):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 14, 15, 26):
|
if wrongstatus(d, 14, 15, 26):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 27, 28, 39):
|
if wrongstatus(d, 27, 28, 39):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 48, 49, 51):
|
if wrongstatus(d, 48, 49, 51):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 54, 55, 56):
|
if wrongstatus(d, 54, 55, 56):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# bits 40-47 and 52-53 shall all be zero
|
# bits 40-47 and 52-53 shall all be zero
|
||||||
|
|
||||||
if common.bin2int(d[39:47]) != 0:
|
if bin2int(d[39:47]) != 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.bin2int(d[51:53]) != 0:
|
if bin2int(d[51:53]) != 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -54,17 +55,17 @@ def selalt40mcp(msg):
|
|||||||
"""Selected altitude, MCP/FCU
|
"""Selected altitude, MCP/FCU
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS40) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: altitude in feet
|
int: altitude in feet
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[0] == "0":
|
if d[0] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
alt = common.bin2int(d[1:13]) * 16 # ft
|
alt = bin2int(d[1:13]) * 16 # ft
|
||||||
return alt
|
return alt
|
||||||
|
|
||||||
|
|
||||||
@@ -72,17 +73,17 @@ def selalt40fms(msg):
|
|||||||
"""Selected altitude, FMS
|
"""Selected altitude, FMS
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS40) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: altitude in feet
|
int: altitude in feet
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[13] == "0":
|
if d[13] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
alt = common.bin2int(d[14:26]) * 16 # ft
|
alt = bin2int(d[14:26]) * 16 # ft
|
||||||
return alt
|
return alt
|
||||||
|
|
||||||
|
|
||||||
@@ -90,17 +91,17 @@ def p40baro(msg):
|
|||||||
"""Barometric pressure setting
|
"""Barometric pressure setting
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS40) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: pressure in millibar
|
float: pressure in millibar
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[26] == "0":
|
if d[26] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
p = common.bin2int(d[27:39]) * 0.1 + 800 # millibar
|
p = bin2int(d[27:39]) * 0.1 + 800 # millibar
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
@@ -117,4 +118,4 @@ def alt40fms(msg):
|
|||||||
"alt40fms() has been renamed to selalt40fms(). It will be removed in the future.",
|
"alt40fms() has been renamed to selalt40fms(). It will be removed in the future.",
|
||||||
DeprecationWarning,
|
DeprecationWarning,
|
||||||
)
|
)
|
||||||
return selalt40fms(msg)
|
return selalt40mcp(msg)
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
# Meteorological routine air report
|
# Meteorological routine air report
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||||
|
|
||||||
|
|
||||||
def is44(msg):
|
def is44(msg):
|
||||||
@@ -12,32 +13,32 @@ def is44(msg):
|
|||||||
Meteorological routine air report
|
Meteorological routine air report
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True or False
|
bool: True or False
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if common.allzeros(msg):
|
if allzeros(msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
# status bit 5, 35, 47, 50
|
# status bit 5, 35, 47, 50
|
||||||
if common.wrongstatus(d, 5, 6, 23):
|
if wrongstatus(d, 5, 6, 23):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 35, 36, 46):
|
if wrongstatus(d, 35, 36, 46):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 47, 48, 49):
|
if wrongstatus(d, 47, 48, 49):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 50, 51, 56):
|
if wrongstatus(d, 50, 51, 56):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Bits 1-4 indicate source, values > 4 reserved and should not occur
|
# Bits 1-4 indicate source, values > 4 reserved and should not occur
|
||||||
if common.bin2int(d[0:4]) > 4:
|
if bin2int(d[0:4]) > 4:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
vw, dw = wind44(msg)
|
vw, dw = wind44(msg)
|
||||||
@@ -55,20 +56,20 @@ def wind44(msg):
|
|||||||
"""Wind speed and direction.
|
"""Wind speed and direction.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(int, float): speed (kt), direction (degree)
|
(int, float): speed (kt), direction (degree)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
status = int(d[4])
|
status = int(d[4])
|
||||||
if not status:
|
if not status:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
speed = common.bin2int(d[5:14]) # knots
|
speed = bin2int(d[5:14]) # knots
|
||||||
direction = common.bin2int(d[14:23]) * 180.0 / 256.0 # degree
|
direction = bin2int(d[14:23]) * 180.0 / 256.0 # degree
|
||||||
|
|
||||||
return round(speed, 0), round(direction, 1)
|
return round(speed, 0), round(direction, 1)
|
||||||
|
|
||||||
@@ -77,18 +78,18 @@ def temp44(msg):
|
|||||||
"""Static air temperature.
|
"""Static air temperature.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float, float: temperature and alternative temperature in Celsius degree.
|
float, float: temperature and alternative temperature in Celsius degree.
|
||||||
Note: Two values returns due to what seems to be an inconsistency
|
Note: Two values returns due to what seems to be an inconsistancy
|
||||||
error in ICAO 9871 (2008) Appendix A-67.
|
error in ICAO 9871 (2008) Appendix A-67.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
sign = int(d[23])
|
sign = int(d[23])
|
||||||
value = common.bin2int(d[24:34])
|
value = bin2int(d[24:34])
|
||||||
|
|
||||||
if sign:
|
if sign:
|
||||||
value = value - 1024
|
value = value - 1024
|
||||||
@@ -106,18 +107,18 @@ def p44(msg):
|
|||||||
"""Static pressure.
|
"""Static pressure.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: static pressure in hPa
|
int: static pressure in hPa
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[34] == "0":
|
if d[34] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
p = common.bin2int(d[35:46]) # hPa
|
p = bin2int(d[35:46]) # hPa
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
@@ -126,17 +127,17 @@ def hum44(msg):
|
|||||||
"""humidity
|
"""humidity
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: percentage of humidity, [0 - 100] %
|
float: percentage of humidity, [0 - 100] %
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[49] == "0":
|
if d[49] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
hm = common.bin2int(d[50:56]) * 100.0 / 64 # %
|
hm = bin2int(d[50:56]) * 100.0 / 64 # %
|
||||||
|
|
||||||
return round(hm, 1)
|
return round(hm, 1)
|
||||||
|
|
||||||
@@ -145,17 +146,17 @@ def turb44(msg):
|
|||||||
"""Turblence.
|
"""Turblence.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: turbulence level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
int: turbulence level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[46] == "0":
|
if d[46] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
turb = common.bin2int(d[47:49])
|
turb = bin2int(d[47:49])
|
||||||
|
|
||||||
return turb
|
return turb
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
# Meteorological hazard report
|
# Meteorological hazard report
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||||
|
|
||||||
|
|
||||||
def is45(msg):
|
def is45(msg):
|
||||||
@@ -12,44 +13,44 @@ def is45(msg):
|
|||||||
Meteorological hazard report
|
Meteorological hazard report
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True or False
|
bool: True or False
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if common.allzeros(msg):
|
if allzeros(msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
# status bit 1, 4, 7, 10, 13, 16, 27, 39
|
# status bit 1, 4, 7, 10, 13, 16, 27, 39
|
||||||
if common.wrongstatus(d, 1, 2, 3):
|
if wrongstatus(d, 1, 2, 3):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 4, 5, 6):
|
if wrongstatus(d, 4, 5, 6):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 7, 8, 9):
|
if wrongstatus(d, 7, 8, 9):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 10, 11, 12):
|
if wrongstatus(d, 10, 11, 12):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 13, 14, 15):
|
if wrongstatus(d, 13, 14, 15):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 16, 17, 26):
|
if wrongstatus(d, 16, 17, 26):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 27, 28, 38):
|
if wrongstatus(d, 27, 28, 38):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 39, 40, 51):
|
if wrongstatus(d, 39, 40, 51):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# reserved
|
# reserved
|
||||||
if common.bin2int(d[51:56]) != 0:
|
if bin2int(d[51:56]) != 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
temp = temp45(msg)
|
temp = temp45(msg)
|
||||||
@@ -64,17 +65,17 @@ def turb45(msg):
|
|||||||
"""Turbulence.
|
"""Turbulence.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: Turbulence level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
int: Turbulence level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
if d[0] == "0":
|
if d[0] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
turb = common.bin2int(d[1:3])
|
turb = bin2int(d[1:3])
|
||||||
return turb
|
return turb
|
||||||
|
|
||||||
|
|
||||||
@@ -82,17 +83,17 @@ def ws45(msg):
|
|||||||
"""Wind shear.
|
"""Wind shear.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: Wind shear level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
int: Wind shear level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
if d[3] == "0":
|
if d[3] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ws = common.bin2int(d[4:6])
|
ws = bin2int(d[4:6])
|
||||||
return ws
|
return ws
|
||||||
|
|
||||||
|
|
||||||
@@ -100,17 +101,17 @@ def mb45(msg):
|
|||||||
"""Microburst.
|
"""Microburst.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: Microburst level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
int: Microburst level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
if d[6] == "0":
|
if d[6] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
mb = common.bin2int(d[7:9])
|
mb = bin2int(d[7:9])
|
||||||
return mb
|
return mb
|
||||||
|
|
||||||
|
|
||||||
@@ -118,17 +119,17 @@ def ic45(msg):
|
|||||||
"""Icing.
|
"""Icing.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: Icing level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
int: Icing level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
if d[9] == "0":
|
if d[9] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ic = common.bin2int(d[10:12])
|
ic = bin2int(d[10:12])
|
||||||
return ic
|
return ic
|
||||||
|
|
||||||
|
|
||||||
@@ -136,17 +137,17 @@ def wv45(msg):
|
|||||||
"""Wake vortex.
|
"""Wake vortex.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: Wake vortex level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
int: Wake vortex level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
if d[12] == "0":
|
if d[12] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ws = common.bin2int(d[13:15])
|
ws = bin2int(d[13:15])
|
||||||
return ws
|
return ws
|
||||||
|
|
||||||
|
|
||||||
@@ -154,16 +155,16 @@ def temp45(msg):
|
|||||||
"""Static air temperature.
|
"""Static air temperature.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: tmeperature in Celsius degree
|
float: tmeperature in Celsius degree
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
sign = int(d[16])
|
sign = int(d[16])
|
||||||
value = common.bin2int(d[17:26])
|
value = bin2int(d[17:26])
|
||||||
|
|
||||||
if sign:
|
if sign:
|
||||||
value = value - 512
|
value = value - 512
|
||||||
@@ -178,16 +179,16 @@ def p45(msg):
|
|||||||
"""Average static pressure.
|
"""Average static pressure.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: static pressure in hPa
|
int: static pressure in hPa
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
if d[26] == "0":
|
if d[26] == "0":
|
||||||
return None
|
return None
|
||||||
p = common.bin2int(d[27:38]) # hPa
|
p = bin2int(d[27:38]) # hPa
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
@@ -195,14 +196,14 @@ def rh45(msg):
|
|||||||
"""Radio height.
|
"""Radio height.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: radio height in ft
|
int: radio height in ft
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
if d[38] == "0":
|
if d[38] == "0":
|
||||||
return None
|
return None
|
||||||
rh = common.bin2int(d[39:51]) * 16
|
rh = bin2int(d[39:51]) * 16
|
||||||
return rh
|
return rh
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
# Track and turn report
|
# Track and turn report
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||||
|
|
||||||
|
|
||||||
def is50(msg):
|
def is50(msg):
|
||||||
@@ -11,36 +12,36 @@ def is50(msg):
|
|||||||
(Track and turn report)
|
(Track and turn report)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True or False
|
bool: True or False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if common.allzeros(msg):
|
if allzeros(msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
# status bit 1, 12, 24, 35, 46
|
# status bit 1, 12, 24, 35, 46
|
||||||
|
|
||||||
if common.wrongstatus(d, 1, 3, 11):
|
if wrongstatus(d, 1, 3, 11):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 12, 13, 23):
|
if wrongstatus(d, 12, 13, 23):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 24, 25, 34):
|
if wrongstatus(d, 24, 25, 34):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 35, 36, 45):
|
if wrongstatus(d, 35, 36, 45):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 46, 47, 56):
|
if wrongstatus(d, 46, 47, 56):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
roll = roll50(msg)
|
roll = roll50(msg)
|
||||||
if (roll is not None) and abs(roll) > 50:
|
if (roll is not None) and abs(roll) > 60:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
gs = gs50(msg)
|
gs = gs50(msg)
|
||||||
@@ -61,19 +62,19 @@ def roll50(msg):
|
|||||||
"""Roll angle, BDS 5,0 message
|
"""Roll angle, BDS 5,0 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS50) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: angle in degrees,
|
float: angle in degrees,
|
||||||
negative->left wing down, positive->right wing down
|
negative->left wing down, positive->right wing down
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[0] == "0":
|
if d[0] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sign = int(d[1]) # 1 -> left wing down
|
sign = int(d[1]) # 1 -> left wing down
|
||||||
value = common.bin2int(d[2:11])
|
value = bin2int(d[2:11])
|
||||||
|
|
||||||
if sign:
|
if sign:
|
||||||
value = value - 512
|
value = value - 512
|
||||||
@@ -86,18 +87,18 @@ def trk50(msg):
|
|||||||
"""True track angle, BDS 5,0 message
|
"""True track angle, BDS 5,0 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS50) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: angle in degrees to true north (from 0 to 360)
|
float: angle in degrees to true north (from 0 to 360)
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[11] == "0":
|
if d[11] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sign = int(d[12]) # 1 -> west
|
sign = int(d[12]) # 1 -> west
|
||||||
value = common.bin2int(d[13:23])
|
value = bin2int(d[13:23])
|
||||||
|
|
||||||
if sign:
|
if sign:
|
||||||
value = value - 1024
|
value = value - 1024
|
||||||
@@ -115,17 +116,17 @@ def gs50(msg):
|
|||||||
"""Ground speed, BDS 5,0 message
|
"""Ground speed, BDS 5,0 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS50) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: ground speed in knots
|
int: ground speed in knots
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[23] == "0":
|
if d[23] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
spd = common.bin2int(d[24:34]) * 2 # kts
|
spd = bin2int(d[24:34]) * 2 # kts
|
||||||
return spd
|
return spd
|
||||||
|
|
||||||
|
|
||||||
@@ -133,12 +134,12 @@ def rtrk50(msg):
|
|||||||
"""Track angle rate, BDS 5,0 message
|
"""Track angle rate, BDS 5,0 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS50) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: angle rate in degrees/second
|
float: angle rate in degrees/second
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[34] == "0":
|
if d[34] == "0":
|
||||||
return None
|
return None
|
||||||
@@ -147,7 +148,7 @@ def rtrk50(msg):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
sign = int(d[35]) # 1 -> negative value, two's complement
|
sign = int(d[35]) # 1 -> negative value, two's complement
|
||||||
value = common.bin2int(d[36:45])
|
value = bin2int(d[36:45])
|
||||||
if sign:
|
if sign:
|
||||||
value = value - 512
|
value = value - 512
|
||||||
|
|
||||||
@@ -159,15 +160,15 @@ def tas50(msg):
|
|||||||
"""Aircraft true airspeed, BDS 5,0 message
|
"""Aircraft true airspeed, BDS 5,0 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS50) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: true airspeed in knots
|
int: true airspeed in knots
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[45] == "0":
|
if d[45] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
tas = common.bin2int(d[46:56]) * 2 # kts
|
tas = bin2int(d[46:56]) * 2 # kts
|
||||||
return tas
|
return tas
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
# Air-referenced state vector
|
# Air-referenced state vector
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||||
|
|
||||||
|
|
||||||
def is53(msg):
|
def is53(msg):
|
||||||
@@ -11,32 +12,32 @@ def is53(msg):
|
|||||||
(Air-referenced state vector)
|
(Air-referenced state vector)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True or False
|
bool: True or False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if common.allzeros(msg):
|
if allzeros(msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
# status bit 1, 13, 24, 34, 47
|
# status bit 1, 13, 24, 34, 47
|
||||||
|
|
||||||
if common.wrongstatus(d, 1, 3, 12):
|
if wrongstatus(d, 1, 3, 12):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 13, 14, 23):
|
if wrongstatus(d, 13, 14, 23):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 24, 25, 33):
|
if wrongstatus(d, 24, 25, 33):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 34, 35, 46):
|
if wrongstatus(d, 34, 35, 46):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 47, 49, 56):
|
if wrongstatus(d, 47, 49, 56):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
ias = ias53(msg)
|
ias = ias53(msg)
|
||||||
@@ -62,18 +63,18 @@ def hdg53(msg):
|
|||||||
"""Magnetic heading, BDS 5,3 message
|
"""Magnetic heading, BDS 5,3 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS53) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: angle in degrees to true north (from 0 to 360)
|
float: angle in degrees to true north (from 0 to 360)
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[0] == "0":
|
if d[0] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sign = int(d[1]) # 1 -> west
|
sign = int(d[1]) # 1 -> west
|
||||||
value = common.bin2int(d[2:12])
|
value = bin2int(d[2:12])
|
||||||
|
|
||||||
if sign:
|
if sign:
|
||||||
value = value - 1024
|
value = value - 1024
|
||||||
@@ -91,17 +92,17 @@ def ias53(msg):
|
|||||||
"""Indicated airspeed, DBS 5,3 message
|
"""Indicated airspeed, DBS 5,3 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits
|
msg (String): 28 bytes hexadecimal message
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: indicated arispeed in knots
|
int: indicated arispeed in knots
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[12] == "0":
|
if d[12] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ias = common.bin2int(d[13:23]) # knots
|
ias = bin2int(d[13:23]) # knots
|
||||||
return ias
|
return ias
|
||||||
|
|
||||||
|
|
||||||
@@ -109,17 +110,17 @@ def mach53(msg):
|
|||||||
"""MACH number, DBS 5,3 message
|
"""MACH number, DBS 5,3 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits
|
msg (String): 28 bytes hexadecimal message
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: MACH number
|
float: MACH number
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[23] == "0":
|
if d[23] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
mach = common.bin2int(d[24:33]) * 0.008
|
mach = bin2int(d[24:33]) * 0.008
|
||||||
return round(mach, 3)
|
return round(mach, 3)
|
||||||
|
|
||||||
|
|
||||||
@@ -127,17 +128,17 @@ def tas53(msg):
|
|||||||
"""Aircraft true airspeed, BDS 5,3 message
|
"""Aircraft true airspeed, BDS 5,3 message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits
|
msg (String): 28 bytes hexadecimal message
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: true airspeed in knots
|
float: true airspeed in knots
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[33] == "0":
|
if d[33] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
tas = common.bin2int(d[34:46]) * 0.5 # kts
|
tas = bin2int(d[34:46]) * 0.5 # kts
|
||||||
return round(tas, 1)
|
return round(tas, 1)
|
||||||
|
|
||||||
|
|
||||||
@@ -145,18 +146,18 @@ def vr53(msg):
|
|||||||
"""Vertical rate
|
"""Vertical rate
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits (BDS60) string
|
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: vertical rate in feet/minutes
|
int: vertical rate in feet/minutes
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[46] == "0":
|
if d[46] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sign = int(d[47]) # 1 -> negative value, two's complement
|
sign = int(d[47]) # 1 -> negative value, two's complement
|
||||||
value = common.bin2int(d[48:56])
|
value = bin2int(d[48:56])
|
||||||
|
|
||||||
if value == 0 or value == 255: # all zeros or all ones
|
if value == 0 or value == 255: # all zeros or all ones
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -3,40 +3,40 @@
|
|||||||
# Heading and speed report
|
# Heading and speed report
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
from pyModeS.extra import aero
|
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||||
|
|
||||||
|
|
||||||
def is60(msg):
|
def is60(msg):
|
||||||
"""Check if a message is likely to be BDS code 6,0
|
"""Check if a message is likely to be BDS code 6,0
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True or False
|
bool: True or False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if common.allzeros(msg):
|
if allzeros(msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
# status bit 1, 13, 24, 35, 46
|
# status bit 1, 13, 24, 35, 46
|
||||||
|
|
||||||
if common.wrongstatus(d, 1, 2, 12):
|
if wrongstatus(d, 1, 2, 12):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 13, 14, 23):
|
if wrongstatus(d, 13, 14, 23):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 24, 25, 34):
|
if wrongstatus(d, 24, 25, 34):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 35, 36, 45):
|
if wrongstatus(d, 35, 36, 45):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if common.wrongstatus(d, 46, 47, 56):
|
if wrongstatus(d, 46, 47, 56):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
ias = ias60(msg)
|
ias = ias60(msg)
|
||||||
@@ -55,14 +55,6 @@ def is60(msg):
|
|||||||
if vr_ins is not None and abs(vr_ins) > 6000:
|
if vr_ins is not None and abs(vr_ins) > 6000:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# additional check knowing altitude
|
|
||||||
if (mach is not None) and (ias is not None) and (common.df(msg) == 20):
|
|
||||||
alt = common.altcode(msg)
|
|
||||||
if alt is not None:
|
|
||||||
ias_ = aero.mach2cas(mach, alt * aero.ft) / aero.kts
|
|
||||||
if abs(ias - ias_) > 20:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -70,18 +62,18 @@ def hdg60(msg):
|
|||||||
"""Megnetic heading of aircraft
|
"""Megnetic heading of aircraft
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: heading in degrees to megnetic north (from 0 to 360)
|
float: heading in degrees to megnetic north (from 0 to 360)
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[0] == "0":
|
if d[0] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sign = int(d[1]) # 1 -> west
|
sign = int(d[1]) # 1 -> west
|
||||||
value = common.bin2int(d[2:12])
|
value = bin2int(d[2:12])
|
||||||
|
|
||||||
if sign:
|
if sign:
|
||||||
value = value - 1024
|
value = value - 1024
|
||||||
@@ -99,17 +91,17 @@ def ias60(msg):
|
|||||||
"""Indicated airspeed
|
"""Indicated airspeed
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: indicated airspeed in knots
|
int: indicated airspeed in knots
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[12] == "0":
|
if d[12] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ias = common.bin2int(d[13:23]) # kts
|
ias = bin2int(d[13:23]) # kts
|
||||||
return ias
|
return ias
|
||||||
|
|
||||||
|
|
||||||
@@ -117,17 +109,17 @@ def mach60(msg):
|
|||||||
"""Aircraft MACH number
|
"""Aircraft MACH number
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: MACH number
|
float: MACH number
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[23] == "0":
|
if d[23] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
mach = common.bin2int(d[24:34]) * 2.048 / 512.0
|
mach = bin2int(d[24:34]) * 2.048 / 512.0
|
||||||
return round(mach, 3)
|
return round(mach, 3)
|
||||||
|
|
||||||
|
|
||||||
@@ -135,18 +127,18 @@ def vr60baro(msg):
|
|||||||
"""Vertical rate from barometric measurement, this value may be very noisy.
|
"""Vertical rate from barometric measurement, this value may be very noisy.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: vertical rate in feet/minutes
|
int: vertical rate in feet/minutes
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[34] == "0":
|
if d[34] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sign = int(d[35]) # 1 -> negative value, two's complement
|
sign = int(d[35]) # 1 -> negative value, two's complement
|
||||||
value = common.bin2int(d[36:45])
|
value = bin2int(d[36:45])
|
||||||
|
|
||||||
if value == 0 or value == 511: # all zeros or all ones
|
if value == 0 or value == 511: # all zeros or all ones
|
||||||
return 0
|
return 0
|
||||||
@@ -158,21 +150,21 @@ def vr60baro(msg):
|
|||||||
|
|
||||||
|
|
||||||
def vr60ins(msg):
|
def vr60ins(msg):
|
||||||
"""Vertical rate measurd by onbard equiments (IRS, AHRS)
|
"""Vertical rate messured by onbard equiments (IRS, AHRS)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (str): 28 hexdigits string
|
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: vertical rate in feet/minutes
|
int: vertical rate in feet/minutes
|
||||||
"""
|
"""
|
||||||
d = common.hex2bin(common.data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if d[45] == "0":
|
if d[45] == "0":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sign = int(d[46]) # 1 -> negative value, two's complement
|
sign = int(d[46]) # 1 -> negative value, two's complement
|
||||||
value = common.bin2int(d[47:56])
|
value = bin2int(d[47:56])
|
||||||
|
|
||||||
if value == 0 or value == 511: # all zeros or all ones
|
if value == 0 or value == 511: # all zeros or all ones
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
# ------------------------------------------
|
|
||||||
# BDS 6,1
|
|
||||||
# ADS-B TC=28
|
|
||||||
# Aircraft Airborne status
|
|
||||||
# ------------------------------------------
|
|
||||||
|
|
||||||
from pyModeS import common
|
|
||||||
|
|
||||||
|
|
||||||
def is_emergency(msg: str) -> bool:
|
|
||||||
"""Check if the aircraft is reporting an emergency.
|
|
||||||
|
|
||||||
Non-emergencies are either a subtype of zero (no information) or
|
|
||||||
subtype of one and a value of zero (no emergency).
|
|
||||||
Subtype = 2 indicates an ACAS RA broadcast, look in BDS 3,0
|
|
||||||
|
|
||||||
:param msg: 28 bytes hexadecimal message string
|
|
||||||
:return: if the aircraft has declared an emergency
|
|
||||||
"""
|
|
||||||
if common.typecode(msg) != 28:
|
|
||||||
raise RuntimeError("%s: Not an airborne status message, expecting TC=28" % msg)
|
|
||||||
|
|
||||||
mb = common.hex2bin(msg)[32:]
|
|
||||||
subtype = common.bin2int(mb[5:8])
|
|
||||||
|
|
||||||
if subtype == 2:
|
|
||||||
raise RuntimeError("%s: Emergency message is ACAS-RA, not implemented")
|
|
||||||
|
|
||||||
emergency_state = common.bin2int(mb[8:11])
|
|
||||||
|
|
||||||
if subtype == 1 and emergency_state == 1:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def emergency_state(msg: str) -> int:
|
|
||||||
"""Decode aircraft emergency state.
|
|
||||||
|
|
||||||
Value Meaning
|
|
||||||
----- -----------------------
|
|
||||||
0 No emergency
|
|
||||||
1 General emergency
|
|
||||||
2 Lifeguard/Medical
|
|
||||||
3 Minimum fuel
|
|
||||||
4 No communications
|
|
||||||
5 Unlawful communications
|
|
||||||
6-7 Reserved
|
|
||||||
|
|
||||||
:param msg: 28 bytes hexadecimal message string
|
|
||||||
:return: emergency state
|
|
||||||
"""
|
|
||||||
|
|
||||||
mb = common.hex2bin(msg)[32:]
|
|
||||||
subtype = common.bin2int(mb[5:8])
|
|
||||||
|
|
||||||
if subtype == 2:
|
|
||||||
raise RuntimeError("%s: Emergency message is ACAS-RA, not implemented")
|
|
||||||
|
|
||||||
emergency_state = common.bin2int(mb[8:11])
|
|
||||||
return emergency_state
|
|
||||||
|
|
||||||
|
|
||||||
def emergency_squawk(msg: str) -> str:
|
|
||||||
"""Decode squawk code.
|
|
||||||
|
|
||||||
Emergency value 1: squawk 7700.
|
|
||||||
Emergency value 4: squawk 7600.
|
|
||||||
Emergency value 5: squawk 7500.
|
|
||||||
|
|
||||||
:param msg: 28 bytes hexadecimal message string
|
|
||||||
:return: aircraft squawk code
|
|
||||||
"""
|
|
||||||
if common.typecode(msg) != 28:
|
|
||||||
raise RuntimeError("%s: Not an airborne status message, expecting TC=28" % msg)
|
|
||||||
|
|
||||||
msgbin = common.hex2bin(msg)
|
|
||||||
|
|
||||||
# construct the 13 bits Mode A ID code
|
|
||||||
idcode = msgbin[43:49] + "0" + msgbin[49:55]
|
|
||||||
|
|
||||||
squawk = common.squawk(idcode)
|
|
||||||
return squawk
|
|
||||||
@@ -5,8 +5,7 @@ cdef unsigned char int_to_char(unsigned char i)
|
|||||||
|
|
||||||
cpdef str hex2bin(str hexstr)
|
cpdef str hex2bin(str hexstr)
|
||||||
cpdef long bin2int(str binstr)
|
cpdef long bin2int(str binstr)
|
||||||
cpdef long hex2int(str hexstr)
|
cpdef long hex2int(str binstr)
|
||||||
cpdef str bin2hex(str binstr)
|
|
||||||
|
|
||||||
cpdef unsigned char df(str msg)
|
cpdef unsigned char df(str msg)
|
||||||
cpdef long crc(str msg, bint encode=*)
|
cpdef long crc(str msg, bint encode=*)
|
||||||
@@ -17,12 +16,8 @@ cpdef bint is_icao_assigned(str icao)
|
|||||||
|
|
||||||
cpdef int typecode(str msg)
|
cpdef int typecode(str msg)
|
||||||
cpdef int cprNL(double lat)
|
cpdef int cprNL(double lat)
|
||||||
|
|
||||||
cpdef str idcode(str msg)
|
cpdef str idcode(str msg)
|
||||||
cpdef str squawk(str binstr)
|
|
||||||
|
|
||||||
cpdef int altcode(str msg)
|
cpdef int altcode(str msg)
|
||||||
cpdef int altitude(str binstr)
|
|
||||||
|
|
||||||
cpdef str data(str msg)
|
cdef str data(str msg)
|
||||||
cpdef bint allzeros(str msg)
|
cpdef bint allzeros(str msg)
|
||||||
@@ -5,7 +5,7 @@ from cpython cimport array
|
|||||||
from cpython.bytes cimport PyBytes_GET_SIZE
|
from cpython.bytes cimport PyBytes_GET_SIZE
|
||||||
from cpython.bytearray cimport PyByteArray_GET_SIZE
|
from cpython.bytearray cimport PyByteArray_GET_SIZE
|
||||||
|
|
||||||
from libc.math cimport abs, cos, acos, fabs, M_PI as pi, floor as c_floor
|
from libc.math cimport cos, acos, fabs, M_PI as pi, floor as c_floor
|
||||||
|
|
||||||
|
|
||||||
cdef int char_to_int(unsigned char binstr):
|
cdef int char_to_int(unsigned char binstr):
|
||||||
@@ -66,11 +66,6 @@ cpdef long hex2int(str hexstr):
|
|||||||
cumul = 16*cumul + char_to_int(v_hexstr[i])
|
cumul = 16*cumul + char_to_int(v_hexstr[i])
|
||||||
return cumul
|
return cumul
|
||||||
|
|
||||||
@cython.boundscheck(False)
|
|
||||||
cpdef str bin2hex(str binstr):
|
|
||||||
return "{0:X}".format(int(binstr, 2))
|
|
||||||
|
|
||||||
|
|
||||||
@cython.boundscheck(False)
|
@cython.boundscheck(False)
|
||||||
cpdef unsigned char df(str msg):
|
cpdef unsigned char df(str msg):
|
||||||
"""Decode Downlink Format vaule, bits 1 to 5."""
|
"""Decode Downlink Format vaule, bits 1 to 5."""
|
||||||
@@ -160,7 +155,17 @@ cpdef long floor(double x):
|
|||||||
return <long> c_floor(x)
|
return <long> c_floor(x)
|
||||||
|
|
||||||
cpdef str icao(str msg):
|
cpdef str icao(str msg):
|
||||||
"""Calculate the ICAO address from an Mode-S message."""
|
"""Calculate the ICAO address from an Mode-S message.
|
||||||
|
|
||||||
|
Applicable only with DF4, DF5, DF20, DF21 messages.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
String: ICAO address in 6 bytes hexadecimal string
|
||||||
|
|
||||||
|
"""
|
||||||
cdef unsigned char DF = df(msg)
|
cdef unsigned char DF = df(msg)
|
||||||
cdef long c0, c1
|
cdef long c0, c1
|
||||||
|
|
||||||
@@ -207,7 +212,14 @@ cpdef bint is_icao_assigned(str icao):
|
|||||||
@cython.boundscheck(False)
|
@cython.boundscheck(False)
|
||||||
@cython.wraparound(False)
|
@cython.wraparound(False)
|
||||||
cpdef int typecode(str msg):
|
cpdef int typecode(str msg):
|
||||||
"""Type code of ADS-B message"""
|
"""Type code of ADS-B message
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (string): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: type code number
|
||||||
|
"""
|
||||||
if df(msg) not in (17, 18):
|
if df(msg) not in (17, 18):
|
||||||
return -1
|
return -1
|
||||||
# return None
|
# return None
|
||||||
@@ -219,11 +231,13 @@ cpdef int typecode(str msg):
|
|||||||
cpdef int cprNL(double lat):
|
cpdef int cprNL(double lat):
|
||||||
"""NL() function in CPR decoding."""
|
"""NL() function in CPR decoding."""
|
||||||
|
|
||||||
if abs(lat) <= 1e-08:
|
if lat == 0:
|
||||||
return 59
|
return 59
|
||||||
elif abs(abs(lat) - 87) <= 1e-08 + 1e-05 * 87:
|
|
||||||
|
if lat == 87 or lat == -87:
|
||||||
return 2
|
return 2
|
||||||
elif lat > 87 or lat < -87:
|
|
||||||
|
if lat > 87 or lat < -87:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
cdef int nz = 15
|
cdef int nz = 15
|
||||||
@@ -236,41 +250,45 @@ cpdef int cprNL(double lat):
|
|||||||
@cython.boundscheck(False)
|
@cython.boundscheck(False)
|
||||||
@cython.wraparound(False)
|
@cython.wraparound(False)
|
||||||
cpdef str idcode(str msg):
|
cpdef str idcode(str msg):
|
||||||
"""Compute identity (squawk code)."""
|
"""Compute identity (squawk code).
|
||||||
|
|
||||||
|
Applicable only for DF5 or DF21 messages, bit 20-32.
|
||||||
|
credit: @fbyrkjeland
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: squawk code
|
||||||
|
|
||||||
|
"""
|
||||||
if df(msg) not in [5, 21]:
|
if df(msg) not in [5, 21]:
|
||||||
raise RuntimeError("Message must be Downlink Format 5 or 21.")
|
raise RuntimeError("Message must be Downlink Format 5 or 21.")
|
||||||
|
|
||||||
squawk_code = squawk(hex2bin(msg)[19:32])
|
cdef bytearray _mbin = bytearray(hex2bin(msg).encode())
|
||||||
return squawk_code
|
|
||||||
|
|
||||||
|
|
||||||
@cython.boundscheck(False)
|
|
||||||
@cython.wraparound(False)
|
|
||||||
cpdef str squawk(str binstr):
|
|
||||||
"""Compute identity (squawk code)."""
|
|
||||||
|
|
||||||
if len(binstr) != 13 or set(binstr) != set('01'):
|
|
||||||
raise RuntimeError("Input must be 13 bits binary string")
|
|
||||||
|
|
||||||
cdef bytearray _mbin = bytearray(binstr.encode())
|
|
||||||
cdef unsigned char[:] mbin = _mbin
|
cdef unsigned char[:] mbin = _mbin
|
||||||
|
|
||||||
cdef bytearray _idcode = bytearray(4)
|
cdef bytearray _idcode = bytearray(4)
|
||||||
cdef unsigned char[:] idcode = _idcode
|
cdef unsigned char[:] idcode = _idcode
|
||||||
|
|
||||||
cdef unsigned char C1 = mbin[0]
|
cdef unsigned char C1 = mbin[19]
|
||||||
cdef unsigned char A1 = mbin[1]
|
cdef unsigned char A1 = mbin[20]
|
||||||
cdef unsigned char C2 = mbin[2]
|
cdef unsigned char C2 = mbin[21]
|
||||||
cdef unsigned char A2 = mbin[3]
|
cdef unsigned char A2 = mbin[22]
|
||||||
cdef unsigned char C4 = mbin[4]
|
cdef unsigned char C4 = mbin[23]
|
||||||
cdef unsigned char A4 = mbin[5]
|
cdef unsigned char A4 = mbin[24]
|
||||||
# X = mbin[6]
|
# _ = mbin[25]
|
||||||
cdef unsigned char B1 = mbin[7]
|
cdef unsigned char B1 = mbin[26]
|
||||||
cdef unsigned char D1 = mbin[8]
|
cdef unsigned char D1 = mbin[27]
|
||||||
cdef unsigned char B2 = mbin[9]
|
cdef unsigned char B2 = mbin[28]
|
||||||
cdef unsigned char D2 = mbin[10]
|
cdef unsigned char D2 = mbin[29]
|
||||||
cdef unsigned char B4 = mbin[11]
|
cdef unsigned char B4 = mbin[30]
|
||||||
cdef unsigned char D4 = mbin[12]
|
cdef unsigned char D4 = mbin[31]
|
||||||
|
|
||||||
|
# byte1 = int(A4 + A2 + A1, 2)
|
||||||
|
# byte2 = int(B4 + B2 + B1, 2)
|
||||||
|
# byte3 = int(C4 + C2 + C1, 2)
|
||||||
|
# byte4 = int(D4 + D2 + D1, 2)
|
||||||
|
|
||||||
idcode[0] = int_to_char((char_to_int(A4)*2 + char_to_int(A2))*2 + char_to_int(A1))
|
idcode[0] = int_to_char((char_to_int(A4)*2 + char_to_int(A2))*2 + char_to_int(A1))
|
||||||
idcode[1] = int_to_char((char_to_int(B4)*2 + char_to_int(B2))*2 + char_to_int(B1))
|
idcode[1] = int_to_char((char_to_int(B4)*2 + char_to_int(B2))*2 + char_to_int(B1))
|
||||||
@@ -279,68 +297,68 @@ cpdef str squawk(str binstr):
|
|||||||
|
|
||||||
return _idcode.decode()
|
return _idcode.decode()
|
||||||
|
|
||||||
|
#return str(byte1) + str(byte2) + str(byte3) + str(byte4)
|
||||||
|
|
||||||
|
|
||||||
@cython.boundscheck(False)
|
@cython.boundscheck(False)
|
||||||
@cython.wraparound(False)
|
@cython.wraparound(False)
|
||||||
cpdef int altcode(str msg):
|
cpdef int altcode(str msg):
|
||||||
"""Compute the altitude."""
|
"""Compute the altitude.
|
||||||
|
|
||||||
|
Applicable only for DF4 or DF20 message, bit 20-32.
|
||||||
|
credit: @fbyrkjeland
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: altitude in ft
|
||||||
|
|
||||||
|
"""
|
||||||
if df(msg) not in [0, 4, 16, 20]:
|
if df(msg) not in [0, 4, 16, 20]:
|
||||||
raise RuntimeError("Message must be Downlink Format 0, 4, 16, or 20.")
|
raise RuntimeError("Message must be Downlink Format 0, 4, 16, or 20.")
|
||||||
|
|
||||||
alt = altitude(hex2bin(msg)[19:32])
|
# Altitude code, bit 20-32
|
||||||
return alt
|
cdef bytearray _mbin = bytearray(hex2bin(msg).encode())
|
||||||
|
|
||||||
|
|
||||||
@cython.boundscheck(False)
|
|
||||||
@cython.wraparound(False)
|
|
||||||
cpdef int altitude(str binstr):
|
|
||||||
|
|
||||||
if len(binstr) != 13 or not set(binstr).issubset(set("01")):
|
|
||||||
raise RuntimeError("Input must be 13 bits binary string")
|
|
||||||
|
|
||||||
cdef bytearray _mbin = bytearray(binstr.encode())
|
|
||||||
cdef unsigned char[:] mbin = _mbin
|
cdef unsigned char[:] mbin = _mbin
|
||||||
|
|
||||||
cdef char Mbit = binstr[6]
|
cdef char mbit = mbin[25] # M bit: 26
|
||||||
cdef char Qbit = binstr[8]
|
cdef char qbit = mbin[27] # Q bit: 28
|
||||||
|
|
||||||
cdef int alt = 0
|
cdef int alt = 0
|
||||||
cdef bytearray vbin
|
cdef bytearray vbin
|
||||||
cdef bytearray _graybytes = bytearray(11)
|
cdef bytearray _graybytes = bytearray(11)
|
||||||
cdef unsigned char[:] graybytes = _graybytes
|
cdef unsigned char[:] graybytes = _graybytes
|
||||||
|
|
||||||
if bin2int(binstr) == 0:
|
if mbit == 48: # unit in ft, "0" -> 48
|
||||||
# altitude unknown or invalid
|
if qbit == 49: # 25ft interval, "1" -> 49
|
||||||
alt = -9999
|
vbin = _mbin[19:25] + _mbin[26:27] + _mbin[28:32]
|
||||||
|
|
||||||
elif Mbit == 48: # unit in ft, "0" -> 48
|
|
||||||
if Qbit == 49: # 25ft interval, "1" -> 49
|
|
||||||
vbin = _mbin[:6] + _mbin[7:8] + _mbin[9:]
|
|
||||||
alt = bin2int(vbin.decode()) * 25 - 1000
|
alt = bin2int(vbin.decode()) * 25 - 1000
|
||||||
if Qbit == 48: # 100ft interval, above 50175ft, "0" -> 48
|
if qbit == 48: # 100ft interval, above 50175ft, "0" -> 48
|
||||||
graybytes[8] = mbin[0]
|
graybytes[8] = mbin[19]
|
||||||
graybytes[2] = mbin[1]
|
graybytes[2] = mbin[20]
|
||||||
graybytes[9] = mbin[2]
|
graybytes[9] = mbin[21]
|
||||||
graybytes[3] = mbin[3]
|
graybytes[3] = mbin[22]
|
||||||
graybytes[10] = mbin[4]
|
graybytes[10] = mbin[23]
|
||||||
graybytes[4] = mbin[5]
|
graybytes[4] = mbin[24]
|
||||||
# M = mbin[6]
|
# _ = mbin[25]
|
||||||
graybytes[5] = mbin[7]
|
graybytes[5] = mbin[26]
|
||||||
# Q = mbin[8]
|
# cdef char D1 = mbin[27] # always zero
|
||||||
graybytes[6] = mbin[9]
|
graybytes[6] = mbin[28]
|
||||||
graybytes[0] = mbin[10]
|
graybytes[0] = mbin[29]
|
||||||
graybytes[7] = mbin[11]
|
graybytes[7] = mbin[30]
|
||||||
graybytes[1] = mbin[12]
|
graybytes[1] = mbin[31]
|
||||||
|
# graybytes = D2 + D4 + A1 + A2 + A4 + B1 + B2 + B4 + C1 + C2 + C4
|
||||||
|
|
||||||
alt = gray2alt(_graybytes.decode())
|
alt = gray2alt(_graybytes.decode())
|
||||||
|
|
||||||
elif Mbit == 49: # unit in meter, "1" -> 49
|
if mbit == 49: # unit in meter, "1" -> 49
|
||||||
vbin = _mbin[:6] + _mbin[7:]
|
vbin = _mbin[19:25] + _mbin[26:31]
|
||||||
alt = int(bin2int(vbin.decode()) * 3.28084) # convert to ft
|
alt = int(bin2int(vbin.decode()) * 3.28084) # convert to ft
|
||||||
|
|
||||||
return alt
|
return alt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cpdef int gray2alt(str codestr):
|
cpdef int gray2alt(str codestr):
|
||||||
cdef str gc500 = codestr[:8]
|
cdef str gc500 = codestr[:8]
|
||||||
cdef int n500 = gray2int(gc500)
|
cdef int n500 = gray2int(gc500)
|
||||||
@@ -373,13 +391,21 @@ cdef int gray2int(str graystr):
|
|||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
cpdef str data(str msg):
|
cdef str data(str msg):
|
||||||
"""Return the data frame in the message, bytes 9 to 22."""
|
"""Return the data frame in the message, bytes 9 to 22."""
|
||||||
return msg[8:-6]
|
return msg[8:-6]
|
||||||
|
|
||||||
|
|
||||||
cpdef bint allzeros(str msg):
|
cpdef bint allzeros(str msg):
|
||||||
"""Check if the data bits are all zeros."""
|
"""Check if the data bits are all zeros.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True or False
|
||||||
|
|
||||||
|
"""
|
||||||
d = hex2bin(data(msg))
|
d = hex2bin(data(msg))
|
||||||
|
|
||||||
if bin2int(d) > 0:
|
if bin2int(d) > 0:
|
||||||
@@ -1,27 +1,26 @@
|
|||||||
"""Comm-B module.
|
"""Comm-B Wrapper.
|
||||||
|
|
||||||
The Comm-B module imports all functions from the following modules:
|
The Comm-B wrapper imports all functions from the following modules:
|
||||||
|
|
||||||
ELS - elementary surveillance
|
**ELS - elementary surveillance**
|
||||||
|
- pyModeS.decoder.bds.bds10
|
||||||
|
- pyModeS.decoder.bds.bds17
|
||||||
|
- pyModeS.decoder.bds.bds20
|
||||||
|
- pyModeS.decoder.bds.bds30
|
||||||
|
|
||||||
- pyModeS.decoder.bds.bds10
|
**EHS - enhanced surveillance**
|
||||||
- pyModeS.decoder.bds.bds17
|
- pyModeS.decoder.bds.bds40
|
||||||
- pyModeS.decoder.bds.bds20
|
- pyModeS.decoder.bds.bds50
|
||||||
- pyModeS.decoder.bds.bds30
|
- pyModeS.decoder.bds.bds60
|
||||||
|
|
||||||
EHS - enhanced surveillance
|
**MRAR and MHR**
|
||||||
|
- pyModeS.decoder.bds.bds44
|
||||||
- pyModeS.decoder.bds.bds40
|
- pyModeS.decoder.bds.bds45
|
||||||
- pyModeS.decoder.bds.bds50
|
|
||||||
- pyModeS.decoder.bds.bds60
|
|
||||||
|
|
||||||
MRAR and MHR
|
|
||||||
|
|
||||||
- pyModeS.decoder.bds.bds44
|
|
||||||
- pyModeS.decoder.bds.bds45
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
|
|
||||||
# ELS - elementary surveillance
|
# ELS - elementary surveillance
|
||||||
from pyModeS.decoder.bds.bds10 import *
|
from pyModeS.decoder.bds.bds10 import *
|
||||||
from pyModeS.decoder.bds.bds17 import *
|
from pyModeS.decoder.bds.bds17 import *
|
||||||
|
|||||||
@@ -1,46 +1,40 @@
|
|||||||
from typing import Optional
|
from __future__ import absolute_import, print_function, division
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from textwrap import wrap
|
from textwrap import wrap
|
||||||
|
|
||||||
|
|
||||||
def hex2bin(hexstr: str) -> str:
|
def hex2bin(hexstr):
|
||||||
"""Convert a hexdecimal string to binary string, with zero fillings."""
|
"""Convert a hexdecimal string to binary string, with zero fillings."""
|
||||||
num_of_bits = len(hexstr) * 4
|
num_of_bits = len(hexstr) * 4
|
||||||
binstr = bin(int(hexstr, 16))[2:].zfill(int(num_of_bits))
|
binstr = bin(int(hexstr, 16))[2:].zfill(int(num_of_bits))
|
||||||
return binstr
|
return binstr
|
||||||
|
|
||||||
|
|
||||||
def hex2int(hexstr: str) -> int:
|
def hex2int(hexstr):
|
||||||
"""Convert a hexdecimal string to integer."""
|
"""Convert a hexdecimal string to integer."""
|
||||||
return int(hexstr, 16)
|
return int(hexstr, 16)
|
||||||
|
|
||||||
|
|
||||||
def bin2int(binstr: str) -> int:
|
def bin2int(binstr):
|
||||||
"""Convert a binary string to integer."""
|
"""Convert a binary string to integer."""
|
||||||
return int(binstr, 2)
|
return int(binstr, 2)
|
||||||
|
|
||||||
|
|
||||||
def bin2hex(binstr: str) -> str:
|
def df(msg):
|
||||||
"""Convert a binary string to hexdecimal string."""
|
"""Decode Downlink Format vaule, bits 1 to 5."""
|
||||||
return "{0:X}".format(int(binstr, 2))
|
|
||||||
|
|
||||||
|
|
||||||
def df(msg: str) -> int:
|
|
||||||
"""Decode Downlink Format value, bits 1 to 5."""
|
|
||||||
dfbin = hex2bin(msg[:2])
|
dfbin = hex2bin(msg[:2])
|
||||||
return min(bin2int(dfbin[0:5]), 24)
|
return min(bin2int(dfbin[0:5]), 24)
|
||||||
|
|
||||||
|
|
||||||
def crc(msg: str, encode: bool = False) -> int:
|
def crc(msg, encode=False):
|
||||||
"""Mode-S Cyclic Redundancy Check.
|
"""Mode-S Cyclic Redundancy Check.
|
||||||
|
|
||||||
Detect if bit error occurs in the Mode-S message. When encode option is on,
|
Detect if bit error occurs in the Mode-S message. When encode option is on,
|
||||||
the checksum is generated.
|
the checksum is generated.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg: 28 bytes hexadecimal message string
|
msg (string): 28 bytes hexadecimal message string
|
||||||
encode: True to encode the date only and return the checksum
|
encode (bool): True to encode the date only and return the checksum
|
||||||
Returns:
|
Returns:
|
||||||
int: message checksum, or partity bits (encoder)
|
int: message checksum, or partity bits (encoder)
|
||||||
|
|
||||||
@@ -77,7 +71,7 @@ def crc(msg: str, encode: bool = False) -> int:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def crc_legacy(msg: str, encode: bool = False) -> int:
|
def crc_legacy(msg, encode=False):
|
||||||
"""Mode-S Cyclic Redundancy Check. (Legacy code, 2x slow)."""
|
"""Mode-S Cyclic Redundancy Check. (Legacy code, 2x slow)."""
|
||||||
# the polynominal generattor code for CRC [1111111111111010000001001]
|
# the polynominal generattor code for CRC [1111111111111010000001001]
|
||||||
generator = np.array(
|
generator = np.array(
|
||||||
@@ -105,7 +99,7 @@ def crc_legacy(msg: str, encode: bool = False) -> int:
|
|||||||
return reminder
|
return reminder
|
||||||
|
|
||||||
|
|
||||||
def floor(x: float) -> int:
|
def floor(x):
|
||||||
"""Mode-S floor function.
|
"""Mode-S floor function.
|
||||||
|
|
||||||
Defined as the greatest integer value k, such that k <= x
|
Defined as the greatest integer value k, such that k <= x
|
||||||
@@ -115,7 +109,7 @@ def floor(x: float) -> int:
|
|||||||
return int(np.floor(x))
|
return int(np.floor(x))
|
||||||
|
|
||||||
|
|
||||||
def icao(msg: str) -> Optional[str]:
|
def icao(msg):
|
||||||
"""Calculate the ICAO address from an Mode-S message.
|
"""Calculate the ICAO address from an Mode-S message.
|
||||||
|
|
||||||
Applicable only with DF4, DF5, DF20, DF21 messages.
|
Applicable only with DF4, DF5, DF20, DF21 messages.
|
||||||
@@ -127,7 +121,6 @@ def icao(msg: str) -> Optional[str]:
|
|||||||
String: ICAO address in 6 bytes hexadecimal string
|
String: ICAO address in 6 bytes hexadecimal string
|
||||||
|
|
||||||
"""
|
"""
|
||||||
addr: Optional[str]
|
|
||||||
DF = df(msg)
|
DF = df(msg)
|
||||||
|
|
||||||
if DF in (11, 17, 18):
|
if DF in (11, 17, 18):
|
||||||
@@ -142,7 +135,7 @@ def icao(msg: str) -> Optional[str]:
|
|||||||
return addr
|
return addr
|
||||||
|
|
||||||
|
|
||||||
def is_icao_assigned(icao: str) -> bool:
|
def is_icao_assigned(icao):
|
||||||
"""Check whether the ICAO address is assigned (Annex 10, Vol 3)."""
|
"""Check whether the ICAO address is assigned (Annex 10, Vol 3)."""
|
||||||
if (icao is None) or (not isinstance(icao, str)) or (len(icao) != 6):
|
if (icao is None) or (not isinstance(icao, str)) or (len(icao) != 6):
|
||||||
return False
|
return False
|
||||||
@@ -171,7 +164,7 @@ def is_icao_assigned(icao: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def typecode(msg: str) -> Optional[int]:
|
def typecode(msg):
|
||||||
"""Type code of ADS-B message
|
"""Type code of ADS-B message
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -187,14 +180,16 @@ def typecode(msg: str) -> Optional[int]:
|
|||||||
return bin2int(tcbin[0:5])
|
return bin2int(tcbin[0:5])
|
||||||
|
|
||||||
|
|
||||||
def cprNL(lat: float) -> int:
|
def cprNL(lat):
|
||||||
"""NL() function in CPR decoding."""
|
"""NL() function in CPR decoding."""
|
||||||
|
|
||||||
if np.isclose(lat, 0):
|
if lat == 0:
|
||||||
return 59
|
return 59
|
||||||
elif np.isclose(abs(lat), 87):
|
|
||||||
|
if lat == 87 or lat == -87:
|
||||||
return 2
|
return 2
|
||||||
elif lat > 87 or lat < -87:
|
|
||||||
|
if lat > 87 or lat < -87:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
nz = 15
|
nz = 15
|
||||||
@@ -205,8 +200,11 @@ def cprNL(lat: float) -> int:
|
|||||||
return NL
|
return NL
|
||||||
|
|
||||||
|
|
||||||
def idcode(msg: str) -> str:
|
def idcode(msg):
|
||||||
"""Compute identity code (squawk) encoded in DF5 or DF21 message.
|
"""Compute identity (squawk code).
|
||||||
|
|
||||||
|
Applicable only for DF5 or DF21 messages, bit 20-32.
|
||||||
|
credit: @fbyrkjeland
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (String): 28 bytes hexadecimal message string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
@@ -219,37 +217,20 @@ def idcode(msg: str) -> str:
|
|||||||
raise RuntimeError("Message must be Downlink Format 5 or 21.")
|
raise RuntimeError("Message must be Downlink Format 5 or 21.")
|
||||||
|
|
||||||
mbin = hex2bin(msg)
|
mbin = hex2bin(msg)
|
||||||
idcodebin = mbin[19:32]
|
|
||||||
|
|
||||||
return squawk(idcodebin)
|
C1 = mbin[19]
|
||||||
|
A1 = mbin[20]
|
||||||
|
C2 = mbin[21]
|
||||||
def squawk(binstr: str) -> str:
|
A2 = mbin[22]
|
||||||
"""Decode 13 bits identity (squawk) code.
|
C4 = mbin[23]
|
||||||
|
A4 = mbin[24]
|
||||||
Args:
|
# _ = mbin[25]
|
||||||
binstr (String): 13 bits binary string
|
B1 = mbin[26]
|
||||||
|
D1 = mbin[27]
|
||||||
Returns:
|
B2 = mbin[28]
|
||||||
int: altitude in ft
|
D2 = mbin[29]
|
||||||
|
B4 = mbin[30]
|
||||||
"""
|
D4 = mbin[31]
|
||||||
if len(binstr) != 13 or not set(binstr).issubset(set("01")):
|
|
||||||
raise RuntimeError("Input must be 13 bits binary string")
|
|
||||||
|
|
||||||
C1 = binstr[0]
|
|
||||||
A1 = binstr[1]
|
|
||||||
C2 = binstr[2]
|
|
||||||
A2 = binstr[3]
|
|
||||||
C4 = binstr[4]
|
|
||||||
A4 = binstr[5]
|
|
||||||
# X = binstr[6]
|
|
||||||
B1 = binstr[7]
|
|
||||||
D1 = binstr[8]
|
|
||||||
B2 = binstr[9]
|
|
||||||
D2 = binstr[10]
|
|
||||||
B4 = binstr[11]
|
|
||||||
D4 = binstr[12]
|
|
||||||
|
|
||||||
byte1 = int(A4 + A2 + A1, 2)
|
byte1 = int(A4 + A2 + A1, 2)
|
||||||
byte2 = int(B4 + B2 + B1, 2)
|
byte2 = int(B4 + B2 + B1, 2)
|
||||||
@@ -259,8 +240,11 @@ def squawk(binstr: str) -> str:
|
|||||||
return str(byte1) + str(byte2) + str(byte3) + str(byte4)
|
return str(byte1) + str(byte2) + str(byte3) + str(byte4)
|
||||||
|
|
||||||
|
|
||||||
def altcode(msg: str) -> Optional[int]:
|
def altcode(msg):
|
||||||
"""Compute altitude encoded in DF4 or DF20 message.
|
"""Compute the altitude.
|
||||||
|
|
||||||
|
Applicable only for DF4 or DF20 message, bit 20-32.
|
||||||
|
credit: @fbyrkjeland
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (String): 28 bytes hexadecimal message string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
@@ -269,78 +253,50 @@ def altcode(msg: str) -> Optional[int]:
|
|||||||
int: altitude in ft
|
int: altitude in ft
|
||||||
|
|
||||||
"""
|
"""
|
||||||
alt: Optional[int]
|
|
||||||
|
|
||||||
if df(msg) not in [0, 4, 16, 20]:
|
if df(msg) not in [0, 4, 16, 20]:
|
||||||
raise RuntimeError("Message must be Downlink Format 0, 4, 16, or 20.")
|
raise RuntimeError("Message must be Downlink Format 0, 4, 16, or 20.")
|
||||||
|
|
||||||
# Altitude code, bit 20-32
|
# Altitude code, bit 20-32
|
||||||
mbin = hex2bin(msg)
|
mbin = hex2bin(msg)
|
||||||
|
|
||||||
altitude_code = mbin[19:32]
|
mbit = mbin[25] # M bit: 26
|
||||||
|
qbit = mbin[27] # Q bit: 28
|
||||||
|
|
||||||
alt = altitude(altitude_code)
|
if mbit == "0": # unit in ft
|
||||||
|
if qbit == "1": # 25ft interval
|
||||||
return alt
|
vbin = mbin[19:25] + mbin[26] + mbin[28:32]
|
||||||
|
|
||||||
|
|
||||||
def altitude(binstr: str) -> Optional[int]:
|
|
||||||
"""Decode 13 bits altitude code.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
binstr (String): 13 bits binary string
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: altitude in ft
|
|
||||||
|
|
||||||
"""
|
|
||||||
alt: Optional[int]
|
|
||||||
|
|
||||||
if len(binstr) != 13 or not set(binstr).issubset(set("01")):
|
|
||||||
raise RuntimeError("Input must be 13 bits binary string")
|
|
||||||
|
|
||||||
Mbit = binstr[6]
|
|
||||||
Qbit = binstr[8]
|
|
||||||
|
|
||||||
if bin2int(binstr) == 0:
|
|
||||||
# altitude unknown or invalid
|
|
||||||
alt = None
|
|
||||||
|
|
||||||
elif Mbit == "0": # unit in ft
|
|
||||||
if Qbit == "1": # 25ft interval
|
|
||||||
vbin = binstr[:6] + binstr[7] + binstr[9:]
|
|
||||||
alt = bin2int(vbin) * 25 - 1000
|
alt = bin2int(vbin) * 25 - 1000
|
||||||
if Qbit == "0": # 100ft interval, above 50187.5ft
|
if qbit == "0": # 100ft interval, above 50175ft
|
||||||
C1 = binstr[0]
|
C1 = mbin[19]
|
||||||
A1 = binstr[1]
|
A1 = mbin[20]
|
||||||
C2 = binstr[2]
|
C2 = mbin[21]
|
||||||
A2 = binstr[3]
|
A2 = mbin[22]
|
||||||
C4 = binstr[4]
|
C4 = mbin[23]
|
||||||
A4 = binstr[5]
|
A4 = mbin[24]
|
||||||
# M = binstr[6]
|
# _ = mbin[25]
|
||||||
B1 = binstr[7]
|
B1 = mbin[26]
|
||||||
# Q = binstr[8]
|
# D1 = mbin[27] # always zero
|
||||||
B2 = binstr[9]
|
B2 = mbin[28]
|
||||||
D2 = binstr[10]
|
D2 = mbin[29]
|
||||||
B4 = binstr[11]
|
B4 = mbin[30]
|
||||||
D4 = binstr[12]
|
D4 = mbin[31]
|
||||||
|
|
||||||
graystr = D2 + D4 + A1 + A2 + A4 + B1 + B2 + B4 + C1 + C2 + C4
|
graystr = D2 + D4 + A1 + A2 + A4 + B1 + B2 + B4 + C1 + C2 + C4
|
||||||
alt = gray2alt(graystr)
|
alt = gray2alt(graystr)
|
||||||
|
|
||||||
if Mbit == "1": # unit in meter
|
if mbit == "1": # unit in meter
|
||||||
vbin = binstr[:6] + binstr[7:]
|
vbin = mbin[19:25] + mbin[26:31]
|
||||||
alt = int(bin2int(vbin) * 3.28084) # convert to ft
|
alt = int(bin2int(vbin) * 3.28084) # convert to ft
|
||||||
|
|
||||||
return alt
|
return alt
|
||||||
|
|
||||||
|
|
||||||
def gray2alt(binstr: str) -> Optional[int]:
|
def gray2alt(codestr):
|
||||||
gc500 = binstr[:8]
|
gc500 = codestr[:8]
|
||||||
n500 = gray2int(gc500)
|
n500 = gray2int(gc500)
|
||||||
|
|
||||||
# in 100-ft step must be converted first
|
# in 100-ft step must be converted first
|
||||||
gc100 = binstr[8:]
|
gc100 = codestr[8:]
|
||||||
n100 = gray2int(gc100)
|
n100 = gray2int(gc100)
|
||||||
|
|
||||||
if n100 in [0, 5, 6]:
|
if n100 in [0, 5, 6]:
|
||||||
@@ -356,9 +312,9 @@ def gray2alt(binstr: str) -> Optional[int]:
|
|||||||
return alt
|
return alt
|
||||||
|
|
||||||
|
|
||||||
def gray2int(binstr: str) -> int:
|
def gray2int(graystr):
|
||||||
"""Convert greycode to binary."""
|
"""Convert greycode to binary."""
|
||||||
num = bin2int(binstr)
|
num = bin2int(graystr)
|
||||||
num ^= num >> 8
|
num ^= num >> 8
|
||||||
num ^= num >> 4
|
num ^= num >> 4
|
||||||
num ^= num >> 2
|
num ^= num >> 2
|
||||||
@@ -366,12 +322,12 @@ def gray2int(binstr: str) -> int:
|
|||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def data(msg: str) -> str:
|
def data(msg):
|
||||||
"""Return the data frame in the message, bytes 9 to 22."""
|
"""Return the data frame in the message, bytes 9 to 22."""
|
||||||
return msg[8:-6]
|
return msg[8:-6]
|
||||||
|
|
||||||
|
|
||||||
def allzeros(msg: str) -> bool:
|
def allzeros(msg):
|
||||||
"""Check if the data bits are all zeros.
|
"""Check if the data bits are all zeros.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -389,7 +345,7 @@ def allzeros(msg: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def wrongstatus(data: str, sb: int, msb: int, lsb: int) -> bool:
|
def wrongstatus(data, sb, msb, lsb):
|
||||||
"""Check if the status bit and field bits are consistency.
|
"""Check if the status bit and field bits are consistency.
|
||||||
|
|
||||||
This Function is used for checking BDS code versions.
|
This Function is used for checking BDS code versions.
|
||||||
@@ -9,6 +9,7 @@ The EHS wrapper imports all functions from the following modules:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyModeS.decoder.bds.bds40 import *
|
from pyModeS.decoder.bds.bds40 import *
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ The ELS wrapper imports all functions from the following modules:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
|
|
||||||
from pyModeS.decoder.bds.bds10 import *
|
from pyModeS.decoder.bds.bds10 import *
|
||||||
from pyModeS.decoder.bds.bds17 import *
|
from pyModeS.decoder.bds.bds17 import *
|
||||||
from pyModeS.decoder.bds.bds20 import *
|
from pyModeS.decoder.bds.bds20 import *
|
||||||
|
|||||||
@@ -1,132 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
Decode short roll call surveillance replies, with downlink format 4 or 5
|
Warpper for short roll call surveillance replies DF=4/5
|
||||||
|
|
||||||
|
[To be implemented]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyModeS import common
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from pyModeS.decoder import common
|
||||||
|
|
||||||
def _checkdf(func):
|
|
||||||
"""Ensure downlink format is 4 or 5."""
|
|
||||||
|
|
||||||
def wrapper(msg):
|
|
||||||
df = common.df(msg)
|
|
||||||
if df not in [4, 5]:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Incorrect downlink format, expect 4 or 5, got {}".format(df)
|
|
||||||
)
|
|
||||||
return func(msg)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
@_checkdf
|
|
||||||
def fs(msg):
|
|
||||||
"""Decode flight status.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (str): 14 hexdigits string
|
|
||||||
Returns:
|
|
||||||
int, str: flight status, description
|
|
||||||
|
|
||||||
"""
|
|
||||||
msgbin = common.hex2bin(msg)
|
|
||||||
fs = common.bin2int(msgbin[5:8])
|
|
||||||
text = None
|
|
||||||
|
|
||||||
if fs == 0:
|
|
||||||
text = "no alert, no SPI, aircraft is airborne"
|
|
||||||
elif fs == 1:
|
|
||||||
text = "no alert, no SPI, aircraft is on-ground"
|
|
||||||
elif fs == 2:
|
|
||||||
text = "alert, no SPI, aircraft is airborne"
|
|
||||||
elif fs == 3:
|
|
||||||
text = "alert, no SPI, aircraft is on-ground"
|
|
||||||
elif fs == 4:
|
|
||||||
text = "alert, SPI, aircraft is airborne or on-ground"
|
|
||||||
elif fs == 5:
|
|
||||||
text = "no alert, SPI, aircraft is airborne or on-ground"
|
|
||||||
|
|
||||||
return fs, text
|
|
||||||
|
|
||||||
|
|
||||||
@_checkdf
|
|
||||||
def dr(msg):
|
|
||||||
"""Decode downlink request.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (str): 14 hexdigits string
|
|
||||||
Returns:
|
|
||||||
int, str: downlink request, description
|
|
||||||
|
|
||||||
"""
|
|
||||||
msgbin = common.hex2bin(msg)
|
|
||||||
dr = common.bin2int(msgbin[8:13])
|
|
||||||
|
|
||||||
text = None
|
|
||||||
|
|
||||||
if dr == 0:
|
|
||||||
text = "no downlink request"
|
|
||||||
elif dr == 1:
|
|
||||||
text = "request to send Comm-B message"
|
|
||||||
elif dr == 4:
|
|
||||||
text = "Comm-B broadcast 1 available"
|
|
||||||
elif dr == 5:
|
|
||||||
text = "Comm-B broadcast 2 available"
|
|
||||||
elif dr >= 16:
|
|
||||||
text = "ELM downlink segments available: {}".format(dr - 15)
|
|
||||||
|
|
||||||
return dr, text
|
|
||||||
|
|
||||||
|
|
||||||
@_checkdf
|
|
||||||
def um(msg):
|
|
||||||
"""Decode utility message.
|
|
||||||
|
|
||||||
Utility message contains interrogator identifier and reservation type.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (str): 14 hexdigits string
|
|
||||||
Returns:
|
|
||||||
int, str: interrogator identifier code that triggered the reply, and
|
|
||||||
reservation type made by the interrogator
|
|
||||||
"""
|
|
||||||
msgbin = common.hex2bin(msg)
|
|
||||||
iis = common.bin2int(msgbin[13:17])
|
|
||||||
ids = common.bin2int(msgbin[17:19])
|
|
||||||
if ids == 0:
|
|
||||||
ids_text = None
|
|
||||||
if ids == 1:
|
|
||||||
ids_text = "Comm-B interrogator identifier code"
|
|
||||||
if ids == 2:
|
|
||||||
ids_text = "Comm-C interrogator identifier code"
|
|
||||||
if ids == 3:
|
|
||||||
ids_text = "Comm-D interrogator identifier code"
|
|
||||||
return iis, ids, ids_text
|
|
||||||
|
|
||||||
|
|
||||||
@_checkdf
|
|
||||||
def altitude(msg):
|
|
||||||
"""Decode altitude.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (String): 14 hexdigits string
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: altitude in ft
|
|
||||||
|
|
||||||
"""
|
|
||||||
return common.altcode(msg)
|
|
||||||
|
|
||||||
|
|
||||||
@_checkdf
|
|
||||||
def identity(msg):
|
|
||||||
"""Decode squawk code.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (String): 14 hexdigits string
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
string: squawk code
|
|
||||||
|
|
||||||
"""
|
|
||||||
return common.idcode(msg)
|
|
||||||
|
|||||||
@@ -1,221 +0,0 @@
|
|||||||
from pyModeS import common
|
|
||||||
from textwrap import wrap
|
|
||||||
|
|
||||||
|
|
||||||
def uplink_icao(msg):
|
|
||||||
"""Calculate the ICAO address from a Mode-S interrogation (uplink message)"""
|
|
||||||
p_gen = 0xFFFA0480 << ((len(msg) - 14) * 4)
|
|
||||||
data = int(msg[:-6], 16)
|
|
||||||
PA = int(msg[-6:], 16)
|
|
||||||
ad = 0
|
|
||||||
topbit = 0b1 << (len(msg) * 4 - 25)
|
|
||||||
for j in range(0, len(msg) * 4, 1):
|
|
||||||
if data & topbit:
|
|
||||||
data ^= p_gen
|
|
||||||
data = (data << 1) + ((PA >> 23) & 1)
|
|
||||||
PA = PA << 1
|
|
||||||
if j > (len(msg) * 4 - 26):
|
|
||||||
ad = ad + ((data >> (len(msg) * 4 - 25)) & 1)
|
|
||||||
ad = ad << 1
|
|
||||||
return "%06X" % (ad >> 2)
|
|
||||||
|
|
||||||
|
|
||||||
def uf(msg):
|
|
||||||
"""Decode Uplink Format value, bits 1 to 5."""
|
|
||||||
ufbin = common.hex2bin(msg[:2])
|
|
||||||
return min(common.bin2int(ufbin[0:5]), 24)
|
|
||||||
|
|
||||||
|
|
||||||
def bds(msg):
|
|
||||||
"""Decode requested BDS register from selective (Roll Call) interrogation."""
|
|
||||||
UF = uf(msg)
|
|
||||||
msgbin = common.hex2bin(msg)
|
|
||||||
msgbin_split = wrap(msgbin, 8)
|
|
||||||
mbytes = list(map(common.bin2int, msgbin_split))
|
|
||||||
|
|
||||||
if uf(msg) in {4, 5, 20, 21}:
|
|
||||||
|
|
||||||
di = mbytes[1] & 0x7 # DI - Designator Identification
|
|
||||||
RR = mbytes[1] >> 3 & 0x1F
|
|
||||||
if RR > 15:
|
|
||||||
BDS1 = RR - 16
|
|
||||||
if di == 7:
|
|
||||||
RRS = mbytes[2] & 0x0F
|
|
||||||
BDS2 = RRS
|
|
||||||
elif di == 3:
|
|
||||||
RRS = ((mbytes[2] & 0x1) << 4) | ((mbytes[3] & 0xE0) >> 5)
|
|
||||||
BDS2 = RRS
|
|
||||||
else:
|
|
||||||
BDS2 = 0 # for other values of DI, the BDS2 is assumed 0 (as per ICAO Annex 10 Vol IV)
|
|
||||||
|
|
||||||
return str(BDS1) + str(BDS2)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def pr(msg):
|
|
||||||
"""Decode PR (probability of reply) field from All Call interrogation.
|
|
||||||
Interpretation:
|
|
||||||
0 signifies reply with probability of 1
|
|
||||||
1 signifies reply with probability of 1/2
|
|
||||||
2 signifies reply with probability of 1/4
|
|
||||||
3 signifies reply with probability of 1/8
|
|
||||||
4 signifies reply with probability of 1/16
|
|
||||||
5, 6, 7 not assigned
|
|
||||||
8 signifies disregard lockout, reply with probability of 1
|
|
||||||
9 signifies disregard lockout, reply with probability of 1/2
|
|
||||||
10 signifies disregard lockout, reply with probability of 1/4
|
|
||||||
11 signifies disregard lockout, reply with probability of 1/8
|
|
||||||
12 signifies disregard lockout, reply with probability of 1/16
|
|
||||||
13, 14, 15 not assigned.
|
|
||||||
"""
|
|
||||||
msgbin = common.hex2bin(msg)
|
|
||||||
msgbin_split = wrap(msgbin, 8)
|
|
||||||
mbytes = list(map(common.bin2int, msgbin_split))
|
|
||||||
if uf(msg) == 11:
|
|
||||||
return ((mbytes[0] & 0x7) << 1) | ((mbytes[1] & 0x80) >> 7)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def ic(msg):
|
|
||||||
"""Decode IC (interrogator code) from a ground-based interrogation."""
|
|
||||||
|
|
||||||
UF = uf(msg)
|
|
||||||
msgbin = common.hex2bin(msg)
|
|
||||||
msgbin_split = wrap(msgbin, 8)
|
|
||||||
mbytes = list(map(common.bin2int, msgbin_split))
|
|
||||||
IC = None
|
|
||||||
BDS2 = ""
|
|
||||||
if uf(msg) == 11:
|
|
||||||
|
|
||||||
codeLabel = mbytes[1] & 0x7
|
|
||||||
icField = (mbytes[1] >> 3) & 0xF
|
|
||||||
|
|
||||||
# Store the Interogator Code
|
|
||||||
ic_switcher = {
|
|
||||||
0: "II" + str(icField),
|
|
||||||
1: "SI" + str(icField),
|
|
||||||
2: "SI" + str(icField + 16),
|
|
||||||
3: "SI" + str(icField + 32),
|
|
||||||
4: "SI" + str(icField + 48),
|
|
||||||
}
|
|
||||||
IC = ic_switcher.get(codeLabel, "")
|
|
||||||
|
|
||||||
if uf(msg) in {4, 5, 20, 21}:
|
|
||||||
di = mbytes[1] & 0x7
|
|
||||||
RR = mbytes[1] >> 3 & 0x1F
|
|
||||||
if RR > 15:
|
|
||||||
BDS1 = RR - 16
|
|
||||||
if di == 0 or di == 1 or di == 7:
|
|
||||||
# II
|
|
||||||
II = (mbytes[2] >> 4) & 0xF
|
|
||||||
IC = "II" + str(II)
|
|
||||||
elif di == 3:
|
|
||||||
# SI
|
|
||||||
SI = (mbytes[2] >> 2) & 0x3F
|
|
||||||
IC = "SI" + str(SI)
|
|
||||||
return IC
|
|
||||||
|
|
||||||
|
|
||||||
def lockout(msg):
|
|
||||||
"""Decode the lockout command from selective (Roll Call) interrogation."""
|
|
||||||
msgbin = common.hex2bin(msg)
|
|
||||||
msgbin_split = wrap(msgbin, 8)
|
|
||||||
mbytes = list(map(common.bin2int, msgbin_split))
|
|
||||||
|
|
||||||
if uf(msg) in {4, 5, 20, 21}:
|
|
||||||
lockout = False
|
|
||||||
di = mbytes[1] & 0x7
|
|
||||||
if di == 7:
|
|
||||||
# LOS
|
|
||||||
if ((mbytes[3] & 0x40) >> 6) == 1:
|
|
||||||
lockout = True
|
|
||||||
elif di == 3:
|
|
||||||
# LSS
|
|
||||||
if ((mbytes[2] & 0x2) >> 1) == 1:
|
|
||||||
lockout = True
|
|
||||||
return lockout
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def uplink_fields(msg):
|
|
||||||
"""Decode individual fields of a ground-based interrogation."""
|
|
||||||
msgbin = common.hex2bin(msg)
|
|
||||||
msgbin_split = wrap(msgbin, 8)
|
|
||||||
mbytes = list(map(common.bin2int, msgbin_split))
|
|
||||||
PR = ""
|
|
||||||
LOS = ""
|
|
||||||
IC = ""
|
|
||||||
lockout = False
|
|
||||||
di = ""
|
|
||||||
RR = ""
|
|
||||||
RRS = ""
|
|
||||||
BDS1 = ""
|
|
||||||
BDS2 = ""
|
|
||||||
if uf(msg) == 11:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Probability of Reply decoding
|
|
||||||
|
|
||||||
PR = ((mbytes[0] & 0x7) << 1) | ((mbytes[1] & 0x80) >> 7)
|
|
||||||
|
|
||||||
# Get cl and ic bit fields from the data
|
|
||||||
# Decode the SI or II interrogator code
|
|
||||||
codeLabel = mbytes[1] & 0x7
|
|
||||||
icField = (mbytes[1] >> 3) & 0xF
|
|
||||||
|
|
||||||
# Store the Interogator Code
|
|
||||||
ic_switcher = {
|
|
||||||
0: "II" + str(icField),
|
|
||||||
1: "SI" + str(icField),
|
|
||||||
2: "SI" + str(icField + 16),
|
|
||||||
3: "SI" + str(icField + 32),
|
|
||||||
4: "SI" + str(icField + 48),
|
|
||||||
}
|
|
||||||
IC = ic_switcher.get(codeLabel, "")
|
|
||||||
|
|
||||||
if uf(msg) in {4, 5, 20, 21}:
|
|
||||||
# Decode the DI and get the lockout information conveniently (LSS or LOS)
|
|
||||||
|
|
||||||
# DI - Designator Identification
|
|
||||||
di = mbytes[1] & 0x7
|
|
||||||
RR = mbytes[1] >> 3 & 0x1F
|
|
||||||
if RR > 15:
|
|
||||||
BDS1 = RR - 16
|
|
||||||
BDS2 = 0
|
|
||||||
if di == 0 or di == 1:
|
|
||||||
# II
|
|
||||||
II = (mbytes[2] >> 4) & 0xF
|
|
||||||
IC = "II" + str(II)
|
|
||||||
elif di == 7:
|
|
||||||
# LOS
|
|
||||||
if ((mbytes[3] & 0x40) >> 6) == 1:
|
|
||||||
lockout = True
|
|
||||||
# II
|
|
||||||
II = (mbytes[2] >> 4) & 0xF
|
|
||||||
IC = "II" + str(II)
|
|
||||||
RRS = mbytes[2] & 0x0F
|
|
||||||
BDS2 = RRS
|
|
||||||
elif di == 3:
|
|
||||||
# LSS
|
|
||||||
if ((mbytes[2] & 0x2) >> 1) == 1:
|
|
||||||
lockout = True
|
|
||||||
# SI
|
|
||||||
SI = (mbytes[2] >> 2) & 0x3F
|
|
||||||
IC = "SI" + str(SI)
|
|
||||||
RRS = ((mbytes[2] & 0x1) << 4) | ((mbytes[3] & 0xE0) >> 5)
|
|
||||||
BDS2 = RRS
|
|
||||||
return {
|
|
||||||
"DI": di,
|
|
||||||
"IC": IC,
|
|
||||||
"LOS": lockout,
|
|
||||||
"PR": PR,
|
|
||||||
"RR": RR,
|
|
||||||
"RRS": RRS,
|
|
||||||
"BDS": str(BDS1) + str(BDS2),
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ International Standard Atmosphere
|
|||||||
|
|
||||||
p,rho,T = atmos(H) # atmos as function of geopotential altitude H [m]
|
p,rho,T = atmos(H) # atmos as function of geopotential altitude H [m]
|
||||||
a = vsound(H) # speed of sound [m/s] as function of H[m]
|
a = vsound(H) # speed of sound [m/s] as function of H[m]
|
||||||
p = pressure(H) # calls atmos but returns only pressure [Pa]
|
p = pressure(H) # calls atmos but retruns only pressure [Pa]
|
||||||
T = temperature(H) # calculates temperature [K]
|
T = temperature(H) # calculates temperature [K]
|
||||||
rho = density(H) # calls atmos but returns only pressure [Pa]
|
rho = density(H) # calls atmos but retruns only pressure [Pa]
|
||||||
|
|
||||||
Speed conversion at altitude H[m] in ISA
|
Speed conversion at altitude H[m] in ISA
|
||||||
::
|
::
|
||||||
|
|||||||
@@ -1,120 +1,90 @@
|
|||||||
import time
|
|
||||||
import traceback
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyModeS as pms
|
import pyModeS as pms
|
||||||
|
from rtlsdr import RtlSdr
|
||||||
|
import time
|
||||||
|
|
||||||
try:
|
modes_sample_rate = 2e6
|
||||||
import rtlsdr
|
|
||||||
except:
|
|
||||||
print("------------------------------------------------------------------------")
|
|
||||||
print("! Warining: pyrtlsdr not installed (required for using RTL-SDR devices) ")
|
|
||||||
print("------------------------------------------------------------------------")
|
|
||||||
|
|
||||||
sampling_rate = 2e6
|
|
||||||
smaples_per_microsec = 2
|
|
||||||
|
|
||||||
modes_frequency = 1090e6
|
modes_frequency = 1090e6
|
||||||
buffer_size = 1024 * 200
|
buffer_size = 1024 * 100
|
||||||
read_size = 1024 * 100
|
read_size = 1024 * 20
|
||||||
|
|
||||||
pbits = 8
|
pbits = 8
|
||||||
fbits = 112
|
fbits = 112
|
||||||
preamble = [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0]
|
preamble = [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0]
|
||||||
|
th_amp = 0.2 # signal amplitude threshold for 0 and 1 bit
|
||||||
th_amp_diff = 0.8 # signal amplitude threshold difference between 0 and 1 bit
|
th_amp_diff = 0.8 # signal amplitude threshold difference between 0 and 1 bit
|
||||||
|
|
||||||
|
|
||||||
class RtlReader(object):
|
class RtlReader(object):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(RtlReader, self).__init__()
|
super(RtlReader, self).__init__()
|
||||||
self.signal_buffer = [] # amplitude of the sample only
|
self.signal_buffer = []
|
||||||
self.sdr = rtlsdr.RtlSdr()
|
self.sdr = RtlSdr()
|
||||||
self.sdr.sample_rate = sampling_rate
|
self.sdr.sample_rate = modes_sample_rate
|
||||||
self.sdr.center_freq = modes_frequency
|
self.sdr.center_freq = modes_frequency
|
||||||
self.sdr.gain = "auto"
|
self.sdr.gain = "auto"
|
||||||
|
# sdr.freq_correction = 75
|
||||||
|
|
||||||
self.debug = kwargs.get("debug", False)
|
self.debug = kwargs.get("debug", False)
|
||||||
self.raw_pipe_in = None
|
self.raw_pipe_in = None
|
||||||
self.stop_flag = False
|
self.stop_flag = False
|
||||||
self.noise_floor = 1e6
|
|
||||||
|
|
||||||
self.exception_queue = None
|
|
||||||
|
|
||||||
def _calc_noise(self):
|
|
||||||
"""Calculate noise floor"""
|
|
||||||
window = smaples_per_microsec * 100
|
|
||||||
total_len = len(self.signal_buffer)
|
|
||||||
means = (
|
|
||||||
np.array(self.signal_buffer[: total_len // window * window])
|
|
||||||
.reshape(-1, window)
|
|
||||||
.mean(axis=1)
|
|
||||||
)
|
|
||||||
return min(means)
|
|
||||||
|
|
||||||
def _process_buffer(self):
|
def _process_buffer(self):
|
||||||
"""process raw IQ data in the buffer"""
|
|
||||||
|
|
||||||
# update noise floor
|
|
||||||
self.noise_floor = min(self._calc_noise(), self.noise_floor)
|
|
||||||
|
|
||||||
# set minimum signal amplitude
|
|
||||||
min_sig_amp = 3.162 * self.noise_floor # 10 dB SNR
|
|
||||||
|
|
||||||
# Mode S messages
|
|
||||||
messages = []
|
messages = []
|
||||||
|
|
||||||
|
# signal_array = np.array(self.signal_buffer)
|
||||||
|
# pulses_array = np.where(np.array(self.signal_buffer) < th_amp, 0, 1)
|
||||||
|
# pulses = "".join(str(x) for x in pulses_array)
|
||||||
buffer_length = len(self.signal_buffer)
|
buffer_length = len(self.signal_buffer)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while i < buffer_length:
|
while i < buffer_length:
|
||||||
if self.signal_buffer[i] < min_sig_amp:
|
if self.signal_buffer[i] < th_amp:
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# if pulses[i : i + pbits * 2] == preamble:
|
||||||
if self._check_preamble(self.signal_buffer[i : i + pbits * 2]):
|
if self._check_preamble(self.signal_buffer[i : i + pbits * 2]):
|
||||||
frame_start = i + pbits * 2
|
frame_start = i + pbits * 2
|
||||||
frame_end = i + pbits * 2 + (fbits + 1) * 2
|
frame_end = i + pbits * 2 + (fbits + 1) * 2
|
||||||
frame_length = (fbits + 1) * 2
|
frame_length = (fbits + 1) * 2
|
||||||
frame_pulses = self.signal_buffer[frame_start:frame_end]
|
frame_pulses = self.signal_buffer[frame_start:frame_end]
|
||||||
|
|
||||||
threshold = max(frame_pulses) * 0.2
|
msgbin = ""
|
||||||
|
|
||||||
msgbin = []
|
|
||||||
for j in range(0, frame_length, 2):
|
for j in range(0, frame_length, 2):
|
||||||
p2 = frame_pulses[j : j + 2]
|
p2 = frame_pulses[j : j + 2]
|
||||||
if len(p2) < 2:
|
if len(p2) < 2:
|
||||||
break
|
break
|
||||||
|
|
||||||
if p2[0] < threshold and p2[1] < threshold:
|
if p2[0] < th_amp and p2[1] < th_amp:
|
||||||
break
|
break
|
||||||
elif p2[0] >= p2[1]:
|
elif p2[0] >= p2[1]:
|
||||||
c = 1
|
c = "1"
|
||||||
elif p2[0] < p2[1]:
|
elif p2[0] < p2[1]:
|
||||||
c = 0
|
c = "0"
|
||||||
else:
|
else:
|
||||||
msgbin = []
|
msgbin = ""
|
||||||
break
|
break
|
||||||
|
msgbin += c
|
||||||
msgbin.append(c)
|
|
||||||
|
|
||||||
# advance i with a jump
|
# advance i with a jump
|
||||||
i = frame_start + j
|
i = frame_start + j
|
||||||
|
|
||||||
if len(msgbin) > 0:
|
if len(msgbin) > 0:
|
||||||
msghex = pms.bin2hex("".join([str(i) for i in msgbin]))
|
msghex = pms.bin2hex(msgbin)
|
||||||
if self._check_msg(msghex):
|
if self._check_msg(msghex):
|
||||||
messages.append([msghex, time.time()])
|
messages.append([msghex, time.time()])
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self._debug_msg(msghex)
|
self._debug_msg(msghex)
|
||||||
|
|
||||||
# elif i > buffer_length - 500:
|
elif i > buffer_length - 500:
|
||||||
# # save some for next process
|
# save some for next process
|
||||||
# break
|
break
|
||||||
else:
|
else:
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# reset the buffer
|
# keep reminder of buffer for next iteration
|
||||||
self.signal_buffer = self.signal_buffer[i:]
|
self.signal_buffer = self.signal_buffer[i:]
|
||||||
|
|
||||||
return messages
|
return messages
|
||||||
|
|
||||||
def _check_preamble(self, pulses):
|
def _check_preamble(self, pulses):
|
||||||
@@ -152,8 +122,10 @@ class RtlReader(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def _read_callback(self, data, rtlsdr_obj):
|
def _read_callback(self, data, rtlsdr_obj):
|
||||||
|
# scaling signal (imporatant)
|
||||||
amp = np.absolute(data)
|
amp = np.absolute(data)
|
||||||
self.signal_buffer.extend(amp.tolist())
|
amp_norm = np.interp(amp, (amp.min(), amp.max()), (0, 1))
|
||||||
|
self.signal_buffer.extend(amp_norm.tolist())
|
||||||
|
|
||||||
if len(self.signal_buffer) >= buffer_size:
|
if len(self.signal_buffer) >= buffer_size:
|
||||||
messages = self._process_buffer()
|
messages = self._process_buffer()
|
||||||
@@ -166,25 +138,18 @@ class RtlReader(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def stop(self, *args, **kwargs):
|
def stop(self, *args, **kwargs):
|
||||||
self.sdr.close()
|
self.sdr.cancel_read_async()
|
||||||
|
|
||||||
def run(self, raw_pipe_in=None, stop_flag=None, exception_queue=None):
|
def run(self, raw_pipe_in=None, stop_flag=None):
|
||||||
self.raw_pipe_in = raw_pipe_in
|
self.raw_pipe_in = raw_pipe_in
|
||||||
self.exception_queue = exception_queue
|
|
||||||
self.stop_flag = stop_flag
|
self.stop_flag = stop_flag
|
||||||
|
self.sdr.read_samples_async(self._read_callback, read_size)
|
||||||
|
|
||||||
try:
|
# count = 1
|
||||||
# raise RuntimeError("test exception")
|
# while count < 1000:
|
||||||
|
# count += 1
|
||||||
while True:
|
# data = self.sdr.read_samples(read_size)
|
||||||
data = self.sdr.read_samples(read_size)
|
# self._read_callback(data, None)
|
||||||
self._read_callback(data, None)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
tb = traceback.format_exc()
|
|
||||||
if self.exception_queue is not None:
|
|
||||||
self.exception_queue.put(tb)
|
|
||||||
raise e
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"""Stream beast raw data from a TCP server, convert to mode-s messages."""
|
"""Stream beast raw data from a TCP server, convert to mode-s messages."""
|
||||||
|
|
||||||
|
from __future__ import print_function, division
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
@@ -7,6 +8,11 @@ import pyModeS as pms
|
|||||||
import traceback
|
import traceback
|
||||||
import zmq
|
import zmq
|
||||||
|
|
||||||
|
if sys.version_info > (3, 0):
|
||||||
|
PY_VERSION = 3
|
||||||
|
else:
|
||||||
|
PY_VERSION = 2
|
||||||
|
|
||||||
|
|
||||||
class TcpClient(object):
|
class TcpClient(object):
|
||||||
def __init__(self, host, port, datatype):
|
def __init__(self, host, port, datatype):
|
||||||
@@ -23,8 +29,6 @@ class TcpClient(object):
|
|||||||
self.raw_pipe_in = None
|
self.raw_pipe_in = None
|
||||||
self.stop_flag = False
|
self.stop_flag = False
|
||||||
|
|
||||||
self.exception_queue = None
|
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
self.socket = zmq.Context().socket(zmq.STREAM)
|
self.socket = zmq.Context().socket(zmq.STREAM)
|
||||||
self.socket.setsockopt(zmq.LINGER, 0)
|
self.socket.setsockopt(zmq.LINGER, 0)
|
||||||
@@ -166,7 +170,7 @@ class TcpClient(object):
|
|||||||
Start character '$'
|
Start character '$'
|
||||||
|
|
||||||
MS field - Payload
|
MS field - Payload
|
||||||
Position 1 through 14:
|
Postion 1 through 14:
|
||||||
14 bytes = 112 bits
|
14 bytes = 112 bits
|
||||||
Mode-S payload
|
Mode-S payload
|
||||||
In case of DF types that only carry 7 bytes of information
|
In case of DF types that only carry 7 bytes of information
|
||||||
@@ -250,9 +254,8 @@ class TcpClient(object):
|
|||||||
for msg, t in messages:
|
for msg, t in messages:
|
||||||
print("%15.9f %s" % (t, msg))
|
print("%15.9f %s" % (t, msg))
|
||||||
|
|
||||||
def run(self, raw_pipe_in=None, stop_flag=None, exception_queue=None):
|
def run(self, raw_pipe_in=None, stop_flag=None):
|
||||||
self.raw_pipe_in = raw_pipe_in
|
self.raw_pipe_in = raw_pipe_in
|
||||||
self.exception_queue = exception_queue
|
|
||||||
self.stop_flag = stop_flag
|
self.stop_flag = stop_flag
|
||||||
self.connect()
|
self.connect()
|
||||||
|
|
||||||
@@ -260,9 +263,17 @@ class TcpClient(object):
|
|||||||
try:
|
try:
|
||||||
received = [i for i in self.socket.recv(4096)]
|
received = [i for i in self.socket.recv(4096)]
|
||||||
|
|
||||||
|
if PY_VERSION == 2:
|
||||||
|
received = [ord(i) for i in received]
|
||||||
|
|
||||||
self.buffer.extend(received)
|
self.buffer.extend(received)
|
||||||
# print(''.join(x.encode('hex') for x in self.buffer))
|
# print(''.join(x.encode('hex') for x in self.buffer))
|
||||||
|
|
||||||
|
# process self.buffer when it is longer enough
|
||||||
|
# if len(self.buffer) < 2048:
|
||||||
|
# continue
|
||||||
|
# -- Removed!! Cause delay in low data rate scenario --
|
||||||
|
|
||||||
if self.datatype == "beast":
|
if self.datatype == "beast":
|
||||||
messages = self.read_beast_buffer()
|
messages = self.read_beast_buffer()
|
||||||
elif self.datatype == "raw":
|
elif self.datatype == "raw":
|
||||||
@@ -275,15 +286,22 @@ class TcpClient(object):
|
|||||||
else:
|
else:
|
||||||
self.handle_messages(messages)
|
self.handle_messages(messages)
|
||||||
|
|
||||||
# raise RuntimeError("test exception")
|
|
||||||
|
|
||||||
except zmq.error.Again:
|
|
||||||
continue
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
tb = traceback.format_exc()
|
# Provides the user an option to supply the environment
|
||||||
if self.exception_queue is not None:
|
# variable PYMODES_DEBUG to halt the execution
|
||||||
self.exception_queue.put(tb)
|
# for debugging purposes
|
||||||
raise e
|
debug_intent = os.environ.get("PYMODES_DEBUG", "false")
|
||||||
|
if debug_intent.lower() == "true":
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
|
print("Unexpected Error:", e)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sock = self.connect()
|
||||||
|
time.sleep(1)
|
||||||
|
except Exception as e:
|
||||||
|
print("Unexpected Error:", e)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
@@ -26,7 +27,7 @@ class Decode:
|
|||||||
self.dumpto = None
|
self.dumpto = None
|
||||||
|
|
||||||
def process_raw(self, adsb_ts, adsb_msg, commb_ts, commb_msg, tnow=None):
|
def process_raw(self, adsb_ts, adsb_msg, commb_ts, commb_msg, tnow=None):
|
||||||
"""process a chunk of adsb and commb messages received in the same
|
"""process a chunk of adsb and commb messages recieved in the same
|
||||||
time period.
|
time period.
|
||||||
"""
|
"""
|
||||||
if tnow is None:
|
if tnow is None:
|
||||||
@@ -258,31 +259,26 @@ class Decode:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def get_aircraft(self):
|
def get_aircraft(self):
|
||||||
"""all aircraft that are stored in memory"""
|
"""all aircraft that are stored in memeory"""
|
||||||
acs = self.acs
|
acs = self.acs
|
||||||
return acs
|
return acs
|
||||||
|
|
||||||
def run(self, raw_pipe_out, ac_pipe_in, exception_queue):
|
def run(self, raw_pipe_out, ac_pipe_in):
|
||||||
local_buffer = []
|
local_buffer = []
|
||||||
while True:
|
while True:
|
||||||
try:
|
while raw_pipe_out.poll():
|
||||||
while raw_pipe_out.poll():
|
data = raw_pipe_out.recv()
|
||||||
data = raw_pipe_out.recv()
|
local_buffer.append(data)
|
||||||
local_buffer.append(data)
|
|
||||||
|
|
||||||
for data in local_buffer:
|
for data in local_buffer:
|
||||||
self.process_raw(
|
self.process_raw(
|
||||||
data["adsb_ts"],
|
data["adsb_ts"],
|
||||||
data["adsb_msg"],
|
data["adsb_msg"],
|
||||||
data["commb_ts"],
|
data["commb_ts"],
|
||||||
data["commb_msg"],
|
data["commb_msg"],
|
||||||
)
|
)
|
||||||
local_buffer = []
|
local_buffer = []
|
||||||
|
|
||||||
acs = self.get_aircraft()
|
acs = self.get_aircraft()
|
||||||
ac_pipe_in.send(acs)
|
ac_pipe_in.send(acs)
|
||||||
time.sleep(0.001)
|
time.sleep(0.001)
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
tb = traceback.format_exc()
|
|
||||||
exception_queue.put((e, tb))
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from __future__ import print_function, division
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
import argparse
|
import argparse
|
||||||
import curses
|
import curses
|
||||||
import signal
|
import signal
|
||||||
@@ -12,6 +12,10 @@ from pyModeS.streamer.screen import Screen
|
|||||||
from pyModeS.streamer.source import NetSource, RtlSdrSource
|
from pyModeS.streamer.source import NetSource, RtlSdrSource
|
||||||
|
|
||||||
|
|
||||||
|
# redirect all stdout to null, avoiding messing up with the screen
|
||||||
|
sys.stdout = open(os.devnull, "w")
|
||||||
|
|
||||||
|
|
||||||
support_rawtypes = ["raw", "beast", "skysense"]
|
support_rawtypes = ["raw", "beast", "skysense"]
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@@ -23,9 +27,8 @@ parser.add_argument(
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--connect",
|
"--connect",
|
||||||
help="Define server, port and data type. Supported data types are: {}".format(
|
help="Define server, port and data type. Supported data types are: %s"
|
||||||
support_rawtypes
|
% support_rawtypes,
|
||||||
),
|
|
||||||
nargs=3,
|
nargs=3,
|
||||||
metavar=("SERVER", "PORT", "DATATYPE"),
|
metavar=("SERVER", "PORT", "DATATYPE"),
|
||||||
default=None,
|
default=None,
|
||||||
@@ -68,7 +71,7 @@ elif SOURCE == "net":
|
|||||||
else:
|
else:
|
||||||
SERVER, PORT, DATATYPE = args.connect
|
SERVER, PORT, DATATYPE = args.connect
|
||||||
if DATATYPE not in support_rawtypes:
|
if DATATYPE not in support_rawtypes:
|
||||||
print("Data type not supported, available ones are %s" % support_rawtypes)
|
print("Data type not supported, avaiable ones are %s" % support_rawtypes)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print('Source must be "rtlsdr" or "net".')
|
print('Source must be "rtlsdr" or "net".')
|
||||||
@@ -84,13 +87,13 @@ if DUMPTO is not None:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
# redirect all stdout to null, avoiding messing up with the screen
|
# raw_event = multiprocessing.Event()
|
||||||
sys.stdout = open(os.devnull, "w")
|
# ac_event = multiprocessing.Event()
|
||||||
|
# raw_queue = multiprocessing.Queue()
|
||||||
|
# ac_queue = multiprocessing.Queue()
|
||||||
|
|
||||||
raw_pipe_in, raw_pipe_out = multiprocessing.Pipe()
|
raw_pipe_in, raw_pipe_out = multiprocessing.Pipe()
|
||||||
ac_pipe_in, ac_pipe_out = multiprocessing.Pipe()
|
ac_pipe_in, ac_pipe_out = multiprocessing.Pipe()
|
||||||
exception_queue = multiprocessing.Queue()
|
|
||||||
stop_flag = multiprocessing.Value("b", False)
|
stop_flag = multiprocessing.Value("b", False)
|
||||||
|
|
||||||
if SOURCE == "net":
|
if SOURCE == "net":
|
||||||
@@ -99,38 +102,29 @@ elif SOURCE == "rtlsdr":
|
|||||||
source = RtlSdrSource()
|
source = RtlSdrSource()
|
||||||
|
|
||||||
|
|
||||||
recv_process = multiprocessing.Process(
|
recv_process = multiprocessing.Process(target=source.run, args=(raw_pipe_in, stop_flag))
|
||||||
target=source.run, args=(raw_pipe_in, stop_flag, exception_queue)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
decode = Decode(latlon=LATLON, dumpto=DUMPTO)
|
decode = Decode(latlon=LATLON, dumpto=DUMPTO)
|
||||||
decode_process = multiprocessing.Process(
|
decode_process = multiprocessing.Process(
|
||||||
target=decode.run, args=(raw_pipe_out, ac_pipe_in, exception_queue)
|
target=decode.run, args=(raw_pipe_out, ac_pipe_in)
|
||||||
)
|
)
|
||||||
|
|
||||||
screen = Screen(uncertainty=UNCERTAINTY)
|
screen = Screen(uncertainty=UNCERTAINTY)
|
||||||
screen_process = multiprocessing.Process(
|
screen_process = multiprocessing.Process(target=screen.run, args=(ac_pipe_out,))
|
||||||
target=screen.run, args=(ac_pipe_out, exception_queue)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def shutdown():
|
def closeall(signal, frame):
|
||||||
|
print("KeyboardInterrupt (ID: {}). Cleaning up...".format(signal))
|
||||||
stop_flag.value = True
|
stop_flag.value = True
|
||||||
curses.endwin()
|
curses.endwin()
|
||||||
sys.stdout = sys.__stdout__
|
|
||||||
recv_process.terminate()
|
recv_process.terminate()
|
||||||
decode_process.terminate()
|
decode_process.terminate()
|
||||||
screen_process.terminate()
|
screen_process.terminate()
|
||||||
recv_process.join()
|
recv_process.join()
|
||||||
decode_process.join()
|
decode_process.join()
|
||||||
screen_process.join()
|
screen_process.join()
|
||||||
|
exit(0)
|
||||||
|
|
||||||
def closeall(signal, frame):
|
|
||||||
print("KeyboardInterrupt (ID: {}). Cleaning up...".format(signal))
|
|
||||||
shutdown()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, closeall)
|
signal.signal(signal.SIGINT, closeall)
|
||||||
@@ -138,19 +132,3 @@ signal.signal(signal.SIGINT, closeall)
|
|||||||
recv_process.start()
|
recv_process.start()
|
||||||
decode_process.start()
|
decode_process.start()
|
||||||
screen_process.start()
|
screen_process.start()
|
||||||
|
|
||||||
|
|
||||||
while True:
|
|
||||||
if (
|
|
||||||
(not recv_process.is_alive())
|
|
||||||
or (not decode_process.is_alive())
|
|
||||||
or (not screen_process.is_alive())
|
|
||||||
):
|
|
||||||
shutdown()
|
|
||||||
while not exception_queue.empty():
|
|
||||||
trackback = exception_queue.get()
|
|
||||||
print(trackback)
|
|
||||||
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
time.sleep(0.01)
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
from __future__ import print_function, division
|
||||||
import curses
|
import curses
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
|
||||||
|
|
||||||
COLUMNS = [
|
COLUMNS = [
|
||||||
("call", 10),
|
("call", 10),
|
||||||
@@ -187,32 +187,24 @@ class Screen(object):
|
|||||||
self.screen.refresh()
|
self.screen.refresh()
|
||||||
self.draw_frame()
|
self.draw_frame()
|
||||||
|
|
||||||
def run(self, ac_pipe_out, exception_queue):
|
def run(self, ac_pipe_out):
|
||||||
local_buffer = []
|
local_buffer = []
|
||||||
key_thread = threading.Thread(target=self.kye_handling)
|
key_thread = threading.Thread(target=self.kye_handling)
|
||||||
key_thread.daemon = True
|
|
||||||
key_thread.start()
|
key_thread.start()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
while ac_pipe_out.poll():
|
||||||
|
acs = ac_pipe_out.recv()
|
||||||
|
local_buffer.append(acs)
|
||||||
|
|
||||||
|
for acs in local_buffer:
|
||||||
|
self.update_ac(acs)
|
||||||
|
|
||||||
|
local_buffer = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# raise RuntimeError("test exception")
|
|
||||||
|
|
||||||
while ac_pipe_out.poll():
|
|
||||||
acs = ac_pipe_out.recv()
|
|
||||||
local_buffer.append(acs)
|
|
||||||
|
|
||||||
for acs in local_buffer:
|
|
||||||
self.update_ac(acs)
|
|
||||||
|
|
||||||
local_buffer = []
|
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
except curses.error:
|
except:
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
|
||||||
tb = traceback.format_exc()
|
|
||||||
exception_queue.put(tb)
|
|
||||||
time.sleep(0.1)
|
|
||||||
raise e
|
|
||||||
|
|
||||||
time.sleep(0.001)
|
time.sleep(0.001)
|
||||||
|
|||||||
84
setup.py
84
setup.py
@@ -4,16 +4,24 @@ See:
|
|||||||
https://packaging.python.org/en/latest/distributing.html
|
https://packaging.python.org/en/latest/distributing.html
|
||||||
https://github.com/pypa/sampleproject
|
https://github.com/pypa/sampleproject
|
||||||
|
|
||||||
Steps for deploying a new version:
|
Steps for deploying a new verison:
|
||||||
1. Increase the version number
|
1. Increase the version number
|
||||||
2. remove the old deployment under [dist] and [build] folder
|
2. remove the old deployment under [dist] and [build] folder
|
||||||
3. run: python setup.py sdist
|
3. run: python setup.py sdist
|
||||||
|
run: python setup.py bdist_wheel --universal
|
||||||
4. twine upload dist/*
|
4. twine upload dist/*
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Always prefer setuptools over distutils
|
# Always prefer setuptools over distutils
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
# Compile some parts
|
||||||
|
from setuptools.extension import Extension
|
||||||
|
from Cython.Build import cythonize
|
||||||
|
|
||||||
|
extensions = [Extension("pyModeS.decoder.c_common", ["pyModeS/decoder/c_common.pyx"])]
|
||||||
|
|
||||||
|
|
||||||
# To use a consistent encoding
|
# To use a consistent encoding
|
||||||
from codecs import open
|
from codecs import open
|
||||||
from os import path
|
from os import path
|
||||||
@@ -24,38 +32,78 @@ here = path.abspath(path.dirname(__file__))
|
|||||||
with open(path.join(here, "README.rst"), encoding="utf-8") as f:
|
with open(path.join(here, "README.rst"), encoding="utf-8") as f:
|
||||||
long_description = f.read()
|
long_description = f.read()
|
||||||
|
|
||||||
|
setup(
|
||||||
details = dict(
|
|
||||||
name="pyModeS",
|
name="pyModeS",
|
||||||
version="2.8",
|
# Versions should comply with PEP440. For a discussion on single-sourcing
|
||||||
|
# the version across setup.py and the project code, see
|
||||||
|
# https://packaging.python.org/en/latest/single_source_version.html
|
||||||
|
version="2.4",
|
||||||
description="Python Mode-S and ADS-B Decoder",
|
description="Python Mode-S and ADS-B Decoder",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
|
# The project's main homepage.
|
||||||
url="https://github.com/junzis/pyModeS",
|
url="https://github.com/junzis/pyModeS",
|
||||||
|
# Author details
|
||||||
author="Junzi Sun",
|
author="Junzi Sun",
|
||||||
author_email="j.sun-1@tudelft.nl",
|
author_email="j.sun-1@tudelft.nl",
|
||||||
|
# Choose your license
|
||||||
license="GNU GPL v3",
|
license="GNU GPL v3",
|
||||||
|
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
# How mature is this project? Common values are
|
||||||
|
# 3 - Alpha
|
||||||
|
# 4 - Beta
|
||||||
|
# 5 - Production/Stable
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 4 - Beta",
|
||||||
|
# Indicate who your project is intended for
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"Topic :: Software Development :: Libraries",
|
"Topic :: Software Development :: Libraries",
|
||||||
|
# Pick your license as you wish (should match "license" above)
|
||||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
|
# Specify the Python versions you support here. In particular, ensure
|
||||||
|
# that you indicate whether you support Python 2, Python 3 or both.
|
||||||
|
"Programming Language :: Python :: 2",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
],
|
],
|
||||||
|
ext_modules=cythonize(extensions),
|
||||||
|
# What does your project relate to?
|
||||||
keywords="Mode-S ADS-B EHS ELS Comm-B",
|
keywords="Mode-S ADS-B EHS ELS Comm-B",
|
||||||
|
# You can just specify the packages manually here if your project is
|
||||||
|
# simple. Or you can use find_packages().
|
||||||
packages=find_packages(exclude=["contrib", "docs", "tests"]),
|
packages=find_packages(exclude=["contrib", "docs", "tests"]),
|
||||||
install_requires=["numpy", "pyzmq"],
|
# Alternatively, if you want to distribute just a my_module.py, uncomment
|
||||||
extras_require={"fast": ["Cython"]},
|
# this:
|
||||||
package_data={"pyModeS": ["*.pyx", "*.pxd"]},
|
# py_modules=["my_module"],
|
||||||
|
# List run-time dependencies here. These will be installed by pip when
|
||||||
|
# your project is installed. For an analysis of "install_requires" vs pip's
|
||||||
|
# requirements files see:
|
||||||
|
# https://packaging.python.org/en/latest/requirements.html
|
||||||
|
install_requires=["numpy", "argparse", "pyzmq", "pyrtlsdr"],
|
||||||
|
# List additional groups of dependencies here (e.g. development
|
||||||
|
# dependencies). You can install these using the following syntax,
|
||||||
|
# for example:
|
||||||
|
# $ pip install -e .[dev,test]
|
||||||
|
# extras_require={
|
||||||
|
# 'dev': ['check-manifest'],
|
||||||
|
# 'test': ['coverage'],
|
||||||
|
# },
|
||||||
|
# If there are data files included in your packages that need to be
|
||||||
|
# installed, specify them here. If using Python 2.6 or less, then these
|
||||||
|
# have to be included in MANIFEST.in as well.
|
||||||
|
# package_data={
|
||||||
|
# 'sample': ['package_data.dat'],
|
||||||
|
# },
|
||||||
|
# Although 'package_data' is the preferred approach, in some case you may
|
||||||
|
# need to place data files outside of your packages. See:
|
||||||
|
# http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # noqa
|
||||||
|
# In this case, 'data_file' will be installed into '<sys.prefix>/my_data'
|
||||||
|
# data_files=[('my_data', ['data/data_file'])],
|
||||||
|
# To provide executable scripts, use entry points in preference to the
|
||||||
|
# "scripts" keyword. Entry points provide cross-platform support and allow
|
||||||
|
# pip to create the appropriate form of executable for the target platform.
|
||||||
|
# entry_points={
|
||||||
|
# 'console_scripts': [
|
||||||
|
# 'sample=sample:main',
|
||||||
|
# ],
|
||||||
|
# },
|
||||||
scripts=["pyModeS/streamer/modeslive"],
|
scripts=["pyModeS/streamer/modeslive"],
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
|
||||||
from setuptools.extension import Extension
|
|
||||||
from Cython.Build import cythonize
|
|
||||||
|
|
||||||
extensions = [Extension("pyModeS.c_common", ["pyModeS/c_common.pyx"])]
|
|
||||||
|
|
||||||
setup(**dict(details, ext_modules=cythonize(extensions)))
|
|
||||||
|
|
||||||
except:
|
|
||||||
setup(**details)
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from __future__ import print_function
|
||||||
from pyModeS import commb, common, bds
|
from pyModeS import commb, common, bds
|
||||||
|
|
||||||
# === Decode sample data file ===
|
# === Decode sample data file ===
|
||||||
@@ -46,7 +47,7 @@ def bds_info(BDS, m):
|
|||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
info = []
|
info = None
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
@@ -87,5 +88,5 @@ def commb_decode_all(df, n=None):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
commb_decode_all(df=20, n=500)
|
commb_decode_all(df=20, n=100)
|
||||||
commb_decode_all(df=21, n=500)
|
commb_decode_all(df=21, n=100)
|
||||||
|
|||||||
@@ -76,16 +76,10 @@ def test_adsb_velocity():
|
|||||||
vgs_surface = adsb.velocity("8FC8200A3AB8F5F893096B000000")
|
vgs_surface = adsb.velocity("8FC8200A3AB8F5F893096B000000")
|
||||||
assert vgs == (159, 182.88, -832, "GS")
|
assert vgs == (159, 182.88, -832, "GS")
|
||||||
assert vas == (375, 243.98, -2304, "TAS")
|
assert vas == (375, 243.98, -2304, "TAS")
|
||||||
assert vgs_surface == (19, 42.2, 0, "GS")
|
assert vgs_surface == (19.0, 42.2, 0, "GS")
|
||||||
assert adsb.altitude_diff("8D485020994409940838175B284F") == 550
|
assert adsb.altitude_diff("8D485020994409940838175B284F") == 550
|
||||||
|
|
||||||
|
|
||||||
def test_adsb_emergency():
|
|
||||||
assert not adsb.is_emergency("8DA2C1B6E112B600000000760759")
|
|
||||||
assert adsb.emergency_state("8DA2C1B6E112B600000000760759") == 0
|
|
||||||
assert adsb.emergency_squawk("8DA2C1B6E112B600000000760759") == "6615"
|
|
||||||
|
|
||||||
|
|
||||||
# def test_nic():
|
# def test_nic():
|
||||||
# assert adsb.nic('8D3C70A390AB11F55B8C57F65FE6') == 0
|
# assert adsb.nic('8D3C70A390AB11F55B8C57F65FE6') == 0
|
||||||
# assert adsb.nic('8DE1C9738A4A430B427D219C8225') == 1
|
# assert adsb.nic('8DE1C9738A4A430B427D219C8225') == 1
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
from pyModeS import allcall
|
|
||||||
|
|
||||||
|
|
||||||
def test_icao():
|
|
||||||
assert allcall.icao("5D484FDEA248F5") == "484FDE"
|
|
||||||
|
|
||||||
|
|
||||||
def test_interrogator():
|
|
||||||
assert allcall.interrogator("5D484FDEA248F5") == 22
|
|
||||||
|
|
||||||
|
|
||||||
def test_capability():
|
|
||||||
assert allcall.capability("5D484FDEA248F5")[0] == 5
|
|
||||||
@@ -17,8 +17,8 @@ def test_bds_infer():
|
|||||||
|
|
||||||
def test_bds_is50or60():
|
def test_bds_is50or60():
|
||||||
assert bds.is50or60("A0001838201584F23468207CDFA5", 0, 0, 0) == None
|
assert bds.is50or60("A0001838201584F23468207CDFA5", 0, 0, 0) == None
|
||||||
assert bds.is50or60("A8001EBCFFFB23286004A73F6A5B", 320, 250, 14000) == "BDS50"
|
assert bds.is50or60("A0000000FFDA9517000464000000", 182, 237, 1250) == "BDS50"
|
||||||
assert bds.is50or60("A8001EBCFE1B29287FDCA807BCFC", 320, 250, 14000) == "BDS50"
|
assert bds.is50or60("A0000000919A5927E23444000000", 413, 54, 18700) == "BDS60"
|
||||||
|
|
||||||
|
|
||||||
def test_surface_position():
|
def test_surface_position():
|
||||||
|
|||||||
@@ -1,60 +1,60 @@
|
|||||||
try:
|
from pyModeS.decoder import c_common as common
|
||||||
from pyModeS import c_common
|
|
||||||
|
|
||||||
def test_conversions():
|
|
||||||
assert c_common.hex2bin("6E") == "01101110"
|
|
||||||
assert c_common.bin2hex("01101110") == "6E"
|
|
||||||
assert c_common.bin2hex("1101110") == "6E"
|
|
||||||
|
|
||||||
def test_crc_decode():
|
|
||||||
|
|
||||||
assert c_common.crc("8D406B902015A678D4D220AA4BDA") == 0
|
|
||||||
assert c_common.crc("8d8960ed58bf053cf11bc5932b7d") == 0
|
|
||||||
assert c_common.crc("8d45cab390c39509496ca9a32912") == 0
|
|
||||||
assert c_common.crc("8d74802958c904e6ef4ba0184d5c") == 0
|
|
||||||
assert c_common.crc("8d4400cd9b0000b4f87000e71a10") == 0
|
|
||||||
assert c_common.crc("8d4065de58a1054a7ef0218e226a") == 0
|
|
||||||
|
|
||||||
assert c_common.crc("c80b2dca34aa21dd821a04cb64d4") == 10719924
|
|
||||||
assert c_common.crc("a800089d8094e33a6004e4b8a522") == 4805588
|
|
||||||
assert c_common.crc("a8000614a50b6d32bed000bbe0ed") == 5659991
|
|
||||||
assert c_common.crc("a0000410bc900010a40000f5f477") == 11727682
|
|
||||||
assert c_common.crc("8d4ca251204994b1c36e60a5343d") == 16
|
|
||||||
assert c_common.crc("b0001718c65632b0a82040715b65") == 353333
|
|
||||||
|
|
||||||
def test_crc_encode():
|
|
||||||
parity = c_common.crc("8D406B902015A678D4D220AA4BDA", encode=True)
|
|
||||||
assert parity == 11160538
|
|
||||||
|
|
||||||
def test_icao():
|
|
||||||
assert c_common.icao("8D406B902015A678D4D220AA4BDA") == "406B90"
|
|
||||||
assert c_common.icao("A0001839CA3800315800007448D9") == "400940"
|
|
||||||
assert c_common.icao("A000139381951536E024D4CCF6B5") == "3C4DD2"
|
|
||||||
assert c_common.icao("A000029CFFBAA11E2004727281F1") == "4243D0"
|
|
||||||
|
|
||||||
def test_modes_altcode():
|
|
||||||
assert c_common.altcode("A02014B400000000000000F9D514") == 32300
|
|
||||||
|
|
||||||
def test_modes_idcode():
|
|
||||||
assert c_common.idcode("A800292DFFBBA9383FFCEB903D01") == "1346"
|
|
||||||
|
|
||||||
def test_graycode_to_altitude():
|
|
||||||
assert c_common.gray2alt("00000000010") == -1000
|
|
||||||
assert c_common.gray2alt("00000001010") == -500
|
|
||||||
assert c_common.gray2alt("00000011011") == -100
|
|
||||||
assert c_common.gray2alt("00000011010") == 0
|
|
||||||
assert c_common.gray2alt("00000011110") == 100
|
|
||||||
assert c_common.gray2alt("00000010011") == 600
|
|
||||||
assert c_common.gray2alt("00000110010") == 1000
|
|
||||||
assert c_common.gray2alt("00001001001") == 5800
|
|
||||||
assert c_common.gray2alt("00011100100") == 10300
|
|
||||||
assert c_common.gray2alt("01100011010") == 32000
|
|
||||||
assert c_common.gray2alt("01110000100") == 46300
|
|
||||||
assert c_common.gray2alt("01010101100") == 50200
|
|
||||||
assert c_common.gray2alt("11011110100") == 73200
|
|
||||||
assert c_common.gray2alt("10000000011") == 126600
|
|
||||||
assert c_common.gray2alt("10000000001") == 126700
|
|
||||||
|
|
||||||
|
|
||||||
except:
|
def test_conversions():
|
||||||
pass
|
assert common.hex2bin("6E406B") == "011011100100000001101011"
|
||||||
|
|
||||||
|
|
||||||
|
def test_crc_decode():
|
||||||
|
|
||||||
|
assert common.crc("8D406B902015A678D4D220AA4BDA") == 0
|
||||||
|
assert common.crc("8d8960ed58bf053cf11bc5932b7d") == 0
|
||||||
|
assert common.crc("8d45cab390c39509496ca9a32912") == 0
|
||||||
|
assert common.crc("8d74802958c904e6ef4ba0184d5c") == 0
|
||||||
|
assert common.crc("8d4400cd9b0000b4f87000e71a10") == 0
|
||||||
|
assert common.crc("8d4065de58a1054a7ef0218e226a") == 0
|
||||||
|
|
||||||
|
assert common.crc("c80b2dca34aa21dd821a04cb64d4") == 10719924
|
||||||
|
assert common.crc("a800089d8094e33a6004e4b8a522") == 4805588
|
||||||
|
assert common.crc("a8000614a50b6d32bed000bbe0ed") == 5659991
|
||||||
|
assert common.crc("a0000410bc900010a40000f5f477") == 11727682
|
||||||
|
assert common.crc("8d4ca251204994b1c36e60a5343d") == 16
|
||||||
|
assert common.crc("b0001718c65632b0a82040715b65") == 353333
|
||||||
|
|
||||||
|
|
||||||
|
def test_crc_encode():
|
||||||
|
parity = common.crc("8D406B902015A678D4D220AA4BDA", encode=True)
|
||||||
|
assert parity == 11160538
|
||||||
|
|
||||||
|
|
||||||
|
def test_icao():
|
||||||
|
assert common.icao("8D406B902015A678D4D220AA4BDA") == "406B90"
|
||||||
|
assert common.icao("A0001839CA3800315800007448D9") == "400940"
|
||||||
|
assert common.icao("A000139381951536E024D4CCF6B5") == "3C4DD2"
|
||||||
|
assert common.icao("A000029CFFBAA11E2004727281F1") == "4243D0"
|
||||||
|
|
||||||
|
|
||||||
|
def test_modes_altcode():
|
||||||
|
assert common.altcode("A02014B400000000000000F9D514") == 32300
|
||||||
|
|
||||||
|
|
||||||
|
def test_modes_idcode():
|
||||||
|
assert common.idcode("A800292DFFBBA9383FFCEB903D01") == "1346"
|
||||||
|
|
||||||
|
|
||||||
|
def test_graycode_to_altitude():
|
||||||
|
assert common.gray2alt("00000000010") == -1000
|
||||||
|
assert common.gray2alt("00000001010") == -500
|
||||||
|
assert common.gray2alt("00000011011") == -100
|
||||||
|
assert common.gray2alt("00000011010") == 0
|
||||||
|
assert common.gray2alt("00000011110") == 100
|
||||||
|
assert common.gray2alt("00000010011") == 600
|
||||||
|
assert common.gray2alt("00000110010") == 1000
|
||||||
|
assert common.gray2alt("00001001001") == 5800
|
||||||
|
assert common.gray2alt("00011100100") == 10300
|
||||||
|
assert common.gray2alt("01100011010") == 32000
|
||||||
|
assert common.gray2alt("01110000100") == 46300
|
||||||
|
assert common.gray2alt("01010101100") == 50200
|
||||||
|
assert common.gray2alt("11011110100") == 73200
|
||||||
|
assert common.gray2alt("10000000011") == 126600
|
||||||
|
assert common.gray2alt("10000000001") == 126700
|
||||||
|
|||||||
62
tests/test_common.py
Normal file
62
tests/test_common.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
from pyModeS.decoder import common
|
||||||
|
|
||||||
|
|
||||||
|
def test_conversions():
|
||||||
|
assert common.hex2bin("6E406B") == "011011100100000001101011"
|
||||||
|
|
||||||
|
|
||||||
|
def test_crc_decode():
|
||||||
|
assert common.crc_legacy("8D406B902015A678D4D220AA4BDA") == 0
|
||||||
|
|
||||||
|
assert common.crc("8D406B902015A678D4D220AA4BDA") == 0
|
||||||
|
assert common.crc("8d8960ed58bf053cf11bc5932b7d") == 0
|
||||||
|
assert common.crc("8d45cab390c39509496ca9a32912") == 0
|
||||||
|
assert common.crc("8d49d3d4e1089d00000000744c3b") == 0
|
||||||
|
assert common.crc("8d74802958c904e6ef4ba0184d5c") == 0
|
||||||
|
assert common.crc("8d4400cd9b0000b4f87000e71a10") == 0
|
||||||
|
assert common.crc("8d4065de58a1054a7ef0218e226a") == 0
|
||||||
|
|
||||||
|
assert common.crc("c80b2dca34aa21dd821a04cb64d4") == 10719924
|
||||||
|
assert common.crc("a800089d8094e33a6004e4b8a522") == 4805588
|
||||||
|
assert common.crc("a8000614a50b6d32bed000bbe0ed") == 5659991
|
||||||
|
assert common.crc("a0000410bc900010a40000f5f477") == 11727682
|
||||||
|
assert common.crc("8d4ca251204994b1c36e60a5343d") == 16
|
||||||
|
assert common.crc("b0001718c65632b0a82040715b65") == 353333
|
||||||
|
|
||||||
|
|
||||||
|
def test_crc_encode():
|
||||||
|
parity = common.crc("8D406B902015A678D4D220AA4BDA", encode=True)
|
||||||
|
assert parity == 11160538
|
||||||
|
|
||||||
|
|
||||||
|
def test_icao():
|
||||||
|
assert common.icao("8D406B902015A678D4D220AA4BDA") == "406B90"
|
||||||
|
assert common.icao("A0001839CA3800315800007448D9") == "400940"
|
||||||
|
assert common.icao("A000139381951536E024D4CCF6B5") == "3C4DD2"
|
||||||
|
assert common.icao("A000029CFFBAA11E2004727281F1") == "4243D0"
|
||||||
|
|
||||||
|
|
||||||
|
def test_modes_altcode():
|
||||||
|
assert common.altcode("A02014B400000000000000F9D514") == 32300
|
||||||
|
|
||||||
|
|
||||||
|
def test_modes_idcode():
|
||||||
|
assert common.idcode("A800292DFFBBA9383FFCEB903D01") == "1346"
|
||||||
|
|
||||||
|
|
||||||
|
def test_graycode_to_altitude():
|
||||||
|
assert common.gray2alt("00000000010") == -1000
|
||||||
|
assert common.gray2alt("00000001010") == -500
|
||||||
|
assert common.gray2alt("00000011011") == -100
|
||||||
|
assert common.gray2alt("00000011010") == 0
|
||||||
|
assert common.gray2alt("00000011110") == 100
|
||||||
|
assert common.gray2alt("00000010011") == 600
|
||||||
|
assert common.gray2alt("00000110010") == 1000
|
||||||
|
assert common.gray2alt("00001001001") == 5800
|
||||||
|
assert common.gray2alt("00011100100") == 10300
|
||||||
|
assert common.gray2alt("01100011010") == 32000
|
||||||
|
assert common.gray2alt("01110000100") == 46300
|
||||||
|
assert common.gray2alt("01010101100") == 50200
|
||||||
|
assert common.gray2alt("11011110100") == 73200
|
||||||
|
assert common.gray2alt("10000000011") == 126600
|
||||||
|
assert common.gray2alt("10000000001") == 126700
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
from pyModeS import py_common
|
|
||||||
|
|
||||||
|
|
||||||
def test_conversions():
|
|
||||||
assert py_common.hex2bin("6E") == "01101110"
|
|
||||||
assert py_common.bin2hex("01101110") == "6E"
|
|
||||||
assert py_common.bin2hex("1101110") == "6E"
|
|
||||||
|
|
||||||
|
|
||||||
def test_crc_decode():
|
|
||||||
assert py_common.crc_legacy("8D406B902015A678D4D220AA4BDA") == 0
|
|
||||||
|
|
||||||
assert py_common.crc("8D406B902015A678D4D220AA4BDA") == 0
|
|
||||||
assert py_common.crc("8d8960ed58bf053cf11bc5932b7d") == 0
|
|
||||||
assert py_common.crc("8d45cab390c39509496ca9a32912") == 0
|
|
||||||
assert py_common.crc("8d49d3d4e1089d00000000744c3b") == 0
|
|
||||||
assert py_common.crc("8d74802958c904e6ef4ba0184d5c") == 0
|
|
||||||
assert py_common.crc("8d4400cd9b0000b4f87000e71a10") == 0
|
|
||||||
assert py_common.crc("8d4065de58a1054a7ef0218e226a") == 0
|
|
||||||
|
|
||||||
assert py_common.crc("c80b2dca34aa21dd821a04cb64d4") == 10719924
|
|
||||||
assert py_common.crc("a800089d8094e33a6004e4b8a522") == 4805588
|
|
||||||
assert py_common.crc("a8000614a50b6d32bed000bbe0ed") == 5659991
|
|
||||||
assert py_common.crc("a0000410bc900010a40000f5f477") == 11727682
|
|
||||||
assert py_common.crc("8d4ca251204994b1c36e60a5343d") == 16
|
|
||||||
assert py_common.crc("b0001718c65632b0a82040715b65") == 353333
|
|
||||||
|
|
||||||
|
|
||||||
def test_crc_encode():
|
|
||||||
parity = py_common.crc("8D406B902015A678D4D220AA4BDA", encode=True)
|
|
||||||
assert parity == 11160538
|
|
||||||
|
|
||||||
|
|
||||||
def test_icao():
|
|
||||||
assert py_common.icao("8D406B902015A678D4D220AA4BDA") == "406B90"
|
|
||||||
assert py_common.icao("A0001839CA3800315800007448D9") == "400940"
|
|
||||||
assert py_common.icao("A000139381951536E024D4CCF6B5") == "3C4DD2"
|
|
||||||
assert py_common.icao("A000029CFFBAA11E2004727281F1") == "4243D0"
|
|
||||||
|
|
||||||
|
|
||||||
def test_modes_altcode():
|
|
||||||
assert py_common.altcode("A02014B400000000000000F9D514") == 32300
|
|
||||||
|
|
||||||
|
|
||||||
def test_modes_idcode():
|
|
||||||
assert py_common.idcode("A800292DFFBBA9383FFCEB903D01") == "1346"
|
|
||||||
|
|
||||||
|
|
||||||
def test_graycode_to_altitude():
|
|
||||||
assert py_common.gray2alt("00000000010") == -1000
|
|
||||||
assert py_common.gray2alt("00000001010") == -500
|
|
||||||
assert py_common.gray2alt("00000011011") == -100
|
|
||||||
assert py_common.gray2alt("00000011010") == 0
|
|
||||||
assert py_common.gray2alt("00000011110") == 100
|
|
||||||
assert py_common.gray2alt("00000010011") == 600
|
|
||||||
assert py_common.gray2alt("00000110010") == 1000
|
|
||||||
assert py_common.gray2alt("00001001001") == 5800
|
|
||||||
assert py_common.gray2alt("00011100100") == 10300
|
|
||||||
assert py_common.gray2alt("01100011010") == 32000
|
|
||||||
assert py_common.gray2alt("01110000100") == 46300
|
|
||||||
assert py_common.gray2alt("01010101100") == 50200
|
|
||||||
assert py_common.gray2alt("11011110100") == 73200
|
|
||||||
assert py_common.gray2alt("10000000011") == 126600
|
|
||||||
assert py_common.gray2alt("10000000001") == 126700
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
from pyModeS import surv
|
|
||||||
|
|
||||||
|
|
||||||
def test_fs():
|
|
||||||
assert surv.fs("2A00516D492B80")[0] == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_dr():
|
|
||||||
assert surv.dr("2A00516D492B80")[0] == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_um():
|
|
||||||
assert surv.um("200CBE4ED80137")[0] == 9
|
|
||||||
assert surv.um("200CBE4ED80137")[1] == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_identity():
|
|
||||||
assert surv.identity("2A00516D492B80") == "0356"
|
|
||||||
|
|
||||||
|
|
||||||
def test_altitude():
|
|
||||||
assert surv.altitude("20001718029FCD") == 36000
|
|
||||||
Reference in New Issue
Block a user