84 lines
2.8 KiB
Python
84 lines
2.8 KiB
Python
#!/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_TYPE_METADATA,
|
|
Frame,
|
|
IntegrityTracker,
|
|
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
|
|
integrity = IntegrityTracker()
|
|
with args.output.open("w", encoding="utf-8", newline="") as target:
|
|
writer = csv.writer(target)
|
|
writer.writerow(CSV_COLUMNS)
|
|
for frame in frames:
|
|
integrity.observe(frame)
|
|
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"packet_gaps={integrity.packet_gap_count}, "
|
|
f"packet_resets={integrity.packet_reset_count}, "
|
|
f"sample_gaps={integrity.sample_gap_count}, "
|
|
f"sample_resets={integrity.sample_reset_count}, "
|
|
f"timing_anomalies={integrity.timing_anomaly_count}, "
|
|
"timestamp_saturation_frames="
|
|
f"{integrity.timestamp_saturation_frame_count}, "
|
|
f"accel_stale={integrity.accel_stale_count}, "
|
|
f"accel_overrun={integrity.accel_overrun_count}, "
|
|
f"gyro_stale={integrity.gyro_stale_count}, "
|
|
f"gyro_overrun={integrity.gyro_overrun_count}, "
|
|
f"dropped={integrity.final_dropped_sample_count}, "
|
|
f"acquisition_loop_overruns={integrity.final_loop_overrun_count}, "
|
|
f"trailing_partial_bytes={stream.buffered_bytes}; saved {args.output}"
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|