One LoRa symbol, decoded two ways. Real LoRa chirps wrap from the top of the band back to the bottom partway through, and radios do not keep the carrier phase continuous across that frequency jump. Standard decoding adds the two halves of the symbol together and can lose energy to the phase jump; LoRaTrimmer trims each candidate symbol at its own jump point and adds the two halves' powers instead.
Paper: MobiCom 2024 · decoders ported from the reference code (BW 125 kHz, sampled at 1 MHz) · everything runs in your browser.
Spectrograms show frequency over time. Click a spectrogram to choose the short window shown in the zoomed panel; the raw waveform is too fast to read at full length.
A chirp starting at the symbol's frequency, jumping back to −62.5 kHz at t₀.
The same symbol at the chosen SNR. Below about −10 dB it disappears into the noise.
In-phase part of 64 samples: clean (top) and received (bottom), each on its own scale.
Dechirp, take one long FFT, then fold the two copies of the peak together by adding their magnitudes.
Multiplying by a downchirp turns the symbol into two tones: one before the jump, one a full bandwidth lower after it.
Two peaks, at f₀ and f₀ − 125 kHz. Their relative phase is the radio's random phase jump.
|first half| + |second half| per symbol value; the highest wins. The FFT ran over the whole symbol, so each half also collects noise from the other.
Each candidate symbol value k has its own jump time t₀ = (1 − k/N)·T. LoRaTrimmer correlates the part before that time and the part after it separately (two triangular matrices, E1 and E2) and adds their powers, so the unknown phase jump can't cancel anything.
Drag to inspect any candidate. Blue = E1 window (before its jump), orange = E2 window (after).
|E1·x|² and |E2·x|² for every candidate, stacked. At the true symbol both halves line up with the signal.
|E1·x|² + |E2·x|²; the highest wins. This is the optimal rule at low SNR when the phase jump is unknown (paper, Sec. 3.2).
One symbol can go either way by luck. This runs fresh symbols (random value, random phase jump, fresh noise) through both decoders at each SNR, in a background thread.