Exemple #1
0
def callsign(msg):
    """Aircraft callsign

    Args:
        msg (string): 28 bytes hexadecimal message string

    Returns:
        string: callsign
    """

    if common.typecode(msg) < 1 or common.typecode(msg) > 4:
        raise RuntimeError("%s: Not a identification message" % msg)

    chars = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######"
    msgbin = common.hex2bin(msg)
    csbin = msgbin[40:96]

    cs = ""
    cs += chars[common.bin2int(csbin[0:6])]
    cs += chars[common.bin2int(csbin[6:12])]
    cs += chars[common.bin2int(csbin[12:18])]
    cs += chars[common.bin2int(csbin[18:24])]
    cs += chars[common.bin2int(csbin[24:30])]
    cs += chars[common.bin2int(csbin[30:36])]
    cs += chars[common.bin2int(csbin[36:42])]
    cs += chars[common.bin2int(csbin[42:48])]

    # clean string, remove spaces and marks, if any.
    # cs = cs.replace('_', '')
    cs = cs.replace("#", "")
    return cs
Exemple #2
0
def surface_velocity(msg, source=False):
    """Decode surface velocity from a surface position message

    Args:
        msg (str): 28 hexdigits string
        source (boolean): Include direction and vertical rate sources in return. Default to False.
            If set to True, the function will return six value instead of four.

    Returns:
        int, float, int, string, [string], [string]: Four or six parameters, including:
            - Speed (kt)
            - Angle (degree), ground track
            - Vertical rate, always 0
            - Speed type ('GS' for ground speed, 'AS' for airspeed)
            - [Optional] Direction source ('TRUE_NORTH')
            - [Optional] Vertical rate source (None)

    """
    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 movement / 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)

    if source:
        return spd, trk, 0, "GS", "TRUE_NORTH", None
    else:
        return spd, trk, 0, "GS"
Exemple #3
0
def surface_velocity(msg, rtn_sources=False):
    """Decode surface velocity from from a surface position message
    Args:
        msg (string): 28 bytes hexadecimal message string
        rtn_source (boolean): If the function will return
            the sources for direction of travel and vertical
            rate. This will change the return value from a four
            element array to a six element array.

    Returns:
        (int, float, int, string, string, None): speed (kt),
            ground track (degree), None for rate of climb/descend (ft/min),
            and speed type ('GS' for ground speed), direction source
            ('true_north' for ground track / true north as reference),
            None rate of climb/descent source.
    """

    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 movement / 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)

    if rtn_sources:
        return spd, trk, 0, "GS", "true_north", None
    else:
        return spd, trk, 0, "GS"
Exemple #4
0
def lnav_mode(msg) -> bool:
    """Decode LNAV mode.

    Args:
        msg (str): 28 hexdigits string

    Returns:
        bool: LNAV mode engaged

    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 0:
        raise RuntimeError("%s: ADS-B version 1 target state and status message does not contain lnav mode, use horizontal mode instead" % msg)

    if int(mb[46]) == 0:
        return None

    lnav_mode = True if int(mb[53]) == 1 else False

    return lnav_mode
Exemple #5
0
def approach_mode(msg) -> bool:
    """Decode approach mode.

    Args:
        msg (str): 28 hexdigits string

    Returns:
        bool: Approach mode engaged

    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 0:
        raise RuntimeError("%s: ADS-B version 1 target state and status message does not contain approach mode" % msg)

    if int(mb[46]) == 0:
        return None

    app_mode = True if int(mb[51]) == 1 else False

    return app_mode
