#!/usr/bin/env python3 """ A script to parse and convert Kogger SB protocol logs into a human-readable format. """ import csv import struct import sys import ast import os # Based on the Kogger SB protocol specification PDF and user-provided details. # (From PDF page 5 and others) ID_MAP = { 0x01: "ID_TIMESTAMP", 0x02: "ID_DIST", 0x03: "ID_CHART", 0x04: "ID_ATTITUDE", 0x05: "ID_TEMP", 0x10: "ID_DATASET", 0x11: "ID_DIST_SETUP", 0x12: "ID_CHART_SETUP", 0x14: "ID_TRANSC", 0x15: "ID_SND_SPD", 0x18: "ID_UART", 0x1B: "ID_IMU_SETUP", 0x20: "ID_VERSION", 0x21: "ID_MARK", 0x22: "ID_DIAG", 0x23: "ID_FLASH", 0x24: "ID_BOOT", 0x25: "ID_UPDATE", 0x64: "ID_NAV", 0x65: "ID_USBL_SOLUTION", 0x66: "ID_SIGNAL_ENCODER", 0x67: "ID_SIGNAL_DECODER", 0x68: "ID_USBL_CONTROL", 0x79: "ID_DVL_VEL", # Some IDs have aliases 102: "ID_SIGNAL_ENCODER", 103: "ID_SIGNAL_DECODER", 121: "ID_DVL_VEL", } def parse_route(route_byte): """Parses the ROUTE byte.""" dev_address = route_byte & 0b1111 return {"dev_address": dev_address} def parse_mode(mode_byte): """Parses the MODE byte.""" type_val = mode_byte & 0b11 type_map = { 0: "Reserved", 1: "CON:DEV→HST", 2: "SET:HST→DEV", 3: "GET:HST→DEV", } version = (mode_byte >> 3) & 0b111 mark = (mode_byte >> 6) & 0b1 response = (mode_byte >> 7) & 0b1 return { "type": type_map.get(type_val, "Unknown"), "type_val": type_val, "version": version, "mark": mark, "response": response, } def parse_payload(msg_id, mode, payload): """Parses the payload based on message ID, mode, and version.""" msg_name = ID_MAP.get(msg_id) # Check for generic RESP message first (PDF page 6) if mode['type_val'] == 1 and len(payload) == 3: # CONTENT from DEVICE try: code, _check1, _check2 = struct.unpack('= 8: # Minimum possible message size if not reassembly_buffer.startswith(b'\xbbU'): # Buffer doesn't start with sync bytes, find the next sync sequence sync_pos = reassembly_buffer.find(b'\xbbU') if sync_pos == -1: # No sync bytes, discard the whole buffer reassembly_buffer = b'' break else: # Discard garbage before the sync bytes reassembly_buffer = reassembly_buffer[sync_pos:] if len(reassembly_buffer) < 6: # Not enough data for a header break # Try to read the payload length payload_len = reassembly_buffer[5] full_msg_len = 6 + payload_len + 2 # header + payload + checksum if len(reassembly_buffer) >= full_msg_len: # We have a complete message message_to_parse = reassembly_buffer[:full_msg_len] human_readable_msg = parse_message(message_to_parse) print_save("RX", timestamp, human_readable_msg, outfile) # Keep the rest of the buffer for the next message reassembly_buffer = reassembly_buffer[full_msg_len:] else: # Not enough data for a full message, wait for more break except FileNotFoundError: print(f"Error: File not found at {log_file}", file=sys.stderr) sys.exit(1) except Exception as e: print(f"An unexpected error occurred: {e}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": if len(sys.argv) != 2: print(f"Usage: python {sys.argv[0]} ", file=sys.stderr) sys.exit(1) main(sys.argv[1])