Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

obiedeh/wireless-link-intelligence-system

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Wireless Link Intelligence System

Signal-processing correctness + AI-assisted link estimation + edge deployment evidence. A production-discipline reference for physical-layer AI: a classical QPSK baseband simulator with deterministic BER vs SNR sweeps, four scikit-learn estimators that learn link conditions from constellation statistics, an ONNX export path validated against a Jetson benchmark template, and end-to-end reproducibility from a fresh clone in under five minutes.

The deliverable is the engineering pattern, not a production receiver. Every BER number is regenerable from a deterministic seed; every ML metric is reported on a stratified holdout; the channel classifier's weak 0.472 accuracy is surfaced as a calibrated finding rather than hidden in a footnote.

Open the live dashboard  ·  Tech brief  ·  Source code


Why this exists

AI-RAN and edge-AI wireless systems depend on a measurable physical layer. Most repos in this space either skip the signal-processing work (claim ML wins without showing the classical baseline) or skip the discipline (claim production-grade results from notebook-only experiments). Neither is a defensible engineering pattern.

This repo demonstrates the engineering pattern that makes physical-layer ML credible: classical baseline first (verified BER curves matching textbook predictions), ML estimators second (with honest holdout metrics and disclosed weaknesses), edge deployment path third (ONNX export + Jetson benchmark template, ready when hardware lands).

The discipline is the deliverable. The QPSK math is textbook, the dataset is synthetic, the Jetson latency row is honest <TO MEASURE> until hardware arrives. What's defensible end-to-end is the methodology, the reproducibility, and the calibration of confidence.


Headline Evidence

Signal Value Source
PHY modem CP-OFDM, 64 subcarriers, adaptive QAM 4 / 16 / 64 / 256 (Gray-coded) qpsk_link/ofdm.py · reports/ber_full_ofdm_awgn.csv
3GPP channel models TDL-A / TDL-B / TDL-C from TR 38.901 §7.7.2 qpsk_link/tdl_channel.py · reports/bler_full_tdl_ofdm.csv
Channel estimation LS / MMSE / Neural (PyTorch) head-to-head on TDL-C — neural wins at low SNR qpsk_link/channel_estimation.py · reports/channel_estimation_comparison.csv
Edge deployment PyTorch FP32 → ONNX FP32 → ONNX INT8 (dynamic PTQ), ~3.3× CPU latency drop train_snr_torch.py · reports/snr_quantization_comparison.json
Jetson AGX Thor benchmark template hardware-ready; run via JETSON_BENCHMARK_GUIDE.md edge/jetson_benchmark_template.py
AWGN BER full sweep (1M bits) 2.42e-3 @ 0 dB → 1.83e-4 @ 2 dB → below 1e-6 sim floor at 6+ dB reports/ber_full_awgn.csv
Ensemble Rayleigh BER (200 × 10k bits, transmit-power-SNR) 4.18e-2 @ 0 dB → 5.2e-4 @ 20 dB reports/ber_full_rayleigh.csv
SNR estimator — MAE / R² (synthetic features) 0.118 dB / 0.999 reports/link_estimation_metrics.json
Channel classifier — accuracy 0.472 (honest weak result — surfaced, not hidden) same
Test suite 77 tests, green on CI matrix (Python 3.11 + 3.12) .github/workflows/ci.yml
End-to-end reproducible make verify regenerates every committed artifact under reports/
Executive dashboard reports/dashboard.html one HTML page on GitHub Pages
Tech brief TECH_BRIEF.md one-page hiring-manager summary

Full numbers and methodology in Measured Metrics below. Limitations and what production would require in Positioning.


Dashboard Evidence

Open the rendered dashboard here: reports/dashboard.html.

It summarizes:

  • adaptive QAM BER across CP-OFDM QPSK / 16-QAM / 64-QAM / 256-QAM
  • 3GPP TR 38.901 TDL-A/B/C BLER multipath stress behavior
  • LS vs MMSE vs neural channel-estimation comparison
  • ONNX FP32/INT8 deployment path and quantization trade-off
  • weak channel classifier result disclosed instead of hidden
  • Jetson AGX Thor benchmark template ready, with latency marked pending until measured