Exemple #6
0
def baro_pressure_setting(msg):
    """Decode barometric pressure setting.

    Args:
        msg (str): 28 hexdigits string

    Returns:
        float: Barometric pressure setting (millibars)

    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 0:
        raise RuntimeError("%s: ADS-B version 1 target state and status message does not contain barometric pressure setting" % msg)

    baro = common.bin2int(mb[20:29])
    baro = None if baro == 0 else 800 + (baro - 1) * 0.8
    if baro is not None:
        baro = round(baro, 1)

    return baro
Exemple #7
0
def selected_heading(msg):
    """Decode selected heading.

    Args:
        msg (str): 28 bytes hexadecimal message string

    Returns:
        float: Selected heading (degree)
    
    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 0:
        raise RuntimeError("%s: ADS-B version 1 target state and status message does not contain selected heading, use target angle instead" % msg)

    if int(mb[29]) == 0:
        hdg = None
    else:
        hdg_sign = int(mb[30])
        hdg = (hdg_sign+1) * common.bin2int(mb[31:39]) * (180/256)
        hdg = round(hdg, 2)

    return hdg
Exemple #8
0
def is_emergency(msg: str) -> bool:
    """Check if the aircraft is reporting an emergency.

    Non-emergencies are either a subtype of zero (no information) or
    subtype of one and a value of zero (no emergency).
    Subtype = 2 indicates an ACAS RA broadcast, look in BDS 3,0

    :param msg: 28 bytes hexadecimal message string
    :return: if the aircraft has declared an emergency
    """
    if common.typecode(msg) != 28:
        raise RuntimeError(
            "%s: Not an airborne status message, expecting TC=28" % msg)

    mb = common.hex2bin(msg)[32:]
    subtype = common.bin2int(mb[5:8])

    if subtype == 2:
        raise RuntimeError("%s: Emergency message is ACAS-RA, not implemented")

    emergency_state = common.bin2int(mb[8:11])

    if subtype == 1 and emergency_state == 1:
        return True
    else:
        return False
Exemple #9
0
def emergency_status(msg) -> int:
    """Decode aircraft emergency status.

    Value   Meaning
    -----   -----------------------
    0       No emergency
    1       General emergency
    2       Lifeguard/medical emergency
    3       Minimum fuel
    4       No communications
    5       Unlawful interference
    6       Downed aircraft
    7       Reserved

    Args:
        msg (str): 28 bytes hexadecimal message string

    Returns:
        int: Emergency status

    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 1:
        raise RuntimeError("%s: ADS-B version 2 target state and status message does not contain emergency status" % msg)

    return common.bin2int(mb[53:56])
Exemple #10
0
def altitude(msg):
    """Decode aircraft altitude

    Args:
        msg (str): 28 hexdigits string

    Returns:
        int: altitude in feet
    """

    tc = common.typecode(msg)

    if tc < 9 or tc == 19 or tc > 22:
        raise RuntimeError("%s: Not a airborn position message" % msg)

    mb = common.hex2bin(msg)[32:]

    if tc < 19:
        # barometric altitude
        q = mb[15]
        if q:
            n = common.bin2int(mb[8:15] + mb[16:20])
            alt = n * 25 - 1000
        else:
            alt = None
    else:
        # GNSS altitude, meters -> feet
        alt = common.bin2int(mb[8:20]) * 3.28084

    return alt
Exemple #11
0
def altitude(msg):
    """Decode aircraft altitude

    Args:
        msg (str): 28 hexdigits string

    Returns:
        int: altitude in feet
    """

    tc = common.typecode(msg)

    if tc < 9 or tc == 19 or tc > 22:
        raise RuntimeError("%s: Not a airborn position message" % msg)

    mb = common.hex2bin(msg)[32:]
    altbin = mb[8:20]

    if tc < 19:
        altcode = altbin[0:6] + "0" + altbin[6:]
    else:
        altcode = altbin[0:6] + "0" + altbin[6:]

    alt = common.altitude(altcode)

    return alt
Exemple #12
0
def vertical_mode(msg):
    """Decode vertical mode.

    Value   Meaning
    -----   -----------------------
    1       "Acquiring" mode
    2       "Capturing" or "Maintaining" mode
    3       Reserved

    Args:
        msg (str): 28 hexdigits string

    Returns:
        int: Vertical mode

    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 1:
        raise RuntimeError("%s: ADS-B version 2 target state and status message does not contain vertical mode, use vnav mode instead" % msg)

    vertical_mode = common.bin2int(mb[13:15])
    if vertical_mode == 0:
        return None

    return vertical_mode
