コード例 #1
0
ファイル: allcall.py プロジェクト: umeier/pyModeS
 def wrapper(msg):
     df = common.df(msg)
     if df != 11:
         raise RuntimeError(
             "Incorrect downlink format, expect 11, got {}".format(df)
         )
     return func(msg)
コード例 #2
0
ファイル: sample_run_commb.py プロジェクト: zh199225/pyModeS
def commb_decode_all(df, n=None):
    import csv

    print("===== Decode Comm-B sample data (DF=%s)=====" % df)

    f = open("tests/data/sample_data_commb_df%s.csv" % df, "rt")

    for i, r in enumerate(csv.reader(f)):
        if n and i > n:
            break

        ts = r[0]
        m = r[2]

        df = common.df(m)
        icao = common.icao(m)
        BDS = bds.infer(m)
        code = common.altcode(m) if df == 20 else common.idcode(m)

        if not BDS:
            print(ts, m, icao, df, "%5s" % code, "UNKNOWN")
            continue

        if len(BDS.split(",")) > 1:
            print(ts, m, icao, df, "%5s" % code, end=" ")
            for i, _bds in enumerate(BDS.split(",")):
                if i == 0:
                    print(_bds, *bds_info(_bds, m))
                else:
                    print(" " * 55, _bds, *bds_info(_bds, m))

        else:
            print(ts, m, icao, df, "%5s" % code, BDS, *bds_info(BDS, m))
コード例 #3
0
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
コード例 #4
0
 def check_msg(self, msg):
     df = common.df(msg)
     msglen = len(msg)
     if df == 17 and msglen == 28:
         if common.crc(msg) == 0:
             return True
     elif df in [20, 21] and msglen == 28:
         return True
     elif df in [4, 5, 11] and msglen == 14:
         return True
     else:
         return False
コード例 #5
0
ファイル: adsb.py プロジェクト: wrobell/pyModeS
def df(msg):
    return common.df(msg)
