"""line_of_sight_test.py —— PixelToAngle 单元测试。 移植自 reference-c++/visual_track_control/tests/line_of_sight_test.cpp。 覆盖(物理追踪思路 §4.1/§5.1,C2):零偏移、画面边缘偏移 = 半视场角、 奇对称、单调性、与线性近似的偏离(全程针孔模型)、构造期非法参数抛异常。 """ import math import pytest from visual_track_control.line_of_sight import PixelToAngle def test_zero_offset_gives_zero_angle(): conv = PixelToAngle(1000.0, math.radians(90.0)) assert conv.convert(0.0) == 0.0 def test_half_size_offset_gives_half_fov(): # 针孔模型:偏移 = size/2 时 angle = atan(tan(θ/2)) = θ/2 conv = PixelToAngle(1000.0, math.radians(90.0)) assert conv.convert(500.0) == pytest.approx(math.radians(45.0), abs=1e-12) assert conv.convert(-500.0) == pytest.approx(-math.radians(45.0), abs=1e-12) def test_odd_symmetry(): conv = PixelToAngle(1920.0, math.radians(60.0)) for offset in (1.0, 100.0, 730.5): assert conv.convert(-offset) == pytest.approx(-conv.convert(offset), abs=1e-15) def test_monotonic_increasing(): conv = PixelToAngle(1080.0, math.radians(35.0)) prev = conv.convert(-600.0) for x in range(-500, 601, 100): cur = conv.convert(float(x)) assert cur > prev prev = cur def test_deviates_from_linear_approximation(): # C2:全程针孔模型。θ=90°、偏移比例 0.25 处线性近似误差约 4°, # 针孔结果必须与之明显偏离(证明未走线性支路)。 conv = PixelToAngle(1000.0, math.radians(90.0)) pinhole_deg = math.degrees(conv.convert(250.0)) # atan(250·2·tan45°/1000) linear_deg = 250.0 * (90.0 / 1000.0) # θ·offset/size = 22.5° assert pinhole_deg == pytest.approx(math.degrees(math.atan(0.5)), abs=1e-9) assert abs(pinhole_deg - linear_deg) > 2.0 def test_constructor_rejects_invalid_args(): with pytest.raises(ValueError): PixelToAngle(0.0, 1.0) with pytest.raises(ValueError): PixelToAngle(-10.0, 1.0) with pytest.raises(ValueError): PixelToAngle(1000.0, 0.0) with pytest.raises(ValueError): PixelToAngle(1000.0, math.pi) with pytest.raises(ValueError): PixelToAngle(1000.0, math.pi + 0.1)