Engineering practices that matter here

These are the concrete decisions that separate a clean physical-layer reference from a notebook with a model in it:

  • CP-OFDM with adaptive QAM — not just QPSK. qpsk_link/ofdm.py implements a 64-subcarrier CP-OFDM modem with Gray-coded square QAM at M = 4 / 16 / 64 / 256. Constellations normalised to unit average symbol energy; Gray property verified by an explicit test that walks the I/Q grid and checks every neighbour pair has Hamming distance exactly 1. The resulting BER vs SNR curves match textbook 5G NR link-adaptation tables.
  • 3GPP TR 38.901 TDL-A / TDL-B / TDL-C channels. qpsk_link/tdl_channel.py transcribes the literal NLOS tap profiles from TR 38.901 §7.7.2 Tables 7.7.2-1/2/3. Block fading per realisation, power normalised so E[Σ|h|²] = 1. Ensemble BLER curves committed to reports/bler_full_tdl_ofdm.csv. The honest finding (BLER ~10% even at 30 dB without coding) is the signal that motivates LDPC + HARQ — surfaced, not polished away.
  • Pilot-based channel estimation with LS / MMSE / neural compared head-to-head. qpsk_link/channel_estimation.py runs all three on the same TDL-C realisations and reports both channel-MSE and resulting BLER. The PyTorch MLP is the DeepRx pattern in miniature; the calibrated finding is that neural wins at low SNR (denoising), MMSE wins at high SNR (correct prior + low noise = closed-form optimum). LS lags everywhere.
  • PyTorch + INT8 ONNX deployment pipeline. train_snr_torch.py trains a small MLP, exports FP32 ONNX, dynamic-PTQ quantises to INT8 ONNX, and benchmarks holdout MAE + file size + CPU latency for all three forms. Measured: ~3.3× CPU latency reduction and ~2× smaller file with sub-0.01 dB accuracy drift — textbook PTQ payoff. INT8 ONNX lands directly on Jetson AGX Thor via edge/jetson_benchmark_template.py.
  • No feature leakage for the link-condition estimators: fading_abs and fading_phase are saved in the dataset CSV as labels but excluded from FEATURE_COLUMNS in ai_link_estimation/features.py. They encode oracle channel knowledge and would trivially inflate any classifier built on them — AGENTS.md non-negotiable rule #1.
  • Two-pass channel verification. A bug in earlier revisions had add_awgn referencing noise to received power instead of transmit power, making the Rayleigh penalty cancel out at the receiver. Caught by ensemble measurement, fixed (add_awgn gained an optional reference_power, apply_channel now passes pre-fading transmit power), and verified with a regression gate: reports/ber_smoke_awgn.csv must regenerate bit-identically.
  • CI runs on Python 3.11 AND 3.12. Most portfolio repos pin one version; this one validates both, including the PyTorch + ONNX + INT8 quantisation pipeline.
  • Deterministic everywhere it matters. np.random.default_rng(seed) is threaded through the BER sweep, dataset generation, model training, channel-estimator training, and ONNX export — every committed number is byte-reproducible from the committed seed.

If you are evaluating physical-layer ML engineering: these are the signals that distinguish a reference repo from a tutorial.


Core Stack

Implemented: Python · NumPy · SciPy · scikit-learn · matplotlib · ONNX export · BER analysis · permutation-aware feature design

Optional extension: Jetson ONNX Runtime latency benchmarking (template ready, hardware not yet measured)

Python NumPy SciPy scikit-learn ONNX Jetson benchmark template


What this is