Exemple #13
0
def category(msg):
    """Aircraft category number

    Args:
        msg (string): 28 bytes hexadecimal message string

    Returns:
        int: category number
    """

    if common.typecode(msg) < 1 or common.typecode(msg) > 4:
        raise RuntimeError("%s: Not a identification message" % msg)

    msgbin = common.hex2bin(msg)
    mebin = msgbin[32:87]
    return common.bin2int(mebin[5:8])
Exemple #14
0
def selected_altitude(msg):
    """Decode selected altitude.

    Args:
        msg (str): 28 hexdigits string

    Returns:
        int: Selected altitude (ft)
        string: Source ('MCP/FCU' or 'FMS')

    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 0:
        raise RuntimeError("%s: ADS-B version 1 target state and status message does not contain selected altitude, use target altitude instead" % msg)

    alt = common.bin2int(mb[9:20])
    alt = None if alt == 0 else (alt - 1) * 32
    alt_source = "MCP/FCU" if int(mb[8]) == 0 else "FMS"

    return alt, alt_source
Exemple #15
0
def altitude_diff(msg):
    """Decode the difference between GNSS and barometric altitude.

    Args:
        msg (str): 28 hexdigits string, TC=19

    Returns:
        int: Altitude difference in feet. 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.
Exemple #16
0
def emergency_squawk(msg: str) -> str:
    """Decode squawk code.

    Emergency value 1: squawk 7700.
    Emergency value 4: squawk 7600.
    Emergency value 5: squawk 7500.

    :param msg: 28 bytes hexadecimal message string
    :return: aircraft squawk code
    """
    if common.typecode(msg) != 28:
        raise RuntimeError(
            "%s: Not an airborne status message, expecting TC=28" % msg)

    msgbin = common.hex2bin(msg)

    # construct the 13 bits Mode A ID code
    idcode = msgbin[43:49] + "0" + msgbin[49:55]

    squawk = common.squawk(idcode)
    return squawk
Exemple #17
0
def target_angle(msg):
    """Decode target heading/track angle.

    Args:
        msg (str): 28 bytes hexadecimal message string

    Returns:
        int: Target angle (degree)
        string: Angle type ('Heading' or 'Track')
        string: Source ('MCP/FCU', 'Autopilot Mode' or 'FMS/RNAV')
    
    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 1:
        raise RuntimeError("%s: ADS-B version 2 target state and status message does not contain target angle, use selected heading instead" % msg)

    angle_avail = common.bin2int(mb[25:27])
    if angle_avail == 0:
        angle = None
    else:
        angle = common.bin2int(mb[27:36])

        if angle_avail == 1:
            angle_source = "MCP/FCU"
        elif angle_avail == 2:
            angle_source = "Autopilot mode"
        else:
            angle_source = "FMS/RNAV"
        
        angle_type = "Heading" if int(mb[36]) else "Track"

    return angle, angle_type, angle_source
Exemple #18
0
def target_altitude(msg):
    """Decode target altitude.

    Args:
        msg (str): 28 hexdigits string

    Returns:
        int: Target altitude (ft)
        string: Source ('MCP/FCU', 'Holding mode' or 'FMS/RNAV')
        string: Altitude reference, either pressure altitude or barometric corrected altitude ('FL' or 'MSL')

    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 1:
        raise RuntimeError("%s: ADS-B version 2 target state and status message does not contain target altitude, use selected altitude instead" % msg)

    alt_avail = common.bin2int(mb[7:9])
    if alt_avail == 0:
        return None
    elif alt_avail == 1:
        alt_source = "MCP/FCU"
    elif alt_avail == 2:
        alt_source = "Holding mode"
    else:
        alt_source = "FMS/RNAV"

    alt_ref = "FL" if int(mb[9]) == 0 else "MSL"

    alt = -1000 + common.bin2int(mb[15:25]) * 100

    return alt, alt_source, alt_ref
