Merge rebased aircraft emergency contribution for HAL-522 validation

# Conflicts:
#	DATA_SOURCES.md
This commit is contained in:
Mazeyar Moeini Feizabadi committed 2026-10-13 17:25:47 +02:00
commit 69e8b18e02
31 files changed
+1448 -1207

No files matched your search

+89
View File
@@ -0,0 +1,89 @@
// src/data/aircraftEmergency.js
/**
* Transponder emergency status for one aircraft, from the Mode A squawk and
* the ADS-B emergency/priority field.
*
* Emergency squawks are ICAO-standard worldwide: 7500 unlawful interference,
* 7600 radio failure, 7700 general emergency. readsb (adsb.lol) also reports
* `emergency`, "a superset of the 7x00 squawks": none, general, lifeguard,
* minfuel, nordo, unlawful, downed, reserved. OpenSky state vectors carry the
* squawk only.
*
* The status is what the transponder broadcast in THIS poll, never a finding:
* crews pass through 7x00 codes while dialling, and a real emergency clears
* when the code does. Callers keep it per poll (not sticky).
*/
/** @constant {string} Tint for aircraft broadcasting an emergency (not a priority). */
export const EMERGENCY_TINT_CSS = '#FF4444';
const SQUAWK_KINDS = { 7500: 'unlawful', 7600: 'nordo', 7700: 'general' };
const KINDS = {
general: { label: 'General emergency', severity: 'emergency' },
unlawful: { label: 'Unlawful interference', severity: 'emergency' },
nordo: { label: 'Radio failure', severity: 'emergency' },
downed: { label: 'Downed aircraft', severity: 'emergency' },
minfuel: { label: 'Minimum fuel', severity: 'priority' },
lifeguard: { label: 'Lifeguard (medical priority)', severity: 'priority' },
};
/**
* A Mode A code as its four octal digits, else null.
* @param {unknown} value
* @returns {string|null}
*/
export function normalizeSquawk(value) {
const code = String(value ?? '').trim();
return /^[0-7]{4}$/.test(code) ? code : null;
}
/**
* Emergency status from one observation. An explicit ADS-B status wins; a
* 7x00 squawk counts even when the ADS-B field says `none`.
* @param {{squawk?: unknown, emergency?: unknown}} [observation]
* @returns {{kind: string, label: string, severity: 'emergency'|'priority',
* squawk: string|null, source: 'ads-b'|'squawk'}|null} `source` is
* 'squawk' when the Mode A code itself names the status (so it can be
* quoted), else 'ads-b'.
*/
export function aircraftEmergency({ squawk, emergency } = {}) {
const code = normalizeSquawk(squawk);
const reported = String(emergency ?? '')
.trim()
.toLowerCase();
const fromAdsb = Object.hasOwn(KINDS, reported);
const kind = fromAdsb ? reported : (SQUAWK_KINDS[code] ?? null);
if (!kind) return null;
return {
kind,
...KINDS[kind],
squawk: code,
source: SQUAWK_KINDS[code] === kind ? 'squawk' : 'ads-b',
};
}
/**
* How a summary may phrase the status without flattening it into an incident:
* quote the code when the Mode A squawk itself names the status, otherwise
* attribute it to the ADS-B emergency field.
* @param {{kind?: string, squawk?: string|null, source?: string}|null|undefined} status
* @returns {string|null} e.g. 'broadcasting squawk 7700 (general emergency)'
* or 'ADS-B reports general emergency'; null when there is no status.
*/
export function emergencyBroadcastPhrase(status) {
if (!status || !Object.hasOwn(KINDS, status.kind)) return null;
const label = KINDS[status.kind].label.toLowerCase();
return status.source === 'squawk' && status.squawk
? `broadcasting squawk ${status.squawk} (${label})`
: `ADS-B reports ${label}`;
}
/**
* True when the aircraft is broadcasting an emergency (not a priority status).
* @param {{severity?: string}|null|undefined} status
* @returns {boolean}
*/
export function isEmergency(status) {
return status?.severity === 'emergency';
}
+84
View File
@@ -0,0 +1,84 @@
// src/data/aircraftEmergency.test.mjs
import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
aircraftEmergency,
emergencyBroadcastPhrase,
isEmergency,
normalizeSquawk,
} from './aircraftEmergency.js';
test('the three ICAO emergency squawks map to their kinds', () => {
assert.equal(aircraftEmergency({ squawk: '7500' }).kind, 'unlawful');
assert.equal(aircraftEmergency({ squawk: '7600' }).kind, 'nordo');
const general = aircraftEmergency({ squawk: ' 7700 ' });
assert.deepEqual(general, {
kind: 'general',
label: 'General emergency',
severity: 'emergency',
squawk: '7700',
source: 'squawk',
});
});
test('ordinary codes, blanks and non-octal values are not emergencies', () => {
for (const squawk of ['1200', '7000', '2000', '', null, undefined, '7800'])
assert.equal(aircraftEmergency({ squawk }), null, String(squawk));
assert.equal(aircraftEmergency(), null);
assert.equal(normalizeSquawk('7800'), null);
assert.equal(normalizeSquawk(7700), '7700');
assert.equal(normalizeSquawk('770'), null);
});
test('the ADS-B emergency field wins, and `none` defers to the squawk', () => {
const minfuel = aircraftEmergency({ squawk: '4521', emergency: 'MinFuel' });
assert.equal(minfuel.kind, 'minfuel');
assert.equal(minfuel.severity, 'priority');
assert.equal(minfuel.source, 'ads-b');
assert.equal(minfuel.squawk, '4521');
assert.equal(
aircraftEmergency({ squawk: '7700', emergency: 'none' }).source,
'squawk',
);
assert.equal(
aircraftEmergency({ squawk: '7700', emergency: 'general' }).source,
'squawk',
'a code that names the status stays quotable',
);
assert.equal(
aircraftEmergency({ squawk: '1200', emergency: 'general' }).source,
'ads-b',
);
assert.equal(aircraftEmergency({ emergency: 'downed' }).kind, 'downed');
assert.equal(aircraftEmergency({ emergency: 'none' }), null);
assert.equal(aircraftEmergency({ emergency: 'reserved' }), null);
});
test('priority statuses are reported but are not emergencies', () => {
assert.equal(isEmergency(aircraftEmergency({ squawk: '7700' })), true);
assert.equal(
isEmergency(aircraftEmergency({ emergency: 'lifeguard' })),
false,
);
assert.equal(isEmergency(aircraftEmergency({ emergency: 'minfuel' })), false);
assert.equal(isEmergency(null), false);
});
test('the broadcast phrase quotes a naming code, else attributes ADS-B', () => {
assert.equal(
emergencyBroadcastPhrase(aircraftEmergency({ squawk: '7700' })),
'broadcasting squawk 7700 (general emergency)',
);
assert.equal(
emergencyBroadcastPhrase(
aircraftEmergency({ squawk: '1200', emergency: 'general' }),
),
'ADS-B reports general emergency',
);
assert.equal(
emergencyBroadcastPhrase(aircraftEmergency({ emergency: 'lifeguard' })),
'ADS-B reports lifeguard (medical priority)',
);
assert.equal(emergencyBroadcastPhrase(null), null);
assert.equal(emergencyBroadcastPhrase({ kind: 'reserved' }), null);
});
+152
View File
@@ -0,0 +1,152 @@
// src/data/aircraftEmergencyAnalyst.test.mjs
// Source-to-analyst fixture: an OpenSky state vector squawking 7700 and an
// adsb.lol (readsb) aircraft whose ADS-B `emergency` field says `general`
// on an ordinary code must reach the analyst with the same kind but distinct
// provenance, so a summary never flattens one into the other.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { openSkySnapshot, readsbSnapshot } from '../sources/live/index.js';
import { FlightRecords } from '../layers/flights/records.js';
import { MilitaryFlightRecords } from '../layers/military/records.js';
import { mapAnalystRecord as mapFlight } from './flights.js';
import { mapAnalystRecord as mapMilitary } from './militaryFlights.js';
import { createAnalystEngine } from './analystEngine.js';
const NOW_MS = 1800000000000;
// OpenSky /states/all: [icao24, callsign, origin_country, time_position,
// last_contact, lon, lat, baro_alt, on_ground, velocity, true_track,
// vertical_rate, sensors, geo_alt, squawk, spi, position_source, category].
const OPENSKY = {
time: NOW_MS / 1000,
states: [
[
'a1b2c3',
'SWA696 ',
'United States',
NOW_MS / 1000 - 6,
NOW_MS / 1000 - 3,
-97.67,
30.19,
3000,
false,
150,
180,
-5,
null,
3040,
'7700',
false,
0,
4,
],
],
};
// adsb.lol /v2/mil: readsb aircraft with `seen`/`seen_pos` ages in seconds.
const ADSB_LOL = {
ac: [
{
hex: 'ae01ce',
flight: 'RCH123 ',
t: 'C17',
lat: 31.1,
lon: -97.4,
alt_baro: 18000,
gs: 300,
track: 90,
squawk: '1200',
emergency: 'general',
seen: 1.5,
seen_pos: 2,
},
],
};
function civilAnalystRecord() {
const [row] = openSkySnapshot(OPENSKY, { now: NOW_MS }).records;
const store = new FlightRecords({
geoidHeight: () => 0,
cachedGroundFloor: () => null,
floorAltitudeM: (altitude) => altitude,
approxDistanceKm: () => 0,
});
const { meta } = store.receive(row, {
viewerLatDeg: null,
viewerLonDeg: null,
trackedId: null,
floorWarmPoints: [],
});
return mapFlight(row.id, meta);
}
function militaryAnalystRecord() {
const [row] = readsbSnapshot(ADSB_LOL, {
observedAtMs: NOW_MS,
now: NOW_MS,
}).records;
const store = new MilitaryFlightRecords({
geoidHeight: () => 0,
cachedGroundFloor: () => null,
floorAltitudeM: (altitude) => altitude,
});
const { meta } = store.receive(row, {
observedAtMs: NOW_MS,
floorWarmPoints: [],
modelOwnsVisual: false,
});
return mapMilitary(row.id, meta);
}
test('OpenSky 7700 and adsb.lol emergency=general differ only in provenance', () => {
const civil = civilAnalystRecord();
const military = militaryAnalystRecord();
assert.equal(civil.emergency, true);
assert.equal(civil.emergencyKind, 'general');
assert.equal(civil.squawk, '7700');
assert.equal(civil.emergencySource, 'squawk');
assert.equal(
civil.emergencyBroadcast,
'broadcasting squawk 7700 (general emergency)',
);
assert.equal(civil.observedAtMs, NOW_MS - 3000, 'OpenSky last_contact');
assert.equal(military.emergency, true);
assert.equal(military.emergencyKind, 'general');
assert.equal(military.squawk, '1200', 'the code is not an emergency code');
assert.equal(military.emergencySource, 'ads-b');
assert.equal(military.emergencyBroadcast, 'ADS-B reports general emergency');
assert.equal(military.observedAtMs, NOW_MS - 1500, 'snapshot time - seen');
});
test('the analyst can filter emergencies by source', async () => {
const records = {
flights: [civilAnalystRecord()],
military: [militaryAnalystRecord()],
};
const engine = createAnalystEngine({
getRecords: (key) => records[key] || [],
resolveRegionRing: async () => null,
getViewContext: () => ({ lat: 30.5, lon: -97.5, viewRadiusKm: 500 }),
});
const query = (value) =>
engine.query({
layers: ['flights', 'military'],
scope: { kind: 'anywhere' },
filters: [
{ field: 'emergency', op: 'eq', value: true },
{ field: 'emergencySource', op: 'eq', value },
],
});
const bySquawk = await query('squawk');
assert.deepEqual(
bySquawk.items.map((item) => [item.icao24, item.emergencyBroadcast]),
[['a1b2c3', 'broadcasting squawk 7700 (general emergency)']],
);
const byAdsb = await query('ads-b');
assert.deepEqual(
byAdsb.items.map((item) => [item.icao24, item.emergencyBroadcast]),
[['ae01ce', 'ADS-B reports general emergency']],
);
});
+15 -1
View File
@@ -7,7 +7,7 @@ const TEXLAND = { name: 'Texland', ring: [[-100, 28], [-94, 28], [-94, 33], [-10
const FLIGHTS = [
{ id: 'SWA1', lat: 30.2, lon: -97.7, altitudeM: 11000, speedMps: 240, military: false, onGround: false, routeOrigin: 'AUS', routeDestination: 'LAX' },
{ id: 'RCH01', lat: 31.0, lon: -97.0, altitudeM: 13500, speedMps: 250, military: true, onGround: false, routeOrigin: null, routeDestination: null },
{ id: 'N123', lat: 45.0, lon: -122.0, altitudeM: 2000, speedMps: 80, military: false, onGround: false, routeOrigin: null, routeDestination: null },
{ id: 'N123', lat: 45.0, lon: -122.0, altitudeM: 2000, speedMps: 80, military: false, onGround: false, squawk: '7700', emergency: true, emergencyKind: 'general', routeOrigin: null, routeDestination: null },
{ id: 'GND1', lat: 30.19, lon: -97.66, altitudeM: 150, speedMps: 5, military: false, onGround: true, routeOrigin: null, routeDestination: null },
];
const SHIPS = [
@@ -325,6 +325,19 @@ test('analyst: route fields queryable from cached enrichment only', async () =>
assert.deepEqual(r.items.map((i) => i.id), ['SWA1'], 'null route fields never match');
});
test('analyst: aircraft broadcasting an emergency are filterable by flag and code', async () => {
const flagged = await makeEngine().query({
layers: ['flights'], scope: { kind: 'anywhere' },
filters: [{ field: 'emergency', op: 'eq', value: true }],
});
assert.deepEqual(flagged.items.map((i) => i.id), ['N123']);
const coded = await makeEngine().query({
layers: ['flights'], scope: { kind: 'anywhere' },
filters: [{ field: 'squawk', op: 'eq', value: '7700' }],
});
assert.deepEqual(coded.items.map((i) => i.id), ['N123']);
});
test('analyst: satellites and local infrastructure are queryable layers', async () => {
const SATS = [
{ id: 'ISS (ZARYA)', noradId: '25544', name: 'ISS (ZARYA)', lat: 30.3, lon: -97.7, altitudeM: 410000, satelliteClass: 'STATION · ISS', group: 'stations' },
@@ -506,6 +519,7 @@ test('time fields filter by ISO and derive ageHours; the feed window is exposed'
assert.equal(age.coverage.layersQueried[0].window, 'last 24 h');
});
test('follow-up provenance stays attached to old rows after a feed recovers', async () => {
let state = 'stale';
const engine = createAnalystEngine({
+242 -879
View File
File diff suppressed because it is too large. Load diff
+116 -298
View File
@@ -24,12 +24,8 @@ test('share-Follow absence requires an accepted adsb.lol snapshot', async () =>
(await militaryFlightsLayer.resolveTrackingRestoreTarget('ae1234')).status,
'source-unavailable',
);
_setMilitaryTrackingRefreshOutcomeForTest({
status: 'accepted',
ids: ['different'],
});
const missing =
await militaryFlightsLayer.resolveTrackingRestoreTarget('ae1234');
_setMilitaryTrackingRefreshOutcomeForTest({ status: 'accepted', ids: ['different'] });
const missing = await militaryFlightsLayer.resolveTrackingRestoreTarget('ae1234');
assert.equal(missing.status, 'missing');
assert.equal(missing.reason, 'target-absent-from-snapshot');
});
@@ -71,11 +67,34 @@ test('military analyst record: full record maps every contract field', () => {
aircraftClass: 'widebody',
originCountry: null,
operator: 'United States Air Force',
squawk: null,
emergency: false,
emergencyKind: null,
emergencySource: null,
emergencyBroadcast: null,
observedAtMs: null,
routeOrigin: null,
routeDestination: null,
});
});
test('military analyst record: emergency flag is only raised for emergency severity', () => {
const hijack = mapAnalystRecord('ae01ce', {
...FULL_INFO,
squawk: '7500',
emergency: { kind: 'unlawful', severity: 'emergency', squawk: '7500' },
});
assert.equal(hijack.squawk, '7500');
assert.equal(hijack.emergency, true);
assert.equal(hijack.emergencyKind, 'unlawful');
const lifeguard = mapAnalystRecord('ae01ce', {
...FULL_INFO,
emergency: { kind: 'lifeguard', severity: 'priority', squawk: null },
});
assert.equal(lifeguard.emergency, false);
assert.equal(lifeguard.emergencyKind, 'lifeguard');
});
test('military analyst record: military is ALWAYS true, routes/country always null', () => {
const r = mapAnalystRecord('ae01ce', undefined);
assert.equal(r.military, true);
@@ -85,14 +104,8 @@ test('military analyst record: military is ALWAYS true, routes/country always nu
});
test('military analyst record: no callsign falls back to registration, then icao24', () => {
assert.equal(
mapAnalystRecord('ae01ce', { ...FULL_INFO, callsign: '' }).id,
'05-8152',
);
assert.equal(
mapAnalystRecord('ae01ce', { callsign: '', registration: ' ' }).id,
'ae01ce',
);
assert.equal(mapAnalystRecord('ae01ce', { ...FULL_INFO, callsign: '' }).id, '05-8152');
assert.equal(mapAnalystRecord('ae01ce', { callsign: '', registration: ' ' }).id, 'ae01ce');
});
test('military analyst record: empty info yields nulls, never NaN/undefined', () => {
@@ -101,8 +114,7 @@ test('military analyst record: empty info yields nulls, never NaN/undefined', ()
assert.equal(r.onGround, false);
for (const [key, value] of Object.entries(r)) {
assert.notEqual(value, undefined, `${key} must not be undefined`);
if (typeof value === 'number')
assert.ok(Number.isFinite(value), `${key} must not be NaN`);
if (typeof value === 'number') assert.ok(Number.isFinite(value), `${key} must not be NaN`);
}
});
@@ -114,17 +126,12 @@ test('military analyst record: output is JSON-safe (no Cesium types)', () => {
test('military first update forwards caller cancellation into the feed request', async () => {
const realFetch = globalThis.fetch;
let observedSignal = null;
globalThis.fetch = (_url, options = {}) =>
new Promise((_resolve, reject) => {
observedSignal = options.signal;
options.signal?.addEventListener(
'abort',
() => {
reject(new DOMException('aborted', 'AbortError'));
},
{ once: true },
);
});
globalThis.fetch = (_url, options = {}) => new Promise((_resolve, reject) => {
observedSignal = options.signal;
options.signal?.addEventListener('abort', () => {
reject(new DOMException('aborted', 'AbortError'));
}, { once: true });
});
try {
const controller = new AbortController();
const work = militaryFlightsLayer.update({}, { signal: controller.signal });
@@ -154,22 +161,11 @@ test('nonempty adsb.lol payload with zero usable rows cannot prove share target
json: async () => ({ ac: [null, {}, { hex: 'ae1234' }] }),
});
try {
await militaryFlightsLayer.update({
camera: { positionCartographic: null },
scene: {},
});
const resolution =
await militaryFlightsLayer.resolveTrackingRestoreTarget('ae1234');
await militaryFlightsLayer.update({ camera: { positionCartographic: null }, scene: {} });
const resolution = await militaryFlightsLayer.resolveTrackingRestoreTarget('ae1234');
assert.equal(resolution.status, 'source-unavailable');
assert.match(
militaryFlightsLayer.getStats().error,
/Malformed adsb\.lol aircraft rows/,
);
assert.equal(
militaryFlightsLayer.getAnalystRecords().length,
1,
'warm aircraft data is preserved',
);
assert.match(militaryFlightsLayer.getStats().error, /Malformed adsb\.lol aircraft rows/);
assert.equal(militaryFlightsLayer.getAnalystRecords().length, 1, 'warm aircraft data is preserved');
} finally {
globalThis.fetch = realFetch;
}
@@ -215,26 +211,21 @@ test('military poll refreshes tracked callsign/altitude/kts and marks a missed p
ok: true,
status: 200,
json: async () => ({
ac:
poll++ === 0
? [
{
hex: icao24,
lon: -96.9,
lat: 31.1,
alt_baro: 28_000,
alt_geom: 28_100,
track: 95,
gs: 400,
seen: 0,
seen_pos: 0,
flight: 'RCH451 ',
t: 'C17',
r: '05-8152',
ownOp: 'United States Air Force',
},
]
: [],
ac: poll++ === 0 ? [{
hex: icao24,
lon: -96.9,
lat: 31.1,
alt_baro: 28_000,
alt_geom: 28_100,
track: 95,
gs: 400,
seen: 0,
seen_pos: 0,
flight: 'RCH451 ',
t: 'C17',
r: '05-8152',
ownOp: 'United States Air Force',
}] : [],
}),
});
@@ -259,10 +250,7 @@ test('cached military snapshots retain their observation time and expose stale f
_setTrackedMilitaryRefreshStateForTest({
icao24: 'ae01ce',
entity: { gevLabelModel: { title: 'RCH451', details: [] } },
billboard: {
position: Cesium.Cartesian3.fromDegrees(-97, 31, 8000),
show: false,
},
billboard: { position: Cesium.Cartesian3.fromDegrees(-97, 31, 8000), show: false },
billboardCollection: { show: false, remove() {} },
viewer,
history,
@@ -300,18 +288,11 @@ test('cached military snapshots retain their observation time and expose stale f
await militaryFlightsLayer.update(viewer);
assert.equal(history.length, 1);
assert.equal(history[0].epochMs, receivedAt - 2000);
for (const [status, age] of [
['HIT', 5000],
['STALE', 45000],
]) {
for (const [status, age] of [['HIT', 5000], ['STALE', 45000]]) {
cache = status;
now = receivedAt + age;
await militaryFlightsLayer.update(viewer);
assert.equal(
history.length,
1,
'replayed positions cannot create a new fix',
);
assert.equal(history.length, 1, 'replayed positions cannot create a new fix');
assert.equal(militaryFlightsLayer.getStats().lastUpdate, receivedAt);
assert.equal(militaryFlightsLayer.getStats().stale, status === 'STALE');
assert.equal(militaryFlightsLayer.getStats().error, null);
@@ -338,18 +319,14 @@ test('real military track path creates no native label and publishes every cache
trackedEntityChanged,
clock: { currentTime: now },
camera: {
cancelFlight() {
cancelledFlights += 1;
},
cancelFlight() { cancelledFlights += 1; },
position: new Cesium.Cartesian3(0, -10_000, 7_000),
positionWC: new Cesium.Cartesian3(0, -10_000, 7_000),
direction: Cesium.Cartesian3.UNIT_Y,
transform: Cesium.Matrix4.IDENTITY,
viewMatrix: Cesium.Matrix4.IDENTITY,
frustum: { projectionMatrix: Cesium.Matrix4.IDENTITY },
lookAtTransform() {
appliedFrames += 1;
},
lookAtTransform() { appliedFrames += 1; },
},
scene: {
canvas: { clientWidth: 1600, clientHeight: 900 },
@@ -388,12 +365,9 @@ test('real military track path creates no native label and publishes every cache
const realFetch = globalThis.fetch;
globalThis.window = new EventTarget();
const selectionEvents = [];
globalThis.window.addEventListener(
'gev:awareness-subject-selected',
(event) => {
selectionEvents.push(event.detail);
},
);
globalThis.window.addEventListener('gev:awareness-subject-selected', (event) => {
selectionEvents.push(event.detail);
});
globalThis.fetch = async () => ({ ok: false });
_setTrackedOverlayHostForTest(host);
try {
@@ -405,15 +379,13 @@ test('real military track path creates no native label and publishes every cache
billboardCollection: { show: true, remove() {} },
viewer,
tracked: false,
history: [
{
time: now,
epochMs: Date.now(),
position,
velocity: 231.5,
track: 92.1,
},
],
history: [{
time: now,
epochMs: Date.now(),
position,
velocity: 231.5,
track: 92.1,
}],
meta: {
...FULL_INFO,
type: 'C17',
@@ -423,72 +395,34 @@ test('real military track path creates no native label and publishes every cache
},
});
assert.equal(
militaryFlightsLayer.trackById(icao24, { origin: 'programmatic' }),
true,
);
assert.equal(militaryFlightsLayer.trackById(icao24, { origin: 'programmatic' }), true);
const entity = viewer.trackedEntity;
assert.ok(
entity instanceof Cesium.Entity,
'trackById must create the real Cesium entity',
);
assert.ok(entity instanceof Cesium.Entity, 'trackById must create the real Cesium entity');
assert.equal(entity.label, undefined);
assert.ok(
entities.values.every((candidate) => candidate.label === undefined),
);
assert.ok(entities.values.every((candidate) => candidate.label === undefined));
assert.deepEqual(entity.gevLabelModel, {
title: 'RCH451',
details: ['C17 · 05-8152', 'United States Air Force · 28000 ft · 450 kt'],
details: [
'C17 · 05-8152',
'United States Air Force · 28000 ft · 450 kt',
],
accent: '#ffd166',
});
viewer.scene.preUpdate.raiseEvent();
const initialAppliedFrames = appliedFrames;
const initialCancelledFlights = cancelledFlights;
assert.equal(
militaryFlightsLayer.trackById(icao24, { origin: 'user' }),
true,
);
assert.equal(
cancelledFlights,
initialCancelledFlights,
'ordinary repeated tracking stays camera-idempotent',
);
assert.equal(
selectionEvents.at(-1)?.origin,
'user',
'same-target selection upgrades durable authority',
);
assert.equal(militaryFlightsLayer.trackById(icao24, { origin: 'user' }), true);
assert.equal(cancelledFlights, initialCancelledFlights, 'ordinary repeated tracking stays camera-idempotent');
assert.equal(selectionEvents.at(-1)?.origin, 'user', 'same-target selection upgrades durable authority');
assert.equal(entity.gevSelectionOrigin, 'user');
assert.equal(
militaryFlightsLayer.refocusTrackedById('different-flight'),
false,
);
assert.equal(
militaryFlightsLayer.refocusTrackedById(icao24, { origin: 'voice' }),
true,
);
assert.equal(
selectionEvents.at(-1)?.origin,
'voice',
'same-target refocus forwards explicit authority',
);
assert.equal(militaryFlightsLayer.refocusTrackedById('different-flight'), false);
assert.equal(militaryFlightsLayer.refocusTrackedById(icao24, { origin: 'voice' }), true);
assert.equal(selectionEvents.at(-1)?.origin, 'voice', 'same-target refocus forwards explicit authority');
assert.equal(militaryFlightsLayer.refocusTrackedById(icao24), true);
viewer.scene.preUpdate.raiseEvent();
assert.equal(
viewer.trackedEntity,
entity,
'refocus must retain the exact tracked entity',
);
assert.equal(
appliedFrames,
initialAppliedFrames + 1,
'repeated refocus keeps one camera-frame owner',
);
assert.equal(
cancelledFlights,
initialCancelledFlights + 2,
'only explicit refocus requests cancel camera flights',
);
assert.equal(viewer.trackedEntity, entity, 'refocus must retain the exact tracked entity');
assert.equal(appliedFrames, initialAppliedFrames + 1, 'repeated refocus keeps one camera-frame owner');
assert.equal(cancelledFlights, initialCancelledFlights + 2, 'only explicit refocus requests cancel camera flights');
const publication = publications.at(-1);
assert.equal(publication.sourceId, 'tracked');
const entry = publication.entries[0];
@@ -499,40 +433,23 @@ test('real military track path creates no native label and publishes every cache
const display = entity.position.getValue(now);
assert.ok(display, 'tracked position callback must seed its frame cache');
assert.equal(
entry.position(),
display,
'host must reuse the exact cached display Cartesian',
);
assert.equal(entry.position(), display, 'host must reuse the exact cached display Cartesian');
militaryFlightsLayer.setParams(
{ selectedMilitaryTrackingId: 'late001' },
{ origin: 'share-restore' },
);
assert.equal(_pendingMilitaryTrackingRestoreForTest(), 'late001');
assert.equal(
militaryFlightsLayer.trackById(icao24, { origin: 'user' }),
true,
);
assert.equal(
_pendingMilitaryTrackingRestoreForTest(),
null,
'new user selection cancels stale restore',
);
assert.equal(militaryFlightsLayer.trackById(icao24, { origin: 'user' }), true);
assert.equal(_pendingMilitaryTrackingRestoreForTest(), null, 'new user selection cancels stale restore');
_addMilitaryTrackingCandidateForTest({
icao24: 'late001',
billboard: {
...billboard,
position: Cesium.Cartesian3.fromDegrees(-96.9, 31.1, 8_000),
},
billboard: { ...billboard, position: Cesium.Cartesian3.fromDegrees(-96.9, 31.1, 8_000) },
meta: { ...FULL_INFO, callsign: 'LATE1', rawLat: 31.1, rawLon: -96.9 },
history: [],
});
_applyPendingMilitaryTrackingRestoreForTest();
assert.equal(
militaryFlightsLayer.getParams().selectedMilitaryTrackingId,
icao24,
);
assert.equal(militaryFlightsLayer.getParams().selectedMilitaryTrackingId, icao24);
militaryFlightsLayer.stopTracking();
militaryFlightsLayer.setParams(
@@ -541,25 +458,15 @@ test('real military track path creates no native label and publishes every cache
);
assert.equal(_pendingMilitaryTrackingRestoreForTest(), 'late002');
militaryFlightsLayer.stopTracking({ origin: 'user' });
assert.equal(
_pendingMilitaryTrackingRestoreForTest(),
null,
'explicit clear cancels stale restore',
);
assert.equal(_pendingMilitaryTrackingRestoreForTest(), null, 'explicit clear cancels stale restore');
_addMilitaryTrackingCandidateForTest({
icao24: 'late002',
billboard: {
...billboard,
position: Cesium.Cartesian3.fromDegrees(-96.8, 31.2, 7_000),
},
billboard: { ...billboard, position: Cesium.Cartesian3.fromDegrees(-96.8, 31.2, 7_000) },
meta: { ...FULL_INFO, callsign: 'LATE2', rawLat: 31.2, rawLon: -96.8 },
history: [],
});
_applyPendingMilitaryTrackingRestoreForTest();
assert.equal(
militaryFlightsLayer.getParams().selectedMilitaryTrackingId,
null,
);
assert.equal(militaryFlightsLayer.getParams().selectedMilitaryTrackingId, null);
militaryFlightsLayer.setParams(
{ selectedMilitaryTrackingId: 'late003' },
@@ -574,18 +481,12 @@ test('real military track path creates no native label and publishes every cache
);
_addMilitaryTrackingCandidateForTest({
icao24: 'late003',
billboard: {
...billboard,
position: Cesium.Cartesian3.fromDegrees(-97.3, 30.6, 7_000),
},
billboard: { ...billboard, position: Cesium.Cartesian3.fromDegrees(-97.3, 30.6, 7_000) },
meta: { ...FULL_INFO, callsign: 'LATE3', rawLat: 30.6, rawLon: -97.3 },
history: [],
});
_applyPendingMilitaryTrackingRestoreForTest();
assert.equal(
militaryFlightsLayer.getParams().selectedMilitaryTrackingId,
null,
);
assert.equal(militaryFlightsLayer.getParams().selectedMilitaryTrackingId, null);
militaryFlightsLayer.setParams(
{ selectedMilitaryTrackingId: 'late004' },
@@ -594,27 +495,18 @@ test('real military track path creates no native label and publishes every cache
assert.equal(_pendingMilitaryTrackingRestoreForTest(), 'late004');
_addMilitaryTrackingCandidateForTest({
icao24: 'late004',
billboard: {
...billboard,
position: Cesium.Cartesian3.fromDegrees(-97.2, 30.7, 6_000),
},
billboard: { ...billboard, position: Cesium.Cartesian3.fromDegrees(-97.2, 30.7, 6_000) },
meta: { ...FULL_INFO, callsign: 'LATE4', rawLat: 30.7, rawLon: -97.2 },
history: [],
});
assert.equal(_applyPendingMilitaryTrackingRestoreForTest(), true);
assert.equal(
militaryFlightsLayer.getParams().selectedMilitaryTrackingId,
'late004',
);
assert.equal(militaryFlightsLayer.getParams().selectedMilitaryTrackingId, 'late004');
militaryFlightsLayer.setParams(
{ selectedMilitaryTrackingId: 'late005' },
{ origin: 'local-restore' },
);
assert.equal(
militaryFlightsLayer.trackById(icao24, { origin: 'programmatic' }),
true,
);
assert.equal(militaryFlightsLayer.trackById(icao24, { origin: 'programmatic' }), true);
assert.equal(
_pendingMilitaryTrackingRestoreForTest(),
'late005',
@@ -622,18 +514,12 @@ test('real military track path creates no native label and publishes every cache
);
_addMilitaryTrackingCandidateForTest({
icao24: 'late005',
billboard: {
...billboard,
position: Cesium.Cartesian3.fromDegrees(-96.6, 31.4, 5_000),
},
billboard: { ...billboard, position: Cesium.Cartesian3.fromDegrees(-96.6, 31.4, 5_000) },
meta: { ...FULL_INFO, callsign: 'LATE5', rawLat: 31.4, rawLon: -96.6 },
history: [],
});
assert.equal(_applyPendingMilitaryTrackingRestoreForTest(), true);
assert.equal(
militaryFlightsLayer.getParams().selectedMilitaryTrackingId,
'late005',
);
assert.equal(militaryFlightsLayer.getParams().selectedMilitaryTrackingId, 'late005');
} finally {
militaryFlightsLayer.stopTracking();
viewer.scene.preUpdate.raiseEvent();
@@ -678,47 +564,21 @@ test('a callsign-less contact is findable by the tail number the app displays',
// The identity analyst_query hands back for this contact.
const analystId = mapAnalystRecord(icao24, {
callsign: null,
registration: '6606',
rawLat: 40.71,
rawLon: -74.01,
callsign: null, registration: '6606', rawLat: 40.71, rawLon: -74.01,
}).id;
assert.equal(
analystId,
'6606',
'the analyst names a callsign-less contact by its tail',
);
assert.equal(analystId, '6606', 'the analyst names a callsign-less contact by its tail');
const found = militaryFlightsLayer.findByQuery(analystId);
assert.equal(
found?.icao24,
icao24,
'and the tracker must resolve that same identity',
);
assert.equal(
militaryFlightsLayer.findByQuery('660')?.icao24,
icao24,
'prefix match too',
);
assert.equal(
militaryFlightsLayer.findByQuery(icao24)?.icao24,
icao24,
'hex still wins outright',
);
assert.equal(found?.icao24, icao24, 'and the tracker must resolve that same identity');
assert.equal(militaryFlightsLayer.findByQuery('660')?.icao24, icao24, 'prefix match too');
assert.equal(militaryFlightsLayer.findByQuery(icao24)?.icao24, icao24, 'hex still wins outright');
});
test('the analyst record carries the hex key the tracker keys on', () => {
const record = mapAnalystRecord('ae7f01', {
callsign: null,
registration: '6606',
rawLat: 40.71,
rawLon: -74.01,
callsign: null, registration: '6606', rawLat: 40.71, rawLon: -74.01,
});
assert.equal(
record.icao24,
'ae7f01',
'a resolvable key must ride along with the display label',
);
assert.equal(record.icao24, 'ae7f01', 'a resolvable key must ride along with the display label');
});
test('a registration never outranks another contact’s exact callsign', () => {
@@ -731,35 +591,18 @@ test('a registration never outranks another contact’s exact callsign', () => {
show: true,
});
const base = {
type: 'C17',
operator: 'USAF',
altitudeFt: 25000,
speedMps: 180,
track: 90,
klass: 'widebody',
onGround: false,
type: 'C17', operator: 'USAF', altitudeFt: 25000, speedMps: 180,
track: 90, klass: 'widebody', onGround: false,
};
// Registered FIRST, so insertion order favours it.
_addMilitaryTrackingCandidateForTest({
icao24: 'ae0aa1',
meta: {
...base,
callsign: null,
registration: 'ZULU777',
rawLat: 31.0,
rawLon: -97.0,
},
meta: { ...base, callsign: null, registration: 'ZULU777', rawLat: 31.0, rawLon: -97.0 },
billboard: billboardAt(-97.0, 31.0),
});
_addMilitaryTrackingCandidateForTest({
icao24: 'ae0bb2',
meta: {
...base,
callsign: 'ZULU777',
registration: '05-9999',
rawLat: 31.2,
rawLon: -97.2,
},
meta: { ...base, callsign: 'ZULU777', registration: '05-9999', rawLat: 31.2, rawLon: -97.2 },
billboard: billboardAt(-97.2, 31.2),
});
@@ -782,49 +625,24 @@ test('two contacts matching at the same strength resolve deterministically', ()
show: true,
});
const base = {
type: 'C130',
operator: 'USAF',
altitudeFt: 21000,
speedMps: 150,
track: 45,
klass: 'turboprop',
onGround: false,
type: 'C130', operator: 'USAF', altitudeFt: 21000, speedMps: 150,
track: 45, klass: 'turboprop', onGround: false,
};
// Both registrations begin with QQ7 — a same-tier prefix tie. Registered
// highest-hex first so insertion order would pick the wrong one.
_addMilitaryTrackingCandidateForTest({
icao24: 'aef002',
meta: {
...base,
callsign: null,
registration: 'QQ7-222',
rawLat: 32.0,
rawLon: -98.0,
},
meta: { ...base, callsign: null, registration: 'QQ7-222', rawLat: 32.0, rawLon: -98.0 },
billboard: billboardAt(-98.0, 32.0),
});
_addMilitaryTrackingCandidateForTest({
icao24: 'aef001',
meta: {
...base,
callsign: null,
registration: 'QQ7-111',
rawLat: 32.1,
rawLon: -98.1,
},
meta: { ...base, callsign: null, registration: 'QQ7-111', rawLat: 32.1, rawLon: -98.1 },
billboard: billboardAt(-98.1, 32.1),
});
const first = militaryFlightsLayer.findByQuery('QQ7')?.icao24;
const second = militaryFlightsLayer.findByQuery('qq7')?.icao24;
assert.equal(
first,
'aef001',
'the stable key (lowest hex) wins, not the feed order',
);
assert.equal(
second,
first,
'and the same query resolves the same way every time',
);
assert.equal(first, 'aef001', 'the stable key (lowest hex) wins, not the feed order');
assert.equal(second, first, 'and the same query resolves the same way every time');
});