fix: keep fallback emergency status paired with admitted aircraft

This commit is contained in:
Mazeyar Moeini Feizabadi committed 2026-10-13 18:11:41 +02:00
1 parent 45e9c5ab0a
commit c349a89bb2
2 files changed
+43 -14

No files matched your search

+24 -14
View File
@@ -43,7 +43,14 @@ export function normalizeAdsbLolAircraftState(aircraft, nowSeconds) {
.toLowerCase(); .toLowerCase();
const latitude = finiteNumber(aircraft?.lat); const latitude = finiteNumber(aircraft?.lat);
const longitude = finiteNumber(aircraft?.lon); const longitude = finiteNumber(aircraft?.lon);
if (!hex || latitude === null || longitude === null) return null; if (
!hex ||
latitude === null ||
longitude === null ||
Math.abs(latitude) > 90 ||
Math.abs(longitude) > 180
)
return null;
const seenPosition = Math.max( const seenPosition = Math.max(
0, 0,
@@ -95,21 +102,24 @@ export function normalizeAdsbLolPointResponse(payload) {
responseNow > 10_000_000_000 ? responseNow / 1000 : responseNow, responseNow > 10_000_000_000 ? responseNow / 1000 : responseNow,
); );
const aircraft = Array.isArray(payload?.ac) ? payload.ac : []; const aircraft = Array.isArray(payload?.ac) ? payload.ac : [];
const states = aircraft const states = [];
.map((aircraft) => normalizeAdsbLolAircraftState(aircraft, nowSeconds))
.filter(Boolean);
// Keep the OpenSky vectors unchanged. The regional fallback's extra readsb // Keep the OpenSky vectors unchanged. The regional fallback's extra readsb
// status travels alongside them, keyed only to admitted aircraft. // status travels alongside them, keyed only to admitted aircraft.
const admitted = new Set(states.map((row) => row[0])); const admitted = new Set();
const emergencyByIcao = Object.fromEntries( const statuses = [];
aircraft.flatMap((row) => { for (const row of aircraft) {
const id = String(row?.hex || '') const state = normalizeAdsbLolAircraftState(row, nowSeconds);
.trim() if (!state) continue;
.toLowerCase(); states.push(state);
if (!admitted.has(id) || typeof row?.emergency !== 'string') return []; // The live contract admits the first valid vector for each ICAO. Its
return [[id, row.emergency.trim().toLowerCase()]]; // status must come from that same row, never an invalid or later duplicate.
}), const id = state[0];
); if (admitted.has(id)) continue;
admitted.add(id);
if (typeof row.emergency === 'string')
statuses.push([id, row.emergency.trim().toLowerCase()]);
}
const emergencyByIcao = Object.fromEntries(statuses);
return { return {
time: nowSeconds, time: nowSeconds,
states, states,
@@ -16,6 +16,25 @@ import { createGevActionRunner } from '../voice/gevActions.js';
const NOW_MS = 1800000000000; const NOW_MS = 1800000000000;
test('fallback status belongs to the first valid admitted aircraft row', () => {
const valid = { hex: 'a1b2c3', lat: 30, lon: -97, squawk: '1200' };
for (const status of [undefined, 'general', 'none']) {
const payload = normalizeAdsbLolPointResponse({
now: NOW_MS,
ac: [
{ ...valid, lat: 100, emergency: 'general' },
{ ...valid, emergency: status },
{ ...valid, lat: null, emergency: 'general' },
{ ...valid, emergency: status === 'general' ? 'none' : 'general' },
],
});
const snapshot = openSkySnapshot(payload, { now: NOW_MS });
assert.equal(snapshot.records.length, 1);
assert.equal(snapshot.records[0].emergency, status ?? null);
assert.equal(snapshot.records[0].latitude, 30);
}
});
function civilStore() { function civilStore() {
return new FlightRecords({ return new FlightRecords({
geoidHeight: () => 0, geoidHeight: () => 0,