Skip to main content

RTCM relay service for custom GPS correction servers

Project description

SP-RTK-Base-Relay

A Python service that relays RTCM correction data from RTK GPS base stations to multiple destinations simultaneously โ€” Sure-Path servers, NTRIP casters, and local TCP clients.

CI Release codecov PyPI version PyPI - Python Version PyPI - Downloads License Ruff

Overview

SP-Base-Relay v2.1 is a production-ready multi-destination RTCM relay and embeddable Python library. A single GPS input source (TCP, serial, or Bluetooth) feeds correction data to any combination of destinations โ€” proprietary Sure-Path servers, NTRIP v1.0/v2.0 casters (RTK2go, Onocoy, rtkdirect), and local TCP rebroadcast servers.

Key Features

  • ๐Ÿ”€ Multi-Destination Broadcast: Fan-out RTCM data to 1โ€“N destinations simultaneously
  • ๐ŸŒ NTRIP v1.0 + v2.0: Push corrections to any NTRIP caster (RTK2go, Onocoy, rtkdirect)
  • ๐Ÿ“ก TCP Rebroadcast Server: Serve corrections to LAN rovers via TCP
  • ๐Ÿ” Sure-Path Protocol: Custom INIT:user:pass* auth with $HB$ heartbeat monitoring
  • ๐Ÿ”Œ Multiple Input Sources: TCP (RTKBase), Serial UART, USB Serial, Bluetooth GPS
  • ๐ŸŽฏ Per-Destination Filtering: Pass-all, allowlist, or blocklist RTCM message types per destination
  • ๐Ÿ“Š Per-Destination Prometheus Metrics: Individual throughput, errors, queue depth per destination
  • ๐Ÿ”„ Automatic Recovery: Exponential backoff reconnection per destination โ€” independent fault isolation
  • ๐Ÿ”ง Self-Healing Bluetooth: Automatic Bluetooth GPS recovery without manual intervention
  • ๐Ÿ›ก๏ธ Production Ready: Systemd integration, structured logging, comprehensive error handling
  • ๐Ÿงช Well Tested: 88% code coverage with 1,106 passing tests
  • ๐Ÿงฉ Embeddable Library (v2.1): RelayEngine API for programmatic control by external applications
  • ๐Ÿ“ข Real-Time Events (v2.1): EventBus with typed events for state change notifications
  • ๐Ÿ”Œ Hot Plug Destinations (v2.1): Add/remove/start/stop destinations while running

Architecture

                              โ”Œโ”€โ”€โ–ถ [SurePath Thread] โ”€โ”€โ–ถ Sure-Path Server
[Input Source] โ”€โ”€โ–ถ [BroadcastHub] โ”€โ”คโ”€โ”€โ–ถ [NTRIP Thread]   โ”€โ”€โ–ถ RTK2go / Onocoy / etc.
  TCP / Serial       Fan-out    โ”‚โ”€โ”€โ–ถ [NTRIP Thread]   โ”€โ”€โ–ถ rtkdirect
  / Bluetooth       + Filter    โ””โ”€โ”€โ–ถ [TCP Srv Thread]  โ”€โ”€โ–ถ LAN Rover Clients
                                        โ”‚
                                [Prometheus Metrics] โ—„โ”€โ”€โ”€โ”€ per-destination labels

Each destination runs in its own thread with an independent queue, so a failure in one destination never affects the others.

Quick Start

Installation

git clone https://github.com/rodenj1/sp-rtk-base-relay.git
cd sp-rtk-base-relay
sudo ./tools/install.sh

Configuration

Edit /etc/sp-rtk-base-relay/config.yaml (or see config.example.yaml):

input:
  source: "tcp"
  config:
    host: "192.168.1.100"
    port: 3000