Layer What it does
QPSK modem Gray-coded mapping, root-raised-cosine pulse shaping, matched filtering, hard-decision demapping. Deterministic from a single seed.
Channel models AWGN with explicit reference_power parameter; flat Rayleigh fading with optional ensemble averaging. The transmit-power-SNR convention is enforced — the Rayleigh diversity penalty is visible, not cancelled.
BER sweep harness Single-shot (run_sim.py) and ensemble (run_sim_ensemble.py) over a configurable SNR grid; CSV + SVG output. Smoke (2k bits) and full (1M bits) regimes.
Link-condition dataset Synthetic CSV (data/link_conditions.csv) with 12 constellation statistics + 4 labels (SNR, BER, channel type, link-quality score).
ML link estimators Four scikit-learn estimators: SNR regressor (R² 0.999), BER regressor (R² 0.968), channel-type classifier (acc 0.472 — disclosed weak), link-quality scorer (R² 0.904).
ONNX export Each .joblib estimator converts to ONNX via skl2onnx. Output models live under models/onnx/ (gitignored).
Edge benchmark template edge/jetson_benchmark_template.py runs onnxruntime on any host; designed to drop onto a Jetson and emit latency p50/p95/p99 into reports/jetson_inference_benchmark.json when hardware lands.
Reports BER CSVs + SVGs, model metrics JSON, plain-text link-estimation report, single-page HTML executive dashboard.

Measured Metrics

Source: reports/link_estimation_metrics.json (mirror at models/metrics.json). Dataset: synthetic link-condition CSV with 500 samples, 125-sample stratified holdout, 12 constellation-statistic features.

Metric Value Status
SNR estimator — MAE / R² 0.118 dB / 0.999 measured
BER predictor — MAE / R² 0.000453 / 0.968 measured
Channel classifier — accuracy 0.472 measured (honest weak result — see Interpretation)
Link-quality scorer — MAE / R² 4.089 / 0.904 measured
AWGN BER smoke (2 000 bits) 0.0025 @ 0 dB; 0.0 @ 2–20 dB measured (reports/ber_smoke_awgn.csv) — coarse, hits resolution floor above 0 dB
AWGN BER full sweep (1 000 000 bits) 2.42e-3 @ 0 dB · 1.83e-4 @ 2 dB · 5.0e-6 @ 4 dB · 0 @ 6–20 dB (below 1e-6 sim floor) measured (reports/ber_full_awgn.csv) — make run-sim-full
Rayleigh BER smoke (2 000 bits, single fading realization, seed=7) 4.8e-2 @ 0 dB · 0 @ 2–20 dB measured (reports/ber_smoke_rayleigh.csv) — make run-sim-rayleigh. Single-realization caveat — see note below.
Ensemble-averaged Rayleigh BER (N=200 realizations × 10 000 bits, transmit-power-SNR) 4.18e-2 @ 0 dB · 4.70e-2 @ 2 dB · 2.67e-2 @ 4 dB · 1.24e-2 @ 6 dB · 8.3e-3 @ 8 dB · 7.7e-3 @ 10 dB · 5.8e-3 @ 12 dB · 3.6e-3 @ 14 dB · 2.6e-4 @ 16 dB · 2.9e-3 @ 18 dB · 5.2e-4 @ 20 dB measured (reports/ber_full_rayleigh.csv) — make run-sim-rayleigh-full. Classical 1/SNR diversity-1 penalty visible vs AWGN's exponential decay
Jetson ONNX inference latency (p50/p95/p99) <TO MEASURE> Plan: run edge/jetson_benchmark_template.py on Jetson when hardware lands; capture mean latency and inferences/sec into reports/jetson_inference_benchmark.json

The channel classifier scoring 0.472 on a two-class problem is a useful negative result, not noise to hide: the current 12-feature set supports SNR/BER estimation much better than channel-type recognition. See reports/link_estimation_report.md for the full interpretation.

The Rayleigh smoke row is a single-realization result: channel.rayleigh_fading draws one complex-Gaussian h per simulation call, and the demodulator receives that h as perfect channel-state information. With seed=7 the single draw produces a deep enough fade at 0 dB to push BER to 4.8 %, while higher-SNR draws happen to land at favourable |h|² and clear below the 2000-bit resolution floor.