コード例 #6
0
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
コード例 #7
0
ファイル: __init__.py プロジェクト: wrobell/pyModeS
def tell(msg: str) -> None:
    from pyModeS import common, adsb, commb, bds

    def _print(label, value, unit=None):
        print("%20s: " % label, end="")
        print("%s " % value, end="")
        if unit:
            print(unit)
        else:
            print()

    df = common.df(msg)
    icao = common.icao(msg)

    _print("Message", msg)
    _print("ICAO address", icao)
    _print("Downlink Format", df)

    if df == 17:
        _print("Protocol", "Mode-S Extended Squitter (ADS-B)")

        tc = common.typecode(msg)
        if 1 <= tc <= 4:  # callsign
            callsign = adsb.callsign(msg)
            _print("Type", "Identitification and category")
            _print("Callsign:", callsign)

        if 5 <= tc <= 8:  # surface position
            _print("Type", "Surface position")
            oe = adsb.oe_flag(msg)
            msgbin = common.hex2bin(msg)
            cprlat = common.bin2int(msgbin[54:71]) / 131072.0
            cprlon = common.bin2int(msgbin[71:88]) / 131072.0
            v = adsb.surface_velocity(msg)
            _print("CPR format", "Odd" if oe else "Even")
            _print("CPR Latitude", cprlat)
            _print("CPR Longitude", cprlon)
            _print("Speed", v[0], "knots")
            _print("Track", v[1], "degrees")

        if 9 <= tc <= 18:  # airborne position
            _print("Type", "Airborne position (with barometric altitude)")
            alt = adsb.altitude(msg)
            oe = adsb.oe_flag(msg)
            msgbin = common.hex2bin(msg)
            cprlat = common.bin2int(msgbin[54:71]) / 131072.0
            cprlon = common.bin2int(msgbin[71:88]) / 131072.0
            _print("CPR format", "Odd" if oe else "Even")
            _print("CPR Latitude", cprlat)
            _print("CPR Longitude", cprlon)
            _print("Altitude", alt, "feet")

        if tc == 19:
            _print("Type", "Airborne velocity")
            spd, trk, vr, t = adsb.velocity(msg)
            types = {"GS": "Ground speed", "TAS": "True airspeed"}
            _print("Speed", spd, "knots")
            _print("Track", trk, "degrees")
            _print("Vertical rate", vr, "feet/minute")
            _print("Type", types[t])

        if 20 <= tc <= 22:  # airborne position
            _print("Type", "Airborne position (with GNSS altitude)")
            alt = adsb.altitude(msg)
            oe = adsb.oe_flag(msg)
            msgbin = common.hex2bin(msg)
            cprlat = common.bin2int(msgbin[54:71]) / 131072.0
            cprlon = common.bin2int(msgbin[71:88]) / 131072.0
            _print("CPR format", "Odd" if oe else "Even")
            _print("CPR Latitude", cprlat)
            _print("CPR Longitude", cprlon)
            _print("Altitude", alt, "feet")

    if df == 20:
        _print("Protocol", "Mode-S Comm-B altitude reply")
        _print("Altitude", common.altcode(msg), "feet")

    if df == 21:
        _print("Protocol", "Mode-S Comm-B identity reply")
        _print("Squawk code", common.idcode(msg))

    if df == 20 or df == 21:
        labels = {
            "BDS10": "Data link capability",
            "BDS17": "GICB capability",
            "BDS20": "Aircraft identification",
            "BDS30": "ACAS resolution",
            "BDS40": "Vertical intention report",
            "BDS50": "Track and turn report",
            "BDS60": "Heading and speed report",
            "BDS44": "Meteorological routine air report",
            "BDS45": "Meteorological hazard report",
            "EMPTY": "[No information available]",
        }

        BDS = bds.infer(msg, mrar=True)
        if BDS in labels.keys():
            _print("BDS", "%s (%s)" % (BDS, labels[BDS]))
        else:
            _print("BDS", BDS)

        if BDS == "BDS20":
            callsign = commb.cs20(msg)
            _print("Callsign", callsign)

        if BDS == "BDS40":
            _print("MCP target alt", commb.selalt40mcp(msg), "feet")
            _print("FMS Target alt", commb.selalt40fms(msg), "feet")
            _print("Pressure", commb.p40baro(msg), "millibar")

        if BDS == "BDS50":
            _print("Roll angle", commb.roll50(msg), "degrees")
            _print("Track angle", commb.trk50(msg), "degrees")
            _print("Track rate", commb.rtrk50(msg), "degree/second")
            _print("Ground speed", commb.gs50(msg), "knots")
            _print("True airspeed", commb.tas50(msg), "knots")

        if BDS == "BDS60":
            _print("Megnatic Heading", commb.hdg60(msg), "degrees")
            _print("Indicated airspeed", commb.ias60(msg), "knots")
            _print("Mach number", commb.mach60(msg))
            _print("Vertical rate (Baro)", commb.vr60baro(msg), "feet/minute")
            _print("Vertical rate (INS)", commb.vr60ins(msg), "feet/minute")

        if BDS == "BDS44":
            _print("Wind speed", commb.wind44(msg)[0], "knots")
            _print("Wind direction", commb.wind44(msg)[1], "degrees")
            _print("Temperature 1", commb.temp44(msg)[0], "Celsius")
            _print("Temperature 2", commb.temp44(msg)[1], "Celsius")
            _print("Pressure", commb.p44(msg), "hPa")
            _print("Humidity", commb.hum44(msg), "%")
            _print("Turbulence", commb.turb44(msg))

        if BDS == "BDS45":
            _print("Turbulence", commb.turb45(msg))
            _print("Wind shear", commb.ws45(msg))
            _print("Microbust", commb.mb45(msg))
            _print("Icing", commb.ic45(msg))
            _print("Wake vortex", commb.wv45(msg))
            _print("Temperature", commb.temp45(msg), "Celsius")
            _print("Pressure", commb.p45(msg), "hPa")
            _print("Radio height", commb.rh45(msg), "feet")