destinations:
  - name: surepath
    type: surepath
    enabled: true
    filter:
      mode: pass_all
    config:
      host: "server.example.com"
      port: 50010
      username: "USER01"
      password: "abc1"

  - name: rtk2go
    type: ntrip
    enabled: true
    filter:
      mode: pass_all
    config:
      caster: "rtk2go.com"
      port: 2101
      mountpoint: "MY_MOUNT"
      password: "my_password"
      version: "2.0"

  - name: local_tcp
    type: tcp_server
    enabled: false
    filter:
      mode: pass_all
    config:
      host: "0.0.0.0"
      port: 5016
      max_clients: 10

metrics:
  enabled: true
  port: 8080

logging:
  level: "INFO"
  format: "json"

Running

# As a systemd service
sudo systemctl start sp-rtk-base-relay

# Or foreground
sp-rtk-base-relay --config config.yaml --foreground

Destination Types

Sure-Path (surepath)

Custom proprietary protocol with INIT:user:pass* authentication and $HB$ heartbeat monitoring. Wraps the battle-tested v1.x RTCMClient.

NTRIP (ntrip)

Pushes RTCM corrections to NTRIP casters. Supports both protocol versions:

  • v2.0 (default): HTTP POST + Basic auth + chunked transfer encoding
  • v1.0: SOURCE auth + raw binary streaming

Tested against RTK2go, Onocoy, and rtkdirect.

TCP Server (tcp_server)

Local TCP rebroadcast server for LAN clients. Multiple rovers can connect and receive corrections simultaneously. Features max_clients enforcement and per-client write timeout for backpressure handling.

Message Filtering

Each destination can independently filter RTCM messages by type ID:

filter:
  mode: pass_all          # Forward everything (zero overhead)

filter:
  mode: allowlist         # Only forward these message types
  message_ids: [1005, 1077, 1087, 1097, 1127]

filter:
  mode: blocklist         # Forward everything except these
  message_ids: [4072]     # Drop proprietary messages

Monitoring

Prometheus Metrics

Per-destination metrics with {destination="..."} labels:

Metric Type Description
sp_rtk_base_relay_dest_bytes_sent_total Counter Bytes sent per destination
sp_rtk_base_relay_dest_messages_sent_total Counter Messages sent per destination
sp_rtk_base_relay_dest_messages_dropped_total Counter Queue overflow drops per destination
sp_rtk_base_relay_dest_connection_status Gauge Connection state (1/0) per destination
sp_rtk_base_relay_dest_errors_total Counter Errors per destination
sp_rtk_base_relay_dest_queue_depth Gauge Queue depth per destination
sp_rtk_base_relay_input_connection_status Gauge Input source connection state
sp_rtk_base_relay_input_seconds_since_last_data Gauge No-data watchdog
sp_rtk_base_relay_tcp_server_connected_clients Gauge TCP server client count
sp_rtk_base_relay_service_uptime_seconds Gauge Service uptime

Grafana Dashboard

Import templates/grafana_dashboard.json for a pre-built v2 dashboard with per-destination panels, throughput graphs, and the no-data watchdog.

Input Sources

Source Use Case Config Key
tcp RTKBase integration, network base stations host, port, timeout
serial Direct GNSS receiver via UART port, baudrate
usb_serial USB-to-serial adapters port, baudrate
bluetooth Bluetooth GPS devices device_address, channel

Project Structure

