Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
228306dd5f | ||
|
|
1c6b39322f | ||
|
|
9dde1b63d6 | ||
|
|
560b737739 | ||
|
|
d50f05d00d | ||
|
|
11a560dbb5 | ||
|
|
b51facbdc6 | ||
|
|
d77d1f7f6b | ||
|
|
5286355bf6 | ||
|
|
fe9e033a64 | ||
|
|
57307109e6 | ||
|
|
6571fe6fc0 | ||
|
|
aa64d4e7a9 | ||
|
|
db57d7419f | ||
|
|
ed18352c0c | ||
|
|
a75d6fd050 | ||
|
|
0ff628bb8e | ||
|
|
ea7653ef79 | ||
|
|
2046b1de07 | ||
|
|
768b80df8e | ||
|
|
e52d43f963 | ||
|
|
b60b31d4fb | ||
|
|
cb67c63326 | ||
|
|
bddaf9aec6 | ||
|
|
00fc1475ff | ||
|
|
489c405de0 | ||
|
|
ec2721cfdc | ||
|
|
2f13524a7c |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,6 +5,9 @@ __pycache__/
|
||||
*.py[cod]
|
||||
.pytest_cache/
|
||||
|
||||
#cython
|
||||
*.c
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
|
||||
20
Makefile
Normal file
20
Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
install:
|
||||
pip install . --upgrade
|
||||
|
||||
uninstall:
|
||||
pip uninstall pyModeS -y
|
||||
|
||||
ext:
|
||||
python setup.py build_ext --inplace
|
||||
|
||||
test:
|
||||
python setup.py build_ext --inplace
|
||||
python -m pytest tests
|
||||
|
||||
clean:
|
||||
find pyModeS -type f -name '*.c' -delete
|
||||
find pyModeS -type f -name '*.so' -delete
|
||||
find . | grep -E "(__pycache__|\.pyc|\.pyo$$)" | xargs rm -rf
|
||||
rm -rf *.egg-info
|
||||
rm -rf .pytest_cache
|
||||
rm -rf build/*
|
||||
109
README.rst
109
README.rst
@@ -1,6 +1,42 @@
|
||||
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::
|
||||
|
||||
@article{sun2019pymodes,
|
||||
@@ -14,40 +50,6 @@ 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
|
||||
-----------
|
||||
@@ -55,15 +57,15 @@ Check out and contribute to this open-source project at:
|
||||
https://github.com/junzis/pyModeS
|
||||
|
||||
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:
|
||||
http://pymodes.readthedocs.io
|
||||
https://mode-s.org/api
|
||||
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
Basic installation
|
||||
-------------------
|
||||
|
||||
Installation examples::
|
||||
|
||||
@@ -76,6 +78,16 @@ Installation examples::
|
||||
|
||||
Dependencies ``numpy``, ``pyzmq`` and ``pyrtlsdr`` are installed automatically during previous installations processes.
|
||||
|
||||
Advanced installation (using c modules)
|
||||
------------------------------------------
|
||||
|
||||
If you want to make use of the (faster) c module, install ``pyModeS`` as follows::
|
||||
|
||||
git clone https://github.com/junzis/pyModeS
|
||||
cd pyModeS
|
||||
make ext
|
||||
make install
|
||||
|
||||
|
||||
View live traffic (modeslive)
|
||||
----------------------------------------------------
|
||||
@@ -141,7 +153,7 @@ Common functions
|
||||
pms.hex2bin(str) # Convert hexadecimal string to binary string
|
||||
pms.bin2int(str) # Convert binary string to integer
|
||||
pms.hex2int(str) # Convert hexadecimal string to integer
|
||||
pms.gray2int(str) # Convert grey code to interger
|
||||
pms.gray2int(str) # Convert grey code to integer
|
||||
|
||||
|
||||
Core functions for ADS-B decoding
|
||||
@@ -155,7 +167,7 @@ Core functions for ADS-B decoding
|
||||
# Typecode 1-4
|
||||
pms.adsb.callsign(msg)
|
||||
|
||||
# Typecode 5-8 (surface), 9-18 (airborne, barometric height), and 9-18 (airborne, GNSS height)
|
||||
# Typecode 5-8 (surface), 9-18 (airborne, barometric height), and 20-22 (airborne, GNSS height)
|
||||
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.surface_position(msg_even, msg_odd, t_even, t_odd, lat_ref, lon_ref)
|
||||
@@ -321,8 +333,19 @@ Here is an example:
|
||||
|
||||
Unit test
|
||||
---------
|
||||
To perform unit tests. First, install ``tox`` through pip. Then, run the following commands:
|
||||
To perform unit tests, ``pytest`` must be install first.
|
||||
|
||||
.. code:: bash
|
||||
Build Cython extensions
|
||||
::
|
||||
|
||||
$ tox
|
||||
$ make ext
|
||||
|
||||
Run unit tests
|
||||
::
|
||||
|
||||
$ make test
|
||||
|
||||
Clean build files
|
||||
::
|
||||
|
||||
$ make clean
|
||||
|
||||
@@ -17,5 +17,5 @@ help:
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
rm -f source/pyModeS*.rst source/modules.rst
|
||||
sphinx-apidoc -f -e -M -o source/ ../pyModeS
|
||||
sphinx-apidoc -f -e -M -o source/ ../pyModeS ../pyModeS/decoder/ehs.py ../pyModeS/decoder/els.py ../pyModeS/streamer ../pyModeS/extra
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
17
doc/source/_templates/layout.html
Normal file
17
doc/source/_templates/layout.html
Normal file
@@ -0,0 +1,17 @@
|
||||
{% 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,19 +14,20 @@
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../.."))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'pyModeS'
|
||||
copyright = '2019, Junzi Sun'
|
||||
author = 'Junzi Sun'
|
||||
project = "pyModeS"
|
||||
copyright = "2019, Junzi Sun"
|
||||
author = "Junzi Sun"
|
||||
|
||||
# The short X.Y version
|
||||
version = ''
|
||||
version = ""
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = ''
|
||||
release = ""
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
@@ -39,26 +40,24 @@ release = ''
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.mathjax',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.githubpages',
|
||||
'sphinx.ext.napoleon',
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.mathjax",
|
||||
"sphinx.ext.viewcode",
|
||||
"sphinx.ext.githubpages",
|
||||
"sphinx.ext.napoleon",
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
# templates_path = ['']
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
source_suffix = ".rst"
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
master_doc = "index"
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@@ -82,6 +81,10 @@ pygments_style = None
|
||||
# a list of builtin themes.
|
||||
#
|
||||
# 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
|
||||
# further. For a list of options available for each theme, see the
|
||||
@@ -108,7 +111,7 @@ pygments_style = None
|
||||
# -- Options for HTMLHelp output ---------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'pyModeSdoc'
|
||||
htmlhelp_basename = "pyModeSdoc"
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
@@ -117,15 +120,12 @@ latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
@@ -135,8 +135,7 @@ latex_elements = {
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'pyModeS.tex', 'pyModeS Documentation',
|
||||
'Junzi Sun', 'manual'),
|
||||
(master_doc, "pyModeS.tex", "pyModeS Documentation", "Junzi Sun", "manual")
|
||||
]
|
||||
|
||||
|
||||
@@ -144,10 +143,7 @@ latex_documents = [
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (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 ----------------------------------------------
|
||||
@@ -156,9 +152,15 @@ man_pages = [
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'pyModeS', 'pyModeS Documentation',
|
||||
author, 'pyModeS', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
(
|
||||
master_doc,
|
||||
"pyModeS",
|
||||
"pyModeS Documentation",
|
||||
author,
|
||||
"pyModeS",
|
||||
"One line description of project.",
|
||||
"Miscellaneous",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@@ -177,7 +179,7 @@ epub_title = project
|
||||
# epub_uid = ''
|
||||
|
||||
# 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 -------------------------------------------------
|
||||
|
||||
@@ -9,11 +9,49 @@ Welcome to pyModeS documentation!
|
||||
The source code can be found at: https://github.com/junzis/pyModeS
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:caption: Core modules
|
||||
: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
|
||||
|
||||
|
||||
----
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
@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
|
||||
7
doc/source/pyModeS.c_common.rst
Normal file
7
doc/source/pyModeS.c_common.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
pyModeS.c\_common module
|
||||
========================
|
||||
|
||||
.. automodule:: pyModeS.c_common
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
doc/source/pyModeS.common.rst
Normal file
7
doc/source/pyModeS.common.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
pyModeS.common module
|
||||
=====================
|
||||
|
||||
.. automodule:: pyModeS.common
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.acas module
|
||||
===========================
|
||||
|
||||
.. automodule:: pyModeS.decoder.acas
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.adsb module
|
||||
===========================
|
||||
|
||||
.. automodule:: pyModeS.decoder.adsb
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.allcall module
|
||||
==============================
|
||||
|
||||
.. automodule:: pyModeS.decoder.allcall
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds05 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds05
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds06 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds06
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds08 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds08
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds09 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds09
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds10 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds10
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds17 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds17
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds20 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds20
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds30 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds30
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds40 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds40
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds44 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds44
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds45 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds45
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds50 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds50
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds53 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds53
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.bds.bds60 module
|
||||
================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds.bds60
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,14 +2,15 @@ pyModeS.decoder.bds package
|
||||
===========================
|
||||
|
||||
.. automodule:: pyModeS.decoder.bds
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
pyModeS.decoder.bds.bds05
|
||||
pyModeS.decoder.bds.bds06
|
||||
@@ -25,4 +26,3 @@ Submodules
|
||||
pyModeS.decoder.bds.bds50
|
||||
pyModeS.decoder.bds.bds53
|
||||
pyModeS.decoder.bds.bds60
|
||||
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.commb module
|
||||
============================
|
||||
|
||||
.. automodule:: pyModeS.decoder.commb
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
pyModeS.decoder.common module
|
||||
=============================
|
||||
|
||||
.. automodule:: pyModeS.decoder.common
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -1,7 +0,0 @@
|
||||
pyModeS.decoder.ehs module
|
||||
==========================
|
||||
|
||||
.. automodule:: pyModeS.decoder.ehs
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -1,7 +0,0 @@
|
||||
pyModeS.decoder.els module
|
||||
==========================
|
||||
|
||||
.. automodule:: pyModeS.decoder.els
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -2,29 +2,28 @@ pyModeS.decoder package
|
||||
=======================
|
||||
|
||||
.. automodule:: pyModeS.decoder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
pyModeS.decoder.bds
|
||||
pyModeS.decoder.bds
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
pyModeS.decoder.acas
|
||||
pyModeS.decoder.adsb
|
||||
pyModeS.decoder.allcall
|
||||
pyModeS.decoder.commb
|
||||
pyModeS.decoder.common
|
||||
pyModeS.decoder.ehs
|
||||
pyModeS.decoder.els
|
||||
pyModeS.decoder.surv
|
||||
pyModeS.decoder.uncertainty
|
||||
|
||||
pyModeS.decoder.uplink
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.surv module
|
||||
===========================
|
||||
|
||||
.. automodule:: pyModeS.decoder.surv
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -2,6 +2,6 @@ pyModeS.decoder.uncertainty module
|
||||
==================================
|
||||
|
||||
.. automodule:: pyModeS.decoder.uncertainty
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
7
doc/source/pyModeS.decoder.uplink.rst
Normal file
7
doc/source/pyModeS.decoder.uplink.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
pyModeS.decoder.uplink module
|
||||
=============================
|
||||
|
||||
.. automodule:: pyModeS.decoder.uplink
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -1,7 +0,0 @@
|
||||
pyModeS.extra.aero module
|
||||
=========================
|
||||
|
||||
.. automodule:: pyModeS.extra.aero
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -1,16 +0,0 @@
|
||||
pyModeS.extra package
|
||||
=====================
|
||||
|
||||
.. automodule:: pyModeS.extra
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
|
||||
pyModeS.extra.aero
|
||||
pyModeS.extra.tcpclient
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
pyModeS.extra.tcpclient module
|
||||
==============================
|
||||
|
||||
.. automodule:: pyModeS.extra.tcpclient
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -2,16 +2,23 @@ pyModeS package
|
||||
===============
|
||||
|
||||
.. automodule:: pyModeS
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
pyModeS.decoder
|
||||
pyModeS.extra
|
||||
pyModeS.streamer
|
||||
pyModeS.decoder
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
pyModeS.c_common
|
||||
pyModeS.common
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
pyModeS.streamer package
|
||||
========================
|
||||
|
||||
.. automodule:: pyModeS.streamer
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
|
||||
pyModeS.streamer.screen
|
||||
pyModeS.streamer.stream
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
pyModeS.streamer.screen module
|
||||
==============================
|
||||
|
||||
.. automodule:: pyModeS.streamer.screen
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -1,7 +0,0 @@
|
||||
pyModeS.streamer.stream module
|
||||
==============================
|
||||
|
||||
.. automodule:: pyModeS.streamer.stream
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
0
doc/warnings
Normal file
0
doc/warnings
Normal file
@@ -1,17 +1,21 @@
|
||||
from __future__ import absolute_import, print_function, division
|
||||
|
||||
import os
|
||||
import warnings
|
||||
|
||||
from .decoder.common import *
|
||||
try:
|
||||
from . import c_common as common
|
||||
from .c_common import *
|
||||
except:
|
||||
from . import py_common as common
|
||||
from .py_common import *
|
||||
|
||||
from .decoder import tell
|
||||
from .decoder import adsb
|
||||
from .decoder import commb
|
||||
from .decoder import common
|
||||
from .decoder import bds
|
||||
from .extra import aero
|
||||
from .extra import tcpclient
|
||||
|
||||
|
||||
warnings.simplefilter("once", DeprecationWarning)
|
||||
|
||||
dirpath = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
24
pyModeS/c_common.pxd
Normal file
24
pyModeS/c_common.pxd
Normal file
@@ -0,0 +1,24 @@
|
||||
# cython: language_level=3
|
||||
|
||||
cdef int char_to_int(unsigned char binstr)
|
||||
cdef unsigned char int_to_char(unsigned char i)
|
||||
|
||||
cpdef str hex2bin(str hexstr)
|
||||
cpdef long bin2int(str binstr)
|
||||
cpdef long hex2int(str hexstr)
|
||||
cpdef str bin2hex(str binstr)
|
||||
|
||||
cpdef unsigned char df(str msg)
|
||||
cpdef long crc(str msg, bint encode=*)
|
||||
|
||||
cpdef long floor(double x)
|
||||
cpdef str icao(str msg)
|
||||
cpdef bint is_icao_assigned(str icao)
|
||||
|
||||
cpdef int typecode(str msg)
|
||||
cpdef int cprNL(double lat)
|
||||
cpdef str idcode(str msg)
|
||||
cpdef int altcode(str msg)
|
||||
|
||||
cpdef str data(str msg)
|
||||
cpdef bint allzeros(str msg)
|
||||
434
pyModeS/c_common.pyx
Normal file
434
pyModeS/c_common.pyx
Normal file
@@ -0,0 +1,434 @@
|
||||
# cython: language_level=3
|
||||
|
||||
cimport cython
|
||||
from cpython cimport array
|
||||
from cpython.bytes cimport PyBytes_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
|
||||
|
||||
|
||||
cdef int char_to_int(unsigned char binstr):
|
||||
if 48 <= binstr <= 57: # 0 to 9
|
||||
return binstr - 48
|
||||
if 97 <= binstr <= 102: # a to f
|
||||
return binstr - 97 + 10
|
||||
if 65 <= binstr <= 70: # A to F
|
||||
return binstr - 65 + 10
|
||||
return 0
|
||||
|
||||
cdef unsigned char int_to_char(unsigned char i):
|
||||
if i < 10:
|
||||
return 48 + i # "0" + i
|
||||
return 97 - 10 + i # "a" - 10 + i
|
||||
|
||||
@cython.boundscheck(False)
|
||||
@cython.overflowcheck(False)
|
||||
cpdef str hex2bin(str hexstr):
|
||||
"""Convert a hexdecimal string to binary string, with zero fillings."""
|
||||
# num_of_bits = len(hexstr) * 4
|
||||
cdef hexbytes = bytes(hexstr.encode())
|
||||
cdef Py_ssize_t len_hexstr = PyBytes_GET_SIZE(hexbytes)
|
||||
# binstr = bin(int(hexbytes, 16))[2:].zfill(int(num_of_bits))
|
||||
cdef bytearray _binstr = bytearray(4 * len_hexstr)
|
||||
cdef unsigned char[:] binstr = _binstr
|
||||
cdef unsigned char int_
|
||||
cdef Py_ssize_t i
|
||||
for i in range(len_hexstr):
|
||||
int_ = char_to_int(hexbytes[i])
|
||||
binstr[4*i] = int_to_char((int_ >> 3) & 1)
|
||||
binstr[4*i+1] = int_to_char((int_ >> 2) & 1)
|
||||
binstr[4*i+2] = int_to_char((int_ >> 1) & 1)
|
||||
binstr[4*i+3] = int_to_char((int_) & 1)
|
||||
return _binstr.decode()
|
||||
|
||||
@cython.boundscheck(False)
|
||||
cpdef long bin2int(str binstr):
|
||||
"""Convert a binary string to integer."""
|
||||
# return int(binstr, 2)
|
||||
cdef bytearray binbytes = bytearray(binstr.encode())
|
||||
cdef Py_ssize_t len_ = PyByteArray_GET_SIZE(binbytes)
|
||||
cdef long cumul = 0
|
||||
cdef unsigned char[:] v_binstr = binbytes
|
||||
for i in range(len_):
|
||||
cumul = 2*cumul + char_to_int(v_binstr[i])
|
||||
return cumul
|
||||
|
||||
@cython.boundscheck(False)
|
||||
cpdef long hex2int(str hexstr):
|
||||
"""Convert a binary string to integer."""
|
||||
# return int(hexstr, 2)
|
||||
cdef bytearray binbytes = bytearray(hexstr.encode())
|
||||
cdef Py_ssize_t len_ = PyByteArray_GET_SIZE(binbytes)
|
||||
cdef long cumul = 0
|
||||
cdef unsigned char[:] v_hexstr = binbytes
|
||||
for i in range(len_):
|
||||
cumul = 16*cumul + char_to_int(v_hexstr[i])
|
||||
return cumul
|
||||
|
||||
@cython.boundscheck(False)
|
||||
cpdef str bin2hex(str binstr):
|
||||
return "{0:X}".format(int(binstr, 2))
|
||||
|
||||
|
||||
@cython.boundscheck(False)
|
||||
cpdef unsigned char df(str msg):
|
||||
"""Decode Downlink Format vaule, bits 1 to 5."""
|
||||
cdef str dfbin = hex2bin(msg[:2])
|
||||
# return min(bin2int(dfbin[0:5]), 24)
|
||||
cdef long df = bin2int(dfbin[0:5])
|
||||
if df > 24:
|
||||
return 24
|
||||
return df
|
||||
|
||||
# the CRC generator
|
||||
# G = [int("11111111", 2), int("11111010", 2), int("00000100", 2), int("10000000", 2)]
|
||||
cdef array.array _G = array.array('l', [0b11111111, 0b11111010, 0b00000100, 0b10000000])
|
||||
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cpdef long crc(str msg, bint encode=False):
|
||||
"""Mode-S Cyclic Redundancy Check.
|
||||
|
||||
Detect if bit error occurs in the Mode-S message. When encode option is on,
|
||||
the checksum is generated.
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
encode (bool): True to encode the date only and return the checksum
|
||||
Returns:
|
||||
int: message checksum, or partity bits (encoder)
|
||||
|
||||
"""
|
||||
# the CRC generator
|
||||
# G = [int("11111111", 2), int("11111010", 2), int("00000100", 2), int("10000000", 2)]
|
||||
# cdef array.array _G = array.array('l', [0b11111111, 0b11111010, 0b00000100, 0b10000000])
|
||||
cdef long[4] G = _G
|
||||
|
||||
# msgbin_split = wrap(msgbin, 8)
|
||||
# mbytes = list(map(bin2int, msgbin_split))
|
||||
cdef bytearray _msgbin = bytearray(hex2bin(msg).encode())
|
||||
cdef unsigned char[:] msgbin = _msgbin
|
||||
|
||||
cdef Py_ssize_t len_msgbin = PyByteArray_GET_SIZE(_msgbin)
|
||||
cdef Py_ssize_t len_mbytes = len_msgbin // 8
|
||||
cdef Py_ssize_t i
|
||||
|
||||
if encode:
|
||||
for i in range(len_msgbin - 24, len_msgbin):
|
||||
msgbin[i] = 0
|
||||
|
||||
cdef array.array _mbytes = array.array(
|
||||
'l', [bin2int(_msgbin[8*i:8*i+8].decode()) for i in range(len_mbytes)]
|
||||
)
|
||||
|
||||
cdef long[:] mbytes = _mbytes
|
||||
|
||||
cdef long bits, mask
|
||||
cdef Py_ssize_t ibyte, ibit
|
||||
|
||||
for ibyte in range(len_mbytes - 3):
|
||||
for ibit in range(8):
|
||||
mask = 0x80 >> ibit
|
||||
bits = mbytes[ibyte] & mask
|
||||
|
||||
if bits > 0:
|
||||
mbytes[ibyte] = mbytes[ibyte] ^ (G[0] >> ibit)
|
||||
mbytes[ibyte + 1] = mbytes[ibyte + 1] ^ (
|
||||
0xFF & ((G[0] << 8 - ibit) | (G[1] >> ibit))
|
||||
)
|
||||
mbytes[ibyte + 2] = mbytes[ibyte + 2] ^ (
|
||||
0xFF & ((G[1] << 8 - ibit) | (G[2] >> ibit))
|
||||
)
|
||||
mbytes[ibyte + 3] = mbytes[ibyte + 3] ^ (
|
||||
0xFF & ((G[2] << 8 - ibit) | (G[3] >> ibit))
|
||||
)
|
||||
|
||||
cdef long result = (mbytes[len_mbytes-3] << 16) | (mbytes[len_mbytes-2] << 8) | mbytes[len_mbytes-1]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
||||
cpdef long floor(double x):
|
||||
"""Mode-S floor function.
|
||||
|
||||
Defined as the greatest integer value k, such that k <= x
|
||||
For example: floor(3.6) = 3 and floor(-3.6) = -4
|
||||
|
||||
"""
|
||||
return <long> c_floor(x)
|
||||
|
||||
cpdef str icao(str msg):
|
||||
"""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 long c0, c1
|
||||
|
||||
if DF in (11, 17, 18):
|
||||
addr = msg[2:8]
|
||||
elif DF in (0, 4, 5, 16, 20, 21):
|
||||
c0 = crc(msg, encode=True)
|
||||
c1 = hex2int(msg[-6:])
|
||||
addr = "%06X" % (c0 ^ c1)
|
||||
else:
|
||||
addr = None
|
||||
|
||||
return addr
|
||||
|
||||
|
||||
cpdef bint is_icao_assigned(str icao):
|
||||
"""Check whether the ICAO address is assigned (Annex 10, Vol 3)."""
|
||||
if (icao is None) or (not isinstance(icao, str)) or (len(icao) != 6):
|
||||
return False
|
||||
|
||||
cdef long icaoint = hex2int(icao)
|
||||
|
||||
if 0x200000 < icaoint < 0x27FFFF:
|
||||
return False # AFI
|
||||
if 0x280000 < icaoint < 0x28FFFF:
|
||||
return False # SAM
|
||||
if 0x500000 < icaoint < 0x5FFFFF:
|
||||
return False # EUR, NAT
|
||||
if 0x600000 < icaoint < 0x67FFFF:
|
||||
return False # MID
|
||||
if 0x680000 < icaoint < 0x6F0000:
|
||||
return False # ASIA
|
||||
if 0x900000 < icaoint < 0x9FFFFF:
|
||||
return False # NAM, PAC
|
||||
if 0xB00000 < icaoint < 0xBFFFFF:
|
||||
return False # CAR
|
||||
if 0xD00000 < icaoint < 0xDFFFFF:
|
||||
return False # future
|
||||
if 0xF00000 < icaoint < 0xFFFFFF:
|
||||
return False # future
|
||||
|
||||
return True
|
||||
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cpdef int typecode(str msg):
|
||||
"""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):
|
||||
return -1
|
||||
# return None
|
||||
|
||||
cdef str tcbin = hex2bin(msg[8:10])
|
||||
return bin2int(tcbin[0:5])
|
||||
|
||||
@cython.cdivision(True)
|
||||
cpdef int cprNL(double lat):
|
||||
"""NL() function in CPR decoding."""
|
||||
|
||||
if abs(lat) <= 1e-08:
|
||||
return 59
|
||||
elif abs(abs(lat) - 87) <= 1e-08 + 1e-05 * 87:
|
||||
return 2
|
||||
elif lat > 87 or lat < -87:
|
||||
return 1
|
||||
|
||||
cdef int nz = 15
|
||||
cdef double a = 1 - cos(pi / (2 * nz))
|
||||
cdef double b = cos(pi / 180.0 * fabs(lat)) ** 2
|
||||
cdef double nl = 2 * pi / (acos(1 - a / b))
|
||||
NL = floor(nl)
|
||||
return NL
|
||||
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cpdef str idcode(str msg):
|
||||
"""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]:
|
||||
raise RuntimeError("Message must be Downlink Format 5 or 21.")
|
||||
|
||||
cdef bytearray _mbin = bytearray(hex2bin(msg).encode())
|
||||
cdef unsigned char[:] mbin = _mbin
|
||||
|
||||
cdef bytearray _idcode = bytearray(4)
|
||||
cdef unsigned char[:] idcode = _idcode
|
||||
|
||||
cdef unsigned char C1 = mbin[19]
|
||||
cdef unsigned char A1 = mbin[20]
|
||||
cdef unsigned char C2 = mbin[21]
|
||||
cdef unsigned char A2 = mbin[22]
|
||||
cdef unsigned char C4 = mbin[23]
|
||||
cdef unsigned char A4 = mbin[24]
|
||||
# _ = mbin[25]
|
||||
cdef unsigned char B1 = mbin[26]
|
||||
cdef unsigned char D1 = mbin[27]
|
||||
cdef unsigned char B2 = mbin[28]
|
||||
cdef unsigned char D2 = mbin[29]
|
||||
cdef unsigned char B4 = mbin[30]
|
||||
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[1] = int_to_char((char_to_int(B4)*2 + char_to_int(B2))*2 + char_to_int(B1))
|
||||
idcode[2] = int_to_char((char_to_int(C4)*2 + char_to_int(C2))*2 + char_to_int(C1))
|
||||
idcode[3] = int_to_char((char_to_int(D4)*2 + char_to_int(D2))*2 + char_to_int(D1))
|
||||
|
||||
return _idcode.decode()
|
||||
|
||||
#return str(byte1) + str(byte2) + str(byte3) + str(byte4)
|
||||
|
||||
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cpdef int altcode(str msg):
|
||||
"""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]:
|
||||
raise RuntimeError("Message must be Downlink Format 0, 4, 16, or 20.")
|
||||
|
||||
# Altitude code, bit 20-32
|
||||
cdef bytearray _mbin = bytearray(hex2bin(msg).encode())
|
||||
cdef unsigned char[:] mbin = _mbin
|
||||
|
||||
cdef char mbit = mbin[25] # M bit: 26
|
||||
cdef char qbit = mbin[27] # Q bit: 28
|
||||
cdef int alt = 0
|
||||
cdef bytearray vbin
|
||||
cdef bytearray _graybytes = bytearray(11)
|
||||
cdef unsigned char[:] graybytes = _graybytes
|
||||
|
||||
if mbit == 48: # unit in ft, "0" -> 48
|
||||
if qbit == 49: # 25ft interval, "1" -> 49
|
||||
vbin = _mbin[19:25] + _mbin[26:27] + _mbin[28:32]
|
||||
alt = bin2int(vbin.decode()) * 25 - 1000
|
||||
if qbit == 48: # 100ft interval, above 50175ft, "0" -> 48
|
||||
graybytes[8] = mbin[19]
|
||||
graybytes[2] = mbin[20]
|
||||
graybytes[9] = mbin[21]
|
||||
graybytes[3] = mbin[22]
|
||||
graybytes[10] = mbin[23]
|
||||
graybytes[4] = mbin[24]
|
||||
# _ = mbin[25]
|
||||
graybytes[5] = mbin[26]
|
||||
# cdef char D1 = mbin[27] # always zero
|
||||
graybytes[6] = mbin[28]
|
||||
graybytes[0] = mbin[29]
|
||||
graybytes[7] = mbin[30]
|
||||
graybytes[1] = mbin[31]
|
||||
# graybytes = D2 + D4 + A1 + A2 + A4 + B1 + B2 + B4 + C1 + C2 + C4
|
||||
|
||||
alt = gray2alt(_graybytes.decode())
|
||||
|
||||
if mbit == 49: # unit in meter, "1" -> 49
|
||||
vbin = _mbin[19:25] + _mbin[26:31]
|
||||
alt = int(bin2int(vbin.decode()) * 3.28084) # convert to ft
|
||||
|
||||
return alt
|
||||
|
||||
|
||||
|
||||
cpdef int gray2alt(str codestr):
|
||||
cdef str gc500 = codestr[:8]
|
||||
cdef int n500 = gray2int(gc500)
|
||||
|
||||
# in 100-ft step must be converted first
|
||||
cdef str gc100 = codestr[8:]
|
||||
cdef int n100 = gray2int(gc100)
|
||||
|
||||
if n100 in [0, 5, 6]:
|
||||
return -1
|
||||
#return None
|
||||
|
||||
if n100 == 7:
|
||||
n100 = 5
|
||||
|
||||
if n500 % 2:
|
||||
n100 = 6 - n100
|
||||
|
||||
alt = (n500 * 500 + n100 * 100) - 1300
|
||||
return alt
|
||||
|
||||
|
||||
cdef int gray2int(str graystr):
|
||||
"""Convert greycode to binary."""
|
||||
cdef int num = bin2int(graystr)
|
||||
num ^= num >> 8
|
||||
num ^= num >> 4
|
||||
num ^= num >> 2
|
||||
num ^= num >> 1
|
||||
return num
|
||||
|
||||
|
||||
cpdef str data(str msg):
|
||||
"""Return the data frame in the message, bytes 9 to 22."""
|
||||
return msg[8:-6]
|
||||
|
||||
|
||||
cpdef bint allzeros(str msg):
|
||||
"""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))
|
||||
|
||||
if bin2int(d) > 0:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def wrongstatus(data, sb, msb, lsb):
|
||||
"""Check if the status bit and field bits are consistency.
|
||||
|
||||
This Function is used for checking BDS code versions.
|
||||
|
||||
"""
|
||||
# status bit, most significant bit, least significant bit
|
||||
status = int(data[sb - 1])
|
||||
value = bin2int(data[msb - 1 : lsb])
|
||||
|
||||
if not status:
|
||||
if value != 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -1,9 +1,6 @@
|
||||
from __future__ import absolute_import, print_function, division
|
||||
|
||||
from pyModeS.decoder import adsb, commb, common, bds
|
||||
|
||||
|
||||
def tell(msg):
|
||||
from pyModeS import common, adsb, commb, bds
|
||||
|
||||
def _print(label, value, unit=None):
|
||||
print("%20s: " % label, end="")
|
||||
print("%s " % value, end="")
|
||||
@@ -20,7 +17,7 @@ def tell(msg):
|
||||
_print("Downlink Format", df)
|
||||
|
||||
if df == 17:
|
||||
_print("Protocal", "Mode-S Extended Squitter (ADS-B)")
|
||||
_print("Protocol", "Mode-S Extended Squitter (ADS-B)")
|
||||
|
||||
tc = common.typecode(msg)
|
||||
if 1 <= tc <= 4: # callsign
|
||||
@@ -29,7 +26,7 @@ def tell(msg):
|
||||
_print("Callsign:", callsign)
|
||||
|
||||
if 5 <= tc <= 8: # surface position
|
||||
_print("Type", "Surface postition")
|
||||
_print("Type", "Surface position")
|
||||
oe = adsb.oe_flag(msg)
|
||||
msgbin = common.hex2bin(msg)
|
||||
cprlat = common.bin2int(msgbin[54:71]) / 131072.0
|
||||
@@ -75,11 +72,11 @@ def tell(msg):
|
||||
_print("Altitude", alt, "feet")
|
||||
|
||||
if df == 20:
|
||||
_print("Protocal", "Mode-S Comm-B altitude reply")
|
||||
_print("Protocol", "Mode-S Comm-B altitude reply")
|
||||
_print("Altitude", common.altcode(msg), "feet")
|
||||
|
||||
if df == 21:
|
||||
_print("Protocal", "Mode-S Comm-B identity reply")
|
||||
_print("Protocol", "Mode-S Comm-B identity reply")
|
||||
_print("Squawk code", common.idcode(msg))
|
||||
|
||||
if df == 20 or df == 21:
|
||||
|
||||
@@ -1,23 +1,5 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Decoding Air-Air Surveillance (ACAS) DF=0/16
|
||||
|
||||
[To be implemented]
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder import common
|
||||
|
||||
@@ -1,44 +1,25 @@
|
||||
# Copyright (C) 2015 Junzi Sun (TU Delft)
|
||||
"""ADS-B module.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
The ADS-B module also imports functions from the following modules:
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""ADS-B Wrapper.
|
||||
|
||||
The ADS-B wrapper also imports functions from the following modules:
|
||||
|
||||
- pyModeS.decoder.bds.bds05
|
||||
Functions: ``airborne_position``, ``airborne_position_with_ref``, ``altitude``
|
||||
- pyModeS.decoder.bds.bds06
|
||||
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``
|
||||
- pyModeS.decoder.bds.bds05: ``airborne_position()``, ``airborne_position_with_ref()``, ``altitude()``
|
||||
- pyModeS.decoder.bds.bds06: ``surface_position()``, ``surface_position_with_ref()``, ``surface_velocity()``
|
||||
- pyModeS.decoder.bds.bds08: ``category()``, ``callsign()``
|
||||
- pyModeS.decoder.bds.bds09: ``airborne_velocity()``, ``altitude_diff()``
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
|
||||
import pyModeS as pms
|
||||
from pyModeS.decoder import common
|
||||
|
||||
from pyModeS import common
|
||||
|
||||
from pyModeS.decoder import uncertainty
|
||||
|
||||
# from pyModeS.decoder.bds import bds05, bds06, bds09
|
||||
from pyModeS.decoder.bds.bds05 import (
|
||||
airborne_position,
|
||||
airborne_position_with_ref,
|
||||
altitude,
|
||||
altitude as altitude05,
|
||||
)
|
||||
from pyModeS.decoder.bds.bds06 import (
|
||||
surface_position,
|
||||
@@ -62,27 +43,32 @@ def typecode(msg):
|
||||
|
||||
|
||||
def position(msg0, msg1, t0, t1, lat_ref=None, lon_ref=None):
|
||||
"""Decode position from a pair of even and odd position message
|
||||
(works with both airborne and surface position messages)
|
||||
"""Decode surface or airborne position from a pair of even and odd
|
||||
position messages.
|
||||
|
||||
Note, that to decode surface position using the position message pair,
|
||||
the reference position has to be provided.
|
||||
|
||||
Args:
|
||||
msg0 (string): even message (28 bytes hexadecimal string)
|
||||
msg1 (string): odd message (28 bytes hexadecimal string)
|
||||
msg0 (string): even message (28 hexdigits)
|
||||
msg1 (string): odd message (28 hexdigits)
|
||||
t0 (int): timestamps for the even message
|
||||
t1 (int): timestamps for the odd message
|
||||
lat_ref (float): latitude of reference position
|
||||
lon_ref (float): longitude of reference position
|
||||
|
||||
Returns:
|
||||
(float, float): (latitude, longitude) of the aircraft
|
||||
|
||||
"""
|
||||
tc0 = typecode(msg0)
|
||||
tc1 = typecode(msg1)
|
||||
|
||||
if 5 <= tc0 <= 8 and 5 <= tc1 <= 8:
|
||||
if (not lat_ref) or (not lon_ref):
|
||||
if lat_ref is None or lon_ref is None:
|
||||
raise RuntimeError(
|
||||
"Surface position encountered, a reference \
|
||||
position lat/lon required. Location of \
|
||||
receiver can be used."
|
||||
"Surface position encountered, a reference position"
|
||||
" lat/lon required. Location of receiver can be used."
|
||||
)
|
||||
else:
|
||||
return surface_position(msg0, msg1, t0, t1, lat_ref, lon_ref)
|
||||
@@ -96,19 +82,20 @@ def position(msg0, msg1, t0, t1, lat_ref=None, lon_ref=None):
|
||||
return airborne_position(msg0, msg1, t0, t1)
|
||||
|
||||
else:
|
||||
raise RuntimeError("incorrect or inconsistant message types")
|
||||
raise RuntimeError("Incorrect or inconsistent message types")
|
||||
|
||||
|
||||
def position_with_ref(msg, lat_ref, lon_ref):
|
||||
"""Decode position with only one message,
|
||||
knowing reference nearby location, such as previously
|
||||
calculated location, ground station, or airport location, etc.
|
||||
Works with both airborne and surface position messages.
|
||||
"""Decode position with only one message.
|
||||
|
||||
A reference position is required, which can be previously
|
||||
calculated location, ground station, or airport location.
|
||||
The function works with both airborne and surface position messages.
|
||||
The reference position shall be with in 180NM (airborne) or 45NM (surface)
|
||||
of the true position.
|
||||
|
||||
Args:
|
||||
msg (string): even message (28 bytes hexadecimal string)
|
||||
msg (str): even message (28 hexdigits)
|
||||
lat_ref: previous known latitude
|
||||
lon_ref: previous known longitude
|
||||
|
||||
@@ -125,72 +112,62 @@ def position_with_ref(msg, lat_ref, lon_ref):
|
||||
return airborne_position_with_ref(msg, lat_ref, lon_ref)
|
||||
|
||||
else:
|
||||
raise RuntimeError("incorrect or inconsistant message types")
|
||||
raise RuntimeError("incorrect or inconsistent message types")
|
||||
|
||||
|
||||
def altitude(msg):
|
||||
"""Decode aircraft altitude
|
||||
"""Decode aircraft altitude.
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: altitude in feet
|
||||
"""
|
||||
|
||||
"""
|
||||
tc = typecode(msg)
|
||||
|
||||
if tc < 5 or tc == 19 or tc > 22:
|
||||
raise RuntimeError("%s: Not a position message" % msg)
|
||||
|
||||
if tc >= 5 and tc <= 8:
|
||||
elif tc >= 5 and tc <= 8:
|
||||
# surface position, altitude 0
|
||||
return 0
|
||||
|
||||
msgbin = common.hex2bin(msg)
|
||||
q = msgbin[47]
|
||||
if q:
|
||||
n = common.bin2int(msgbin[40:47] + msgbin[48:52])
|
||||
alt = n * 25 - 1000
|
||||
return alt
|
||||
else:
|
||||
return None
|
||||
# airborn position
|
||||
return altitude05(msg)
|
||||
|
||||
|
||||
def velocity(msg, rtn_sources=False):
|
||||
"""Calculate the speed, heading, and vertical rate
|
||||
(handles both airborne or surface message)
|
||||
def velocity(msg, source=False):
|
||||
"""Calculate the speed, heading, and vertical rate (handles both airborne or surface message).
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
rtn_source (boolean): If the function will return
|
||||
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.
|
||||
msg (str): 28 hexdigits string
|
||||
source (boolean): Include direction and vertical rate sources in return. Default to False.
|
||||
If set to True, the function will return six value instead of four.
|
||||
|
||||
Returns:
|
||||
(int, float, int, string, string, string): speed (kt),
|
||||
ground track or heading (degree),
|
||||
rate of climb/descent (ft/min), speed type
|
||||
('GS' for ground speed, 'AS' for airspeed),
|
||||
direction source ('true_north' for ground track / true north
|
||||
as refrence, 'mag_north' for magnetic north as reference),
|
||||
rate of climb/descent source ('Baro' for barometer, 'GNSS'
|
||||
for GNSS constellation).
|
||||
|
||||
In the case of surface messages, None will be put in place
|
||||
for vertical rate and its respective sources.
|
||||
"""
|
||||
int, float, int, string, [string], [string]: Four or six parameters, including:
|
||||
- Speed (kt)
|
||||
- Angle (degree), either ground track or heading
|
||||
- Vertical rate (ft/min)
|
||||
- Speed type ('GS' for ground speed, 'AS' for airspeed)
|
||||
- [Optional] Direction source ('TRUE_NORTH' or 'MAGENTIC_NORTH')
|
||||
- [Optional] Vertical rate source ('BARO' or 'GNSS')
|
||||
|
||||
For surface messages, vertical rate and its respective sources are set to None.
|
||||
|
||||
"""
|
||||
if 5 <= typecode(msg) <= 8:
|
||||
return surface_velocity(msg, rtn_sources)
|
||||
return surface_velocity(msg, source)
|
||||
|
||||
elif typecode(msg) == 19:
|
||||
return airborne_velocity(msg, rtn_sources)
|
||||
return airborne_velocity(msg, source)
|
||||
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"incorrect or inconsistant message types, expecting 4<TC<9 or TC=19"
|
||||
"incorrect or inconsistent message types, expecting 4<TC<9 or TC=19"
|
||||
)
|
||||
|
||||
|
||||
@@ -199,7 +176,7 @@ def speed_heading(msg):
|
||||
(handles both airborne or surface message)
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
(int, float): speed (kt), ground track or heading (degree)
|
||||
@@ -211,7 +188,7 @@ def speed_heading(msg):
|
||||
def oe_flag(msg):
|
||||
"""Check the odd/even flag. Bit 54, 0 for even, 1 for odd.
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
Returns:
|
||||
int: 0 or 1, for even or odd frame
|
||||
"""
|
||||
@@ -223,7 +200,7 @@ def version(msg):
|
||||
"""ADS-B Version
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string, TC = 31
|
||||
msg (str): 28 hexdigits string, TC = 31
|
||||
|
||||
Returns:
|
||||
int: version number
|
||||
@@ -245,7 +222,7 @@ def nuc_p(msg):
|
||||
"""Calculate NUCp, Navigation Uncertainty Category - Position (ADS-B version 1)
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string,
|
||||
msg (str): 28 hexdigits string,
|
||||
|
||||
Returns:
|
||||
int: Horizontal Protection Limit
|
||||
@@ -281,7 +258,7 @@ def nuc_v(msg):
|
||||
"""Calculate NUCv, Navigation Uncertainty Category - Velocity (ADS-B version 1)
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string,
|
||||
msg (str): 28 hexdigits string,
|
||||
|
||||
Returns:
|
||||
int or string: 95% Horizontal Velocity Error
|
||||
@@ -310,7 +287,7 @@ def nic_v1(msg, NICs):
|
||||
"""Calculate NIC, navigation integrity category, for ADS-B version 1
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
NICs (int or string): NIC supplement
|
||||
|
||||
Returns:
|
||||
@@ -344,7 +321,7 @@ def nic_v2(msg, NICa, NICbc):
|
||||
"""Calculate NIC, navigation integrity category, for ADS-B version 2
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
NICa (int or string): NIC supplement - A
|
||||
NICbc (int or srting): NIC supplement - B or C
|
||||
|
||||
@@ -382,7 +359,7 @@ def nic_s(msg):
|
||||
"""Obtain NIC supplement bit, TC=31 message
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: NICs number (0 or 1)
|
||||
@@ -404,7 +381,7 @@ def nic_a_c(msg):
|
||||
"""Obtain NICa/c, navigation integrity category supplements a and c
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
(int, int): NICa and NICc number (0 or 1)
|
||||
@@ -427,7 +404,7 @@ def nic_b(msg):
|
||||
"""Obtain NICb, navigation integrity category supplement-b
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: NICb number (0 or 1)
|
||||
@@ -449,7 +426,7 @@ def nac_p(msg):
|
||||
"""Calculate NACp, Navigation Accuracy Category - Position
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string, TC = 29 or 31
|
||||
msg (str): 28 hexdigits string, TC = 29 or 31
|
||||
|
||||
Returns:
|
||||
int or string: 95% horizontal accuracy bounds, Estimated Position Uncertainty
|
||||
@@ -484,7 +461,7 @@ def nac_v(msg):
|
||||
"""Calculate NACv, Navigation Accuracy Category - Velocity
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string, TC = 19
|
||||
msg (str): 28 hexdigits string, TC = 19
|
||||
|
||||
Returns:
|
||||
int or string: 95% horizontal accuracy bounds for velocity, Horizontal Figure of Merit
|
||||
@@ -513,7 +490,7 @@ def sil(msg, version):
|
||||
"""Calculate SIL, Surveillance Integrity Level
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string with TC = 29, 31
|
||||
msg (str): 28 hexdigits string with TC = 29, 31
|
||||
|
||||
Returns:
|
||||
int or string: Probability of exceeding Horizontal Radius of Containment RCu
|
||||
@@ -524,7 +501,7 @@ def sil(msg, version):
|
||||
|
||||
if tc not in [29, 31]:
|
||||
raise RuntimeError(
|
||||
"%s: Not a target state and status messag, \
|
||||
"%s: Not a target state and status message, \
|
||||
or operation status message, expecting TC = 29 or 31"
|
||||
% msg
|
||||
)
|
||||
|
||||
@@ -1,23 +1,5 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Decoding all call replies DF=11
|
||||
|
||||
[To be implemented]
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder import common
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
Common functions for Mode-S decoding
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
import numpy as np
|
||||
|
||||
from pyModeS.extra import aero
|
||||
from pyModeS.decoder import common
|
||||
from pyModeS import common
|
||||
|
||||
from pyModeS.decoder.bds import (
|
||||
bds05,
|
||||
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.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
spd_ref (float): reference speed (ADS-B ground speed), kts
|
||||
trk_ref (float): reference track (ADS-B track angle), deg
|
||||
alt_ref (float): reference altitude (ADS-B altitude), ft
|
||||
@@ -108,7 +108,7 @@ def infer(msg, mrar=False):
|
||||
"""Estimate the most likely BDS code of an message.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
mrar (bool): Also infer MRAR (BDS 44) and MHR (BDS 45). Defaults to False.
|
||||
|
||||
Returns:
|
||||
@@ -125,7 +125,7 @@ def infer(msg, mrar=False):
|
||||
tc = common.typecode(msg)
|
||||
|
||||
if 1 <= tc <= 4:
|
||||
return "BDS08" # indentification and category
|
||||
return "BDS08" # identification and category
|
||||
if 5 <= tc <= 8:
|
||||
return "BDS06" # surface movement
|
||||
if 9 <= tc <= 18:
|
||||
|
||||
@@ -1,36 +1,18 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 0,5
|
||||
# ADS-B TC=9-18
|
||||
# Airborn position
|
||||
# ------------------------------------------
|
||||
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder import common
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def airborne_position(msg0, msg1, t0, t1):
|
||||
"""Decode airborn position from a pair of even and odd position message
|
||||
|
||||
Args:
|
||||
msg0 (string): even message (28 bytes hexadecimal string)
|
||||
msg1 (string): odd message (28 bytes hexadecimal string)
|
||||
msg0 (string): even message (28 hexdigits)
|
||||
msg1 (string): odd message (28 hexdigits)
|
||||
t0 (int): timestamps for the even message
|
||||
t1 (int): timestamps for the odd message
|
||||
|
||||
@@ -103,7 +85,7 @@ def airborne_position_with_ref(msg, lat_ref, lon_ref):
|
||||
be with in 180NM of the true position.
|
||||
|
||||
Args:
|
||||
msg (string): even message (28 bytes hexadecimal string)
|
||||
msg (str): even message (28 hexdigits)
|
||||
lat_ref: previous known latitude
|
||||
lon_ref: previous known longitude
|
||||
|
||||
@@ -145,7 +127,7 @@ def altitude(msg):
|
||||
"""Decode aircraft altitude
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: altitude in feet
|
||||
|
||||
@@ -1,28 +1,10 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 0,6
|
||||
# ADS-B TC=5-8
|
||||
# Surface position
|
||||
# Surface movment
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder import common
|
||||
import math
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def surface_position(msg0, msg1, t0, t1, lat_ref, lon_ref):
|
||||
@@ -30,8 +12,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.
|
||||
|
||||
Args:
|
||||
msg0 (string): even message (28 bytes hexadecimal string)
|
||||
msg1 (string): odd message (28 bytes hexadecimal string)
|
||||
msg0 (string): even message (28 hexdigits)
|
||||
msg1 (string): odd message (28 hexdigits)
|
||||
t0 (int): timestamps for the even message
|
||||
t1 (int): timestamps for the odd message
|
||||
lat_ref (float): latitude of the receiver
|
||||
@@ -107,7 +89,7 @@ def surface_position_with_ref(msg, lat_ref, lon_ref):
|
||||
be with in 45NM of the true position.
|
||||
|
||||
Args:
|
||||
msg (string): even message (28 bytes hexadecimal string)
|
||||
msg (str): even message (28 hexdigits)
|
||||
lat_ref: previous known latitude
|
||||
lon_ref: previous known longitude
|
||||
|
||||
@@ -145,23 +127,24 @@ def surface_position_with_ref(msg, lat_ref, lon_ref):
|
||||
return round(lat, 5), round(lon, 5)
|
||||
|
||||
|
||||
def surface_velocity(msg, rtn_sources=False):
|
||||
"""Decode surface velocity from from a surface position message
|
||||
def surface_velocity(msg, source=False):
|
||||
"""Decode surface velocity from a surface position message
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
rtn_source (boolean): If the function will return
|
||||
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.
|
||||
msg (str): 28 hexdigits string
|
||||
source (boolean): Include direction and vertical rate sources in return. Default to False.
|
||||
If set to True, the function will return six value instead of four.
|
||||
|
||||
Returns:
|
||||
(int, float, int, string, string, None): speed (kt),
|
||||
ground track (degree), None for rate of climb/descend (ft/min),
|
||||
and speed type ('GS' for ground speed), direction source
|
||||
('true_north' for ground track / true north as reference),
|
||||
None rate of climb/descent source.
|
||||
"""
|
||||
int, float, int, string, [string], [string]: Four or six parameters, including:
|
||||
- Speed (kt)
|
||||
- Angle (degree), ground track
|
||||
- Vertical rate, always 0
|
||||
- Speed type ('GS' for ground speed, 'AS' for airspeed)
|
||||
- [Optional] Direction source ('TRUE_NORTH')
|
||||
- [Optional] Vertical rate source (None)
|
||||
|
||||
"""
|
||||
if common.typecode(msg) < 5 or common.typecode(msg) > 8:
|
||||
raise RuntimeError("%s: Not a surface message, expecting 5<TC<8" % msg)
|
||||
|
||||
@@ -175,7 +158,7 @@ def surface_velocity(msg, rtn_sources=False):
|
||||
else:
|
||||
trk = None
|
||||
|
||||
# ground movment / speed
|
||||
# ground movement / speed
|
||||
mov = common.bin2int(mb[5:12])
|
||||
|
||||
if mov == 0 or mov > 124:
|
||||
@@ -192,7 +175,7 @@ def surface_velocity(msg, rtn_sources=False):
|
||||
spd = kts[i - 1] + (mov - movs[i - 1]) * step
|
||||
spd = round(spd, 2)
|
||||
|
||||
if rtn_sources:
|
||||
return spd, trk, 0, "GS", "true_north", None
|
||||
if source:
|
||||
return spd, trk, 0, "GS", "TRUE_NORTH", None
|
||||
else:
|
||||
return spd, trk, 0, "GS"
|
||||
|
||||
@@ -1,34 +1,17 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 0,8
|
||||
# ADS-B TC=1-4
|
||||
# Aircraft identitification and category
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder import common
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def category(msg):
|
||||
"""Aircraft category number
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: category number
|
||||
@@ -46,7 +29,7 @@ def callsign(msg):
|
||||
"""Aircraft callsign
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
string: callsign
|
||||
|
||||
@@ -1,51 +1,33 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 0,9
|
||||
# ADS-B TC=19
|
||||
# Aircraft Airborn velocity
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder import common
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
import math
|
||||
|
||||
|
||||
def airborne_velocity(msg, rtn_sources=False):
|
||||
"""Calculate the speed, track (or heading), and vertical rate
|
||||
def airborne_velocity(msg, source=False):
|
||||
"""Decode airborne velocity.
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string
|
||||
rtn_source (boolean): If the function will return
|
||||
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.
|
||||
msg (str): 28 hexdigits string
|
||||
source (boolean): Include direction and vertical rate sources in return. Default to False.
|
||||
If set to True, the function will return six value instead of four.
|
||||
|
||||
Returns:
|
||||
(int, float, int, string, string, string): speed (kt),
|
||||
ground track or heading (degree),
|
||||
rate of climb/descent (ft/min), speed type
|
||||
('GS' for ground speed, 'AS' for airspeed),
|
||||
direction source ('true_north' for ground track / true north
|
||||
as refrence, 'mag_north' for magnetic north as reference),
|
||||
rate of climb/descent source ('Baro' for barometer, 'GNSS'
|
||||
for GNSS constellation).
|
||||
"""
|
||||
int, float, int, string, [string], [string]: Four or six parameters, including:
|
||||
- Speed (kt)
|
||||
- Angle (degree), either ground track or heading
|
||||
- Vertical rate (ft/min)
|
||||
- Speed type ('GS' for ground speed, 'AS' for airspeed)
|
||||
- [Optional] Direction source ('TRUE_NORTH' or 'MAGENTIC_NORTH')
|
||||
- [Optional] Vertical rate source ('BARO' or 'GNSS')
|
||||
|
||||
"""
|
||||
if common.typecode(msg) != 19:
|
||||
raise RuntimeError("%s: Not a airborne velocity message, expecting TC=19" % msg)
|
||||
|
||||
@@ -77,9 +59,9 @@ def airborne_velocity(msg, rtn_sources=False):
|
||||
trk = math.degrees(trk) # convert to degrees
|
||||
trk = trk if trk >= 0 else trk + 360 # no negative val
|
||||
|
||||
tag = "GS"
|
||||
spd_type = "GS"
|
||||
trk_or_hdg = round(trk, 2)
|
||||
dir_type = "true_north"
|
||||
dir_type = "TRUE_NORTH"
|
||||
|
||||
else:
|
||||
if mb[13] == "0":
|
||||
@@ -96,32 +78,33 @@ def airborne_velocity(msg, rtn_sources=False):
|
||||
spd *= 4
|
||||
|
||||
if mb[24] == "0":
|
||||
tag = "IAS"
|
||||
spd_type = "IAS"
|
||||
else:
|
||||
tag = "TAS"
|
||||
spd_type = "TAS"
|
||||
|
||||
dir_type = "mag_north"
|
||||
dir_type = "MAGENTIC_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 = common.bin2int(mb[37:46])
|
||||
rocd = None if vr == 0 else int(vr_sign * (vr - 1) * 64)
|
||||
vs = None if vr == 0 else int(vr_sign * (vr - 1) * 64)
|
||||
|
||||
if rtn_sources:
|
||||
return spd, trk_or_hdg, rocd, tag, dir_type, vr_source
|
||||
if source:
|
||||
return spd, trk_or_hdg, vs, spd_type, dir_type, vr_source
|
||||
else:
|
||||
return spd, trk_or_hdg, rocd, tag
|
||||
return spd, trk_or_hdg, vs, spd_type
|
||||
|
||||
|
||||
def altitude_diff(msg):
|
||||
"""Decode the differece between GNSS and barometric altitude
|
||||
"""Decode the differece between GNSS and barometric altitude.
|
||||
|
||||
Args:
|
||||
msg (string): 28 bytes hexadecimal message string, TC=19
|
||||
msg (str): 28 hexdigits string, TC=19
|
||||
|
||||
Returns:
|
||||
int: Altitude difference in ft. Negative value indicates GNSS altitude
|
||||
below barometric altitude.
|
||||
int: Altitude difference in feet. Negative value indicates GNSS altitude
|
||||
below barometric altitude.
|
||||
|
||||
"""
|
||||
tc = common.typecode(msg)
|
||||
|
||||
|
||||
@@ -1,54 +1,38 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 1,0
|
||||
# Data link capability report
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def is10(msg):
|
||||
"""Check if a message is likely to be BDS code 1,0
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
bool: True or False
|
||||
"""
|
||||
|
||||
if allzeros(msg):
|
||||
if common.allzeros(msg):
|
||||
return False
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
# first 8 bits must be 0x10
|
||||
if d[0:8] != "00010000":
|
||||
return False
|
||||
|
||||
# bit 10 to 14 are reserved
|
||||
if bin2int(d[9:14]) != 0:
|
||||
if common.bin2int(d[9:14]) != 0:
|
||||
return False
|
||||
|
||||
# overlay capabilty conflict
|
||||
if d[14] == "1" and bin2int(d[16:23]) < 5:
|
||||
# overlay capability conflict
|
||||
if d[14] == "1" and common.bin2int(d[16:23]) < 5:
|
||||
return False
|
||||
if d[14] == "0" and bin2int(d[16:23]) > 4:
|
||||
if d[14] == "0" and common.bin2int(d[16:23]) > 4:
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -58,11 +42,11 @@ def ovc10(msg):
|
||||
"""Return the overlay control capability
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: Whether the transponder is OVC capable
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
return int(d[14])
|
||||
|
||||
@@ -1,45 +1,27 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 1,7
|
||||
# Common usage GICB capability report
|
||||
# ------------------------------------------
|
||||
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def is17(msg):
|
||||
"""Check if a message is likely to be BDS code 1,7
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
bool: True or False
|
||||
"""
|
||||
|
||||
if allzeros(msg):
|
||||
if common.allzeros(msg):
|
||||
return False
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if bin2int(d[28:56]) != 0:
|
||||
if common.bin2int(d[28:56]) != 0:
|
||||
return False
|
||||
|
||||
caps = cap17(msg)
|
||||
@@ -60,10 +42,10 @@ def cap17(msg):
|
||||
"""Extract capacities from BDS 1,7 message
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
list: list of suport BDS codes
|
||||
list: list of support BDS codes
|
||||
"""
|
||||
allbds = [
|
||||
"05",
|
||||
@@ -96,7 +78,7 @@ def cap17(msg):
|
||||
"E2",
|
||||
]
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
idx = [i for i, v in enumerate(d[:28]) if v == "1"]
|
||||
capacity = ["BDS" + allbds[i] for i in idx if allbds[i] is not "NA"]
|
||||
|
||||
|
||||
@@ -1,41 +1,25 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 2,0
|
||||
# Aircraft identification
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def is20(msg):
|
||||
"""Check if a message is likely to be BDS code 2,0
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
bool: True or False
|
||||
"""
|
||||
|
||||
if allzeros(msg):
|
||||
if common.allzeros(msg):
|
||||
return False
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[0:8] != "00100000":
|
||||
return False
|
||||
@@ -52,23 +36,23 @@ def cs20(msg):
|
||||
"""Aircraft callsign
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS40) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
string: callsign, max. 8 chars
|
||||
"""
|
||||
chars = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######"
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
cs = ""
|
||||
cs += chars[bin2int(d[8:14])]
|
||||
cs += chars[bin2int(d[14:20])]
|
||||
cs += chars[bin2int(d[20:26])]
|
||||
cs += chars[bin2int(d[26:32])]
|
||||
cs += chars[bin2int(d[32:38])]
|
||||
cs += chars[bin2int(d[38:44])]
|
||||
cs += chars[bin2int(d[44:50])]
|
||||
cs += chars[bin2int(d[50:56])]
|
||||
cs += chars[common.bin2int(d[8:14])]
|
||||
cs += chars[common.bin2int(d[14:20])]
|
||||
cs += chars[common.bin2int(d[20:26])]
|
||||
cs += chars[common.bin2int(d[26:32])]
|
||||
cs += chars[common.bin2int(d[32:38])]
|
||||
cs += chars[common.bin2int(d[38:44])]
|
||||
cs += chars[common.bin2int(d[44:50])]
|
||||
cs += chars[common.bin2int(d[50:56])]
|
||||
|
||||
return cs
|
||||
|
||||
@@ -1,41 +1,25 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 3,0
|
||||
# ACAS active resolution advisory
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def is30(msg):
|
||||
"""Check if a message is likely to be BDS code 2,0
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
bool: True or False
|
||||
"""
|
||||
|
||||
if allzeros(msg):
|
||||
if common.allzeros(msg):
|
||||
return False
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[0:8] != "00110000":
|
||||
return False
|
||||
@@ -45,7 +29,7 @@ def is30(msg):
|
||||
return False
|
||||
|
||||
# reserved for ACAS III, in far future
|
||||
if bin2int(d[15:22]) >= 48:
|
||||
if common.bin2int(d[15:22]) >= 48:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -1,66 +1,50 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 4,0
|
||||
# Selected vertical intention
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
import warnings
|
||||
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def is40(msg):
|
||||
"""Check if a message is likely to be BDS code 4,0
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
bool: True or False
|
||||
"""
|
||||
|
||||
if allzeros(msg):
|
||||
if common.allzeros(msg):
|
||||
return False
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
# status bit 1, 14, and 27
|
||||
|
||||
if wrongstatus(d, 1, 2, 13):
|
||||
if common.wrongstatus(d, 1, 2, 13):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 14, 15, 26):
|
||||
if common.wrongstatus(d, 14, 15, 26):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 27, 28, 39):
|
||||
if common.wrongstatus(d, 27, 28, 39):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 48, 49, 51):
|
||||
if common.wrongstatus(d, 48, 49, 51):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 54, 55, 56):
|
||||
if common.wrongstatus(d, 54, 55, 56):
|
||||
return False
|
||||
|
||||
# bits 40-47 and 52-53 shall all be zero
|
||||
|
||||
if bin2int(d[39:47]) != 0:
|
||||
if common.bin2int(d[39:47]) != 0:
|
||||
return False
|
||||
|
||||
if bin2int(d[51:53]) != 0:
|
||||
if common.bin2int(d[51:53]) != 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -70,17 +54,17 @@ def selalt40mcp(msg):
|
||||
"""Selected altitude, MCP/FCU
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS40) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: altitude in feet
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[0] == "0":
|
||||
return None
|
||||
|
||||
alt = bin2int(d[1:13]) * 16 # ft
|
||||
alt = common.bin2int(d[1:13]) * 16 # ft
|
||||
return alt
|
||||
|
||||
|
||||
@@ -88,17 +72,17 @@ def selalt40fms(msg):
|
||||
"""Selected altitude, FMS
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS40) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: altitude in feet
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[13] == "0":
|
||||
return None
|
||||
|
||||
alt = bin2int(d[14:26]) * 16 # ft
|
||||
alt = common.bin2int(d[14:26]) * 16 # ft
|
||||
return alt
|
||||
|
||||
|
||||
@@ -106,17 +90,17 @@ def p40baro(msg):
|
||||
"""Barometric pressure setting
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS40) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
float: pressure in millibar
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[26] == "0":
|
||||
return None
|
||||
|
||||
p = bin2int(d[27:39]) * 0.1 + 800 # millibar
|
||||
p = common.bin2int(d[27:39]) * 0.1 + 800 # millibar
|
||||
return p
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 4,4
|
||||
# Meteorological routine air report
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def is44(msg):
|
||||
@@ -28,32 +12,32 @@ def is44(msg):
|
||||
Meteorological routine air report
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
bool: True or False
|
||||
|
||||
"""
|
||||
if allzeros(msg):
|
||||
if common.allzeros(msg):
|
||||
return False
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
# status bit 5, 35, 47, 50
|
||||
if wrongstatus(d, 5, 6, 23):
|
||||
if common.wrongstatus(d, 5, 6, 23):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 35, 36, 46):
|
||||
if common.wrongstatus(d, 35, 36, 46):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 47, 48, 49):
|
||||
if common.wrongstatus(d, 47, 48, 49):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 50, 51, 56):
|
||||
if common.wrongstatus(d, 50, 51, 56):
|
||||
return False
|
||||
|
||||
# Bits 1-4 indicate source, values > 4 reserved and should not occur
|
||||
if bin2int(d[0:4]) > 4:
|
||||
if common.bin2int(d[0:4]) > 4:
|
||||
return False
|
||||
|
||||
vw, dw = wind44(msg)
|
||||
@@ -71,20 +55,20 @@ def wind44(msg):
|
||||
"""Wind speed and direction.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
(int, float): speed (kt), direction (degree)
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
status = int(d[4])
|
||||
if not status:
|
||||
return None, None
|
||||
|
||||
speed = bin2int(d[5:14]) # knots
|
||||
direction = bin2int(d[14:23]) * 180.0 / 256.0 # degree
|
||||
speed = common.bin2int(d[5:14]) # knots
|
||||
direction = common.bin2int(d[14:23]) * 180.0 / 256.0 # degree
|
||||
|
||||
return round(speed, 0), round(direction, 1)
|
||||
|
||||
@@ -93,18 +77,18 @@ def temp44(msg):
|
||||
"""Static air temperature.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
float, float: temperature and alternative temperature in Celsius degree.
|
||||
Note: Two values returns due to what seems to be an inconsistancy
|
||||
Note: Two values returns due to what seems to be an inconsistency
|
||||
error in ICAO 9871 (2008) Appendix A-67.
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
sign = int(d[23])
|
||||
value = bin2int(d[24:34])
|
||||
value = common.bin2int(d[24:34])
|
||||
|
||||
if sign:
|
||||
value = value - 1024
|
||||
@@ -122,18 +106,18 @@ def p44(msg):
|
||||
"""Static pressure.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: static pressure in hPa
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[34] == "0":
|
||||
return None
|
||||
|
||||
p = bin2int(d[35:46]) # hPa
|
||||
p = common.bin2int(d[35:46]) # hPa
|
||||
|
||||
return p
|
||||
|
||||
@@ -142,17 +126,17 @@ def hum44(msg):
|
||||
"""humidity
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
float: percentage of humidity, [0 - 100] %
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[49] == "0":
|
||||
return None
|
||||
|
||||
hm = bin2int(d[50:56]) * 100.0 / 64 # %
|
||||
hm = common.bin2int(d[50:56]) * 100.0 / 64 # %
|
||||
|
||||
return round(hm, 1)
|
||||
|
||||
@@ -161,17 +145,17 @@ def turb44(msg):
|
||||
"""Turblence.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: turbulence level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[46] == "0":
|
||||
return None
|
||||
|
||||
turb = bin2int(d[47:49])
|
||||
turb = common.bin2int(d[47:49])
|
||||
|
||||
return turb
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 4,5
|
||||
# Meteorological hazard report
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def is45(msg):
|
||||
@@ -28,44 +12,44 @@ def is45(msg):
|
||||
Meteorological hazard report
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
bool: True or False
|
||||
|
||||
"""
|
||||
if allzeros(msg):
|
||||
if common.allzeros(msg):
|
||||
return False
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
# status bit 1, 4, 7, 10, 13, 16, 27, 39
|
||||
if wrongstatus(d, 1, 2, 3):
|
||||
if common.wrongstatus(d, 1, 2, 3):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 4, 5, 6):
|
||||
if common.wrongstatus(d, 4, 5, 6):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 7, 8, 9):
|
||||
if common.wrongstatus(d, 7, 8, 9):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 10, 11, 12):
|
||||
if common.wrongstatus(d, 10, 11, 12):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 13, 14, 15):
|
||||
if common.wrongstatus(d, 13, 14, 15):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 16, 17, 26):
|
||||
if common.wrongstatus(d, 16, 17, 26):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 27, 28, 38):
|
||||
if common.wrongstatus(d, 27, 28, 38):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 39, 40, 51):
|
||||
if common.wrongstatus(d, 39, 40, 51):
|
||||
return False
|
||||
|
||||
# reserved
|
||||
if bin2int(d[51:56]) != 0:
|
||||
if common.bin2int(d[51:56]) != 0:
|
||||
return False
|
||||
|
||||
temp = temp45(msg)
|
||||
@@ -80,17 +64,17 @@ def turb45(msg):
|
||||
"""Turbulence.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: Turbulence level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
if d[0] == "0":
|
||||
return None
|
||||
|
||||
turb = bin2int(d[1:3])
|
||||
turb = common.bin2int(d[1:3])
|
||||
return turb
|
||||
|
||||
|
||||
@@ -98,17 +82,17 @@ def ws45(msg):
|
||||
"""Wind shear.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: Wind shear level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
if d[3] == "0":
|
||||
return None
|
||||
|
||||
ws = bin2int(d[4:6])
|
||||
ws = common.bin2int(d[4:6])
|
||||
return ws
|
||||
|
||||
|
||||
@@ -116,17 +100,17 @@ def mb45(msg):
|
||||
"""Microburst.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: Microburst level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
if d[6] == "0":
|
||||
return None
|
||||
|
||||
mb = bin2int(d[7:9])
|
||||
mb = common.bin2int(d[7:9])
|
||||
return mb
|
||||
|
||||
|
||||
@@ -134,17 +118,17 @@ def ic45(msg):
|
||||
"""Icing.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: Icing level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
if d[9] == "0":
|
||||
return None
|
||||
|
||||
ic = bin2int(d[10:12])
|
||||
ic = common.bin2int(d[10:12])
|
||||
return ic
|
||||
|
||||
|
||||
@@ -152,17 +136,17 @@ def wv45(msg):
|
||||
"""Wake vortex.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: Wake vortex level. 0=NIL, 1=Light, 2=Moderate, 3=Severe
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
if d[12] == "0":
|
||||
return None
|
||||
|
||||
ws = bin2int(d[13:15])
|
||||
ws = common.bin2int(d[13:15])
|
||||
return ws
|
||||
|
||||
|
||||
@@ -170,16 +154,16 @@ def temp45(msg):
|
||||
"""Static air temperature.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
float: tmeperature in Celsius degree
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
sign = int(d[16])
|
||||
value = bin2int(d[17:26])
|
||||
value = common.bin2int(d[17:26])
|
||||
|
||||
if sign:
|
||||
value = value - 512
|
||||
@@ -194,16 +178,16 @@ def p45(msg):
|
||||
"""Average static pressure.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: static pressure in hPa
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
if d[26] == "0":
|
||||
return None
|
||||
p = bin2int(d[27:38]) # hPa
|
||||
p = common.bin2int(d[27:38]) # hPa
|
||||
return p
|
||||
|
||||
|
||||
@@ -211,14 +195,14 @@ def rh45(msg):
|
||||
"""Radio height.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: radio height in ft
|
||||
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
if d[38] == "0":
|
||||
return None
|
||||
rh = bin2int(d[39:51]) * 16
|
||||
rh = common.bin2int(d[39:51]) * 16
|
||||
return rh
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 5,0
|
||||
# Track and turn report
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def is50(msg):
|
||||
@@ -27,32 +11,32 @@ def is50(msg):
|
||||
(Track and turn report)
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
bool: True or False
|
||||
"""
|
||||
|
||||
if allzeros(msg):
|
||||
if common.allzeros(msg):
|
||||
return False
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
# status bit 1, 12, 24, 35, 46
|
||||
|
||||
if wrongstatus(d, 1, 3, 11):
|
||||
if common.wrongstatus(d, 1, 3, 11):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 12, 13, 23):
|
||||
if common.wrongstatus(d, 12, 13, 23):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 24, 25, 34):
|
||||
if common.wrongstatus(d, 24, 25, 34):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 35, 36, 45):
|
||||
if common.wrongstatus(d, 35, 36, 45):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 46, 47, 56):
|
||||
if common.wrongstatus(d, 46, 47, 56):
|
||||
return False
|
||||
|
||||
roll = roll50(msg)
|
||||
@@ -77,19 +61,19 @@ def roll50(msg):
|
||||
"""Roll angle, BDS 5,0 message
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS50) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
float: angle in degrees,
|
||||
negative->left wing down, positive->right wing down
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[0] == "0":
|
||||
return None
|
||||
|
||||
sign = int(d[1]) # 1 -> left wing down
|
||||
value = bin2int(d[2:11])
|
||||
value = common.bin2int(d[2:11])
|
||||
|
||||
if sign:
|
||||
value = value - 512
|
||||
@@ -102,18 +86,18 @@ def trk50(msg):
|
||||
"""True track angle, BDS 5,0 message
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS50) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
float: angle in degrees to true north (from 0 to 360)
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[11] == "0":
|
||||
return None
|
||||
|
||||
sign = int(d[12]) # 1 -> west
|
||||
value = bin2int(d[13:23])
|
||||
value = common.bin2int(d[13:23])
|
||||
|
||||
if sign:
|
||||
value = value - 1024
|
||||
@@ -131,17 +115,17 @@ def gs50(msg):
|
||||
"""Ground speed, BDS 5,0 message
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS50) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: ground speed in knots
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[23] == "0":
|
||||
return None
|
||||
|
||||
spd = bin2int(d[24:34]) * 2 # kts
|
||||
spd = common.bin2int(d[24:34]) * 2 # kts
|
||||
return spd
|
||||
|
||||
|
||||
@@ -149,12 +133,12 @@ def rtrk50(msg):
|
||||
"""Track angle rate, BDS 5,0 message
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS50) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
float: angle rate in degrees/second
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[34] == "0":
|
||||
return None
|
||||
@@ -163,7 +147,7 @@ def rtrk50(msg):
|
||||
return None
|
||||
|
||||
sign = int(d[35]) # 1 -> negative value, two's complement
|
||||
value = bin2int(d[36:45])
|
||||
value = common.bin2int(d[36:45])
|
||||
if sign:
|
||||
value = value - 512
|
||||
|
||||
@@ -175,15 +159,15 @@ def tas50(msg):
|
||||
"""Aircraft true airspeed, BDS 5,0 message
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS50) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: true airspeed in knots
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[45] == "0":
|
||||
return None
|
||||
|
||||
tas = bin2int(d[46:56]) * 2 # kts
|
||||
tas = common.bin2int(d[46:56]) * 2 # kts
|
||||
return tas
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 5,3
|
||||
# Air-referenced state vector
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def is53(msg):
|
||||
@@ -27,32 +11,32 @@ def is53(msg):
|
||||
(Air-referenced state vector)
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
bool: True or False
|
||||
"""
|
||||
|
||||
if allzeros(msg):
|
||||
if common.allzeros(msg):
|
||||
return False
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
# status bit 1, 13, 24, 34, 47
|
||||
|
||||
if wrongstatus(d, 1, 3, 12):
|
||||
if common.wrongstatus(d, 1, 3, 12):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 13, 14, 23):
|
||||
if common.wrongstatus(d, 13, 14, 23):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 24, 25, 33):
|
||||
if common.wrongstatus(d, 24, 25, 33):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 34, 35, 46):
|
||||
if common.wrongstatus(d, 34, 35, 46):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 47, 49, 56):
|
||||
if common.wrongstatus(d, 47, 49, 56):
|
||||
return False
|
||||
|
||||
ias = ias53(msg)
|
||||
@@ -78,18 +62,18 @@ def hdg53(msg):
|
||||
"""Magnetic heading, BDS 5,3 message
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS53) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
float: angle in degrees to true north (from 0 to 360)
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[0] == "0":
|
||||
return None
|
||||
|
||||
sign = int(d[1]) # 1 -> west
|
||||
value = bin2int(d[2:12])
|
||||
value = common.bin2int(d[2:12])
|
||||
|
||||
if sign:
|
||||
value = value - 1024
|
||||
@@ -107,17 +91,17 @@ def ias53(msg):
|
||||
"""Indicated airspeed, DBS 5,3 message
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message
|
||||
msg (str): 28 hexdigits
|
||||
|
||||
Returns:
|
||||
int: indicated arispeed in knots
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[12] == "0":
|
||||
return None
|
||||
|
||||
ias = bin2int(d[13:23]) # knots
|
||||
ias = common.bin2int(d[13:23]) # knots
|
||||
return ias
|
||||
|
||||
|
||||
@@ -125,17 +109,17 @@ def mach53(msg):
|
||||
"""MACH number, DBS 5,3 message
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message
|
||||
msg (str): 28 hexdigits
|
||||
|
||||
Returns:
|
||||
float: MACH number
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[23] == "0":
|
||||
return None
|
||||
|
||||
mach = bin2int(d[24:33]) * 0.008
|
||||
mach = common.bin2int(d[24:33]) * 0.008
|
||||
return round(mach, 3)
|
||||
|
||||
|
||||
@@ -143,17 +127,17 @@ def tas53(msg):
|
||||
"""Aircraft true airspeed, BDS 5,3 message
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message
|
||||
msg (str): 28 hexdigits
|
||||
|
||||
Returns:
|
||||
float: true airspeed in knots
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[33] == "0":
|
||||
return None
|
||||
|
||||
tas = bin2int(d[34:46]) * 0.5 # kts
|
||||
tas = common.bin2int(d[34:46]) * 0.5 # kts
|
||||
return round(tas, 1)
|
||||
|
||||
|
||||
@@ -161,18 +145,18 @@ def vr53(msg):
|
||||
"""Vertical rate
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||
msg (str): 28 hexdigits (BDS60) string
|
||||
|
||||
Returns:
|
||||
int: vertical rate in feet/minutes
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[46] == "0":
|
||||
return None
|
||||
|
||||
sign = int(d[47]) # 1 -> negative value, two's complement
|
||||
value = bin2int(d[48:56])
|
||||
value = common.bin2int(d[48:56])
|
||||
|
||||
if value == 0 or value == 255: # all zeros or all ones
|
||||
return 0
|
||||
|
||||
@@ -1,57 +1,41 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ------------------------------------------
|
||||
# BDS 6,0
|
||||
# Heading and speed report
|
||||
# ------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder.common import hex2bin, bin2int, data, allzeros, wrongstatus
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def is60(msg):
|
||||
"""Check if a message is likely to be BDS code 6,0
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
bool: True or False
|
||||
"""
|
||||
|
||||
if allzeros(msg):
|
||||
if common.allzeros(msg):
|
||||
return False
|
||||
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
# status bit 1, 13, 24, 35, 46
|
||||
|
||||
if wrongstatus(d, 1, 2, 12):
|
||||
if common.wrongstatus(d, 1, 2, 12):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 13, 14, 23):
|
||||
if common.wrongstatus(d, 13, 14, 23):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 24, 25, 34):
|
||||
if common.wrongstatus(d, 24, 25, 34):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 35, 36, 45):
|
||||
if common.wrongstatus(d, 35, 36, 45):
|
||||
return False
|
||||
|
||||
if wrongstatus(d, 46, 47, 56):
|
||||
if common.wrongstatus(d, 46, 47, 56):
|
||||
return False
|
||||
|
||||
ias = ias60(msg)
|
||||
@@ -77,18 +61,18 @@ def hdg60(msg):
|
||||
"""Megnetic heading of aircraft
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
float: heading in degrees to megnetic north (from 0 to 360)
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[0] == "0":
|
||||
return None
|
||||
|
||||
sign = int(d[1]) # 1 -> west
|
||||
value = bin2int(d[2:12])
|
||||
value = common.bin2int(d[2:12])
|
||||
|
||||
if sign:
|
||||
value = value - 1024
|
||||
@@ -106,17 +90,17 @@ def ias60(msg):
|
||||
"""Indicated airspeed
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: indicated airspeed in knots
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[12] == "0":
|
||||
return None
|
||||
|
||||
ias = bin2int(d[13:23]) # kts
|
||||
ias = common.bin2int(d[13:23]) # kts
|
||||
return ias
|
||||
|
||||
|
||||
@@ -124,17 +108,17 @@ def mach60(msg):
|
||||
"""Aircraft MACH number
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
float: MACH number
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[23] == "0":
|
||||
return None
|
||||
|
||||
mach = bin2int(d[24:34]) * 2.048 / 512.0
|
||||
mach = common.bin2int(d[24:34]) * 2.048 / 512.0
|
||||
return round(mach, 3)
|
||||
|
||||
|
||||
@@ -142,18 +126,18 @@ def vr60baro(msg):
|
||||
"""Vertical rate from barometric measurement, this value may be very noisy.
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: vertical rate in feet/minutes
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[34] == "0":
|
||||
return None
|
||||
|
||||
sign = int(d[35]) # 1 -> negative value, two's complement
|
||||
value = bin2int(d[36:45])
|
||||
value = common.bin2int(d[36:45])
|
||||
|
||||
if value == 0 or value == 511: # all zeros or all ones
|
||||
return 0
|
||||
@@ -165,21 +149,21 @@ def vr60baro(msg):
|
||||
|
||||
|
||||
def vr60ins(msg):
|
||||
"""Vertical rate messured by onbard equiments (IRS, AHRS)
|
||||
"""Vertical rate measurd by onbard equiments (IRS, AHRS)
|
||||
|
||||
Args:
|
||||
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||
msg (str): 28 hexdigits string
|
||||
|
||||
Returns:
|
||||
int: vertical rate in feet/minutes
|
||||
"""
|
||||
d = hex2bin(data(msg))
|
||||
d = common.hex2bin(common.data(msg))
|
||||
|
||||
if d[45] == "0":
|
||||
return None
|
||||
|
||||
sign = int(d[46]) # 1 -> negative value, two's complement
|
||||
value = bin2int(d[47:56])
|
||||
value = common.bin2int(d[47:56])
|
||||
|
||||
if value == 0 or value == 511: # all zeros or all ones
|
||||
return 0
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
"""Comm-B Wrapper.
|
||||
"""Comm-B module.
|
||||
|
||||
The Comm-B wrapper imports all functions from the following modules:
|
||||
The Comm-B module imports all functions from the following modules:
|
||||
|
||||
**ELS - elementary surveillance**
|
||||
- pyModeS.decoder.bds.bds10
|
||||
- pyModeS.decoder.bds.bds17
|
||||
- pyModeS.decoder.bds.bds20
|
||||
- pyModeS.decoder.bds.bds30
|
||||
ELS - elementary surveillance
|
||||
|
||||
**EHS - enhanced surveillance**
|
||||
- pyModeS.decoder.bds.bds40
|
||||
- pyModeS.decoder.bds.bds50
|
||||
- pyModeS.decoder.bds.bds60
|
||||
- pyModeS.decoder.bds.bds10
|
||||
- pyModeS.decoder.bds.bds17
|
||||
- pyModeS.decoder.bds.bds20
|
||||
- pyModeS.decoder.bds.bds30
|
||||
|
||||
**MRAR and MHR**
|
||||
- pyModeS.decoder.bds.bds44
|
||||
- pyModeS.decoder.bds.bds45
|
||||
EHS - enhanced surveillance
|
||||
|
||||
- pyModeS.decoder.bds.bds40
|
||||
- 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
|
||||
from pyModeS.decoder.bds.bds10 import *
|
||||
from pyModeS.decoder.bds.bds17 import *
|
||||
|
||||
@@ -9,7 +9,6 @@ The EHS wrapper imports all functions from the following modules:
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
import warnings
|
||||
|
||||
from pyModeS.decoder.bds.bds40 import *
|
||||
|
||||
@@ -10,8 +10,6 @@ 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.bds17 import *
|
||||
from pyModeS.decoder.bds.bds20 import *
|
||||
|
||||
@@ -1,23 +1,5 @@
|
||||
# Copyright (C) 2018 Junzi Sun (TU Delft)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Warpper for short roll call surveillance replies DF=4/5
|
||||
|
||||
[To be implemented]
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
from pyModeS.decoder import common
|
||||
|
||||
25
pyModeS/decoder/uplink.py
Normal file
25
pyModeS/decoder/uplink.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
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)
|
||||
@@ -1 +0,0 @@
|
||||
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]
|
||||
a = vsound(H) # speed of sound [m/s] as function of H[m]
|
||||
p = pressure(H) # calls atmos but retruns only pressure [Pa]
|
||||
p = pressure(H) # calls atmos but returns only pressure [Pa]
|
||||
T = temperature(H) # calculates temperature [K]
|
||||
rho = density(H) # calls atmos but retruns only pressure [Pa]
|
||||
rho = density(H) # calls atmos but returns only pressure [Pa]
|
||||
|
||||
Speed conversion at altitude H[m] in ISA
|
||||
::
|
||||
|
||||
@@ -1,90 +1,112 @@
|
||||
import traceback
|
||||
import numpy as np
|
||||
import pyModeS as pms
|
||||
from rtlsdr import RtlSdr
|
||||
import time
|
||||
|
||||
modes_sample_rate = 2e6
|
||||
sampling_rate = 2e6
|
||||
smaples_per_microsec = 2
|
||||
|
||||
modes_frequency = 1090e6
|
||||
buffer_size = 1024 * 100
|
||||
read_size = 1024 * 20
|
||||
buffer_size = 1024 * 200
|
||||
read_size = 1024 * 100
|
||||
|
||||
pbits = 8
|
||||
fbits = 112
|
||||
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
|
||||
|
||||
|
||||
class RtlReader(object):
|
||||
def __init__(self, **kwargs):
|
||||
super(RtlReader, self).__init__()
|
||||
self.signal_buffer = []
|
||||
self.signal_buffer = [] # amplitude of the sample only
|
||||
self.sdr = RtlSdr()
|
||||
self.sdr.sample_rate = modes_sample_rate
|
||||
self.sdr.sample_rate = sampling_rate
|
||||
self.sdr.center_freq = modes_frequency
|
||||
self.sdr.gain = "auto"
|
||||
# sdr.freq_correction = 75
|
||||
|
||||
self.debug = kwargs.get("debug", False)
|
||||
self.raw_pipe_in = None
|
||||
self.stop_flag = False
|
||||
self.noise_floor = 1e6
|
||||
|
||||
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):
|
||||
"""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 = []
|
||||
|
||||
# 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)
|
||||
|
||||
i = 0
|
||||
while i < buffer_length:
|
||||
if self.signal_buffer[i] < th_amp:
|
||||
if self.signal_buffer[i] < min_sig_amp:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# if pulses[i : i + pbits * 2] == preamble:
|
||||
if self._check_preamble(self.signal_buffer[i : i + pbits * 2]):
|
||||
frame_start = i + pbits * 2
|
||||
frame_end = i + pbits * 2 + (fbits + 1) * 2
|
||||
frame_length = (fbits + 1) * 2
|
||||
frame_pulses = self.signal_buffer[frame_start:frame_end]
|
||||
|
||||
msgbin = ""
|
||||
threshold = max(frame_pulses) * 0.2
|
||||
|
||||
msgbin = []
|
||||
for j in range(0, frame_length, 2):
|
||||
p2 = frame_pulses[j : j + 2]
|
||||
if len(p2) < 2:
|
||||
break
|
||||
|
||||
if p2[0] < th_amp and p2[1] < th_amp:
|
||||
if p2[0] < threshold and p2[1] < threshold:
|
||||
break
|
||||
elif p2[0] >= p2[1]:
|
||||
c = "1"
|
||||
c = 1
|
||||
elif p2[0] < p2[1]:
|
||||
c = "0"
|
||||
c = 0
|
||||
else:
|
||||
msgbin = ""
|
||||
msgbin = []
|
||||
break
|
||||
msgbin += c
|
||||
|
||||
msgbin.append(c)
|
||||
|
||||
# advance i with a jump
|
||||
i = frame_start + j
|
||||
|
||||
if len(msgbin) > 0:
|
||||
msghex = pms.bin2hex(msgbin)
|
||||
msghex = pms.bin2hex("".join([str(i) for i in msgbin]))
|
||||
if self._check_msg(msghex):
|
||||
messages.append([msghex, time.time()])
|
||||
if self.debug:
|
||||
self._debug_msg(msghex)
|
||||
|
||||
elif i > buffer_length - 500:
|
||||
# save some for next process
|
||||
break
|
||||
# elif i > buffer_length - 500:
|
||||
# # save some for next process
|
||||
# break
|
||||
else:
|
||||
i += 1
|
||||
|
||||
# keep reminder of buffer for next iteration
|
||||
# reset the buffer
|
||||
self.signal_buffer = self.signal_buffer[i:]
|
||||
|
||||
return messages
|
||||
|
||||
def _check_preamble(self, pulses):
|
||||
@@ -122,10 +144,8 @@ class RtlReader(object):
|
||||
pass
|
||||
|
||||
def _read_callback(self, data, rtlsdr_obj):
|
||||
# scaling signal (imporatant)
|
||||
amp = np.absolute(data)
|
||||
amp_norm = np.interp(amp, (amp.min(), amp.max()), (0, 1))
|
||||
self.signal_buffer.extend(amp_norm.tolist())
|
||||
self.signal_buffer.extend(amp.tolist())
|
||||
|
||||
if len(self.signal_buffer) >= buffer_size:
|
||||
messages = self._process_buffer()
|
||||
@@ -138,18 +158,24 @@ class RtlReader(object):
|
||||
pass
|
||||
|
||||
def stop(self, *args, **kwargs):
|
||||
self.sdr.cancel_read_async()
|
||||
self.sdr.close()
|
||||
|
||||
def run(self, raw_pipe_in=None, stop_flag=None):
|
||||
def run(self, raw_pipe_in=None, stop_flag=None, exception_queue=None):
|
||||
self.raw_pipe_in = raw_pipe_in
|
||||
self.stop_flag = stop_flag
|
||||
self.sdr.read_samples_async(self._read_callback, read_size)
|
||||
|
||||
# count = 1
|
||||
# while count < 1000:
|
||||
# count += 1
|
||||
# data = self.sdr.read_samples(read_size)
|
||||
# self._read_callback(data, None)
|
||||
try:
|
||||
# raise RuntimeError("test exception")
|
||||
|
||||
while True:
|
||||
data = self.sdr.read_samples(read_size)
|
||||
self._read_callback(data, None)
|
||||
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
if exception_queue is not None:
|
||||
exception_queue.put(tb)
|
||||
raise e
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Stream beast raw data from a TCP server, convert to mode-s messages."""
|
||||
|
||||
from __future__ import print_function, division
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
@@ -170,7 +169,7 @@ class TcpClient(object):
|
||||
Start character '$'
|
||||
|
||||
MS field - Payload
|
||||
Postion 1 through 14:
|
||||
Position 1 through 14:
|
||||
14 bytes = 112 bits
|
||||
Mode-S payload
|
||||
In case of DF types that only carry 7 bytes of information
|
||||
@@ -254,7 +253,7 @@ class TcpClient(object):
|
||||
for msg, t in messages:
|
||||
print("%15.9f %s" % (t, msg))
|
||||
|
||||
def run(self, raw_pipe_in=None, stop_flag=None):
|
||||
def run(self, raw_pipe_in=None, stop_flag=None, exception_queue=None):
|
||||
self.raw_pipe_in = raw_pipe_in
|
||||
self.stop_flag = stop_flag
|
||||
self.connect()
|
||||
@@ -269,11 +268,6 @@ class TcpClient(object):
|
||||
self.buffer.extend(received)
|
||||
# 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":
|
||||
messages = self.read_beast_buffer()
|
||||
elif self.datatype == "raw":
|
||||
@@ -286,22 +280,12 @@ class TcpClient(object):
|
||||
else:
|
||||
self.handle_messages(messages)
|
||||
|
||||
except Exception as e:
|
||||
# Provides the user an option to supply the environment
|
||||
# variable PYMODES_DEBUG to halt the execution
|
||||
# for debugging purposes
|
||||
debug_intent = os.environ.get("PYMODES_DEBUG", "false")
|
||||
if debug_intent.lower() == "true":
|
||||
traceback.print_exc()
|
||||
sys.exit()
|
||||
else:
|
||||
print("Unexpected Error:", e)
|
||||
# raise RuntimeError("test exception")
|
||||
|
||||
try:
|
||||
sock = self.connect()
|
||||
time.sleep(1)
|
||||
except Exception as e:
|
||||
print("Unexpected Error:", e)
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
exception_queue.put(tb)
|
||||
raise e
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from __future__ import absolute_import, print_function, division
|
||||
import numpy as np
|
||||
from textwrap import wrap
|
||||
|
||||
@@ -15,34 +14,18 @@ def hex2int(hexstr):
|
||||
return int(hexstr, 16)
|
||||
|
||||
|
||||
def int2hex(n):
|
||||
"""Convert a integer to hexadecimal string."""
|
||||
# strip 'L' for python 2
|
||||
return hex(n)[2:].rjust(6, "0").upper().rstrip("L")
|
||||
|
||||
|
||||
def bin2int(binstr):
|
||||
"""Convert a binary string to integer."""
|
||||
return int(binstr, 2)
|
||||
|
||||
|
||||
def bin2hex(hexstr):
|
||||
"""Convert a hexdecimal string to integer."""
|
||||
return int2hex(bin2int(hexstr))
|
||||
|
||||
|
||||
def bin2np(binstr):
|
||||
"""Convert a binary string to numpy array."""
|
||||
return np.array([int(i) for i in binstr])
|
||||
|
||||
|
||||
def np2bin(npbin):
|
||||
"""Convert a binary numpy array to string."""
|
||||
return np.array2string(npbin, separator="")[1:-1]
|
||||
def bin2hex(binstr):
|
||||
"""Convert a binary string to hexdecimal string."""
|
||||
return "{0:X}".format(int(binstr, 2))
|
||||
|
||||
|
||||
def df(msg):
|
||||
"""Decode Downlink Format vaule, bits 1 to 5."""
|
||||
"""Decode Downlink Format value, bits 1 to 5."""
|
||||
dfbin = hex2bin(msg[:2])
|
||||
return min(bin2int(dfbin[0:5]), 24)
|
||||
|
||||
@@ -100,7 +83,7 @@ def crc_legacy(msg, encode=False):
|
||||
)
|
||||
ng = len(generator)
|
||||
|
||||
msgnpbin = bin2np(hex2bin(msg))
|
||||
msgnpbin = np.array([int(i) for i in hex2bin(msg)])
|
||||
|
||||
if encode:
|
||||
msgnpbin[-24:] = [0] * 24
|
||||
@@ -114,7 +97,9 @@ def crc_legacy(msg, encode=False):
|
||||
msgnpbin[i : i + ng] = np.bitwise_xor(msgnpbin[i : i + ng], generator)
|
||||
|
||||
# last 24 bits
|
||||
reminder = bin2int(np2bin(msgnpbin[-24:]))
|
||||
msgbin = np.array2string(msgnpbin[-24:], separator="")[1:-1]
|
||||
reminder = bin2int(msgbin)
|
||||
|
||||
return reminder
|
||||
|
||||
|
||||
@@ -146,7 +131,7 @@ def icao(msg):
|
||||
addr = msg[2:8]
|
||||
elif DF in (0, 4, 5, 16, 20, 21):
|
||||
c0 = crc(msg, encode=True)
|
||||
c1 = hex2int(msg[-6:])
|
||||
c1 = int(msg[-6:], 16)
|
||||
addr = "%06X" % (c0 ^ c1)
|
||||
else:
|
||||
addr = None
|
||||
@@ -159,7 +144,7 @@ def is_icao_assigned(icao):
|
||||
if (icao is None) or (not isinstance(icao, str)) or (len(icao) != 6):
|
||||
return False
|
||||
|
||||
icaoint = hex2int(icao)
|
||||
icaoint = int(icao, 16)
|
||||
|
||||
if 0x200000 < icaoint < 0x27FFFF:
|
||||
return False # AFI
|
||||
@@ -202,13 +187,11 @@ def typecode(msg):
|
||||
def cprNL(lat):
|
||||
"""NL() function in CPR decoding."""
|
||||
|
||||
if lat == 0:
|
||||
if np.isclose(lat, 0):
|
||||
return 59
|
||||
|
||||
if lat == 87 or lat == -87:
|
||||
elif np.isclose(abs(lat), 87):
|
||||
return 2
|
||||
|
||||
if lat > 87 or lat < -87:
|
||||
elif lat > 87 or lat < -87:
|
||||
return 1
|
||||
|
||||
nz = 15
|
||||
@@ -1,4 +1,3 @@
|
||||
from __future__ import absolute_import, print_function, division
|
||||
import os
|
||||
import time
|
||||
import datetime
|
||||
@@ -27,7 +26,7 @@ class Decode:
|
||||
self.dumpto = None
|
||||
|
||||
def process_raw(self, adsb_ts, adsb_msg, commb_ts, commb_msg, tnow=None):
|
||||
"""process a chunk of adsb and commb messages recieved in the same
|
||||
"""process a chunk of adsb and commb messages received in the same
|
||||
time period.
|
||||
"""
|
||||
if tnow is None:
|
||||
@@ -259,26 +258,31 @@ class Decode:
|
||||
return
|
||||
|
||||
def get_aircraft(self):
|
||||
"""all aircraft that are stored in memeory"""
|
||||
"""all aircraft that are stored in memory"""
|
||||
acs = self.acs
|
||||
return acs
|
||||
|
||||
def run(self, raw_pipe_out, ac_pipe_in):
|
||||
def run(self, raw_pipe_out, ac_pipe_in, exception_queue):
|
||||
local_buffer = []
|
||||
while True:
|
||||
while raw_pipe_out.poll():
|
||||
data = raw_pipe_out.recv()
|
||||
local_buffer.append(data)
|
||||
try:
|
||||
while raw_pipe_out.poll():
|
||||
data = raw_pipe_out.recv()
|
||||
local_buffer.append(data)
|
||||
|
||||
for data in local_buffer:
|
||||
self.process_raw(
|
||||
data["adsb_ts"],
|
||||
data["adsb_msg"],
|
||||
data["commb_ts"],
|
||||
data["commb_msg"],
|
||||
)
|
||||
local_buffer = []
|
||||
for data in local_buffer:
|
||||
self.process_raw(
|
||||
data["adsb_ts"],
|
||||
data["adsb_msg"],
|
||||
data["commb_ts"],
|
||||
data["commb_msg"],
|
||||
)
|
||||
local_buffer = []
|
||||
|
||||
acs = self.get_aircraft()
|
||||
ac_pipe_in.send(acs)
|
||||
time.sleep(0.001)
|
||||
acs = self.get_aircraft()
|
||||
ac_pipe_in.send(acs)
|
||||
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
|
||||
|
||||
from __future__ import print_function, division
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import argparse
|
||||
import curses
|
||||
import signal
|
||||
@@ -15,7 +15,6 @@ 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"]
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
@@ -71,7 +70,7 @@ elif SOURCE == "net":
|
||||
else:
|
||||
SERVER, PORT, DATATYPE = args.connect
|
||||
if DATATYPE not in support_rawtypes:
|
||||
print("Data type not supported, avaiable ones are %s" % support_rawtypes)
|
||||
print("Data type not supported, available ones are %s" % support_rawtypes)
|
||||
|
||||
else:
|
||||
print('Source must be "rtlsdr" or "net".')
|
||||
@@ -87,13 +86,9 @@ if DUMPTO is not None:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# raw_event = multiprocessing.Event()
|
||||
# ac_event = multiprocessing.Event()
|
||||
# raw_queue = multiprocessing.Queue()
|
||||
# ac_queue = multiprocessing.Queue()
|
||||
|
||||
raw_pipe_in, raw_pipe_out = multiprocessing.Pipe()
|
||||
ac_pipe_in, ac_pipe_out = multiprocessing.Pipe()
|
||||
exception_queue = multiprocessing.Queue()
|
||||
stop_flag = multiprocessing.Value("b", False)
|
||||
|
||||
if SOURCE == "net":
|
||||
@@ -102,29 +97,38 @@ elif SOURCE == "rtlsdr":
|
||||
source = RtlSdrSource()
|
||||
|
||||
|
||||
recv_process = multiprocessing.Process(target=source.run, args=(raw_pipe_in, stop_flag))
|
||||
recv_process = multiprocessing.Process(
|
||||
target=source.run, args=(raw_pipe_in, stop_flag, exception_queue)
|
||||
)
|
||||
|
||||
|
||||
decode = Decode(latlon=LATLON, dumpto=DUMPTO)
|
||||
decode_process = multiprocessing.Process(
|
||||
target=decode.run, args=(raw_pipe_out, ac_pipe_in)
|
||||
target=decode.run, args=(raw_pipe_out, ac_pipe_in, exception_queue)
|
||||
)
|
||||
|
||||
screen = Screen(uncertainty=UNCERTAINTY)
|
||||
screen_process = multiprocessing.Process(target=screen.run, args=(ac_pipe_out,))
|
||||
screen_process = multiprocessing.Process(
|
||||
target=screen.run, args=(ac_pipe_out, exception_queue)
|
||||
)
|
||||
|
||||
|
||||
def closeall(signal, frame):
|
||||
print("KeyboardInterrupt (ID: {}). Cleaning up...".format(signal))
|
||||
def shutdown():
|
||||
stop_flag.value = True
|
||||
curses.endwin()
|
||||
sys.stdout = sys.__stdout__
|
||||
recv_process.terminate()
|
||||
decode_process.terminate()
|
||||
screen_process.terminate()
|
||||
recv_process.join()
|
||||
decode_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)
|
||||
@@ -132,3 +136,19 @@ signal.signal(signal.SIGINT, closeall)
|
||||
recv_process.start()
|
||||
decode_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 numpy as np
|
||||
import time
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
COLUMNS = [
|
||||
("call", 10),
|
||||
@@ -187,24 +187,32 @@ class Screen(object):
|
||||
self.screen.refresh()
|
||||
self.draw_frame()
|
||||
|
||||
def run(self, ac_pipe_out):
|
||||
def run(self, ac_pipe_out, exception_queue):
|
||||
local_buffer = []
|
||||
key_thread = threading.Thread(target=self.kye_handling)
|
||||
key_thread.daemon = True
|
||||
key_thread.start()
|
||||
|
||||
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:
|
||||
# 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()
|
||||
except:
|
||||
except curses.error:
|
||||
pass
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
exception_queue.put(tb)
|
||||
time.sleep(0.1)
|
||||
raise e
|
||||
|
||||
time.sleep(0.001)
|
||||
|
||||
75
setup.py
75
setup.py
@@ -4,11 +4,10 @@ See:
|
||||
https://packaging.python.org/en/latest/distributing.html
|
||||
https://github.com/pypa/sampleproject
|
||||
|
||||
Steps for deploying a new verison:
|
||||
Steps for deploying a new version:
|
||||
1. Increase the version number
|
||||
2. remove the old deployment under [dist] and [build] folder
|
||||
3. run: python setup.py sdist
|
||||
run: python setup.py bdist_wheel --universal
|
||||
4. twine upload dist/*
|
||||
"""
|
||||
|
||||
@@ -25,77 +24,37 @@ here = path.abspath(path.dirname(__file__))
|
||||
with open(path.join(here, "README.rst"), encoding="utf-8") as f:
|
||||
long_description = f.read()
|
||||
|
||||
setup(
|
||||
|
||||
details = dict(
|
||||
name="pyModeS",
|
||||
# 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",
|
||||
version="2.8",
|
||||
description="Python Mode-S and ADS-B Decoder",
|
||||
long_description=long_description,
|
||||
# The project's main homepage.
|
||||
url="https://github.com/junzis/pyModeS",
|
||||
# Author details
|
||||
author="Junzi Sun",
|
||||
author_email="j.sun-1@tudelft.nl",
|
||||
# Choose your license
|
||||
license="GNU GPL v3",
|
||||
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
classifiers=[
|
||||
# How mature is this project? Common values are
|
||||
# 3 - Alpha
|
||||
# 4 - Beta
|
||||
# 5 - Production/Stable
|
||||
"Development Status :: 4 - Beta",
|
||||
# Indicate who your project is intended for
|
||||
"Intended Audience :: Developers",
|
||||
"Topic :: Software Development :: Libraries",
|
||||
# Pick your license as you wish (should match "license" above)
|
||||
"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",
|
||||
],
|
||||
# What does your project relate to?
|
||||
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"]),
|
||||
# Alternatively, if you want to distribute just a my_module.py, uncomment
|
||||
# this:
|
||||
# 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',
|
||||
# ],
|
||||
# },
|
||||
install_requires=["numpy", "pyzmq", "pyrtlsdr"],
|
||||
package_data={"pyModeS": ["*.pyx", "*.pxd"]},
|
||||
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)
|
||||
|
||||
130
tests/benchmark.py
Normal file
130
tests/benchmark.py
Normal file
@@ -0,0 +1,130 @@
|
||||
import sys
|
||||
import time
|
||||
import pandas as pd
|
||||
from tqdm import tqdm
|
||||
from pyModeS.decoder import adsb
|
||||
|
||||
fin = sys.argv[1]
|
||||
|
||||
df = pd.read_csv(fin, names=["ts", "df", "icao", "msg"])
|
||||
df_adsb = df[df["df"] == 17].copy()
|
||||
|
||||
total = df_adsb.shape[0]
|
||||
|
||||
|
||||
def native():
|
||||
|
||||
from pyModeS.decoder import common
|
||||
|
||||
# airborne position
|
||||
m_air_0 = None
|
||||
m_air_1 = None
|
||||
|
||||
# surface position
|
||||
m_surf_0 = None
|
||||
m_surf_1 = None
|
||||
|
||||
for i, r in tqdm(df_adsb.iterrows(), total=total):
|
||||
ts = r.ts
|
||||
m = r.msg
|
||||
|
||||
downlink_format = common.df(m)
|
||||
crc = common.crc(m)
|
||||
icao = adsb.icao(m)
|
||||
tc = adsb.typecode(m)
|
||||
|
||||
if 1 <= tc <= 4:
|
||||
category = adsb.category(m)
|
||||
callsign = adsb.callsign(m)
|
||||
if tc == 19:
|
||||
velocity = adsb.velocity(m)
|
||||
|
||||
if 5 <= tc <= 8:
|
||||
if adsb.oe_flag(m):
|
||||
m_surf_1 = m
|
||||
t1 = ts
|
||||
else:
|
||||
m_surf_0 = m
|
||||
t0 = ts
|
||||
|
||||
if m_surf_0 and m_surf_1:
|
||||
position = adsb.surface_position(
|
||||
m_surf_0, m_surf_1, t0, t1, 50.01, 4.35
|
||||
)
|
||||
altitude = adsb.altitude(m)
|
||||
|
||||
if 9 <= tc <= 18:
|
||||
if adsb.oe_flag(m):
|
||||
m_air_1 = m
|
||||
t1 = ts
|
||||
else:
|
||||
m_air_0 = m
|
||||
t0 = ts
|
||||
|
||||
if m_air_0 and m_air_1:
|
||||
position = adsb.position(m_air_0, m_air_1, t0, t1)
|
||||
altitude = adsb.altitude(m)
|
||||
|
||||
|
||||
def cython():
|
||||
|
||||
from pyModeS.decoder import c_common as common
|
||||
|
||||
# airborne position
|
||||
m_air_0 = None
|
||||
m_air_1 = None
|
||||
|
||||
# surface position
|
||||
m_surf_0 = None
|
||||
m_surf_1 = None
|
||||
|
||||
for i, r in tqdm(df_adsb.iterrows(), total=total):
|
||||
ts = r.ts
|
||||
m = r.msg
|
||||
|
||||
downlink_format = common.df(m)
|
||||
crc = common.crc(m)
|
||||
icao = adsb.icao(m)
|
||||
tc = adsb.typecode(m)
|
||||
|
||||
if 1 <= tc <= 4:
|
||||
category = adsb.category(m)
|
||||
callsign = adsb.callsign(m)
|
||||
if tc == 19:
|
||||
velocity = adsb.velocity(m)
|
||||
|
||||
if 5 <= tc <= 8:
|
||||
if adsb.oe_flag(m):
|
||||
m_surf_1 = m
|
||||
t1 = ts
|
||||
else:
|
||||
m_surf_0 = m
|
||||
t0 = ts
|
||||
|
||||
if m_surf_0 and m_surf_1:
|
||||
position = adsb.surface_position(
|
||||
m_surf_0, m_surf_1, t0, t1, 50.01, 4.35
|
||||
)
|
||||
altitude = adsb.altitude(m)
|
||||
|
||||
if 9 <= tc <= 18:
|
||||
if adsb.oe_flag(m):
|
||||
m_air_1 = m
|
||||
t1 = ts
|
||||
else:
|
||||
m_air_0 = m
|
||||
t0 = ts
|
||||
|
||||
if m_air_0 and m_air_1:
|
||||
position = adsb.position(m_air_0, m_air_1, t0, t1)
|
||||
altitude = adsb.altitude(m)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
t1 = time.time()
|
||||
native()
|
||||
dt1 = time.time() - t1
|
||||
|
||||
t2 = time.time()
|
||||
cython()
|
||||
dt2 = time.time() - t2
|
||||
@@ -1,44 +1,46 @@
|
||||
from __future__ import print_function
|
||||
from pyModeS import adsb, ehs
|
||||
import sys
|
||||
import time
|
||||
import csv
|
||||
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "cython":
|
||||
from pyModeS.c_decoder import adsb
|
||||
else:
|
||||
from pyModeS.decoder import adsb
|
||||
|
||||
print("===== Decode ADS-B sample data=====")
|
||||
|
||||
f = open("tests/data/sample_data_adsb.csv", "rt")
|
||||
|
||||
msg0 = None
|
||||
msg1 = None
|
||||
|
||||
tstart = time.time()
|
||||
for i, r in enumerate(csv.reader(f)):
|
||||
|
||||
ts = int(r[0])
|
||||
m = r[1].encode()
|
||||
|
||||
icao = adsb.icao(m)
|
||||
tc = adsb.typecode(m)
|
||||
|
||||
if 1 <= tc <= 4:
|
||||
print(ts, m, icao, tc, adsb.category(m), adsb.callsign(m))
|
||||
if tc == 19:
|
||||
print(ts, m, icao, tc, adsb.velocity(m))
|
||||
if 5 <= tc <= 18:
|
||||
if adsb.oe_flag(m):
|
||||
msg1 = m
|
||||
t1 = ts
|
||||
else:
|
||||
msg0 = m
|
||||
t0 = ts
|
||||
|
||||
if msg0 and msg1:
|
||||
pos = adsb.position(msg0, msg1, t0, t1)
|
||||
alt = adsb.altitude(m)
|
||||
print(ts, m, icao, tc, pos, alt)
|
||||
|
||||
|
||||
# === Decode sample data file ===
|
||||
dt = time.time() - tstart
|
||||
|
||||
|
||||
def adsb_decode_all(n=None):
|
||||
print("===== Decode ADS-B sample data=====")
|
||||
import csv
|
||||
|
||||
f = open("tests/data/sample_data_adsb.csv", "rt")
|
||||
|
||||
msg0 = None
|
||||
msg1 = None
|
||||
|
||||
for i, r in enumerate(csv.reader(f)):
|
||||
if n and i > n:
|
||||
break
|
||||
|
||||
ts = r[0]
|
||||
m = r[1]
|
||||
icao = adsb.icao(m)
|
||||
tc = adsb.typecode(m)
|
||||
if 1 <= tc <= 4:
|
||||
print(ts, m, icao, tc, adsb.category(m), adsb.callsign(m))
|
||||
if tc == 19:
|
||||
print(ts, m, icao, tc, adsb.velocity(m))
|
||||
if 5 <= tc <= 18:
|
||||
if adsb.oe_flag(m):
|
||||
msg1 = m
|
||||
t1 = ts
|
||||
else:
|
||||
msg0 = m
|
||||
t0 = ts
|
||||
|
||||
if msg0 and msg1:
|
||||
pos = adsb.position(msg0, msg1, t0, t1)
|
||||
alt = adsb.altitude(m)
|
||||
print(ts, m, icao, tc, pos, alt)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
adsb_decode_all(n=100)
|
||||
print("Execution time: {} seconds".format(dt))
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from __future__ import print_function
|
||||
from pyModeS import commb, common, bds
|
||||
|
||||
# === Decode sample data file ===
|
||||
|
||||
62
tests/test_c_common.py
Normal file
62
tests/test_c_common.py
Normal file
@@ -0,0 +1,62 @@
|
||||
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
|
||||
@@ -1,64 +0,0 @@
|
||||
from pyModeS import common
|
||||
|
||||
|
||||
def test_conversions():
|
||||
assert common.hex2bin("6E406B") == "011011100100000001101011"
|
||||
assert common.bin2hex("011011100100000001101011") == "6E406B"
|
||||
assert common.int2hex(11160538) == "AA4BDA"
|
||||
|
||||
|
||||
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 common.int2hex(parity) == "AA4BDA"
|
||||
|
||||
|
||||
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
|
||||
64
tests/test_py_common.py
Normal file
64
tests/test_py_common.py
Normal file
@@ -0,0 +1,64 @@
|
||||
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
|
||||
Reference in New Issue
Block a user