def mach53(msg): """MACH number, DBS 5,3 message Args: msg (String): 28 bytes hexadecimal message Returns: float: MACH number """ d = hex2bin(data(msg)) if d[23] == "0": return None mach = bin2int(d[24:33]) * 0.008 return round(mach, 3)
def tas53(msg): """Aircraft true airspeed, BDS 5,3 message Args: msg (String): 28 bytes hexadecimal message Returns: float: true airspeed in knots """ d = hex2bin(data(msg)) if d[33] == "0": return None tas = bin2int(d[34:46]) * 0.5 # kts return round(tas, 1)
def ias53(msg): """Indicated airspeed, DBS 5,3 message Args: msg (String): 28 bytes hexadecimal message Returns: int: indicated arispeed in knots """ d = hex2bin(data(msg)) if d[12] == "0": return None ias = bin2int(d[13:23]) # knots return ias
def sil(msg, version): """Calculate SIL, Surveillance Integrity Level Args: msg (string): 28 bytes hexadecimal message string with TC = 29, 31 Returns: int or string: Probability of exceeding Horizontal Radius of Containment RCu int or string: Probability of exceeding Vertical Integrity Containment Region VPL string: SIL supplement based on "per hour" or "per sample" """ tc = typecode(msg) if tc not in [29, 31]: raise RuntimeError("%s: Not a target state and status messag, \ or operation status message, expecting TC = 29 or 31" % msg) sil_df = pd.read_csv( '/home/josmilrom/Libraries/pyModeS/pyModeS/decoder/adsb_ua_parameters/SIL.csv', sep=',') msgbin = common.hex2bin(msg) if tc == 29: sil = common.bin2int(msgbin[76:78]) elif tc == 31: sil = common.bin2int(msg[82:84]) sil_df_extract = sil_df[sil_df.NACv == sil] PR_RCu = sil_df_extract['PR_RCu'][0] PE_VPL = sil_df_extract['PE_VPL'][0] if version == 1: return PR_RCu, PE_VPL if version == 2: if tc == 29: sil_sup = common.bin2int(msgbin[39]) elif tc == 31: sil_sup = common.bin2int(msgbin[86]) if sil_sup == 0: base = "per hour" elif sil_sup == 1: base = "per sample" return PR_RCu, PE_VPL, base
def hum44(msg): """humidity Args: msg (String): 28 bytes hexadecimal message string Returns: float: percentage of humidity, [0 - 100] % """ d = hex2bin(data(msg)) if d[49] == "0": return None hm = bin2int(d[50:56]) * 100.0 / 64 # % return round(hm, 1)
def cap17(msg): """Extract capacities from BDS 1,7 message Args: msg (String): 28 bytes hexadecimal message string Returns: list: list of support BDS codes """ allbds = [ "05", "06", "07", "08", "09", "0A", "20", "21", "40", "41", "42", "43", "44", "45", "48", "50", "51", "52", "53", "54", "55", "56", "5F", "60", "NA", "NA", "E1", "E2", ] d = hex2bin(data(msg)) idx = [i for i, v in enumerate(d[:28]) if v == "1"] capacity = ["BDS" + allbds[i] for i in idx if allbds[i] is not "NA"] return capacity
def sil(msg, version): """Calculate SIL, Surveillance Integrity Level Args: msg (string): 28 bytes hexadecimal message string with TC = 29, 31 Returns: int or string: Probability of exceeding Horizontal Radius of Containment RCu int or string: Probability of exceeding Vertical Integrity Containment Region VPL string: SIL supplement based on per "hour" or "sample", or 'unknown' """ tc = typecode(msg) if tc not in [29, 31]: raise RuntimeError("%s: Not a target state and status messag, \ or operation status message, expecting TC = 29 or 31" % msg) msgbin = common.hex2bin(msg) if tc == 29: SIL = common.bin2int(msgbin[76:78]) elif tc == 31: SIL = common.bin2int(msgbin[82:84]) try: PE_RCu = uncertainty.SIL[SIL]["PE_RCu"] PE_VPL = uncertainty.SIL[SIL]["PE_VPL"] except KeyError: PE_RCu, PE_VPL = uncertainty.NA, uncertainty.NA base = "unknown" if version == 2: if tc == 29: SIL_SUP = common.bin2int(msgbin[39]) elif tc == 31: SIL_SUP = common.bin2int(msgbin[86]) if SIL_SUP == 0: base = "hour" elif SIL_SUP == 1: base = "sample" return PE_RCu, PE_VPL, base
def p44(msg): """Static pressure. Args: msg (String): 28 bytes hexadecimal message string Returns: int: static pressure in hPa """ d = hex2bin(data(msg)) if d[34] == "0": return None p = bin2int(d[35:46]) # hPa return p
def turb44(msg): """Turblence. Args: msg (String): 28 bytes hexadecimal message string Returns: int: turbulence level. 0=NIL, 1=Light, 2=Moderate, 3=Severe """ d = hex2bin(data(msg)) if d[46] == "0": return None turb = bin2int(d[47:49]) return turb
def cap17(msg): """Extract capacities from BDS 1,7 message Args: msg (String): 28 bytes hexadecimal message string Returns: list: list of suport BDS codes """ allbds = ['05', '06', '07', '08', '09', '0A', '20', '21', '40', '41', '42', '43', '44', '45', '48', '50', '51', '52', '53', '54', '55', '56', '5F', '60', 'NA', 'NA', 'E1', 'E2'] d = hex2bin(data(msg)) idx = [i for i, v in enumerate(d[:28]) if v=='1'] capacity = ['BDS'+allbds[i] for i in idx if allbds[i] is not 'NA'] return capacity
def version(msg): """ADS-B Version Args: msg (string): 28 bytes hexadecimal message string, TC = 31 Returns: int: version number """ tc = typecode(msg) if tc != 31: raise RuntimeError( "%s: Not a status operation message, expecting TC = 31" % msg) msgbin = common.hex2bin(msg) version = common.bin2int(msgbin[72:75]) return version
def nic_s(msg): """Obtain NIC supplement bit, TC=31 message Args: msg (string): 28 bytes hexadecimal message string Returns: int: NICs number (0 or 1) """ tc = typecode(msg) if tc != 31: raise RuntimeError( "%s: Not a status operation message, expecting TC = 31" % msg) msgbin = common.hex2bin(msg) nic_s = int(msgbin[75]) return nic_s
def nic_b(msg): """Obtain NICb, navigation integrity category supplement-b Args: msg (string): 28 bytes hexadecimal message string Returns: int: NICb number (0 or 1) """ tc = typecode(msg) if tc < 9 or tc > 18: raise RuntimeError( "%s: Not a airborne position message, expecting 8<TC<19" % msg) msgbin = common.hex2bin(msg) nic_b = int(msgbin[39]) return nic_b
def is44(msg): """Check if a message is likely to be BDS code 4,4. Meteorological routine air report Args: msg (String): 28 bytes hexadecimal message string Returns: bool: True or False """ if allzeros(msg): return False d = hex2bin(data(msg)) # status bit 5, 35, 47, 50 if wrongstatus(d, 5, 6, 23): return False if wrongstatus(d, 35, 36, 46): return False if wrongstatus(d, 47, 48, 49): return False if wrongstatus(d, 50, 51, 56): return False # Bits 1-4 indicate source, values > 4 reserved and should not occur if bin2int(d[0:4]) > 4: return False vw, dw = wind44(msg) if vw is not None and vw > 250: return False temp, temp2 = temp44(msg) if min(temp, temp2) > 60 or max(temp, temp2) < -80: return False return True
def surface_position_with_ref(msg, lat_ref, lon_ref): """Decode surface position with only one message, knowing reference nearby location, such as previously calculated location, ground station, or airport location, etc. The reference position shall be with in 45NM 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 """ mb = common.hex2bin(msg)[32:] cprlat = common.bin2int(mb[22:39]) / 131072.0 cprlon = common.bin2int(mb[39:56]) / 131072.0 i = int(mb[21]) d_lat = 90.0 / 59 if i else 90.0 / 60 j = common.floor(lat_ref / d_lat) + common.floor( 0.5 + ((lat_ref % d_lat) / d_lat) - cprlat ) lat = d_lat * (j + cprlat) ni = common.cprNL(lat) - i if ni > 0: d_lon = 90.0 / ni else: d_lon = 90.0 m = common.floor(lon_ref / d_lon) + common.floor( 0.5 + ((lon_ref % d_lon) / d_lon) - cprlon ) lon = d_lon * (m + cprlon) return round(lat, 5), round(lon, 5)
def surface_velocity(msg): """Decode surface velocity from from a surface position message Args: msg (string): 28 bytes hexadecimal message string Returns: (int, float, int, string): speed (kt), ground track (degree), rate of climb/descend (ft/min), and speed type ('GS' for ground speed, 'AS' for airspeed) """ if common.typecode(msg) < 5 or common.typecode(msg) > 8: raise RuntimeError("%s: Not a surface message, expecting 5<TC<8" % msg) mb = common.hex2bin(msg)[32:] # ground track trk_status = int(mb[12]) if trk_status == 1: trk = common.bin2int(mb[13:20]) * 360.0 / 128.0 trk = round(trk, 1) else: trk = None # ground movment / speed mov = common.bin2int(mb[5:12]) if mov == 0 or mov > 124: spd = None elif mov == 1: spd = 0 elif mov == 124: spd = 175 else: movs = [2, 9, 13, 39, 94, 109, 124] kts = [0.125, 1, 2, 15, 70, 100, 175] i = next(m[0] for m in enumerate(movs) if m[1] > mov) step = (kts[i] - kts[i - 1]) * 1.0 / (movs[i] - movs[i - 1]) spd = kts[i - 1] + (mov - movs[i - 1]) * step spd = round(spd, 2) return spd, trk, 0, 'GS'
def nic_a_c(msg): """Obtain NICa/c, navigation integrity category supplements a and c Args: msg (string): 28 bytes hexadecimal message string Returns: (int, int): NICa and NICc number (0 or 1) """ tc = typecode(msg) if tc != 31: raise RuntimeError( "%s: Not a status operation message, expecting TC = 31" % msg) msgbin = common.hex2bin(msg) nic_a = int(msgbin[75]) nic_c = int(msgbin[51]) return nic_a, nic_c
def wind44(msg): """Wind speed and direction. Args: msg (String): 28 bytes hexadecimal message string Returns: (int, float): speed (kt), direction (degree) """ d = hex2bin(data(msg)) status = int(d[4]) if not status: return None, None speed = bin2int(d[5:14]) # knots direction = bin2int(d[14:23]) * 180.0 / 256.0 # degree return round(speed, 0), round(direction, 1)
def is40(msg): """Check if a message is likely to be BDS code 4,0 Args: msg (String): 28 bytes hexadecimal message string Returns: bool: True or False """ if allzeros(msg): return False d = hex2bin(data(msg)) # status bit 1, 14, and 27 if wrongstatus(d, 1, 2, 13): return False if wrongstatus(d, 14, 15, 26): return False if wrongstatus(d, 27, 28, 39): return False if wrongstatus(d, 48, 49, 51): return False if wrongstatus(d, 54, 55, 56): return False # bits 40-47 and 52-53 shall all be zero if bin2int(d[39:47]) != 0: return False if bin2int(d[51:53]) != 0: return False return True
def temp45(msg): """Static air temperature. Args: msg (String): 28 bytes hexadecimal message string Returns: float: tmeperature in Celsius degree """ d = hex2bin(data(msg)) sign = int(d[16]) value = bin2int(d[17:26]) if sign: value = value - 512 temp = value * 0.25 # celsius temp = round(temp, 1) return temp
def temp44(msg, rev=False): """reported air temperature Args: msg (String): 28 bytes hexadecimal message (BDS44) string rev (bool): using revised version Returns: float: tmeperature in Celsius degree """ d = hex2bin(data(msg)) if not rev: # if d[22] == '0': # return None sign = int(d[23]) value = bin2int(d[24:34]) if sign: value = value - 1024 temp = value * 0.125 # celsius temp = round(temp, 1) else: # if d[23] == '0': # return None sign = int(d[24]) value = bin2int(d[25:35]) if sign: value = value - 1024 temp = value * 0.125 # celsius temp = round(temp, 1) return temp
def altitude_diff(msg): """Decode the differece between GNSS and barometric altitude Args: msg (string): 28 bytes hexadecimal message string, TC=19 Returns: int: Altitude difference in ft. Negative value indicates GNSS altitude below barometric altitude. """ tc = common.typecode(msg) if tc != 19: raise RuntimeError("%s: Not a airborne velocity message, expecting TC=19" % msg) msgbin = common.hex2bin(msg) sign = -1 if int(msgbin[80]) else 1 value = common.bin2int(msgbin[81:88]) if value == 0 or value == 127: return None else: return sign * (value - 1) * 25 # in ft.
def nuc_v(msg): """Calculate NUCv, Navigation Uncertainty Category - Velocity (ADS-B version 1) Args: msg (string): 28 bytes hexadecimal message string, Returns: int or string: 95% Horizontal Velocity Error int or string: 95% Vertical Velocity Error """ tc = typecode(msg) if tc != 19: raise RuntimeError( "%s: Not an airborne velocity message, expecting TC = 19" % msg) msgbin = common.hex2bin(msg) NUCv = common.bin2int(msgbin[42:45]) HVE = uncertainty.NUCv[NUCv]['HVE'] VVE = uncertainty.NUCv[NUCv]['VVE'] return HVE, VVE
def nac_v(msg): """Calculate NACv, Navigation Accuracy Category - Velocity Args: msg (string): 28 bytes hexadecimal message string, TC = 19 Returns: int or string: 95% horizontal accuracy bounds for velocity, Horizontal Figure of Merit int or string: 95% vertical accuracy bounds for velocity, Vertical Figure of Merit """ tc = typecode(msg) if tc != 19: raise RuntimeError( "%s: Not an airborne velocity message, expecting TC = 19" % msg) msgbin = common.hex2bin(msg) NACv = common.bin2int(msgbin[42:45]) HFOMr = uncertainty.NACv[NACv]['HFOMr'] VFOMr = uncertainty.NACv[NACv]['VFOMr'] return HFOMr, VFOMr
def roll50(msg): """Roll angle, BDS 5,0 message Args: msg (String): 28 bytes hexadecimal message (BDS50) string Returns: float: angle in degrees, negative->left wing down, positive->right wing down """ d = hex2bin(data(msg)) if d[0] == '0': return None sign = int(d[1]) # 1 -> left wing down value = bin2int(d[2:11]) if sign: value = value - 512 angle = value * 45.0 / 256.0 # degree return round(angle, 1)
def vr60ins(msg): """Vertical rate messured by onbard equiments (IRS, AHRS) Args: msg (String): 28 bytes hexadecimal message (BDS60) string Returns: int: vertical rate in feet/minutes """ d = hex2bin(data(msg)) if d[45] == '0': return None sign = int(d[46]) # 1 -> negative value, two's complement value = bin2int(d[47:56]) if value == 0 or value == 511: # all zeros or all ones return 0 value = value - 512 if sign else value roc = value * 32 # feet/min return roc
def vr53(msg): """Vertical rate Args: msg (String): 28 bytes hexadecimal message (BDS60) string Returns: int: vertical rate in feet/minutes """ d = hex2bin(data(msg)) if d[46] == '0': return None sign = int(d[47]) # 1 -> negative value, two's complement value = bin2int(d[48:56]) if value == 0 or value == 255: # all zeros or all ones return 0 value = value - 256 if sign else value roc = value * 64 # feet/min return roc
def rtrk50(msg): """Track angle rate, BDS 5,0 message Args: msg (String): 28 bytes hexadecimal message (BDS50) string Returns: float: angle rate in degrees/second """ d = hex2bin(data(msg)) if d[34] == '0': return None if d[36:45] == "111111111": return None sign = int(d[35]) # 1 -> negative value, two's complement value = bin2int(d[36:45]) if sign: value = value - 512 angle = value * 8.0 / 256.0 # degree / sec return round(angle, 3)
def is20(msg): """Check if a message is likely to be BDS code 2,0 Args: msg (String): 28 bytes hexadecimal message string Returns: bool: True or False """ if allzeros(msg): return False d = hex2bin(data(msg)) if d[0:8] != '00100000': return False cs = cs20(msg) if '#' in cs: return False return True
def cs20(msg): """Aircraft callsign Args: msg (String): 28 bytes hexadecimal message (BDS40) string Returns: string: callsign, max. 8 chars """ chars = '#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######' d = hex2bin(data(msg)) cs = '' cs += chars[bin2int(d[8:14])] cs += chars[bin2int(d[14:20])] cs += chars[bin2int(d[20:26])] cs += chars[bin2int(d[26:32])] cs += chars[bin2int(d[32:38])] cs += chars[bin2int(d[38:44])] cs += chars[bin2int(d[44:50])] cs += chars[bin2int(d[50:56])] return cs