sp-rtk-base-relay/
โ”œโ”€โ”€ src/sp_rtk_base_relay/
โ”‚   โ”œโ”€โ”€ main.py                      # Service orchestration (v2)
โ”‚   โ”œโ”€โ”€ config.py                    # YAML config with destinations: list
โ”‚   โ”œโ”€โ”€ metrics.py                   # Per-destination Prometheus metrics
โ”‚   โ”œโ”€โ”€ exceptions.py                # DestinationError, NtripError, etc.
โ”‚   โ”œโ”€โ”€ logger.py                    # Structured logging
โ”‚   โ”œโ”€โ”€ rtcm_decoder.py             # RTCM 3.x frame parser
โ”‚   โ””โ”€โ”€ core/
โ”‚       โ”œโ”€โ”€ broadcast_hub.py         # Fan-out coordinator (input โ†’ N queues)
โ”‚       โ”œโ”€โ”€ message_filter.py        # Per-destination RTCM filtering
โ”‚       โ”œโ”€โ”€ rtcm_client.py           # Sure-Path protocol client
โ”‚       โ”œโ”€โ”€ connection_states.py     # Connection state machine
โ”‚       โ”œโ”€โ”€ bluetooth_manager.py     # Bluetooth GPS recovery
โ”‚       โ”œโ”€โ”€ destinations/
โ”‚       โ”‚   โ”œโ”€โ”€ base_destination.py       # ABC + queue + stats
โ”‚       โ”‚   โ”œโ”€โ”€ destination_factory.py    # Registry-based factory
โ”‚       โ”‚   โ”œโ”€โ”€ surepath_destination.py   # Sure-Path server
โ”‚       โ”‚   โ”œโ”€โ”€ ntrip_destination.py      # NTRIP v1.0 + v2.0
โ”‚       โ”‚   โ””โ”€โ”€ tcp_server_destination.py # Async TCP rebroadcast
โ”‚       โ””โ”€โ”€ input_sources/
โ”‚           โ”œโ”€โ”€ base_input.py        # Input ABC
โ”‚           โ”œโ”€โ”€ tcp_input.py         # TCP input
โ”‚           โ”œโ”€โ”€ serial_input.py      # Serial input
โ”‚           โ””โ”€โ”€ bluetooth_input.py   # Bluetooth input
โ”œโ”€โ”€ tests/                           # 942 tests, 88% coverage
โ”‚   โ”œโ”€โ”€ unit/                        # Unit tests (26 test files)
โ”‚   โ”œโ”€โ”€ integration/                 # Hardware integration tests
โ”‚   โ”œโ”€โ”€ manual/                      # Manual production tests
โ”‚   โ””โ”€โ”€ fixtures/                    # Mock servers and generators
โ”œโ”€โ”€ tools/                           # Install/uninstall, systemd, bluetooth
โ”œโ”€โ”€ templates/                       # Grafana dashboard v2
โ”œโ”€โ”€ docs/                            # Documentation
โ”‚   โ”œโ”€โ”€ v2-architecture-plan.md      # Full architecture + design decisions
โ”‚   โ”œโ”€โ”€ deployment-guide.md          # Installation & deployment
โ”‚   โ”œโ”€โ”€ metrics-guide.md             # Prometheus metrics reference
โ”‚   โ””โ”€โ”€ bluetooth-*.md               # Bluetooth GPS guides
โ””โ”€โ”€ config.example.yaml              # v2 configuration template

Development

# Clone and install
git clone https://github.com/rodenj1/sp-rtk-base-relay.git
cd sp-rtk-base-relay
uv sync --all-extras
source .venv/bin/activate

# Run tests
uv run pytest

# Run with coverage
uv run pytest --cov=src/sp_rtk_base_relay --cov-report=html

Code Quality Standards

  • Python 3.10+ with modern type hints (dict, list, X | None)
  • PEP8 code style, pyright strict mode
  • 88% test coverage (942 tests)

  • UV package management

CLI Usage

sp-rtk-base-relay [OPTIONS]

Options:
  --version                Show version and exit
  -c, --config PATH        Configuration file path
  --validate               Validate configuration and exit
  --generate-config        Generate example configuration
  --foreground             Run in foreground
  --log-level LEVEL        Override log level

Embedded Usage (v2.1)

SP-Base-Relay can be used as a Python library by external applications (e.g., GPS configuration UIs). The RelayEngine facade provides full programmatic control:

from sp_rtk_base_relay import RelayEngine
from sp_rtk_base_relay.config import InputConfig, DestinationConfig

# 1. Create engine with input source
engine = RelayEngine(InputConfig(source="tcp", config={"host": "192.168.1.100", "port": 3000}))

# 2. Start with destinations
engine.start([
    DestinationConfig(name="rtk2go", type="ntrip", enabled=True,
                      config={"caster": "rtk2go.com", "port": 2101,
                              "mountpoint": "MY_MOUNT", "password": "pass"}),
])

