retain telemetry across transport outages

This commit is contained in:
Jay
2026-08-17 14:00:14 -04:00
parent bc4a2856e1
commit 291c6b54e6
11 changed files with 222 additions and 37 deletions
+12 -2
View File
@@ -13,6 +13,7 @@ import serial
from trikke_protocol import (
CSV_COLUMNS,
PACKET_FLAG_TIMESTAMP_DELTA_SATURATED,
PACKET_TYPE_METADATA,
Frame,
Metadata,
@@ -76,6 +77,7 @@ def main() -> int:
sample_gap_count = 0
sample_reset_count = 0
timing_anomaly_count = 0
timestamp_saturation_frame_count = 0
accel_stale_count = 0
accel_overrun_count = 0
gyro_stale_count = 0
@@ -85,6 +87,7 @@ def main() -> int:
previous_timestamp_us = None
final_dropped_count = 0
final_loop_overrun_count = 0
serial_error: serial.SerialException | None = None
def render_frame(frame: Frame, writer: csv.writer) -> None:
nonlocal sample_count, sample_gap_count, sample_reset_count
@@ -144,6 +147,8 @@ def main() -> int:
else:
packet_reset_count += 1
previous_packet_sequence = frame.packet_sequence
if frame.flags & PACKET_FLAG_TIMESTAMP_DELTA_SATURATED:
timestamp_saturation_frame_count += 1
if frame.packet_type == PACKET_TYPE_METADATA:
metadata = frame.metadata
metadata_count += 1
@@ -156,16 +161,19 @@ def main() -> int:
decoded.flush()
except serial.SerialException as exc:
print(f"Serial error: {exc}", file=sys.stderr)
return 1
serial_error = exc
print(
f"Stopped after {sample_count} samples and {metadata_count} metadata frames; "
f"packet_gaps={packet_gap_count}, packet_resets={packet_reset_count}, "
f"sample_gaps={sample_gap_count}, sample_resets={sample_reset_count}, "
f"timing_anomalies={timing_anomaly_count}, "
f"timestamp_saturation_frames={timestamp_saturation_frame_count}, "
f"startup_crc_rejects={parser.startup_crc_errors}, "
f"stream_crc_errors={parser.crc_errors}, "
f"header_errors={parser.header_errors}, skipped_nonframe_bytes={parser.skipped_bytes}"
f"header_errors={parser.header_errors}, "
f"skipped_nonframe_bytes={parser.skipped_bytes}, "
f"trailing_partial_bytes={parser.buffered_bytes}"
)
print(
f"Status totals: accel_stale={accel_stale_count}, "
@@ -174,6 +182,8 @@ def main() -> int:
f"acquisition_loop_overruns={final_loop_overrun_count}"
)
print(f"Saved {output} and {csv_output}")
if serial_error is not None:
return 1
return 0 if metadata is not None else 4
+11 -2
View File
@@ -7,6 +7,7 @@ from pathlib import Path
from trikke_protocol import (
CSV_COLUMNS,
PACKET_FLAG_TIMESTAMP_DELTA_SATURATED,
PACKET_TYPE_METADATA,
Frame,
StreamParser,
@@ -31,15 +32,21 @@ def main() -> int:
None,
)
if metadata is None:
print("No valid metadata frame found")
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
@@ -54,7 +61,9 @@ def main() -> int:
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}; saved {args.output}"
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
+6
View File
@@ -16,6 +16,7 @@ MAX_RECORDS = 8
PACKET_TYPE_METADATA = 1
PACKET_TYPE_SAMPLES = 2
PACKET_FLAG_TIMESTAMP_DELTA_SATURATED = 0x01
HEADER = struct.Struct("<4sBBBBBBHIQIII")
METADATA = struct.Struct("<HHHH10f")
@@ -127,6 +128,11 @@ class StreamParser:
self.startup_crc_errors = 0
self.crc_errors = 0
@property
def buffered_bytes(self) -> int:
"""Bytes retained because they do not yet form a complete frame."""
return len(self._buffer)
def feed(self, data: bytes) -> list[Frame]:
self._buffer.extend(data)
frames: list[Frame] = []