Implement precision timing recovery functions: - _refine_symbol_boundary(): Scans at 1/32-symbol resolution to find exact chirp boundary by maximizing dechirped SNR - _find_sfd_boundary(): FFT-based correlation with downchirp template to find exact data start position Bug fixes: - Fix _is_downchirp() false positives by comparing both correlations - Fix _estimate_cfo() to return values in [0, N) range The improved sync_from_samples() now produces bins identical to the reference lora_decode_gpu decoder.
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
Description
Languages
Python
97.2%
CMake
2.8%