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.