Initial commit

This commit is contained in:
brentperteet
2026-06-24 11:12:44 -05:00
commit 5703c05c1d
30 changed files with 8149 additions and 0 deletions

184
ntrip/ble_client.py Normal file
View File

@@ -0,0 +1,184 @@
#!/usr/bin/env python3
"""
BLE NTRIP Client for RTK Receiver
Connects to an RTK receiver via Bluetooth Low Energy (BLE) to receive NMEA data
and forward RTCM corrections from an NTRIP caster.
This script uses the bleak library for cross-platform BLE communication.
"""
import asyncio
import sys
from bleak import BleakClient, BleakScanner
# ============================================================================
# Configuration
# ============================================================================
# BLE Device Configuration
DEVICE_NAME = "ML-NA001-250079-BLE"
DEVICE_UUID = "B2DDE9B2-881D-1BE2-5A1B-C44CB646BB1D"
# BLE Service and Characteristic UUIDs
SERVICE_UUID = "0000fff0-0000-1000-8000-00805f9b34fb" # Custom service
CHARACTERISTIC_UUID = "0000fff2-0000-1000-8000-00805f9b34fb" # Read/Notify characteristic for NMEA
# Debug options
DEBUG_RAW_DATA = True # Show raw bytes received
DEBUG_NMEA = True # Show parsed NMEA sentences
# ============================================================================
# BLE Connection and Data Handler
# ============================================================================
class BLENTRIPClient:
"""BLE NTRIP client for RTK receiver."""
def __init__(self):
self.client = None
self.running = False
self.nmea_buffer = bytearray()
async def find_device(self):
"""Scan for the RTK receiver BLE device."""
print(f"Scanning for BLE device: {DEVICE_NAME} ({DEVICE_UUID})...")
devices = await BleakScanner.discover(timeout=10.0)
for device in devices:
print(f"Found: {device.name} ({device.address})")
# Match by UUID or name
if (device.address.upper() == DEVICE_UUID.upper() or
device.name == DEVICE_NAME):
print(f"✓ Found target device: {device.name} at {device.address}")
return device.address
print(f"✗ Device not found. Scanned {len(devices)} devices.")
return None
def notification_handler(self, sender, data):
"""Handle incoming BLE notifications with NMEA data."""
if DEBUG_RAW_DATA:
print(f"Raw data ({len(data)} bytes): {data.hex()}")
# Add data to buffer
self.nmea_buffer.extend(data)
# Process complete NMEA sentences (terminated with \r\n)
while b'\n' in self.nmea_buffer:
# Find the end of the sentence
newline_idx = self.nmea_buffer.index(b'\n')
sentence_bytes = self.nmea_buffer[:newline_idx + 1]
self.nmea_buffer = self.nmea_buffer[newline_idx + 1:]
try:
# Decode NMEA sentence
sentence = sentence_bytes.decode('ascii').strip()
if sentence and DEBUG_NMEA:
print(f"NMEA: {sentence}")
# TODO: Parse NMEA sentences (GGA, etc.) for position data
except UnicodeDecodeError as e:
print(f"Failed to decode NMEA data: {e}")
async def connect_and_monitor(self):
"""Connect to the BLE device and monitor NMEA data."""
# Find the device
device_address = await self.find_device()
if not device_address:
print("Could not find RTK receiver. Make sure device is powered on and in range.")
return False
# Connect to device
print(f"\nConnecting to {device_address}...")
try:
async with BleakClient(device_address) as client:
self.client = client
if not client.is_connected:
print("✗ Failed to connect")
return False
print(f"✓ Connected to {DEVICE_NAME}")
# List available services and characteristics
print("\nAvailable services:")
for service in client.services:
print(f" Service: {service.uuid}")
for char in service.characteristics:
props = ','.join(char.properties)
print(f" Characteristic: {char.uuid} ({props})")
# Start notifications on NMEA characteristic
print(f"\nSubscribing to NMEA notifications on {CHARACTERISTIC_UUID}...")
await client.start_notify(CHARACTERISTIC_UUID, self.notification_handler)
print("✓ Subscribed to notifications\n")
# Keep connection alive and monitor data
self.running = True
print("Monitoring NMEA data (Ctrl+C to stop)...\n")
while self.running:
await asyncio.sleep(1)
# Check connection status
if not client.is_connected:
print("✗ Connection lost")
break
# Stop notifications before disconnecting
await client.stop_notify(CHARACTERISTIC_UUID)
except Exception as e:
print(f"✗ Error: {e}")
return False
return True
def stop(self):
"""Stop the client."""
self.running = False
# ============================================================================
# Main Entry Point
# ============================================================================
async def main():
"""Main entry point."""
client = BLENTRIPClient()
try:
await client.connect_and_monitor()
except KeyboardInterrupt:
print("\n\nStopping...")
client.stop()
except Exception as e:
print(f"\n✗ Fatal error: {e}")
sys.exit(1)
if __name__ == "__main__":
print("=" * 70)
print("BLE NTRIP Client for RTK Receiver")
print("=" * 70)
print()
# Check if bleak is installed
try:
import bleak
except ImportError:
print("✗ Error: 'bleak' library not found")
print("\nInstall it with:")
print(" pip install bleak")
print()
sys.exit(1)
asyncio.run(main())