Skip to content

Quick start

The LoRaDecoder is constructed once with everything needed to decode a packet, then used in one of three ways: decode a file, decode an in-memory buffer, or feed a live stream.

Decode a recording

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)

decode_file streams the file from disk in chunks, so multi-GB recordings decode with bounded memory. A recording may hold several packets — the result is always a list[Packet].

Decode an in-memory buffer

from softlora import LoRaDecoder, load_iq

iq = load_iq("recording.cfile")          # or np.fromfile / your own source
packets = LoRaDecoder(sf=9, bw=125_000, fs=250_000, fc=402e6).decode_iq(iq)

Live streaming decode

Feed IQ chunks from an SDR or a GNU Radio pipe; packets are returned as soon as they are fully received, even when a packet spans chunk boundaries:

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

while True:
    chunk = read_iq_from_sdr(8192)       # complex baseband samples at fs
    if not chunk:
        break
    for p in decoder.decode_stream(chunk):
        print(f"{p.timestamp_sec:.0f} {p.payload_text!r} {p.crc_valid}")

for p in decoder.flush():                # decode the remaining tail
    print(p)

Call flush() at end-of-transmission and reset() to start a new transmission.

Implicit headers

When packets carry no header, tell the decoder the forced parameters (payload_len is required):

decoder = LoRaDecoder(sf=10, bw=125_000, fs=125_000, fc=437e6,
                      implicit_header=True, payload_len=19,
                      crc_enabled=True, code_rate=1)

Chase decoding

Chase is a decoder mode (DecoderSettings(decode_mode='chase')): it is the payload decoder from the start, working from the per-symbol soft spectra (max-likelihood symbols first, then symbol/bit flips for the least-reliable positions):

from softlora import DecoderSettings

decoder = LoRaDecoder(sf=10, bw=125_000, fs=125_000, fc=437e6,
                      settings=DecoderSettings(decode_mode='chase'))
packets = decoder.decode_file("recording.wav")
for p in packets:
    print(p)

What success means

Packet.ok tells you a packet was found and demodulated; the data-integrity verdict is Packet.crc_valid. Filter on p.crc_valid is True for guaranteed-good payloads. See Packet semantics.