Skip to content

softlora

A pure-Python LoRa physical-layer (PHY) receiver that synchronizes, demodulates and decodes LoRa packets from IQ recordings and live streams. It implements two synchronization algorithms for carrier-frequency-offset (CFO) and symbol-timing-offset (STO) estimation and correction. Tested on synthetic data, over-the-air captures and satellite recordings.

Satellite LoRa packet

Real over-the-air LoRa packet (IE6ISP 004 capture, t≈39.39 s)

A real over-the-air LoRa packet from the IE6ISP 004 capture (SF8, 62.5 kHz bandwidth, recorded at 2 MHz), zoomed to the packet span. Top: raw spectrogram — the rising preamble upchirps, the 2.25 SFD downchirps, then the header + payload. Bottom: dechirped against the preamble downchirp, so the preamble/network-id upchirps collapse to flat lines (bins 0/8/16).

IE6ISP ground station

The ground station that recorded the capture (IE6ISP).

Features

  • Preamble detection in complex baseband IQ recordings
  • CFO & STO estimation and compensation
  • Demodulation of LoRa symbols from corrected signals
  • Packet decoding with explicit headers (standard LoRa) and implicit headers (no header, forced parameters)
  • Streaming decode — plug in your SDR, GNU Radio or a large IQ file and decode it seamlessly, with bounded memory
  • CRC-16 verification for payload integrity
  • Chase (soft-decision) decoding

Quick example

from softlora import LoRaDecoder

decoder = LoRaDecoder(sf=10, bw=125_000, fs=125_000, fc=437e6)
packets = decoder.decode_file("recording.wav")

for p in packets:
    print(p.payload_text, "CRC:", p.crc_valid, "SNR:", p.snr_est)

See Quick start for file, in-memory, streaming and implicit-header examples.

Documentation layout