支持 HI13/H32 主机 UTC 桥接对齐、多会话联合标定与 CAD 平移先验。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
lichun.qu
2026-08-10 13:26:32 +08:00
co-authored by Cursor
parent 30f7e66db3
commit 2237be77a4
20 changed files with 1830 additions and 347 deletions
+15 -1
View File
@@ -8,7 +8,8 @@ from pathlib import Path
import numpy as np
from tools.h32_dlog.difop import CHANNELS, HORIZONTAL_START, VERTICAL_START, parse_difop_angles
from tools.h32_dlog.dobject import discover_records, iter_payloads, resolve_dlog_root
from tools.h32_dlog.dobject import RECORD_RE, discover_records, iter_payloads, resolve_dlog_root
from tools.h32_dlog.timeutil import local_wall_to_dotnet_ticks
from tools.h32_dlog.load_session import load_h32_dlog_lidar
from tools.h32_dlog.payload_v1 import (
MsopPacketItem,
@@ -90,6 +91,19 @@ def _write_dorec_record(
return start
def test_recovered_index_line_and_local_ticks():
line = (
">DObject `frontlidar-msop-raw` post len=15532B, id:9CF1, "
"tic:639218060782100466, @data.bin:0"
)
match = RECORD_RE.search(line)
assert match is not None
assert match.group("name") == "frontlidar-msop-raw"
assert match.group("file") == "data.bin"
assert int(match.group("offset")) == 0
assert local_wall_to_dotnet_ticks("2026-08-08T17:14:38") == 639218060780000000
def test_parse_msop_and_difop_payload_roundtrip():
packet = _make_msop_packet(seconds=1700000000, microseconds=123456)
item = MsopPacketItem(
+79
View File
@@ -0,0 +1,79 @@
"""Unit tests for HI13 / HI91 IMU decoding."""
from __future__ import annotations
import struct
from tools.rscap_v2.capture_format_v2 import CaptureFile, CaptureHeader, RawChunk
from tools.rscap_v2.hi13_imu import crc16_hi13, iter_hi13_imu_samples, parse_hi91_frame
def _hi91_frame(
*,
device_ms: int = 123456,
accel_g=(0.0, 0.0, 1.0),
gyro_dps=(1.0, -2.0, 3.0),
) -> bytes:
payload = bytearray(76)
payload[0] = 0x91
struct.pack_into("<H", payload, 1, 0) # pps
payload[3] = 25 # temp
struct.pack_into("<f", payload, 4, 101325.0)
struct.pack_into("<I", payload, 8, device_ms)
struct.pack_into("<fff", payload, 12, *accel_g)
struct.pack_into("<fff", payload, 24, *gyro_dps)
# remaining mag/rpy/quat left zero
payload_length = len(payload)
header = bytearray(6)
header[0] = 0x5A
header[1] = 0xA5
header[2] = payload_length & 0xFF
header[3] = (payload_length >> 8) & 0xFF
frame_wo_crc = bytes(header[:4]) + bytes(payload)
# crc over header[0:4] + payload
tmp = bytearray(6 + payload_length)
tmp[0:4] = header[0:4]
tmp[6:] = payload
crc = crc16_hi13(tmp, payload_length)
header[4] = crc & 0xFF
header[5] = (crc >> 8) & 0xFF
return bytes(header) + bytes(payload)
def test_parse_hi91_units():
frame = _hi91_frame(device_ms=5000, accel_g=(0.0, 0.0, 1.0), gyro_dps=(57.2957795, 0.0, 0.0))
parsed = parse_hi91_frame(frame)
assert parsed is not None
gyro, accel, device_ms = parsed
assert device_ms == 5000
assert abs(accel[2] - 9.80665) < 1e-4
assert abs(gyro[0] - 1.0) < 1e-5
def test_iter_hi13_from_capture():
frame = _hi91_frame(device_ms=42)
header = CaptureHeader(
sensor_kind="hi13r4-imu",
session_id="t",
session_start_utc_ticks=0,
session_start_monotonic_ticks=0,
monotonic_frequency=10_000_000,
port="COM1",
baud=115200,
file_start_utc_ticks=0,
)
chunk = RawChunk(
sequence=1,
receive_utc_ticks=100,
receive_monotonic_ticks=1,
raw=frame,
record_file_offset=0,
raw_file_offset=0,
record_crc32=0,
crc_valid=True,
)
capture = CaptureFile(path="mem", header=header, chunks=[chunk], footer=None)
samples = iter_hi13_imu_samples(capture)
assert len(samples) == 1
assert samples[0].device_timestamp_us == 42_000
assert abs(samples[0].t_s - 0.042) < 1e-12