DNAV-1 "GPS-denied navigation" · D-HOWTEST-1 "How we test, explained" · 13 Oct 2026 · All documents

How we test Pathfinder

Every result comes from one pipeline: a written-down flight, flown in the ArduPilot simulator, scored against the simulator's true position, then judged by acceptance tests whose pass marks were locked before any result existed. A change reaches main only when the numbers improve.

Pages: Jury deck · Results explorer (deck slide 26) · Flight simulator · Results · Demo · Start here · How we test · Index · 14 Oct day summary

The pipeline

  1. Scenario

    A YAML file: route, wind, sensor noise, when GPS is jammed or faked, and a fixed random seed.

    sim/scenarios/
  2. Simulated flight

    The real ArduPilot autopilot (SITL) flies the route closed loop; Pathfinder feeds it a position over MAVLink after the jam.

    tests/harness/
  3. Scoring

    Pathfinder's estimate is compared with the simulator's true position once per second from the jam on.

    results JSON per flight
  4. Locked acceptance tests

    AC-01 to AC-09. Pass marks written on 7 Oct; files hash-locked, so no threshold moves after a result.

    tests/acceptance/LOCK.sha256
  5. Merge rule

    A branch merges only if it beats the current best at 2 min, is not worse at 1 min, breaks no locked test and every test passes.

    D-MERGE-NIGHT-1

What a score means

Schematic: position error grows after the jam; we read it at 60, 120 and 300 seconds and note when it first passes 5 metres 5 m 60 s120 s300 sjamtime to pass 5 m error, metres

Schematic, not data.

MetricHow it is read
Error at 60 / 120 / 300 sDistance between estimate and truth 1, 2 and 5 min after the jam; the median over held-out flights.
Time to pass 5 mSeconds from the jam until the error first exceeds 5 m.
Honest error circleShare of samples where the truth lies inside the reported 95% radius; pass band 90-98% (AC-03).
Mission keptAutopilot stays in AUTO and reaches every waypoint (AC-01).
Spoof caughtFake GPS flagged within 5 s, no jump past the radius (AC-04).

Held-out flights and the comparison set

Held-out flights

Scored flights live in sim/scenarios/heldout/. Their seeds are never used for training, so a model is always judged on flights it has not seen.

Every flight is flown by every system

  • ArduPilot alone
  • Physics only
  • Physics + speed AI (each model version)
  • Physics + drift-correcting AI
  • Speed AI + drift AI
  • Drift AI, gated
  • Terrain matching

Defined once in sim/tools/naming.py (COMPARE_SET). A system whose setup preset is not on main yet is skipped with a note, never silently.

Held-out aircraft

Training flights are tagged by frame (quad, hexa, octa); sim/tools/fly_training.py:30 skips hexa unless --allow-hexa, so hexa scores a frame the model never saw. Pick it with lab.py --frame. In-flight learning (experimental-learn-air) is scored against the same system with it off.

The nine locked acceptance tests

TestProvesPass mark
AC-01Mission continuesAUTO kept, all waypoints, in 95% of held-out runs
AC-02Less driftmedian error 50% below ArduPilot alone, and below physics only
AC-03Honest radiustruth inside r95 in 90-98% of samples
AC-04Spoofing rejectedflagged within 5 s, autopilot switched, no jump past r95
AC-05GPS returnre-trusted within 15 s
AC-06Vehicle profileframe, motors and IMUs detected on every frame
AC-07Calibration helpslower median error at 2 min with it on
AC-08Cross-platformcore tests pass on Linux x86, ARM and Windows; builds for Android
AC-09Deterministicthe same log replayed twice gives bit-identical output

Fast run (DNAV-TEST-1 "Acceptance tests in under 1 hour"): fast run 2 took 1,086 s, measured (sim). The files in tests/acceptance/ are never edited by a branch; the merge rule checks that too.

The Drift Benchmark

How drift grows with flight time, weather and turns: 135 scenarios, seeds 3000-3134, in sim/scenarios/benchmark/.

  • 1-10 min flights
  • jam at 30 s
  • jam at 30 s, GPS back at 90 s
  • jam at 2 min
  • calm · wind 6 m/s · gusts
  • straight · zigzag · curve