# 3. Hot-add a destination while running
engine.add_destination(DestinationConfig(
    name="local_tcp", type="tcp_server", enabled=True,
    config={"host": "0.0.0.0", "port": 5016}
))

# 4. Get typed status snapshot
status = engine.get_status()
print(f"Running: {status.is_running}, Destinations: {len(status.destinations)}")

# 5. Subscribe to real-time events
sub = engine.subscribe_events()
event = sub.get_event(timeout=1.0)
if event:
    print(f"Event: {event.event_type} โ€” {event.message}")
sub.close()

# 6. Stop when done (releases serial port for other tools)
engine.stop()

Exported API

Symbol Description
RelayEngine High-level facade โ€” start/stop/manage relay
EventBus Pub/sub event system
EventSubscription Per-subscriber event queue (iterable)
RelayEvent Typed event (event_type, message, timestamp, payload)
RelayStatus Frozen status snapshot
DestinationStatus Per-destination status
InputStatus Input source status

See Relay Engine API Spec for the full technical specification.

Migration from v1.x

v2.0 is a breaking change. Key differences:

v1.x v2.0
server: config key destinations: list
Single destination 1โ€“N destinations
Global metrics Per-destination {destination="..."} labels
DataPipelineCoordinator BroadcastHub + DestinationFactory
v1 Grafana dashboard v2 dashboard with $destination variable

See config.example.yaml for the new format. Old server: configs are detected with a clear migration error message.

Releasing

Releases to PyPI are fully automated via .github/workflows/release.yml. Bump the version in pyproject.toml, push a vX.Y.Z tag, then publish a GitHub Release โ€” the workflow re-runs the full test matrix, builds, and publishes to PyPI via Trusted Publishing (OIDC, no API tokens). See docs/release-process.md for the per-release checklist and the one-time PyPI / GitHub-environment setup.

Documentation

License

MIT License โ€” see LICENSE.

Project Status

Current Version: 2.1.0

  • โœ… Multi-destination broadcast (Sure-Path, NTRIP v1.0/v2.0, TCP server)
  • โœ… Per-destination message filtering and Prometheus metrics
  • โœ… BroadcastHub fan-out architecture with independent fault isolation
  • โœ… Embeddable RelayEngine API with EventBus and typed status (v2.1)
  • โœ… 1,106 tests, 88% coverage, production-stable

Made with โค๏ธ for the RTK GPS community

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sp_rtk_base_relay-2.1.0.tar.gz (87.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sp_rtk_base_relay-2.1.0-py3-none-any.whl (105.6 kB view details)

Uploaded Python 3

File details

Details for the file sp_rtk_base_relay-2.1.0.tar.gz.

File metadata

  • Download URL: sp_rtk_base_relay-2.1.0.tar.gz
  • Upload date:
  • Size: 87.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for sp_rtk_base_relay-2.1.0.tar.gz
Algorithm Hash digest
SHA256 8dc3f6695675fe16fe19e6b964344940683715f3dded90d3623d18e0288127d6
MD5 416b41f334cc374a2446880531ca9276
BLAKE2b-256 f14268b26ea2ebb476ba74e78d321afdcc168178981b50fac4c71a9cfa255363

See more details on using hashes here.

Provenance

The following attestation bundles were made for sp_rtk_base_relay-2.1.0.tar.gz:

Publisher: release.yml on rodenj1/sp-rtk-base-relay

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sp_rtk_base_relay-2.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for sp_rtk_base_relay-2.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b39375e1701b8d180bb246d53a4c19d4ba6c73529c2c772343f6d0143265d27f
MD5 e845b6fe8a0614a6a37460d7f43b5c45
BLAKE2b-256 e45af501cc524315f1af6f43a7ae666030f45811f22fc6d85c40f15911fca4ef

See more details on using hashes here.

Provenance

The following attestation bundles were made for sp_rtk_base_relay-2.1.0-py3-none-any.whl:

Publisher: release.yml on rodenj1/sp-rtk-base-relay

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page