Exemple #19
0
def tcas_ra(msg) -> bool:
    """Decode TCAS/ACAS Resolution advisory.

    Args:
        msg (str): 28 bytes hexadecimal message string

    Returns:
        bool: TCAS/ACAS Resolution advisory active

    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 1:
        raise RuntimeError("%s: ADS-B version 2 target state and status message does not contain TCAS/ACAS RA" % msg)

    tcas_ra = True if int(mb[52]) == 1 else False

    return tcas_ra
Exemple #20
0
def tcas_operational(msg) -> bool:
    """Decode TCAS/ACAS operational.

    Args:
        msg (str): 28 bytes hexadecimal message string

    Returns:
        bool: TCAS/ACAS operational

    """

    if common.typecode(msg) != 29:
        raise RuntimeError("%s: Not a target state and status message, expecting TC=29" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:7])

    if subtype == 0:
        tcas = True if int(mb[51]) == 0 else False
    else:
        tcas = True if int(mb[52]) == 1 else False

    return tcas
Exemple #21
0
def typecode(msg):
    return common.typecode(msg)
Exemple #22
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
Exemple #23
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", "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")
Exemple #24
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
Exemple #25
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")
Exemple #26
0
def airborne_velocity(msg, source=False):
    """Decode airborne velocity.

    Args:
        msg (str): 28 hexdigits string
        source (boolean): Include direction and vertical rate sources in return. Default to False.
            If set to True, the function will return six value instead of four.

    Returns:
        int, float, int, string, [string], [string]: Four or six parameters, including:
            - Speed (kt)
            - Angle (degree), either ground track or heading
            - Vertical rate (ft/min)
            - Speed type ('GS' for ground speed, 'AS' for airspeed)
            - [Optional] Direction source ('TRUE_NORTH' or 'MAGNETIC_NORTH')
            - [Optional] Vertical rate source ('BARO' or 'GNSS')

    """
    if common.typecode(msg) != 19:
        raise RuntimeError(
            "%s: Not a airborne velocity message, expecting TC=19" % msg)

    mb = common.hex2bin(msg)[32:]

    subtype = common.bin2int(mb[5:8])

    if subtype in (1, 2):

        v_ew = common.bin2int(mb[14:24])
        v_ns = common.bin2int(mb[25:35])

        if v_ew == 0 or v_ns == 0:
            spd = None
            trk_or_hdg = None
            vs = None
        else:
            v_ew_sign = -1 if mb[13] == "1" else 1
            v_ew = v_ew - 1  # east-west velocity
            if subtype == 2:  # Supersonic
                v_ew *= 4

            v_ns_sign = -1 if mb[24] == "1" else 1
            v_ns = v_ns - 1  # north-south velocity
            if subtype == 2:  # Supersonic
                v_ns *= 4

            v_we = v_ew_sign * v_ew
            v_sn = v_ns_sign * v_ns

            spd = math.sqrt(v_sn * v_sn + v_we * v_we)  # unit in kts
            spd = int(spd)

            trk = math.atan2(v_we, v_sn)
            trk = math.degrees(trk)  # convert to degrees
            trk = trk if trk >= 0 else trk + 360  # no negative val

        spd_type = "GS"
        trk_or_hdg = round(trk, 2)
        dir_type = "TRUE_NORTH"

    else:
        if mb[13] == "0":
            hdg = None
        else:
            hdg = common.bin2int(mb[14:24]) / 1024 * 360.0
            hdg = round(hdg, 2)

        trk_or_hdg = hdg

        spd = common.bin2int(mb[25:35])

        spd = None if spd == 0 else spd - 1

        if subtype == 4:  # Supersonic
            spd *= 4

        if mb[24] == "0":
            spd_type = "IAS"
        else:
            spd_type = "TAS"

        dir_type = "MAGNETIC_NORTH"

    vr_source = "GNSS" if mb[35] == "0" else "BARO"
    vr_sign = -1 if mb[36] == "1" else 1
    vr = common.bin2int(mb[37:46])
    vs = None if vr == 0 else int(vr_sign * (vr - 1) * 64)

    if source:
        return spd, trk_or_hdg, vs, spd_type, dir_type, vr_source
    else:
        return spd, trk_or_hdg, vs, spd_type
Exemple #27
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