A terminal-based RTK GNSS client for real-time kinematic positioning with NTRIP correction data. Supports multiple GNSS receivers including Quectel LC29H(DA) and any standard NMEA module.
- Real-time GNSS position display with RTK Fixed/Float status
- NTRIP client for receiving RTCM3 correction data
- Optional TLS/SSL encryption for NTRIP connections
- Multi-constellation support (GPS, GLONASS, Galileo, BeiDou, QZSS)
- Multi-module support (LC29H, generic NMEA, extensible via profiles)
- Curses-based terminal UI with satellite tracking, SNR statistics, and live log
- Automatic NTRIP reconnection with exponential backoff
- YAML configuration file support
- CSV position logging
- Serial port auto-discovery
- State persistence across restarts
- Configurable via config file, CLI arguments, and environment variables
- Python 3.9+
- Serial GNSS receiver (e.g. Quectel LC29HDA, u-blox, or any NMEA module) connected via USB
- Access to an NTRIP caster for RTK corrections
pip install -r requirements.txtFor development (linting, tests):
pip install -r requirements-dev.txtpython3 rtk_client.py [OPTIONS]| Option | Default | Description |
|---|---|---|
--config |
(none) | Path to YAML config file |
--gnss-module |
lc29h |
GNSS module type (lc29h, generic) |
--port |
/dev/ttyUSB0 |
Serial port (auto for auto-discovery) |
--baud |
115200 |
Baud rate for serial connection |
--ntrip-server |
193.137.94.71 |
NTRIP caster server address |
--ntrip-port |
2101 |
NTRIP caster server port |
--ntrip-mountpoint |
PNM1 |
NTRIP caster mountpoint |
--ntrip-user |
(none) | NTRIP username (or NTRIP_USER env var) |
--ntrip-pass |
(none) | NTRIP password (or NTRIP_PASS env var) |
--ntrip-tls |
off | Enable TLS/SSL for NTRIP connection |
--default-lat |
40.109 |
Fallback latitude (used when no fix) |
--default-lon |
-7.154 |
Fallback longitude |
--default-alt |
476.68 |
Fallback altitude (meters) |
--log-file |
rtk_client.log |
Log file name |
--debug |
off | Enable debug level logging |
--position-log |
(none) | Log positions to CSV file |
--position-log-interval |
5.0 |
Position log interval in seconds |
--demo |
off | Run with simulated GNSS data (no hardware needed) |
Instead of many CLI arguments, use a YAML config file:
python3 rtk_client.py --config config.yamlSee config.example.yaml for the full structure. CLI arguments override config file values.
# With environment variables
export NTRIP_USER=myuser
export NTRIP_PASS=mypass
python3 rtk_client.py --port /dev/ttyUSB0 --ntrip-server caster.example.com
# With config file
python3 rtk_client.py --config config.yaml
# Auto-discover serial port + generic NMEA module
python3 rtk_client.py --port auto --gnss-module generic
# With position logging
python3 rtk_client.py --position-log positions.csv --position-log-interval 2.0
# Demo mode — no hardware needed
python3 rtk_client.py --demo| Key | Action |
|---|---|
q |
Quit application |
r |
Reset NTRIP connection |
? |
Show help overlay |
Tab |
Cycle panel focus (Satellite / Messages / None) |
Up/Down |
Scroll within focused panel |
Enter |
Show satellite detail (when Satellite panel focused) |
| Module | --gnss-module |
Notes |
|---|---|---|
| Quectel LC29H(DA) | lc29h (default) |
Full support with PAIR/PQTM config commands |
| Any NMEA receiver | generic |
No proprietary config, standard NMEA + RTCM3 only |
To add support for a new module, create a ModuleProfile subclass in module_profiles.py.
Credentials should be passed via environment variables rather than CLI arguments to avoid exposure in process listings and shell history:
export NTRIP_USER=your_username
export NTRIP_PASS=your_passwordSee .env.example for reference. CLI arguments (--ntrip-user, --ntrip-pass) are supported but not recommended for production.
TLS/SSL can be enabled with --ntrip-tls for encrypted NTRIP connections. This uses Python's ssl.create_default_context() with hostname verification.
Serial Port (GNSS Receiver)
|
v
GnssDevice --> NmeaParser --> GnssState <-- NtripClient (RTCM3)
|
PositionLogger (CSV)
|
v
StatusDisplay (Curses UI)
The application runs up to 4 threads:
- Main thread: Curses UI event loop and display updates
- GNSS reader: Continuous serial port reading and NMEA parsing
- NTRIP client: Network communication, sends GGA positions, receives RTCM3 corrections
- Position logger (optional): Periodic CSV position recording
rtk_client.py - Application entry point
main.py - Convenience entry point (delegates to rtk_client.py)
rtk_controller.py - Component orchestrator
demo_device.py - Simulated GNSS device for demo mode
demo_ntrip.py - Simulated NTRIP client for demo mode
data/demo.nmea - NMEA replay data for demo mode
rtk_state.py - Thread-safe shared state
gnss_device.py - Serial communication with GNSS receiver
module_profiles.py - GNSS module profiles (LC29H, generic)
ntrip_client.py - NTRIP protocol client
ntrip_connection_state.py - NTRIP connection state machine
rtcm_parser.py - RTCM3 message type extraction
nmea_parser.py - NMEA sentence parser
status_display.py - Curses-based terminal UI
position_logger.py - CSV position logging
state_persistence.py - JSON state save/load
rtk_config.py - CLI argument + YAML config parsing
rtk_constants.py - Shared constants
Log files use rotation (5 MB max, 3 backups):
- Default:
rtk_client.log(configurable with--log-file) - Debug level: add
--debugflag - Previous logs preserved as
rtk_client.log.1,.2,.3
# Run linter
ruff check .
# Run tests (302 tests)
pytest -vSee CHANGELOG.md for version history.
- Ensure the GNSS antenna has a clear sky view
- Check the serial connection (
--port,--baud) - Wait for satellite acquisition (can take 30-60s cold start)
- Verify credentials are set (
NTRIP_USER,NTRIP_PASS) - Check server/port/mountpoint configuration
- Try with
--debugto see detailed connection logs - If the caster requires TLS, add
--ntrip-tls
- Minimum terminal size: 50 columns x 15 rows
- Resize your terminal and press any key to trigger redraw
- Use
--port autoto auto-discover available ports - Check device permissions (
sudo usermod -aG dialout $USER)
This project is licensed under the GNU Affero General Public License v3.0.