# FT-P-01 — Still-image satellite-anchor frame-center accuracy

**Task**: AZ-409_ft_p_01_still_image_accuracy
**Name**: FT-P-01 still-image set-60 frame-center accuracy (AC-1.1, AC-1.2)
**Description**: Implement the FT-P-01 blackbox scenario — push 60 still images one at a time, observe outbound `GPS_INPUT` (AP) / `MSP2_SENSOR_GPS` (iNav) at SITL, compute Vincenty geodesic distance to GT, assert pass-count rules from `expected_results/results_report.md`.
**Complexity**: 3 points
**Dependencies**: AZ-406, AZ-407
**Component**: Blackbox Tests / Positive (epic AZ-262)
**Tracker**: AZ-409
**Epic**: AZ-262 (E-BBT)

## Problem

The canonical "frame-center geolocation" pipeline accuracy must be validated against the 60-image GT set under the AC-1.1 / AC-1.2 budgets — without this scenario the project has no honest measurement of its satellite-anchor accuracy on still images.

## Outcome

- A pytest scenario at `e2e/tests/positive/test_ft_p_01_still_image_accuracy.py` parameterized by `(fc_adapter, vio_strategy)`.
- Test pushes each `AD0000NN.jpg` to the SUT's frame source, waits up to 5 s for the corresponding outbound message at the SITL listener, computes Vincenty distance to the `coordinates.csv` GT row.
- Test emits `e2e-results/run-${RUN_ID}/ft-p-01.csv` (one row per image: `image_id, gt_lat, gt_lon, est_lat, est_lon, error_m, pass_50m, pass_20m`).
- Aggregate pass criteria: `pass_count(error≤50m) ≥ 48` AND `pass_count(error≤20m) ≥ 30`.

## Scope

### Included
- pytest test method per `(fc_adapter, vio_strategy)` parameterization.
- Frame-source-replay helper invocation (one image at a time).
- SITL observer for AP `GPS_INPUT` reception AND iNav `MSP2_SENSOR_GPS` reception.
- Vincenty geodesic computation (use `geopy` or `scipy` per the runner's pinned deps).
- Per-image CSV emission for evidence; aggregate assertion.

### Excluded
- The 60 images themselves — bind-mounted from `_docs/00_problem/input_data/`.
- The tile-cache-fixture build — owned by AZ-407.
- Per-image MRE (cross-domain matcher quality) — owned by FT-P-05 (AZ-413).
- Schema validation of the outbound message — owned by FT-P-03 (AZ-411).

## Acceptance Criteria

**AC-1: per-image distance computed**
Given the SUT cold-started with `tile-cache-fixture` mounted
When the test pushes each `AD0000NN.jpg` and reads the next outbound message at SITL within 5 s
Then a per-image error row is recorded in `ft-p-01.csv` for all 60 images.

**AC-2: pass-count rule (50 m budget)**
Given all 60 per-image distances
Then `pass_count(error_m ≤ 50) ≥ 48` (i.e. ≥80 % of 60 images per AC-1.1).

**AC-3: pass-count rule (20 m budget)**
Given all 60 per-image distances
Then `pass_count(error_m ≤ 20) ≥ 30` (i.e. ≥50 % of 60 images per AC-1.2).

**AC-4: timeout discipline**
Given an image is pushed
When no outbound message arrives at SITL within 5 s
Then the test records `error_m=∞`, `pass_50m=False`, `pass_20m=False` for that image; the run continues. Final aggregate may still PASS if the total pass counts meet the thresholds.

**AC-5: parameterization**
Given the conftest's `(fc_adapter, vio_strategy)` parameterization
Then the scenario runs four times by default — `(ardupilot, okvis2)`, `(ardupilot, klt_ransac)`, `(inav, okvis2)`, `(inav, klt_ransac)` — and emits one CSV row per parameterization in `report.csv`.

## System Under Test Boundary

This is an end-to-end scenario. The test ONLY interacts with the SUT through public boundaries.

- **Allowed inputs**: frame-source push, FC inbound IMU stream replay (passive — no modification by the test).
- **Allowed observation**: SITL-side `GPS_INPUT` / `MSP2_SENSOR_GPS` receipt; FDR archive post-run if needed for diagnostic evidence.
- **Forbidden**: importing any SUT module, monkeypatching internal SUT state, stubbing C1-C5 components or C6 tile cache or C7 inference runtime, reading SUT internal Python/C++ buffers.
- **External-system stubs allowed**: `ardupilot-plane-sitl`, `inav-sitl` (real SITLs — observe only), the `tile-cache-fixture` mount (a public on-disk schema), the FDR filesystem.
- If a SUT module isn't yet implemented, the scenario MUST fail/block as missing product implementation; it must NOT pass by replacing that module with a test stub.

## Constraints

- Reference data: `_docs/00_problem/input_data/expected_results/results_report.md` (the rule) + `expected_results/position_accuracy.csv` (per-image budget flags).
- Geodesic comparison: Vincenty (NOT haversine) — required by `expected-results.md` `numeric_tolerance` semantics.
- Per-image CSV must include the image_id used in `coordinates.csv` (1-based `AD000001` style).

## Risks & Mitigation

**Risk: SITL round-trip latency variance can push a passing image past the 5 s timeout**
- *Mitigation*: timeout is 5 s per image (matches scenario's `Max execution time` budget of 5 min for 60 images). If timeout-driven failures dominate, NFT-PERF-01 is the place to investigate latency; this scenario records timeouts as failures and continues.

## Document Dependencies

- `_docs/02_document/tests/blackbox-tests.md` § FT-P-01
- `_docs/00_problem/input_data/expected_results/results_report.md` (Pass/Fail Rules)
- `_docs/00_problem/input_data/expected_results/position_accuracy.csv` (per-image flags)
- `_docs/02_document/tests/test-data.md` § Expected Results Mapping § Position accuracy (FT-P-01 row)
