LoRa Chipset Features - RSSI, SNR, and Frequency Offset

By Jon Adams N7UV

Did you know that a LoRa receiver, in addition to doing all the cool LoRa stuff, natively provides received signal strength indication (RSSI) and received signal to noise ratio (SNR)? And signal frequency offset in hertz? For every packet? 

For a few US$ product, it's amazing the features and performance that are built into a LoRa chip. Most RF chips for the past quarter century have featured received-signal estimators and LoRa devices are no different.  

A LoRa transceiver handles an entire APRS packet as just a payload to be transmitted encapsulated in a LoRa frame. All forward error correction, LoRa headers and preambles, LoRa CRCs, are all managed between two LoRa transceivers, requiring no input from host processors at either end. All a host processor at the originating end has to is stuff a pre-built APRS packet (in TNC2 plain-text) into the LoRa frame buffer and hit send. At the receiving LoRa radio, the frame is decoded, any errors corrected, everything taken care of, and then an interrupt to the receiving host processor alerting it to a new received frame. There is NO need for AX.25, HDLC, or any other weird bit manipulating legacy functions to occur. 

For a typical LoRa transceiver, all three parameters seem to be stable. A Heltec V3 will likely have different numbers than a LilyGO T-Beam, but between like devices the receiver parameters are reproducible. All the received signal parameters are calculated over the entire packet consisting of a few dozen to several hundred (depends on BW and SF settings, and number of bytes in the packet) individual symbols. The reported numbers appear to be an average over all symbols. 

RSSI is great for comparing the received signal strength to what may be modeled from the transmitter at the far end to the receiver that reports the RSSI, taking into account transmitter output power, antenna directivity, feedline losses, filtering losses, Fresnel zone interference, free space path loss, and receiver sensitivity and noise figure. Significant changes in RSSI between two stations can provide insight to atmospheric effects like ducting, diurnal variations in path, etc., or warn of hardware issues at one end or the other.

SNR gives a solid idea of the "cleanliness" of the channel - for LoRa, an SNR greater than zero dB is good. LoRa SNRs rarely exceed +12 dB, and I think their estimator algorithm maxes out around +13 dB. At BW125 SF7, the lowest useable SNR is on the order of -10 to -12 dB, for greater SF values (each step a factor of 2 slower on the RF channel) it works to below -20 dB. LoRa is extremely resistant to static RF carriers, typical RF waveforms used for signaling, and noise bursts. It's even fairly good at rejecting another LoRa waveform at different BW and SF settings on the same channel. 

The frequency offset (FO) in Hz is interesting - it's consistent over time from unit to unit. A LoRa symbol needs good frequency stability, and every LoRa chip after the 1st generation SX127x family supports an in-package temperature-controlled crystal oscillator to ensure stable performance. The FO appears to contribute to the SNR measurement. The FO may be able to act as a signature for an individual transmitter. Could be good for T-hunting! 

For a typical LoRa APRS network that is RF-centric (i.e., RF hops are expected for a given packet) the digipeaters on the hop path each collect and then discard the received packet's RSSI, SNR, and FO. There is no way right now to get that data to someone on the ground, except by turning that digi into an iGate. That wouldn't work for many of our AZ network's remote site digipeaters where there's no Internet. Wouldn't it be cool to instead append that information to the end of the packet, outside the protected packet CRC16? That way, a digi operator could know directly how well their digi hears other stations without having to camp at the digipeater site! 

For now, let's imagine a crude approximation so that the 3 receiver values each fit into a single base-91 character. RSSI can go from -140 to nearly 0 dBm, but that base 91 value will represent only -140 dBm to -49 dBm. Any more than -49 dBm is irrelevant. For SNR, we can do -20 to +13 in 0.5 dB steps, that's 66 values, which fits in a single base-91 character. And finally, for FO we'll assume +/ 1kHz variation, so 90 values can encode FO to a precision of about 22 Hz. All the important receiver telemetry is now encoded as 3 base-91 characters. Since an average APRS packet is only 50-70 bytes long, and the LoRa frame buffer can handle over 250 payload bytes, these additional pieces of information don't add much overhead, but do reveal a whole new level of network connectivity. This is only a quick and dirty experiment to get that network performance data back to a ground-based observer!  

