- Python 99.8%
- Shell 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
ci / test (3.12) (push) Successful in 2m57s
ci / test (3.14) (push) Successful in 2m53s
ci / test (3.9) (push) Successful in 2m3s
ci / testgnss (3.12) (push) Successful in 2m3s
ci / testgnss (3.14) (push) Successful in 2m25s
ci / testgnss (3.9) (push) Successful in 2m13s
Reviewed-on: https://git.ymslws.v6.rocks/ykp/gengnss/pulls/50 |
||
| .forgejo/workflows | ||
| docs | ||
| scripts | ||
| src | ||
| tests | ||
| .gitignore | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| ruff.toml | ||
gengnss
GNSS open-service baseband signal generator — GPS, Galileo, GLONASS, BeiDou — for research, education and receiver testing.
Generates complex-baseband IQ samples of GNSS open-service signals with plausible (randomly drawn, reproducible) Doppler shifts and code delays, streamed to integer-IQ files with a full sidecar scenario record.
⚠️ Legal notice: this tool produces file/baseband output only. Radiating GNSS-like signals requires a license in virtually all jurisdictions (ITU RNSS allocations; interference with aviation receivers is prohibited, e.g. by FCC rules). Do not use this software to spoof real receivers.
Features (v1.2.0)
- GPS: L1 C/A (PRN 1–37), L2C (CM/CL TDM), L5 (I/Q + NH codes), L1C (Weil codes + TMBOC(6,1,4/33)) — Tier-1 generators
- GLONASS: L1OF/L2OF FDMA (shared 511-chip ST code, per-slot frequency channels) — Tier-1
- BeiDou: B1I, B3I (SV 1–63), B1C (Weil codes + QMBOC(6,1,4/33)), B2a (Gold codes + 00010/100-chip Weil secondaries) — Tier-1
- Galileo: E5a/E5b (QPSK(10) I/Q pairs, ICD Table 15–19 LFSR codes + CS4/CS20/CS100 secondary codes, 50 PRNs each), E1 OS (E1-B/E1-C memory codes + CBOC(6,1,1/11), E1-C on CS25_1) — Tier-1
- GLONASS CDMA (L1OC/L2OC/L3OC): defined in the signal registry, Tier 2 —
the ICD appendices are not publicly available (see
docs/done/018b-bds-b2a.md) - BeiDou B2b: defined in the signal registry, Tier 2 — not yet implemented (ICD code tables pending)
- Live feasibility computation: signals that do not fit the sample-rate bandwidth around the chosen center frequency are excluded with an explicit warning (never aliased, never a hard stop)
- Reproducible scenarios: seeded Doppler/delay/phase/power draws recorded in a sidecar JSON; the same seed reproduces the file bit-for-bit
- Streaming generation: memory independent of duration
- Almanac-driven satellite selection: fetch a GPS almanac (CelesTrak Yuma/SEM,
cached 24 h, offline mode) or import a local Yuma/SEM/JSON file, optionally
filter to satellites visible from an observer (subpoint/look-angle math),
and apply the healthy/visible set to the CLI selection or the GUI palettes
(
gengnss almanac,run/show --visible, GUI Load almanac…) - Planned: navigation-message encoders, AWGN, dynamic Doppler (v2) —
see
docs/spec.md§9/§10
Signal definitions: docs/gnss/ (one file per
constellation, based on the official SIS ICDs). Functional spec:
docs/spec.md. Implementation plan:
docs/backlog/000-overview.md.
Install
pip install -e .[dev] # plain pip; or: uv pip install -e .[dev]
pytest -q # GUI tests skip on a headless machine
ruff check src tests
Python ≥ 3.9 (CI runs 3.9, 3.12 and 3.14). Core dependencies: numpy,
sdr==0.0.30.
CLI usage
# 8 GPS C/A PRNs, 1 second, at 4.092 Msps centered on L1
gengnss run --fs 4.092e6 --center 1575.42e6 --duration 1 --seed 2026 \
--preset gps-l1x8 --output gps_l1.i16
# GLONASS FDMA: slots select the frequency channel (k)
gengnss run --fs 8e6 --center 1602e6 --duration 1 --seed 2026 \
--signal glo_l1of:R2:-4 --signal glo_l1of:R10:0 \
--output glonass_l1.i16
# BeiDou B1I (needs > 4.092 Msps: the B1I mainlobe is 4.092 MHz wide)
gengnss run --fs 4.2e6 --center 1561.098e6 --duration 1 --seed 2026 \
--signal bds_b1i:C1 --signal bds_b1i:C5 --output beidou_b1i.i16
# inspect the resolved scenario + feasibility report without generating
gengnss show --signal gps_l1ca:PRN1 --seed 42
# what's in the sky right now (GPS almanac from CelesTrak, cached 24h)
gengnss almanac --lat 48.85 --lon 2.35
# simulate every healthy almanac satellite visible from an observer
# (selections are frozen in the sidecar -> regenerating stays bit-identical)
gengnss run --visible --lat 48.85 --lon 2.35 --seed 42 --output visible.i16
# regenerate bit-identically from a sidecar scenario
gengnss scenario gps_l1.i16.scenario.json --output gps_l1_again.i16
# open the GUI
gengnss --gui
GUI
A Tkinter desktop app (stdlib only, same engine as the CLI): notebook tabs for
settings, signal selection and channel model, a live feasibility table, a
scenario summary, and a Generate button with progress and cancel
(gengnss --gui). Settings entries can be browsed to an output path, the
current scenario can be saved to / loaded from a sidecar JSON, and a
Generate scenario button writes just the scenario JSON (seed + drawn
channels) for gengnss scenario FILE -o out.i16. Satellites are picked with
point-and-click palettes of real ICD IDs per signal (GLONASS offers slot and
frequency-channel views; the spec text entry stays for advanced use). A
Load almanac… dialog fetches a GPS almanac (CelesTrak Yuma/SEM) in the
background or imports a local Yuma/SEM/JSON file, optionally filters healthy
satellites by visibility from an observer position, and pre-checks the
matching satellites in the palettes. The headless equivalents are gengnss run / show /
scenario.
Prerequisites: a graphical session (Windows desktop, Linux desktop, WSLg on Windows, or X11 forwarding over SSH) and a Python interpreter with Tkinter available.
Linux
The distribution Python usually needs Tkinter installed separately:
# Debian / Ubuntu (pick the package matching your Python version)
sudo apt install python3-tk # e.g. python3.12-tk for Python 3.12
# Fedora
sudo dnf install python3-tkinter
# Arch
sudo pacman -S tk
Verify before launching: python3 -c "import tkinter" (prints nothing on
success). Then install and launch:
# plain Python
python3 -m venv .venv
. .venv/bin/activate
pip install -e .
gengnss --gui
# uv
uv venv
uv pip install -e .
uv run gengnss --gui
Older interpreters (e.g. Python 3.9): replace python3 with your interpreter
(python3.9 -m venv .venv) or uv venv --python 3.9 — everything else is
identical.
Note: uv-managed Python interpreters on Linux do not bundle Tkinter (python-build-standalone). If
uv runfails withModuleNotFoundError: No module named 'tkinter', use a system Python withpython3-tkinstalled instead.
Windows
Tkinter ships with every Windows Python (python.org installer, Microsoft Store app, and uv-managed interpreters), so no extra package is needed. In PowerShell:
# plain Python
py -3.12 -m venv .venv
.\venv\Scripts\Activate.ps1
python -m pip install -e .
gengnss --gui
# uv
uv venv
uv pip install -e .
uv run gengnss --gui
(cmd.exe: activate with .\venv\Scripts\activate.bat; under WSL, follow
the Linux steps — the window renders via WSLg.)
Troubleshooting: if gengnss is not found, activate the virtualenv first (or
re-create it); if the window fails to open, check import tkinter and that a
display is available (a bare SSH session has none — use ssh -X).
Every generation writes <output>.scenario.json — the resolved scenario with
drawn channels and the feasibility verdicts — so any file can be reproduced or
fed into a receiver test harness.
End-to-end smoke run
sh scripts/e2e_smoke.sh
Generates one second per band-separated scenario (GPS L1 ×8, GLONASS L1OF ×4,
BeiDou B1I ×2), validates every file with the spec-7.1 self-correlation
harness (every drawn signal must be re-acquired within ±50 Hz Doppler and
±1 chip code phase), and prints SHA-256 sums. Runs locally (currently not in
CI: the self-hosted runner's workspace checkout proved unreliable - see
.forgejo/workflows/ci.yml header note).
Output format
Interleaved integer IQ (little-endian), i16 (default) or i8, peak
normalized to the configured full-scale percentage. See the sidecar JSON for
the exact scenario.
License
MIT (see LICENSE).