Beispiel #1
0
def correct_single_bit_error(msg):
    """Attempts to correct a bit-flip error in an ADS-B message.
	
	Parameters
	----------
	msg : str
		The hex-string ADS-B message.
	
	Returns
	-------
	str
		If a solution is found, returns the corrected ADS-B message.
		Returns None if no solution found.
	"""

    num = int(msg, 16)
    bit_length = len(bin(num)[2:])

    for k in range(bit_length):
        test_num = num ^ (1 << k)
        test_msg = hex(test_num)[2:]

        # CRC check for long message
        if (pms.crc(test_msg) == 0):
            print('*', end='', flush=True)
            return test_msg
        # CRC check for short message
        elif (pms.crc(test_msg[:14]) == 0):
            print('*', end='', flush=True)
            return test_msg[:14]

    return None
Beispiel #2
0
    def handle_messages(self, messages):
        # get the current date file
        today = str(datetime.datetime.now().strftime("%Y%m%d"))
        csvfile = dataroot + 'ADSB_RAW_%s.csv' % today

        with open(csvfile, 'a') as f:
            writer = csv.writer(f)

            for msg, ts in messages:
                if len(msg) < 28:
                    continue

                df = pms.df(msg)

                if df != 17:
                    continue

                if '1' in pms.crc(msg):
                    continue

                addr = pms.adsb.icao(msg)
                tc = pms.adsb.typecode(msg)

                line = ['%.6f'%ts, addr, '%02d'%tc, msg]

                writer.writerow(line)
Beispiel #3
0
    def handle_messages(self, messages):
        # get the current date file
        today = str(datetime.datetime.now().strftime("%Y%m%d"))
        csvfile = dataroot + 'ADSB_RAW_%s.csv' % today

        for msg, ts in messages:
            if len(msg) < 28:
                continue

            df = pms.df(msg)

            if df != 17:
                continue

            if '1' in pms.crc(msg):
                continue

            addr = pms.adsb.icao(msg)
            tc = pms.adsb.typecode(msg)

            line = ['%.6f' % ts, addr, '%02d' % tc, msg]

            self.lines.append(line)

        if len(self.lines) > 1000:
            try:
                fcsv = open(csvfile, 'a')
                writer = csv.writer(fcsv)
                writer.writerows(self.lines)
                fcsv.close()
            except Exception, err:
                print err

            self.lines = []
Beispiel #4
0
    def handle_messages(self, messages):
        self.i += 1
        for msg, ts in messages:
            if len(msg) != 28:  # wrong data length
                continue

            df = pms.df(msg)

            if df != 17:  # not ADSB
                continue

            if pms.crc(msg) !=0:  # CRC fail
                continue

            icao = pms.adsb.icao(msg)
            tc = pms.adsb.typecode(msg)
            flight = None

            if icao in self.flights:
                flight = self.flights[icao]
            else:
                flight = Flight(icao)

            flight.last_seen = datetime.now()

            # Message Type Codes:  https://mode-s.org/api/
            if tc >= 1 and tc <= 4:
                # Typecode 1-4
                flight.call_sign = pms.adsb.callsign(msg).strip('_')
            elif tc >= 9 and tc <= 18:
                # Typecode 9-18 (airborne, barometric height)
                flight.location = pms.adsb.airborne_position_with_ref(msg,
                        self.lat_ref, self.lon_ref)
                flight.altitude_ft = pms.adsb.altitude(msg)
                flight.sent = False
            elif tc == 19:
                # Typecode: 19
                # Ground Speed (GS) or Airspeed (IAS/TAS)
                # Output (speed, track angle, vertical speed, tag):
                (flight.speed_kts, flight.track_angle_deg, flight.vertical_speed_fpm,
                    flight.speed_ref) = pms.adsb.velocity(msg)

            self.flights[icao] = flight

            if self.i > 10:
                self.i = 0
                #print("Flights: ", len(self.flights))
                for key in list(self.flights):
                    f = self.flights[key]
                    if f.has_info() and not f.sent:
                        #f.pretty_print()
                        f.json_print()
                        f.sent = True
                    elif f.last_seen < (datetime.now() - timedelta(minutes=5)):
                        #print("Deleting ", key)
                        del self.flights[key]
Beispiel #5
0
 def _check_msg(self, msg):
     df = pms.df(msg)
     msglen = len(msg)
     if df == 17 and msglen == 28:
         if pms.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
