#!/usr/bin/env python3 """BladeRF RYLR998 transmitter example. Transmits LoRa frames compatible with RYLR998 modules via BladeRF SDR. Usage: python3 bladerf_tx.py --freq 915e6 --networkid 18 --payload "Hello" python3 bladerf_tx.py --output frame.raw --sf 9 --payload "Test" """ import sys import argparse import numpy as np # Add parent directory for imports sys.path.insert(0, '../python') from rylr998 import PHYEncode, FrameGen def generate_frame(payload: bytes, sf: int = 9, cr: int = 1, networkid: int = 18, preamble_len: int = 8, verbose: bool = False) -> np.ndarray: """Generate a LoRa frame as IQ samples. Args: payload: Payload bytes sf: Spreading factor cr: Coding rate (1-4) networkid: RYLR998 NETWORKID preamble_len: Number of preamble symbols verbose: Print debug info Returns: Complex64 IQ samples """ fs = 125e3 # Sample rate = bandwidth N = 1 << sf if verbose: print(f"Generating frame: SF{sf} CR4/{cr+4} NETWORKID={networkid}") print(f" Payload ({len(payload)}B): {payload!r}") # PHY encode encoder = PHYEncode(sf=sf, cr=cr, has_crc=True) data_bins = encoder.encode(payload) if verbose: print(f" Encoded: {len(data_bins)} symbols") # Frame generate frame_gen = FrameGen(sf=sf, sample_rate=fs, networkid=networkid, preamble_len=preamble_len) iq = frame_gen.generate_frame(data_bins) if verbose: duration_ms = len(iq) / fs * 1000 print(f" Generated: {len(iq)} samples ({duration_ms:.2f} ms)") return iq def main(): parser = argparse.ArgumentParser( description="BladeRF RYLR998 LoRa transmitter") parser.add_argument("--freq", type=float, default=915e6, help="Center frequency (Hz)") parser.add_argument("--sf", type=int, default=9, help="Spreading factor (7-12)") parser.add_argument("--cr", type=int, default=1, help="Coding rate (1-4)") parser.add_argument("--networkid", type=int, default=18, help="NETWORKID (0-255)") parser.add_argument("--preamble", type=int, default=8, help="Preamble length (symbols)") parser.add_argument("--payload", type=str, default="Hello, LoRa!", help="Payload string") parser.add_argument("--output", "-o", type=str, default=None, help="Output file (complex64 raw IQ)") parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output") args = parser.parse_args() # Generate frame iq = generate_frame( args.payload.encode(), sf=args.sf, cr=args.cr, networkid=args.networkid, preamble_len=args.preamble, verbose=args.verbose ) if args.output: # Save to file iq.tofile(args.output) print(f"Wrote {len(iq)} samples to {args.output}") print(f"\nTo transmit with bladeRF-cli:") print(f" bladeRF-cli -e 'set frequency tx {args.freq/1e6:.1f}M; " f"set samplerate 125k; set bandwidth 125k; " f"tx config file={args.output} format=bin; tx start; tx wait'") else: # Live transmit (requires bladeRF Python bindings) print("Live transmit mode requires bladeRF Python bindings") print("Install with: pip install pybladerf") print(f"\nAlternatively, save to file with: {sys.argv[0]} -o frame.raw") return 0 if __name__ == "__main__": sys.exit(main())