Skip to main content

SoftLoRa

Google Summer of Code 2026

Decode LoRa packets from radio recordings and live SDR streams, in pure Python.

Point it at an IQ recording and it finds the packets, corrects the frequency and timing errors introduced by the radio link, and gives you back the payload. No GNU Radio or C++ needed — just numpy and scipy.

It was built for satellite downlinks, where signals are weak and Doppler-shifted, and is tested on synthetic data, over-the-air captures and real satellite passes.

Developed for Google Summer of Code 2026 with LibreCube.

Install

pip install softlora

Python 3.9+. The decoder needs only numpy and scipy.

Receiving live off an SDR additionally needs the SoapySDR bindings, which are a system package rather than a pip one — see Receive from an SDR.

Decode a recording

from softlora import LoRaDecoder

decoder = LoRaDecoder(sf=10, bw=125_000, fs=125_000, fc=437e6)

for packet in decoder.decode_file("recording.wav"):
    print(packet.payload_text, packet.crc_valid)

The four arguments describe the signal you are decoding:

Argument Meaning
sf Spreading factor, 7–12
bw LoRa bandwidth in Hz
fs Sample rate of your recording in Hz
fc Center frequency in Hz

.wav, .cfile, .dat and .bin files are supported. A recording may hold several packets, so you always get a list back.

Decode a live stream

Feed IQ chunks as they arrive from an SDR. Packets are returned as soon as they are complete, even when one spans two chunks:

decoder = LoRaDecoder(sf=10, bw=125_000, fs=250_000, fc=437e6)

while receiving:
    for packet in decoder.decode_stream(read_iq_from_sdr(8192)):
        print(packet.payload_text)

for packet in decoder.flush():      # decode whatever is left in the buffer
    print(packet)

Receive from an SDR

Ready-to-run receivers for three common radios, in examples/:

python examples/sdr_live_rtl_sdr.py           # RTL-SDR
python examples/sdr_live_airspy_mini_sdr.py   # Airspy Mini
python examples/sdr_live_hackrf_sdr.py        # HackRF One
436.13125 MHz  sf=8  bw=125k  fs=3M /3 -> 1000k (8x)  gain=lna14/mix12/vga10  preamble=15

[16:03:26] t=  19.15s  snr=+11.2dB  cfo=     -9Hz
  len=11  cr=4/7  crc=off
  hex  68656c6c6f20776f726c64
  text 'hello world'

Each one tunes off-channel to dodge the radio's DC spike, decimates to a rate the decoder likes, and folds each packet's residual frequency error back into the software shift — so the receiver calibrates itself out of its own crystal error while it runs. --record keeps the IQ for offline replay.

These scripts need SoapySDR, which is not on PyPI:

sudo apt install soapysdr-tools python3-soapysdr
sudo apt install soapysdr-module-rtlsdr    # or -airspy, or -hackrf
python3 -m venv --system-site-packages .venv

Reading the result

Every decode path returns Packet objects. The fields you will usually want:

packet.payload_bytes   # the data
packet.payload_text    # the same data decoded as UTF-8
packet.crc_valid       # True when the payload passed its checksum
packet.snr_est         # signal-to-noise estimate in dB
packet.ok              # a packet was found and demodulated

ok and crc_valid answer different questions. ok=True, crc_valid=False means a packet arrived but was corrupted on the way. Filter on packet.crc_valid is True when you only want trustworthy payloads.

A LoRa packet

Each packet starts with a preamble of plain upchirps, then a sync word, then a 2.25-symbol start-of-frame delimiter (SFD) made of downchirps, and finally the header, payload and CRC. All four regions are visible below.

A real LoRa packet received from the Polytech Universe-3 (PU-3) satellite (SF8, 62.5 kHz bandwidth). The payload is truncated for display.

Going further

Topic Where
Runnable examples examples/
Packets without a header Quick start
Tuning the decoder (DecoderSettings) The decode pipeline
Rescuing weak packets with Chase decoding Chase guide
Live receive off RTL-SDR / Airspy / HackRF examples/README.md
Live SDR chain via GNU Radio examples/README.md
Doppler and carrier offset for satellites Ground-station use
Benchmarks across SF 7–12 Performance
How synchronization works Sync algorithm
Full API reference Documentation site

References

[1] M. Xhonneux, O. Afisiadis, D. Bol, and J. Louveaux, "A Low-Complexity LoRa Synchronization Algorithm Robust to Sampling Time Offsets," IEEE Internet of Things Journal, 2021. arXiv:1912.11344

[2] J. Tapparel, O. Afisiadis, P. Mayoraz, A. Balatsoukas-Stimming, and A. Burg, "An Open-Source LoRa Physical Layer Prototype on GNU Radio," SPAWC, 2020.

License

MIT — see LICENSE and CONTRIBUTORS.txt.

Metadata

Release files for softlora 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for softlora 0.4.0
File Size Uploaded Provenance Actions
softlora-0.4.0.tar.gz 97.2 kB Without provenance Details for softlora-0.4.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for softlora 0.4.0
File Interpreter ABI Platform Actions
softlora-0.4.0-py3-none-any.whl Python 3 none any Details for softlora-0.4.0-py3-none-any.whl

Total release size: 146.6 kB

Release files / softlora-0.4.0.tar.gz

Download URL softlora-0.4.0.tar.gz
Size 97.2 kB
Tags Source
SHA-256 checksum
How to use checksums
2cea6d9794bb4f6b1f1780c2532b07cc1ce50669ea487a8abd5700cd6e6da868
BLAKE2b-256 checksum
How to use checksums
10e8ac1d26ee6afc56d9b3c395af235a380f6a904612cee5ade6f15864069f48
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.11.16

Release files / softlora-0.4.0-py3-none-any.whl

Download URL softlora-0.4.0-py3-none-any.whl
Size 49.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0d4e3eb30540b93036e79f2ed487ffd05714ca176f111aa5505b808c12adda3a
BLAKE2b-256 checksum
How to use checksums
05c780198a229623b6a96cbcd936a782f384e8b626af8e91de34c97ba22120c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.11.16

Security for softlora 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page