#!venv/bin/python # # This script is a very simple APRS-IS LoRa igate that # runs on a raspberry pi and expects a RFM9x on the SPI bus. # # Also, there are two status LEDs on D23 and D24 that count # the incoming messages. # # It is a send-only i-gate that uses the HTTP POST mechanism described # on http://aprs-is.net/SendOnlyPorts.aspx # import datetime import socket import struct import time import requests import busio from digitalio import DigitalInOut, Direction, Pull import board # Import the RFM9x radio module. import adafruit_rfm9x # For APRS-IS APRS_IS_SERVER = "france.aprs2.net" APRS_IS_PORT = 8080 CALLSIGN = "HB9EGM-10" # See https://github.com/aprsorg/aprs-deviceid/blob/main/ALLOCATING.md APRS_DEVICEID = "APZEGM" # 46°23.07' N 6°13.28' LATITUDE = "4623.07N" LONGITUDE = "00613.28E" COMMENT = "RPi RFM98W Python igate, https://mpb.li/git/lora-aprs-hb9egm/tree/python/igate.py" PASSCODE = "17832" REPORTING_DISTANCE = "10" def decode_encoded_position_report(packet): """Returns a string with decoded latitude and longitude, or raises ValueError""" ix = packet.index(b":!") gps_packet = packet[ix+3:] encoded_latitude = gps_packet[:4] encoded_longitude = gps_packet[4:8] (y1, y2, y3, y4) = struct.unpack(" 16: rfm9x.low_datarate_optimize = 1 else: rfm9x.low_datarate_optimize = 0 print("Waiting for packets...") while True: packet = rfm9x.receive(timeout=4, with_header=True) if packet is not None: packet = bytes(packet) # otherwise it's a bytearray print("{}: {}".format(datetime.datetime.now(), packet)) #print(" {}".format(" ".join(hex(x) for x in packet))) if b":!" in packet: try: print(decode_encoded_position_report(packet)) except Exception as e: print(f"Failed to decode packet: {e}") # :! are compressed position reports # :> are messages # := are uncompressed position reports if any(pattern in packet for pattern in [b":!", b":>", b":="]): try: # Prefix igate path path, _, message = packet[3:].partition(b":") new_frame = path + b",qAO," + CALLSIGN.encode() + b":" + message + b"\n" send_frame_to_aprs_is(new_frame) except Exception as ex: print("Error sending position report to APRS-IS") print(ex) print(" RSSI: {}".format(rfm9x.last_rssi)) print() update_leds() now = time.time() if last_beacon_time + 1800 < now: last_beacon_time = now packet = f"{CALLSIGN}>{APRS_DEVICEID},qAC:={LATITUDE}L{LONGITUDE}&{COMMENT}" try: send_frame_to_aprs_is(packet.encode()) except Exception as ex: print("Error sending beacon to APRS-IS") print(ex) except RuntimeError as error: print(f'RFM9x Error: {error}')