Skip to main content
GNSS HAT Library Documentation
Shop GitHub Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

RTK Base Station

RTK base station broadcasting corrections from a survey antenna to satellites

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.

Note
HAT 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

Setup

Get a working RTK base station running with sane defaults in a few commands.

1. Install Dependencies & Build

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

2. Run the Install Script

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).

3. Start the Service

sudo systemctl start gnsshat-rtk-base
sudo journalctl -u gnsshat-rtk-base -f

4. Check the logs

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.

Tip
Point your rover’s NTRIP client at <base-ip>:2101, mountpoint GNSS_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.

Note
To remove the service entirely, run sudo ./uninstall-rtk-base-service.sh (add --purge to also delete the config file and the gnsshat system user).

Configuration

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

Base Position: Survey-In vs. Fixed Position

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:

FieldDescription
min_observation_time_sMinimum observation time, in seconds, before the survey can complete
required_accuracy_mRequired 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_m is the WGS-84 ellipsoidal height (HAE), not mean-sea-level.
  • type = "ecef" — Earth-Centered, Earth-Fixed x_m / y_m / z_m.

accuracy_m sets the position accuracy the receiver reports for the fixed position.

Note
Tip: promote a survey-in result to a fixed position — Run the base once in survey-in mode. While running, its periodic status line logs alt_hae=... alt_msl=... along with the surveyed lat/lon. Copy those values into [base.fixed_position] and switch mode = "fixed" so future restarts skip the survey. While you’re at it, also switch reset_mode from cold to hot — 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.

NTRIP

Set with [ntrip] mode:

  • caster (default) — the tool runs its own NTRIP caster; rovers connect directly to this machine at host:port/mountpoint. Use username/password to require authentication, max_clients to cap concurrent rovers.
  • server — the tool acts as an NTRIP client and pushes corrections to a remote caster. host is the remote caster’s hostname/IP; username/password authenticate against it. auto_reconnect (with reconnect_initial_ms/reconnect_max_ms exponential 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 and Service Behavior

[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).

Troubleshooting & Tips

Managing the Service

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.

Customizing the Unit (systemd drop-in)

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

Command-Line Options

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