The ensemble-averaged Rayleigh row (N=200 × 10 000 bits per SNR point) is the right curve to compare against AWGN. It uses the transmit-power-SNR convention: channel.add_awgn references noise variance to the pre-fading transmit signal power (via an explicit reference_power passed from apply_channel), so the |h|² factor does not cancel out and the fade penalty propagates to the receiver.

Earlier revisions used a per-realization-SNR convention (noise referenced to received power), under which the Rayleigh curve tracked AWGN at the same SNR. That earlier behaviour was confirmed by ensemble measurement and then fixed: channel.add_awgn gained an optional reference_power parameter, and apply_channel now passes the pre-fading transmit power on both AWGN and Rayleigh paths. The verification gate (AWGN BER on reports/ber_smoke_awgn.csv and reports/ber_full_awgn.csv must regenerate bit-identically) is satisfied — the AWGN path is mathematically unchanged because the input signal to add_awgn already equals the transmit signal there.

Reproduce locally:

make install-dev
make generate-evidence
make train-evidence
cat reports/link_estimation_metrics.json

Architecture

flowchart LR
    A["Random bits"] --> B["QPSK mapper<br/>(Gray coding)"]
    B --> C["Pulse shaping<br/>(root-raised cosine)"]
    C --> D["Wireless channel<br/>AWGN / Rayleigh"]
    D --> E["Matched filter<br/>symbol timing recovery"]
    E --> F["QPSK demapper<br/>(hard decision)"]
    F --> G["BER + constellation statistics"]
    G --> H["Classical BER vs SNR curve<br/>textbook verification"]
    G --> I["ML link estimators<br/>SNR / BER / channel / quality"]
    I --> J["ONNX export"]
    J --> K["Jetson benchmark template<br/>latency p50 / p95 / p99"]
    G --> L["Static dashboard<br/>operator engineering readout"]
Loading

Repository Structure

.
├── run_sim.py                            # BER sweep CLI (--fading, --num-bits, --snr-min/max/step)
├── run_sim_ensemble.py                   # Ensemble Rayleigh BER sweep (--n-realizations)
├── qpsk_modem.py                         # QPSK mapper, RRC filter, matched filter, demap
├── channel.py                            # AWGN + flat Rayleigh fading (transmit-power-SNR)
├── plots.py                              # BER curves, constellation plots
├── generate_dataset.py                   # Synthetic link-condition CSV CLI
├── train_link_models.py                  # Train SNR/BER/channel/quality estimators
├── export_onnx.py                        # Export trained estimators to ONNX
│
├── ai_link_estimation/                   # ML link-estimation package
│   ├── dataset.py                        # simulate_link_sample, generate_dataset, schema
│   ├── features.py                       # FEATURE_COLUMNS, constellation_statistics
│   ├── models.py                         # train_models, write_comparison_report
│   └── onnx_export.py                    # ONNX conversion via skl2onnx
│
├── edge/
│   └── jetson_benchmark_template.py      # ONNX Runtime latency benchmark
│
├── tests/
│   ├── test_modem.py                     # DSP unit tests
│   └── test_features.py                  # Feature extraction + link-quality score tests
│
├── reports/                              # Committed proof artifacts (regenerable via make verify)
│   ├── ber_smoke_awgn.{csv,svg}          # 2k-bit AWGN smoke curve
│   ├── ber_full_awgn.{csv,svg}           # 1M-bit AWGN full sweep
│   ├── ber_smoke_rayleigh.{csv,svg}      # Single-realization Rayleigh smoke (disclosed caveat)
│   ├── ber_full_rayleigh.{csv,svg}       # Ensemble-averaged Rayleigh (N=200 × 10k bits)
│   ├── link_estimation_metrics.json      # ML estimator holdout metrics
│   ├── link_estimation_report.md         # Plain-text comparison report
│   ├── edge_inference_plan.md            # ONNX / TensorRT path notes
│   └── dashboard.html                    # Executive HTML dashboard (regenerated)
│
└── notebooks/legacy/                     # Original exploration notebooks (provenance only)

