sp-rtk-base 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. sp_rtk_base-0.2.0/PKG-INFO +306 -0
  2. sp_rtk_base-0.2.0/README.md +272 -0
  3. sp_rtk_base-0.2.0/pyproject.toml +271 -0
  4. sp_rtk_base-0.2.0/src/sp_rtk_base/__init__.py +5 -0
  5. sp_rtk_base-0.2.0/src/sp_rtk_base/api/__init__.py +3 -0
  6. sp_rtk_base-0.2.0/src/sp_rtk_base/api/config.py +99 -0
  7. sp_rtk_base-0.2.0/src/sp_rtk_base/api/destinations.py +161 -0
  8. sp_rtk_base-0.2.0/src/sp_rtk_base/api/device.py +489 -0
  9. sp_rtk_base-0.2.0/src/sp_rtk_base/api/events.py +74 -0
  10. sp_rtk_base-0.2.0/src/sp_rtk_base/api/health.py +22 -0
  11. sp_rtk_base-0.2.0/src/sp_rtk_base/api/metrics.py +67 -0
  12. sp_rtk_base-0.2.0/src/sp_rtk_base/api/relay.py +135 -0
  13. sp_rtk_base-0.2.0/src/sp_rtk_base/api/settings.py +124 -0
  14. sp_rtk_base-0.2.0/src/sp_rtk_base/app.py +103 -0
  15. sp_rtk_base-0.2.0/src/sp_rtk_base/cli/__init__.py +1 -0
  16. sp_rtk_base-0.2.0/src/sp_rtk_base/cli/config_audit.py +601 -0
  17. sp_rtk_base-0.2.0/src/sp_rtk_base/main.py +35 -0
  18. sp_rtk_base-0.2.0/src/sp_rtk_base/models/__init__.py +3 -0
  19. sp_rtk_base-0.2.0/src/sp_rtk_base/models/api_models.py +198 -0
  20. sp_rtk_base-0.2.0/src/sp_rtk_base/models/config_models.py +300 -0
  21. sp_rtk_base-0.2.0/src/sp_rtk_base/models/device_models.py +405 -0
  22. sp_rtk_base-0.2.0/src/sp_rtk_base/py.typed +0 -0
  23. sp_rtk_base-0.2.0/src/sp_rtk_base/services/__init__.py +123 -0
  24. sp_rtk_base-0.2.0/src/sp_rtk_base/services/config_service.py +322 -0
  25. sp_rtk_base-0.2.0/src/sp_rtk_base/services/device_service.py +473 -0
  26. sp_rtk_base-0.2.0/src/sp_rtk_base/services/drivers/__init__.py +88 -0
  27. sp_rtk_base-0.2.0/src/sp_rtk_base/services/drivers/base.py +296 -0
  28. sp_rtk_base-0.2.0/src/sp_rtk_base/services/drivers/ublox.py +1029 -0
  29. sp_rtk_base-0.2.0/src/sp_rtk_base/services/event_bridge.py +198 -0
  30. sp_rtk_base-0.2.0/src/sp_rtk_base/services/metrics_service.py +182 -0
  31. sp_rtk_base-0.2.0/src/sp_rtk_base/services/relay_service.py +233 -0
  32. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/__init__.py +3 -0
  33. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/components/__init__.py +7 -0
  34. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/components/status_card.py +37 -0
  35. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/layout.py +153 -0
  36. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/__init__.py +3 -0
  37. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/dashboard.py +412 -0
  38. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/gps_config.py +647 -0
  39. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/input.py +637 -0
  40. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/outputs.py +402 -0
  41. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/settings.py +111 -0
  42. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/survey.py +1085 -0
  43. sp_rtk_base-0.2.0/src/sp_rtk_base/ui/validators.py +109 -0