コード例 #8
0
    def message(self, msg):
        # Printout of statistics
        if self.signal_hup == 1:
            self.logstats()
            self.signal_hup = 0

        ret_dict = {}
        ret_dict['ret'] = 0
        ret_dict['type'] = ""

        self.msgs_curr_total = self.msgs_curr_total + 1

        if len(msg) == 26 or len(msg) == 40:
            # Some version of dump1090 have the 12 first characters used w/
            # some date (timestamp ?). E.g. sdbr245 feeding flightradar24.
            # Strip 12 first characters.
            msg = msg[12:]

        if len(msg) < 28:  # Message length 112 bits
            self.msgs_curr_short = self.msgs_curr_short + 1
        else:
            self.msgs_curr_len28 = self.msgs_curr_len28 + 1

        ret_dict['crc'] = self.check_msg(msg)
        if ret_dict['crc']:
            self.parity_check_ok = self.parity_check_ok + 1
        else:
            self.parity_check_ko = self.parity_check_ko + 1

        # Do not manage messages with bad CRC
        if ret_dict['crc'] is not True:
            raise ValueError("CrcKO")

        dfmt = common.df(msg)
        ret_dict['dfmt'] = dfmt
        self.df[dfmt] = self.df[dfmt] + 1

        ret_dict['ic'] = common.icao(msg)

        if dfmt in [17, 18]:  # Downlink format 17 or 18
            tc = common.typecode(msg)
            ret_dict['tc'] = tc
            self.tc[tc] = self.tc[tc] + 1

            lat_ref = float(self.params["lat"])
            long_ref = float(self.params["long"])

            if tc == 4:  # Aircraft identification
                self.msgs_discovered = self.msgs_discovered + 1
                ret_dict['type'] = "CS"
                ret_dict['cs'] = adsb.callsign(msg)
                ca = adsb_ca(msg)
                ret_dict['ca'] = ca_msg[ca]
                self.ca[ca] = self.ca[ca] + 1
            elif 9 <= tc <= 18:
                self.msgs_discovered = self.msgs_discovered + 1
                ret_dict['type'] = "LB"
                ret_dict['altb'] = adsb.altitude(msg)
                (lat, long) = adsb.position_with_ref(msg, lat_ref, long_ref)
                ret_dict['lat'] = lat
                ret_dict['long'] = long
            elif tc == 19:
                self.msgs_discovered = self.msgs_discovered + 1
                ret_dict['type'] = "VH"
                _dict = adsb.velocity(msg)
                if _dict is None:
                    raise ValueError("AdsbVelocity")
                (ret_dict['speed'], ret_dict['head'], ret_dict['rocd'],
                 var) = _dict
                if ret_dict['head'] is None:
                    raise ValueError("AdsbHeading")
                if ret_dict['rocd'] is None:
                    raise ValueError("AdsbRocd")
            elif 20 <= tc <= 22:
                self.msgs_discovered = self.msgs_discovered + 1
                ret_dict['type'] = "LG"
                ret_dict['altg'] = adsb.altitude(msg)
                (lat, long) = adsb.position_with_ref(msg, lat_ref, long_ref)
                ret_dict['lat'] = lat
                ret_dict['long'] = long
        elif dfmt in [5, 21]:
            self.msgs_discovered = self.msgs_discovered + 1
            ret_dict['type'] = "SQ"
            ret_dict['sq'] = common.idcode(msg)

        if dfmt in [0, 4, 16, 20]:
            self.msgs_discovered = self.msgs_discovered + 1
            ret_dict['type'] = "AL"
            _alt = common.altcode(msg)
            alt = _alt if _alt is not None else 0
            ret_dict['alt'] = alt

        return ret_dict