Beispiel #6
0
 def _debug_msg(self, msg):
     df = pms.df(msg)
     msglen = len(msg)
     if df == 17 and msglen == 28:
         print(msg, pms.icao(msg), pms.crc(msg))
     elif df in [20, 21] and msglen == 28:
         print(msg, pms.icao(msg))
     elif df in [4, 5, 11] and msglen == 14:
         print(msg, pms.icao(msg))
     else:
         # print("[*]", msg)
         pass
Beispiel #7
0
def decode_ADSB(signal, fix_1bit_errors=False):
    """Attempts to decode the given signal as an ADS-B message and calculate SNR.
	
	Parameters
	----------
	signal : numpy.array
		The RF signal to decode. Must have a sample rate of 2MHz and be at least 240 samples long.
	fix_1bit_errors : bool, optional
		Whether or not to attempt to fix single bit errors.
	
	Returns
	-------
	string
		If the CRC check passes, returns the hex string for the ADS-B packet. Returns None if the CRC check fails.
	"""

    row_size = 16 + 112 * 2
    msg = None

    # Exit if signal's sample size is too small
    if (len(signal) < row_size):
        return

    # Decode the signal to binary (assume Manchester encoded)
    # Taken from the EE123 Lab 2 code
    bits = signal[16::2] > signal[17::2]
    tmp = ''.join(['1' if x else '0' for x in bits])
    msg = hex(int(tmp, 2))[2:]

    # CRC check for long message
    if (pms.crc(msg) == 0):
        return msg
    # CRC check for short message
    elif (pms.crc(msg[:14]) == 0):
        return msg[:14]

    if fix_1bit_errors:
        return correct_single_bit_error(msg)
    else:
        return None
Beispiel #8
0
    def __rtl_thread(self):
        """ Internal thread for running the rtl binary """
        cmd = [ self.opts['rtlbin'] ]

        if self.opts['device'] is not None:
            cmd.append('-d')
	    cmd.append("{}".format(self.opts['device']))

        if self.opts['gain'] is not None:
            cmd.append('-g')
	    cmd.append("{}".format(self.opts['gain']))

        seen_any_valid = False
        failed_once = False

        try:
            FNULL = open(os.devnull, 'w')
            self.rtl_exec = subprocess.Popen(cmd, stderr=FNULL, stdout=subprocess.PIPE)

            while True:
		hex_data = self.rtl_exec.stdout.readline().decode('ascii').strip()[1:-1]
		if pms.crc(hex_data) == "000000000000000000000000":
		    for row in airplanes:
		        if pms.adsb.icao(hex_data) == row[0]:
			    msg = { "icao": row[0] , "regid": row[1] , "mdl": row[2] , "type": row[3] , "operator": row[4] }
		    if 1 <= pms.adsb.typecode(hex_data) <= 4:
			msg = { "icao": pms.adsb.icao(hex_data), "callsign": pms.adsb.callsign(hex_data) }
		    if 5 <= pms.adsb.typecode(hex_data) <= 8:
			msg = { "icao": pms.adsb.icao(hex_data), "altitude": pms.adsb.altitude(hex_data) }
		    if pms.adsb.typecode(hex_data) == 19:
			airborneInfo = pms.adsb.airborne_velocity(hex_data)
			msg = { "icao": pms.adsb.icao(hex_data), "speed": airborneInfo[0], "heading": airborneInfo[1], "altitude": airborneInfo[2], "GSAS": airborneInfo[3] }
		    l = json.dumps(msg)

                    if not self.handle_json(l):
                        raise RuntimeError('could not process response from rtladsb')

                    seen_any_valid = True


        except Exception as e:
            # Catch all errors, but don't die if we're reconfiguring rtl; then we need
            # to relaunch the binary
            if not self.rtl_reconfigure:
                self.kismet.send_datasource_error_report(message = "Unable to process output from rtladsb: {}".format(e))
        finally:
            if not seen_any_valid and not self.rtl_reconfigure:
                self.kismet.send_datasource_error_report(message = "An error occurred in rtladsb and no valid devices were seen; is your USB device plugged in?  Try running rtladsb in a terminal and confirm that it can connect to your device.")
                self.kismet.spindown()

            self.rtl_exec.kill()
