"""VPAIR nominal e2e — fixed-wing, downward, no raw IMU. Tests that require full ESKF path are skipped because VPAIR ships poses only. The VO + GPR + graph path is still exercised. """ from pathlib import Path import pytest from gps_denied.testing.datasets.vpair import VPAIRAdapter from gps_denied.testing.harness import E2EHarness from gps_denied.testing.metrics import absolute_trajectory_error VPAIR_SAMPLE_RMSE_CEILING_M = 20.0 # initial target, calibrate after first runs @pytest.mark.e2e @pytest.mark.e2e_slow @pytest.mark.needs_dataset @pytest.mark.asyncio async def test_vpair_sample_pipeline_completes(vpair_sample_root: Path): adapter = VPAIRAdapter(vpair_sample_root) if not adapter.capabilities.has_raw_imu: # ESKF path is skipped automatically inside the product because IMU # callbacks are never fired; we only check completion here. pass harness = E2EHarness(adapter) result = await harness.run() assert result.num_frames_submitted > 0 @pytest.mark.e2e @pytest.mark.e2e_slow @pytest.mark.needs_dataset @pytest.mark.asyncio async def test_vpair_sample_trajectory_bounded(vpair_sample_root: Path): adapter = VPAIRAdapter(vpair_sample_root) harness = E2EHarness(adapter) result = await harness.run() if result.estimated_positions_enu.shape[0] == 0: pytest.xfail( "Pipeline produced no GPS estimates on VPAIR sample. " "Expected until VO + GPR tuning for 300-400m nadir imagery is validated." ) n = min(result.estimated_positions_enu.shape[0], result.ground_truth.shape[0]) ate = absolute_trajectory_error( result.estimated_positions_enu[:n], result.ground_truth[:n] ) assert ate["rmse"] < VPAIR_SAMPLE_RMSE_CEILING_M, f"ATE RMSE={ate['rmse']:.2f}m"