sp-rtk-base-relay 2.1.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 (33) hide show
  1. sp_rtk_base_relay-2.1.0/PKG-INFO +405 -0
  2. sp_rtk_base_relay-2.1.0/README.md +367 -0
  3. sp_rtk_base_relay-2.1.0/pyproject.toml +202 -0
  4. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/__init__.py +26 -0
  5. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/config.py +1274 -0
  6. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/__init__.py +13 -0
  7. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/bluetooth_manager.py +744 -0
  8. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/broadcast_hub.py +719 -0
  9. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/connection_states.py +60 -0
  10. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/data_pipeline.py +583 -0
  11. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/destinations/__init__.py +43 -0
  12. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/destinations/base_destination.py +378 -0
  13. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/destinations/destination_factory.py +204 -0
  14. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/destinations/ntrip_destination.py +416 -0
  15. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/destinations/surepath_destination.py +223 -0
  16. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/destinations/tcp_server_destination.py +378 -0
  17. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/events.py +485 -0
  18. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/input_sources/__init__.py +21 -0
  19. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/input_sources/base_input.py +184 -0
  20. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/input_sources/bluetooth_input.py +333 -0
  21. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/input_sources/input_factory.py +417 -0
  22. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/input_sources/serial_input.py +360 -0
  23. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/input_sources/tcp_input.py +405 -0
  24. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/message_filter.py +222 -0
  25. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/rtcm_client.py +555 -0
  26. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/core/status.py +270 -0
  27. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/engine.py +356 -0
  28. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/exceptions.py +340 -0
  29. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/logger.py +503 -0
  30. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/main.py +464 -0
  31. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/metrics.py +780 -0
  32. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/py.typed +0 -0
  33. sp_rtk_base_relay-2.1.0/src/sp_rtk_base_relay/rtcm_decoder.py +532 -0