Beispiel #9
0
    def process_adsb(self, msg: str) -> None:
        self.messages += 1

        msg = self.correct(msg)

        if pms.bin2int(pms.crc(msg)) != 0:
            self.rejects += 1
            return
        # print('Error rate:', 100 * self.rejects / self.messages)

        tc = pms.adsb.typecode(msg)
        icao = pms.adsb.icao(msg)

        if tc in TC_IDENTIFICATION:
            callsign = pms.adsb.callsign(msg).strip('_')
            print(msg, '%2d' % tc, pms.adsb.icao(msg), callsign)
            aircraft = self.context.registerAircraft(pms.adsb.icao(msg),
                                                     callsign)
            aircraft.messages.add(msg)
            self.context.notify(aircraft)

        if tc in TC_POSITION:
            altitude = pms.adsb.altitude(msg)
            aircraft = self.context.getAircraft(icao)

            if aircraft is not None:
                aircraft.messages.add(msg)
                aircraft.lastmessage = datetime.datetime.now()
                aircraft.altitude = altitude
                lat, lon = compute_location(aircraft)
                if lat is not None and lon is not None:
                    aircraft.lat = lat
                    aircraft.lon = lon

                self.context.notify(aircraft)

            print(msg, '%2d' % tc, icao, altitude, aircraft)

        if tc in TC_VELOCITY:
            velocity = pms.adsb.velocity(msg)
            aircraft = self.context.getAircraft(icao)

            if aircraft is not None:
                aircraft.messages.add(msg)
                aircraft.lastmessage = datetime.datetime.now()
                aircraft.speed, aircraft.heading, \
                aircraft.vspeed, aircraft.sptype = velocity

                self.context.notify(aircraft)

            print(msg, '%2d' % tc, icao, velocity, aircraft)
Beispiel #10
0
    def correct(self, msg):
        crc = pms.bin2int(pms.crc(msg))
        result = msg

        if crc != 0:
            msg_bin = pms.hex2bin(msg)
            for i in range(len(msg_bin) - 24):
                if msg_bin[i] == '0':
                    msg_bin = self.replace(msg_bin, i, '1')
                else:
                    msg_bin = self.replace(msg_bin, i, '0')

                patched_msg = hex(int(msg_bin, 2))[2:]
                if pms.bin2int(pms.crc(patched_msg)) == 0:
                    result = patched_msg
                    break

                if msg_bin[i] == '0':
                    msg_bin = self.replace(msg_bin, i, '1')
                else:
                    msg_bin = self.replace(msg_bin, i, '0')

        return result
Beispiel #11
0
    def handle_messages(self, messages):
        for msg, ts in messages:
            if len(msg) != 28:  # wrong data length
                continue

            df = pms.df(msg)

            if df != 17:  # not ADSB
                continue

            if pms.crc(msg) != 0:  # CRC fail
                continue

            icao = pms.adsb.icao(msg)
            saved = self.r.get(icao)
            to_save = pms.get_all(msg)
            if saved is not None:
                saved = json.loads(saved)
                try:
                    if 'location_message' in saved and 'cpr_latitude' in to_save:
                        if saved['location_type'] == to_save['type'] and saved[
                                'cpr_format'] != to_save['cpr_format']:
                            if saved['cpr_latitude'] != to_save[
                                    'cpr_latitude'] and saved[
                                        'cpr_longitude'] != to_save[
                                            'cpr_longitude']:
                                gps = pms.adsb.position(
                                    msg, saved['location_message'], ts,
                                    saved['location_timestamp'])
                                if gps is not None:
                                    to_save['gps_latitude'] = gps[0]
                                    to_save['gps_longitude'] = gps[1]
                except:
                    pass

                saved.update(to_save)
                if 'cpr_latitude' in to_save:
                    saved['location_type'] = to_save['type']
                    saved['location_message'] = msg
                    saved['location_timestamp'] = ts
                self.r.set(icao, json.dumps(saved), ex=60)
            else:
                if 'cpr_latitude' in to_save:
                    to_save['location_type'] = to_save['type']
                    to_save['location_message'] = msg
                    to_save['location_timestamp'] = ts
                self.r.set(icao, json.dumps(to_save), ex=60)
