gr-rylr998/docs/NETWORKID_MAPPING.md
Ryan Malloy c839d225a8 Initial release: complete LoRa TX/RX for RYLR998 modems
GNU Radio Out-of-Tree module providing:
- Complete TX chain: PHYEncode → FrameGen → CSSMod
- Complete RX chain: CSSDemod → FrameSync → PHYDecode
- NETWORKID extraction/encoding (0-255 range)
- All SF (7-12) and CR (4/5-4/8) combinations
- Loopback tested with 24/24 configurations passing

Key features:
- Fractional SFD (2.25 downchirp) handling
- Gray encode/decode with proper inverse operations
- gr-lora_sdr compatible decode modes
- GRC block definitions and example flowgraphs
- Comprehensive documentation

Discovered RYLR998 sync word mapping:
  sync_bin_1 = (NETWORKID >> 4) * 8
  sync_bin_2 = (NETWORKID & 0x0F) * 8
2026-02-05 13:38:07 -07:00

2.8 KiB
Raw Permalink Blame History

RYLR998 NETWORKID ↔ LoRa Sync Word Mapping

Discovery

Through SDR captures and analysis, we discovered that the RYLR998 module's NETWORKID (0-255) maps directly to the LoRa sync word byte using a simple nibble-to-bin transformation.

The Mapping

Sync Word Byte: 0xHH where H = high nibble, L = low nibble

CSS Symbol 1 (sync_delta_1) = H × 8  = (NID >> 4) × 8
CSS Symbol 2 (sync_delta_2) = L × 8  = (NID & 0x0F) × 8

The factor of 8 is a fixed constant used by RYLR998 (verified through SDR captures). This differs from standard LoRa which would use N/16 scaling.

Verification

NETWORKID Hex High Nibble Low Nibble Sync Bin 1 Sync Bin 2 Verified
3 0x03 0 3 0 24
5 0x05 0 5 0 40
17 0x11 1 1 8 8
18 0x12 1 2 8 16
52 0x34 3 4 24 32 (LoRaWAN)

Standard LoRa Sync Words

Name Sync Byte NETWORKID Sync Bins
Private 0x12 18 [8, 16]
LoRaWAN Public 0x34 52 [24, 32]

Code Implementation

Python

def networkid_to_sync_word(nid: int) -> tuple[int, int]:
    """Convert NETWORKID to sync word symbol deltas."""
    high_nibble = (nid >> 4) & 0x0F
    low_nibble = nid & 0x0F
    return (high_nibble * 8, low_nibble * 8)

def sync_word_to_networkid(deltas: tuple[int, int]) -> int:
    """Convert sync word deltas back to NETWORKID."""
    high_nibble = deltas[0] // 8
    low_nibble = deltas[1] // 8
    return (high_nibble << 4) | low_nibble

Full Range Table

NID=0   (0x00) → sync=[  0,   0]
NID=1   (0x01) → sync=[  0,   8]
NID=2   (0x02) → sync=[  0,  16]
...
NID=16  (0x10) → sync=[  8,   0]
NID=17  (0x11) → sync=[  8,   8]
NID=18  (0x12) → sync=[  8,  16]
...
NID=255 (0xFF) → sync=[120, 120]

Why This Matters

  1. Interoperability: You can decode RYLR998 traffic with standard LoRa hardware by knowing the sync word
  2. Filtering: Filter frames by NETWORKID in software without full decode
  3. Testing: Generate RYLR998-compatible frames from any SDR

SDR Capture Notes

When capturing RYLR998 frames:

  1. The preamble consists of 8+ upchirps at bin 0
  2. Sync word is 2 upchirps with bins = nibble × (N/16)
  3. SFD is 2.25 downchirps
  4. Data follows with the encoded payload

The sync word symbols are measured relative to the preamble bin (which represents the carrier frequency offset).