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
87 lines
2.9 KiB
Python
87 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Find offsets that show consistent heading-like values in BOTH pcaps."""
|
|
|
|
import struct
|
|
from collections import defaultdict
|
|
|
|
def decode_float(data, offset):
|
|
if offset + 4 > len(data):
|
|
return None
|
|
try:
|
|
val = struct.unpack('<f', data[offset:offset+4])[0]
|
|
if val != val:
|
|
return None
|
|
return val
|
|
except:
|
|
return None
|
|
|
|
def read_pcap(filename):
|
|
packets = []
|
|
with open(filename, 'rb') as f:
|
|
header = f.read(24)
|
|
magic = struct.unpack('<I', header[0:4])[0]
|
|
swapped = magic == 0xd4c3b2a1
|
|
endian = '>' if swapped else '<'
|
|
|
|
while True:
|
|
pkt_header = f.read(16)
|
|
if len(pkt_header) < 16:
|
|
break
|
|
ts_sec, ts_usec, incl_len, orig_len = struct.unpack(f'{endian}IIII', pkt_header)
|
|
pkt_data = f.read(incl_len)
|
|
if len(pkt_data) < incl_len:
|
|
break
|
|
|
|
if len(pkt_data) > 42 and pkt_data[12:14] == b'\x08\x00':
|
|
ip_header_len = (pkt_data[14] & 0x0F) * 4
|
|
payload_start = 14 + ip_header_len + 8
|
|
if payload_start < len(pkt_data):
|
|
packets.append(pkt_data[payload_start:])
|
|
return packets
|
|
|
|
# Load both pcaps
|
|
pcap1 = read_pcap("raymarine_sample.pcap")
|
|
pcap2 = read_pcap("raymarine_sample_twd_69-73.pcap")
|
|
|
|
print("Looking for offsets where values differ significantly between captures")
|
|
print("(indicating actual sensor data that changed, like heading)\n")
|
|
|
|
# Focus on 344 and 446 byte packets (most common with wind data)
|
|
for target_size in [344, 446]:
|
|
print(f"\n{'='*70}")
|
|
print(f"PACKET SIZE: {target_size} bytes")
|
|
print("=" * 70)
|
|
|
|
pkts1 = [p for p in pcap1 if len(p) == target_size][:10]
|
|
pkts2 = [p for p in pcap2 if len(p) == target_size][:10]
|
|
|
|
if not pkts1 or not pkts2:
|
|
print(" Not enough packets")
|
|
continue
|
|
|
|
print(f"\n{'Offset':<10} {'Original pcap':<20} {'TWD pcap':<20} {'Diff':<10}")
|
|
print("-" * 60)
|
|
|
|
# Check offsets from 0x50 to 0x180
|
|
for offset in range(0x50, min(target_size - 4, 0x180)):
|
|
vals1 = [decode_float(p, offset) for p in pkts1]
|
|
vals2 = [decode_float(p, offset) for p in pkts2]
|
|
|
|
# Filter valid radian values (0 to 2*pi)
|
|
degs1 = [v * 57.2958 for v in vals1 if v and 0 < v < 6.5]
|
|
degs2 = [v * 57.2958 for v in vals2 if v and 0 < v < 6.5]
|
|
|
|
if not degs1 or not degs2:
|
|
continue
|
|
|
|
avg1 = sum(degs1) / len(degs1)
|
|
avg2 = sum(degs2) / len(degs2)
|
|
diff = abs(avg2 - avg1)
|
|
|
|
# Look for offsets where:
|
|
# 1. Values are valid angles (not 0, not garbage)
|
|
# 2. Values changed between captures (diff > 5°)
|
|
# 3. Both values are in reasonable heading range (0-360°)
|
|
if 5 < avg1 < 355 and 5 < avg2 < 355 and diff > 5:
|
|
print(f"0x{offset:04x} {avg1:6.1f}° ({len(degs1)} hits) {avg2:6.1f}° ({len(degs2)} hits) {diff:5.1f}°")
|