Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
555b2eea40 | ||
|
|
dea7cde317 | ||
|
|
e16d34bc06 | ||
|
|
9bb87b00be | ||
|
|
3dae0438bf | ||
|
|
02c5117de5 | ||
|
|
3f24f78d3a | ||
|
|
cf3828d2a0 | ||
|
|
f70d1f2f1f | ||
|
|
dfeb65fbd7 | ||
|
|
13b283666a | ||
|
|
6144b88188 | ||
|
|
b503beb3fd | ||
|
|
c804cd876c | ||
|
|
d48caed7e6 | ||
|
|
eb675d5ca3 | ||
|
|
b04a1bd49c |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,6 +5,9 @@ __pycache__/
|
|||||||
*.py[cod]
|
*.py[cod]
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|
||||||
|
#cython
|
||||||
|
.c
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
|
|||||||
19
Makefile
Normal file
19
Makefile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
install:
|
||||||
|
pip install . --upgrade
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
pip uninstall pyModeS -y
|
||||||
|
|
||||||
|
ext:
|
||||||
|
python setup.py build_ext --inplace
|
||||||
|
|
||||||
|
test:
|
||||||
|
python -m pytest
|
||||||
|
|
||||||
|
clean:
|
||||||
|
find pyModeS/decoder -type f -name '*.c' -delete
|
||||||
|
find pyModeS/decoder -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/*
|
||||||
21
README.rst
21
README.rst
@@ -141,7 +141,7 @@ Common functions
|
|||||||
pms.hex2bin(str) # Convert hexadecimal string to binary string
|
pms.hex2bin(str) # Convert hexadecimal string to binary string
|
||||||
pms.bin2int(str) # Convert binary string to integer
|
pms.bin2int(str) # Convert binary string to integer
|
||||||
pms.hex2int(str) # Convert hexadecimal string to integer
|
pms.hex2int(str) # Convert hexadecimal string to integer
|
||||||
pms.gray2int(str) # Convert grey code to integer
|
pms.gray2int(str) # Convert grey code to interger
|
||||||
|
|
||||||
|
|
||||||
Core functions for ADS-B decoding
|
Core functions for ADS-B decoding
|
||||||
@@ -155,7 +155,7 @@ Core functions for ADS-B decoding
|
|||||||
# Typecode 1-4
|
# Typecode 1-4
|
||||||
pms.adsb.callsign(msg)
|
pms.adsb.callsign(msg)
|
||||||
|
|
||||||
# Typecode 5-8 (surface), 9-18 (airborne, barometric height), and 20-22 (airborne, GNSS height)
|
# Typecode 5-8 (surface), 9-18 (airborne, barometric height), and 9-18 (airborne, GNSS height)
|
||||||
pms.adsb.position(msg_even, msg_odd, t_even, t_odd, lat_ref=None, lon_ref=None)
|
pms.adsb.position(msg_even, msg_odd, t_even, t_odd, lat_ref=None, lon_ref=None)
|
||||||
pms.adsb.airborne_position(msg_even, msg_odd, t_even, t_odd)
|
pms.adsb.airborne_position(msg_even, msg_odd, t_even, t_odd)
|
||||||
pms.adsb.surface_position(msg_even, msg_odd, t_even, t_odd, lat_ref, lon_ref)
|
pms.adsb.surface_position(msg_even, msg_odd, t_even, t_odd, lat_ref, lon_ref)
|
||||||
@@ -321,8 +321,19 @@ Here is an example:
|
|||||||
|
|
||||||
Unit test
|
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
|
||||||
|
|||||||
@@ -3,11 +3,16 @@ from __future__ import absolute_import, print_function, division
|
|||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from .decoder.common import *
|
try:
|
||||||
|
from .decoder import c_common as common
|
||||||
|
from .decoder.c_common import *
|
||||||
|
except:
|
||||||
|
from .decoder import common
|
||||||
|
from .decoder.common import *
|
||||||
|
|
||||||
from .decoder import tell
|
from .decoder import tell
|
||||||
from .decoder import adsb
|
from .decoder import adsb
|
||||||
from .decoder import commb
|
from .decoder import commb
|
||||||
from .decoder import common
|
|
||||||
from .decoder import bds
|
from .decoder import bds
|
||||||
from .extra import aero
|
from .extra import aero
|
||||||
from .extra import tcpclient
|
from .extra import tcpclient
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ def tell(msg):
|
|||||||
_print("Downlink Format", df)
|
_print("Downlink Format", df)
|
||||||
|
|
||||||
if df == 17:
|
if df == 17:
|
||||||
_print("Protocol", "Mode-S Extended Squitter (ADS-B)")
|
_print("Protocal", "Mode-S Extended Squitter (ADS-B)")
|
||||||
|
|
||||||
tc = common.typecode(msg)
|
tc = common.typecode(msg)
|
||||||
if 1 <= tc <= 4: # callsign
|
if 1 <= tc <= 4: # callsign
|
||||||
@@ -29,7 +29,7 @@ def tell(msg):
|
|||||||
_print("Callsign:", callsign)
|
_print("Callsign:", callsign)
|
||||||
|
|
||||||
if 5 <= tc <= 8: # surface position
|
if 5 <= tc <= 8: # surface position
|
||||||
_print("Type", "Surface position")
|
_print("Type", "Surface postition")
|
||||||
oe = adsb.oe_flag(msg)
|
oe = adsb.oe_flag(msg)
|
||||||
msgbin = common.hex2bin(msg)
|
msgbin = common.hex2bin(msg)
|
||||||
cprlat = common.bin2int(msgbin[54:71]) / 131072.0
|
cprlat = common.bin2int(msgbin[54:71]) / 131072.0
|
||||||
@@ -75,11 +75,11 @@ def tell(msg):
|
|||||||
_print("Altitude", alt, "feet")
|
_print("Altitude", alt, "feet")
|
||||||
|
|
||||||
if df == 20:
|
if df == 20:
|
||||||
_print("Protocol", "Mode-S Comm-B altitude reply")
|
_print("Protocal", "Mode-S Comm-B altitude reply")
|
||||||
_print("Altitude", common.altcode(msg), "feet")
|
_print("Altitude", common.altcode(msg), "feet")
|
||||||
|
|
||||||
if df == 21:
|
if df == 21:
|
||||||
_print("Protocol", "Mode-S Comm-B identity reply")
|
_print("Protocal", "Mode-S Comm-B identity reply")
|
||||||
_print("Squawk code", common.idcode(msg))
|
_print("Squawk code", common.idcode(msg))
|
||||||
|
|
||||||
if df == 20 or df == 21:
|
if df == 20 or df == 21:
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
Decoding Air-Air Surveillance (ACAS) DF=0/16
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# Copyright (C) 2015 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/>.
|
|
||||||
|
|
||||||
"""ADS-B Wrapper.
|
"""ADS-B Wrapper.
|
||||||
|
|
||||||
The ADS-B wrapper also imports functions from the following modules:
|
The ADS-B wrapper also imports functions from the following modules:
|
||||||
@@ -38,7 +23,7 @@ from pyModeS.decoder import uncertainty
|
|||||||
from pyModeS.decoder.bds.bds05 import (
|
from pyModeS.decoder.bds.bds05 import (
|
||||||
airborne_position,
|
airborne_position,
|
||||||
airborne_position_with_ref,
|
airborne_position_with_ref,
|
||||||
altitude,
|
altitude as altitude05,
|
||||||
)
|
)
|
||||||
from pyModeS.decoder.bds.bds06 import (
|
from pyModeS.decoder.bds.bds06 import (
|
||||||
surface_position,
|
surface_position,
|
||||||
@@ -96,7 +81,7 @@ def position(msg0, msg1, t0, t1, lat_ref=None, lon_ref=None):
|
|||||||
return airborne_position(msg0, msg1, t0, t1)
|
return airborne_position(msg0, msg1, t0, t1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("incorrect or inconsistent message types")
|
raise RuntimeError("incorrect or inconsistant message types")
|
||||||
|
|
||||||
|
|
||||||
def position_with_ref(msg, lat_ref, lon_ref):
|
def position_with_ref(msg, lat_ref, lon_ref):
|
||||||
@@ -125,7 +110,7 @@ def position_with_ref(msg, lat_ref, lon_ref):
|
|||||||
return airborne_position_with_ref(msg, lat_ref, lon_ref)
|
return airborne_position_with_ref(msg, lat_ref, lon_ref)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("incorrect or inconsistent message types")
|
raise RuntimeError("incorrect or inconsistant message types")
|
||||||
|
|
||||||
|
|
||||||
def altitude(msg):
|
def altitude(msg):
|
||||||
@@ -143,18 +128,13 @@ def altitude(msg):
|
|||||||
if tc < 5 or tc == 19 or tc > 22:
|
if tc < 5 or tc == 19 or tc > 22:
|
||||||
raise RuntimeError("%s: Not a position message" % msg)
|
raise RuntimeError("%s: Not a position message" % msg)
|
||||||
|
|
||||||
if tc >= 5 and tc <= 8:
|
elif tc >= 5 and tc <= 8:
|
||||||
# surface position, altitude 0
|
# surface position, altitude 0
|
||||||
return 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:
|
else:
|
||||||
return None
|
# airborn position
|
||||||
|
return altitude05(msg)
|
||||||
|
|
||||||
|
|
||||||
def velocity(msg, rtn_sources=False):
|
def velocity(msg, rtn_sources=False):
|
||||||
@@ -174,7 +154,7 @@ def velocity(msg, rtn_sources=False):
|
|||||||
rate of climb/descent (ft/min), speed type
|
rate of climb/descent (ft/min), speed type
|
||||||
('GS' for ground speed, 'AS' for airspeed),
|
('GS' for ground speed, 'AS' for airspeed),
|
||||||
direction source ('true_north' for ground track / true north
|
direction source ('true_north' for ground track / true north
|
||||||
as reference, 'mag_north' for magnetic north as reference),
|
as refrence, 'mag_north' for magnetic north as reference),
|
||||||
rate of climb/descent source ('Baro' for barometer, 'GNSS'
|
rate of climb/descent source ('Baro' for barometer, 'GNSS'
|
||||||
for GNSS constellation).
|
for GNSS constellation).
|
||||||
|
|
||||||
@@ -190,7 +170,7 @@ def velocity(msg, rtn_sources=False):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"incorrect or inconsistent message types, expecting 4<TC<9 or TC=19"
|
"incorrect or inconsistant message types, expecting 4<TC<9 or TC=19"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -524,7 +504,7 @@ def sil(msg, version):
|
|||||||
|
|
||||||
if tc not in [29, 31]:
|
if tc not in [29, 31]:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"%s: Not a target state and status message, \
|
"%s: Not a target state and status messag, \
|
||||||
or operation status message, expecting TC = 29 or 31"
|
or operation status message, expecting TC = 29 or 31"
|
||||||
% msg
|
% msg
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
Decoding all call replies DF=11
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ def infer(msg, mrar=False):
|
|||||||
tc = common.typecode(msg)
|
tc = common.typecode(msg)
|
||||||
|
|
||||||
if 1 <= tc <= 4:
|
if 1 <= tc <= 4:
|
||||||
return "BDS08" # identification and category
|
return "BDS08" # indentification and category
|
||||||
if 5 <= tc <= 8:
|
if 5 <= tc <= 8:
|
||||||
return "BDS06" # surface movement
|
return "BDS06" # surface movement
|
||||||
if 9 <= tc <= 18:
|
if 9 <= tc <= 18:
|
||||||
|
|||||||
@@ -1,19 +1,3 @@
|
|||||||
# 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
|
# BDS 0,5
|
||||||
# ADS-B TC=9-18
|
# ADS-B TC=9-18
|
||||||
|
|||||||
@@ -1,19 +1,3 @@
|
|||||||
# 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
|
# BDS 0,6
|
||||||
# ADS-B TC=5-8
|
# ADS-B TC=5-8
|
||||||
@@ -175,7 +159,7 @@ def surface_velocity(msg, rtn_sources=False):
|
|||||||
else:
|
else:
|
||||||
trk = None
|
trk = None
|
||||||
|
|
||||||
# ground movement / speed
|
# ground movment / speed
|
||||||
mov = common.bin2int(mb[5:12])
|
mov = common.bin2int(mb[5:12])
|
||||||
|
|
||||||
if mov == 0 or mov > 124:
|
if mov == 0 or mov > 124:
|
||||||
|
|||||||
@@ -1,19 +1,3 @@
|
|||||||
# 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
|
# BDS 0,8
|
||||||
# ADS-B TC=1-4
|
# ADS-B TC=1-4
|
||||||
|
|||||||
@@ -1,19 +1,3 @@
|
|||||||
# 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
|
# BDS 0,9
|
||||||
# ADS-B TC=19
|
# ADS-B TC=19
|
||||||
@@ -41,7 +25,7 @@ def airborne_velocity(msg, rtn_sources=False):
|
|||||||
rate of climb/descent (ft/min), speed type
|
rate of climb/descent (ft/min), speed type
|
||||||
('GS' for ground speed, 'AS' for airspeed),
|
('GS' for ground speed, 'AS' for airspeed),
|
||||||
direction source ('true_north' for ground track / true north
|
direction source ('true_north' for ground track / true north
|
||||||
as reference, 'mag_north' for magnetic north as reference),
|
as refrence, 'mag_north' for magnetic north as reference),
|
||||||
rate of climb/descent source ('Baro' for barometer, 'GNSS'
|
rate of climb/descent source ('Baro' for barometer, 'GNSS'
|
||||||
for GNSS constellation).
|
for GNSS constellation).
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
# BDS 1,0
|
||||||
# Data link capability report
|
# Data link capability report
|
||||||
@@ -45,7 +30,7 @@ def is10(msg):
|
|||||||
if bin2int(d[9:14]) != 0:
|
if bin2int(d[9:14]) != 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# overlay capability conflict
|
# overlay capabilty conflict
|
||||||
if d[14] == "1" and bin2int(d[16:23]) < 5:
|
if d[14] == "1" and bin2int(d[16:23]) < 5:
|
||||||
return False
|
return False
|
||||||
if d[14] == "0" and bin2int(d[16:23]) > 4:
|
if d[14] == "0" and bin2int(d[16:23]) > 4:
|
||||||
|
|||||||
@@ -1,19 +1,3 @@
|
|||||||
# 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
|
# BDS 1,7
|
||||||
# Common usage GICB capability report
|
# Common usage GICB capability report
|
||||||
@@ -63,7 +47,7 @@ def cap17(msg):
|
|||||||
msg (String): 28 bytes hexadecimal message string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: list of support BDS codes
|
list: list of suport BDS codes
|
||||||
"""
|
"""
|
||||||
allbds = [
|
allbds = [
|
||||||
"05",
|
"05",
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
# BDS 2,0
|
||||||
# Aircraft identification
|
# Aircraft identification
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
# BDS 3,0
|
||||||
# ACAS active resolution advisory
|
# ACAS active resolution advisory
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
# BDS 4,0
|
||||||
# Selected vertical intention
|
# Selected vertical intention
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
# BDS 4,4
|
||||||
# Meteorological routine air report
|
# Meteorological routine air report
|
||||||
@@ -97,7 +82,7 @@ def temp44(msg):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float, float: temperature and alternative temperature in Celsius degree.
|
float, float: temperature and alternative temperature in Celsius degree.
|
||||||
Note: Two values returns due to what seems to be an inconsistency
|
Note: Two values returns due to what seems to be an inconsistancy
|
||||||
error in ICAO 9871 (2008) Appendix A-67.
|
error in ICAO 9871 (2008) Appendix A-67.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
# BDS 4,5
|
||||||
# Meteorological hazard report
|
# Meteorological hazard report
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
# BDS 5,0
|
||||||
# Track and turn report
|
# Track and turn report
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
# BDS 5,3
|
||||||
# Air-referenced state vector
|
# Air-referenced state vector
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
# BDS 6,0
|
||||||
# Heading and speed report
|
# Heading and speed report
|
||||||
@@ -165,7 +150,7 @@ def vr60baro(msg):
|
|||||||
|
|
||||||
|
|
||||||
def vr60ins(msg):
|
def vr60ins(msg):
|
||||||
"""Vertical rate measurd by onbard equiments (IRS, AHRS)
|
"""Vertical rate messured by onbard equiments (IRS, AHRS)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (String): 28 bytes hexadecimal message (BDS60) string
|
msg (String): 28 bytes hexadecimal message (BDS60) string
|
||||||
|
|||||||
23
pyModeS/decoder/c_common.pxd
Normal file
23
pyModeS/decoder/c_common.pxd
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# 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 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)
|
||||||
|
|
||||||
|
cdef str data(str msg)
|
||||||
|
cpdef bint allzeros(str msg)
|
||||||
431
pyModeS/decoder/c_common.pyx
Normal file
431
pyModeS/decoder/c_common.pyx
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
# 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 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 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 lat == 0:
|
||||||
|
return 59
|
||||||
|
|
||||||
|
if lat == 87 or lat == -87:
|
||||||
|
return 2
|
||||||
|
|
||||||
|
if 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
|
||||||
|
|
||||||
|
|
||||||
|
cdef 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
|
||||||
@@ -15,34 +15,13 @@ def hex2int(hexstr):
|
|||||||
return int(hexstr, 16)
|
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):
|
def bin2int(binstr):
|
||||||
"""Convert a binary string to integer."""
|
"""Convert a binary string to integer."""
|
||||||
return int(binstr, 2)
|
return int(binstr, 2)
|
||||||
|
|
||||||
|
|
||||||
def bin2hex(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 df(msg):
|
def df(msg):
|
||||||
"""Decode Downlink Format value, bits 1 to 5."""
|
"""Decode Downlink Format vaule, bits 1 to 5."""
|
||||||
dfbin = hex2bin(msg[:2])
|
dfbin = hex2bin(msg[:2])
|
||||||
return min(bin2int(dfbin[0:5]), 24)
|
return min(bin2int(dfbin[0:5]), 24)
|
||||||
|
|
||||||
@@ -63,10 +42,8 @@ def crc(msg, encode=False):
|
|||||||
# the CRC generator
|
# the CRC generator
|
||||||
G = [int("11111111", 2), int("11111010", 2), int("00000100", 2), int("10000000", 2)]
|
G = [int("11111111", 2), int("11111010", 2), int("00000100", 2), int("10000000", 2)]
|
||||||
|
|
||||||
if encode and isinstance(msg, str):
|
if encode:
|
||||||
msg = msg[:-6] + "000000"
|
msg = msg[:-6] + "000000"
|
||||||
elif encode:
|
|
||||||
msg = msg[:-6] + b"000000"
|
|
||||||
|
|
||||||
msgbin = hex2bin(msg)
|
msgbin = hex2bin(msg)
|
||||||
msgbin_split = wrap(msgbin, 8)
|
msgbin_split = wrap(msgbin, 8)
|
||||||
@@ -102,7 +79,7 @@ def crc_legacy(msg, encode=False):
|
|||||||
)
|
)
|
||||||
ng = len(generator)
|
ng = len(generator)
|
||||||
|
|
||||||
msgnpbin = bin2np(hex2bin(msg))
|
msgnpbin = np.array([int(i) for i in hex2bin(msg)])
|
||||||
|
|
||||||
if encode:
|
if encode:
|
||||||
msgnpbin[-24:] = [0] * 24
|
msgnpbin[-24:] = [0] * 24
|
||||||
@@ -116,7 +93,9 @@ def crc_legacy(msg, encode=False):
|
|||||||
msgnpbin[i : i + ng] = np.bitwise_xor(msgnpbin[i : i + ng], generator)
|
msgnpbin[i : i + ng] = np.bitwise_xor(msgnpbin[i : i + ng], generator)
|
||||||
|
|
||||||
# last 24 bits
|
# last 24 bits
|
||||||
reminder = bin2int(np2bin(msgnpbin[-24:]))
|
msgbin = np.array2string(msgnpbin[-24:], separator="")[1:-1]
|
||||||
|
reminder = bin2int(msgbin)
|
||||||
|
|
||||||
return reminder
|
return reminder
|
||||||
|
|
||||||
|
|
||||||
@@ -148,7 +127,7 @@ def icao(msg):
|
|||||||
addr = msg[2:8]
|
addr = msg[2:8]
|
||||||
elif DF in (0, 4, 5, 16, 20, 21):
|
elif DF in (0, 4, 5, 16, 20, 21):
|
||||||
c0 = crc(msg, encode=True)
|
c0 = crc(msg, encode=True)
|
||||||
c1 = hex2int(msg[-6:])
|
c1 = int(msg[-6:], 16)
|
||||||
addr = "%06X" % (c0 ^ c1)
|
addr = "%06X" % (c0 ^ c1)
|
||||||
else:
|
else:
|
||||||
addr = None
|
addr = None
|
||||||
@@ -161,7 +140,7 @@ def is_icao_assigned(icao):
|
|||||||
if (icao is None) or (not isinstance(icao, str)) or (len(icao) != 6):
|
if (icao is None) or (not isinstance(icao, str)) or (len(icao) != 6):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
icaoint = hex2int(icao)
|
icaoint = int(icao, 16)
|
||||||
|
|
||||||
if 0x200000 < icaoint < 0x27FFFF:
|
if 0x200000 < icaoint < 0x27FFFF:
|
||||||
return False # AFI
|
return False # AFI
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
# 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
|
Warpper for short roll call surveillance replies DF=4/5
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
from pyModeS.decoder 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)
|
|
||||||
@@ -9,9 +9,9 @@ International Standard Atmosphere
|
|||||||
|
|
||||||
p,rho,T = atmos(H) # atmos as function of geopotential altitude H [m]
|
p,rho,T = atmos(H) # atmos as function of geopotential altitude H [m]
|
||||||
a = vsound(H) # speed of sound [m/s] as function of H[m]
|
a = vsound(H) # speed of sound [m/s] as function of H[m]
|
||||||
p = pressure(H) # calls atmos but returns only pressure [Pa]
|
p = pressure(H) # calls atmos but retruns only pressure [Pa]
|
||||||
T = temperature(H) # calculates temperature [K]
|
T = temperature(H) # calculates temperature [K]
|
||||||
rho = density(H) # calls atmos but returns only pressure [Pa]
|
rho = density(H) # calls atmos but retruns only pressure [Pa]
|
||||||
|
|
||||||
Speed conversion at altitude H[m] in ISA
|
Speed conversion at altitude H[m] in ISA
|
||||||
::
|
::
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ class TcpClient(object):
|
|||||||
Start character '$'
|
Start character '$'
|
||||||
|
|
||||||
MS field - Payload
|
MS field - Payload
|
||||||
Position 1 through 14:
|
Postion 1 through 14:
|
||||||
14 bytes = 112 bits
|
14 bytes = 112 bits
|
||||||
Mode-S payload
|
Mode-S payload
|
||||||
In case of DF types that only carry 7 bytes of information
|
In case of DF types that only carry 7 bytes of information
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class Decode:
|
|||||||
self.dumpto = None
|
self.dumpto = None
|
||||||
|
|
||||||
def process_raw(self, adsb_ts, adsb_msg, commb_ts, commb_msg, tnow=None):
|
def process_raw(self, adsb_ts, adsb_msg, commb_ts, commb_msg, tnow=None):
|
||||||
"""process a chunk of adsb and commb messages received in the same
|
"""process a chunk of adsb and commb messages recieved in the same
|
||||||
time period.
|
time period.
|
||||||
"""
|
"""
|
||||||
if tnow is None:
|
if tnow is None:
|
||||||
@@ -259,7 +259,7 @@ class Decode:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def get_aircraft(self):
|
def get_aircraft(self):
|
||||||
"""all aircraft that are stored in memory"""
|
"""all aircraft that are stored in memeory"""
|
||||||
acs = self.acs
|
acs = self.acs
|
||||||
return acs
|
return acs
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ elif SOURCE == "net":
|
|||||||
else:
|
else:
|
||||||
SERVER, PORT, DATATYPE = args.connect
|
SERVER, PORT, DATATYPE = args.connect
|
||||||
if DATATYPE not in support_rawtypes:
|
if DATATYPE not in support_rawtypes:
|
||||||
print("Data type not supported, available ones are %s" % support_rawtypes)
|
print("Data type not supported, avaiable ones are %s" % support_rawtypes)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print('Source must be "rtlsdr" or "net".')
|
print('Source must be "rtlsdr" or "net".')
|
||||||
|
|||||||
10
setup.py
10
setup.py
@@ -4,7 +4,7 @@ See:
|
|||||||
https://packaging.python.org/en/latest/distributing.html
|
https://packaging.python.org/en/latest/distributing.html
|
||||||
https://github.com/pypa/sampleproject
|
https://github.com/pypa/sampleproject
|
||||||
|
|
||||||
Steps for deploying a new version:
|
Steps for deploying a new verison:
|
||||||
1. Increase the version number
|
1. Increase the version number
|
||||||
2. remove the old deployment under [dist] and [build] folder
|
2. remove the old deployment under [dist] and [build] folder
|
||||||
3. run: python setup.py sdist
|
3. run: python setup.py sdist
|
||||||
@@ -15,6 +15,13 @@ Steps for deploying a new version:
|
|||||||
# Always prefer setuptools over distutils
|
# Always prefer setuptools over distutils
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
# Compile some parts
|
||||||
|
from setuptools.extension import Extension
|
||||||
|
from Cython.Build import cythonize
|
||||||
|
|
||||||
|
extensions = [Extension("pyModeS.decoder.c_common", ["pyModeS/decoder/c_common.pyx"])]
|
||||||
|
|
||||||
|
|
||||||
# To use a consistent encoding
|
# To use a consistent encoding
|
||||||
from codecs import open
|
from codecs import open
|
||||||
from os import path
|
from os import path
|
||||||
@@ -57,6 +64,7 @@ setup(
|
|||||||
"Programming Language :: Python :: 2",
|
"Programming Language :: Python :: 2",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
],
|
],
|
||||||
|
ext_modules=cythonize(extensions),
|
||||||
# What does your project relate to?
|
# What does your project relate to?
|
||||||
keywords="Mode-S ADS-B EHS ELS Comm-B",
|
keywords="Mode-S ADS-B EHS ELS Comm-B",
|
||||||
# You can just specify the packages manually here if your project is
|
# You can just specify the packages manually here if your project is
|
||||||
|
|||||||
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
|
import sys
|
||||||
from pyModeS import adsb, ehs
|
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
|
||||||
|
|
||||||
|
print("Execution time: {} seconds".format(dt))
|
||||||
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)
|
|
||||||
|
|||||||
60
tests/test_c_common.py
Normal file
60
tests/test_c_common.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
from pyModeS.decoder import c_common as common
|
||||||
|
|
||||||
|
|
||||||
|
def test_conversions():
|
||||||
|
assert common.hex2bin("6E406B") == "011011100100000001101011"
|
||||||
|
|
||||||
|
|
||||||
|
def test_crc_decode():
|
||||||
|
|
||||||
|
assert common.crc("8D406B902015A678D4D220AA4BDA") == 0
|
||||||
|
assert common.crc("8d8960ed58bf053cf11bc5932b7d") == 0
|
||||||
|
assert common.crc("8d45cab390c39509496ca9a32912") == 0
|
||||||
|
assert common.crc("8d74802958c904e6ef4ba0184d5c") == 0
|
||||||
|
assert common.crc("8d4400cd9b0000b4f87000e71a10") == 0
|
||||||
|
assert common.crc("8d4065de58a1054a7ef0218e226a") == 0
|
||||||
|
|
||||||
|
assert common.crc("c80b2dca34aa21dd821a04cb64d4") == 10719924
|
||||||
|
assert common.crc("a800089d8094e33a6004e4b8a522") == 4805588
|
||||||
|
assert common.crc("a8000614a50b6d32bed000bbe0ed") == 5659991
|
||||||
|
assert common.crc("a0000410bc900010a40000f5f477") == 11727682
|
||||||
|
assert common.crc("8d4ca251204994b1c36e60a5343d") == 16
|
||||||
|
assert common.crc("b0001718c65632b0a82040715b65") == 353333
|
||||||
|
|
||||||
|
|
||||||
|
def test_crc_encode():
|
||||||
|
parity = common.crc("8D406B902015A678D4D220AA4BDA", encode=True)
|
||||||
|
assert parity == 11160538
|
||||||
|
|
||||||
|
|
||||||
|
def test_icao():
|
||||||
|
assert common.icao("8D406B902015A678D4D220AA4BDA") == "406B90"
|
||||||
|
assert common.icao("A0001839CA3800315800007448D9") == "400940"
|
||||||
|
assert common.icao("A000139381951536E024D4CCF6B5") == "3C4DD2"
|
||||||
|
assert common.icao("A000029CFFBAA11E2004727281F1") == "4243D0"
|
||||||
|
|
||||||
|
|
||||||
|
def test_modes_altcode():
|
||||||
|
assert common.altcode("A02014B400000000000000F9D514") == 32300
|
||||||
|
|
||||||
|
|
||||||
|
def test_modes_idcode():
|
||||||
|
assert common.idcode("A800292DFFBBA9383FFCEB903D01") == "1346"
|
||||||
|
|
||||||
|
|
||||||
|
def test_graycode_to_altitude():
|
||||||
|
assert common.gray2alt("00000000010") == -1000
|
||||||
|
assert common.gray2alt("00000001010") == -500
|
||||||
|
assert common.gray2alt("00000011011") == -100
|
||||||
|
assert common.gray2alt("00000011010") == 0
|
||||||
|
assert common.gray2alt("00000011110") == 100
|
||||||
|
assert common.gray2alt("00000010011") == 600
|
||||||
|
assert common.gray2alt("00000110010") == 1000
|
||||||
|
assert common.gray2alt("00001001001") == 5800
|
||||||
|
assert common.gray2alt("00011100100") == 10300
|
||||||
|
assert common.gray2alt("01100011010") == 32000
|
||||||
|
assert common.gray2alt("01110000100") == 46300
|
||||||
|
assert common.gray2alt("01010101100") == 50200
|
||||||
|
assert common.gray2alt("11011110100") == 73200
|
||||||
|
assert common.gray2alt("10000000011") == 126600
|
||||||
|
assert common.gray2alt("10000000001") == 126700
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
from pyModeS import common
|
from pyModeS.decoder import common
|
||||||
|
|
||||||
|
|
||||||
def test_conversions():
|
def test_conversions():
|
||||||
assert common.hex2bin("6E406B") == "011011100100000001101011"
|
assert common.hex2bin("6E406B") == "011011100100000001101011"
|
||||||
assert common.bin2hex("011011100100000001101011") == "6E406B"
|
|
||||||
assert common.int2hex(11160538) == "AA4BDA"
|
|
||||||
|
|
||||||
|
|
||||||
def test_crc_decode():
|
def test_crc_decode():
|
||||||
@@ -28,7 +26,7 @@ def test_crc_decode():
|
|||||||
|
|
||||||
def test_crc_encode():
|
def test_crc_encode():
|
||||||
parity = common.crc("8D406B902015A678D4D220AA4BDA", encode=True)
|
parity = common.crc("8D406B902015A678D4D220AA4BDA", encode=True)
|
||||||
assert common.int2hex(parity) == "AA4BDA"
|
assert parity == 11160538
|
||||||
|
|
||||||
|
|
||||||
def test_icao():
|
def test_icao():
|
||||||
|
|||||||
Reference in New Issue
Block a user