Beispiel #12
0
def start(msg):
    global aircrafts

    # verificação da mensagem
    try:
        if int(pms.crc(msg[0], encode=False)) != 0:
            with open('../mensagens/output/corrupted.txt', 'a') as f:
                f.write('{} {}\n'.format(msg[0], msg[1]))
        elif 17 != pms.df(msg[0]) != 18:
            with open('../mensagens/output/incorrectDF.txt', 'a') as f:
                f.write('{} {}\n'.format(msg[0], msg[1]))
        else:
            icao = pms.adsb.icao(msg[0])  # ICAO aeronave

            if icao not in aircrafts:
                aircrafts[icao] = Aircraft(icao)

            aircrafts[icao].code(msg)
    except:
        print("Erro com a mensage: ", msg)
    data = data[data.find(char1) + 1:data.find(char2)]
    data_length = len(data)
    try:
        dl_format = pms.df(data)
    except:
        break

    #CHECK MESSAGE CORRUPTED
    try:
        MSG = pms.hex2bin(data)
    except:
        pass

    if data_length > 14:
        CRC = pms.crc(MSG, encode=False)

        if int(CRC, 2) == 0:
            #print "Received Data:", data
            #print 'MSG is CORRECT'
            message = data
            type_code = pms.adsb.typecode(message)
            velocity = ''
            icao = ''
            lon = ''
            lat = ''
            callsign = ''
            altitude = ''
            position = ''
            speed = ''
            heading = ''
Beispiel #14
0
 def handle_messages(self, messages):
     for msg, ts in messages:
         if all((len(msg) == 28, pms.df(msg) == 17, pms.crc(msg) == 0)):
             icao = pms.adsb.icao(msg)
             tc = pms.adsb.typecode(msg)
             self.process_msg(msg, ts, icao, tc)
Beispiel #15
0
#!/usr/bin/python

import sys, getopt
import pyModeS as mds

msg = sys.argv[1]

print mds.adsb.icao(msg);
print mds.adsb.callsign(msg);
print mds.crc(msg);
Beispiel #16
0
    def process(
        self,
        time: datetime,
        msg: bytes,
        *args,
        spd: Optional[float] = None,
        trk: Optional[float] = None,
        alt: Optional[float] = None,
    ) -> None:

        if len(msg) != 28:
            return

        df = pms.df(msg)

        if df == 4 or df == 20:
            icao = pms.icao(msg)
            ac = self.acs[icao.lower()]
            ac.altcode = time, msg

        if df == 5 or df == 21:
            icao = pms.icao(msg)
            ac = self.acs[icao.lower()]
            ac.idcode = time, msg

        if df == 17 or df == 18:  # ADS-B

            if pms.crc(msg, encode=False) != 0:
                return

            tc = pms.adsb.typecode(msg)
            icao = pms.icao(msg)
            ac = self.acs[icao.lower()]

            if 1 <= tc <= 4:
                ac.callsign = time, msg

            if 5 <= tc <= 8:
                ac.surface = time, msg

            if tc == 19:
                ac.speed = time, msg

            if 9 <= tc <= 18:
                # This is barometric altitude
                ac.position = time, msg

            if 20 <= tc <= 22:
                # Only GNSS altitude
                pass

            # if 9 <= tc <= 18:
            #     ac["nic_bc"] = pms.adsb.nic_b(msg)

            # if (5 <= tc <= 8) or (9 <= tc <= 18) or (20 <= tc <= 22):
            #     ac["HPL"], ac["RCu"], ac["RCv"] = pms.adsb.nuc_p(msg)

            #     if (ac["ver"] == 1) and ("nic_s" in ac.keys()):
            #         ac["Rc"], ac["VPL"] = pms.adsb.nic_v1(msg, ac["nic_s"])
            #     elif (
            #         (ac["ver"] == 2)
            #         and ("nic_a" in ac.keys())
            #         and ("nic_bc" in ac.keys())
            #     ):
            #         ac["Rc"] = pms.adsb.nic_v2(msg, ac["nic_a"], ac["nic_bc"])

            # if tc == 19:
            #     ac["HVE"], ac["VVE"] = pms.adsb.nuc_v(msg)
            #     if ac["ver"] in [1, 2]:
            #         ac["EPU"], ac["VEPU"] = pms.adsb.nac_v(msg)

            # if tc == 29:
            #     ac["PE_RCu"], ac["PE_VPL"], ac["base"] = pms.adsb.sil(
            #         msg, ac["ver"]
            #     )
            #     ac["HFOMr"], ac["VFOMr"] = pms.adsb.nac_p(msg)

            # if tc == 31:
            #     ac["ver"] = pms.adsb.version(msg)
            #     ac["HFOMr"], ac["VFOMr"] = pms.adsb.nac_p(msg)
            #     ac["PE_RCu"], ac["PE_VPL"], ac["sil_base"] = pms.adsb.sil(
            #         msg, ac["ver"]
            #     )

            #     if ac["ver"] == 1:
            #         ac["nic_s"] = pms.adsb.nic_s(msg)
            #     elif ac["ver"] == 2:
            #         ac["nic_a"], ac["nic_bc"] = pms.adsb.nic_a_c(msg)

        elif df == 20 or df == 21:

            bds = pms.bds.infer(msg)
            icao = pms.icao(msg)
            ac = self.acs[icao.lower()]

            if bds == "BDS20":
                ac.bds20 = time, msg
                return

            if bds == "BDS40":
                ac.bds40 = time, msg
                return

            if bds == "BDS44":
                ac.bds44 = time, msg
                return

            if bds == "BDS45":
                ac.bds45 = time, msg
                return

            if bds == "BDS50,BDS60":
                if spd is not None and trk is not None and alt is not None:
                    bds = pms.bds.is50or60(msg, spd, trk, alt)
                elif (ac.spd is not None and ac.trk is not None
                      and ac.alt is not None):
                    bds = pms.bds.is50or60(msg, ac.spd, ac.trk, ac.alt)
                else:
                    return
                # do not return!

            if bds == "BDS50":
                ac.bds50 = time, msg
                return

            if bds == "BDS60":
                ac.bds60 = time, msg
                return
        return pms.adsb.position(even[0], odd[0], even[1], odd[1], -15.8037544,
                                 -48.0866267)


