RTK Base Station
gnsshat-rtk-base turns an L1/L5 RTK HAT into a standing RTK base station. It configures the receiver, determines the base’s position (survey-in or a fixed position you supply), and serves RTCM3 correction messages to rovers over NTRIP — either from a local caster or by pushing to a remote one.
NoteHAT Compatibility — Requires the L1/L5 RTK HAT (NEO-F9P). See RTK for how RTK corrections work at the library level.
Source: gnsshat/tools/gnsshat-rtk-base.cpp
Get a working RTK base station running with sane defaults in a few commands.
Install the base dependencies — libtoml11-dev is required since gnsshat-rtk-base reads its config via TOML. See Installation for the full list:
sudo apt-get install build-essential cmake libgpiod-dev libtoml11-dev
Then build and install the library with the CLI tools enabled (BUILD_TOOLS is off by default upstream):
cmake -B build -DBUILD_TOOLS=ON
cmake --build build
sudo cmake --install build
cd gnsshat/tools/scripts
sudo ./install-rtk-base-service.sh
The script creates a dedicated gnsshat system user (with spi/gpio/dialout group access for hardware permissions), copies a default config to /etc/gnsshat/rtk-base.toml only if one doesn’t already exist, installs the gnsshat-rtk-base.service systemd unit, and enables it (without starting it).
sudo systemctl start gnsshat-rtk-base
sudo journalctl -u gnsshat-rtk-base -f
sudo journalctl -u gnsshat-rtk-base -f
Out of the box — with no config edits — the service starts with the shipped defaults: Survey-In base mode (min_observation_time_s = 120, required_accuracy_m = 50.0), a cold receiver reset, and a local, unauthenticated NTRIP caster listening on port 2101 (all interfaces, 0.0.0.0) with mountpoint GNSS_HAT.
TipPoint your rover’s NTRIP client at<base-ip>:2101, mountpointGNSS_HAT, no username/password — it’ll start receiving corrections right away.
Need a fixed position, authentication, or a remote caster instead? Edit the config and restart:
sudo nano /etc/gnsshat/rtk-base.toml
sudo systemctl restart gnsshat-rtk-base
See Configuration below for all available options.
NoteTo remove the service entirely, runsudo ./uninstall-rtk-base-service.sh(add--purgeto also delete the config file and thegnsshatsystem user).
The service reads /etc/gnsshat/rtk-base.toml (installed from example-rtk-base.toml). All fields are optional — the defaults describe a survey-in base with a local, unauthenticated NTRIP caster on port 2101. Any option can also be overridden on the command line (see Troubleshooting & Tips).
# Receiver reset before start: "cold", "hot", or "none"
reset_mode = "cold"
# GNSS measurement rate in Hz (1-25)
measurement_rate_hz = 1
# Dynamic model: "stationary", "portable", "pedestrian", "automotive", ...
dynamic_model = "stationary"
[timepulse]
active = true
frequency_hz = 1
duty_cycle = 0.1
polarity = "rising"
[base]
mode = "survey_in" # "survey_in" or "fixed"
[base.survey_in]
min_observation_time_s = 120
required_accuracy_m = 50.0
[base.fixed_position]
type = "lla" # "lla" or "ecef"
latitude_deg = 52.232222222
longitude_deg = 21.008055556
height_m = 110.0
accuracy_m = 0.5
[ntrip]
mode = "caster" # "caster" or "server"
host = "0.0.0.0"
port = 2101
mountpoint = "GNSS_HAT"
max_clients = 10
auto_reconnect = true
[ntrip.tls]
enabled = false
[logging]
log_level = "info"
[service]
mode = "auto"
watchdog = true
Set with [base] mode = "survey_in" or "fixed".
Survey-In ([base.survey_in]) — the default. The receiver observes satellites over time and settles on a position once both conditions are met:
| Field | Description |
|---|---|
min_observation_time_s | Minimum observation time, in seconds, before the survey can complete |
required_accuracy_m | Required 3D position accuracy, in meters, before the survey completes |
Fixed Position ([base.fixed_position]) — supply an already-known, surveyed position so the base skips the survey and starts correcting immediately on startup. Two coordinate formats are supported via type:
type = "lla"— latitude/longitude/height.height_mis the WGS-84 ellipsoidal height (HAE), not mean-sea-level.type = "ecef"— Earth-Centered, Earth-Fixedx_m/y_m/z_m.
accuracy_m sets the position accuracy the receiver reports for the fixed position.
NoteTip: promote a survey-in result to a fixed position — Run the base once in survey-in mode. While running, its periodic status line logsalt_hae=... alt_msl=...along with the surveyed lat/lon. Copy those values into[base.fixed_position]and switchmode = "fixed"so future restarts skip the survey. While you’re at it, also switchreset_modefromcoldtohot— with a known fixed position there’s no need to force a full cold reset, and a hot start reuses the receiver’s stored ephemeris/almanac data for a much faster time-to-first-fix on every restart.
Set with [ntrip] mode:
caster(default) — the tool runs its own NTRIP caster; rovers connect directly to this machine athost:port/mountpoint. Useusername/passwordto require authentication,max_clientsto cap concurrent rovers.server— the tool acts as an NTRIP client and pushes corrections to a remote caster.hostis the remote caster’s hostname/IP;username/passwordauthenticate against it.auto_reconnect(withreconnect_initial_ms/reconnect_max_msexponential backoff) keeps retrying the connection if it drops.
[ntrip.tls] enables TLS for either mode: verify_peer (server mode) validates the remote caster’s certificate, while cert_file/key_file (caster mode) supply a PEM certificate/key so rovers can connect over https-style NTRIP.
[logging] controls verbosity (log_level: error/warning/info/debug) and the periodic status-line interval (interval_sec, 0 = auto: 1 s interactively, 30 s under systemd).
[service] controls output formatting: mode = "auto" detects whether it’s running under systemd/non-TTY and switches between human-readable timestamped lines and journald-style priority-prefixed lines; watchdog toggles systemd watchdog pings (WatchdogSec in the unit file).
sudo systemctl start gnsshat-rtk-base
sudo systemctl stop gnsshat-rtk-base
sudo systemctl status gnsshat-rtk-base
sudo journalctl -u gnsshat-rtk-base -f
The unit runs as Type=notify with a 30 s watchdog, restarts on failure with stepwise backoff, and will not auto-restart on config/hardware errors (exit code 78) — those require manual intervention. It’s sandboxed with standard systemd hardening (ProtectSystem=full, NoNewPrivileges, capability bounding, etc.) and only allows access to the SPI/UART/GPIO devices it needs.
To customize the unit without editing the shipped file (e.g. to point at a different config path or pass extra flags), use a systemd drop-in:
sudo systemctl edit gnsshat-rtk-base
In the editor, add:
[Service]
ExecStart=
ExecStart=/usr/local/bin/gnsshat-rtk-base --config /etc/gnsshat/rtk-base.toml --no-watchdog
The empty ExecStart= line clears the shipped command so the line below can fully replace it (systemd drop-ins otherwise append to the existing ExecStart). Then apply it:
sudo systemctl daemon-reload && sudo systemctl restart gnsshat-rtk-base
CLI arguments override values loaded from the config file:
Usage: gnsshat-rtk-base [options]
--config <path> Load TOML configuration file
--port <port> NTRIP port (default: 2101)
--mountpoint <name> NTRIP mountpoint (default: GNSS_HAT)
--host <host> NTRIP host/bind address (default: 0.0.0.0)
--mode <caster|server> NTRIP mode (default: caster)
--username <user> NTRIP username
--password <pass> NTRIP password
--survey-in Use survey-in base mode (default)
--fixed-lla <lat,lon,h> Use fixed LLA position (h = WGS-84 HAE in meters)
--fixed-ecef <x,y,z> Use fixed ECEF position
--accuracy <meters> Position accuracy for fixed mode (default: 0.5)
--reset <cold|hot|none> Receiver reset mode (default: cold)
--validate Validate config and exit (no hardware access)
--service Force service-mode output
--no-service Force interactive output (default when on a TTY)
--log-level <lvl> error|warning|info|debug (default: info)
--log-interval <sec> Summary interval in seconds (0 = auto)
--no-watchdog Disable systemd watchdog pings
--help, -h Show this help
The unit file already runs gnsshat-rtk-base --config /etc/gnsshat/rtk-base.toml --validate as an ExecStartPre check, so a broken config fails fast before the service actually starts.
# Validate the installed config without touching hardware
gnsshat-rtk-base --config /etc/gnsshat/rtk-base.toml --validate
# Run interactively with a fixed LLA position and a local caster on port 2101
gnsshat-rtk-base --fixed-lla 52.232222,21.008056,110.0 --port 2101
# Push corrections to a remote NTRIP caster
gnsshat-rtk-base --mode server --host caster.example.com --port 2101 \
--mountpoint GNSS_HAT --username myuser --password mypass