Mechanism
LoRa modulates data with Chirp Spread Spectrum (CSS): a sinusoid sweeps linearly across the channel bandwidth, and the symbol value is the chirp’s cyclic start frequency offset, for spreading factor SF, a symbol carries SF bits, so there are 2^SF distinguishable start offsets [marquet2020] [tapparel2020]. The regional plans use 125/250/500 kHz bandwidths and SF7 to SF12, trading data rate for sensitivity (higher SF spreads the same energy over a longer symbol, reaching lower SNR) [rp002].
Demodulation is de-chirping: the receiver multiplies the incoming chirp by a locally generated reference down-chirp, which collapses the swept tone into a single constant-frequency tone, then takes an FFT, the symbol is read off as the index of the FFT bin holding the energy peak [marquet2020] [xu2022]. This is the standard, well-understood LoRa receiver and is what every open-source SDR demodulator implements.
Doing it reliably on real captures is the hard part. The energy peak is degraded by sampling time offset (STO) and carrier frequency offset (CFO) between the unsynchronised transmitter and the SDR, and at low SNR the peak can be distorted or buried in noise so the FFT bin is read wrong [xu2022]. A complete receiver therefore needs frame detection (correlating against the up-chirp preamble), then STO and CFO estimation and correction before demodulation; the gr-lora_sdr design implements exactly this chain, preamble sync, CFO/STO compensation, demodulation, Gray-demap, deinterleave, Hamming decode, dewhiten, CRC, and is built to keep decoding at very low SNR [tapparel2020] [grlorasdr] [tapparel2024].
Beyond the per-symbol decision, the choice between hard-decision (emit the single nearest bin) and soft-decision demodulation (carry the per-bin likelihoods forward into the Hamming/FEC stage) changes how many frames survive at the edge of the link budget: soft information lets the decoder recover frames a nearest-bin decoder loses, so hard-decision yields are a lower bound on what is actually on the air [marquet2020] [xu2022]. This is a capture-completeness concern for the auditor, not a vulnerability in the target, hence info.
This is a passive PHY check: it recovers symbols and frame bits, but the LoRaWAN application payload stays AES-128 encrypted until the keys are assessed at the crypto layer. There is no off-the-shelf hardware demodulator for LoRa CSS in the standard kit; the de-chirp happens in software on the SDR, which is why capturing and framing live together at the next (link) layer (RFSAM-RES-07).
Procedure
Work only on signals you are authorised to receive and analyse. Receiving and demodulating LoRa is passive, but capturing third-party traffic may still be regulated in your jurisdiction, have explicit permission for the deployment under test.
-
Confirm the band and channel plan (from RFSAM-LORA-SP / RFSAM-LORA-IG): the regional band sets the centre frequency, and the device profile sets bandwidth and spreading factor. If you only know the band, plan to scan SF7 to SF12 [rp002].
-
Capture I/Q over the channel. Tune the SDR to the channel centre with sample rate ≥ the LoRa bandwidth. Example with an RTL-SDR for EU868 channel 0:
rtl_sdr -f 868100000 -s 1000000 -g 40 -n 6000000 lora_eu868_ch0.iqProduces a raw
uint8I/Q file.-s 1000000(1 Msps) comfortably covers 125 kHz; increase the sample rate to cover 250/500 kHz channels. Expect a file growing at sample_rate × 2 bytes/s. -
De-chirp and demodulate in software. Run the captured I/Q through a gr-lora_sdr receiver flowgraph (or the bundled examples), set to the channel’s SF and BW:
git clone https://github.com/tapparelj/gr-lora_sdr # build/install per its README, then run an RX flowgraph (GRC or Python) # configured: center_freq=868.1e6, bw=125000, sf=7, samp_rate to match the captureExpected output: for each detected frame the receiver prints the demodulated/decoded payload bytes (after Gray-demap, deinterleave, Hamming decode, dewhiten, CRC). A frame that passes CRC is a clean PHY demodulation; CRC failures or “no frame detected” mean the SF/BW guess, sync, or SNR is wrong [grlorasdr] [tapparel2020].
-
If you do not yet know SF/BW, or want the whole sub-band at once, stream wideband I/Q and let the decoder sweep SF7 to SF12 across every bandwidth:
git clone https://github.com/persistentcache/Lora-Wideband-Decoder # run per its README against a bladeRF/USRP feed; decoded packets, node IDs # and a live waterfall appear in the local Flask web UIExpected output: decoded LoRa/LoRaWAN/Meshtastic packets surfacing in the web UI as they are demodulated across the band, useful when the exact channel or SF is unknown.
-
Read the result as a yield. Note how many frames demodulate cleanly (CRC-OK) versus how many the waterfall shows but the decoder drops. A non-trivial drop rate at the edge of range is the soft-vs-hard-decision gap, and the cue to confirm a soft-decision-capable demodulator before treating the capture as complete [marquet2020] [xu2022]. Export CRC-OK frames as LoRaTap PCAP for the link layer (RFSAM-LORA-LL, RFSAM-RES-07).
-
Single-board alternative without an SDR de-chirp flow: the CatSniffer’s SX1262 can capture and decode LoRa into Wireshark via
catniponce region/SF/BW are set, a hardware-radio capture path to cross-check the SDR demodulation against.
Field case
Two bench sessions in the Electronic Cats lab, both receive-only on the SDR side, against the lab’s own transmitters at 1 m line of sight. Receiver: RTL-SDR with its stock telescopic antenna. Each capture is 30 s at 1 MS/s.
Session 1: SF7, 125 kHz, 916.0 MHz. Transmitter: the laboratory FlatSat.
rtl_sdr -f 916000000 -s 1000000 -g 40 -n 30000000
Demodulated with the gr-lora_sdr receiver flowgraph set to 916 MHz, 125 kHz, SF7, once with soft_decoding on and once off. The operator recorded 12 frames with a valid CRC out of 12 in both modes, so on this capture the two modes gave the same yield. The decoder logs of this session were not kept.
Session 2: SF11, 250 kHz, 915.0 MHz. Transmitter: an STM32F446 with a DX-LR30 transceiver, CR 4/5, sync word 0x34, one packet every 5 s, numbered payloads.
rtl_sdr -f 915000000 -s 1000000 -g 40 -n 30000000
The capture holds 5 frames (packets #314 to #318). Both modes found all 5 and validated all 5 headers. The payload CRC is where they differ:
| Mode | Headers valid | Payload CRC valid |
|---|---|---|
| Soft-decision | 5 of 5 | 3 of 5 |
| Hard-decision | 5 of 5 | 1 of 5 |
The counts are from the decoder output of each run. Packet #315 is the clearest single example: soft-decision returned it with a valid CRC, hard-decision returned the same packet with one payload character missing and an invalid CRC.
How far this goes. The direction agrees with the literature: soft-decision recovers frames that hard-decision drops [marquet2020] [xu2022]. The size of the gap here comes from 5 frames in one capture, so it is a single observation and not a yield figure to generalise. Signal-to-noise ratio was not measured in either session.
For a passive survey aiming to enumerate a network, every frame the demodulator drops is potentially a device or a join you never see, which is why this PHY-completeness check precedes the link-layer enumeration rather than being assumed.
Remediation
This control verifies an auditor capability, so most of the action is auditor-side; the developer/integrator/operator guidance is about what the demodulability of the PHY implies for the deployment.
-
Auditor. Use a soft-decision-capable LoRa demodulator (gr-lora_sdr class) for assessment, and treat hard-decision-only yields as a lower bound on what is on the air, not a complete enumeration [marquet2020] [xu2022]. Confirm the SF/BW set actually in use (devices may use multiple data rates) before declaring a capture complete; sweep SF7 to SF12 if the profile is unknown.
-
Developer / integrator. The CSS PHY is, by design, open and demodulable, its protection is not obscurity but the AES-128 crypto layer (FRMPayload confidentiality, MIC integrity). Do not treat “LoRa is hard to demodulate” as a security property: an SDR plus open-source software recovers the frames. Ensure all sensitive data and commands rely on the LoRaWAN crypto, not on the difficulty of demodulation, and assess that crypto under the CR-layer controls.
-
Operator. Assume any LoRa transmission in range is demodulable to frames by a passive listener; plan the threat model around the link-layer metadata that travels in clear (DevAddr, and the join-request DevEUI/JoinEUI/DevNonce) being observable, and around the encrypted payload’s confidentiality resting entirely on key management. PHY demodulation itself exposes no payload, but it is the prerequisite for every analysis above it.