def is17(msg): """Check if a message is likely to be BDS code 1,7 Args: msg (String): 28 bytes hexadecimal message string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) if common.bin2int(d[28:56]) != 0: return False caps = cap17(msg) # basic BDS codes for ADS-B shall be supported # assuming ADS-B out is installed (2017EU/2020US mandate) # if not set(['BDS05', 'BDS06', 'BDS08', 'BDS09', 'BDS20']).issubset(caps): # return False # at least you can respond who you are if "BDS20" not in caps: return False return True
def is10(msg): """Check if a message is likely to be BDS code 1,0 Args: msg (str): 28 hexdigits string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) # first 8 bits must be 0x10 if d[0:8] != "00010000": return False # bit 10 to 14 are reserved if common.bin2int(d[9:14]) != 0: return False # overlay capability conflict if d[14] == "1" and common.bin2int(d[16:23]) < 5: return False if d[14] == "0" and common.bin2int(d[16:23]) > 4: return False return True
def is30(msg): """Check if a message is likely to be BDS code 2,0 Args: msg (str): 28 hexdigits string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) if d[0:8] != "00110000": return False # threat type 3 not assigned if d[28:30] == "11": return False # reserved for ACAS III, in far future if common.bin2int(d[15:22]) >= 48: return False return True
def is20(msg): """Check if a message is likely to be BDS code 2,0 Args: msg (str): 28 hexdigits string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) if d[0:8] != "00100000": return False # allow empty callsign if common.bin2int(d[8:56]) == 0 return True if "#" in cs20(msg): return False return True
def is60(msg): """Check if a message is likely to be BDS code 6,0 Args: msg (str): 28 hexdigits string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) # status bit 1, 13, 24, 35, 46 if common.wrongstatus(d, 1, 2, 12): return False if common.wrongstatus(d, 13, 14, 23): return False if common.wrongstatus(d, 24, 25, 34): return False if common.wrongstatus(d, 35, 36, 45): return False if common.wrongstatus(d, 46, 47, 56): return False ias = ias60(msg) if ias is not None and ias > 500: return False mach = mach60(msg) if mach is not None and mach > 1: return False vr_baro = vr60baro(msg) if vr_baro is not None and abs(vr_baro) > 6000: return False vr_ins = vr60ins(msg) if vr_ins is not None and abs(vr_ins) > 6000: return False # additional check knowing altitude if (mach is not None) and (ias is not None) and (common.df(msg) == 20): alt = common.altcode(msg) if alt is not None: ias_ = aero.mach2cas(mach, alt * aero.ft) / aero.kts if abs(ias - ias_) > 20: return False return True
def is45(msg): """Check if a message is likely to be BDS code 4,5. Meteorological hazard report Args: msg (str): 28 hexdigits string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) # status bit 1, 4, 7, 10, 13, 16, 27, 39 if common.wrongstatus(d, 1, 2, 3): return False if common.wrongstatus(d, 4, 5, 6): return False if common.wrongstatus(d, 7, 8, 9): return False if common.wrongstatus(d, 10, 11, 12): return False if common.wrongstatus(d, 13, 14, 15): return False if common.wrongstatus(d, 16, 17, 26): return False if common.wrongstatus(d, 27, 28, 38): return False if common.wrongstatus(d, 39, 40, 51): return False # reserved if common.bin2int(d[51:56]) != 0: return False temp = temp45(msg) if temp: if temp > 60 or temp < -80: return False return True
def is53(msg): """Check if a message is likely to be BDS code 5,3 (Air-referenced state vector) Args: msg (String): 28 bytes hexadecimal message string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) # status bit 1, 13, 24, 34, 47 if common.wrongstatus(d, 1, 3, 12): return False if common.wrongstatus(d, 13, 14, 23): return False if common.wrongstatus(d, 24, 25, 33): return False if common.wrongstatus(d, 34, 35, 46): return False if common.wrongstatus(d, 47, 49, 56): return False ias = ias53(msg) if ias is not None and ias > 500: return False mach = mach53(msg) if mach is not None and mach > 1: return False tas = tas53(msg) if tas is not None and tas > 500: return False vr = vr53(msg) if vr is not None and abs(vr) > 8000: return False return True
def is50(msg): """Check if a message is likely to be BDS code 5,0 (Track and turn report) Args: msg (str): 28 hexdigits string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) # status bit 1, 12, 24, 35, 46 if common.wrongstatus(d, 1, 3, 11): return False if common.wrongstatus(d, 12, 13, 23): return False if common.wrongstatus(d, 24, 25, 34): return False if common.wrongstatus(d, 35, 36, 45): return False if common.wrongstatus(d, 46, 47, 56): return False roll = roll50(msg) if (roll is not None) and abs(roll) > 50: return False gs = gs50(msg) if gs is not None and gs > 600: return False tas = tas50(msg) if tas is not None and tas > 500: return False if (gs is not None) and (tas is not None) and (abs(tas - gs) > 200): return False return True
def is60(msg): """Check if a message is likely to be BDS code 6,0 Args: msg (str): 28 hexdigits string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) # status bit 1, 13, 24, 35, 46 if common.wrongstatus(d, 1, 2, 12): return False if common.wrongstatus(d, 13, 14, 23): return False if common.wrongstatus(d, 24, 25, 34): return False if common.wrongstatus(d, 35, 36, 45): return False if common.wrongstatus(d, 46, 47, 56): return False ias = ias60(msg) if ias is not None and ias > 500: return False mach = mach60(msg) if mach is not None and mach > 1: return False vr_baro = vr60baro(msg) if vr_baro is not None and abs(vr_baro) > 6000: return False vr_ins = vr60ins(msg) if vr_ins is not None and abs(vr_ins) > 6000: return False return True
def is44(msg): """Check if a message is likely to be BDS code 4,4. Meteorological routine air report Args: msg (str): 28 hexdigits string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) # status bit 5, 35, 47, 50 if common.wrongstatus(d, 5, 6, 23): return False if common.wrongstatus(d, 35, 36, 46): return False if common.wrongstatus(d, 47, 48, 49): return False if common.wrongstatus(d, 50, 51, 56): return False # Bits 1-4 indicate source, values > 4 reserved and should not occur if common.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 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 common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) # status bit 1, 14, and 27 if common.wrongstatus(d, 1, 2, 13): return False if common.wrongstatus(d, 14, 15, 26): return False if common.wrongstatus(d, 27, 28, 39): return False if common.wrongstatus(d, 48, 49, 51): return False if common.wrongstatus(d, 54, 55, 56): return False # bits 40-47 and 52-53 shall all be zero if common.bin2int(d[39:47]) != 0: return False if common.bin2int(d[51:53]) != 0: return False return True
def is20(msg): """Check if a message is likely to be BDS code 2,0 Args: msg (str): 28 hexdigits string Returns: bool: True or False """ if common.allzeros(msg): return False d = common.hex2bin(common.data(msg)) if d[0:8] != "00100000": return False cs = cs20(msg) if "#" in cs: return False return True
def infer(msg, mrar=False): """Estimate the most likely BDS code of an message. Args: msg (str): 28 hexdigits string mrar (bool): Also infer MRAR (BDS 44) and MHR (BDS 45). Defaults to False. Returns: String or None: BDS version, or possible versions, or None if nothing matches. """ df = common.df(msg) if common.allzeros(msg): return "EMPTY" # For ADS-B / Mode-S extended squitter if df == 17: tc = common.typecode(msg) if 1 <= tc <= 4: return "BDS08" # identification and category if 5 <= tc <= 8: return "BDS06" # surface movement if 9 <= tc <= 18: return "BDS05" # airborne position, baro-alt if tc == 19: return "BDS09" # airborne velocity if 20 <= tc <= 22: return "BDS05" # airborne position, gnss-alt if tc == 28: return "BDS61" # aircraft status if tc == 29: return "BDS62" # target state and status if tc == 31: return "BDS65" # operational status # For Comm-B replies IS10 = bds10.is10(msg) IS17 = bds17.is17(msg) IS20 = bds20.is20(msg) IS30 = bds30.is30(msg) IS40 = bds40.is40(msg) IS50 = bds50.is50(msg) IS60 = bds60.is60(msg) IS44 = bds44.is44(msg) IS45 = bds45.is45(msg) if mrar: allbds = np.array([ "BDS10", "BDS17", "BDS20", "BDS30", "BDS40", "BDS44", "BDS45", "BDS50", "BDS60", ]) mask = [IS10, IS17, IS20, IS30, IS40, IS44, IS45, IS50, IS60] else: allbds = np.array( ["BDS10", "BDS17", "BDS20", "BDS30", "BDS40", "BDS50", "BDS60"]) mask = [IS10, IS17, IS20, IS30, IS40, IS50, IS60] bds = ",".join(sorted(allbds[mask])) if len(bds) == 0: return None else: return bds