Files
gps/soc/software/fusion/serial_capture.py
T

74 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""Capture a line-oriented bare-metal UART stream with host timestamps."""
from __future__ import annotations
import argparse
import csv
import time
from pathlib import Path
import serial
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--port", default="/dev/ttyUSB2")
parser.add_argument("--baud", type=int, default=115200)
parser.add_argument("--duration", type=float, default=60.0)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument(
"--stop-text",
help="stop early after a line containing this text (duration remains a limit)",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
args.output.parent.mkdir(parents=True, exist_ok=True)
line_count = 0
byte_count = 0
start_wall_ns = time.time_ns()
start_mono_ns = time.monotonic_ns()
deadline_ns = start_mono_ns + int(args.duration * 1e9)
with serial.Serial(args.port, args.baud, timeout=0.1) as uart, args.output.open(
"w", newline="", encoding="utf-8"
) as stream:
writer = csv.writer(stream)
writer.writerow(["host_monotonic_s", "host_unix_s", "uart_line"])
uart.reset_input_buffer()
while time.monotonic_ns() < deadline_ns:
raw = uart.readline()
if not raw:
continue
now_mono_ns = time.monotonic_ns()
now_wall_ns = time.time_ns()
byte_count += len(raw)
line = raw.decode("utf-8", errors="replace").rstrip("\r\n")
writer.writerow(
[
f"{(now_mono_ns - start_mono_ns) / 1e9:.9f}",
f"{now_wall_ns / 1e9:.9f}",
line,
]
)
line_count += 1
if args.stop_text and args.stop_text in line:
break
elapsed_s = (time.monotonic_ns() - start_mono_ns) / 1e9
print(
f"captured lines={line_count} bytes={byte_count} "
f"elapsed={elapsed_s:.3f}s output={args.output} "
f"start_unix={start_wall_ns / 1e9:.6f}"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())