gr-rylr998/README.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

4.4 KiB
Raw Blame History

gr-rylr998

GNU Radio Out-of-Tree Module for RYLR998 LoRa Modems

Overview

gr-rylr998 provides complete TX/RX blocks for RYLR998 LoRa modems, implementing the full PHY layer compatible with the Semtech LoRa standard.

Key Features

  • Complete TX/RX chains for LoRa modulation/demodulation
  • NETWORKID support - Extracts and encodes RYLR998 NETWORKID from sync word
  • gr-lora_sdr compatible - Uses the same signal processing chain
  • Python-only - No C++ compilation required (GNU Radio 3.10+)
  • Well-documented - Educational comments throughout

Installation

From Source (Development)

cd gr-rylr998
pip install -e .

With CMake (GNU Radio Integration)

mkdir build && cd build
cmake ..
make
sudo make install

Quick Start

Python API

from rylr998 import PHYEncode, PHYDecode, FrameGen, FrameSync
import numpy as np

# === Transmit ===
encoder = PHYEncode(sf=9, cr=1, has_crc=True)
frame_gen = FrameGen(sf=9, networkid=18)

payload = b"Hello, LoRa!"
bins = encoder.encode(payload)
iq = frame_gen.generate_frame(bins)

# === Receive (loopback) ===
sync = FrameSync(sf=9)
result = sync.sync_from_samples(iq)

decoder = PHYDecode(sf=9)
# For loopback: use_grlora_gray=False, soft_decoding=True
# For real SDR captures: use defaults (True, False)
frame = decoder.decode(
    result.data_symbols,
    cfo_bin=int(result.cfo_bin),
    use_grlora_gray=False,
    soft_decoding=True
)

print(f"NETWORKID: {result.networkid}")
print(f"Payload: {frame.payload}")
print(f"CRC OK: {frame.crc_ok}")

Command Line

# Run loopback test
python examples/loopback_test.py

# Test all SF/CR combinations
python examples/loopback_test.py --all

# Generate TX frame
python examples/bladerf_tx.py --payload "Test" --output frame.raw

# Decode from file
python examples/bladerf_rx.py --input capture.raw --verbose

Block Inventory

Block Type Description
css_demod Demod FFT-based CSS demodulator
css_mod Mod Chirp generator
frame_sync Sync Preamble + NETWORKID detection
frame_gen Framing Preamble + sync word + SFD
phy_decode PHY Gray → deinterleave → Hamming → dewhiten
phy_encode PHY Whiten → Hamming → interleave → Gray
rylr998_rx Hier Complete RX chain
rylr998_tx Hier Complete TX chain

NETWORKID Mapping

The RYLR998 NETWORKID (0-255) maps directly to the LoRa sync word:

sync_bin_1 = (NETWORKID >> 4) * 8   # High nibble × 8
sync_bin_2 = (NETWORKID & 0x0F) * 8 # Low nibble × 8
NETWORKID Sync Bins Use
18 (0x12) [8, 16] Private networks
52 (0x34) [24, 32] LoRaWAN public

Directory Structure

gr-rylr998/
├── python/rylr998/     # Python module
│   ├── __init__.py
│   ├── networkid.py    # NETWORKID utilities
│   ├── css_demod.py    # CSS demodulator
│   ├── css_mod.py      # CSS modulator
│   ├── phy_decode.py   # PHY RX chain
│   ├── phy_encode.py   # PHY TX chain
│   ├── frame_sync.py   # Frame synchronization
│   └── frame_gen.py    # Frame generation
├── grc/                # GRC block definitions (.yml)
├── examples/           # Example scripts and flowgraphs
│   ├── loopback_test.py
│   ├── bladerf_rx.py
│   ├── bladerf_tx.py
│   ├── rylr998_loopback.grc
│   ├── rylr998_bladerf_rx.grc
│   └── rylr998_bladerf_tx.grc
├── docs/               # Documentation
│   ├── BLOCK_REFERENCE.md
│   ├── NETWORKID_MAPPING.md
│   └── GRC_FLOWGRAPHS.md
├── CMakeLists.txt      # CMake build
└── pyproject.toml      # Python packaging

Documentation

Document Description
BLOCK_REFERENCE.md All blocks, parameters, troubleshooting
NETWORKID_MAPPING.md RYLR998 NETWORKID ↔ sync word mapping
GRC_FLOWGRAPHS.md GNU Radio Companion usage guide

Dependencies

  • Python 3.10+
  • NumPy
  • GNU Radio 3.10+ (optional, for GRC integration)

References

License

GPL-3.0-or-later