RadioLib icon indicating copy to clipboard operation
RadioLib copied to clipboard

Receiving APRS

Open sebastian-king opened this issue 1 year ago • 16 comments

I am interested in receiving APRS data using RadioLib, however, I am struggling to figure out how to do so. The APRSClient only provides methods for sending packets, not receiving them.

I am using the following code to try to receive an APRS packet, however, even though my handheld radio is receiving APRS just fine, my SX1276 module does not show any received packets. I am able to transmit ax.25 packets without issue.

#include <RadioLib.h>
#include "utilities.h"
#include "boards.h"

SX1276 radio = new Module(RADIO_CS_PIN, RADIO_DI0_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);

void setup() {
  Serial.begin(9600);

  // initialize SX1276 with default settings
  Serial.print(F("[SX1276] Initializing ... "));
  int state = radio.beginFSK(144.39, 1.2);
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }
}

void loop() {
  Serial.print(F("[SX1276] Waiting for incoming transmission ... "));

  String str;
  int state = radio.receive(str);

  if (state == RADIOLIB_ERR_NONE) {
    // packet was successfully received
    Serial.println(F("success!"));

    // print the data of the packet
    Serial.print(F("[SX1276] Data:\t\t\t"));
    Serial.println(str);

    // print the RSSI (Received Signal Strength Indicator)
    // of the last received packet
    Serial.print(F("[SX1276] RSSI:\t\t\t"));
    Serial.print(radio.getRSSI());
    Serial.println(F(" dBm"));

    // print the SNR (Signal-to-Noise Ratio)
    // of the last received packet
    Serial.print(F("[SX1276] SNR:\t\t\t"));
    Serial.print(radio.getSNR());
    Serial.println(F(" dB"));

    // print frequency error
    // of the last received packet
    Serial.print(F("[SX1276] Frequency error:\t"));
    Serial.print(radio.getFrequencyError());
    Serial.println(F(" Hz"));

  } else if (state == RADIOLIB_ERR_RX_TIMEOUT) {
    // timeout occurred while waiting for a packet
    Serial.println(F("timeout!"));

  } else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
    // packet was received, but is malformed
    Serial.println(F("CRC error!"));

  } else {
    // some other error occurred
    Serial.print(F("failed, code "));
    Serial.println(state);

  }
}

If it helps, I am using a TTGO T-Beam (V1.1).

sebastian-king avatar Sep 12 '22 22:09 sebastian-king

At the moment, RadioLib does not support receiving APRS and most other HAM modes like RTTY. This is because even transmitting it is achieved by a massive hack (basically by using the transceiver to only send a carrier and directly modulating that).

I recently added Morse code reception, and the same mechanism used for that might be usable to receive AFSK signal (and so APRS). Basically by putting the module to a special mode in which it outputs demodulated data on one of its pins, and so AFSK tones become a square wave signal. However, it's a big question whether the processing of the AFSK signal can be made reliable enough. And even bigger question is - is it worth it? I can easily imagine a use case for APRS transmissions using RadioLib - in microcontroller-based trackers. However, less so for receiving - why build microcontroller-based repeaters or IGates?

jgromes avatar Sep 13 '22 08:09 jgromes

@sebastian-king I did some experiments, and it looks like could be possible using the direct mode reception. In this mode, the radio outputs the demodulated data as digital signals on two of its pins (one is data, the other is clock). With that, the AFSK audio signal becomes digital waveform. The high and low tones are clearly visible as 1200/2200 Hz. This signal can then be sampled by using the clock signal as a source for interrupt to read the data line.

The result is an array of bits representing the AFSK signal, like this:

01010011001100110011001100110101010101010101010101010101010011010101010101010101010101010011000101010101010101010101010011010101

01010 is a 2200 Hz tone, while 00110011 is a 1200 Hz tone. In the above, there's the encoded AX.25 preamble starting with bytes 0x7E 0x01 0x01 etc.

However, there's going to be significant signal processing required - for example, due to slight timing errors, it's not as simple as e.g. taking every 4 bits to resolve the tones.

TL;DR - It's possible, but not straightforward and will likely require manual tuning.

jgromes avatar Sep 18 '22 15:09 jgromes