Skip to content

IQ file formats

The receiver works on complex baseband IQ. Two helpers load recordings:

  • load_iq — loads the whole file into memory as complex64.
  • iter_iq_chunks — streams the same formats as complex64 chunks without loading the file; used internally by decode_file.

Both auto-detect the format.

Raw interleaved IQ

Extensions: .cfile, .cf32, .dat, .bin, .iq.

Samples are stored as interleaved I/Q pairs:

Encoding Byte layout Notes
float32 I Q I Q ... GNU Radio File Sink default (.cfile)
int16 I Q I Q ... common SDR format

Detection: the first ~1000 samples are read as float32; if their mean power is effectively zero, the file is reinterpreted as int16 (and scaled to [-1, 1)).

fs is not stored in the file — pass it to the LoRaDecoder constructor.

WAV

Extensions: .wav, .wave.

Stereo WAVs are treated as IQ: channel 0 = I, channel 1 = Q. Supported encodings:

  • int16 PCM (most common)
  • float32 IEEE

Mono WAVs raise an error (no I/Q channels). For WAV files the sample rate is stored in the header — read it with sample_rate:

from softlora import LoRaDecoder, sample_rate

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

Sample-rate and bandwidth

LoRaDecoder needs:

  • fs — the rate of your IQ (or WAV header rate).
  • bw — the LoRa channel bandwidth. The decoder resamples fs → bw so that each LoRa symbol is exactly N = 2**sf samples.

The three are linked: a standard LoRa uplink is sf=7..12 at bw=125 kHz, often captured at fs = 250 kHz (factor-2 oversampling).

Persistence

save_packets writes each packet's raw payload to packet_XXXX.bin, appends one machine-readable metadata.jsonl line with the full Packet.to_dict serialization, and renders a human-readable packets.txt summary (each packet's Packet.__str__ detail table) — a convenient ground-station log.