コード例 #9
0
def tell(msg: str) -> None:
    from pyModeS import common, adsb, commb, bds

    def _print(label, value, unit=None):
        print("%20s: " % label, end="")
        print("%s " % value, end="")
        if unit:
            print(unit)
        else:
            print()

    df = common.df(msg)
    icao = common.icao(msg)

    _print("Message", msg)
    _print("ICAO address", icao)
    _print("Downlink Format", df)

    if df == 17:
        _print("Protocol", "Mode-S Extended Squitter (ADS-B)")

        tc = common.typecode(msg)
        if 1 <= tc <= 4:  # callsign
            callsign = adsb.callsign(msg)
            _print("Type", "Identification and category")
            _print("Callsign:", callsign)

        if 5 <= tc <= 8:  # surface position
            _print("Type", "Surface position")
            oe = adsb.oe_flag(msg)
            msgbin = common.hex2bin(msg)
            cprlat = common.bin2int(msgbin[54:71]) / 131072.0
            cprlon = common.bin2int(msgbin[71:88]) / 131072.0
            v = adsb.surface_velocity(msg)
            _print("CPR format", "Odd" if oe else "Even")
            _print("CPR Latitude", cprlat)
            _print("CPR Longitude", cprlon)
            _print("Speed", v[0], "knots")
            _print("Track", v[1], "degrees")

        if 9 <= tc <= 18:  # airborne position
            _print("Type", "Airborne position (with barometric altitude)")
            alt = adsb.altitude(msg)
            oe = adsb.oe_flag(msg)
            msgbin = common.hex2bin(msg)
            cprlat = common.bin2int(msgbin[54:71]) / 131072.0
            cprlon = common.bin2int(msgbin[71:88]) / 131072.0
            _print("CPR format", "Odd" if oe else "Even")
            _print("CPR Latitude", cprlat)
            _print("CPR Longitude", cprlon)
            _print("Altitude", alt, "feet")

        if tc == 19:
            _print("Type", "Airborne velocity")
            spd, trk, vr, t = adsb.velocity(msg)
            types = {"GS": "Ground speed", "TAS": "True airspeed"}
            _print("Speed", spd, "knots")
            _print("Track", trk, "degrees")
            _print("Vertical rate", vr, "feet/minute")
            _print("Type", types[t])

        if 20 <= tc <= 22:  # airborne position
            _print("Type", "Airborne position (with GNSS altitude)")
            alt = adsb.altitude(msg)
            oe = adsb.oe_flag(msg)
            msgbin = common.hex2bin(msg)
            cprlat = common.bin2int(msgbin[54:71]) / 131072.0
            cprlon = common.bin2int(msgbin[71:88]) / 131072.0
            _print("CPR format", "Odd" if oe else "Even")
            _print("CPR Latitude", cprlat)
            _print("CPR Longitude", cprlon)
            _print("Altitude", alt, "feet")

        if tc == 29:  # target state and status
            _print("Type", "Target State and Status")
            subtype = common.bin2int((common.hex2bin(msg)[32:])[5:7])
            _print("Subtype", subtype)
            tcas_operational = adsb.tcas_operational(msg)
            types = {0: "Not Engaged", 1: "Engaged"}
            tcas_operational_types = {0: "Not Operational", 1: "Operational"}
            if subtype == 0:
                emergency_types = {
                    0: "No emergency",
                    1: "General emergency",
                    2: "Lifeguard/medical emergency",
                    3: "Minimum fuel",
                    4: "No communications",
                    5: "Unlawful interference",
                    6: "Downed aircraft",
                    7: "Reserved"
                }
                vertical_horizontal_types = {
                    1: "Acquiring mode",
                    2: "Capturing/Maintaining mode"
                }
                tcas_ra_types = {0: "Not active", 1: "Active"}
                alt, alt_source, alt_ref = adsb.target_altitude(msg)
                angle, angle_type, angle_source = adsb.target_angle(msg)
                vertical_mode = adsb.vertical_mode(msg)
                horizontal_mode = adsb.horizontal_mode(msg)
                tcas_ra = adsb.tcas_ra(msg)
                emergency_status = adsb.emergency_status(msg)
                _print("Target altitude", alt, "feet")
                _print("Altitude source", alt_source)
                _print("Altitude reference", alt_ref)
                _print("Angle", angle, "°")
                _print("Angle Type", angle_type)
                _print("Angle Source", angle_source)
                _print("Vertical mode",
                       vertical_horizontal_types[vertical_mode])
                _print("Horizontal mode",
                       vertical_horizontal_types[horizontal_mode])
                _print("TCAS/ACAS", tcas_operational_types[tcas_operational])
                _print("TCAS/ACAS RA", tcas_ra_types[tcas_ra])
                _print("Emergency status", emergency_types[emergency_status])
            else:
                alt, alt_source = adsb.selected_altitude(msg)
                baro = adsb.baro_pressure_setting(msg)
                hdg = adsb.selected_heading(msg)
                autopilot = adsb.autopilot(msg)
                vnav = adsb.vnav_mode(msg)
                alt_hold = adsb.altitude_hold_mode(msg)
                app = adsb.approach_mode(msg)
                lnav = adsb.lnav_mode(msg)
                _print("Selected altitude", alt, "feet")
                _print("Altitude source", alt_source)
                _print("Barometric pressure setting", baro, "millibars")
                _print("Selected Heading", hdg, "°")
                if not (common.bin2int((common.hex2bin(msg)[32:])[46]) == 0):
                    _print("Autopilot", types[autopilot])
                    _print("VNAV mode", types[vnav])
                    _print("Altitude hold mode", types[alt_hold])
                    _print("Approach mode", types[app])
                    _print("TCAS/ACAS",
                           tcas_operational_types[tcas_operational])
                    _print("LNAV mode", types[lnav])

    if df == 20:
        _print("Protocol", "Mode-S Comm-B altitude reply")
        _print("Altitude", common.altcode(msg), "feet")

    if df == 21:
        _print("Protocol", "Mode-S Comm-B identity reply")
        _print("Squawk code", common.idcode(msg))

    if df == 20 or df == 21:
        labels = {
            "BDS10": "Data link capability",
            "BDS17": "GICB capability",
            "BDS20": "Aircraft identification",
            "BDS30": "ACAS resolution",
            "BDS40": "Vertical intention report",
            "BDS50": "Track and turn report",
            "BDS60": "Heading and speed report",
            "BDS44": "Meteorological routine air report",
            "BDS45": "Meteorological hazard report",
            "EMPTY": "[No information available]",
        }

        BDS = bds.infer(msg, mrar=True)
        if BDS in labels.keys():
            _print("BDS", "%s (%s)" % (BDS, labels[BDS]))
        else:
            _print("BDS", BDS)

        if BDS == "BDS20":
            callsign = commb.cs20(msg)
            _print("Callsign", callsign)

        if BDS == "BDS40":
            _print("MCP target alt", commb.selalt40mcp(msg), "feet")
            _print("FMS Target alt", commb.selalt40fms(msg), "feet")
            _print("Pressure", commb.p40baro(msg), "millibar")

        if BDS == "BDS50":
            _print("Roll angle", commb.roll50(msg), "degrees")
            _print("Track angle", commb.trk50(msg), "degrees")
            _print("Track rate", commb.rtrk50(msg), "degree/second")
            _print("Ground speed", commb.gs50(msg), "knots")
            _print("True airspeed", commb.tas50(msg), "knots")

        if BDS == "BDS60":
            _print("Megnatic Heading", commb.hdg60(msg), "degrees")
            _print("Indicated airspeed", commb.ias60(msg), "knots")
            _print("Mach number", commb.mach60(msg))
            _print("Vertical rate (Baro)", commb.vr60baro(msg), "feet/minute")
            _print("Vertical rate (INS)", commb.vr60ins(msg), "feet/minute")

        if BDS == "BDS44":
            _print("Wind speed", commb.wind44(msg)[0], "knots")
            _print("Wind direction", commb.wind44(msg)[1], "degrees")
            _print("Temperature 1", commb.temp44(msg)[0], "Celsius")
            _print("Temperature 2", commb.temp44(msg)[1], "Celsius")
            _print("Pressure", commb.p44(msg), "hPa")
            _print("Humidity", commb.hum44(msg), "%")
            _print("Turbulence", commb.turb44(msg))

        if BDS == "BDS45":
            _print("Turbulence", commb.turb45(msg))
            _print("Wind shear", commb.ws45(msg))
            _print("Microbust", commb.mb45(msg))
            _print("Icing", commb.ic45(msg))
            _print("Wake vortex", commb.wv45(msg))
            _print("Temperature", commb.temp45(msg), "Celsius")
            _print("Pressure", commb.p45(msg), "hPa")
            _print("Radio height", commb.rh45(msg), "feet")
