* Merge a primary (radio) list with a secondary (API) list by hex, preferring * whichever fix is fresher. Radio is biased a couple seconds so it wins while * it's tracking; the API takes over only once the radio fix goes stale.
(radio: Aircraft[], api: Aircraft[])
| 113 | * it's tracking; the API takes over only once the radio fix goes stale. |
| 114 | */ |
| 115 | function mergeSources(radio: Aircraft[], api: Aircraft[]): Aircraft[] { |
| 116 | const byHex = new Map<string, Aircraft>(); |
| 117 | for (const a of api) byHex.set(a.hex, a); |
| 118 | for (const r of radio) { |
| 119 | const existing = byHex.get(r.hex); |
| 120 | if (!existing) { |
| 121 | byHex.set(r.hex, r); |
| 122 | continue; |
| 123 | } |
| 124 | const rSeen = (r.seen ?? 0) - 2; // bias toward the local radio |
| 125 | const aSeen = existing.seen ?? 999; |
| 126 | byHex.set(r.hex, rSeen <= aSeen ? r : existing); |
| 127 | } |
| 128 | return [...byHex.values()]; |
| 129 | } |
| 130 | |
| 131 | /** Enrichment we've resolved for an aircraft, kept sticky for its session. */ |
| 132 | interface StickyEnrichment { |