A synthetic simulation of the estimation problem behind air surveillance. A target
flies a path you never get to observe directly, two sensors report noisy and
incomplete measurements of it, and a filter has to reconstruct where the target is
while being honest about how sure it is.
The setup
A target enters at about 219 m/s, cruises for a while, then pulls a
24.7 m/s² turn for eight seconds starting at t = 25 s. Two
sensors watch it, and neither one is enough on its own:
-
Radar, at 10 Hz, measures range, azimuth and elevation, but it only detects the
target 93% of the time.
-
Infrared, at 20 Hz, measures bearing very precisely, but it cannot measure range
at all, and it degrades with distance through atmospheric attenuation.
Fusing the two beats radar alone by roughly 36% in position error, because the IR
sensor's sharp angles pin down exactly the directions radar measures worst.
The interesting failure
Accuracy is the obvious thing to measure, but it isn't the dangerous one. A filter
with large errors is fine as long as it reports large uncertainty. A filter with
small errors is dangerous if it claims even smaller ones. NEES measures that
directly, by normalizing the true error against the covariance the filter itself
claims, and a trustworthy 6-state filter averages 6.
A single constant-velocity Kalman filter has to commit to one guess about how hard
the target might maneuver. During the turn, that guess is badly wrong. Its reported
uncertainty actually contracts while its error grows eightfold, so it isn't just
imprecise, it's confidently wrong. NEES peaks near 1470 against an ideal of 6.
The fix: an adaptive model bank
An interacting multiple model filter refuses to make that choice. It runs several
filters in parallel, one tuned for quiet cruise and one for a hard maneuver, and
lets each measurement vote on which is currently right. The part that matters is
that the combined uncertainty carries a term for how much the models disagree, so
the moment a maneuver starts the reported uncertainty grows, before any single
model has had time to adapt.
Baseline scenario, identical measurements fed to both filters
| Metric | Single filter | Model bank |
| Mean NEES (ideal 6) | 174.18 | 4.33 |
| Peak NEES | 1470 | 45 |
| Position error (RMSE) | 29.46 m | 10.35 m |
| Worst 5% of frames | 75.40 m | 18.27 m |
| Frames over-confident | 23.9% | 3.7% |
Across 200 randomized runs, 186 of the 200 single-filter runs came out badly
over-confident. None of the model-bank runs did.
The model bank is not free, though, and the Benign cruise scenario is there to
show the bill. It is the baseline flight with the maneuver taken out and nothing
else changed, so the constant-velocity assumption genuinely holds. On that run
the single filter is the better estimator: mean NEES 3.67 against 2.94, and 8.26 m
of position error against 9.28 m. With no maneuver to detect, the disagreement
term just inflates the covariance for nothing.
Reading the scene
The Scenario menu picks which flight to replay and the EKF/IMM toggle picks which
filter's recording of that same flight to show, so switching between them compares
the two estimators rather than two different flights. Scrub to the turn at
t = 25 s and watch the purple uncertainty ellipsoid as you flip
between them.
- The path the target actually flew, which the filter never sees
- What the filter believes, built only from noisy measurements
- The filter's claimed uncertainty, exaggerated by the Scale control so it stays visible at this range
- Stretches where the filter was provably over-confident
On a model-bank run, the Maneuver model meter shows the filter's own live belief
that the target is turning. It climbs from about 30% to nearly 90% within a second
of the real maneuver starting.
How it is built
The simulation and both estimators are dependency-free C++20, and the analysis and
plots are Python. This viewer is plain JavaScript with canvas 2D: no framework, no
build step, no dependencies. It replays recorded CSV output rather than driving the
simulation, so every frame you see here is reproducible from a scenario file plus a
seed.
This is a portfolio-grade synthetic model, not an operational sensor simulation. It
deliberately leaves out Earth curvature, terrain, clutter, false alarms, data
association and sensor bias.