Quick Start

Primary target: Linux / macOS / Windows. Core simulator runs on standard CPU Python. Jetson usage is the optional ONNX Runtime benchmark path after models are exported.

git clone https://github.com/obiedeh/wireless-link-intelligence-system.git
cd wireless-link-intelligence-system
python -m venv .venv
source .venv/bin/activate          # PowerShell: .\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
python run_sim.py

Full verification (Linux / macOS):

make install-dev
make verify

make verify runs ruff lint → 15 pytest tests → BER smoke sweep → synthetic dataset generation → model training → artifact existence check.


Run This Demo

make verify
python build_dashboard.py

Then open the rendered dashboard:

For a fast CLI-only path, run:

python run_sim.py
python generate_dataset.py --samples 500 --num-bits 4000
python train_link_models.py

AI-assisted link estimation

Generate synthetic link-condition data:

python generate_dataset.py --samples 500 --num-bits 4000

This creates data/link_conditions.csv with: SNR, measured BER from the classical baseline, channel type, fading magnitude and phase, and 12 constellation statistics (power, I/Q moments, EVM, phase spread, radius spread, quadrant balance).

Train the estimators and produce the comparison report:

python train_link_models.py

Training artifacts:

Artifact What it tells you
models/snr_estimator.joblib Trained SNR estimator (regression: constellation statistics → SNR in dB).
models/ber_predictor.joblib Trained BER predictor (regression: constellation statistics → measured BER).
models/channel_classifier.joblib Trained channel classifier (AWGN vs Rayleigh; weak — see Measured Metrics).
models/link_quality_scorer.joblib Trained link-quality scorer (regression: constellation statistics → synthetic 0–100 score).
models/metrics.json Machine-readable holdout metrics and example predictions for all four estimators.
reports/link_estimation_report.md Human-readable comparison report with example rows and interpretation.
reports/link_estimation_metrics.json Mirror of models/metrics.json under reports/ for the proof-artifact pattern.

CI validates this path with a smaller deterministic dataset so the repository proves tests, simulation, dataset generation, training, and evidence artifact creation on Ubuntu (Python 3.11 + 3.12).


Edge deployment path

This is an edge benchmark path, not a TensorRT-validated production deployment claim. Use it to generate measurable latency evidence on Jetson hardware.

Export trained models to ONNX:

python -m pip install ".[edge]"
python export_onnx.py

Benchmark on Jetson (or any host with onnxruntime):

python edge/jetson_benchmark_template.py --model models/onnx/snr_estimator.onnx --runs 1000

See reports/edge_inference_plan.md for TensorRT-ready notes. The current ONNX path is a practical edge inference bridge for the tabular estimators. For TensorRT acceleration, the tree-based estimators would need to be distilled into a small neural network and validated for parity across Python, ONNX Runtime, and TensorRT — a known scope expansion, intentionally deferred until Jetson hardware lands.

For the reviewer-facing checklist see PORTFOLIO_DELIVERABLES.md. For the executive one-pager see TECH_BRIEF.md.


Positioning

This repository is a production-discipline reference for physical-layer AI engineering. It is not:

  • a full AI-RAN base station
  • a scheduler
  • a standards-compliant modem
  • a production telecom receiver

What it is: a measurable QPSK simulation testbed with verified BER curves, an ML link-estimation layer with honest holdout evaluation, and an ONNX export path validated end-to-end on commodity hardware (Jetson latency <TO MEASURE> until the device lands).

The methodology — classical baseline first, ML second, edge path third, honest weak results surfaced — applies beyond QPSK to any physical-layer AI work.


License

MIT License.

About

Wireless link intelligence dashboard for SINR, RSSI, path-loss interpretation, and RF decision support.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.