Organizes 11 projects for Cerbo GX/Venus OS into a single repository: - axiom-nmea: Raymarine LightHouse protocol decoder - dbus-generator-ramp: Generator current ramp controller - dbus-lightning: Blitzortung lightning monitor - dbus-meteoblue-forecast: Meteoblue weather forecast - dbus-no-foreign-land: noforeignland.com tracking - dbus-tides: Tide prediction from depth + harmonics - dbus-vrm-history: VRM cloud history proxy - dbus-windy-station: Windy.com weather upload - mfd-custom-app: MFD app deployment package - venus-html5-app: Custom Victron HTML5 app fork - watermaker: Watermaker PLC control UI Adds root README, .gitignore, project template, and per-project .gitignore files. Sensitive config files excluded via .gitignore with .example templates provided. Made-with: Cursor
134 lines
3.7 KiB
Python
134 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
PCAP to NMEA Conversion Example
|
|
|
|
This example demonstrates how to use the raymarine_nmea library to
|
|
read Raymarine packets from a PCAP file and generate NMEA sentences.
|
|
|
|
Useful for:
|
|
- Testing without a live Raymarine network
|
|
- Analyzing captured data
|
|
- Generating NMEA data for replay
|
|
|
|
Usage:
|
|
python pcap_to_nmea.py capture.pcap
|
|
|
|
# Output JSON instead of NMEA
|
|
python pcap_to_nmea.py capture.pcap --json
|
|
|
|
# Output specific sentence types only
|
|
python pcap_to_nmea.py capture.pcap --sentences gga,mwd,mtw
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Add repo root to path for development
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
|
|
|
|
from raymarine_nmea import (
|
|
RaymarineDecoder,
|
|
SensorData,
|
|
NMEAGenerator,
|
|
)
|
|
from raymarine_nmea.listeners import PcapReader
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Convert Raymarine PCAP to NMEA sentences"
|
|
)
|
|
parser.add_argument(
|
|
'pcap',
|
|
help='Path to PCAP file'
|
|
)
|
|
parser.add_argument(
|
|
'--json',
|
|
action='store_true',
|
|
help='Output JSON instead of NMEA'
|
|
)
|
|
parser.add_argument(
|
|
'--sentences',
|
|
help='Comma-separated list of sentence types to output'
|
|
)
|
|
parser.add_argument(
|
|
'-v', '--verbose',
|
|
action='store_true',
|
|
help='Verbose output (show decode progress)'
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Check file exists
|
|
if not os.path.exists(args.pcap):
|
|
print(f"Error: File not found: {args.pcap}")
|
|
sys.exit(1)
|
|
|
|
# Read PCAP
|
|
print(f"Reading {args.pcap}...", file=sys.stderr)
|
|
reader = PcapReader(args.pcap)
|
|
print(f"Found {len(reader)} packets", file=sys.stderr)
|
|
|
|
# Decode all packets
|
|
decoder = RaymarineDecoder(verbose=args.verbose)
|
|
data = SensorData()
|
|
|
|
decoded_count = 0
|
|
for packet in reader:
|
|
result = decoder.decode(packet)
|
|
if result.has_data():
|
|
decoded_count += 1
|
|
data.update(result)
|
|
|
|
print(f"Decoded {decoded_count} packets with data", file=sys.stderr)
|
|
|
|
# Output format
|
|
if args.json:
|
|
# JSON output
|
|
print(json.dumps(data.to_dict(), indent=2))
|
|
else:
|
|
# NMEA output
|
|
generator = NMEAGenerator()
|
|
|
|
# Filter sentences if specified
|
|
if args.sentences:
|
|
enabled = set(s.strip().lower() for s in args.sentences.split(','))
|
|
generator.enabled = enabled
|
|
|
|
sentences = generator.generate_all(data)
|
|
|
|
if sentences:
|
|
print("\n# Generated NMEA sentences:", file=sys.stderr)
|
|
for sentence in sentences:
|
|
print(sentence, end='')
|
|
else:
|
|
print("No NMEA sentences generated (no valid data)", file=sys.stderr)
|
|
|
|
# Summary
|
|
print("\n# Summary:", file=sys.stderr)
|
|
print(f"# Packets: {data.packet_count}", file=sys.stderr)
|
|
print(f"# Decoded: {data.decode_count}", file=sys.stderr)
|
|
|
|
with data._lock:
|
|
if data.latitude:
|
|
print(f"# GPS: {data.latitude:.6f}, {data.longitude:.6f}",
|
|
file=sys.stderr)
|
|
if data.heading_deg:
|
|
print(f"# Heading: {data.heading_deg:.1f}°", file=sys.stderr)
|
|
if data.twd_deg:
|
|
print(f"# Wind: {data.tws_kts:.1f} kts @ {data.twd_deg:.1f}°",
|
|
file=sys.stderr)
|
|
if data.depth_m:
|
|
print(f"# Depth: {data.depth_m:.1f} m", file=sys.stderr)
|
|
if data.tanks:
|
|
print(f"# Tanks: {len(data.tanks)}", file=sys.stderr)
|
|
if data.batteries:
|
|
print(f"# Batteries: {len(data.batteries)}", file=sys.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|