Here's an example.

UT_A intends to transmit a packet with a FROM_CALL = UT_A, a TO_CALL = APRSnn, and a PATH = WIDE1-1,WIDE2-2. The static parts of the packet are the FROM_CALL, TO_CALL, and payload (blah).  UT_A calculates a CRC16 based upon those static elements, and hands that whole packet as a payload to the LoRa transmitter, which then transmits the packet over the air. 

UT_A>APRSnn,WIDE1-1,WIDE2-2:blah. 

The PATH is going to be modified during the routing, since each digi will replace a PATH element with its call. Every station that hears this packet will make a note of those portions of the packet including the CRC16, put that into a de-duplication memory where it will be available for, say, 30 seconds.  

DG_B hears UT_A's transmitted packet. It receives it successfully, and notes RSSI_DGA, SNR_DGA, and FO_DGA (for now, let's assume that each of these is encoded as a single base91 character). It checks its own de-duplication memory for the protected parts of the packet, and if it finds that it hasn't transmitted this packet in the past 30 seconds, it goes ahead and modifies the PATH from WIDE1-1,WIDE2-2 to DG_A*,WIDE2-2. It appends to the end of the payload a new field delimited with curly braces and containing RSSI_DGA, SNR_DGA, FO_DGA. The new packet looks like this 

UT_A>APRSnn,DG_A*,WIDE2-2:blah{RSSI_DGA,SNR_DGA,FO_DGA} 

The original TO_CALL, FROM_CALL, payload, and CRC16 remain the same. 

DG_B receives the packet transmitted by DG_A. DG_B records the RSSI, SNR, and FO. It modifies the PATH statement and appends the added receiver information to the block at the end. Since it sees that the {} field is already there, it just adds RSSI_DGB, SNR_DGB, and FO_DGB to the field. It transmits the following packet: 

UT_A>APRSnn,DG_A,DG_B*,WIDE2-1:blah{RSSI_DGA,SNR_DGA,FO_DGA,RSSI_DGB,SNR_DGB,FO_DGB}

Digipeater DG_C receives this packet, as does DG_A. DG_A checks its de-duplication memory, and finds that it transmitted the packet less than 30 seconds ago, so it ignores the packet. However, DG_C checks its de-duplication memory, finds it hasn't transmitted it before, and sees there's one remaining hop requested, so it will transmit the following frame: 

UT_A>APRSnn,DG_A,DG_B,DG_C*:blah{RSSI_DGA,SNR_DGA,FO_DGA,RSSI_DGB,SNR_DGB,FO_DGB,RSSI_DGC,SNR_DGC,FO_DGC}


UT_B now hears the packet transmitted by DG_C. It also sees the frame has been digipeated 3 times (PATH shows DG_A,DG_B,DG_C*) and it sees the {} field with 9 values at the end of the LoRa payload. UT_B extracts the FROM_CALL, TO_CALL, payload "blah", and the nine values held in the {} field. UT_B assigns the first 3 values to DG_A, the second 3 to DG_B, and the last 3 to DG_C. Not only does UT_B get the packet, it also gets solid insight into what's going on at each digipeater! 

Importantly, these frames carrying extra data can flow transparently to aprs-is, so there's no impact there. All communications to/from aprs-is is required to be in TNC2 format. The added characters at the end are just more comment field stuff. 

An app that's not aware of what the {ABCDEF} field means sees it as just text. The packet can be digipeated out on to traditional 2 m APRS without issue. 

Using a tool like Chuck N0NHJ's Node-RED project, the digipeater or network operator/maintainer can collect over time an excellent map of the actual connectivity between nodes. That will be handy when it comes time to attempt dynamic routing. 

And we haven't even started to touch on next-generation compression techniques yet! 

Pretty cool!

--Cheers and 73 - Jon N7UV