def handleBeacon(self, raw_message, timestamp=None, date=None): try: beacon = parse(raw_message, timestamp) self.logger.debug( 'Receive beacon {aprs_type}, raw data: {raw_message}'.format( **beacon)) handlers = { 'position': self.handlePosition, 'status': self.handleStatus, 'comment': self.handleComment, 'server': self.handleServer, 'position_weather': self.handleWeather, } func = handlers.get( beacon.get('aprs_type'), lambda beacon, date: self.logger.warning('aprs type ' + beacon[ 'aprs_type'] + ' is unknown, beacon not handle.')) func(beacon, date) except ParseError as parsingError: # self.logger.error("Exception occurred", exc_info=True) self.logger.info(parsingError) except KeyboardInterrupt: self.logger.error('Keyboard interrupt') raise (KeyboardInterrupt) except: self.logger.exception( 'Unexpected error when handling following aprs beacon {raw_message}' .format(**beacon))
def process_beacon(raw_message, reference_date=None): from ogn.parser import parse, ParseError if raw_message[0] != '#': try: message = parse(raw_message, reference_date) except NotImplementedError as e: print('Received message: {}'.format(raw_message)) print(e) return None except ParseError as e: print('Received message: {}'.format(raw_message)) print('Drop packet, {}'.format(e.message)) return None except TypeError as e: print('TypeError: {}'.format(raw_message)) return None except Exception as e: print(raw_message) print(e) return None if message['aprs_type'] == 'status' or message[ 'beacon_type'] == 'receiver_beacon': return None else: subset_message = { k: message[k] for k in message.keys() & { 'name', 'address', 'timestamp', 'latitude', 'longitude', 'altitude', 'track', 'ground_speed', 'climb_rate', 'turn_rate' } } return subset_message
def process_beacon(raw_message): # print("RAW:", raw_message) # accept only supported types of messages: if raw_message[:3] in ['OGN', 'FLR', 'ICA']: bp.enqueueForProcessing(raw_message) elif DEBUG: try: if raw_message[:3] in [ 'PAW', 'RND' ]: # # PAW (PilotAWare), RND(?) (not decided if they are worth processing yet) return beacon = parse(raw_message) if 'beacon_type' in beacon: bType = beacon['beacon_type'] if bType in ['aprs_aircraft']: # still an aircraft, right? print('## ACFT BCN:', beacon) elif bType not in ('unknown', 'receiver', 'fanet', 'aprs_receiver', 'pilot_aware', 'flymaster'): print('## TYPE:', bType, '\t\t', beacon) except AprsParseError: pass
def string_to_message(raw_string, reference_date): global receivers try: message = parse(raw_string, reference_date) except NotImplementedError as e: app.logger.error( "No parser implemented for message: {}".format(raw_string)) return None except ParseError as e: app.logger.error("Parsing error with message: {}".format(raw_string)) return None except TypeError as e: app.logger.error("TypeError with message: {}".format(raw_string)) return None except Exception as e: app.logger.error("Other Exception with string: {}".format(raw_string)) return None # update reference receivers and distance to the receiver if message["aprs_type"] == "position": if message[ "beacon_type"] in AIRCRAFT_BEACON_TYPES + RECEIVER_BEACON_TYPES: latitude = message["latitude"] longitude = message["longitude"] location = Location(longitude, latitude) message["location"] = location.to_wkt() location_mgrs = myMGRS.toMGRS(latitude, longitude).decode("utf-8") message["location_mgrs"] = location_mgrs message["location_mgrs_short"] = location_mgrs[ 0:5] + location_mgrs[5:7] + location_mgrs[10:12] if message[ "beacon_type"] in AIRCRAFT_BEACON_TYPES and "gps_quality" in message: if message["gps_quality"] is not None and "horizontal" in message[ "gps_quality"]: message["gps_quality_horizontal"] = message["gps_quality"][ "horizontal"] message["gps_quality_vertical"] = message["gps_quality"][ "vertical"] del message["gps_quality"] # TODO: Fix python-ogn-client 0.91 if "senders_messages" in message and message[ "senders_messages"] is not None: message["senders_messages"] = int(message["senders_messages"]) if "good_senders" in message and message["good_senders"] is not None: message["good_senders"] = int(message["good_senders"]) if "good_and_bad_senders" in message and message[ "good_and_bad_senders"] is not None: message["good_and_bad_senders"] = int(message["good_and_bad_senders"]) return message
def process_message(raw_message): if raw_message[0] == '#': return try: message = parse(raw_message) print("{}: {}".format(message['beacon_type'], raw_message)) except NotImplementedError as e: print("{}: {}".format(e, raw_message)) return if self.remaining_messages > 0: self.remaining_messages -= 1 else: raise KeyboardInterrupt
def process_beacon(raw_message): global db db_check_connect() try: beacon = parse(raw_message) if beacon['aprs_type']=='position' and beacon['beacon_type']=='aprs_aircraft': beacon['timestamp'] = calendar.timegm(beacon['timestamp'].timetuple()) beacon['reference_timestamp'] = calendar.timegm(beacon['reference_timestamp'].timetuple()) print(beacon) db.execute("INSERT INTO positions VALUES (null, :name, :timestamp, :reference_timestamp, :latitude, :longitude, :climb_rate, :turn_rate, :ground_speed, :altitude)", beacon) db_commit() except Exception as e: print('Error, {}'.format(e))
def initial_file_scan(file): """Scan file and get rowcount and first server timestamp.""" row_count = 0 timestamp = None for row in file: row_count += 1 if timestamp is None and row[0] == '#': message = parse(row) if message['aprs_type'] == 'server': timestamp = message['timestamp'] file.seek(0) return row_count, timestamp
def _processMessage(self, raw_message: str): beacon = None try: beacon = parse(raw_message) if not beacon or 'beacon_type' not in beacon.keys( ) or beacon['beacon_type'] != 'aprs_aircraft': return except ParseError as e: # print('[ERROR] when parsing a beacon: {}'.format(e.message)) # print("Failed BEACON:", raw_message) return except Exception as e: # print('[ERROR] {}'.format(e)) # if beacon: # print("Failed BEACON:", beacon) return self.numProcessed += 1 # we are not interested in para, baloons, uavs and other crazy flying stuff: aircraftType = beacon['aircraft_type'] if aircraftType not in [1, 2, 6, 8, 9]: return dt = beacon['timestamp'].replace(tzinfo=pytz.UTC) ts = round(dt.timestamp()) # [s] now = datetime.utcnow().replace(tzinfo=pytz.UTC) if ts - now.timestamp( ) > 30: # timestamp from the future? We'll 30s time offset at most.. print(f"[WARN] Timestamp from the future: {dt}, now is {now}") return address = beacon['address'] lat = beacon['latitude'] lon = beacon['longitude'] altitude = int(beacon['altitude']) groundSpeed = beacon['ground_speed'] verticalSpeed = beacon['climb_rate'] turnRate = beacon['turn_rate'] if not turnRate: turnRate = 0 # get altitude above ground level (AGL): agl = self._getAgl(lat, lon, altitude) # insert into influx: # pos ~ position, vs = vertical speed, tr = turn rate if groundSpeed >= 10 and 0 <= agl < 128000: # 10 km/h threshold q = f"pos,addr={address} lat={lat:.6f},lon={lon:.6f},alt={altitude:.0f},gs={groundSpeed:.2f},vs={verticalSpeed:.2f},tr={turnRate:.2f},agl={agl:.0f} {ts}000000000" self.influxDb.addStatement(q) prevStatus: Status = None statusKey = f"{address}-status" ps = self._getFromRedis(statusKey) if ps: try: prevStatus = Status.parse(ps) except ValueError as e: print('[ERROR] when parsing prev. status: ', e) gsKey = f"{address}-gs" if not prevStatus: # we have no prior information self._saveToRedis(statusKey, Status( s=0, ts=ts)) # 0 = on ground, 1 = airborne, -1 = unknown self._saveToRedis(gsKey, 0, 120) # gs = 0 return prevGroundSpeed = float(self._getFromRedis(gsKey, 0)) # filter speed change a bit (sometimes there are glitches in speed with badly placed gps antenna): groundSpeed = groundSpeed * 0.6 + prevGroundSpeed * 0.4 self._saveToRedis(gsKey, groundSpeed, 3600) currentStatus: Status = Status( ts=ts, s=0 if groundSpeed < getGroundSpeedThreshold(aircraftType, forEvent='T') else 1) # 0 = on ground, 1 = airborne, -1 = unknown if prevStatus.s == 0: # 0 = on ground, 1 = airborne, -1 = unknown currentStatus.s = 1 if groundSpeed > getGroundSpeedThreshold( aircraftType, forEvent='T') else 0 else: # when airborne currentStatus.s = 0 if groundSpeed < getGroundSpeedThreshold( aircraftType, forEvent='L') else 1 if currentStatus.s != prevStatus.s: addressType = beacon['address_type'] addressTypeStr = self.ADDRESS_TYPES.get(addressType, 'X') event = 'L' if currentStatus.s == 0 else 'T' # L = landing, T = take-off flightTime = 0 if event == 'L': flightTime = currentStatus.ts - prevStatus.ts # [s] if flightTime < 120: # [s] return if flightTime > 12 * 3600: # some relic from the previous day self.redis.delete(statusKey) self.redis.delete(gsKey) return # check altitude above ground level: if agl and agl > 150: # [m] return # most likely a false detection # if event == 'T': # # check altitude above ground level: # agl = self._getAgl(lat, lon, altitude) # if agl and agl < 50: # [m] # return # most likely a false detection icaoLocation = self.airfieldManager.getNearest(lat, lon) dt = datetime.fromtimestamp(ts) dtStr = dt.strftime('%H:%M:%S') print( f"[INFO] event: {dtStr}; {icaoLocation}; [{addressTypeStr}] {address}; {event}; {flightTime}" ) icaoLocation = f"'{icaoLocation}'" if icaoLocation else 'null' strSql = f"INSERT INTO logbook_events " \ f"(ts, address, address_type, aircraft_type, event, lat, lon, location_icao, flight_time) " \ f"VALUES " \ f"({ts}, '{address}', {addressType}, '{aircraftType}', " \ f"'{event}', {lat:.5f}, {lon:.5f}, {icaoLocation}, {flightTime});" # print('strSql:', strSql) self.dbThread.addStatement(strSql) self._saveToRedis( statusKey, currentStatus ) # even if the status is the same - do not let the key to expire(!)
ix = packet_str.find('>') cc = packet_str[0:ix] packet_str = cc.upper() + packet_str[ ix:] # convert the ID to uppercase msg = {} # if not a heartbeat from the server if len(packet_str) > 0 and packet_str[0] != "#" and packet_str[ ix + 1:ix + 7] != "OGNFNT": ######################################################################################### # deal with a normal APRS message s = packet_str ph = s.find(":>") hora = s[ph + 2:ph + 9] # get the hora as: hhmmssh try: beacon = parse(s) # parse the APRS message except Exception as e: print("DLY: parse error >>>>", e, s, "<<<<\n", file=sys.stderr) continue # nothing else to do !! # check if it is a OGN tracker status messagea #if beacon["dstcall"] == "OGNTTN": #print ("\n\nBBB", beacon, "\n\n") if beacon["aprs_type"] == "status" and ( beacon["beacon_type"] == "tracker" or beacon["beacon_type"] == "unknown") and (beacon["dstcall"] == "OGNTRK" or beacon["dstcall"] == "OGTTN2" or beacon['dstcall'] == "OGOBS"): comment = s[ph + 10:] # get the comment where it is the data #print ("CCC:", comment.rstrip(" \n\r"), ":", len(comment), s)
def _processMessage(self, raw_message: str): beacon = None try: beacon = parse(raw_message) if not beacon or 'aprs_type' not in beacon.keys() or (beacon.get( 'aprs_type', None) != 'position'): # print('[WARN] cannot process:', raw_message) # print('bt:', beacon.get('beacon_type', None), str(beacon)) return except ParseError as e: # print(f'[ERROR] when parsing a beacon: {str(e)}', raw_message, file=sys.stderr) return except Exception as e: # print(f'[ERROR] Some other error in _processMessage() {str(e)}', raw_message, file=sys.stderr) return self.numProcessed += 1 addressType = beacon.get('address_type', 1) # 1 = icao, 2 = flarm, 3 = ogn addressTypeStr = ADDRESS_TYPES.get(addressType, 'X') aircraftType = beacon.get( 'aircraft_type', 8) # icao-crafts are often 'powered aircraft's if 'address' not in beacon: address = beacon['name'][3:] beacon['address_type'] = 1 else: address = beacon['address'] # we are not interested in para, baloons, uavs and other crazy flying stuff: if aircraftType not in [1, 2, 6, 8, 9, 10]: return dt = beacon['timestamp'].replace(tzinfo=pytz.UTC) ts = round(dt.timestamp()) # UTC [s] now = datetime.utcnow().replace(tzinfo=pytz.UTC) if ts - now.timestamp( ) > 30: # timestamp from the future? We'll 30s time offset at most.. # print(f"[WARN] Timestamp from the future: {dt}, now is {now}") return lat = beacon.get('latitude') or None # [deg] lon = beacon.get('longitude') or None # [deg] altitude = int(beacon.get('altitude')) or 0 # [m] groundSpeed = beacon.get('ground_speed') or 0 # [km/h] verticalSpeed = beacon.get('climb_rate') or 0 # [m/s] turnRate = beacon.get('turn_rate') or 0 # [deg/s] if addressType == 1 and groundSpeed > 400: # ignore fast (icao) airliners and jets return # get altitude above ground level (AGL): agl = self._getAgl(lat, lon, altitude) # [m] # insert into influx: # pos ~ position, vs = vertical speed, tr = turn rate if agl is None or agl < 128000: # groundSpeed > 0 and aglStr = 0 if agl is None else f"{agl:.0f}" q = f"pos,addr={ADDRESS_TYPE_PREFIX[addressType]}{address} lat={lat:.6f},lon={lon:.6f},alt={altitude:.0f},gs={groundSpeed:.2f},vs={verticalSpeed:.2f},tr={turnRate:.2f},agl={aglStr} {ts}000000000" self.influxDb.addStatement(q) prevStatus: Status = None statusKey = f"{addressTypeStr}{address}-status" ps = self._getFromRedis(statusKey) if ps: try: prevStatus = Status.parse(ps) except ValueError as e: print('[ERROR] when parsing prev. status: ', e) gsKey = f"{addressTypeStr}{address}-gs" if not prevStatus: # we have no prior information self._saveToRedis(statusKey, Status( s=0, ts=ts)) # 0 = on ground, 1 = airborne, -1 = unknown self._saveToRedis(gsKey, 0, 120) # gs = 0 return prevGroundSpeed = float(self._getFromRedis(gsKey, 0)) if prevGroundSpeed > 0: # filter speed change a bit (sometimes there are glitches in speed with badly placed gps antenna): groundSpeed = groundSpeed * 0.7 + prevGroundSpeed * 0.3 self._saveToRedis(gsKey, groundSpeed, 3600) currentStatus: Status = Status( ts=ts, s=-1) # 0 = on ground, 1 = airborne, -1 = unknown if prevStatus.s == 0: # 0 = on ground, 1 = airborne, -1 = unknown currentStatus.s = 1 if groundSpeed >= getGroundSpeedThreshold( aircraftType, forEvent='T') else 0 else: # when airborne currentStatus.s = 0 if groundSpeed <= getGroundSpeedThreshold( aircraftType, forEvent='L') else 1 if currentStatus.s != prevStatus.s: event = 'L' if currentStatus.s == 0 else 'T' # L = landing, T = take-off flightTime = 0 if event == 'L': flightTime = currentStatus.ts - prevStatus.ts # [s] if flightTime < 120: # [s] return if flightTime > 12 * 3600: # some relic from the previous day self.redis.delete(statusKey) self.redis.delete(gsKey) return # check altitude above ground level: if agl and agl > AGL_LANDING_LIMIT: # [m] return # most likely a false detection elif event == 'T': # check altitude above ground level: if agl is not None and agl < 50: # [m] return # most likely a false detection self._saveToRedis(statusKey, currentStatus) icaoLocation = self.airfieldManager.getNearest(lat, lon) dt = datetime.fromtimestamp(ts) dtStr = dt.strftime('%H:%M:%S') print( f"[INFO] event: {dtStr}; {icaoLocation}; [{addressTypeStr}] {address}; {event}; {flightTime}" ) icaoLocation = f"'{icaoLocation}'" if icaoLocation else 'null' strSql = f"INSERT INTO logbook_events " \ f"(ts, address, address_type, aircraft_type, event, lat, lon, location_icao, flight_time) " \ f"VALUES " \ f"({ts}, '{address}', '{addressTypeStr}', '{aircraftType}', " \ f"'{event}', {lat:.5f}, {lon:.5f}, {icaoLocation}, {flightTime});" # print('strSql:', strSql) self.dbThread.addStatement(strSql)
def _processMessage(self, raw_message: str): beacon = None try: beacon = parse(raw_message) if not beacon or 'beacon_type' not in beacon.keys( ) or beacon['beacon_type'] != 'aprs_aircraft': return except ParseError as e: # print('[ERROR] when parsing a beacon: {}'.format(e.message)) # print("Failed BEACON:", raw_message) return except Exception as e: # print('[ERROR] {}'.format(e)) # if beacon: # print("Failed BEACON:", beacon) return # we are not interested in para, baloons, uavs, static stuff and others: aircraftType = beacon['aircraft_type'] if aircraftType in [4, 6, 7, 13, 11, 15, 16]: return address = beacon['address'] groundSpeed = beacon['ground_speed'] ts = round(beacon['timestamp'].timestamp()) # [s] # print(f"[INFO] {address} gs: {groundSpeed:.0f}") # print('[DEBUG] Gps H:', beacon['gps_quality']['horizontal']) currentStatus: Status = Status(ts=ts) currentStatus.s = 0 if groundSpeed < SPEED_THRESHOLD else 1 # 0 = on ground, 1 = airborne, -1 = unknown # TODO add AGL check (?) # TODO threshold by aircraftType prevStatus: Status = None statusKey = f"{address}-status" ps = self.redis.get(statusKey) if ps: try: prevStatus = Status.parse(ps) except ValueError as e: print('[ERROR] when parsing prev. status: ', e) if not prevStatus: # we have no prior information self._saveToRedis(statusKey, currentStatus) return if currentStatus.s != prevStatus.s: addressType = beacon['address_type'] aircraftType = beacon['aircraft_type'] lat = beacon['latitude'] lon = beacon['longitude'] icaoLocation = AirfieldManager().getNearest(lat, lon) if not icaoLocation: return event = 'L' if currentStatus.s == 0 else 'T' # L = landing, T = take-off flightTime = 0 if event == 'L': flightTime = currentStatus.ts - prevStatus.ts # [s] if flightTime < 60: return self._saveToRedis(statusKey, currentStatus) dt = datetime.fromtimestamp(ts) dtStr = dt.strftime('%H:%M:%S') print( f"[INFO] {dtStr}; {icaoLocation}; {address}; {event}; {flightTime}" ) strSql = f"INSERT INTO logbook_events " \ f"(ts, address, address_type, aircraft_type, event, lat, lon, location_icao, flight_time) " \ f"VALUES " \ f"({ts}, '{address}', {addressType}, '{aircraftType}', " \ f"'{event}', {lat:.5f}, {lon:.5f}, '{icaoLocation}', {flightTime});" # print('strSql:', strSql) self.dbThread.addStatement(strSql)
def add(self, raw_string): try: message = parse(raw_string, reference_timestamp=self.reference_timestamp) except NotImplementedError as e: current_app.logger.error( "No parser implemented for message: {}".format(raw_string)) return except ParseError as e: current_app.logger.error( "Parsing error with message: {}".format(raw_string)) return except TypeError as e: current_app.logger.error( "TypeError with message: {}".format(raw_string)) return except Exception as e: current_app.logger.error( "Other Exception with string: {}".format(raw_string)) return if message['aprs_type'] not in ('server', 'position'): return elif message[ 'aprs_type'] == 'server' and self.auto_update_timestamp is True: self.reference_timestamp = message['timestamp'] return elif message['aprs_type'] == 'position': latitude = message["latitude"] longitude = message["longitude"] location = Location(longitude, latitude) message["location"] = location.to_wkt() location_mgrs = self.mgrs.toMGRS(latitude, longitude).decode("utf-8") message["location_mgrs"] = location_mgrs message["location_mgrs_short"] = location_mgrs[ 0:5] + location_mgrs[5:7] + location_mgrs[10:12] if "aircraft_type" in message: message["aircraft_type"] = AircraftType( message["aircraft_type"] ).name if message["aircraft_type"] in AircraftType.list( ) else AircraftType.UNKNOWN.name if "gps_quality" in message: if message[ "gps_quality"] is not None and "horizontal" in message[ "gps_quality"]: message["gps_quality_horizontal"] = message["gps_quality"][ "horizontal"] message["gps_quality_vertical"] = message["gps_quality"][ "vertical"] del message["gps_quality"] if message["beacon_type"] in RECEIVER_BEACON_TYPES: complete_message = ",".join([ str(message[k]) if k in message and message[k] is not None else "\\N" for k in BEACON_KEY_FIELDS + RECEIVER_BEACON_FIELDS ]) self.receiver_buffer.write(complete_message) self.receiver_buffer.write("\n") elif message["beacon_type"] in AIRCRAFT_BEACON_TYPES: complete_message = ",".join([ str(message[k]) if k in message and message[k] is not None else "\\N" for k in BEACON_KEY_FIELDS + AIRCRAFT_BEACON_FIELDS ]) self.aircraft_buffer.write(complete_message) self.aircraft_buffer.write("\n") else: current_app.logger.error("Ignore beacon_type: {}".format( message["beacon_type"])) return if datetime.utcnow() - self.last_flush >= timedelta(seconds=5): self._flush() self.last_flush = datetime.utcnow()