From ba2fbe0ae8c762318931d82c1fea46bb02850620 Mon Sep 17 00:00:00 2001 From: Joerg Schultze-Lutter Date: Thu, 9 Sep 2021 22:13:33 +0200 Subject: [PATCH] ackMsgNo support (http://www.aprs.org/aprs11/replyacks.txt) Decode the 'new' ackMsgNo for ack/rej responses and standard APRS messages while still honoring the original format from aprs101.pdf The following assumptions apply when handling APRS messages in general: Option 1: no message ID present: send no ACK outgoing messages have no msg number attachment Example data exchange 1: DF1JSL-4>APRS,TCPIP*,qAC,T2PRT::WXBOT :94043 WXBOT>APRS,qAS,KI6WJP::DF1JSL-4 :Mountain View CA. Today,Sunny High 60 Example data exchange 2: DF1JSL-4>APRS,TCPIP*,qAC,T2SPAIN::EMAIL-2 :blah@gmail.com Hallo EMAIL-2>APJIE4,TCPIP*,qAC,AE5PL-JF::DF1JSL-4 :Email sent to blah@gmail.com Option 2: old message number format is present: (example: msg{12345) Send ack with message number from original message (ack12345) All outgoing messages have trailing msg number ( {abcde ); can be numeric or slphanumeric counter. See aprs101.pdf chapter 14 Example data exchange 1: DF1JSL-4>APRS,TCPIP*,qAC,T2SP::EMAIL-2 :blah@gmail.com Hallo{12345 EMAIL-2>APJIE4,TCPIP*,qAC,AE5PL-JF::DF1JSL-4 :ack12345 EMAIL-2>APJIE4,TCPIP*,qAC,AE5PL-JF::DF1JSL-4 :Email sent to blah@gmail.com{891 DF1JSL-4>APOSB,TCPIP*,qAS,DF1JSL::EMAIL-2 :ack891 Example data exchange 2: DF1JSL-4>APRS,TCPIP*,qAC,T2CSNGRAD::EMAIL-2 :blah@gmail.com{ABCDE EMAIL-2>APJIE4,TCPIP*,qAC,AE5PL-JF::DF1JSL-4 :ackABCDE EMAIL-2>APJIE4,TCPIP*,qAC,AE5PL-JF::DF1JSL-4 :Email sent to blah@gmail.com{893 DF1JSL-4>APOSB,TCPIP*,qAS,DF1JSL::EMAIL-2 :ack893 Option 3: new messages with message ID but without trailing retry msg ids: msg{AB} Do NOT send extra ack All outgoing messages have 2-character msg id, followed by message ID from original message Example: User sends message "Hello{AB}" to MPAD MPAD responds "Message content line 1{DE}AB" to user MPAD responds "Message content line 2{DF}AB" to user AB -> original message DE, DF -> message IDs generated by MPAD Example data exchange 1: DF1JSL-4>APRS,TCPIP*,qAC,T2NUERNBG::WXBOT :99801{AB} WXBOT>APRS,qAS,KI6WJP::DF1JSL-4 :Lemon Creek AK. Today,Scattered Rain/Snow and Patchy Fog 50% {QL}AB DF1JSL-4>APOSB,TCPIP*,qAS,DF1JSL::WXBOT :ackQL}AB WXBOT>APRS,qAS,KI6WJP::DF1JSL-4 :High 40{QM}AB DF1JSL-4>APOSB,TCPIP*,qAS,DF1JSL::WXBOT :ackQM}AB Example data exchange 2: DF1JSL-4>APRS,TCPIP*,qAC,T2SPAIN::EMAIL-2 :blah@gmail.com Hallo{AB} EMAIL-2>APJIE4,TCPIP*,qAC,AE5PL-JF::DF1JSL-4 :Email sent to blah@gmail.com{OQ}AB DF1JSL-4>APOSB,TCPIP*,qAS,DF1JSL::EMAIL-2 :ackOQ}AB Option 4: new messages with message ID and with trailing retry msg ids: msg{AB}CD Follow the instructions as per http://www.aprs.org/aprs11/replyacks.txt Example data exchange 1: DF1JSL-4>APRS,TCPIP*,qAC,T2CZECH::WXBOT :99801{LM}AA WXBOT>APRS,qAS,KI6WJP::DF1JSL-4 :Lemon Creek AK. Today,Scattered Rain/Snow and Patchy Fog 50% {QP}LM DF1JSL-4>APOSB,TCPIP*,qAS,DF1JSL::WXBOT :ackQP}LM WXBOT>APRS,qAS,KI6WJP::DF1JSL-4 :High 40{QQ}LM DF1JSL-4>APOSB,TCPIP*,qAS,DF1JSL::WXBOT :ackQQ}LM Example data exchange 2: DF1JSL-4>APRS,TCPIP*,qAC,T2SP::EMAIL-2 :blah@gmail.com Welt{DE}FG EMAIL-2>APJIE4,TCPIP*,qAC,AE5PL-JF::DF1JSL-4 :Email sent to blah@gmail.com{OS}DE DF1JSL-4>APOSB,TCPIP*,qAS,DF1JSL::EMAIL-2 :ackOS}DE --- aprslib/parsing/message.py | 88 +++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 7 deletions(-) diff --git a/aprslib/parsing/message.py b/aprslib/parsing/message.py index 3995629..e06ef88 100644 --- a/aprslib/parsing/message.py +++ b/aprslib/parsing/message.py @@ -72,25 +72,99 @@ def parse_message(body): else: logger.debug("Packet is just a regular message") parsed.update({'format': 'message'}) + has_matched = False + # APRS supports two different message formats: + # - the standard format which is described in 'aprs101.pdf': + # http://www.aprs.org/doc/APRS101.PDF + # - an addendum from 1999 which introduces a new format: + # http://www.aprs.org/aprs11/replyacks.txt + # + # A message (ack/rej as well as a standard msg text body) can either have: + # - no message number at all + # - a message number in the old format (1..5 characters / digits) + # - a message number in the new format (2 characters / digits) without trailing 'ack msg no' + # - a message number in the new format with trailing 'free ack msg no' (2 characters / digits) + + # + # ack / rej parser + # + # search for: ack/rej new format with msgNo present and ackMsgNo present + if not has_matched: + match = re.search(r"^(ack|rej)([A-Za-z0-9]{2})}([A-Za-z0-9]{2})$", body) + if match: + parsed.update({ + 'response': match.group(1), + 'msgNo': match.group(2), + 'ackMsgNo': match.group(3) + }) + if "message_text" in parsed: + parsed.pop("message_text") + has_matched = True + + # search for: ack/rej new format with msgNo present and ackMsgNo not present + if not has_matched: + match = re.search(r"^(ack|rej)([A-Za-z0-9]{2})}$", body) + if match: + parsed.update({ + 'response': match.group(1), + 'msgNo': match.group(2), + }) + if "message_text" in parsed: + parsed.pop("message_text") + has_matched = True + + # search for: ack/rej standard format as per aprs101.pdf chapter 14 match = re.search(r"^(ack|rej)([A-Za-z0-9]{1,5})$", body) if match: parsed.update({ 'response': match.group(1), 'msgNo': match.group(2), }) - else: - body = body[0:70] + if "message_text" in parsed: + parsed.pop("message_text") + has_matched = True - match = re.search(r"{([A-Za-z0-9]{1,5})$", body) + # + # regular message body parser + # + if not has_matched: + # new message format: http://www.aprs.org/aprs11/replyacks.txt + # search for: msgText/msgNo/ackMsgNo all present + match = re.search(r"{([A-Za-z0-9]{2})}([A-Za-z0-9]{2})$", body) if match: msgNo = match.group(1) - body = body[:len(body) - 1 - len(msgNo)] - + ackMsgNo = match.group(2) + body = body[:len(body) - 3 - len(msgNo) - len(ackMsgNo)] parsed.update({'msgNo': msgNo}) + parsed.update({'ackMsgNo': ackMsgNo}) + has_matched = True + parsed.update({'message_text': body[0:67].strip()}) - parsed.update({'message_text': body.strip(' ')}) + # new message format: http://www.aprs.org/aprs11/replyacks.txt + # search for: msgText/msgNo present and ackMsgNo not present + if not has_matched: + match = re.search(r"{([A-Za-z0-9]{2})}$", body) + if match: + msgNo = match.group(1) + body = body[:len(body) - 2 - len(msgNo)] + parsed.update({'msgNo': msgNo}) + has_matched = True + body = body[0:67].strip() + parsed.update({'message_text': body[0:67].strip()}) - break + # old message format - see aprs101.pdf. + # search for: msgNo present + if not has_matched: + match = re.search(r"{([A-Za-z0-9]{1,5})$", body) + if match: + msgNo = match.group(1) + body = body[:len(body) - 1 - len(msgNo)] + parsed.update({'msgNo': msgNo}) + has_matched = True + parsed.update({'message_text': body[0:67].strip()}) + + # break free from the eternal 'while' + break return ('', parsed)