add DF4/20 altitude and DF5/21 squawk decoding
This commit is contained in:
parent
27daf52850
commit
aa9f49b470
25
README.rst
25
README.rst
@ -48,7 +48,8 @@ Usage
|
|||||||
import pyModeS as pms
|
import pyModeS as pms
|
||||||
|
|
||||||
|
|
||||||
Common function for Mode-S message:
|
Common functions:
|
||||||
|
*****************
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
@ -59,8 +60,12 @@ Common function for Mode-S message:
|
|||||||
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.bin2gray(str) # Convert binary string to grey code
|
||||||
|
pms.gray2bin(str) # Convert grey code to binary string
|
||||||
|
|
||||||
|
|
||||||
Core functions for ADS-B decoding:
|
Core functions for ADS-B decoding:
|
||||||
|
**********************************
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
@ -83,17 +88,31 @@ Core functions for ADS-B decoding:
|
|||||||
pms.adsb.airborne_velocity(msg)
|
pms.adsb.airborne_velocity(msg)
|
||||||
|
|
||||||
|
|
||||||
**Hint: When you have a fix position of the aircraft, it is convenient to
|
Hint: When you have a fix position of the aircraft, it is convenient to
|
||||||
use `position_with_ref()` method to decode with only one position message
|
use `position_with_ref()` method to decode with only one position message
|
||||||
(either odd or even). This works with both airborne and surface position
|
(either odd or even). This works with both airborne and surface position
|
||||||
messages. But the reference position shall be with in 180NM (airborne)
|
messages. But the reference position shall be with in 180NM (airborne)
|
||||||
or 45NM (surface) of the true position.**
|
or 45NM (surface) of the true position.
|
||||||
|
|
||||||
|
Core functions for ELS decoding:
|
||||||
|
********************************
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
pms.els.icao(msg) # ICAO address
|
||||||
|
pms.els.df4alt(msg) # Altitude from any DF4 message
|
||||||
|
pms.ehs.df5id(msg) # Squawk code from any DF5 message
|
||||||
|
|
||||||
|
|
||||||
Core functions for EHS decoding:
|
Core functions for EHS decoding:
|
||||||
|
********************************
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
pms.ehs.icao(msg) # ICAO address
|
pms.ehs.icao(msg) # ICAO address
|
||||||
|
pms.ehs.df20alt(msg) # Altitude from any DF20 message
|
||||||
|
pms.ehs.df21id(msg) # Squawk code from any DF21 message
|
||||||
|
|
||||||
pms.ehs.BDS(msg) # Comm-B Data Selector Version
|
pms.ehs.BDS(msg) # Comm-B Data Selector Version
|
||||||
|
|
||||||
# for BDS version 2,0
|
# for BDS version 2,0
|
||||||
|
@ -3,3 +3,4 @@ from __future__ import absolute_import, print_function, division
|
|||||||
from .util import *
|
from .util import *
|
||||||
from . import adsb
|
from . import adsb
|
||||||
from . import ehs
|
from . import ehs
|
||||||
|
from . import els
|
||||||
|
107
pyModeS/ehs.py
107
pyModeS/ehs.py
@ -18,65 +18,15 @@ A python package for decoding ModeS (DF20, DF21) messages.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import absolute_import, print_function, division
|
from __future__ import absolute_import, print_function, division
|
||||||
from . import util
|
from . import util, modes_common
|
||||||
|
|
||||||
def df(msg):
|
|
||||||
"""Get the downlink format (DF) number
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (String): 28 bytes hexadecimal message string
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: DF number
|
|
||||||
"""
|
|
||||||
return util.df(msg)
|
|
||||||
|
|
||||||
|
def icao(msg):
|
||||||
|
return modes_common.icao(msg)
|
||||||
|
|
||||||
def data(msg):
|
def data(msg):
|
||||||
"""Return the data frame in the message, bytes 9 to 22"""
|
"""Return the data frame in the message, bytes 9 to 22"""
|
||||||
return msg[8:22]
|
return msg[8:22]
|
||||||
|
|
||||||
|
|
||||||
def icao(msg):
|
|
||||||
"""Calculate the ICAO address from an Mode-S message
|
|
||||||
with DF4, DF5, DF20, DF21
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (String): 28 bytes hexadecimal message string
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
String: ICAO address in 6 bytes hexadecimal string
|
|
||||||
"""
|
|
||||||
|
|
||||||
if df(msg) not in (4, 5, 20, 21):
|
|
||||||
# raise RuntimeError("Message DF must be in (4, 5, 20, 21)")
|
|
||||||
return None
|
|
||||||
|
|
||||||
c0 = util.bin2int(util.crc(msg, encode=True))
|
|
||||||
c1 = util.hex2int(msg[-6:])
|
|
||||||
icao = '%06X' % (c0 ^ c1)
|
|
||||||
return icao
|
|
||||||
|
|
||||||
|
|
||||||
def checkbits(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 = util.bin2int(data[msb-1:lsb])
|
|
||||||
|
|
||||||
if not status:
|
|
||||||
if value != 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------
|
|
||||||
# Common functions
|
|
||||||
# ------------------------------------------
|
|
||||||
def isnull(msg):
|
def isnull(msg):
|
||||||
"""check if the data bits are all zeros
|
"""check if the data bits are all zeros
|
||||||
|
|
||||||
@ -93,9 +43,27 @@ def isnull(msg):
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def checkbits(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 = util.bin2int(data[msb-1:lsb])
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
if value != 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
# ------------------------------------------
|
||||||
|
# Common functions
|
||||||
|
# ------------------------------------------
|
||||||
|
|
||||||
def df20alt(msg):
|
def df20alt(msg):
|
||||||
"""Computes the altitude from DF20 bit 20-32
|
"""Computes the altitude from DF20 message, bit 20-32
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg (String): 28 bytes hexadecimal message string
|
msg (String): 28 bytes hexadecimal message string
|
||||||
@ -104,29 +72,26 @@ def df20alt(msg):
|
|||||||
int: altitude in ft
|
int: altitude in ft
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if df(msg) != 20:
|
if util.df(msg) != 20:
|
||||||
raise RuntimeError("Message must be Downlink Format 20.")
|
raise RuntimeError("Message must be Downlink Format 20.")
|
||||||
|
|
||||||
# Altitude code, bit 20-32
|
return modes_common.altcode(msg)
|
||||||
mbin = util.hex2bin(msg)
|
|
||||||
|
|
||||||
mbit = mbin[25] # M bit: 26
|
|
||||||
qbit = mbin[27] # Q bit: 28
|
|
||||||
|
|
||||||
|
|
||||||
if mbit == '0': # unit in ft
|
def df21id(msg):
|
||||||
if qbit == '1': # 25ft interval
|
"""Computes identity (squawk code) from DF21, bit 20-32
|
||||||
vbin = mbin[19:25] + mbin[26] + mbin[28:32]
|
|
||||||
alt = util.bin2int(vbin) * 25 - 1000
|
|
||||||
if qbit == '0': # 100ft interval
|
|
||||||
# to be implemented
|
|
||||||
alt = None
|
|
||||||
if mbit == '1': # unit in meter
|
|
||||||
vbin = mbin[19:25] + mbin[26:31]
|
|
||||||
alt = int(util.bin2int(vbin) * 3.28084) # convert to ft
|
|
||||||
|
|
||||||
return alt
|
Args:
|
||||||
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: squawk code
|
||||||
|
"""
|
||||||
|
|
||||||
|
if util.df(msg) != 21:
|
||||||
|
raise RuntimeError("Message must be Downlink Format 21.")
|
||||||
|
|
||||||
|
return modes_common.idcode(msg)
|
||||||
|
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
# BDS 1,7
|
# BDS 1,7
|
||||||
|
37
pyModeS/els.py
Normal file
37
pyModeS/els.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from . import util, modes_common
|
||||||
|
|
||||||
|
def icao(msg):
|
||||||
|
return modes_common.icao(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def df4alt(msg):
|
||||||
|
"""Computes the altitude from DF4 message, bit 20-32
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: altitude in ft
|
||||||
|
"""
|
||||||
|
|
||||||
|
if util.df(msg) != 4:
|
||||||
|
raise RuntimeError("Message must be Downlink Format 4.")
|
||||||
|
|
||||||
|
return modes_common.altcode(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def df5id(msg):
|
||||||
|
"""Computes identity (squawk code) from DF5 message, bit 20-32
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: squawk code
|
||||||
|
"""
|
||||||
|
|
||||||
|
if util.df(msg) != 5:
|
||||||
|
raise RuntimeError("Message must be Downlink Format 5.")
|
||||||
|
|
||||||
|
return modes_common.idcode(msg)
|
121
pyModeS/modes_common.py
Normal file
121
pyModeS/modes_common.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
from __future__ import absolute_import, print_function, division
|
||||||
|
from . import util
|
||||||
|
|
||||||
|
|
||||||
|
def icao(msg):
|
||||||
|
"""Calculate the ICAO address from an Mode-S message
|
||||||
|
with DF4, DF5, DF20, DF21
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
String: ICAO address in 6 bytes hexadecimal string
|
||||||
|
"""
|
||||||
|
|
||||||
|
if util.df(msg) not in (4, 5, 20, 21):
|
||||||
|
# raise RuntimeError("Message DF must be in (4, 5, 20, 21)")
|
||||||
|
return None
|
||||||
|
|
||||||
|
c0 = util.bin2int(util.crc(msg, encode=True))
|
||||||
|
c1 = util.hex2int(msg[-6:])
|
||||||
|
addr = '%06X' % (c0 ^ c1)
|
||||||
|
return addr
|
||||||
|
|
||||||
|
|
||||||
|
def idcode(msg):
|
||||||
|
"""Computes identity (squawk code) from DF5 or DF21 message, bit 20-32
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: squawk code
|
||||||
|
"""
|
||||||
|
|
||||||
|
if util.df(msg) not in [5, 21]:
|
||||||
|
raise RuntimeError("Message must be Downlink Format 5 or 21.")
|
||||||
|
|
||||||
|
mbin = util.hex2bin(msg)
|
||||||
|
|
||||||
|
C1 = mbin[19]
|
||||||
|
A1 = mbin[20]
|
||||||
|
C2 = mbin[21]
|
||||||
|
A2 = mbin[22]
|
||||||
|
C4 = mbin[23]
|
||||||
|
A4 = mbin[24]
|
||||||
|
# _ = mbin[25]
|
||||||
|
B1 = mbin[26]
|
||||||
|
D1 = mbin[27]
|
||||||
|
B2 = mbin[28]
|
||||||
|
D2 = mbin[29]
|
||||||
|
B4 = mbin[30]
|
||||||
|
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)
|
||||||
|
|
||||||
|
return str(byte1) + str(byte2) + str(byte3) + str(byte4)
|
||||||
|
|
||||||
|
|
||||||
|
def altcode(msg):
|
||||||
|
"""Computes the altitude from DF4 or DF20 message, bit 20-32
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (String): 28 bytes hexadecimal message string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: altitude in ft
|
||||||
|
"""
|
||||||
|
|
||||||
|
if util.df(msg) not in [4, 20]:
|
||||||
|
raise RuntimeError("Message must be Downlink Format 4 or 20.")
|
||||||
|
|
||||||
|
# Altitude code, bit 20-32
|
||||||
|
mbin = util.hex2bin(msg)
|
||||||
|
|
||||||
|
mbit = mbin[25] # M bit: 26
|
||||||
|
qbit = mbin[27] # Q bit: 28
|
||||||
|
|
||||||
|
|
||||||
|
if mbit == '0': # unit in ft
|
||||||
|
if qbit == '1': # 25ft interval
|
||||||
|
vbin = mbin[19:25] + mbin[26] + mbin[28:32]
|
||||||
|
alt = util.bin2int(vbin) * 25 - 1000
|
||||||
|
if qbit == '0': # 100ft interval, above 50175ft
|
||||||
|
C1 = mbin[19]
|
||||||
|
A1 = mbin[20]
|
||||||
|
C2 = mbin[21]
|
||||||
|
A2 = mbin[22]
|
||||||
|
C4 = mbin[23]
|
||||||
|
A4 = mbin[24]
|
||||||
|
# _ = mbin[25]
|
||||||
|
B1 = mbin[26]
|
||||||
|
# _ = mbin[27]
|
||||||
|
B2 = mbin[28]
|
||||||
|
D2 = mbin[29]
|
||||||
|
B4 = mbin[30]
|
||||||
|
D4 = mbin[31]
|
||||||
|
|
||||||
|
# standard greycode
|
||||||
|
gc5 = D2 + D4 + A1 + A2 + A4 + B1 + B2 + B4
|
||||||
|
N5 = int(util.gray2bin(gc5, 8), 2)
|
||||||
|
|
||||||
|
# in 100-ft step must be converted
|
||||||
|
gc1 = C1 + C2 + C4
|
||||||
|
N1 = int(util.gray2bin(gc1, 3), 2) - 1
|
||||||
|
|
||||||
|
if N1 == 6:
|
||||||
|
N1 = 4
|
||||||
|
|
||||||
|
if N5%2 != 0:
|
||||||
|
N1 = 4 - N1
|
||||||
|
|
||||||
|
alt = (N5*500 + N1*100) - 1200
|
||||||
|
if mbit == '1': # unit in meter
|
||||||
|
vbin = mbin[19:25] + mbin[26:31]
|
||||||
|
alt = int(util.bin2int(vbin) * 3.28084) # convert to ft
|
||||||
|
|
||||||
|
return alt
|
@ -85,3 +85,16 @@ def floor(x):
|
|||||||
eg.: floor(3.6) = 3, while floor(-3.6) = -4
|
eg.: floor(3.6) = 3, while floor(-3.6) = -4
|
||||||
"""
|
"""
|
||||||
return int(math.floor(x))
|
return int(math.floor(x))
|
||||||
|
|
||||||
|
|
||||||
|
def bin2gray(binary, nbits):
|
||||||
|
"""Convert binary to greycode"""
|
||||||
|
graycode = binary
|
||||||
|
for i in range(1, nbits):
|
||||||
|
bit = str(int(binary[i-1]) ^ int(binary[i]))
|
||||||
|
graycode = str(graycode[:i]) + str(bit)
|
||||||
|
return graycode
|
||||||
|
|
||||||
|
def gray2bin(greycode, nbits):
|
||||||
|
"""Convert greycode to binary"""
|
||||||
|
return bin2gray(greycode, nbits) # simply XOR again
|
||||||
|
Loading…
Reference in New Issue
Block a user