def test_unicode(self): _unicode = str if sys.version_info[0] >= 3 else unicode # 7bit ascii result = parse("A>B:>status") self.assertIsInstance(result['status'], _unicode) self.assertEqual(result['status'], _u("status")) # string with degree sign result = parse("A>B:>status\xb0") self.assertIsInstance(result['status'], _unicode) self.assertEqual(result['status'], _u("status\xb0", 'latin-1')) # str with utf8 result = parse("A>B:>статус") self.assertIsInstance(result['status'], _unicode) self.assertEqual(result['status'], _u("статус")) # unicode input result = parse(_u("A>B:>статус")) self.assertIsInstance(result['status'], _unicode) self.assertEqual(result['status'], _u("статус"))
def test_empty_body_of_format_that_is_not_status(self): self.assertRaises(ParseError, parse, "A>B:!") try: parse("A>B:>") except: self.fail("empty status packet shouldn't raise exception")
def test_message_format_branch(self): self.m.StubOutWithMock(parsing, "parse_message") parsing.parse_message("test").AndReturn(('', {'format': ''})) self.m.ReplayAll() parse("A>B::test") self.m.VerifyAll()
def test_message_format_branch(self): self.m.StubOutWithMock(parsing, "_parse_message") parsing._parse_message("test").AndReturn(('', {'format': ''})) self.m.ReplayAll() parse("A>B::test") self.m.VerifyAll()
def test_unsupported_formats_raising(self): with self.assertRaises(UnknownFormat): for packet_type in '#$%)*,<?T[_{': packet = "A>B:%saaa" % packet_type try: parse(packet) except UnknownFormat as exp: self.assertEqual(exp.packet, packet) raise
def test_mice_format_branch(self): self.m.StubOutWithMock(parsing, "_parse_mice") parsing._parse_mice("B", "test").AndReturn(('', {'format': ''})) parsing._parse_mice("D", "test").AndReturn(('', {'format': ''})) self.m.ReplayAll() parse("A>B:`test") parse("C>D:'test") self.m.VerifyAll()
def test_unsupported_formats_raising(self): with self.assertRaises(UnknownFormat): for packet_type in '#$%)*,<?T[_{}': packet = "A>B:%saaa" % packet_type try: parse(packet) except UnknownFormat as exp: self.assertEqual(exp.packet, packet) raise
def test_mice_format_branch(self): self.m.StubOutWithMock(parsing, "parse_mice") parsing.parse_mice("B", "test").AndReturn(('', {'format': ''})) parsing.parse_mice("D", "test").AndReturn(('', {'format': ''})) self.m.ReplayAll() parse("A>B:`test") parse("C>D:'test") self.m.VerifyAll()
def to_readable_output(self, serialized_packet): """Converts the decoded, but serialized packet to a clean, readable output. Intended for human readability. Args: serialized_packet: The raw, decoded APRS packet string. """ try: packet = aprslib.parse(serialized_packet) table_data = [[ "From ", "To ", "Lat ", "Long ", "Alt ", "Comment ", "Text " ], [ self.__get_formatted(packet, 'from', 9), self.__get_formatted(packet, 'to', 9), self.__get_formatted(packet, 'latitude', 8), self.__get_formatted(packet, 'longitude', 8), self.__get_formatted(packet, 'altitude', 8), self.__get_formatted(packet, 'comment', 27), self.__get_formatted(packet, 'text', 27), ]] table_instance = AsciiTable(table_data, ' Packet ') table_instance.inner_heading_row_border = False table_instance.inner_row_border = True return '\n' + table_instance.table except (aprslib.ParseError, aprslib.UnknownFormat): return serialized_packet
def checkPacket(self, packet): if packet.decode('latin-1')[0] == '#': # Client Banner self.logger.info("FWD: %s (station status)" % packet.decode('latin-1')) return True else: try: parsed = parse(packet) if parsed['from'] in self.trackable: self.logger.info("FWD: %s" % packet.decode('utf-8')) return True else: self.logger.info("DROP: %s (noTrack)" % packet.decode('utf-8')) return False except ParseError: if packet.startswith(b'user'): # Login phrase detected callsign = packet.split(b' ')[1].decode('latin-1') self.logger.info("Detected own callsign: %s" % callsign) self.callsigns.append(callsign) self.trackable.append(callsign) return True else: self.logger.info("DROP: %s (invalid packet)" % packet.decode('utf-8')) return False
def test_underscore(header): logger.info(u"Test: test_underscore") failures = list() test_fields = list([u"Humidity", u"Pressure", u'Rain_1H', u"Rain_24H", u"Rain_Since_Midnight", u"Temperature", u"Wind_Direction", u"Wind_Gust", u"Wind_Speed"]) value_fields = list() value_fields.append([64, 1016.0, 0, 21.844, 0.0, 30.0, 359, 0.0, 0.0]) value_fields.append([99, 1018.2, 0.0, 21.844, 8.89, 23.8, 359, 0, 0]) test_message = list() test_message.append(u"_05311550c359s000g000t086r000p086P000h64b10160tU2k") test_message.append(u"_05312040c359s000g000t075r000p086P035h99b10182tU2k") for m, footer in enumerate(test_message): logger.debug(u"Footer : .{}.".format(footer)) logger.debug(u"Header : .{}.".format(header)) header_fields, aprs_addresses = parse_aprs_header(header, footer) logger.debug(u"header_fields : .{}.".format(header_fields)) logger.debug(u"aprs_addresses : .{}.".format(aprs_addresses)) fields = aprslib.parse(aprs_addresses) fields[u"Message_Type"] = u"_" fields.update(header_fields) nf = {k.title(): v for k, v in fields[u"weather"].items()} del fields[u"weather"] nfa = {k.title(): v for k, v in nf.items()} cf = check_fields(value_fields[m], nfa) assert cf is True
def callback(packet): global thread_pool msg = aprslib.parse(packet) #print "Receive:" #print msg callfrom = msg['from'] + " " callfrom = callfrom[0:9] if msg['format'] == 'message': m = re.search(r'ack(\d+)',msg['message_text']) if m : if callfrom in thread_pool: (th,ackl) = thread_pool[callfrom] thread_pool.update({callfrom:(th,ackl + [int(m.group(1))])}) #print "thread pool ack" + m.group(1) return else: if 'msgNo' in msg : #print "send ack" send_ack(callfrom,msg['msgNo']) if callfrom in thread_pool: #print "thread exist" #print thread_pool return else: th = Send_spot(callfrom,AIS) th.start() thread_pool.update({callfrom:(th,[])})
def parse(self, content): soup = BeautifulSoup(content, 'html.parser') results = soup.get_text().rstrip().splitlines()[-1] aprsData = aprslib.parse(results) weatherData = aprsData['weather'] weatherData.update({ 'measure_time': datetime.datetime.fromtimestamp(int( aprsData['timestamp'])).strftime('%H:%M') }) weatherData.update({ 'measure_date': datetime.datetime.fromtimestamp(int( aprsData['timestamp'])).strftime('%d/%m/%Y') }) dew = (float(weatherData['humidity']) / 100)**(1.0 / 8.0) * ( 112.0 + 0.9 * float(weatherData['temperature'])) + 0.1 * float( weatherData['temperature']) - 112 weatherData.update({'dew_point': dew}) data = {} for k, i in self.data_map.iteritems(): value = str(weatherData[k]) value = self._clean(value, i[1]) data[i[0]] = value return data
def get_aprs_json(filename): if not os.path.exists(filename): return None aprs_json = [] # Foreach line for line in open(filename): if not "[Duplicate" in line: # strip duplicates # Extract time and packet match = time_packet.match(line) if match is not None: time = match.group(1) packet = match.group(2) # Process time dt = datetime.strptime(time, '%Y-%m-%d %H:%M:%S') # Process packet pkt = aprslib.parse(packet) if 'latitude' in pkt and 'longitude' in pkt and 'altitude' in pkt: # valid position packet # construct data data = { 'date': dt.strftime("%y%m%d"), 'time': dt.strftime("%H:%M:%S"), 'latitude': pkt['latitude'], 'longitude': pkt['longitude'], 'altitude': pkt['altitude'], '_parsed': { 'time_parsed': time }, } path_index = 1 rx_call = pkt['path'][-path_index] while rx_call.startswith('T2'): path_index = path_index + 1 rx_call = pkt['path'][-path_index] # construct receivers receivers = { rx_call: {} } # construct doc doc = { 'data': data, 'receivers': receivers, } aprs_json.append({ 'doc': doc }) else: # not a valid position packet. ignore for now False return aprs_json
def parseString(self): try: self.aprsDat = aprslib.parse(self.aprsString) except (aprslib.ParseError, aprslib.UnknownFormat) as exp: logging.error(exp) return False
def parse_aprs_packet(packet, callsign): """Parses APRS packets and returns desired info.""" parsed = aprslib.parse(packet) lat = parsed['latitude'] lon = parsed['longitude'] alt = parsed['altitude'] timestamp = parsed['timestamp'] print(lat, lon, alt, timestamp) return(lat, lon, alt, timestamp)
def parse_aprs_packet(packet, callsign): """Parses APRS packets and returns desired info.""" parsed = aprslib.parse(packet) lat = parsed['latitude'] lon = parsed['longitude'] alt = parsed['altitude'] timestamp = parsed['timestamp'] print(lat, lon, alt, timestamp) return (lat, lon, alt, timestamp)
def parse_packet(self, packet): parsed_packet = None f_packet = self.reformat_packet(packet) try: parsed_packet = aprslib.parse(f_packet) except (aprslib.exceptions.UnknownFormat): # print('INFO: Undecodable Message: {}'.format(f_packet)) pass return parsed_packet
def callback(packet): try: decodiert = aprslib.parse(packet) stn = (decodiert['latitude'], decodiert['longitude']) print(time.strftime('%H:%M:%S'), decodiert['from'], "\t", round(distance.distance(home, stn).km, 1), "km") except (KeyError, UnicodeDecodeError, aprslib.ParseError, aprslib.UnknownFormat ) as e: # UnknownFormat("format is not supported") print("cant parse:", e, " mit ->", packet, "<-") pass
def test_MicE(header, footer, rows=100): header_fields, aprs_addresses = parse_aprs_header(header, footer) try: logger.info(u"10 eMic Format") fields = aprslib.parse(aprs_addresses) fields[u"Message_Type"] = u"MicE" fields[u"longitude"] = u"{:6.2f}W".format(fields[u"longitude"]) fields[u"latitude"] = u"{:6.2f}N".format(fields[u"latitude"]) fields.update(header_fields) except Exception, msg: logger.error(u"Parse Error: {}".format(msg))
def parse(self, aprs_packet): """ Takes in a raw APRS packet and returns a parsed APRS data :param aprs_packet: Raw APRS data (str) :return: Parsed APRS data (dict) """ try: return aprslib.parse(aprs_packet.strip()) except (aprslib.ParseError, aprslib.UnknownFormat) as error: self.error_log.error(traceback.format_exc()) traceback.print_exc(file=sys.stdout)
def decode_packet(self, *args, **kwargs): """We get a frame, which has to be decoded.""" frame = kwargs["frame"] LOG.debug(f"Got an APRS Frame '{frame}'") # try and nuke the * from the fromcall sign. frame.header._source._ch = False payload = str(frame.payload.decode()) msg = f"{str(frame.header)}:{payload}" # msg = frame.tnc2 LOG.debug(f"Decoding {msg}") packet = aprslib.parse(msg) return packet
def aprs_parse(packet, settings): # print "aprs_parse called: ", packet try: p_dict = aprslib.parse(packet) res = check_parse(p_dict, settings) if res == False: return False except ParseError as msg: print "aprs_parse failed: ", packet return False # print "aprs_parse worked" return res
def callback(packet): try: obj = aprslib.parse(packet) print(obj) if obj['format'] == 'message' and obj['addresse'] == callsign: spec = obj['message_text'].split('{') if len(spec) == 2: line = callsign + '>' + dst + ',TCPIP*::' + antitrim(obj['from'], ' ', 9) + ':ack'\ + spec[-1].replace('}', '') AIS.sendall(line) obj['message_text'] = spec[0] respond(obj) except aprslib.ParseError as exp: print(packet)
def filter_callsigns(self, packet, packet_i = -1): if self.verbose: print('raw packet : ', packet) if len(packet) == 0: return try: ppac = aprslib.parse(packet) if _DEBUG or _LOG_ALL: with open(os.path.join(tempfile.gettempdir(), 'aprs2gpaero_all_packet.log'), 'a') as f: # termination chosen so that i can use the file for debugging f.write(packet+'\r\n') if self.verbose: print 'parsed :\n', ppac # the form below is useful for debuggging, but in reality we need exact matches since we need to translate to IMEI values. if any([ppac['from'].startswith(x) for x in self.ids_to_be_tracked.keys()]): # we should drop duplicate packets, or those that are too frequent to be real. # ideally, the packets should have a time stamp; tinytrak has this, and likely others, but it's optional. # check if we've seen this packet recently, and if so, drop it # however, we can't look at the raw packet, since we could have gotten it from a different source, which is what we're trying to deduplicate. # i can't gaurantee that we had an independent time stamp, so we'll just use the location information; # this of couse is not guaranteed unitque, but i'm willing to accept the potential loss if one of the last few packets match exactly. short_packet_data = '{:} {:} {:}'.format(ppac['longitude'], ppac['latitude'], ppac.get('altitude', 0)) # get timestamp from packet, if included - not common. timestamp = ppac.get('timestamp', time.time()) if short_packet_data in self.recent_packets.get(ppac['from'], []): self.packet_stats[ppac['from']]['duplicate'] += 1 logging.warning('Dropping duplicate of recent packet - %s' % packet) elif timestamp - self.last_packet_time.get(ppac['from'], 0) < self.min_packet_dt: logging.warning('Got new packet too soon - %0.1f sec after last one, < %0.1f sec : %s' % (timestamp - self.last_packet_time.get(ppac['from'], 0), self.min_packet_dt, packet)) self.packet_stats[ppac['from']]['rate_limit'] += 1 else: self.packet_stats[ppac['from']]['good'] += 1 # only count valid packet for rate limiting. self.last_packet_time[ppac['from']] = timestamp logging.info('Adding packet : {:}'.format(ppac)) self.locations.append({'srccall' : ppac['from'], 'lng' : ppac['longitude'], 'lat' : ppac['latitude'], 'altitude' : ppac.get('altitude', 0), # exception, mostly for debugging, but i'm willing to accept trackers configured without altitude. 'time' : timestamp}) if _DEBUG or self.verbose: print 'after adding\n', self.locations # adding this packet to the recent ones held for the id, regardless of validity self.recent_packets[ppac['from']].append(short_packet_data) elif self.verbose: print 'from {:}, skip'.format(ppac['from']) except Exception as e: #(aprslib.UnknownFormat, aprslib.ParseError:) as e: logging.debug('filter_callsigns - i = {:0d} failed due to {:} raw packet *{:}*'.format(packet_i, e, packet))
def load(self, obj): if not isinstance(obj, dict): if self.format == 'raw': header, self.body = obj.split(":", 1) obj = parse_header(header) else: obj = parse(obj) for k, v in obj.items(): if k == 'format': continue if k == 'to' or k == 'from': k += 'call' if hasattr(self, k): setattr(self, k, v)
def callback(packet): decodiert = aprslib.parse(packet) try: if decodiert['format'] == a_format: print(strftime("%H:%M:%S"), "Von: ", decodiert['from'], " An: ", decodiert['addresse'], "\033[31m", decodiert['message_text'], "\033[0m.") print("RAW: ", decodiert['raw']) if decodiert['addresse'] == meinemsgid and not decodiert[ 'message_text'].find(":ack") >= 1: # print("Sende ACK an ", decodiert['from']) ack_senden(decodiert['from'], str(decodiert['msgNo'])) except KeyError: print('Wohl ein <ack> ... ?') print(decodiert['raw']) pass
def test_valid_thirdparty_msg(self): packet = "A-1>APRS,B-2,WIDE1*:}C>APU25N,TCPIP,A-1*::DEF :ack56" result = parse(packet) self.assertEqual(result['via'], '') self.assertEqual(result['to'], 'APRS') self.assertEqual(result['from'], 'A-1') self.assertEqual(result['format'], 'thirdparty') self.assertEqual(result['raw'], packet) self.assertEqual(result['path'], ['B-2', 'WIDE1*']) self.assertEqual(result['subpacket']['raw'], packet[21:]) self.assertEqual(result['subpacket']['via'], '') self.assertEqual(result['subpacket']['msgNo'], '56') self.assertEqual(result['subpacket']['from'], 'C') self.assertEqual(result['subpacket']['path'], ['TCPIP', 'A-1*']) self.assertEqual(result['subpacket']['response'], 'ack') self.assertEqual(result['subpacket']['format'], 'message') self.assertEqual(result['subpacket']['to'], 'APU25N') self.assertEqual(result['subpacket']['addressee'], 'DEF')
def aprs_decode(udp_packet): k = {} aprs_data = udp_packet.split("\n") if len(aprs_data) > 1: aa = aprs_data[0].split(" ") try: if aa[3] == str(aprslib.passcode(aa[1].encode("UTF-8"))): try: packet = aprslib.parse(aprs_data[1]) except (aprslib.ParseError, aprslib.UnknownFormat) as exp: print("Aprs parse error %s" % exp) print("Aprs packet %s " % udp_packet) packet = None else: k['id'] = packet['from'].upper() try: k['lat'] = packet['latitude'] except: k['lat'] = 0 try: k['lon'] = packet['longitude'] except: k['lon'] = 0 try: k['speed'] = packet['speed'] except: k['speed'] = 0 try: k['hdop'] = packet['course'] except: k['hdop'] = 0 try: k['timestamp'] = packet[''] except: k['timestamp'] = int(time.time()) try: k['altitude'] = packet['altitude'] except: k['altitude'] = 0 except: print("Aprs split : %s " % aa) else: print("Aprs packet : %s " % udp_packet) return k
def process_packet(packet, conn, rxtime=None, is_subpacket=False): """ Load an unparsed APRS packet into the database packet: APRS packet string conn: psycopg2 database connection rxtime: time packet was received, as seconds since epoch (1/1/1970); if omitted or wrong format, uses system clock is_subpacket: boolean flag for whether this is a sub-packet returns packet_id (positive bigint) if successful, negative integer if not """ # Check for reasonable timestamp if (type(rxtime) is not float and type(rxtime) is not int or (rxtime is None)): rxtime = time.time() try: # Parse it parsed = aprslib.parse(packet) except aprslib.exceptions.ParseError as pe: try: # Salvage what data we can from the header of an unparseable packet parsed = pe.parsed parsed['format'] = "parseerror" except: # Couldn't salvage anything print("Unable to partially parse packet: '" + packet + "' at time " + str(rxtime)) # DEBUG return (-6) # Unable to partially parse packet except aprslib.exceptions.UnknownFormat as uf: # Save what headers we can if the format is unknown parsed = uf.parsed parsed['format'] = "unknown" except: # Something else went wrong print("Unable to parse packet") # DEBUG raise return (-5) # Unable to parse packet # Add the receiving station metadata to the parsed data parsed.update({ 'rxtime': rxtime, 'rxsession': session_id, 'is_subpacket': is_subpacket }) # Send the parsed data on for further processing return (process_parsed(parsed, conn, rxtime, is_subpacket))
def test_status_format_branch(self): def _u(text, c='utf8'): if sys.version_info[0] >= 3: return text else: return text.decode(c) expected = { 'status': 'test', 'raw': _u('A>B:>test'), 'via': '', 'from': _u('A'), 'to': _u('B'), 'path': [], 'format': 'status' } result = parse("A>B:>test") self.assertEqual(result, expected)
def process_frame(self, frame): """ Process an incoming frame according to configured options. """ frame_dict = {} # If we're in debug mode... if self.__debug: print("Incoming frame:") self.hexdump(frame) print("") frame_base64 = "%s" % base64.standard_b64encode(frame).decode() frame_dict.update({'frame_base64': frame_base64}) try: aprs_parsed = "%s" % aprs.parse_frame(frame) frame_dict.update(aprslib.parse(aprs_parsed)) except ValueError: frame_dict.update({'parse_error_message': "ValueError"}) except aprslib.exceptions.ParseError: frame_dict.update({'parse_error_message': "ParseError"}) except aprslib.exceptions.UnknownFormat: frame_dict.update({'parse_error_message': "UnknownFormat"}) # Ugly AF, we really don't want to be doing this. try: self.__m.publish(self.__mqtt_rx_topic, json.dumps(frame_dict)) except (KeyboardInterrupt, SystemExit): raise except: print(traceback.format_exc()) self.__mqtt_connect()
def test_status_format_branch(self): self.m.StubOutWithMock(parsing, "_parse_timestamp") parsing._parse_timestamp(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(("test", {})) self.m.ReplayAll() def _u(text, c='utf8'): if sys.version_info[0] >= 3: return text else: return text.decode(c) expected = { 'status': 'test', 'raw': _u('A>B:>test'), 'via': '', 'from': _u('A'), 'to': _u('B'), 'path': [], 'format': 'status' } result = parse("A>B:>test") self.assertEqual(result, expected) self.m.VerifyAll()
def decode_aprs_messages(self, msgs): """ Decode APRS Messages :param msgs: :return: """ header = None footer = None for n, message in enumerate(msgs): try: header = message[0].lstrip() footer = message[1].lstrip() header_fields, aprs_addresses = parse_aprs_header(header, footer, n=n) logger.debug(u"%3d [%s]" % (n, header[10:])) logger.debug(u" [%s]" % footer) # 1 $ULTW if re.match(r"^\$ULTW.*", footer, re.M | re.I): # # __________________________________________________________________________________ # $ indicates a Ultimeter 2000 # $ULTW 0000 0000 01FF 0004 27C7 0002 CCD3 0001 026E 003A 050F 0004 0000 u""" Field #1, 0000 = Wind Speed Peak over last 5 min. ( reported as 0.1 kph increments) Field #2, 0000 = Wind Direction of Wind Speed Peak (0-255) Field #3, 01FF = Current Outdoor Temp (reported as 0.1 deg F increments) Field #4, 0004 = Rain Long Term Total (reported in 0.01 in. increments) Field #5, 27C7 = Current Barometer (reported in 0.1 mbar increments) Field #6, 0002 = Barometer Delta Value(reported in 0.1 mbar increments) Field #7, CCD3 = Barometer Corr. Factor(LSW) Field #8, 0001 = Barometer Corr. Factor(MSW) Field #9, 026E = Current Outdoor Humidity (reported in 0.1% increments) Field #10, 003A = Date (day of year since January 1) Field #11, 050F = Time (minute of day) Field #12, 0004 = Today's Rain Total (reported as 0.01 inch increments) Field #13, 0000 = 1 Minute Wind Speed Average (reported in 0.1kph increments) """ # 1 2 3 4 5 6 7 8 9 10 11 12 13 # $ULTW 0138 0005 02B3 CEF8 27CC FFEF 8782 0001 0142 003D 0370 0000 00CE # $ULTW 0018 0060 02D8 8214 27C4 0003 8702 0001 03E8 0099 0050 0000 0001 # $ULTW 0000 0000 02D3 670F 27BC FFFD 8765 0001 03E8 0099 0046 0001 0000 # $ULTW 00B0 0042 02D3 25ED 27C2 0010 8935 0001 03E8 0098 050A 0037 006A # $ULTW 0064 00AB 030C 1821 2784 0001 85E5 0001 0323 009B 058C 0000 0026 # 1.7 66 72.3 97.09 1017.8 1.6 35125 1 100.0 152 1290 0.55 10.6 # rem0 = u"^\$ULTW[0-9a-fA-F]{52}" if re.match(rem0, footer): try: logger.debug(u"1 Ultimeter 2000") message_bytes = (5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0) fields = parse_aprs_footer(footer, message_bytes) fields[u"Message_Type"] = u"$ULTW" fields.update(header_fields) if fields[u"Wind Direction"] == 0: fields[u"Wind Direction"] = u"N" self.queue_display(fields, header=header, footer=footer) except Exception as e: logger.warn(u"{} {} {}".format(sys.exc_info()[-1].tb_lineno, type(e), e)) # 2 aprslib _ elif re.match(r"^_.*", footer, re.M | re.I): # __________________________________________________________________________________ # Positionless Weather Report Positionless Weather Data # _ 0731 1701 c189 s004 g006 t094 r000 p000 P000 h00 b1016 5wDAV # _ Message # 0731 Month Day # 1701 Time # c189 Wind Direction # s004 Wind Speed # g006 Wind Gust in the last five minutes # t094 Temperature # r000 Rainfall in the last hour # p000 Rainfall in the last 24 hours # P000 Rainfall since midnight # h00 Humidity # b1016 Barometric Pressure # 5 APRS Software # wDAV WX Unit - WinAPRS # 1 2 3 4 5 6 # 123456789 0123 4567 8901 2345 6789 0123 4567 890 123456 78901234567890123456789 # _01241225 c257 s001 g008 t066 r000 p002 P000 h51 b10219 tU2k # _05311550 c359 s000 g000 t086 r000 p086 P000 h64 b10160 tU2k # _05312040 c359 s000 g000 t075 r000 p086 P035 h99 b10182 tU2k # _06051349 c359 s000 g000 t081 r000 p037 P023 h89 b10084 tU2k # _01241225 c257 s001 g008 t066 r000 p002 P000 h51 b10219 tU2k # rem = u"^_\d{8}c\d{3}s\d{3}g\d{3}t\d{3}r\d{3}p\d{3}P\d{3}h\d{2}b\d{5}.*" try: # This seems to fail alot rem0 = u"^_\d{8}c\d{3}s\d{3}g\d{3}t\d{3}r\d{3}p\d{3}P\d{3}h\d{2}b\d{5}.*" if re.match(rem0, footer, re.M): logger.info(u"_2a Raw Weather Report") fields = aprslib.parse(aprs_addresses) fields[u"Message_Type"] = u"_a" fields.update(header_fields) nf = {k.title(): v for k, v in fields[u"weather"].items()} del fields[u"weather"] nfa = {k.title(): v for k, v in nf.items()} self.queue_display(nfa, header=header, footer=footer) except Exception, msg: try: # MDHM logger.info(u"_2b Positionless Weather Report") message_bytes = (1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 6, 1, 3, 0) fields = parse_aprs_footer(footer, message_bytes) fields[u"Message_Type"] = u"_b" fields.update(header_fields) self.queue_display(fields, header=header, footer=footer) except Exception, msg: logger.info(u"_2c Positionless Weather Report") message_bytes = (1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 5, 1, 4, 0) fields = parse_aprs_footer(footer, message_bytes) fields[u"Message_Type"] = u"_c" fields.update(header_fields) self.queue_display(fields, header=header, footer=footer) # 3 aprslib ! elif re.match(r"^!.*", footer, re.M | re.I): # __________________________________________________________________________________ # Raw Weather Report - Ultimeter # !2749.10N S 08215.39W # PHG56304/W3,FLn Riverview, FL www.ni4ce.org (wind @ 810ft AGL) # ! Message # 2749.10N Latitude # S Symbol Table ID # 08215.39W Longitude # # # PHG56304/W3,FLn Riverview, FL www.ni4ce.org (wind @ 810ft AGL) # !2818.17N/08209.50W#Dade City rem0 = u"^!\d{4}\.\d{2}(N|S)(_|S|P)\d{5}\.\d{2}(E|W)(#|_)(.|,|/| \(\@).+" rem1 = u"^!\d{4}\.\d{2}(N|S)(_|S|P)\d{5}\.\d{2}(E|W).+" rem2 = u"^!\d{4}\.\d{2}(N|S)/\d{5}\.\d{2}(W|E)(#|_|).+" if re.match(rem0, footer): try: logger.info(u"!3a Raw Weather Report") fields = aprslib.parse(aprs_addresses) fields[u"Message_Type"] = u"!a" fields[u"longitude"] = u"{:6.2f}W".format(fields[u"longitude"]) fields[u"latitude"] = u"{:6.2f}N".format(fields[u"latitude"]) fields.update(header_fields) self.queue_display(fields, header=header, footer=footer) except Exception as e: logger.warn( u"decode_aprs_messages {} {} {}".format(sys.exc_info()[-1].tb_lineno, type(e), e)) elif re.match(rem1, footer): try: logger.info(u"!3b Raw Weather Report") message_bytes = (1, 8, 1, 9, 1, 0) fields = parse_aprs_footer(footer, message_bytes) fields[u"Message_Type"] = u"!b" fields[u"longitude"] = u"{:6.2f}".format(fields[u"longitude"]) fields[u"latitude"] = u"{:6.2f}".format(fields[u"latitude"]) fields.update(header_fields) self.queue_display(fields, header=header, footer=footer) except Exception as e: logger.warn( u"decode_aprs_messages {} {} {}".format(sys.exc_info()[-1].tb_lineno, type(e), e))
pass except Exception, msg: try: logger.warn(u"4a %s" % msg) logger.info(u"=4b Complete Weather Report") message_bytes = (1, 1, 4, 4, 1, 2, 1, 4, 4, 4, 4, 4, 4, 3, 6, 0) fields = parse_aprs_footer(footer, message_bytes) fields.update(header_fields) fields[u"Message_Type"] = u"=b" self.queue_display(fields, header=header, footer=footer) except Exception, msg: try: logger.warn(u"4 %s" % msg) logger.info(u"4 Complete Weather Report") fields = aprslib.parse(aprs_addresses) fields.update(header_fields) fields[u"Message_Type"] = u"=c" self.queue_display(fields, header=header, footer=footer) except Exception as e: logger.warn( u"decode_aprs_messages {} {} {}".format(sys.exc_info()[-1].tb_lineno, type(e), e)) # 5 aprslib @ elif re.match(r"^@.*", footer, re.M | re.I): fields = dict() fields[u"Message_Type"] = u"@" fields[u"header"] = header fields[u"footer"] = footer
#!/bin/env python import aprslib, pprint # Put the output from the APRS message into "message" and hope it works message = "VE3YCA-4>APRS-0,DIGI2-1,DIGI1-1:/092231h4437.28N/07930.57Ws000/000/A=000897/Comment" packet = aprslib.parse(message) pprint.pprint(packet)
"Message received on default handler, destined to " + m.channel + ": " + m. as_string() + "\n\n") emitter.on_error = lambda e: print("Error received: " + str(e) + "\n\n") emitter.on_me = lambda me: print("Information about Me received: " + str(me) + "\n\n") emitter.on_keyban = lambda kb: print("Keyban message received: " + str(kb) + "\n\n") emitter.loop_start() while True: data, addr = axudpsocket.recvfrom(RECV_BUFFER_LENGTH) isaprs, metadata = axtostr(data) metadata = {k: int(v) for k, v in metadata.items()} try: aprsdict = aprslib.parse(isaprs) aprsdict.update(metadata) aprsdict.mode = AXUDP_MODE aprsjson = json.dumps(aprsdict) if connected: if "latitude" in aprsdict and "longitude" in aprsdict: emitter.publish( MQTT_KEY, f'/{MQTT_TOPIC}/point/{math.floor(aprsdict["latitude"])}/{math.floor(aprsdict["longitude"])}/{aprsdict["from"]}', aprsjson, {}) emitter.publish(MQTT_KEY, f'{MQTT_TOPIC}/message/{aprsdict["from"]}', aprsjson, {}) except (aprslib.ParseError, aprslib.UnknownFormat) as exp:
def list_loop(): call = "null" # position cursor in -1 slot, as the first thing the loop does is increment slot y = padding + title_bar_height - font.getsize("ABCJQ")[1] x = padding max_lines = (height - title_bar_height - padding) // line_height max_cols = (width // max_line_width) line_count = 0 col_count = 0 while True: line = f.stdout.readline().decode("utf-8", errors="ignore") # watch for regular packet search = re.search("^\[\d\.\d\] (.*)", line) if search is not None: packetstring = search.group(1) packetstring = packetstring.replace('<0x0d>', '\x0d').replace( '<0x1c>', '\x1c').replace('<0x1e>', '\x1e').replace( '<0x1f>', '\0x1f').replace('<0x0a>', '\0x0a') else: continue lastcall = call try: # aprslib has trouble parsing all packets packet = aprslib.parse(packetstring) call = packet['from'] if 'symbol' in packet: symbol = packet['symbol'] symbol_table = packet['symbol_table'] else: symbol = '/' symbol_table = '/' except: # if it fails, let's just snag the callsign #print("aprslib failed to parse.") search = re.search("^\[\d\.\d\] ([a-zA-Z0-9-]*)", line) if search is not None: call = search.group(1) symbol = '/' symbol_table = '/' else: continue offset = ord(symbol) - 33 row = offset // 16 col = offset % 16 if call == lastcall: # blink duplicates time.sleep(0.5) draw.text( (x + symbol_dimension + (symbol_dimension // 8), y), call, font=font, fill="#000000") # start text after symbol, relative padding with display_lock: disp.image(image) time.sleep(0.1) draw.text( (x + symbol_dimension + (symbol_dimension // 8), y), call, font=font, fill="#AAAAAA") # start text after symbol, relative padding with display_lock: disp.image(image) else: y += line_height if line_count == max_lines: # about to write off bottom edge of screen col_count += 1 x = col_count * max_line_width y = padding + title_bar_height line_count = 0 if col_count == max_cols: # about to write off right edge of screen x = padding y = padding + title_bar_height draw.rectangle((0, title_bar_height + 1, width, height), outline=0, fill="#000000") # erase lines line_count = 0 col_count = 0 time.sleep(2.0) crop_area = (col * symbol_dimension, row * symbol_dimension, col * symbol_dimension + symbol_dimension, row * symbol_dimension + symbol_dimension) if symbol_table == '/': symbolimage = symbol_chart0x64.crop(crop_area) else: symbolimage = symbol_chart1x64.crop(crop_area) image.paste(symbolimage, (x, y), symbolimage) draw.text( (x + symbol_dimension + (symbol_dimension // 8), y), call, font=font, fill="#AAAAAA") # start text after symbol, relative padding line_count += 1 with display_lock: disp.image(image)
def single_loop(): symbol_chart0x64 = Image.open("aprs-symbols-64-0.png") symbol_chart1x64 = Image.open("aprs-symbols-64-1.png") # we try to get callsign, symbol and four relevant info lines from every packet while True: info1 = info2 = info3 = info4 = '' # sane defaults line = f.stdout.readline().decode("utf-8", errors="ignore") search = re.search( "^\[\d\.\d\] (.*)", line) # see if logfile line is an incoming packet over RF if search is not None: packetstring = search.group(1) packetstring = packetstring.replace('<0x0d>', '\x0d').replace( '<0x1c>', '\x1c').replace('<0x1e>', '\x1e').replace( '<0x1f>', '\0x1f').replace('<0x0a>', '\0x0a') else: continue try: packet = aprslib.parse(packetstring) # parse packet #print(packet) call = packet['from'] supported_packet = True except Exception as e: # aprslib doesn't support all packet types #print("Exception: aprslib: ", str(e), ": ", packetstring) supported_packet = False packet = {} search = re.search("^\[\d\.\d\] ([a-zA-Z0-9-]*)", line) # snag callsign from unsupported packet if search is not None: call = search.group(1) symbol = '/' # unsupported packet symbol set to red ball symbol_table = '/' else: continue try: if supported_packet: if 'symbol' in packet: # get symbol from valid packet or use red ball symbol = packet['symbol'] symbol_table = packet['symbol_table'] else: symbol = '/' symbol_table = '/' # extract relevant info lines if not supported_packet: info1 = info2 = info3 = info4 = '' # no info in unsupported packet elif 'weather' in packet: # weather (often contained in compressed/uncompressed type packets) info1 = round(packet['weather']['temperature']) info1 = str(int(info1) * 1.8 + 32) + 'F' #print(info1) info2 = str( packet['weather']['rain_since_midnight']) + '\" rain' #print(info2) info3 = str(round(packet['weather']['wind_speed'])) + ' m/h' info3 = info3 + ' ' + str( packet['weather']['wind_direction']) + '\'' #print(info3) info4 = str(packet['comment']) #print(info4) # position packet elif packet['format'] == 'mic-e' or packet[ 'format'] == 'compressed' or packet[ 'format'] == 'uncompressed' or packet[ 'format'] == 'object': info4 = packet[ 'comment'] # fixme: comment is jibberish in all compressed packets elif 'status' in packet: # status packet info4 = packet['status'] except Exception as e: print("Malformed/missing data: ", str(e), ": ", packetstring) symbol_dimension = 64 offset = ord(symbol) - 33 row = offset // 16 col = offset % 16 y = height // 3 x = width // 3 draw.rectangle((0, title_bar_height, width, height), outline=0, fill="#000000") # erase most of screen crop_area = (col * symbol_dimension, row * symbol_dimension, col * symbol_dimension + symbol_dimension, row * symbol_dimension + symbol_dimension) if symbol_table == '/': symbolimage = symbol_chart0x64.crop(crop_area) else: symbolimage = symbol_chart1x64.crop(crop_area) symbolimage = symbolimage.resize((height // 2, height // 2), Image.NEAREST) #image.paste(symbolimage, (0, 36), symbolimage) image.paste(symbolimage, (0, title_bar_height), symbolimage) draw.text((120, 50), str(info1), font=font_small, fill="#AAAAAA") draw.text((120, 70), str(info2), font=font_small, fill="#AAAAAA") draw.text((120, 90), str(info3), font=font_small, fill="#AAAAAA") draw.text((5, 144), str(info4), font=font_small, fill="#AAAAAA") draw.text((5, height - font_epic.getsize("X")[1] - 3), call, font=font_epic, fill="#AAAAAA") # text up from bottom edge with display_lock: disp.image(image) time.sleep(1)
timestamps = [] # populate timestamps with first value in every line (ended by a colon) before actual APRS packet for packet_index in range(0, len(aprs_packets)): current_line = aprs_packets[packet_index].split(': ') timestamps.append(current_line[0]) aprs_packets[packet_index] = current_line[1] # open output and write header, then write parsed APRS data with open(output_filename, "w") as output_file: output_file.write('timestamp,' + ','.join(desired_fields) + '\n') # for each line in the input data, parse the APRS data using aprslib and extract the desired fields for packet_index in range(0, len(aprs_packets)): # parse data and append timestamp to front current_aprs_packet = aprslib.parse(aprs_packets[packet_index]) current_output_data = [str(timestamps[packet_index])] # extract and append desired fields, preserving CSV format by replacing commas with semicolons for field_index in range(0, len(desired_fields)): # get current field name field = desired_fields[field_index] # check if field exists in current packet if field in current_aprs_packet: # replace commas in data with semicolons to preserve CSV format current_output_data.append( str(current_aprs_packet[field]).replace(',', ';')) else: # if the field is not in the current APRS packet, append the null data value instead current_output_data.append(null_data_string)
def process(package): if myCall in package: try: interpret(aprslib.parse(package)) except (aprslib.ParseError, aprslib.UnknownFormat) as exp: pass