@@ -0,0 +1,405 @@
1
+ Metadata-Version: 2.3
2
+ Name: sp-rtk-base-relay
3
+ Version: 2.1.0
4
+ Summary: RTCM relay service for custom GPS correction servers
5
+ Keywords: rtcm,gps,gnss,rtk,base-station,corrections
6
+ Author: John Roden
7
+ Author-email: John Roden <rodenj@gmail.com>
8
+ License: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Scientific/Engineering :: GIS
18
+ Classifier: Topic :: System :: Networking
19
+ Requires-Dist: pyserial>=3.5
20
+ Requires-Dist: pyyaml>=6.0
21
+ Requires-Dist: prometheus-client>=0.17.0
22
+ Requires-Dist: dbus-fast>=2.0.0
23
+ Requires-Dist: pytest>=7.0.0 ; extra == 'dev'
24
+ Requires-Dist: pytest-cov>=4.0.0 ; extra == 'dev'
25
+ Requires-Dist: pytest-asyncio>=0.21.0 ; extra == 'dev'
26
+ Requires-Dist: mypy>=1.5.0 ; extra == 'dev'
27
+ Requires-Dist: types-pyyaml ; extra == 'dev'
28
+ Requires-Dist: ruff>=0.6.0 ; extra == 'dev'
29
+ Requires-Dist: pylint>=2.17.0 ; extra == 'dev'
30
+ Requires-Dist: pre-commit>=3.0.0 ; extra == 'dev'
31
+ Requires-Python: >=3.10
32
+ Project-URL: Homepage, https://github.com/rodenj1/sp-rtk-base-relay
33
+ Project-URL: Documentation, https://github.com/rodenj1/sp-rtk-base-relay#readme
34
+ Project-URL: Repository, https://github.com/rodenj1/sp-rtk-base-relay
35
+ Project-URL: Issues, https://github.com/rodenj1/sp-rtk-base-relay/issues
36
+ Provides-Extra: dev
37
+ Description-Content-Type: text/markdown
38
+
39
+ # SP-RTK-Base-Relay
40
+
41
+ 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.
42
+
43
+ [![CI](https://github.com/rodenj1/sp-rtk-base-relay/actions/workflows/ci.yml/badge.svg)](https://github.com/rodenj1/sp-rtk-base-relay/actions/workflows/ci.yml)
44
+ [![Release](https://github.com/rodenj1/sp-rtk-base-relay/actions/workflows/release.yml/badge.svg)](https://github.com/rodenj1/sp-rtk-base-relay/actions/workflows/release.yml)
45
+ [![codecov](https://codecov.io/gh/rodenj1/sp-rtk-base-relay/branch/main/graph/badge.svg?token=T5XTVO92KQ)](https://codecov.io/gh/rodenj1/sp-rtk-base-relay)
46
+ [![PyPI version](https://img.shields.io/pypi/v/sp-rtk-base-relay.svg)](https://pypi.org/project/sp-rtk-base-relay/)
47
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/sp-rtk-base-relay.svg)](https://pypi.org/project/sp-rtk-base-relay/)
48
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/sp-rtk-base-relay.svg)](https://pypi.org/project/sp-rtk-base-relay/)
49
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
50
+ [![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)
51
+
52
+ ## Overview
53
+
54
+ 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.
55
+
56
+ ### Key Features
57
+
58
+ - ๐Ÿ”€ **Multi-Destination Broadcast**: Fan-out RTCM data to 1โ€“N destinations simultaneously
59
+ - ๐ŸŒ **NTRIP v1.0 + v2.0**: Push corrections to any NTRIP caster (RTK2go, Onocoy, rtkdirect)
60
+ - ๐Ÿ“ก **TCP Rebroadcast Server**: Serve corrections to LAN rovers via TCP
61
+ - ๐Ÿ” **Sure-Path Protocol**: Custom `INIT:user:pass*` auth with `$HB$` heartbeat monitoring
62
+ - ๐Ÿ”Œ **Multiple Input Sources**: TCP (RTKBase), Serial UART, USB Serial, Bluetooth GPS
63
+ - ๐ŸŽฏ **Per-Destination Filtering**: Pass-all, allowlist, or blocklist RTCM message types per destination
64
+ - ๐Ÿ“Š **Per-Destination Prometheus Metrics**: Individual throughput, errors, queue depth per destination
65
+ - ๐Ÿ”„ **Automatic Recovery**: Exponential backoff reconnection per destination โ€” independent fault isolation
66
+ - ๐Ÿ”ง **Self-Healing Bluetooth**: Automatic Bluetooth GPS recovery without manual intervention
67
+ - ๐Ÿ›ก๏ธ **Production Ready**: Systemd integration, structured logging, comprehensive error handling
68
+ - ๐Ÿงช **Well Tested**: 88% code coverage with 1,106 passing tests
69
+ - ๐Ÿงฉ **Embeddable Library** (v2.1): `RelayEngine` API for programmatic control by external applications
70
+ - ๐Ÿ“ข **Real-Time Events** (v2.1): EventBus with typed events for state change notifications
71
+ - ๐Ÿ”Œ **Hot Plug Destinations** (v2.1): Add/remove/start/stop destinations while running
72
+
73
+ ## Architecture
74
+
75
+ ```
76
+ โ”Œโ”€โ”€โ–ถ [SurePath Thread] โ”€โ”€โ–ถ Sure-Path Server
77
+ [Input Source] โ”€โ”€โ–ถ [BroadcastHub] โ”€โ”คโ”€โ”€โ–ถ [NTRIP Thread] โ”€โ”€โ–ถ RTK2go / Onocoy / etc.
78
+ TCP / Serial Fan-out โ”‚โ”€โ”€โ–ถ [NTRIP Thread] โ”€โ”€โ–ถ rtkdirect
79
+ / Bluetooth + Filter โ””โ”€โ”€โ–ถ [TCP Srv Thread] โ”€โ”€โ–ถ LAN Rover Clients
80
+ โ”‚
81
+ [Prometheus Metrics] โ—„โ”€โ”€โ”€โ”€ per-destination labels
82
+ ```
83
+
84
+ Each destination runs in its own thread with an independent queue, so a failure in one destination never affects the others.
85
+
86
+ ## Quick Start
87
+
88
+ ### Installation
89
+
90
+ ```bash
91
+ git clone https://github.com/rodenj1/sp-rtk-base-relay.git
92
+ cd sp-rtk-base-relay
93
+ sudo ./tools/install.sh
94
+ ```
95
+
96
+ ### Configuration
97
+
98
+ Edit `/etc/sp-rtk-base-relay/config.yaml` (or see `config.example.yaml`):
99
+
100
+ ```yaml
101
+ input:
102
+ source: "tcp"
103
+ config:
104
+ host: "192.168.1.100"
105
+ port: 3000
106
+
107
+ destinations:
108
+ - name: surepath
109
+ type: surepath
110
+ enabled: true
111
+ filter:
112
+ mode: pass_all
113
+ config:
114
+ host: "server.example.com"
115
+ port: 50010
116
+ username: "USER01"
117
+ password: "abc1"
118
+
119
+ - name: rtk2go
120
+ type: ntrip
121
+ enabled: true
122
+ filter:
123
+ mode: pass_all
124
+ config:
125
+ caster: "rtk2go.com"
126
+ port: 2101
127
+ mountpoint: "MY_MOUNT"
128
+ password: "my_password"
129
+ version: "2.0"
130
+
131
+ - name: local_tcp
132
+ type: tcp_server
133
+ enabled: false
134
+ filter:
135
+ mode: pass_all
136
+ config:
137
+ host: "0.0.0.0"
138
+ port: 5016
139
+ max_clients: 10
140
+
141
+ metrics:
142
+ enabled: true
143
+ port: 8080
144
+
145
+ logging:
146
+ level: "INFO"
147
+ format: "json"
148
+ ```
149
+
150
+ ### Running
151
+
152
+ ```bash
153
+ # As a systemd service
154
+ sudo systemctl start sp-rtk-base-relay
155
+
156
+ # Or foreground
157
+ sp-rtk-base-relay --config config.yaml --foreground
158
+ ```
159
+
160
+ ## Destination Types
161
+
162
+ ### Sure-Path (`surepath`)
163
+ Custom proprietary protocol with `INIT:user:pass*` authentication and `$HB$` heartbeat monitoring. Wraps the battle-tested v1.x RTCMClient.
164
+
165
+ ### NTRIP (`ntrip`)
166
+ Pushes RTCM corrections to NTRIP casters. Supports both protocol versions:
167
+ - **v2.0** (default): HTTP POST + Basic auth + chunked transfer encoding
168
+ - **v1.0**: SOURCE auth + raw binary streaming
169
+
170
+ Tested against RTK2go, Onocoy, and rtkdirect.
171
+
172
+ ### TCP Server (`tcp_server`)
173
+ 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.
174
+
175
+ ## Message Filtering
176
+
177
+ Each destination can independently filter RTCM messages by type ID:
178
+
179
+ ```yaml
180
+ filter:
181
+ mode: pass_all # Forward everything (zero overhead)
182
+
183
+ filter:
184
+ mode: allowlist # Only forward these message types
185
+ message_ids: [1005, 1077, 1087, 1097, 1127]
186
+
187
+ filter:
188
+ mode: blocklist # Forward everything except these
189
+ message_ids: [4072] # Drop proprietary messages
190
+ ```
191
+
192
+ ## Monitoring
193
+
194
+ ### Prometheus Metrics
195
+
196
+ Per-destination metrics with `{destination="..."}` labels:
197
+
198
+ | Metric | Type | Description |
199
+ |--------|------|-------------|
200
+ | `sp_rtk_base_relay_dest_bytes_sent_total` | Counter | Bytes sent per destination |
201
+ | `sp_rtk_base_relay_dest_messages_sent_total` | Counter | Messages sent per destination |
202
+ | `sp_rtk_base_relay_dest_messages_dropped_total` | Counter | Queue overflow drops per destination |
203
+ | `sp_rtk_base_relay_dest_connection_status` | Gauge | Connection state (1/0) per destination |
204
+ | `sp_rtk_base_relay_dest_errors_total` | Counter | Errors per destination |
205
+ | `sp_rtk_base_relay_dest_queue_depth` | Gauge | Queue depth per destination |
206
+ | `sp_rtk_base_relay_input_connection_status` | Gauge | Input source connection state |
207
+ | `sp_rtk_base_relay_input_seconds_since_last_data` | Gauge | No-data watchdog |
208
+ | `sp_rtk_base_relay_tcp_server_connected_clients` | Gauge | TCP server client count |
209
+ | `sp_rtk_base_relay_service_uptime_seconds` | Gauge | Service uptime |
210
+
211
+ ### Grafana Dashboard
212
+
213
+ Import `templates/grafana_dashboard.json` for a pre-built v2 dashboard with per-destination panels, throughput graphs, and the no-data watchdog.
214
+
215
+ ## Input Sources
216
+
217
+ | Source | Use Case | Config Key |
218
+ |--------|----------|------------|
219
+ | `tcp` | RTKBase integration, network base stations | `host`, `port`, `timeout` |
220
+ | `serial` | Direct GNSS receiver via UART | `port`, `baudrate` |
221
+ | `usb_serial` | USB-to-serial adapters | `port`, `baudrate` |
222
+ | `bluetooth` | Bluetooth GPS devices | `device_address`, `channel` |
223
+
224
+ ## Project Structure
225
+
226
+ ```
227
+ sp-rtk-base-relay/
228
+ โ”œโ”€โ”€ src/sp_rtk_base_relay/
229
+ โ”‚ โ”œโ”€โ”€ main.py # Service orchestration (v2)
230
+ โ”‚ โ”œโ”€โ”€ config.py # YAML config with destinations: list
231
+ โ”‚ โ”œโ”€โ”€ metrics.py # Per-destination Prometheus metrics
232
+ โ”‚ โ”œโ”€โ”€ exceptions.py # DestinationError, NtripError, etc.
233
+ โ”‚ โ”œโ”€โ”€ logger.py # Structured logging
234
+ โ”‚ โ”œโ”€โ”€ rtcm_decoder.py # RTCM 3.x frame parser
235
+ โ”‚ โ””โ”€โ”€ core/
236
+ โ”‚ โ”œโ”€โ”€ broadcast_hub.py # Fan-out coordinator (input โ†’ N queues)
237
+ โ”‚ โ”œโ”€โ”€ message_filter.py # Per-destination RTCM filtering
238
+ โ”‚ โ”œโ”€โ”€ rtcm_client.py # Sure-Path protocol client
239
+ โ”‚ โ”œโ”€โ”€ connection_states.py # Connection state machine
240
+ โ”‚ โ”œโ”€โ”€ bluetooth_manager.py # Bluetooth GPS recovery
241
+ โ”‚ โ”œโ”€โ”€ destinations/
242
+ โ”‚ โ”‚ โ”œโ”€โ”€ base_destination.py # ABC + queue + stats
243
+ โ”‚ โ”‚ โ”œโ”€โ”€ destination_factory.py # Registry-based factory
244
+ โ”‚ โ”‚ โ”œโ”€โ”€ surepath_destination.py # Sure-Path server
245
+ โ”‚ โ”‚ โ”œโ”€โ”€ ntrip_destination.py # NTRIP v1.0 + v2.0
246
+ โ”‚ โ”‚ โ””โ”€โ”€ tcp_server_destination.py # Async TCP rebroadcast
247
+ โ”‚ โ””โ”€โ”€ input_sources/
248
+ โ”‚ โ”œโ”€โ”€ base_input.py # Input ABC
249
+ โ”‚ โ”œโ”€โ”€ tcp_input.py # TCP input
250
+ โ”‚ โ”œโ”€โ”€ serial_input.py # Serial input
251
+ โ”‚ โ””โ”€โ”€ bluetooth_input.py # Bluetooth input
252
+ โ”œโ”€โ”€ tests/ # 942 tests, 88% coverage
253
+ โ”‚ โ”œโ”€โ”€ unit/ # Unit tests (26 test files)
254
+ โ”‚ โ”œโ”€โ”€ integration/ # Hardware integration tests
255
+ โ”‚ โ”œโ”€โ”€ manual/ # Manual production tests
256
+ โ”‚ โ””โ”€โ”€ fixtures/ # Mock servers and generators
257
+ โ”œโ”€โ”€ tools/ # Install/uninstall, systemd, bluetooth
258
+ โ”œโ”€โ”€ templates/ # Grafana dashboard v2
259
+ โ”œโ”€โ”€ docs/ # Documentation
260
+ โ”‚ โ”œโ”€โ”€ v2-architecture-plan.md # Full architecture + design decisions
261
+ โ”‚ โ”œโ”€โ”€ deployment-guide.md # Installation & deployment
262
+ โ”‚ โ”œโ”€โ”€ metrics-guide.md # Prometheus metrics reference
263
+ โ”‚ โ””โ”€โ”€ bluetooth-*.md # Bluetooth GPS guides
264
+ โ””โ”€โ”€ config.example.yaml # v2 configuration template
265
+ ```
266
+
267
+ ## Development
268
+
269
+ ```bash
270
+ # Clone and install
271
+ git clone https://github.com/rodenj1/sp-rtk-base-relay.git
272
+ cd sp-rtk-base-relay
273
+ uv sync --all-extras
274
+ source .venv/bin/activate
275
+
276
+ # Run tests
277
+ uv run pytest
278
+
279
+ # Run with coverage
280
+ uv run pytest --cov=src/sp_rtk_base_relay --cov-report=html
281
+ ```
282
+
283
+ ### Code Quality Standards
284
+ - Python 3.10+ with modern type hints (`dict`, `list`, `X | None`)
285
+ - PEP8 code style, pyright strict mode
286
+ - >88% test coverage (942 tests)
287
+ - UV package management
288
+
289
+ ## CLI Usage
290
+
291
+ ```
292
+ sp-rtk-base-relay [OPTIONS]
293
+
294
+ Options:
295
+ --version Show version and exit
296
+ -c, --config PATH Configuration file path
297
+ --validate Validate configuration and exit
298
+ --generate-config Generate example configuration
299
+ --foreground Run in foreground
300
+ --log-level LEVEL Override log level
301
+ ```
302
+
303
+ ## Embedded Usage (v2.1)
304
+
305
+ 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:
306
+
307
+ ```python
308
+ from sp_rtk_base_relay import RelayEngine
309
+ from sp_rtk_base_relay.config import InputConfig, DestinationConfig
310
+
311
+ # 1. Create engine with input source
312
+ engine = RelayEngine(InputConfig(source="tcp", config={"host": "192.168.1.100", "port": 3000}))
313
+
314
+ # 2. Start with destinations
315
+ engine.start([
316
+ DestinationConfig(name="rtk2go", type="ntrip", enabled=True,
317
+ config={"caster": "rtk2go.com", "port": 2101,
318
+ "mountpoint": "MY_MOUNT", "password": "pass"}),
319
+ ])
320
+
321
+ # 3. Hot-add a destination while running
322
+ engine.add_destination(DestinationConfig(
323
+ name="local_tcp", type="tcp_server", enabled=True,
324
+ config={"host": "0.0.0.0", "port": 5016}
325
+ ))
326
+
327
+ # 4. Get typed status snapshot
328
+ status = engine.get_status()
329
+ print(f"Running: {status.is_running}, Destinations: {len(status.destinations)}")
330
+
331
+ # 5. Subscribe to real-time events
332
+ sub = engine.subscribe_events()
333
+ event = sub.get_event(timeout=1.0)
334
+ if event:
335
+ print(f"Event: {event.event_type} โ€” {event.message}")
336
+ sub.close()
337
+
338
+ # 6. Stop when done (releases serial port for other tools)
339
+ engine.stop()
340
+ ```
341
+
342
+ ### Exported API
343
+
344
+ | Symbol | Description |
345
+ |--------|-------------|
346
+ | `RelayEngine` | High-level facade โ€” start/stop/manage relay |
347
+ | `EventBus` | Pub/sub event system |
348
+ | `EventSubscription` | Per-subscriber event queue (iterable) |
349
+ | `RelayEvent` | Typed event (event_type, message, timestamp, payload) |
350
+ | `RelayStatus` | Frozen status snapshot |
351
+ | `DestinationStatus` | Per-destination status |
352
+ | `InputStatus` | Input source status |
353
+
354
+ See **[Relay Engine API Spec](docs/relay-engine-api-spec.md)** for the full technical specification.
355
+
356
+ ## Migration from v1.x
357
+
358
+ v2.0 is a **breaking change**. Key differences:
359
+
360
+ | v1.x | v2.0 |
361
+ |------|------|
362
+ | `server:` config key | `destinations:` list |
363
+ | Single destination | 1โ€“N destinations |
364
+ | Global metrics | Per-destination `{destination="..."}` labels |
365
+ | `DataPipelineCoordinator` | `BroadcastHub` + `DestinationFactory` |
366
+ | v1 Grafana dashboard | v2 dashboard with `$destination` variable |
367
+
368
+ See `config.example.yaml` for the new format. Old `server:` configs are detected with a clear migration error message.
369
+
370
+ ## Releasing
371
+
372
+ Releases to PyPI are fully automated via `.github/workflows/release.yml`.
373
+ Bump the version in `pyproject.toml`, push a `vX.Y.Z` tag, then publish a
374
+ GitHub Release โ€” the workflow re-runs the full test matrix, builds, and
375
+ publishes to PyPI via Trusted Publishing (OIDC, no API tokens). See
376
+ **[docs/release-process.md](docs/release-process.md)** for the per-release
377
+ checklist and the one-time PyPI / GitHub-environment setup.
378
+
379
+ ## Documentation
380
+
381
+ - **[Release Process](docs/release-process.md)**: PyPI release workflow + runbook
382
+ - **[Relay Engine API Spec](docs/relay-engine-api-spec.md)**: Full v2.1 programmatic API reference
383
+ - **[v2.1 Architecture Plan](docs/v2.1-architecture-plan.md)**: Embeddable relay engine design
384
+ - **[v2.0 Architecture Plan](docs/v2-architecture-plan.md)**: Multi-destination design with 7 DRs
385
+ - **[Configuration Reference](configuration-reference.md)**: Complete config guide (YAML + programmatic)
386
+ - **[Deployment Guide](docs/deployment-guide.md)**: Installation and systemd setup
387
+ - **[Metrics Guide](docs/metrics-guide.md)**: Prometheus metrics reference
388
+ - **[Bluetooth Recovery](docs/bluetooth-recovery.md)**: Self-healing Bluetooth GPS
389
+
390
+ ## License
391
+
392
+ MIT License โ€” see [LICENSE](LICENSE).
393
+
394
+ ## Project Status
395
+
396
+ **Current Version**: 2.1.0
397
+ - โœ… Multi-destination broadcast (Sure-Path, NTRIP v1.0/v2.0, TCP server)
398
+ - โœ… Per-destination message filtering and Prometheus metrics
399
+ - โœ… BroadcastHub fan-out architecture with independent fault isolation
400
+ - โœ… Embeddable RelayEngine API with EventBus and typed status (v2.1)
401
+ - โœ… 1,106 tests, 88% coverage, production-stable
402
+
403
+ ---
404
+
405
+ **Made with โค๏ธ for the RTK GPS community**