GPS NMEA sentence simulator. Fetches real satellite ephemeris from u-blox AssistNow, propagates orbits using Keplerian mechanics (IS-GPS-200), and outputs realistic NMEA sentences over serial or stdout.
Left: coldstart feeding u-center via TCP. Right: real u-blox receiver on COM33. Skyviews and SNR bars match.
Given a position (lat/lon/alt) and a u-blox API key, coldstart:
- Fetches current GPS broadcast ephemeris from u-blox AssistNow Online
- Propagates satellite orbits using Keplerian mechanics with IS-GPS-200 harmonic corrections (Cuc/Cus/Crc/Crs/Cic/Cis)
- Computes per-satellite azimuth, elevation, and simulated SNR for the observer position
- Generates a full set of NMEA 0183 sentences at 1 Hz, synchronized to UTC second boundaries
- Outputs over serial port, stdout, or anything you can pipe to
This is useful for testing GPS receivers, NMEA parsers, and navigation systems without real GPS signals or expensive RF simulators.
nix develop # enter dev shell with Rust toolchain
cargo build # build the binary
cargo test # run the test suite (44 tests)Requires Rust 1.75+, pkg-config, openssl, and libudev dev headers.
cargo build# CLI args (recommended)
coldstart --lat 53.3498 --lon -6.2603 --api-key YOUR_KEY
# Or via environment variables
export LATITUDE=53.3498
export LONGITUDE=-6.2603
export UBLOX_API_KEY=your-api-key-here
coldstart| Flag | Env var | Default | Description |
|---|---|---|---|
--lat |
LATITUDE |
required | Decimal degrees (-90 to 90) |
--lon |
LONGITUDE |
required | Decimal degrees (-180 to 180) |
--alt |
ALTITUDE |
0 |
Altitude in metres |
--api-key |
UBLOX_API_KEY |
required | u-blox AssistNow API key |
--api-host |
UBLOX_API_HOST |
online-live1.services.u-blox.com |
AssistNow host |
-p, --port |
/dev/ttyUSB0 |
Serial port device | |
-b, --baud |
9600 |
Serial baud rate | |
--stdout |
Output to stdout instead of serial | ||
--once |
Emit one burst and exit | ||
-v, --verbose |
Verbose logging (-v info, -vv debug) |
# Serial output at custom baud rate
coldstart --lat 53.35 --lon -6.26 --api-key KEY -p /dev/ttyACM0 -b 115200
# Test without hardware — single burst to stdout
coldstart --lat 53.35 --lon -6.26 --api-key KEY --stdout --once
# Continuous stdout — pipe to socat, netcat, u-center via TCP, etc.
coldstart --lat 53.35 --lon -6.26 --api-key KEY --stdout
# Feed u-center over TCP (as shown in screenshot)
coldstart --lat 53.35 --lon -6.26 --api-key KEY --stdout | socat - TCP-LISTEN:7878,reuseaddrThe simulator syncs to the top of each UTC second and outputs NMEA sentences at 1 Hz. Satellite positions are recomputed every tick.
src/
├── main.rs — CLI (clap), orchestration, serial/stdout output loop
├── nmea.rs — NMEA 0183 sentence generation with checksum
├── orbit.rs — IS-GPS-200 Keplerian propagation, WGS-84 geodesy, ECEF/ENU/AzEl
├── ubx.rs — u-blox UBX protocol parsing, ephemeris struct scaling
└── assist_now.rs — u-blox AssistNow Online API client
AssistNow API → UBX MGA-GPS-EPH → ephemeris scaling → Keplerian propagation
→ satellite ECEF → observer-relative azimuth/elevation
→ NMEA sentence generation → serial port / stdout
| Sentence | Description |
|---|---|
| GGA | Fix data — position, fix quality, satellite count, HDOP |
| GLL | Geographic position — lat/lon with validity status |
| GSA | DOP and active satellites (up to 12) |
| RMC | Recommended minimum — position, velocity, time |
| VTG | Course and speed over ground |
| GSV | Satellites in view — azimuth, elevation, SNR (multi-message) |
| ZDA | UTC date/time with timezone offset |
Satellite positions are computed from broadcast ephemeris using the IS-GPS-200 algorithm (§ 20.3.3.4.3):
- Keplerian orbit propagation with eccentric anomaly solved via Newton-Raphson
- Second harmonic corrections: Cuc/Cus (argument of latitude), Crc/Crs (radius), Cic/Cis (inclination)
- WGS-84 geodetic-to-ECEF conversion for observer position
- ECEF-to-ENU rotation for azimuth/elevation computation
- SNR model: 25 dB-Hz at horizon, 50 dB-Hz at zenith (linear with elevation)
MIT