diff --git a/RPi-LoRa-KISS-TNC/KissHelper.py b/RPi-LoRa-KISS-TNC/KissHelper.py index c09213c..7e00d27 100644 --- a/RPi-LoRa-KISS-TNC/KissHelper.py +++ b/RPi-LoRa-KISS-TNC/KissHelper.py @@ -29,6 +29,7 @@ # Changes by PE1RXF # # 2022-01-23: - in encode_address() added correct handling of has_been_repeated flag '*' +# 2022-01-28: - in encode_kiss() and encode_address(): better exeption handling for corrupted or mal-formatted APRS frames # import struct @@ -56,12 +57,23 @@ def encode_address(s, final): # print("Message has been repeated") ssid = ssid[:-1] encoded_ssid |= 0b10000000 - - encoded_ssid |= (int(ssid) << 1) | 0b01100000 | (0b00000001 if final else 0) - + + # If SSID was not pressent (and we added the default -0 to it), the has_been_repeated flag could be at the end of the call, so check that as well + # Also, there is a lot of bad software around (including this code) and ignorance of the specifications (are there any specs for LoRa APRS?), so always check for the has_been_repeated flag + if call[-1] == 42: + call = call[:-1] + encoded_ssid |= 0b10000000 + + # SSID should now be one or two postions long and contain a number (idealy between 0 and 15). + if len(ssid) == 1 and ssid[0] > 47 and ssid[0] < 58: + encoded_ssid |= (int(ssid) << 1) | 0b01100000 | (0b00000001 if final else 0) + elif len(ssid) == 2 and ssid[0] > 47 and ssid[0] < 58 and ssid[1] > 47 and ssid[2] < 58: + encoded_ssid |= (int(ssid) << 1) | 0b01100000 | (0b00000001 if final else 0) + else: + return None + return encoded_call + [encoded_ssid] - def decode_address(data, cursor): (a1, a2, a3, a4, a5, a6, a7) = struct.unpack("> 5 @@ -74,22 +86,56 @@ def decode_address(data, cursor): call = addr return (call, hrr, ext) - +######################################################################## +# Encode string from LoRa radio to AX.25 over KISS +# +# We must make no assumptions as the incomming frame could be carbage. +# So make sure we think of everthing in order to prevent crashes. +# +# The original code from Thomas Kottek did a good job encoding propper APRS frames. +# But when the frames where not what they should be, the program could crash. +# +######################################################################## def encode_kiss(frame): - # Ugly frame disassembling + + # First check: do we have a semi column (seperator path field and data field) + # Note that we could still be wrong: for example when the field seperator is corrupted and we now find a semi column from, lets say, an internet address in the data field... if not b":" in frame: return None + + # Split the frame in a path field and a data field path = frame.split(b":")[0] + data_field = frame[frame.find(b":") + 1:] + + # The source address is always followed by a greather than sign, so lets see if its there. + # There is always a change that there is another greather than sign because the frame could be corrupted... + if not b">" in path: + return None + + # Split the path into a source address and a digi-path array (because digis should be seperated by commas, but again, corruption....) src_addr = path.split(b">")[0] digis = path[path.find(b">") + 1:].split(b",") + # destination address - packet = encode_address(digis.pop(0).upper(), False) + return_value = encode_address(digis.pop(0).upper(), False) + if return_value is None: + return None + packet = return_value + # source address - packet += encode_address(path.split(b">")[0].upper(), len(digis) == 0) + return_value = encode_address(src_addr.upper(), len(digis) == 0) + if return_value is None: + return None + packet += return_value + # digipeaters for digi in digis: final_addr = digis.index(digi) == len(digis) - 1 - packet += encode_address(digi.upper(), final_addr) + return_value = encode_address(digi.upper(), final_addr) + if return_value is None: + return None + packet += return_value + # control field packet += [0x03] # This is an UI frame # protocol ID