#!/usr/bin/env python3 """Decode a validated or raw TRK1 byte stream into CSV.""" import argparse import csv from pathlib import Path from trikke_protocol import ( CSV_COLUMNS, PACKET_FLAG_TIMESTAMP_DELTA_SATURATED, PACKET_TYPE_METADATA, Frame, StreamParser, sample_to_csv_row, ) def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("input", type=Path) parser.add_argument("output", type=Path) args = parser.parse_args() stream = StreamParser() frames: list[Frame] = [] with args.input.open("rb") as source: while chunk := source.read(64 * 1024): frames.extend(stream.feed(chunk)) metadata = next( (frame.metadata for frame in frames if frame.packet_type == PACKET_TYPE_METADATA), None, ) if metadata is None: print( "No valid metadata frame found; " f"trailing_partial_bytes={stream.buffered_bytes}" ) return 2 args.output.parent.mkdir(parents=True, exist_ok=True) sample_count = 0 timestamp_saturation_frame_count = 0 with args.output.open("w", encoding="utf-8", newline="") as target: writer = csv.writer(target) writer.writerow(CSV_COLUMNS) for frame in frames: if frame.flags & PACKET_FLAG_TIMESTAMP_DELTA_SATURATED: timestamp_saturation_frame_count += 1 if frame.packet_type == PACKET_TYPE_METADATA: if frame.metadata is not None: metadata = frame.metadata continue for sample in frame.samples: writer.writerow( sample_to_csv_row(sample, metadata, frame.loop_overrun_count) ) sample_count += 1 print( f"Decoded {sample_count} samples from {len(frames)} frames; " f"startup_crc_rejects={stream.startup_crc_errors}, " f"stream_crc_errors={stream.crc_errors}, header_errors={stream.header_errors}, " f"skipped_nonframe_bytes={stream.skipped_bytes}, " f"timestamp_saturation_frames={timestamp_saturation_frame_count}, " f"trailing_partial_bytes={stream.buffered_bytes}; saved {args.output}" ) return 0 if __name__ == "__main__": raise SystemExit(main())