"""Regression tests for the RTK–LiDAR coordinate and initialization contract.""" from __future__ import annotations import math import sys from pathlib import Path import numpy as np ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "tools")) sys.path.insert(0, str(ROOT / "code")) from finalize_direct_rtk_lidar import ( # noqa: E402 coordinate_contract_audit, mechanical_self_consistency, ) from prepare_multisensor_station_dataset import heading_to_enu_yaw # noqa: E402 from rtk_attitude import attitude_rotation, rtk_body_rotation # noqa: E402 from rigorous_calibration import ( # noqa: E402 build_parser, load_extrinsic_matrix, params_transform, transform_params, ) def test_left_baseline_heading_plus_90_points_vehicle_forward() -> None: corrected, yaw = heading_to_enu_yaw(270.0, 90.0) assert corrected == 0.0 assert math.degrees(yaw) == 90.0 def test_east_vehicle_heading_maps_to_zero_enu_yaw() -> None: corrected, yaw = heading_to_enu_yaw(0.0, 90.0) assert corrected == 90.0 assert math.degrees(yaw) == 0.0 def test_attitude_rotation_applies_baseline_pitch_elevation() -> None: _, yaw = heading_to_enu_yaw(0.0, 0.0) # heading north → body X = +North rotation = attitude_rotation(yaw, pitch_deg=10.0, roll_deg=0.0) body_x = rotation @ np.array([1.0, 0.0, 0.0]) np.testing.assert_allclose( body_x, [0.0, math.cos(math.radians(10.0)), math.sin(math.radians(10.0))], atol=1e-12, ) def test_vehicle_forward_offset_keeps_pitch_about_baseline() -> None: # Baseline points east (vehicle right if nose north); pitch elevates baseline X. # Vehicle-forward offset -90 must not simply Ry after vehicle yaw. raw_heading = 90.0 pitch = 10.0 r_correct = rtk_body_rotation(raw_heading, -90.0, pitch_deg=pitch, roll_deg=0.0) _, yaw_raw = heading_to_enu_yaw(raw_heading, 0.0) r_baseline = attitude_rotation(yaw_raw, pitch_deg=pitch, roll_deg=0.0) rz90 = np.array([[0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]]) np.testing.assert_allclose(r_correct, r_baseline @ rz90, atol=1e-12) # Level vehicle-forward X should point north. r_level = rtk_body_rotation(raw_heading, -90.0, pitch_deg=0.0, roll_deg=0.0) np.testing.assert_allclose(r_level @ np.array([1.0, 0.0, 0.0]), [0.0, 1.0, 0.0], atol=1e-12) def test_pair_registration_has_no_extrinsic_argument() -> None: parser = build_parser() pair_options = { option for action in parser._subparsers._group_actions[0].choices["pairs"]._actions for option in action.option_strings } assert "--initial-extrinsic" not in pair_options assert "--global-voxel" in pair_options def test_mechanical_initial_is_vehicle_forward_swapped_master() -> None: path = ROOT / "run" / "rtk_lidar_mechanical_initial.json" document = __import__("json").loads(path.read_text(encoding="utf-8-sig")) transform = load_extrinsic_matrix(path) np.testing.assert_allclose(transform[:3, 3], [0.210859360, -0.414179474, 0.078500001]) np.testing.assert_allclose(transform[:3, :3], np.eye(3)) np.testing.assert_allclose(params_transform(transform_params(transform)), transform, atol=1e-12) assert document["baseline_points"] == "vehicle_right" assert document["frame_mode"] == "vehicle_forward_heading_offset" assert document["heading_offset_deg"] == -90.0 assert document["rotation_rpy_deg_xyz"][2] == 0.0 check = mechanical_self_consistency(document) assert check["consistent"] is True def test_mixed_left_xy_plus_right_yaw_mechanical_is_rejected() -> None: mixed = { "baseline_points": "vehicle_left", "translation_m": [0.414179474, 0.210859360, 0.078500001], "rotation_rpy_deg_xyz": [0.0, 0.0, 90.0], "matrix_4x4": [ [0.0, -1.0, 0.0, 0.414179474], [1.0, 0.0, 0.0, 0.210859360], [0.0, 0.0, 1.0, 0.078500001], [0.0, 0.0, 0.0, 1.0], ], } check = mechanical_self_consistency(mixed) assert check["consistent"] is False def test_deprecated_minus_xy_right_baseline_is_rejected_for_swapped_master() -> None: deprecated = { "baseline_points": "vehicle_right", "translation_m": [-0.414179474, -0.210859360, 0.078500001], "rotation_rpy_deg_xyz": [0.0, 0.0, 90.0], } check = mechanical_self_consistency(deprecated) assert check["consistent"] is False def test_near_180_degree_solution_is_flagged_for_physical_axis_check() -> None: initial_path = ROOT / "run" / "rtk_lidar_mechanical_initial.json" initial = load_extrinsic_matrix(initial_path) solution = np.eye(4) solution[:3, :3] = initial[:3, :3] @ np.diag([-1.0, -1.0, 1.0]) solution[:3, 3] = initial[:3, 3] audit = coordinate_contract_audit({ "solver_initial_extrinsic": str(initial_path), "matrix_4x4": solution.tolist(), }) assert audit["status"] == "near_180_degree_axis_conflict" assert audit["requires_physical_axis_confirmation"] is True def test_previous_mixed_result_branch_is_not_recommended() -> None: """Old baseline-frame mixed solution disagrees with vehicle-forward mechanical initial.""" initial_path = ROOT / "run" / "rtk_lidar_mechanical_initial.json" solution = np.array( [ [0.00942353438668686, -0.9999215926659111, 0.00824654595155475, 0.4123055815579212], [0.9998714355322929, 0.009529416150714898, 0.012895837871912157, 0.2173092104098051], [-0.012973411511822136, 0.008123961367132958, 0.9998828390593821, 0.10405760639434848], [0.0, 0.0, 0.0, 1.0], ], float, ) audit = coordinate_contract_audit({ "solver_initial_extrinsic": str(initial_path), "matrix_4x4": solution.tolist(), }) assert audit["requires_physical_axis_confirmation"] is True assert audit["status"] in { "near_180_degree_axis_conflict", "solution_disagrees_with_mechanical_baseline_side", }