Files
venus/axiom-nmea/examples/pcap-to-nmea/pcap_to_nmea.py
dev 9756538f16 Initial commit: Venus OS boat addons monorepo
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
2026-03-16 17:04:16 +00:00

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()