This commit is contained in:
Xavier Olive 2019-10-29 16:53:39 +01:00
parent c804cd876c
commit b503beb3fd
5 changed files with 299 additions and 2 deletions

235
pyModeS/c_decoder/adsb.pyx Normal file
View File

@ -0,0 +1,235 @@
# 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/>.
# cython: language_level=3
"""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``
"""
from libc.math cimport NAN as nan
# from pyModeS.decoder.bds import bds05, bds06, bds09
from .common cimport typecode, icao as c_icao, df, hex2bin, bin2int, char_to_int
from .bds.bds05 import (
airborne_position,
airborne_position_with_ref,
altitude,
)
from .bds.bds06 import (
surface_position,
surface_position_with_ref,
surface_velocity,
)
from .bds.bds08 import category, callsign
from pyModeS.decoder.bds.bds09 import airborne_velocity, altitude_diff
def icao(bytes msg):
return c_icao(msg)
def position(bytes msg0 not None, bytes msg1 not None, int t0, int t1, double lat_ref=nan, double lon_ref=nan):
"""Decode position from a pair of even and odd position message
(works with both airborne and surface position messages)
Args:
msg0 (string): even message (28 bytes hexadecimal string)
msg1 (string): odd message (28 bytes hexadecimal string)
t0 (int): timestamps for the even message
t1 (int): timestamps for the odd message
Returns:
(float, float): (latitude, longitude) of the aircraft
"""
cdef int tc0 = typecode(msg0)
cdef int tc1 = typecode(msg1)
if 5 <= tc0 <= 8 and 5 <= tc1 <= 8:
if (lat_ref != lat_ref) or (lon_ref != lon_ref):
raise RuntimeError(
"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)
elif 9 <= tc0 <= 18 and 9 <= tc1 <= 18:
# Airborne position with barometric height
return airborne_position(msg0, msg1, t0, t1)
elif 20 <= tc0 <= 22 and 20 <= tc1 <= 22:
# Airborne position with GNSS height
return airborne_position(msg0, msg1, t0, t1)
else:
raise RuntimeError("incorrect or inconsistant message types")
def position_with_ref(bytes msg not None, double lat_ref, double 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.
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)
lat_ref: previous known latitude
lon_ref: previous known longitude
Returns:
(float, float): (latitude, longitude) of the aircraft
"""
cdef int tc = typecode(msg)
if 5 <= tc <= 8:
return surface_position_with_ref(msg, lat_ref, lon_ref)
elif 9 <= tc <= 18 or 20 <= tc <= 22:
return airborne_position_with_ref(msg, lat_ref, lon_ref)
else:
raise RuntimeError("incorrect or inconsistant message types")
def altitude(bytes msg):
"""Decode aircraft altitude
Args:
msg (string): 28 bytes hexadecimal message string
Returns:
int: altitude in feet
"""
cdef int 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:
# surface position, altitude 0
return 0
cdef bytearray msgbin = hex2bin(msg)
cdef int q = char_to_int(msgbin[47])
cdef int n
cdef double alt
if q:
n = bin2int(msgbin[40:47] + msgbin[48:52])
alt = n * 25 - 1000
return alt
else:
return nan
def velocity(bytes msg, bint rtn_sources=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.
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.
"""
cdef int tc = typecode(msg)
if 5 <= tc <= 8:
return surface_velocity(msg, rtn_sources)
elif tc == 19:
return airborne_velocity(msg, rtn_sources)
else:
raise RuntimeError(
"incorrect or inconsistant message types, expecting 4<TC<9 or TC=19"
)
def speed_heading(bytes msg):
"""Get speed and ground track (or heading) from the velocity message
(handles both airborne or surface message)
Args:
msg (string): 28 bytes hexadecimal message string
Returns:
(int, float): speed (kt), ground track or heading (degree)
"""
spd, trk_or_hdg, rocd, tag = velocity(msg)
return spd, trk_or_hdg
def oe_flag(bytes msg):
"""Check the odd/even flag. Bit 54, 0 for even, 1 for odd.
Args:
msg (string): 28 bytes hexadecimal message string
Returns:
int: 0 or 1, for even or odd frame
"""
cdef bytearray msgbin = hex2bin(msg.encode())
return char_to_int(msgbin[53])
def version(bytes msg):
"""ADS-B Version
Args:
msg (string): 28 bytes hexadecimal message string, TC = 31
Returns:
int: version number
"""
cdef int tc = typecode(msg)
if tc != 31:
raise RuntimeError(
"%s: Not a status operation message, expecting TC = 31" % msg
)
cdef bytearray msgbin = hex2bin(msg)
cdef int version = bin2int(msgbin[72:75])
return version

View File

@ -25,7 +25,7 @@
cimport cython cimport cython
from .. cimport common from .. cimport common
from libc.math cimport NAN as nan, remainder from libc.math cimport NAN as nan
@cython.cdivision(True) @cython.cdivision(True)

View File

@ -26,7 +26,7 @@ cimport cython
from .. cimport common from .. cimport common
from cpython cimport array from cpython cimport array
from libc.math cimport NAN as nan, remainder from libc.math cimport NAN as nan
import math import math

View File

@ -0,0 +1,61 @@
# cython: language_level=3
cimport cython
from .. cimport common
def category(bytes msg):
"""Aircraft category number
Args:
msg (string): 28 bytes hexadecimal message string
Returns:
int: category number
"""
cdef int tc = common.typecode(msg)
if tc < 1 or tc > 4:
raise RuntimeError("%s: Not a identification message" % msg)
cdef bytearray msgbin = common.hex2bin(msg)
mebin = msgbin[32:87]
return common.bin2int(mebin[5:8])
def callsign(bytes msg):
"""Aircraft callsign
Args:
msg (string): 28 bytes hexadecimal message string
Returns:
string: callsign
"""
cdef int tc = common.typecode(msg)
if tc < 1 or tc > 4:
raise RuntimeError("%s: Not a identification message" % msg)
cdef bytearray _chars = bytearray(
b"#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######"
)
cdef unsigned char[:] chars = _chars
cdef bytearray msgbin = common.hex2bin(msg)
cdef bytearray csbin = msgbin[40:96]
cdef bytearray _cs = bytearray(8)
cdef unsigned char[:] cs = _cs
cs[0] = chars[common.bin2int(csbin[0:6])]
cs[1] = chars[common.bin2int(csbin[6:12])]
cs[2] = chars[common.bin2int(csbin[12:18])]
cs[3] = chars[common.bin2int(csbin[18:24])]
cs[4] = chars[common.bin2int(csbin[24:30])]
cs[5] = chars[common.bin2int(csbin[30:36])]
cs[6] = chars[common.bin2int(csbin[36:42])]
cs[7] = chars[common.bin2int(csbin[42:48])]
# clean string, remove spaces and marks, if any.
# cs = cs.replace('_', '')
return _cs.decode().replace("#", "")

View File

@ -24,6 +24,7 @@ extensions = [
Extension("pyModeS.c_decoder.adsb", ["pyModeS/c_decoder/adsb.pyx"]), Extension("pyModeS.c_decoder.adsb", ["pyModeS/c_decoder/adsb.pyx"]),
Extension("pyModeS.c_decoder.bds.bds05", ["pyModeS/c_decoder/bds/bds05.pyx"]), Extension("pyModeS.c_decoder.bds.bds05", ["pyModeS/c_decoder/bds/bds05.pyx"]),
Extension("pyModeS.c_decoder.bds.bds06", ["pyModeS/c_decoder/bds/bds06.pyx"]), Extension("pyModeS.c_decoder.bds.bds06", ["pyModeS/c_decoder/bds/bds06.pyx"]),
Extension("pyModeS.c_decoder.bds.bds08", ["pyModeS/c_decoder/bds/bds08.pyx"]),
] ]