Fly: python sim/tools/benchmark_run.py, then chart: python sim/tools/benchmark_chart.py. The old names ladder_*.py still work. Since 14 Oct each host flies two simulators side by side (--workers 2: one SITL instance per worker, own ports and lock, RAM guard before each flight); measured (sim): 0 IMU gaps over 50 ms with two running, about 6.5x faster than real time per simulator including boot and teardown.

Smoke, 6 of 6 flown (straight, calm, jam at 30 s, one seed), end error at 1 / 3 / 5 min: physics only 2.5 / 5.0 / 34.4 m; physics + speed AI 16.4 / 7.3 / 11.8 m. measured (sim)

Drift Benchmark smoke: error at the end of the flight against flight length, jam at 30 s
Smoke chart; the other 129 scenarios are not flown yet.

Distance series (D-DIST-1 "Distance suite")

How drift grows with distance flown after the jam: 60 scenarios, seeds 4000-4059, benchmark-dist-* in sim/scenarios/benchmark/. GPS is jammed at 30 s and never comes back.

  • 0.5 · 1 · 2 · 5 · 10 km after the jam
  • 5 · 10 · 15 m/s
  • straight · curve
  • calm · wind 6 m/s
  • every system in the comparison set

Scored as error in m and as % of the distance travelled since the jam (truth track), at the flight's target distance. Fly: python sim/tools/benchmark_run.py --series distance (narrow with --km, --speeds).

Smoke, 9 of 9 flown (straight, calm, 10 m/s, one seed), error at 0.5 / 1 / 2 km: ArduPilot alone 427 / 928 / 1,913 m (86-96%); physics only 0.7 / 1.5 / 17.9 m (0.1-0.9%); physics + speed AI 4.5 / 1.9 / 13.9 m (0.2-0.9%). measured (sim)

Distance series smoke: error as percent of distance since the jam against km, per systemDistance series smoke: error in metres against km since the jam, per system
Smoke charts; 5 and 10 km, 5 and 15 m/s, curve and wind are not flown yet.

Ablation and add-on checks (14 Oct)

An add-on is shipped behind an off switch and joins the headline only if it beats physics only when flown. Three levels of evidence, weakest first: unit test (synthetic input in cargo test), offline (replay) (100 flown logs replayed through the new code, sim/tools/wind_after_jam.py --variants), measured (sim) (flown closed loop).

Ablation benchmark (DNAV-ACC-10 "Ablation benchmark"): python sim/tools/benchmark_run.py --systems ablation flies physics only, each add-on alone and all add-ons fused (experimental-fused) on the priority set; python sim/tools/ablation_chart.py charts error at 1 / 2 / 5 min and % of distance. 324 flights on 4 simulators, flying.

What today's checks showed: last-layer tune won offline (306 to 145 m @120 s) and lost flown (2.45 vs 3.32 m, 1 of 7); wind after jam caught a gust in the unit test (4.09 to 0.41 m/s) but in replay 5 wind flights ran away; speed scale absorbed wind in replay (83.6 to 306 m). An offline win is not a flown win. Detail: 14 Oct day summary.

Results explorer: python sim/tools/export_explorer.py exports every flown result to docs/pitch/explorer-data.json; the deck (slide 26) and docs/pitch/explorer.html redraw from it, no hand numbers.

Repeats and labels

Repeats

Each scenario has one fixed seed, so a flight can be flown again exactly; a recorded log replays bit-identically (AC-09). Flying every system 5-10 times with new seeds is planned, not done: today most numbers rest on one seed per flight.

Every number carries a label

measured (sim) flown closed loop in the simulator.

offline estimate a recorded flight replayed through new code; the autopilot did not react to it.

estimate a calculation or projection, not flown.

What is not tested

Sources: tests/acceptance/, docs/specs/2026-10-07-pathfinder-mvp-design.md, sim/tools/naming.py, sim/tools/benchmark_*.py, LAB.md, RESULTS.html.