Field Guide · algorithm

Also known as: LoRa whitening, data whitening, LoRa PN sequence

LoRa whitening XORs the payload with a pseudo-random (PN) sequence so the transmitted bits carry no long runs or repeating structure — a step that keeps a LoRa frame’s spectrum flat and its symbol stream well-conditioned for timing recovery.1 The sequence comes from an 8-bit linear-feedback shift register with polynomial 0xB8 and seed 0xFF. Because whitening is a bit-for-bit XOR, it is its own inverse: the same routine whitens on transmit and de-whitens on receive.2

LFSR (seed 0xFF, poly 0xB8) payload whitened bytes advance the LFSR 8 times per byte · XOR is self-inverse, so RX runs the identical routine
Each payload byte is XORed with one byte drawn from an 8-bit LFSR; the register advances eight steps per byte, and because XOR is self-inverse the same code de-whitens on receive.

How it works

For each payload byte, GopherTrunk XORs it with the current LFSR state, then clocks the register eight times — one shift per bit of the byte just consumed. Each clock shifts the register right and, when the bit shifted out was set, XORs the feedback polynomial back in:

const whitenSeed, whitenPoly = 0xFF, 0xB8

func Whiten(data []byte) {
    lfsr := uint8(whitenSeed)
    for i := range data {
        data[i] ^= lfsr
        for b := 0; b < 8; b++ {
            lsb := lfsr & 1
            lfsr >>= 1
            if lsb != 0 {
                lfsr ^= whitenPoly
            }
        }
    }
}

0xB8 corresponds to x⁸ + x⁶ + x⁵ + x⁴ + 1 in the reflected convention this shift direction uses. LoRa is a proprietary Semtech modulation, so the exact whitening sequence is reverse-engineered; GopherTrunk’s package notes that byte-for-byte agreement with Semtech silicon is a calibration step gated behind a captured golden packet. The sequence it produces is deterministic and self-consistent, which is what the package’s own modulator/​demodulator round-trip requires.

Whitening versus scrambling

Whitening and scrambling are the same operation — XOR against a PN sequence — put to two different ends, and the distinction is worth keeping straight:

  Whitening Scrambling (as a cipher)
Goal de-correlate bits (flat spectrum, no long runs) obscure content
Sequence fixed, public polynomial + seed secret / keyed
Reversibility trivial — same public LFSR needs the key
Security none the point

LoRa whitening is purely the first kind: a fixed, public LFSR whose only job is to break up structure in the data so the physical layer behaves well. It provides no confidentiality — that is the job of the LoRaWAN AES layer, not the PHY.

The payload CRC

Integrity is checked separately, after de-whitening, by a CRC-16 with polynomial 0x1021 and initial value 0x0000 computed over the payload bytes (excluding the trailing CRC field). This is the XMODEM-style member of the CRC-16/CCITT family — same polynomial as many of GopherTrunk’s other framers, differing only in its init and reflection settings. As with the whitening sequence, byte-order and seed compatibility with Semtech silicon is validated against a golden packet rather than assumed.

Sources

  1. LoRa — Wikipedia, on the LoRa CSS physical layer and its frame processing. 

  2. Scrambler — Wikipedia, on additive (synchronous) scrambling and data whitening with an LFSR. 

See also