# Blackbox Tests ## Positive Scenarios ### FT-P-01: End-to-End Position Accuracy — 50m Threshold **Summary**: Validate that ≥80% of frame positions are within 50m of ground truth GPS across a full 60-frame flight sequence. **Traces to**: AC-01 (80% within 50m) **Category**: Position Accuracy **Preconditions**: - System running with SITL ArduPilot (GPS_TYPE=14) - Camera replay serving flight-sequence-60 at 0.7fps - Satellite tiles for test area loaded - System has completed startup (first satellite match done) **Input data**: flight-sequence-60 (60 frames), coordinates.csv (ground truth), position_accuracy.csv (thresholds) **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Start session via POST /sessions | HTTP 201 with session ID | | 2 | Subscribe to SSE stream GET /sessions/{id}/stream | SSE events begin at ~1Hz | | 3 | Wait for camera-replay to complete all 60 frames (~86s at 0.7fps) | Position events for each processed frame | | 4 | Collect all position events with lat/lon | 60 position estimates (some frames may have multiple updates) | | 5 | For each frame: compute haversine distance between estimated and ground truth position | Distance array | | 6 | Count frames where distance < 50m, compute percentage | ≥80% | **Expected outcome**: ≥48 of 60 frames have position error < 50m from ground truth in coordinates.csv **Max execution time**: 120s --- ### FT-P-02: End-to-End Position Accuracy — 20m Threshold **Summary**: Validate that ≥60% of frame positions are within 20m of ground truth GPS. **Traces to**: AC-02 (60% within 20m) **Category**: Position Accuracy **Preconditions**: Same as FT-P-01 **Input data**: flight-sequence-60, coordinates.csv, position_accuracy.csv **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Reuse position data from FT-P-01 run (or re-run) | 60 position estimates | | 2 | Count frames where distance < 20m, compute percentage | ≥60% | **Expected outcome**: ≥36 of 60 frames have position error < 20m **Max execution time**: 120s (shared with FT-P-01) --- ### FT-P-03: No Single Frame Exceeds Maximum Error **Summary**: Validate that no individual frame position estimate exceeds 100m error. **Traces to**: AC-01, AC-02 (implicit: no catastrophic outliers) **Category**: Position Accuracy **Preconditions**: Same as FT-P-01 **Input data**: flight-sequence-60, coordinates.csv, position_accuracy.csv **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Reuse position data from FT-P-01 | 60 position estimates | | 2 | Find max error across all frames | max(distances) ≤ 100m | **Expected outcome**: Maximum position error across all 60 frames ≤ 100m **Max execution time**: 120s (shared with FT-P-01) --- ### FT-P-04: VO Drift Between Satellite Anchors **Summary**: Validate cumulative VO drift stays below 100m between consecutive satellite correction events. **Traces to**: AC-03 (drift < 100m between anchors) **Category**: Position Accuracy **Preconditions**: Same as FT-P-01; satellite matching active on keyframes **Input data**: flight-sequence-60 SSE stream (includes drift_from_anchor field) **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Subscribe to SSE stream | Events with drift_from_anchor field | | 2 | Record drift_from_anchor values over the full sequence | Array of drift values | | 3 | Find maximum drift_from_anchor value | max(drift) < 100m | **Expected outcome**: drift_from_anchor never exceeds 100m during the 60-frame sequence **Max execution time**: 120s --- ### FT-P-05: GPS_INPUT Message Correctness — Normal Tracking **Summary**: Validate GPS_INPUT message fields are correctly populated during normal satellite-anchored tracking. **Traces to**: AC-08 (GPS_INPUT to FC via MAVLink), AC-04 (confidence score) **Category**: Flight Controller Integration **Preconditions**: System tracking normally with recent satellite match (<30s) **Input data**: Normal frame + satellite match; MAVLink capture from mavlink-inspector **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Read captured GPS_INPUT messages from mavlink-inspector | GPS_INPUT messages at 5-10Hz | | 2 | Verify field: fix_type | fix_type == 3 | | 3 | Verify field: horiz_accuracy | 1.0 ≤ horiz_accuracy ≤ 50.0 | | 4 | Verify field: satellites_visible | satellites_visible == 10 | | 5 | Verify fields: lat, lon | Non-zero, within operational area bounds | | 6 | Verify fields: vn, ve, vd | Populated (non-NaN), magnitude consistent with ~50-70 km/h flight | **Expected outcome**: All GPS_INPUT fields populated correctly per specification **Max execution time**: 30s --- ### FT-P-06: Image Registration Rate **Summary**: Validate that ≥95% of frames in a normal flight are successfully registered by the VO pipeline. **Traces to**: AC-05 (registration > 95%) **Category**: Image Processing Quality **Preconditions**: System running with full 60-frame sequence **Input data**: flight-sequence-60 SSE stream (vo_status field) **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Subscribe to SSE stream | Events with vo_status field | | 2 | Count frames where vo_status == "tracking" | ≥57 of 60 | | 3 | Compute registration rate | ≥95% | **Expected outcome**: ≥57 of 60 frames report vo_status "tracking" **Max execution time**: 120s --- ### FT-P-07: Confidence Tier — HIGH **Summary**: Validate HIGH confidence tier when satellite match is recent and covariance is low. **Traces to**: AC-04 (confidence score per estimate) **Category**: Confidence Scoring **Preconditions**: System running, satellite match completed <30s ago **Input data**: SSE stream during normal tracking **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Read SSE event immediately after satellite match | confidence field | | 2 | Verify confidence == "HIGH" | "HIGH" | | 3 | Read GPS_INPUT fix_type from mavlink-inspector | fix_type == 3 | **Expected outcome**: Confidence tier is HIGH, fix_type is 3 **Max execution time**: 30s --- ### FT-P-08: Confidence Tier — MEDIUM (VO-only, No Recent Satellite Match) **Summary**: Validate MEDIUM confidence tier when VO is tracking but no satellite match in >30s. **Traces to**: AC-04 **Category**: Confidence Scoring **Preconditions**: System running; satellite tile server paused (returns 503) to prevent new matches; >30s since last match **Input data**: SSE stream during VO-only tracking **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Pause satellite-tile-server (Docker pause) | No new satellite matches possible | | 2 | Wait >30s after last satellite match | Confidence should transition | | 3 | Read SSE event | confidence == "MEDIUM" | | 4 | Read GPS_INPUT fix_type | fix_type == 3 | **Expected outcome**: Confidence transitions to MEDIUM; fix_type remains 3 **Max execution time**: 60s --- ### FT-P-09: GPS_INPUT Output Rate **Summary**: Validate GPS_INPUT messages are sent at 5-10Hz continuously. **Traces to**: AC-08 (GPS_INPUT via MAVLink), AC-09 (frame-by-frame streaming) **Category**: Flight Controller Integration **Preconditions**: System running and producing position estimates **Input data**: MAVLink capture from mavlink-inspector (10s window) **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Capture GPS_INPUT messages for 10 seconds | N messages | | 2 | Compute rate: N / 10 | 5 ≤ rate ≤ 10 | | 3 | Verify no gaps > 300ms between consecutive messages | max gap ≤ 300ms | **Expected outcome**: Rate is 5-10Hz, no gap exceeds 300ms **Max execution time**: 15s --- ### FT-P-10: Object Localization **Summary**: Validate object GPS localization from pixel coordinates via the FastAPI endpoint. **Traces to**: AC-16 (object localization), AC-17 (trigonometric calculation) **Category**: Object Localization **Preconditions**: System running with known UAV position (from GPS-denied estimate); known object ground truth GPS **Input data**: pixel_x, pixel_y (center of frame = nadir), gimbal_pan_deg=0, gimbal_tilt_deg=-90, zoom_factor=1.0 **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /objects/locate with pixel at frame center, gimbal pointing straight down | JSON: { lat, lon, alt, accuracy_m, confidence } | | 2 | Compute haversine distance between response lat/lon and current UAV position | Should be < accuracy_m (nadir point ≈ UAV position) | | 3 | Verify accuracy_m is consistent with current system accuracy | accuracy_m > 0, accuracy_m < 100m | **Expected outcome**: Object location at nadir matches UAV position within accuracy_m **Max execution time**: 5s --- ### FT-P-11: Coordinate Transform Round-Trip **Summary**: Validate GPS→NED→pixel→GPS round-trip error is <0.1m. **Traces to**: AC-18 (WGS84 output) **Category**: Coordinate Transforms **Preconditions**: System running, position known **Input data**: Known GPS coordinate within operational area **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Query system for current position via SSE | lat, lon | | 2 | POST /objects/locate with frame center pixel, straight-down gimbal | Returned lat, lon | | 3 | Compute haversine distance between original UAV lat/lon and round-trip result | distance < 0.1m | **Expected outcome**: Round-trip error < 0.1m **Max execution time**: 5s --- ### FT-P-12: Startup — GPS_INPUT Within 60 Seconds **Summary**: Validate the system begins outputting GPS_INPUT messages within 60s of boot. **Traces to**: AC-11 (startup from last GPS) **Category**: Startup & Failsafe **Preconditions**: Fresh system start; SITL ArduPilot running with GLOBAL_POSITION_INT available **Input data**: MAVLink capture from mavlink-inspector **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Start gps-denied-system container | System boots | | 2 | Monitor mavlink-inspector for first GPS_INPUT message | Timestamp of first GPS_INPUT | | 3 | Compute elapsed time from container start to first GPS_INPUT | ≤ 60s | **Expected outcome**: First GPS_INPUT message arrives within 60s of system start **Max execution time**: 90s --- ### FT-P-13: Telemetry Output Rate **Summary**: Validate telemetry NAMED_VALUE_FLOAT messages are sent at 1Hz. **Traces to**: AC-14 (telemetry to ground station) **Category**: Telemetry **Preconditions**: System running normally **Input data**: MAVLink capture from mavlink-inspector (10s window) **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Capture NAMED_VALUE_FLOAT messages for "gps_conf", "gps_drift", "gps_hacc" over 10s | N messages per name | | 2 | Verify rate: ~1Hz per metric (8-12 messages per name in 10s) | 0.8-1.2 Hz | **Expected outcome**: Each telemetry metric sent at ~1Hz **Max execution time**: 15s --- ### FT-P-14: SSE Stream Schema **Summary**: Validate SSE position events contain all required fields with correct types. **Traces to**: AC-14 (streaming to ground station) **Category**: API & Communication **Preconditions**: Active session with SSE stream **Input data**: SSE events from /sessions/{id}/stream **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Subscribe to SSE stream | Events at ~1Hz | | 2 | Parse event JSON | Valid JSON | | 3 | Verify fields: type (string), timestamp (ISO8601), lat (float), lon (float), alt (float), accuracy_h (float), confidence (string), drift_from_anchor (float), vo_status (string), last_satellite_match_age_s (float) | All present with correct types | **Expected outcome**: Every SSE event conforms to the specified schema **Max execution time**: 10s --- ## Negative Scenarios ### FT-N-01: Trajectory Direction Change (Frames 32-43) **Summary**: Validate system continues producing position estimates through a trajectory direction change. **Traces to**: AC-07 (disconnected segments core to system) **Category**: Resilience & Edge Cases **Preconditions**: System running; camera-replay set to serve frames 32-43 (direction change area) **Input data**: Frames AD000032-043.jpg, coordinates for frames 32-43 **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Camera-replay serves frames 32-43 at 0.7fps | System processes frames | | 2 | Collect SSE position events for each frame | ≥12 position estimates (one per frame minimum) | | 3 | Verify no gap >5s without a position update | Continuous output | **Expected outcome**: System produces position estimates for all frames in the direction-change segment; no prolonged output gap **Max execution time**: 30s --- ### FT-N-02: Outlier Frame Handling (350m Gap) **Summary**: Validate system handles a 350m outlier between consecutive photos without position corruption. **Traces to**: AC-06 (350m outlier tolerance) **Category**: Resilience & Edge Cases **Preconditions**: System running with normal tracking established; fault injection: camera-replay skips frames to simulate 350m gap **Input data**: Normal frames followed by a frame 350m away (simulated by frame skip in camera-replay) **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Normal tracking for 10 frames | Position estimates with <50m error | | 2 | Camera-replay jumps forward ~350m (skips multiple frames) | System detects discontinuity | | 3 | Collect position estimates for next 5 frames after the gap | Recovery within 3-5 frames | | 4 | Verify position error of recovered frames | Error < 100m for first valid frame after recovery | **Expected outcome**: System recovers from 350m outlier; post-recovery position error < 100m **Max execution time**: 30s --- ### FT-N-03: Invalid Object Localization Request **Summary**: Validate API rejects invalid pixel coordinates with HTTP 422. **Traces to**: AC-16 (object localization) **Category**: API Error Handling **Preconditions**: System running with active session **Input data**: POST /objects/locate with pixel_x=-100, pixel_y=-100 **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /objects/locate with negative pixel coordinates | HTTP 422 | | 2 | Verify response body contains error description | JSON with "error" or "detail" field | **Expected outcome**: HTTP 422 with validation error **Max execution time**: 2s --- ### FT-N-04: Unauthenticated API Access **Summary**: Validate API rejects unauthenticated requests with HTTP 401. **Traces to**: AC-14 (security — JWT auth) **Category**: API Security **Preconditions**: System running **Input data**: POST /sessions with no Authorization header **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /sessions without JWT token | HTTP 401 | | 2 | GET /sessions/{id}/stream without JWT | HTTP 401 | | 3 | POST /objects/locate without JWT | HTTP 401 | | 4 | GET /health (no auth required) | HTTP 200 | **Expected outcome**: Protected endpoints return 401; /health remains accessible **Max execution time**: 5s --- ### FT-N-05: 3-Consecutive-Failure Re-Localization Request **Summary**: Validate that after VO loss + 3 consecutive satellite match failures, the system sends a re-localization request to the ground station. **Traces to**: AC-08 (3 consecutive failures → re-localization request) **Category**: Resilience & Edge Cases **Preconditions**: System running; camera-replay set to serve featureless frames (VO will fail); satellite-tile-server returning 404 (tile not found) **Input data**: Featureless frames (e.g., blank/uniform images), satellite tile server offline **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Camera-replay serves featureless frames | VO tracking lost | | 2 | Satellite-tile-server returns 404 | Satellite matching fails | | 3 | Wait for 3 camera frames (3 × 1.43s ≈ 4.3s) | 3 consecutive failures | | 4 | Check mavlink-inspector for STATUSTEXT | Message matches `RELOC_REQ: last_lat=.* last_lon=.* uncertainty=.*m` | | 5 | Verify GPS_INPUT fix_type | fix_type == 0 | | 6 | Verify GPS_INPUT horiz_accuracy | horiz_accuracy == 999.0 | **Expected outcome**: RELOC_REQ sent via STATUSTEXT; GPS_INPUT reports no-fix with 999.0 accuracy **Max execution time**: 15s --- ### FT-N-06: IMU-Only Dead Reckoning (VO Lost, No Satellite) **Summary**: Validate system degrades gracefully to IMU-only ESKF prediction when VO and satellite matching both fail. **Traces to**: AC-06 (VO lost behavior), AC-04 (confidence score reflects state) **Category**: Resilience & Edge Cases **Preconditions**: System running; camera-replay paused (no frames); satellite-tile-server paused **Input data**: No camera frames, no satellite tiles; only IMU from SITL **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Pause camera-replay and satellite-tile-server | System loses VO and satellite inputs | | 2 | Read SSE events over 5s | confidence transitions from HIGH/MEDIUM to LOW | | 3 | Read GPS_INPUT from mavlink-inspector | fix_type == 2 | | 4 | Read horiz_accuracy over time | horiz_accuracy ≥ 50m and increasing | | 5 | Verify GPS_INPUT continues at 5-10Hz | Messages continue (IMU-driven ESKF prediction) | **Expected outcome**: System continues GPS_INPUT at 5-10Hz via IMU; confidence drops; accuracy degrades but output never stops **Max execution time**: 15s --- ### FT-N-07: Operator Re-Localization Hint Accepted **Summary**: Validate the system accepts an operator re-localization hint and recovers position. **Traces to**: AC-08 (re-localization), AC-15 (ground station commands) **Category**: Ground Station Integration **Preconditions**: System in FAILED confidence state (3 consecutive failures); satellite-tile-server restored **Input data**: Operator hint: approximate lat/lon (from coordinates.csv ground truth ± 200m offset) **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | Trigger 3-consecutive-failure state (FT-N-05 preconditions) | RELOC_REQ sent | | 2 | Restore satellite-tile-server | Tiles available again | | 3 | POST /sessions/{id}/anchor with approximate lat/lon | HTTP 200 | | 4 | Wait for satellite match attempt (~3-5s) | System searches in new area | | 5 | Read SSE events | confidence transitions back to HIGH/MEDIUM | | 6 | Read GPS_INPUT fix_type | fix_type == 3 | **Expected outcome**: System accepts operator hint, searches satellite tiles in new area, recovers position, confidence returns to HIGH/MEDIUM **Max execution time**: 30s