add DF4/20 altitude and DF5/21 squawk decoding

This commit is contained in:
junzis 2017-07-21 17:40:10 +02:00
parent 27daf52850
commit aa9f49b470
6 changed files with 230 additions and 74 deletions

View File

@ -48,7 +48,8 @@ Usage
import pyModeS as pms
Common function for Mode-S message:
Common functions:
*****************
.. code:: python
@ -59,8 +60,12 @@ Common function for Mode-S message:
pms.bin2int(str) # Convert binary 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:
**********************************
.. code:: python
@ -83,17 +88,31 @@ Core functions for ADS-B decoding:
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
(either odd or even). This works with both airborne and surface position
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:
********************************
.. code:: python
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
# for BDS version 2,0

View File

@ -3,3 +3,4 @@ from __future__ import absolute_import, print_function, division
from .util import *
from . import adsb
from . import ehs
from . import els

View File

@ -18,65 +18,15 @@ A python package for decoding ModeS (DF20, DF21) messages.
"""
from __future__ import absolute_import, print_function, division
from . import util
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)
from . import util, modes_common
def icao(msg):
return modes_common.icao(msg)
def data(msg):
"""Return the data frame in the message, bytes 9 to 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):
"""check if the data bits are all zeros
@ -93,9 +43,27 @@ def isnull(msg):
else:
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):
"""Computes the altitude from DF20 bit 20-32
"""Computes the altitude from DF20 message, bit 20-32
Args:
msg (String): 28 bytes hexadecimal message string
@ -104,29 +72,26 @@ def df20alt(msg):
int: altitude in ft
"""
if df(msg) != 20:
if util.df(msg) != 20:
raise RuntimeError("Message must be Downlink Format 20.")
# Altitude code, bit 20-32
mbin = util.hex2bin(msg)
mbit = mbin[25] # M bit: 26
qbit = mbin[27] # Q bit: 28
return modes_common.altcode(msg)
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
# 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
def df21id(msg):
"""Computes identity (squawk code) from DF21, bit 20-32
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

37
pyModeS/els.py Normal file
View 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
View 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

View File

@ -85,3 +85,16 @@ def floor(x):
eg.: floor(3.6) = 3, while floor(-3.6) = -4
"""
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