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.
- sp_rtk_base-0.2.0/PKG-INFO +306 -0
- sp_rtk_base-0.2.0/README.md +272 -0
- sp_rtk_base-0.2.0/pyproject.toml +271 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/__init__.py +5 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/api/__init__.py +3 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/api/config.py +99 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/api/destinations.py +161 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/api/device.py +489 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/api/events.py +74 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/api/health.py +22 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/api/metrics.py +67 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/api/relay.py +135 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/api/settings.py +124 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/app.py +103 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/cli/__init__.py +1 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/cli/config_audit.py +601 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/main.py +35 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/models/__init__.py +3 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/models/api_models.py +198 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/models/config_models.py +300 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/models/device_models.py +405 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/py.typed +0 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/services/__init__.py +123 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/services/config_service.py +322 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/services/device_service.py +473 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/services/drivers/__init__.py +88 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/services/drivers/base.py +296 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/services/drivers/ublox.py +1029 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/services/event_bridge.py +198 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/services/metrics_service.py +182 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/services/relay_service.py +233 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/__init__.py +3 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/components/__init__.py +7 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/components/status_card.py +37 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/layout.py +153 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/__init__.py +3 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/dashboard.py +412 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/gps_config.py +647 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/input.py +637 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/outputs.py +402 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/settings.py +111 -0
- sp_rtk_base-0.2.0/src/sp_rtk_base/ui/pages/survey.py +1085 -0
- 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
|
+
[](https://github.com/rodenj1/sp-rtk-base/actions/workflows/ci.yml)
|
|
38
|
+
[](https://codecov.io/gh/rodenj1/sp-rtk-base)
|
|
39
|
+
[](https://pypi.org/project/sp-rtk-base/)
|
|
40
|
+
[](https://pypi.org/project/sp-rtk-base/)
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
[](https://github.com/astral-sh/ruff)
|
|
43
|
+
[](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
|
+
[](https://github.com/rodenj1/sp-rtk-base/actions/workflows/ci.yml)
|
|
4
|
+
[](https://codecov.io/gh/rodenj1/sp-rtk-base)
|
|
5
|
+
[](https://pypi.org/project/sp-rtk-base/)
|
|
6
|
+
[](https://pypi.org/project/sp-rtk-base/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://github.com/astral-sh/ruff)
|
|
9
|
+
[](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
|