コード例 #10
0
ファイル: surv.py プロジェクト: wrobell/pyModeS
 def wrapper(msg):
     df = common.df(msg)
     if df not in [4, 5]:
         raise RuntimeError(
             "Incorrect downlink format, expect 4 or 5, got {}".format(df))
     return func(msg)
コード例 #11
0
def get_all(msg: str) -> dict:
    from pyModeS import common, adsb, commb, bds
    _dict = {}

    def push(key, data, unit=None):
        _dict[key] = data

    df = common.df(msg)
    icao = common.icao(msg)

    push("message", msg)
    push("icao", icao)
    push("downlink_format", df)

    if df == 17:
        push("protocol", "Mode-S Extended Squitter (ADS-B)")

        tc = common.typecode(msg)
        if 1 <= tc <= 4:  # callsign
            callsign = adsb.callsign(msg)
            push("type", "Identitification and category")
            push("callsign:", callsign)

        if 5 <= tc <= 8:  # surface position
            push("type", "Surface position")
            oe = adsb.oe_flag(msg)
            msgbin = common.hex2bin(msg)
            cprlat = common.bin2int(msgbin[54:71]) / 131072.0
            cprlon = common.bin2int(msgbin[71:88]) / 131072.0
            v = adsb.surface_velocity(msg)
            push("cpr_format", "Odd" if oe else "Even")
            push("cpr_latitude", cprlat)
            push("cpr_longitude", cprlon)
            push("speed", v[0] * 1.85200, "km")
            push("track", v[1], "degrees")

        if 9 <= tc <= 18:  # airborne position
            push("type", "Airborne position (with barometric altitude)")
            alt = adsb.altitude(msg)
            oe = adsb.oe_flag(msg)
            msgbin = common.hex2bin(msg)
            cprlat = common.bin2int(msgbin[54:71]) / 131072.0
            cprlon = common.bin2int(msgbin[71:88]) / 131072.0
            push("cpr_format", "Odd" if oe else "Even")
            push("cpr_latitude", cprlat)
            push("cpr_longitude", cprlon)
            push("altitude", alt, "feet")

        if tc == 19:
            push("type", "Airborne velocity")
            spd, trk, vr, t = adsb.velocity(msg)
            types = {"GS": "Ground speed", "TAS": "True airspeed"}
            push("speed", spd * 1.85200, "km")
            push("track", trk, "degrees")
            push("vertical rate", vr, "feet/minute")
            push("type", types[t])

        if 20 <= tc <= 22:  # airborne position
            push("type", "Airborne position (with GNSS altitude)")
            alt = adsb.altitude(msg)
            oe = adsb.oe_flag(msg)
            msgbin = common.hex2bin(msg)
            cprlat = common.bin2int(msgbin[54:71]) / 131072.0
            cprlon = common.bin2int(msgbin[71:88]) / 131072.0
            push("cpr_format", "Odd" if oe else "Even")
            push("cpr_latitude", cprlat)
            push("cpr_longitude", cprlon)
            push("altitude", alt, "feet")

        if tc == 29:  # target state and status
            push("type", "Target State and Status")
            subtype = common.bin2int((common.hex2bin(msg)[32:])[5:7])
            push("subtype", subtype)
            tcas_operational = adsb.tcas_operational(msg)
            types = {0: "Not Engaged", 1: "Engaged"}
            tcas_operational_types = {0: "Not Operational", 1: "Operational"}
            if subtype == 0:
                emergency_types = {
                    0: "No emergency",
                    1: "General emergency",
                    2: "Lifeguard/medical emergency",
                    3: "Minimum fuel",
                    4: "No communications",
                    5: "Unlawful interference",
                    6: "Downed aircraft",
                    7: "Reserved"
                }
                vertical_horizontal_types = {
                    1: "Acquiring mode",
                    2: "Capturing/Maintaining mode"
                }
                tcas_ra_types = {0: "Not active", 1: "Active"}
                altitude = adsb.target_altitude(msg)
                if altitude is not None:
                    alt, alt_source, alt_ref = altitude
                angle, angle_type, angle_source = adsb.target_angle(msg)
                vertical_mode = adsb.vertical_mode(msg)
                horizontal_mode = adsb.horizontal_mode(msg)
                tcas_ra = adsb.tcas_ra(msg)
                emergency_status = adsb.emergency_status(msg)
                push("target_altitude", alt, "feet")
                push("altitude_source", alt_source)
                push("altitude_reference", alt_ref)
                push("angle", angle, "°")
                push("angle Type", angle_type)
                push("angle Source", angle_source)
                push("vertical mode", vertical_horizontal_types[vertical_mode])
                push("horizontal mode", vertical_horizontal_types[horizontal_mode])
                push("TCAS/ACAS", tcas_operational_types[tcas_operational])
                push("TCAS/ACAS_RA", tcas_ra_types[tcas_ra])
                push("emergency_status", emergency_types[emergency_status])
            else:
                alt, alt_source = adsb.selected_altitude(msg)
                baro = adsb.baro_pressure_setting(msg)
                hdg = adsb.selected_heading(msg)
                autopilot = adsb.autopilot(msg)
                vnav = adsb.vnav_mode(msg)
                alt_hold = adsb.altitude_hold_mode(msg)
                app = adsb.approach_mode(msg)
                lnav = adsb.lnav_mode(msg)
                push("selected_altitude", alt, "feet")
                push("altitude_source", alt_source)
                push("barometric_pressure_setting", baro, "millibars")
                push("selected_Heading", hdg, "°")
                if not (common.bin2int((common.hex2bin(msg)[32:])[46]) == 0):
                    push("autopilot", types[autopilot])
                    push("VNAV_mode", types[vnav])
                    push("altitude_hold_mode", types[alt_hold])
                    push("approach_mode", types[app])
                    push("TCAS/ACAS", tcas_operational_types[tcas_operational])
                    push("LNAV_mode", types[lnav])

    if df == 20:
        push("protocol", "Mode-S Comm-B altitude reply")
        push("altitude", common.altcode(msg), "feet")

    if df == 21:
        push("protocol", "Mode-S Comm-B identity reply")
        push("squawk_code", common.idcode(msg))

    if df == 20 or df == 21:
        labels = {
            "BDS10": "Data link capability",
            "BDS17": "GICB capability",
            "BDS20": "Aircraft identification",
            "BDS30": "ACAS resolution",
            "BDS40": "Vertical intention report",
            "BDS50": "Track and turn report",
            "BDS60": "Heading and speed report",
            "BDS44": "Meteorological routine air report",
            "BDS45": "Meteorological hazard report",
            "EMPTY": "[No information available]",
        }

        BDS = bds.infer(msg, mrar=True)
        if BDS in labels.keys():
            push("BDS", "%s (%s)" % (BDS, labels[BDS]))
        else:
            push("BDS", BDS)

        if BDS == "BDS20":
            callsign = commb.cs20(msg)
            push("callsign", callsign)

        if BDS == "BDS40":
            push("MCP_target_alt", commb.selalt40mcp(msg), "feet")
            push("FMS_Target_alt", commb.selalt40fms(msg), "feet")
            push("pressure", commb.p40baro(msg), "millibar")

        if BDS == "BDS50":
            push("roll_angle", commb.roll50(msg), "degrees")
            push("track_angle", commb.trk50(msg), "degrees")
            push("track_rate", commb.rtrk50(msg), "degree/second")
            push("ground_speed", commb.gs50(msg)  * 1.85200, "km")
            push("true_airspeed", commb.tas50(msg)  * 1.85200, "km")

        if BDS == "BDS60":
            push("megnatic Heading", commb.hdg60(msg), "degrees")
            push("indicated airspeed", commb.ias60(msg)  * 1.85200, "km")
            push("mach number", commb.mach60(msg))
            push("vertical rate (Baro)", commb.vr60baro(msg), "feet/minute")
            push("vertical rate (INS)", commb.vr60ins(msg), "feet/minute")

        if BDS == "BDS44":
            push("wind_speed", commb.wind44(msg)[0]  * 1.85200, "km")
            push("wind_direction", commb.wind44(msg)[1], "degrees")
            push("temperature_1", commb.temp44(msg)[0], "Celsius")
            push("temperature_2", commb.temp44(msg)[1], "Celsius")
            push("pressure", commb.p44(msg), "hPa")
            push("humidity", commb.hum44(msg), "%")
            push("turbulence", commb.turb44(msg))

        if BDS == "BDS45":
            push("turbulence", commb.turb45(msg))
            push("wind_shear", commb.ws45(msg))
            push("microbust", commb.mb45(msg))
            push("icing", commb.ic45(msg))
            push("wake_vortex", commb.wv45(msg))
            push("temperature", commb.temp45(msg), "Celsius")
            push("pressure", commb.p45(msg), "hPa")
            push("radio_height", commb.rh45(msg), "feet")

    return _dict