with open("../mensagens/adsb_all.txt") as f:
    msgs = []

    for line in f.readlines():
        msgs.append(line.replace("*", "").replace(";", "").replace("\n", ""))

print("Total de mensagens captadas: %d" % (len(msgs)))

for msg in msgs:

    # verificação da mensagem
    if int(pms.crc(msg, encode=False)) != 0:
        corrupted.append(msg)
        continue
    elif 17 != pms.df(msg) != 18:
        incorrectDF.append(msg)
        continue

    icao = pms.adsb.icao(msg)  # ICAO aeronava

    # Informações necessárias da aeronave
    if not icao in data:
        data[icao] = {
            'mensagens': 0,
            'identificação': None,
            'posiçãoSurface': [],
            'BARO': [],
Beispiel #18
0
    def process(
        self,
        time: datetime,
        msg: str,
        *args: Any,
        uncertainty: bool = False,
        spd: Optional[float] = None,
        trk: Optional[float] = None,
        alt: Optional[float] = None,
    ) -> None:

        ac: Aircraft

        if len(msg) != 28:
            return

        df = pms.df(msg)

        if df == 4 or df == 20:
            icao = pms.icao(msg)
            if isinstance(icao, bytes):
                icao = icao.decode()
            ac = self.acs[icao.lower()]
            ac.altcode = time, msg  # type: ignore

        if df == 5 or df == 21:
            icao = pms.icao(msg)
            if isinstance(icao, bytes):
                icao = icao.decode()
            ac = self.acs[icao.lower()]
            ac.idcode = time, msg  # type: ignore

        if df == 17 or df == 18:  # ADS-B

            if pms.crc(msg, encode=False) != 0:
                return

            tc = pms.adsb.typecode(msg)
            icao = pms.icao(msg)

            # before it's fixed in pyModeS release...
            if isinstance(icao, bytes):
                icao = icao.decode()

            ac = self.acs[icao.lower()]

            if 1 <= tc <= 4:
                ac.callsign = time, msg  # type: ignore

            if 5 <= tc <= 8:
                ac.surface = time, msg  # type: ignore

            if tc == 19:
                ac.speed = time, msg  # type: ignore

            if 9 <= tc <= 18:
                # This is barometric altitude
                ac.position = time, msg  # type: ignore

            if 20 <= tc <= 22:
                # Only GNSS altitude
                pass

            if not uncertainty:
                return

            if 9 <= tc <= 18:
                ac.nic_bc = pms.adsb.nic_b(msg)

            if (5 <= tc <= 8) or (9 <= tc <= 18) or (20 <= tc <= 22):
                ac.nuc_p = time, msg  # type: ignore
                if ac.version == 1:
                    ac.nic_v1 = time, msg  # type: ignore
                elif ac.version == 2:
                    ac.nic_v2 = time, msg  # type: ignore

            if tc == 19:
                ac.nuc_r = time, msg  # type: ignore
                if ac.version in [1, 2]:
                    ac.nac_v = time, msg  # type: ignore

            if tc == 29:
                ac.sil = time, msg  # type: ignore
                ac.nac_p = time, msg  # type: ignore

            if tc == 31:
                ac.version = pms.adsb.version(msg)
                ac.sil = time, msg  # type: ignore
                ac.nac_p = time, msg  # type: ignore

                if ac.version == 1:
                    ac.nic_s = pms.adsb.nic_s(msg)
                elif ac.version == 2:
                    ac.nic_a, ac.nic_bc = pms.adsb.nic_a_c(msg)

        elif df == 20 or df == 21:

            bds = pms.bds.infer(msg)
            icao = pms.icao(msg)
            if isinstance(icao, bytes):
                icao = icao.decode()
            ac = self.acs[icao.lower()]

            if bds == "BDS20":
                ac.bds20 = time, msg  # type: ignore
                return

            if bds == "BDS40":
                ac.bds40 = time, msg  # type: ignore
                return

            if bds == "BDS44":
                ac.bds44 = time, msg  # type: ignore
                return

            if bds == "BDS45":
                ac.bds45 = time, msg  # type: ignore
                return

            if bds == "BDS50,BDS60":
                if spd is not None and trk is not None and alt is not None:
                    bds = pms.bds.is50or60(msg, spd, trk, alt)
                elif (ac.spd is not None and ac.trk is not None
                      and ac.alt is not None):
                    bds = pms.bds.is50or60(msg, ac.spd, ac.trk, ac.alt)
                else:
                    return
                # do not return!

            if bds == "BDS50":
                ac.bds50 = time, msg  # type: ignore
                return

            if bds == "BDS60":
                ac.bds60 = time, msg  # type: ignore
                return
Beispiel #19
0
    async def run(self):
        self._logger.info("Running ADSBNetWorker for data_type='%s'",
                          self.data_type)

        self._reset_local_buffer()

        decoder = pyModeS.streamer.decode.Decode()
        net_client = pyModeS.streamer.source.NetSource("x", 1, self.data_type)

        while 1:
            messages = []
            received = await self.net_queue.get()
            if not received:
                continue

            net_client.buffer.extend(received)
            if "beast" in self.data_type:
                messages = net_client.read_beast_buffer()
            elif "raw" in self.data_type:
                messages = net_client.read_raw_buffer()
            elif "skysense" in self.data_type:
                messages = net_client.read_skysense_buffer()

            self._logger.debug("Received %s messages", len(messages))

            if not messages:
                continue
            else:
                for msg, t in messages:
                    if len(msg) != 28:  # wrong data length
                        continue

                    df = pms.df(msg)

                    if df != 17:  # not ADSB
                        continue

                    if pms.crc(msg) != 0:  # CRC fail
                        continue

                    icao = pms.adsb.icao(msg)
                    tc = pms.adsb.typecode(msg)

                    if df == 17 or df == 18:
                        self.local_buffer_adsb_msg.append(msg)
                        self.local_buffer_adsb_ts.append(t)
                    elif df == 20 or df == 21:
                        self.local_buffer_commb_msg.append(msg)
                        self.local_buffer_commb_ts.append(t)
                    else:
                        continue

                if len(self.local_buffer_adsb_msg) > 1:
                    decoder.process_raw(self.local_buffer_adsb_ts,
                                        self.local_buffer_adsb_msg,
                                        self.local_buffer_commb_ts,
                                        self.local_buffer_commb_msg)
                    self._reset_local_buffer()

                acs = decoder.get_aircraft()
                for k, v in acs.items():
                    # self._logger.debug("acs=%s", acs[k])
                    lat = v.get("lat")
                    lon = v.get("lon")
                    flight = v.get("call", k)
                    alt_geom = v.get("alt")
                    gs = v.get("gs")
                    if lat and lon and flight and alt_geom and gs:
                        aircraft = [{
                            "hex": k,
                            "lat": lat,
                            "lon": lon,
                            "flight": flight.replace("_", ""),
                            "alt_geom": alt_geom,
                            "gs": gs
                        }]
                        await self.handle_message(aircraft)
                    else:
                        continue