Track Fusion — Gating Explorer

Holocron · contact correlation

Where track fusion works,
and where it quietly doesn't

A grid search only finds every duplicate if the neighbourhood it scans is at least as wide as the distance gate. A degree of longitude is 2.75 km at 34.5°N but only 1.09 km at 71°N, so a fixed 3×3 ring of 0.03° cells stops covering a 2886 m gate — everywhere in the theatre, and worst in the north. The gates say duplicate; the index never presents the pair. Drag the second contact below and watch the two disagree.

Instrument

The gate stack, and whether the pair is ever reached

Contact A is fixed at centre. Drag B anywhere on the plot. Grid cells are drawn true to scale, so they visibly narrow as you raise the latitude.

Plan view

A — canonical B — drag me 2886 m distance gate prototype 3×3 ring derived ring

Gates

A ↔ B
Prototype — —
Reworked — —
Latitude 52.0°N
B heading 090°
B speed 150
Jump to

Evidence

How many true duplicates each ring can reach

Every sampled pair below is a genuine duplicate: inside the distance gate, matching heading and speed. The only question is whether the search ring ever puts the two in the same comparison.

240 duplicate pairs sampled per latitude, separations spread across the gate, sub-cell phase varied.

Demonstrations

Cases the suite pins down

Each of these is an executable test in src/utils/trackFusion.test.ts. The first three are what fusion is for; the rest are the judgement calls.

Changes

What moved, and why it had to

BehaviourPrototypeReworked
Search neighbourhood Fixed 3×3 cells — narrower than the gate at every European latitude Ring derived from the gate at the worst latitude it will scan
Absent heading / speed NaN > 2 is false, so the gate silently passed Reported unmeasurable; motionPolicy decides, and refuses by default
Which report survives Whichever the map iterated first — could flip between frames Union-find by minimum identifier; a pure function of the set
Retiring a track Cleanup guarded by a predicate that always returned false — cells never released One reconciliation point; a stale entry is structurally impossible
Cost per message Full rescan and flag reset for every asset, on every message Index updated per asset; clustering throttled to one pass per 150 ms
React state Descriptions mutated inside the setState updater Pure updater; identity preserved when no verdict changed