"""Unit tests for rscap → V1 export helpers (no large real captures).""" from __future__ import annotations import struct import numpy as np from tools.rscap_v2.h32_msop import ( CHANNELS, PACKET_LENGTH, decode_packet_points, default_vertical_deg, default_horizontal_deg, device_timestamp_ms, normalize_azimuth_deg, ) from tools.rscap_v2.n300_imu import crc8_fdilink, crc16_fdilink, iter_n300_imu_samples from tools.rscap_v2.capture_format_v2 import CaptureFile, CaptureHeader, RawChunk def _make_msop_packet(*, seconds: int = 100, microseconds: int = 5000, az_deg: float = 10.0) -> bytes: packet = bytearray(PACKET_LENGTH) packet[17] = 1 # 2.5 mm unit sec = seconds.to_bytes(6, "big") packet[20:26] = sec packet[26:30] = int(microseconds).to_bytes(4, "big") az_raw = int(round(az_deg * 100)) for block in range(12): offset = 42 + block * 100 packet[offset] = 255 packet[offset + 1] = 238 packet[offset + 2] = (az_raw >> 8) & 0xFF packet[offset + 3] = az_raw & 0xFF idx = offset + 4 for _ch in range(CHANNELS): # 4.0 m at 2.5 mm/unit => raw = 1600 packet[idx] = (1600 >> 8) & 0xFF packet[idx + 1] = 1600 & 0xFF packet[idx + 2] = 10 idx += 3 return bytes(packet) def test_h32_device_timestamp_and_points(): packet = _make_msop_packet(seconds=1700000000, microseconds=123000) assert device_timestamp_ms(packet) == 1700000000 * 1000 + 123 az_list, pts = decode_packet_points( packet, default_vertical_deg(), default_horizontal_deg(), min_range_m=0.1, max_range_m=50.0, ) assert len(az_list) == 12 assert pts.shape[0] == 12 * CHANNELS assert np.allclose(np.linalg.norm(pts, axis=1), 4.0, atol=1e-3) def test_normalize_azimuth(): assert abs(normalize_azimuth_deg(190.0) + 170.0) < 1e-9 def _n300_imu_frame(device_us: int = 123456) -> bytes: payload = bytearray(56) struct.pack_into("<3f", payload, 0, 0.1, -0.2, 0.3) struct.pack_into("<3f", payload, 12, 0.0, 0.0, 9.81) struct.pack_into("