@@ -0,0 +1,306 @@
1
+ Metadata-Version: 2.3
2
+ Name: sp-rtk-base
3
+ Version: 0.2.0
4
+ Summary: Web UI and API for controlling and monitoring a u-blox GPS RTK base station relay
5
+ Keywords: rtcm,gps,gnss,rtk,base-station,ublox,ntrip,fastapi,nicegui
6
+ Author: John Roden
7
+ Author-email: John Roden <rodenj@gmail.com>
8
+ License: MIT
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: System Administrators
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering :: GIS
19
+ Classifier: Topic :: System :: Networking
20
+ Requires-Dist: fastapi>=0.135.3
21
+ Requires-Dist: nicegui>=3.9.0
22
+ Requires-Dist: pydantic>=2.12.5
23
+ Requires-Dist: pyserial>=3.5
24
+ Requires-Dist: pyubx2>=1.2.43
25
+ Requires-Dist: pyyaml>=6.0.3
26
+ Requires-Dist: sp-rtk-base-relay>=2.1.1
27
+ Requires-Dist: uvicorn>=0.42.0
28
+ Requires-Python: >=3.10
29
+ Project-URL: Homepage, https://github.com/rodenj1/sp-rtk-base
30
+ Project-URL: Documentation, https://github.com/rodenj1/sp-rtk-base#readme
31
+ Project-URL: Repository, https://github.com/rodenj1/sp-rtk-base
32
+ Project-URL: Issues, https://github.com/rodenj1/sp-rtk-base/issues
33
+ Description-Content-Type: text/markdown
34
+
35
+ # SP-Base
36
+
37
+ [![CI](https://github.com/rodenj1/sp-rtk-base/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/rodenj1/sp-rtk-base/actions/workflows/ci.yml)
38
+ [![codecov](https://codecov.io/gh/rodenj1/sp-rtk-base/branch/main/graph/badge.svg)](https://codecov.io/gh/rodenj1/sp-rtk-base)
39
+ [![PyPI version](https://img.shields.io/pypi/v/sp-rtk-base.svg)](https://pypi.org/project/sp-rtk-base/)
40
+ [![Python versions](https://img.shields.io/pypi/pyversions/sp-rtk-base.svg)](https://pypi.org/project/sp-rtk-base/)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
42
+ [![Code style: ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
43
+ [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://www.conventionalcommits.org/en/v1.0.0/)
44
+
45
+ Web UI and REST API for configuring and monitoring a u-blox GPS RTK base station and its RTCM correction data relay.
46
+
47
+ SP-Base wraps the [sp-rtk-base-relay](packages/sp-rtk-base-relay/) engine with a browser-based operator console, adds full u-blox device configuration (survey-in, fixed base, GNSS constellations, RTCM message selection), and exposes everything through a REST API — all from a phone, tablet, or desktop browser.
48
+
49
+ ## Features
50
+
51
+ ### Relay Control
52
+ - **Dashboard** — real-time relay status, input metrics, throughput stats, GPS device summary, live event log
53
+ - **Destination Management** — add/edit/delete/hot-toggle SurePath, NTRIP, and TCP Server outputs (zero-downtime)
54
+ - **Input Sources** — configure TCP, serial, or Bluetooth RTCM input (serial port auto-detect with GPS flagging, Bluetooth scan + pair + test-connection)
55
+ - **Config Import/Export** — YAML download/upload with validation
56
+
57
+ ### GPS Device Management (u-blox)
58
+ - **Connect / Disconnect** — serial port auto-detect (u-blox / FTDI / Prolific / Silicon Labs flagged with ⭐), driver selector, MON-VER device info
59
+ - **Survey-In** — configure duration + accuracy target, live convergence chart (ECharts), auto-promote to fixed base on completion + save-to-flash
60
+ - **Fixed Base** — read-back current config, edit/commit coordinates, save-to-flash
61
+ - **Named Position Profiles** — save surveyed or manual base positions to YAML, restore them directly to device RAM + flash
62
+ - **Live Position** — NAV-PVT display (fix type, RTK status, lat/lon/alt, accuracy, satellites, speed, heading, PDOP) auto-polled every 2 s
63
+ - **GNSS Constellation Selection** — toggle GPS / GLONASS / Galileo / BeiDou / SBAS / QZSS
64
+ - **RTCM Message Selection** — per-port RTCM3 message enable/disable
65
+ - **Save to Flash** — persist any configuration change for reboot survival
66
+ - **Device → Relay Handoff** — disconnect device and start the relay on the same serial port with one click
67
+
68
+ ### Monitoring & API
69
+ - **REST API** — full programmatic control (relay, destinations, settings, events, device, config)
70
+ - **Prometheus Metrics** — `GET /metrics` endpoint for Grafana / Prometheus monitoring
71
+ - **WebSocket Events** — real-time event streaming at `WS /api/events/ws`
72
+ - **Responsive UI** — mobile-first layout, 44 px touch targets, tablet/desktop breakpoints
73
+
74
+ ## Quick Start
75
+
76
+ ### Prerequisites
77
+
78
+ - Python 3.10+
79
+ - [UV](https://docs.astral.sh/uv/) package manager
80
+ - (Optional) BlueZ + `dbus-fast` on Linux for Bluetooth RTCM input
81
+
82
+ ### Install & Run
83
+
84
+ ```bash
85
+ # Clone and install dependencies
86
+ git clone https://github.com/rodenj1/sp-rtk-base.git
87
+ cd sp-rtk-base
88
+ uv sync
89
+
90
+ # Start the application
91
+ uv run sp-rtk-base
92
+ ```
93
+
94
+ Open **http://localhost:8080** in your browser.
95
+
96
+ ### Demo Mode
97
+
98
+ Run with a simulated RTCM source and mock destinations (no hardware needed):
99
+
100
+ ```bash
101
+ uv run python tools/demo_with_simulator.py
102
+ ```
103
+
104
+ This starts:
105
+ - **TCP source simulator** — streams synthetic RTCM3 data on port 19800
106
+ - **Mock NTRIP caster** — accepts NTRIP v1.0 connections (ephemeral port)
107
+ - **SP-Base** — pre-configured with TCP input, a `tcp_server` destination on port 19876, and an NTRIP destination pointing to the mock caster
108
+
109
+ The relay auto-starts on launch. Open **http://localhost:8080** and you'll see live data flowing on the Dashboard immediately.
110
+
111
+ ### Local NTRIP Caster (Dev / Testing)
112
+
113
+ A lightweight Python asyncio NTRIP caster is included for developing and testing NTRIP v1.0 and v2.0 destinations without depending on a public caster:
114
+
115
+ ```bash
116
+ cd docker/ntrip-caster
117
+ docker compose up
118
+ ```
119
+
120
+ See [`docs/local-ntrip-caster.md`](docs/local-ntrip-caster.md) for auth, mountpoint, and protocol details.
121
+
122
+ ## UI Pages
123
+
124
+ | Route | Page | Purpose |
125
+ |-------|------|---------|
126
+ | `/` | **Dashboard** | Relay status, input/output metrics, GPS device summary, event log |
127
+ | `/input` | **Input** | Input source config (TCP / serial / Bluetooth) with discovery helpers |
128
+ | `/outputs` | **Outputs** | Destination CRUD + enable/disable toggles |
129
+ | `/survey` | **Survey-In** | Connect to GPS, run survey-in with live chart, fixed-base config, position profiles |
130
+ | `/settings` | **Settings** | Application settings (auto-start, dark mode, metrics toggle) |
131
+ | `/gps-config` | **Advanced GPS** | RTCM message selection, GNSS constellations, save-to-flash, relay handoff |
132
+
133
+ ## API Endpoints
134
+
135
+ ### Health
136
+ | Method | Path | Description |
137
+ |--------|------|-------------|
138
+ | `GET` | `/api/health` | Health check |
139
+
140
+ ### Relay
141
+ | Method | Path | Description |
142
+ |--------|------|-------------|
143
+ | `GET` | `/api/relay/status` | Relay engine status |
144
+ | `POST` | `/api/relay/start` | Start the relay |
145
+ | `POST` | `/api/relay/stop` | Stop the relay |
146
+
147
+ ### Destinations
148
+ | Method | Path | Description |
149
+ |--------|------|-------------|
150
+ | `GET` | `/api/destinations` | List all destinations |
151
+ | `POST` | `/api/destinations` | Create a destination |
152
+ | `GET` | `/api/destinations/{name}` | Get destination details |
153
+ | `PUT` | `/api/destinations/{name}` | Update a destination |
154
+ | `DELETE` | `/api/destinations/{name}` | Delete a destination |
155
+
156
+ ### Settings
157
+ | Method | Path | Description |
158
+ |--------|------|-------------|
159
+ | `GET` | `/api/settings` | Get application settings |
160
+ | `PUT` | `/api/settings` | Update application settings |
161
+ | `GET` | `/api/input` | Get input source config |
162
+ | `PUT` | `/api/input` | Update input source config |
163
+
164
+ ### Events
165
+ | Method | Path | Description |
166
+ |--------|------|-------------|
167
+ | `GET` | `/api/events` | Poll recent events |
168
+ | `WS` | `/api/events/ws` | WebSocket event stream |
169
+
170
+ ### Metrics & Config
171
+ | Method | Path | Description |
172
+ |--------|------|-------------|
173
+ | `GET` | `/metrics` | Prometheus metrics |
174
+ | `GET` | `/api/config/export` | Download full YAML configuration |
175
+ | `POST` | `/api/config/import` | Upload and validate a YAML configuration |
176
+
177
+ ### GPS Device (u-blox)
178
+ | Method | Path | Description |
179
+ |--------|------|-------------|
180
+ | `GET` | `/api/device/ports` | List available serial ports (GPS-flagged) |
181
+ | `POST` | `/api/device/connect` | Connect to GPS receiver |
182
+ | `POST` | `/api/device/disconnect` | Disconnect from GPS receiver |
183
+ | `GET` | `/api/device/status` | Device connection state + MON-VER info |
184
+ | `GET` | `/api/device/capabilities` | List driver capabilities |
185
+ | `GET` | `/api/device/position` | Live NAV-PVT position snapshot |
186
+ | `GET` | `/api/device/survey-in` | Survey-in progress (NAV-SVIN) |
187
+ | `POST` | `/api/device/configure/survey-in` | Start survey-in mode |
188
+ | `POST` | `/api/device/configure/fixed-base` | Configure fixed-base coordinates |
189
+ | `POST` | `/api/device/configure/rtcm` | Configure RTCM output messages |
190
+ | `GET` | `/api/device/base-config` | Read current base mode (survey/fixed/disabled) |
191
+ | `GET` | `/api/device/gnss` | Get GNSS constellation config |
192
+ | `PUT` | `/api/device/gnss` | Update GNSS constellation config |
193
+ | `POST` | `/api/device/save` | Save current config to device flash |
194
+ | `POST` | `/api/device/promote-survey-in` | Promote completed survey-in to fixed base |
195
+ | `POST` | `/api/device/handoff` | Disconnect device and start relay on same serial port |
196
+ | `GET` | `/api/device/base-positions` | List saved base-station position profiles |
197
+ | `POST` | `/api/device/base-positions` | Save a named base-station position |
198
+ | `DELETE` | `/api/device/base-positions/{name}` | Delete a saved position |
199
+ | `POST` | `/api/device/base-positions/{name}/restore` | Restore a saved position to the device |
200
+
201
+ ## Prometheus Integration
202
+
203
+ SP-Base serves Prometheus metrics at `GET /metrics` on the same port (8080).
204
+
205
+ Example `prometheus.yml` scrape config:
206
+
207
+ ```yaml
208
+ scrape_configs:
209
+ - job_name: "sp-rtk-base"
210
+ scrape_interval: 10s
211
+ static_configs:
212
+ - targets: ["localhost:8080"]
213
+ ```
214
+
215
+ Key metrics include:
216
+ - `sp_rtk_base_relay_running` — relay engine state (1/0)
217
+ - `sp_rtk_base_relay_uptime_seconds` — engine uptime
218
+ - `sp_rtk_base_input_connected` — input source connection state
219
+ - `sp_rtk_base_input_bytes_received` — total bytes from input
220
+ - `sp_rtk_base_active_destinations` / `sp_rtk_base_total_destinations`
221
+ - `sp_rtk_base_dest_connected{destination="name"}` — per-destination status
222
+ - `sp_rtk_base_dest_bytes_sent{destination="name"}` — per-destination throughput
223
+
224
+ ## Development
225
+
226
+ ### Run Tests
227
+
228
+ ```bash
229
+ # Unit tests
230
+ uv run pytest tests/unit/
231
+
232
+ # Integration tests (real relay engine + TCP simulator)
233
+ uv run pytest tests/integration/ --no-cov
234
+
235
+ # All tests
236
+ uv run pytest
237
+ ```
238
+
239
+ ### Type Checking
240
+
241
+ ```bash
242
+ uv run pyright src/
243
+ ```
244
+
245
+ ### Quality Snapshot
246
+ - **Unit tests**: 480 passing
247
+ - **Integration tests**: 20+ (end-to-end + destination management + NTRIP)
248
+ - **Coverage**: 92.28% on measured code (NiceGUI UI pages excluded — they can't be meaningfully unit-tested)
249
+ - **Pyright (strict)**: 0 errors, 0 warnings
250
+ - **Python**: 3.10+ with modern type hints (`dict`, `list`, `X | None`)
251
+
252
+ ### Project Structure
253
+
254
+ ```
255
+ sp-rtk-base/
256
+ ├── src/sp_rtk_base/
257
+ │ ├── api/ # FastAPI REST endpoints
258
+ │ │ ├── config.py # YAML import/export
259
+ │ │ ├── destinations.py # Destination CRUD
260
+ │ │ ├── device.py # GPS device endpoints
261
+ │ │ ├── events.py # Events + WebSocket
262
+ │ │ ├── health.py
263
+ │ │ ├── metrics.py # Prometheus
264
+ │ │ ├── relay.py
265
+ │ │ └── settings.py
266
+ │ ├── models/ # Pydantic config, device & API models
267
+ │ ├── services/ # Business logic
268
+ │ │ ├── config_service.py
269
+ │ │ ├── device_service.py
270
+ │ │ ├── event_bridge.py
271
+ │ │ ├── metrics_service.py
272
+ │ │ ├── relay_service.py
273
+ │ │ └── drivers/ # GPS driver layer
274
+ │ │ ├── base.py # GpsReceiverDriver ABC
275
+ │ │ └── ublox.py # u-blox driver (PyUBX2)
276
+ │ └── ui/ # NiceGUI browser UI
277
+ │ ├── layout.py # Shared navigation layout
278
+ │ ├── validators.py # Shared form validators
279
+ │ ├── components/ # Reusable UI components
280
+ │ └── pages/ # Dashboard, Input, Outputs, Survey, Settings, GPS Config
281
+ ├── tests/
282
+ │ ├── unit/ # Fast unit tests with mocks
283
+ │ ├── integration/ # End-to-end tests with real relay
284
+ │ └── fixtures/ # TCP simulators, mock NTRIP caster, test helpers
285
+ ├── packages/
286
+ │ └── sp-rtk-base-relay/ # RTCM relay engine (workspace dependency)
287
+ ├── docker/
288
+ │ └── ntrip-caster/ # Local NTRIP caster for dev/testing
289
+ ├── docs/ # Architecture, planning, device config reference
290
+ └── tools/ # Demo and hardware test scripts
291
+ ```
292
+
293
+ ## Architecture
294
+
295
+ SP-Base is built on **FastAPI** (REST API + WebSocket) and **NiceGUI** (browser UI) sharing a single ASGI server on port 8080. Core components:
296
+
297
+ - **sp-rtk-base-relay** — RTCM relay engine with TCP / serial / Bluetooth inputs and SurePath / NTRIP / TCP Server outputs, hot destination management, and in-process event bus
298
+ - **GPS driver layer** — abstract `GpsReceiverDriver` base with a u-blox implementation via **PyUBX2** (UBX-MON-VER, CFG-VALSET/VALGET, NAV-PVT, NAV-SVIN, CFG-GNSS, survey-in, fixed-base, RTCM selection, save-to-flash)
299
+ - **Services** — async orchestrators bridging the synchronous relay engine and GPS driver to FastAPI's event loop via `asyncio.to_thread()` and daemon-thread event queues
300
+ - **Operator console** — 6-page workflow (Dashboard → Input → Outputs → Survey-In → Settings → Advanced GPS) driven by a shared navigation layout; the browser renders status and requests actions, while authoritative runtime state lives in the relay engine and backend services
301
+
302
+ Graceful shutdown is wired through `app.on_shutdown` so Ctrl+C cleanly stops the event bridge, relay engine, destination threads, and any active GPS connection before uvicorn exits.
303
+
304
+ ## License
305
+
306
+ MIT
@@ -0,0 +1,272 @@
1
+ # SP-Base
2
+
3
+ [![CI](https://github.com/rodenj1/sp-rtk-base/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/rodenj1/sp-rtk-base/actions/workflows/ci.yml)
4
+ [![codecov](https://codecov.io/gh/rodenj1/sp-rtk-base/branch/main/graph/badge.svg)](https://codecov.io/gh/rodenj1/sp-rtk-base)
5
+ [![PyPI version](https://img.shields.io/pypi/v/sp-rtk-base.svg)](https://pypi.org/project/sp-rtk-base/)
6
+ [![Python versions](https://img.shields.io/pypi/pyversions/sp-rtk-base.svg)](https://pypi.org/project/sp-rtk-base/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
+ [![Code style: ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
9
+ [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://www.conventionalcommits.org/en/v1.0.0/)
10
+
11
+ Web UI and REST API for configuring and monitoring a u-blox GPS RTK base station and its RTCM correction data relay.
12
+
13
+ SP-Base wraps the [sp-rtk-base-relay](packages/sp-rtk-base-relay/) engine with a browser-based operator console, adds full u-blox device configuration (survey-in, fixed base, GNSS constellations, RTCM message selection), and exposes everything through a REST API — all from a phone, tablet, or desktop browser.
14
+
15
+ ## Features
16
+
17
+ ### Relay Control
18
+ - **Dashboard** — real-time relay status, input metrics, throughput stats, GPS device summary, live event log
19
+ - **Destination Management** — add/edit/delete/hot-toggle SurePath, NTRIP, and TCP Server outputs (zero-downtime)
20
+ - **Input Sources** — configure TCP, serial, or Bluetooth RTCM input (serial port auto-detect with GPS flagging, Bluetooth scan + pair + test-connection)
21
+ - **Config Import/Export** — YAML download/upload with validation
22
+
23
+ ### GPS Device Management (u-blox)
24
+ - **Connect / Disconnect** — serial port auto-detect (u-blox / FTDI / Prolific / Silicon Labs flagged with ⭐), driver selector, MON-VER device info
25
+ - **Survey-In** — configure duration + accuracy target, live convergence chart (ECharts), auto-promote to fixed base on completion + save-to-flash
26
+ - **Fixed Base** — read-back current config, edit/commit coordinates, save-to-flash
27
+ - **Named Position Profiles** — save surveyed or manual base positions to YAML, restore them directly to device RAM + flash
28
+ - **Live Position** — NAV-PVT display (fix type, RTK status, lat/lon/alt, accuracy, satellites, speed, heading, PDOP) auto-polled every 2 s
29
+ - **GNSS Constellation Selection** — toggle GPS / GLONASS / Galileo / BeiDou / SBAS / QZSS
30
+ - **RTCM Message Selection** — per-port RTCM3 message enable/disable
31
+ - **Save to Flash** — persist any configuration change for reboot survival
32
+ - **Device → Relay Handoff** — disconnect device and start the relay on the same serial port with one click
33
+
34
+ ### Monitoring & API
35
+ - **REST API** — full programmatic control (relay, destinations, settings, events, device, config)
36
+ - **Prometheus Metrics** — `GET /metrics` endpoint for Grafana / Prometheus monitoring
37
+ - **WebSocket Events** — real-time event streaming at `WS /api/events/ws`
38
+ - **Responsive UI** — mobile-first layout, 44 px touch targets, tablet/desktop breakpoints
39
+
40
+ ## Quick Start
41
+
42
+ ### Prerequisites
43
+
44
+ - Python 3.10+
45
+ - [UV](https://docs.astral.sh/uv/) package manager
46
+ - (Optional) BlueZ + `dbus-fast` on Linux for Bluetooth RTCM input
47
+
48
+ ### Install & Run
49
+
50
+ ```bash
51
+ # Clone and install dependencies
52
+ git clone https://github.com/rodenj1/sp-rtk-base.git
53
+ cd sp-rtk-base
54
+ uv sync
55
+
56
+ # Start the application
57
+ uv run sp-rtk-base
58
+ ```
59
+
60
+ Open **http://localhost:8080** in your browser.
61
+
62
+ ### Demo Mode
63
+
64
+ Run with a simulated RTCM source and mock destinations (no hardware needed):
65
+
66
+ ```bash
67
+ uv run python tools/demo_with_simulator.py
68
+ ```
69
+
70
+ This starts:
71
+ - **TCP source simulator** — streams synthetic RTCM3 data on port 19800
72
+ - **Mock NTRIP caster** — accepts NTRIP v1.0 connections (ephemeral port)
73
+ - **SP-Base** — pre-configured with TCP input, a `tcp_server` destination on port 19876, and an NTRIP destination pointing to the mock caster
74
+
75
+ The relay auto-starts on launch. Open **http://localhost:8080** and you'll see live data flowing on the Dashboard immediately.
76
+
77
+ ### Local NTRIP Caster (Dev / Testing)
78
+
79
+ A lightweight Python asyncio NTRIP caster is included for developing and testing NTRIP v1.0 and v2.0 destinations without depending on a public caster:
80
+
81
+ ```bash
82
+ cd docker/ntrip-caster
83
+ docker compose up
84
+ ```
85
+
86
+ See [`docs/local-ntrip-caster.md`](docs/local-ntrip-caster.md) for auth, mountpoint, and protocol details.
87
+
88
+ ## UI Pages
89
+
90
+ | Route | Page | Purpose |
91
+ |-------|------|---------|
92
+ | `/` | **Dashboard** | Relay status, input/output metrics, GPS device summary, event log |
93
+ | `/input` | **Input** | Input source config (TCP / serial / Bluetooth) with discovery helpers |
94
+ | `/outputs` | **Outputs** | Destination CRUD + enable/disable toggles |
95
+ | `/survey` | **Survey-In** | Connect to GPS, run survey-in with live chart, fixed-base config, position profiles |
96
+ | `/settings` | **Settings** | Application settings (auto-start, dark mode, metrics toggle) |
97
+ | `/gps-config` | **Advanced GPS** | RTCM message selection, GNSS constellations, save-to-flash, relay handoff |
98
+
99
+ ## API Endpoints
100
+
101
+ ### Health
102
+ | Method | Path | Description |
103
+ |--------|------|-------------|
104
+ | `GET` | `/api/health` | Health check |
105
+
106
+ ### Relay
107
+ | Method | Path | Description |
108
+ |--------|------|-------------|
109
+ | `GET` | `/api/relay/status` | Relay engine status |
110
+ | `POST` | `/api/relay/start` | Start the relay |
111
+ | `POST` | `/api/relay/stop` | Stop the relay |
112
+
113
+ ### Destinations
114
+ | Method | Path | Description |
115
+ |--------|------|-------------|
116
+ | `GET` | `/api/destinations` | List all destinations |
117
+ | `POST` | `/api/destinations` | Create a destination |
118
+ | `GET` | `/api/destinations/{name}` | Get destination details |
119
+ | `PUT` | `/api/destinations/{name}` | Update a destination |
120
+ | `DELETE` | `/api/destinations/{name}` | Delete a destination |
121
+
122
+ ### Settings
123
+ | Method | Path | Description |
124
+ |--------|------|-------------|
125
+ | `GET` | `/api/settings` | Get application settings |
126
+ | `PUT` | `/api/settings` | Update application settings |
127
+ | `GET` | `/api/input` | Get input source config |
128
+ | `PUT` | `/api/input` | Update input source config |
129
+
130
+ ### Events
131
+ | Method | Path | Description |
132
+ |--------|------|-------------|
133
+ | `GET` | `/api/events` | Poll recent events |
134
+ | `WS` | `/api/events/ws` | WebSocket event stream |
135
+
136
+ ### Metrics & Config
137
+ | Method | Path | Description |
138
+ |--------|------|-------------|
139
+ | `GET` | `/metrics` | Prometheus metrics |
140
+ | `GET` | `/api/config/export` | Download full YAML configuration |
141
+ | `POST` | `/api/config/import` | Upload and validate a YAML configuration |
142
+
143
+ ### GPS Device (u-blox)
144
+ | Method | Path | Description |
145
+ |--------|------|-------------|
146
+ | `GET` | `/api/device/ports` | List available serial ports (GPS-flagged) |
147
+ | `POST` | `/api/device/connect` | Connect to GPS receiver |
148
+ | `POST` | `/api/device/disconnect` | Disconnect from GPS receiver |
149
+ | `GET` | `/api/device/status` | Device connection state + MON-VER info |
150
+ | `GET` | `/api/device/capabilities` | List driver capabilities |
151
+ | `GET` | `/api/device/position` | Live NAV-PVT position snapshot |
152
+ | `GET` | `/api/device/survey-in` | Survey-in progress (NAV-SVIN) |
153
+ | `POST` | `/api/device/configure/survey-in` | Start survey-in mode |
154
+ | `POST` | `/api/device/configure/fixed-base` | Configure fixed-base coordinates |
155
+ | `POST` | `/api/device/configure/rtcm` | Configure RTCM output messages |
156
+ | `GET` | `/api/device/base-config` | Read current base mode (survey/fixed/disabled) |
157
+ | `GET` | `/api/device/gnss` | Get GNSS constellation config |
158
+ | `PUT` | `/api/device/gnss` | Update GNSS constellation config |
159
+ | `POST` | `/api/device/save` | Save current config to device flash |
160
+ | `POST` | `/api/device/promote-survey-in` | Promote completed survey-in to fixed base |
161
+ | `POST` | `/api/device/handoff` | Disconnect device and start relay on same serial port |
162
+ | `GET` | `/api/device/base-positions` | List saved base-station position profiles |
163
+ | `POST` | `/api/device/base-positions` | Save a named base-station position |
164
+ | `DELETE` | `/api/device/base-positions/{name}` | Delete a saved position |
165
+ | `POST` | `/api/device/base-positions/{name}/restore` | Restore a saved position to the device |
166
+
167
+ ## Prometheus Integration
168
+
169
+ SP-Base serves Prometheus metrics at `GET /metrics` on the same port (8080).
170
+
171
+ Example `prometheus.yml` scrape config:
172
+
173
+ ```yaml
174
+ scrape_configs:
175
+ - job_name: "sp-rtk-base"
176
+ scrape_interval: 10s
177
+ static_configs:
178
+ - targets: ["localhost:8080"]
179
+ ```
180
+
181
+ Key metrics include:
182
+ - `sp_rtk_base_relay_running` — relay engine state (1/0)
183
+ - `sp_rtk_base_relay_uptime_seconds` — engine uptime
184
+ - `sp_rtk_base_input_connected` — input source connection state
185
+ - `sp_rtk_base_input_bytes_received` — total bytes from input
186
+ - `sp_rtk_base_active_destinations` / `sp_rtk_base_total_destinations`
187
+ - `sp_rtk_base_dest_connected{destination="name"}` — per-destination status
188
+ - `sp_rtk_base_dest_bytes_sent{destination="name"}` — per-destination throughput
189
+
190
+ ## Development
191
+
192
+ ### Run Tests
193
+
194
+ ```bash
195
+ # Unit tests
196
+ uv run pytest tests/unit/
197
+
198
+ # Integration tests (real relay engine + TCP simulator)
199
+ uv run pytest tests/integration/ --no-cov
200
+
201
+ # All tests
202
+ uv run pytest
203
+ ```
204
+
205
+ ### Type Checking
206
+
207
+ ```bash
208
+ uv run pyright src/
209
+ ```
210
+
211
+ ### Quality Snapshot
212
+ - **Unit tests**: 480 passing
213
+ - **Integration tests**: 20+ (end-to-end + destination management + NTRIP)
214
+ - **Coverage**: 92.28% on measured code (NiceGUI UI pages excluded — they can't be meaningfully unit-tested)
215
+ - **Pyright (strict)**: 0 errors, 0 warnings
216
+ - **Python**: 3.10+ with modern type hints (`dict`, `list`, `X | None`)
217
+
218
+ ### Project Structure
219
+
220
+ ```
221
+ sp-rtk-base/
222
+ ├── src/sp_rtk_base/
223
+ │ ├── api/ # FastAPI REST endpoints
224
+ │ │ ├── config.py # YAML import/export
225
+ │ │ ├── destinations.py # Destination CRUD
226
+ │ │ ├── device.py # GPS device endpoints
227
+ │ │ ├── events.py # Events + WebSocket
228
+ │ │ ├── health.py
229
+ │ │ ├── metrics.py # Prometheus
230
+ │ │ ├── relay.py
231
+ │ │ └── settings.py
232
+ │ ├── models/ # Pydantic config, device & API models
233
+ │ ├── services/ # Business logic
234
+ │ │ ├── config_service.py
235
+ │ │ ├── device_service.py
236
+ │ │ ├── event_bridge.py
237
+ │ │ ├── metrics_service.py
238
+ │ │ ├── relay_service.py
239
+ │ │ └── drivers/ # GPS driver layer
240
+ │ │ ├── base.py # GpsReceiverDriver ABC
241
+ │ │ └── ublox.py # u-blox driver (PyUBX2)
242
+ │ └── ui/ # NiceGUI browser UI
243
+ │ ├── layout.py # Shared navigation layout
244
+ │ ├── validators.py # Shared form validators
245
+ │ ├── components/ # Reusable UI components
246
+ │ └── pages/ # Dashboard, Input, Outputs, Survey, Settings, GPS Config
247
+ ├── tests/
248
+ │ ├── unit/ # Fast unit tests with mocks
249
+ │ ├── integration/ # End-to-end tests with real relay
250
+ │ └── fixtures/ # TCP simulators, mock NTRIP caster, test helpers
251
+ ├── packages/
252
+ │ └── sp-rtk-base-relay/ # RTCM relay engine (workspace dependency)
253
+ ├── docker/
254
+ │ └── ntrip-caster/ # Local NTRIP caster for dev/testing
255
+ ├── docs/ # Architecture, planning, device config reference
256
+ └── tools/ # Demo and hardware test scripts
257
+ ```
258
+
259
+ ## Architecture
260
+
261
+ SP-Base is built on **FastAPI** (REST API + WebSocket) and **NiceGUI** (browser UI) sharing a single ASGI server on port 8080. Core components:
262
+
263
+ - **sp-rtk-base-relay** — RTCM relay engine with TCP / serial / Bluetooth inputs and SurePath / NTRIP / TCP Server outputs, hot destination management, and in-process event bus
264
+ - **GPS driver layer** — abstract `GpsReceiverDriver` base with a u-blox implementation via **PyUBX2** (UBX-MON-VER, CFG-VALSET/VALGET, NAV-PVT, NAV-SVIN, CFG-GNSS, survey-in, fixed-base, RTCM selection, save-to-flash)
265
+ - **Services** — async orchestrators bridging the synchronous relay engine and GPS driver to FastAPI's event loop via `asyncio.to_thread()` and daemon-thread event queues
266
+ - **Operator console** — 6-page workflow (Dashboard → Input → Outputs → Survey-In → Settings → Advanced GPS) driven by a shared navigation layout; the browser renders status and requests actions, while authoritative runtime state lives in the relay engine and backend services
267
+
268
+ Graceful shutdown is wired through `app.on_shutdown` so Ctrl+C cleanly stops the event bridge, relay engine, destination threads, and any active GPS connection before uvicorn exits.
269
+
270
+ ## License
271
+
272
+ MIT