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
86 lines
2.5 KiB
Python
86 lines
2.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Debug Field 13 extraction across different packet sizes."""
|
|
|
|
import struct
|
|
from protobuf_decoder import ProtobufParser, HEADER_SIZE, WIRE_FIXED32
|
|
|
|
def decode_float(raw):
|
|
if len(raw) != 4:
|
|
return None
|
|
try:
|
|
val = struct.unpack('<f', raw)[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
|
|
|
|
packets = read_pcap("raymarine_sample_TWD_62-70_HDG_29-35.pcap")
|
|
|
|
# Group by size
|
|
by_size = {}
|
|
for pkt in packets:
|
|
size = len(pkt)
|
|
if size not in by_size:
|
|
by_size[size] = []
|
|
by_size[size].append(pkt)
|
|
|
|
print("Analyzing Field 13 by packet size:")
|
|
print("Expected: TWD 62-70°, HDG 29-35°")
|
|
print("=" * 70)
|
|
|
|
for size in sorted(by_size.keys()):
|
|
if size < 200:
|
|
continue
|
|
|
|
pkts = by_size[size][:3] # First 3 of each size
|
|
|
|
print(f"\n{size} bytes ({len(by_size[size])} packets):")
|
|
|
|
for pkt in pkts:
|
|
proto_data = pkt[HEADER_SIZE:]
|
|
parser = ProtobufParser(proto_data)
|
|
fields = parser.parse_message()
|
|
|
|
if 13 not in fields:
|
|
print(" No Field 13")
|
|
continue
|
|
|
|
f13 = fields[13]
|
|
if not f13.children:
|
|
print(" Field 13 has no children")
|
|
continue
|
|
|
|
# Show all float fields in Field 13
|
|
floats = []
|
|
for fnum, child in sorted(f13.children.items()):
|
|
if child.wire_type == WIRE_FIXED32:
|
|
val = decode_float(child.value)
|
|
if val is not None:
|
|
deg = val * 57.2958
|
|
floats.append(f"f{fnum}={deg:.1f}°")
|
|
|
|
print(f" {', '.join(floats)}")
|