diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index a9aeeddbc..dea8a5bec 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -814,6 +814,26 @@ use the supplied asset resolver, including the preload path. Enrichment receives an abort signal and cannot update a later lifecycle after destruction. Existing camera, terrain floor, trail, selection and measured model-size policies remain. +## Transponder emergency highlighting + +`src/data/aircraftEmergency.js` derives one status per poll from the Mode A +squawk (7500 unlawful interference, 7600 radio failure, 7700 general emergency; +ICAO-standard worldwide) and, where the feed carries it, the readsb ADS-B +`emergency` field (adsb.lol; OpenSky state vectors carry the squawk only). An +explicit ADS-B status wins over the squawk. `general`, `unlawful`, `nordo` and +`downed` are emergencies; `minfuel` and `lifeguard` are priorities. + +Both aircraft layers keep `squawk` and `emergency` on each record and replace +them on every poll, so the status is never sticky. Civil and military fleet +billboards and Cockpit far-contact dots are tinted `EMERGENCY_TINT_CSS` red +only for emergencies; priorities keep the normal tint. The military layer uses +the same tint when a billboard is first created and when a tracked contact is +released. While a broadcasting aircraft is tracked, the Cockpit aircraft-meta +line reads `SQUAWK 7700 · GENERAL EMERGENCY` when the code names the status, +else `ADS-B EMERGENCY · …` or `ADS-B PRIORITY · …`, in red. Analyst records and +the voice query fields expose `squawk`, `emergency` (flag) and `emergencyKind`. +`scripts/qa-emergency-squawk.mjs` proves the tint, HUD line and clearing. + ## Browser live-source observations Flights, Military Flights and AIS Vessels obtain snapshots and optional history diff --git a/scripts/format-scope.json b/scripts/format-scope.json index 4973bd484..ea1531793 100644 --- a/scripts/format-scope.json +++ b/scripts/format-scope.json @@ -18,6 +18,9 @@ "src/tooling/panelStorageDocs.test.mjs", "src/tooling/packageBoundaries.test.mjs", "src/data/infrastructure.js", + "src/data/aircraftEmergency.js", + "src/data/aircraftEmergency.test.mjs", + "scripts/qa-emergency-squawk.mjs", "src/data/infrastructure.test.mjs", "src/data/localGeojsonCore.js", "src/data/localGeojsonLod.js", diff --git a/scripts/qa-emergency-squawk.mjs b/scripts/qa-emergency-squawk.mjs index bbcc98c22..a1625ef6e 100644 --- a/scripts/qa-emergency-squawk.mjs +++ b/scripts/qa-emergency-squawk.mjs @@ -14,6 +14,8 @@ * the broadcast code on the aircraft-meta line, in red. * (iii) CLEAR — the next poll drops the code: the HUD line returns to its * normal wording (the status is not sticky). + * (iv) RELEASE — the code returns and tracking stops: the restored fleet + * billboard is red, not the plain military amber. * * Screenshots saved to qa-shots/emergency-*.png (gitignored). * @@ -410,6 +412,31 @@ async function main() { JSON.stringify(after), ); await shoot(page, 'cockpit-cleared'); + + // ── (iv) RELEASE ────────────────────────────────────────────────────── + console.log('\n(iv) RELEASE — the code returns, then tracking stops...'); + await page.evaluate(async () => { + window.__squawkQaPhase = 'emergency'; + const dm = window.__godsEyeView.dataManager; + await dm.refreshLayer('military'); + dm.layers.get('military').module.stopTracking({ origin: 'user' }); + }); + const releaseProbe = `(${billboardColors})(["ae7700"])`; + const released = await page + .waitForFunction(`Boolean((${releaseProbe}).ae7700)`, { + timeout: 20000, + polling: 250, + }) + .then( + () => true, + () => false, + ); + const releasedColor = (await page.evaluate(releaseProbe)).ae7700; + record( + 'RELEASE: untracked emergency billboard is red', + released && isRed(releasedColor), + JSON.stringify(releasedColor ?? null), + ); record( 'no uncaught browser errors', errors.length === 0, diff --git a/src/cockpitMath.js b/src/cockpitMath.js index 4c9666ee9..74e6d1451 100644 --- a/src/cockpitMath.js +++ b/src/cockpitMath.js @@ -85,8 +85,11 @@ export function cockpitAltitudeDisplayFt(altitudeM, onGround) { /** * HUD wording for a tracked aircraft's transponder emergency, or null. - * States the broadcast code, never a conclusion about the flight. - * @param {{label?: string, squawk?: string|null}|null|undefined} emergency + * States what was broadcast, never a conclusion about the flight: the Mode A + * code when it names the status, else the ADS-B field it came from (an + * unrelated squawk is dropped). + * @param {{label?: string, squawk?: string|null, source?: string, + * severity?: string}|null|undefined} emergency * @returns {string|null} */ export function formatCockpitEmergency(emergency) { @@ -94,7 +97,10 @@ export function formatCockpitEmergency(emergency) { .trim() .toUpperCase(); if (!label) return null; - return emergency.squawk ? `SQUAWK ${emergency.squawk} · ${label}` : label; + if (emergency.source === 'squawk' && emergency.squawk) + return `SQUAWK ${emergency.squawk} · ${label}`; + const field = emergency.severity === 'priority' ? 'PRIORITY' : 'EMERGENCY'; + return `ADS-B ${field} · ${label}`; } /** Format the cockpit Context scope without overstating installation coverage. */ diff --git a/src/cockpitMath.test.mjs b/src/cockpitMath.test.mjs index 8abbf1c3e..2189ca35e 100644 --- a/src/cockpitMath.test.mjs +++ b/src/cockpitMath.test.mjs @@ -10,10 +10,10 @@ import { cockpitGroundSafeHeight, cockpitSurfaceWaitExpired, cockpitUiUpdateDue, - formatCockpitEmergency, compassDivisions, formatAltitudeRulerTick, formatCockpitContextScope, + formatCockpitEmergency, formatCompassDivision, formatSpeedRulerTick, normalizeHeading, @@ -42,11 +42,7 @@ test('cockpit anchor correction cannot turn a forward step into a reversal', () const speedMps = 200; const dtSec = 0.1; const forwardStepM = speedMps * dtSec; - const backwardCorrectionM = cockpitAnchorCorrectionStep( - 1000, - speedMps, - dtSec, - ); + const backwardCorrectionM = cockpitAnchorCorrectionStep(1000, speedMps, dtSec); assert.ok(backwardCorrectionM > 0); assert.ok(backwardCorrectionM < forwardStepM); }); @@ -82,7 +78,10 @@ test('cockpit Context scope distinguishes radius-complete feeds from viewport in }); test('cockpit Context scope preserves zero and replaces missing values intentionally', () => { - assert.equal(formatCockpitContextScope('', 0), '— · 0 KM AIR/SEA WINDOW'); + assert.equal( + formatCockpitContextScope('', 0), + '— · 0 KM AIR/SEA WINDOW', + ); assert.equal( formatCockpitContextScope(undefined, Number.NaN, {}), '— · — KM AIR/SEA WINDOW', @@ -170,29 +169,26 @@ test('relative bearing stays on the shortest signed arc', () => { }); test('HUD rail layout centers between intersecting upper and lower obstacles', () => { - assert.deepEqual( - resolveHudRailLayout({ - viewportHeight: 1000, - panelHeight: 200, - laneLeft: 50, - laneRight: 410, - baseTop: 280, - baseBottom: 940, - gap: 10, - obstacles: [ - { left: 40, right: 240, top: 320, bottom: 460 }, - { left: 300, right: 1000, top: 780, bottom: 930 }, - { left: 900, right: 1100, top: 300, bottom: 700 }, - ], - }), - { - top: 520, - maxHeight: 300, - safeTop: 470, - safeBottom: 770, - constrained: false, - }, - ); + assert.deepEqual(resolveHudRailLayout({ + viewportHeight: 1000, + panelHeight: 200, + laneLeft: 50, + laneRight: 410, + baseTop: 280, + baseBottom: 940, + gap: 10, + obstacles: [ + { left: 40, right: 240, top: 320, bottom: 460 }, + { left: 300, right: 1000, top: 780, bottom: 930 }, + { left: 900, right: 1100, top: 300, bottom: 700 }, + ], + }), { + top: 520, + maxHeight: 300, + safeTop: 470, + safeBottom: 770, + constrained: false, + }); }); test('HUD rail layout constrains an oversized panel to the safe corridor', () => { @@ -246,10 +242,7 @@ function contextSnapshot(subject, extra = {}) { } test('Contact panel hides only when there is no snapshot at all', () => { - const readout = resolveCockpitContextReadout({ - snapshot: null, - info: TRACKED_INFO, - }); + const readout = resolveCockpitContextReadout({ snapshot: null, info: TRACKED_INFO }); assert.equal(readout.visible, false); assert.equal(readout.mode, 'standby'); assert.equal( @@ -265,22 +258,14 @@ test('Contact panel survives NEXT onto a vessel or installation subject', () => // camera: "click next... whole left panel disappears"). for (const subject of [ { layerId: 'ais-live-vessels', id: '353136000', label: 'MAERSK DETROIT' }, - { - layerId: 'military-installations', - id: 'fort-hood', - label: 'FORT CAVAZOS', - }, + { layerId: 'military-installations', id: 'fort-hood', label: 'FORT CAVAZOS' }, { layerId: 'military', id: 'ae01ce', label: 'RCH451' }, ]) { const readout = resolveCockpitContextReadout({ snapshot: contextSnapshot(subject), info: TRACKED_INFO, }); - assert.equal( - readout.visible, - true, - `${subject.layerId} subject must keep the panel up`, - ); + assert.equal(readout.visible, true, `${subject.layerId} subject must keep the panel up`); assert.equal(readout.mode, 'foreign'); assert.equal(readout.subjectMatchesTracked, false); } @@ -288,11 +273,7 @@ test('Contact panel survives NEXT onto a vessel or installation subject', () => test('a foreign subject dashes the aircraft-relative fields and keeps the rest live', () => { const readout = resolveCockpitContextReadout({ - snapshot: contextSnapshot({ - layerId: 'ais-live-vessels', - id: '353136000', - label: 'MAERSK DETROIT', - }), + snapshot: contextSnapshot({ layerId: 'ais-live-vessels', id: '353136000', label: 'MAERSK DETROIT' }), info: TRACKED_INFO, }); assert.equal(readout.visible, true); @@ -301,20 +282,12 @@ test('a foreign subject dashes the aircraft-relative fields and keeps the rest l // frame while every other row value is measured from the subject; rendering // both live would present one mixed-frame reading as a single measurement. assert.equal(readout.aircraftRelative, false); - assert.equal( - readout.contactLost, - false, - 'subject-frame values still refresh', - ); + assert.equal(readout.contactLost, false, 'subject-frame values still refresh'); }); test('the tracked aircraft as subject keeps every field in its own frame', () => { const readout = resolveCockpitContextReadout({ - snapshot: contextSnapshot({ - layerId: 'flights', - id: 'aaa077', - label: 'SWA1234', - }), + snapshot: contextSnapshot({ layerId: 'flights', id: 'aaa077', label: 'SWA1234' }), info: TRACKED_INFO, }); assert.equal(readout.visible, true); @@ -333,11 +306,7 @@ test('a fast-culled subject keeps the panel up as CONTACT LOST instead of collap snapshot: contextSnapshot(subject, { subjectPresent: false }), info: TRACKED_INFO, }); - assert.equal( - readout.visible, - true, - 'a lost contact must not take the panel down', - ); + assert.equal(readout.visible, true, 'a lost contact must not take the panel down'); assert.equal(readout.mode, 'lost'); assert.equal(readout.contactLost, true, 'last-known values stay on screen'); } @@ -345,10 +314,7 @@ test('a fast-culled subject keeps the panel up as CONTACT LOST instead of collap test('an explicitly present subject is never reported lost', () => { const present = resolveCockpitContextReadout({ - snapshot: contextSnapshot( - { layerId: 'flights', id: 'aaa077' }, - { subjectPresent: true }, - ), + snapshot: contextSnapshot({ layerId: 'flights', id: 'aaa077' }, { subjectPresent: true }), info: TRACKED_INFO, }); assert.equal(present.contactLost, false); @@ -367,36 +333,20 @@ test('the tracked flight layer is resolved by normalized tracked identity', () = // Both layers describe a tracked aircraft during a cross-layer handoff; // civilian-first precedence would hand the cockpit the wrong aircraft. assert.equal( - resolveTrackedAircraftInfo({ - civilian, - military, - trackedId: 'military:ae01ce', - }).layerId, + resolveTrackedAircraftInfo({ civilian, military, trackedId: 'military:ae01ce' }).layerId, 'military', ); assert.equal( - resolveTrackedAircraftInfo({ - civilian, - military, - trackedId: 'military:ae01ce', - }).icao24, + resolveTrackedAircraftInfo({ civilian, military, trackedId: 'military:ae01ce' }).icao24, 'ae01ce', ); assert.equal( - resolveTrackedAircraftInfo({ - civilian, - military, - trackedId: 'flights:aaa077', - }).layerId, + resolveTrackedAircraftInfo({ civilian, military, trackedId: 'flights:aaa077' }).layerId, 'flights', ); // Case-insensitive: layers stamp lowercase hex, callers may not. assert.equal( - resolveTrackedAircraftInfo({ - civilian, - military, - trackedId: 'MILITARY:AE01CE', - }).layerId, + resolveTrackedAircraftInfo({ civilian, military, trackedId: 'MILITARY:AE01CE' }).layerId, 'military', ); }); @@ -406,37 +356,29 @@ test('tracked-identity resolution falls back to layer precedence', () => { const military = { icao24: 'ae01ce' }; // No stamped identity (or one that matches neither layer) keeps the historic // civilian-first order so tracking paths without gevTrackedId still work. + assert.equal(resolveTrackedAircraftInfo({ civilian, military }).layerId, 'flights'); assert.equal( - resolveTrackedAircraftInfo({ civilian, military }).layerId, + resolveTrackedAircraftInfo({ civilian, military, trackedId: 'satellites:25544' }).layerId, 'flights', ); - assert.equal( - resolveTrackedAircraftInfo({ - civilian, - military, - trackedId: 'satellites:25544', - }).layerId, - 'flights', - ); - assert.equal( - resolveTrackedAircraftInfo({ military, trackedId: '' }).layerId, - 'military', - ); - assert.equal( - resolveTrackedAircraftInfo({ trackedId: 'flights:aaa077' }), - null, - ); + assert.equal(resolveTrackedAircraftInfo({ military, trackedId: '' }).layerId, 'military'); + assert.equal(resolveTrackedAircraftInfo({ trackedId: 'flights:aaa077' }), null); assert.equal(resolveTrackedAircraftInfo(), null); }); test('cockpit emergency wording states the broadcast code, not a conclusion', () => { assert.equal( - formatCockpitEmergency({ label: 'General emergency', squawk: '7700' }), + formatCockpitEmergency({ label: 'General emergency', squawk: '7700', source: 'squawk', severity: 'emergency' }), 'SQUAWK 7700 · GENERAL EMERGENCY', ); assert.equal( - formatCockpitEmergency({ label: 'Minimum fuel', squawk: null }), - 'MINIMUM FUEL', + formatCockpitEmergency({ label: 'Downed aircraft', squawk: '1200', source: 'ads-b', severity: 'emergency' }), + 'ADS-B EMERGENCY · DOWNED AIRCRAFT', + 'an unrelated squawk is not quoted beside an ADS-B status', + ); + assert.equal( + formatCockpitEmergency({ label: 'Minimum fuel', squawk: null, source: 'ads-b', severity: 'priority' }), + 'ADS-B PRIORITY · MINIMUM FUEL', ); assert.equal(formatCockpitEmergency(null), null); assert.equal(formatCockpitEmergency({ label: '' }), null); diff --git a/src/data/aircraftEmergency.js b/src/data/aircraftEmergency.js index e7ee3aca5..3fd6ab688 100644 --- a/src/data/aircraftEmergency.js +++ b/src/data/aircraftEmergency.js @@ -43,7 +43,9 @@ export function normalizeSquawk(value) { * 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} + * 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); @@ -57,7 +59,7 @@ export function aircraftEmergency({ squawk, emergency } = {}) { kind, ...KINDS[kind], squawk: code, - source: fromAdsb ? 'ads-b' : 'squawk', + source: SQUAWK_KINDS[code] === kind ? 'squawk' : 'ads-b', }; } diff --git a/src/data/aircraftEmergency.test.mjs b/src/data/aircraftEmergency.test.mjs index cae9f6c50..2cff800b9 100644 --- a/src/data/aircraftEmergency.test.mjs +++ b/src/data/aircraftEmergency.test.mjs @@ -39,6 +39,15 @@ test('the ADS-B emergency field wins, and `none` defers to the squawk', () => { 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); diff --git a/src/data/analystEngine.test.mjs b/src/data/analystEngine.test.mjs index 3476af7b4..3bb0dd06e 100644 --- a/src/data/analystEngine.test.mjs +++ b/src/data/analystEngine.test.mjs @@ -1,89 +1,18 @@ import { test } from 'node:test'; import assert from 'node:assert/strict'; -import { - createAnalystEngine, - applyScope, - haversineKm, -} from './analystEngine.js'; +import { createAnalystEngine, applyScope, haversineKm } from './analystEngine.js'; // Stub world: a square "Texland" region, flights + ships + fires around it. -const TEXLAND = { - name: 'Texland', - ring: [ - [-100, 28], - [-94, 28], - [-94, 33], - [-100, 33], - ], -}; +const TEXLAND = { name: 'Texland', ring: [[-100, 28], [-94, 28], [-94, 33], [-100, 33]] }; 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, - 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, - }, + { 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, 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 = [ - { - id: 'EVERGIVEN', - lat: 29.5, - lon: -94.9, - speedKts: 12, - shipType: 'Cargo', - destination: 'OAKLAND', - navStatus: 'under way', - }, - { - id: 'SLOWBOAT', - lat: 29.6, - lon: -95.0, - speedKts: 0.2, - shipType: 'Tanker', - destination: 'HOUSTON', - navStatus: 'anchored', - }, + { id: 'EVERGIVEN', lat: 29.5, lon: -94.9, speedKts: 12, shipType: 'Cargo', destination: 'OAKLAND', navStatus: 'under way' }, + { id: 'SLOWBOAT', lat: 29.6, lon: -95.0, speedKts: 0.2, shipType: 'Tanker', destination: 'HOUSTON', navStatus: 'anchored' }, ]; const FIRES = [ { id: 'FIRE-1', lat: 30.5, lon: -98.2, frp: 1500 }, @@ -93,10 +22,7 @@ const FIRES = [ function makeEngine() { return createAnalystEngine({ - getRecords: (key) => - ({ flights: FLIGHTS, 'ais-live-vessels': SHIPS, 'local-firms': FIRES })[ - key - ] || [], + getRecords: (key) => ({ flights: FLIGHTS, 'ais-live-vessels': SHIPS, 'local-firms': FIRES }[key] || []), resolveRegionRing: async (name) => (/texland/i.test(name) ? TEXLAND : null), getViewContext: () => ({ lat: 30.27, lon: -97.74, viewRadiusKm: 150 }), }); @@ -105,10 +31,7 @@ function makeEngine() { /** Same world, but Contacts is up with a subject far from the parked camera. */ function makeContactsEngine(subject) { return createAnalystEngine({ - getRecords: (key) => - ({ flights: FLIGHTS, 'ais-live-vessels': SHIPS, 'local-firms': FIRES })[ - key - ] || [], + getRecords: (key) => ({ flights: FLIGHTS, 'ais-live-vessels': SHIPS, 'local-firms': FIRES }[key] || []), resolveRegionRing: async (name) => (/texland/i.test(name) ? TEXLAND : null), // Parked far away, as a high-altitude camera often is. getViewContext: () => ({ lat: 45.0, lon: -122.0, viewRadiusKm: 150 }), @@ -129,11 +52,7 @@ test('analyst: a radius query centers on the active contact, not the parked came assert.equal(r.ok, true); // Austin-area flights, not the Oregon one the camera is parked over. assert.equal(r.count, 3); - assert.equal( - r.centeredOn, - 'SWA1', - 'the answer names the centre it measured from', - ); + assert.equal(r.centeredOn, 'SWA1', 'the answer names the centre it measured from'); assert.ok(r.coverage.scope.includes('@SWA1')); }); @@ -144,11 +63,7 @@ test('analyst: an explicit center still wins over the active contact', async () scope: { kind: 'radius', km: 250, center: { lat: 45.0, lon: -122.0 } }, limit: 50, }); - assert.equal( - r.count, - 1, - 'only the Oregon flight is within 250 km of the given center', - ); + assert.equal(r.count, 1, 'only the Oregon flight is within 250 km of the given center'); assert.equal(r.centeredOn, undefined, 'an explicit center is not relabelled'); }); @@ -158,11 +73,7 @@ test('analyst: with Contacts off, radius still centers on the view', async () => scope: { kind: 'radius', km: 250 }, limit: 50, }); - assert.equal( - r.count, - 1, - 'view-centred behaviour is unchanged outside Contacts', - ); + assert.equal(r.count, 1, 'view-centred behaviour is unchanged outside Contacts'); assert.equal(r.centeredOn, undefined); assert.equal(r.coverage.scope, 'radius:250km'); }); @@ -172,25 +83,13 @@ test('analyst: a subject without usable coordinates cannot lend its name to a ca // number. A subject present but position-less fell back to the camera and // kept the contact's name on the answer, so a camera-centred count read as // contact-centred with nothing in the payload to catch it. - const r = await makeContactsEngine({ - lat: null, - lon: null, - label: 'SWA1', - }).query({ + const r = await makeContactsEngine({ lat: null, lon: null, label: 'SWA1' }).query({ layers: ['flights'], scope: { kind: 'radius', km: 250 }, limit: 50, }); - assert.equal( - r.count, - 1, - 'the count is the camera-centred one it actually measured', - ); - assert.equal( - r.centeredOn, - undefined, - 'and it must not claim a centre it did not use', - ); + assert.equal(r.count, 1, 'the count is the camera-centred one it actually measured'); + assert.equal(r.centeredOn, undefined, 'and it must not claim a centre it did not use'); assert.equal(r.coverage.scope, 'radius:250km'); assert.equal(r.scopeLabel, 'within 250 km'); }); @@ -200,47 +99,33 @@ test('analyst: every scope names itself in words', async () => { // answers look like a contradiction, so each scope carries its own phrasing. const subject = { lat: 30.2, lon: -97.7, label: 'DYNO11' }; const centred = await makeContactsEngine(subject).query({ - layers: ['flights'], - scope: { kind: 'radius', km: 250 }, - limit: 1, + layers: ['flights'], scope: { kind: 'radius', km: 250 }, limit: 1, }); assert.equal(centred.scopeLabel, 'within 250 km of DYNO11'); const plainRadius = await makeContactsEngine(null).query({ - layers: ['flights'], - scope: { kind: 'radius', km: 250 }, - limit: 1, + layers: ['flights'], scope: { kind: 'radius', km: 250 }, limit: 1, }); assert.equal(plainRadius.scopeLabel, 'within 250 km'); const inView = await makeEngine().query({ - layers: ['flights'], - scope: { kind: 'view' }, - limit: 1, + layers: ['flights'], scope: { kind: 'view' }, limit: 1, }); assert.equal(inView.scopeLabel, 'in view'); const region = await makeEngine().query({ - layers: ['flights'], - scope: { kind: 'region', name: 'Texland' }, - limit: 1, + layers: ['flights'], scope: { kind: 'region', name: 'Texland' }, limit: 1, }); assert.equal(region.scopeLabel, 'over Texland'); const anywhere = await makeEngine().query({ - layers: ['flights'], - scope: { kind: 'anywhere' }, - limit: 1, + layers: ['flights'], scope: { kind: 'anywhere' }, limit: 1, }); assert.equal(anywhere.scopeLabel, 'anywhere in the loaded data'); }); test('analyst: count flights over a region', async () => { - const r = await makeEngine().query({ - layers: ['flights'], - scope: { kind: 'region', name: 'Texland' }, - limit: 50, - }); + const r = await makeEngine().query({ layers: ['flights'], scope: { kind: 'region', name: 'Texland' }, limit: 50 }); assert.equal(r.ok, true); assert.equal(r.count, 3, 'Oregon flight excluded'); assert.ok(r.coverage.scope.includes('Texland')); @@ -248,65 +133,32 @@ test('analyst: count flights over a region', async () => { test('analyst: attribute filter — above 40,000 ft (~12,192 m)', async () => { const r = await makeEngine().query({ - layers: ['flights'], - scope: { kind: 'anywhere' }, + layers: ['flights'], scope: { kind: 'anywhere' }, filters: [{ field: 'altitudeM', op: 'gt', value: 12192 }], }); - assert.deepEqual( - r.items.map((i) => i.id), - ['RCH01'], - ); + assert.deepEqual(r.items.map((i) => i.id), ['RCH01']); }); test('analyst: military flag + region compose', async () => { const r = await makeEngine().query({ - layers: ['flights'], - scope: { kind: 'region', name: 'Texland' }, + layers: ['flights'], scope: { kind: 'region', name: 'Texland' }, filters: [{ field: 'military', op: 'eq', value: true }], }); assert.equal(r.count, 1); assert.equal(r.items[0].id, 'RCH01'); }); -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: ships headed to Oakland (destination contains)', async () => { const r = await makeEngine().query({ - layers: ['ais-live-vessels'], - scope: { kind: 'anywhere' }, + layers: ['ais-live-vessels'], scope: { kind: 'anywhere' }, filters: [{ field: 'destination', op: 'contains', value: 'oakland' }], }); - assert.deepEqual( - r.items.map((i) => i.id), - ['EVERGIVEN'], - ); + assert.deepEqual(r.items.map((i) => i.id), ['EVERGIVEN']); }); test('analyst: superlative — biggest fire in view radius', async () => { const r = await makeEngine().query({ - layers: ['local-firms'], - scope: { kind: 'view' }, - sortBy: 'frp', - limit: 1, + layers: ['local-firms'], scope: { kind: 'view' }, sortBy: 'frp', limit: 1, }); assert.equal(r.items[0].id, 'FIRE-1', 'BC monster is out of view scope'); assert.equal(r.summary.frpMax, 1500); @@ -314,10 +166,7 @@ test('analyst: superlative — biggest fire in view radius', async () => { test('analyst: nearest sorting attaches distanceKm ascending', async () => { const r = await makeEngine().query({ - layers: ['flights'], - scope: { kind: 'view' }, - sortBy: 'distance', - limit: 3, + layers: ['flights'], scope: { kind: 'view' }, sortBy: 'distance', limit: 3, }); assert.ok(r.items[0].distanceKm <= r.items[1].distanceKm); assert.ok(Number.isFinite(r.items[0].distanceKm)); @@ -417,10 +266,7 @@ test('analyst: failed pre-commit work cannot replace follow-up memory', async () }); test('analyst: unresolved region is an honest failure, not empty success', async () => { - const r = await makeEngine().query({ - layers: ['flights'], - scope: { kind: 'region', name: 'Atlantis' }, - }); + const r = await makeEngine().query({ layers: ['flights'], scope: { kind: 'region', name: 'Atlantis' } }); assert.equal(r.ok, false); assert.match(r.error, /Atlantis/); }); @@ -428,17 +274,10 @@ test('analyst: unresolved region is an honest failure, not empty success', async test('analyst: a region lookup timeout is reported as region-timeout', async () => { const eng = createAnalystEngine({ getRecords: () => FLIGHTS, - resolveRegionRing: async (name) => ({ - name, - ring: null, - error: 'region-timeout', - }), + resolveRegionRing: async (name) => ({ name, ring: null, error: 'region-timeout' }), getViewContext: () => ({ lat: 30.27, lon: -97.74, viewRadiusKm: 150 }), }); - const r = await eng.query({ - layers: ['flights'], - scope: { kind: 'region', name: 'Texas' }, - }); + const r = await eng.query({ layers: ['flights'], scope: { kind: 'region', name: 'Texas' } }); assert.equal(r.ok, false); assert.equal(r.code, 'region-timeout'); assert.match(r.error, /Texas/); @@ -480,84 +319,45 @@ test('analyst: region lookup receives and obeys the owning action signal', async test('analyst: route fields queryable from cached enrichment only', async () => { const r = await makeEngine().query({ - layers: ['flights'], - scope: { kind: 'anywhere' }, + layers: ['flights'], scope: { kind: 'anywhere' }, filters: [{ field: 'routeDestination', op: 'eq', value: 'LAX' }], }); - assert.deepEqual( - r.items.map((i) => i.id), - ['SWA1'], - 'null route fields never match', - ); + 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', - }, - { - id: 'GPS BIIR-2', - noradId: '24876', - name: 'GPS BIIR-2', - lat: 51.0, - lon: 0.0, - altitudeM: 20200000, - satelliteClass: 'NAV · GPS', - group: 'gps-ops', - }, + { id: 'ISS (ZARYA)', noradId: '25544', name: 'ISS (ZARYA)', lat: 30.3, lon: -97.7, altitudeM: 410000, satelliteClass: 'STATION · ISS', group: 'stations' }, + { id: 'GPS BIIR-2', noradId: '24876', name: 'GPS BIIR-2', lat: 51.0, lon: 0.0, altitudeM: 20200000, satelliteClass: 'NAV · GPS', group: 'gps-ops' }, ]; const DAMS = [ - { - id: 'Austin Dam', - name: 'Austin Dam', - lat: 30.27, - lon: -97.74, - operator: 'LCRA', - river: 'Colorado', - output: '2 MW', - }, - { - id: 'Far Dam', - name: 'Far Dam', - lat: 45.0, - lon: -122.0, - operator: 'USACE', - river: 'Columbia', - output: '1000 MW', - }, + { id: 'Austin Dam', name: 'Austin Dam', lat: 30.27, lon: -97.74, operator: 'LCRA', river: 'Colorado', output: '2 MW' }, + { id: 'Far Dam', name: 'Far Dam', lat: 45.0, lon: -122.0, operator: 'USACE', river: 'Columbia', output: '1000 MW' }, ]; const DCS = [ - { - id: 'AUS-1', - name: 'AUS-1', - lat: 30.28, - lon: -97.75, - operator: 'Example Cloud', - capacity: '27 MW', - }, + { id: 'AUS-1', name: 'AUS-1', lat: 30.28, lon: -97.75, operator: 'Example Cloud', capacity: '27 MW' }, ]; const eng = createAnalystEngine({ - getRecords: (key) => - ({ satellites: SATS, 'local-dams': DAMS, 'local-datacenters': DCS })[ - key - ] || [], + getRecords: (key) => ({ satellites: SATS, 'local-dams': DAMS, 'local-datacenters': DCS }[key] || []), resolveRegionRing: async (name) => (/texland/i.test(name) ? TEXLAND : null), getViewContext: () => ({ lat: 30.27, lon: -97.74, viewRadiusKm: 150 }), }); const sats = await eng.query({ - layers: ['satellites'], - scope: { kind: 'view' }, - sortBy: 'distance', - limit: 2, + layers: ['satellites'], scope: { kind: 'view' }, sortBy: 'distance', limit: 2, }); assert.equal(sats.ok, true); assert.equal(sats.count, 1, 'GPS sat is out of the Austin view radius'); @@ -565,28 +365,20 @@ test('analyst: satellites and local infrastructure are queryable layers', async assert.ok(Number.isFinite(sats.items[0].distanceKm)); const nav = await eng.query({ - layers: ['satellites'], - scope: { kind: 'anywhere' }, + layers: ['satellites'], scope: { kind: 'anywhere' }, filters: [{ field: 'satelliteClass', op: 'contains', value: 'NAV' }], }); - assert.deepEqual( - nav.items.map((i) => i.id), - ['GPS BIIR-2'], - ); + assert.deepEqual(nav.items.map((i) => i.id), ['GPS BIIR-2']); const dams = await eng.query({ - layers: ['local-dams'], - scope: { kind: 'view' }, - sortBy: 'distance', - limit: 5, + layers: ['local-dams'], scope: { kind: 'view' }, sortBy: 'distance', limit: 5, }); assert.equal(dams.count, 1); assert.equal(dams.items[0].id, 'Austin Dam'); assert.equal(dams.items[0].river, 'Colorado'); const dcs = await eng.query({ - layers: ['local-datacenters'], - scope: { kind: 'region', name: 'Texland' }, + layers: ['local-datacenters'], scope: { kind: 'region', name: 'Texland' }, filters: [{ field: 'operator', op: 'contains', value: 'cloud' }], }); assert.equal(dcs.count, 1); @@ -596,11 +388,7 @@ test('analyst: satellites and local infrastructure are queryable layers', async test('helpers: haversine sanity + scope radius', () => { const km = haversineKm(30.2672, -97.7431, 29.7604, -95.3698); // Austin→Houston assert.ok(km > 200 && km < 280, `Austin-Houston ~235km, got ${km}`); - const scoped = applyScope( - FLIGHTS, - { kind: 'radius' }, - { center: { lat: 30.27, lon: -97.74 }, km: 50 }, - ); + const scoped = applyScope(FLIGHTS, { kind: 'radius' }, { center: { lat: 30.27, lon: -97.74 }, km: 50 }); assert.deepEqual(scoped.map((f) => f.id).sort(), ['GND1', 'SWA1']); }); @@ -610,11 +398,7 @@ test('capped cohorts report returned/total/truncated and keep it on follow-up', getRecordCoverage: () => ({ total: 3000, truncated: true }), getViewContext: () => ({ lat: 0, lon: 0, viewRadiusKm: 25 }), }); - const result = await engine.query({ - layers: ['local-dams'], - scope: { kind: 'anywhere' }, - sortBy: 'distance', - }); + const result = await engine.query({ layers: ['local-dams'], scope: { kind: 'anywhere' }, sortBy: 'distance' }); assert.equal(result.count, 1); const layer = result.coverage.layersQueried[0]; assert.deepEqual({ returned: layer.returned, total: layer.total, truncated: layer.truncated }, { returned: 1, total: 3000, truncated: true }); @@ -735,15 +519,12 @@ 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({ getRecords: () => [{ id: 'A', lat: 0, lon: 0 }], - getLayerSnapshot: () => ({ - id: 'flights', - enabled: true, - feedState: state, - }), + getLayerSnapshot: () => ({ id: 'flights', enabled: true, feedState: state }), getViewContext: () => ({ lat: 0, lon: 0, viewRadiusKm: 25 }), }); const first = await engine.query(); @@ -753,10 +534,7 @@ test('follow-up provenance stays attached to old rows after a feed recovers', as assert.equal(followUp.coverage.feedProvenance.overall, 'stale'); engine.reset(); assert.equal(engine.hasMemory(), false); - assert.equal( - (await engine.query()).coverage.feedProvenance.overall, - 'nominal', - ); + assert.equal((await engine.query()).coverage.feedProvenance.overall, 'nominal'); }); test('a large loaded set sorts and summarizes without overflowing the stack', async () => { diff --git a/src/data/flights.test.mjs b/src/data/flights.test.mjs index 740f1afcc..0e7021251 100644 --- a/src/data/flights.test.mjs +++ b/src/data/flights.test.mjs @@ -18,10 +18,8 @@ import flightsLayer, { } from './flights.js'; import { setMilitaryLayerActive } from './militaryRegistry.js'; import { - GROUND_FLOOR_LIFT_M, - reportMeshFloorCell, - setMeshFloorPreferred, - _clearMeshFloorCellsForTest, + GROUND_FLOOR_LIFT_M, reportMeshFloorCell, + setMeshFloorPreferred, _clearMeshFloorCellsForTest, } from './groundFloor.js'; import { _setTrackedOverlayHostForTest, @@ -35,10 +33,7 @@ test('share-Follow absence requires an accepted OpenSky snapshot', async () => { (await flightsLayer.resolveTrackingRestoreTarget('abc123')).status, 'source-unavailable', ); - _setFlightTrackingRefreshOutcomeForTest({ - status: 'accepted', - ids: ['different'], - }); + _setFlightTrackingRefreshOutcomeForTest({ status: 'accepted', ids: ['different'] }); const missing = await flightsLayer.resolveTrackingRestoreTarget('abc123'); assert.equal(missing.status, 'missing'); assert.equal(missing.reason, 'target-absent-from-snapshot'); @@ -60,10 +55,7 @@ const FULL_INFO = { }; test('flights analyst record: full record maps every contract field', () => { - const r = mapAnalystRecord('a1b2c3', FULL_INFO, { - military: false, - routeOk: true, - }); + const r = mapAnalystRecord('a1b2c3', FULL_INFO, { military: false, routeOk: true }); assert.deepEqual(r, { id: 'SWA696', icao24: 'a1b2c3', @@ -101,8 +93,7 @@ test('flights analyst record: empty info yields nulls, never NaN/undefined', () assert.equal(r.military, 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`); } }); @@ -110,31 +101,14 @@ test('flights analyst record: no callsign falls back to registration, then icao2 const r = mapAnalystRecord('abc123', { ...FULL_INFO, callsign: ' ' }); assert.equal(r.id, 'abc123'); // FULL_INFO carries no registration assert.equal(r.callsign, null); - assert.equal( - mapAnalystRecord('abc123', { - ...FULL_INFO, - callsign: '', - registration: 'N123AB', - }).id, - 'N123AB', - ); - assert.equal( - mapAnalystRecord('abc123', { callsign: '', registration: ' ' }).id, - 'abc123', - ); + assert.equal(mapAnalystRecord('abc123', { ...FULL_INFO, callsign: '', registration: 'N123AB' }).id, 'N123AB'); + assert.equal(mapAnalystRecord('abc123', { callsign: '', registration: ' ' }).id, 'abc123'); }); test('flights analyst record: NaN kinematics become null, military flag passes through', () => { - const r = mapAnalystRecord( - 'ae01ce', - { - ...FULL_INFO, - velocity: NaN, - true_track: undefined, - verticalRate: null, - }, - { military: true }, - ); + const r = mapAnalystRecord('ae01ce', { + ...FULL_INFO, velocity: NaN, true_track: undefined, verticalRate: null, + }, { military: true }); assert.equal(r.speedMps, null); assert.equal(r.heading, null); assert.equal(r.verticalRateMps, null); @@ -149,25 +123,17 @@ test('flights analyst record: output is JSON-safe (no Cesium types)', () => { test('flights 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 = flightsLayer.update( - { camera: { positionCartographic: null }, scene: {} }, - { - signal: controller.signal, - }, - ); + const work = flightsLayer.update({ camera: { positionCartographic: null }, scene: {} }, { + signal: controller.signal, + }); await Promise.resolve(); assert.ok(observedSignal); controller.abort(); @@ -192,27 +158,14 @@ test('nonempty OpenSky payload with zero usable rows cannot prove share target a ok: true, status: 200, headers: { get: () => null }, - json: async () => ({ - states: [null, {}, ['abc123', null, null, null, null, null, null]], - }), + json: async () => ({ states: [null, {}, ['abc123', null, null, null, null, null, null]] }), }); try { - await flightsLayer.update({ - camera: { positionCartographic: null }, - scene: {}, - }); - const resolution = - await flightsLayer.resolveTrackingRestoreTarget('abc123'); + await flightsLayer.update({ camera: { positionCartographic: null }, scene: {} }); + const resolution = await flightsLayer.resolveTrackingRestoreTarget('abc123'); assert.equal(resolution.status, 'source-unavailable'); - assert.match( - flightsLayer.getStats().error, - /Malformed OpenSky aircraft rows/, - ); - assert.equal( - flightsLayer.getAnalystRecords().length, - 1, - 'warm aircraft data is preserved', - ); + assert.match(flightsLayer.getStats().error, /Malformed OpenSky aircraft rows/); + assert.equal(flightsLayer.getAnalystRecords().length, 1, 'warm aircraft data is preserved'); } finally { globalThis.fetch = realFetch; } @@ -350,31 +303,13 @@ test('flights poll refreshes tracked callsign/FL/kts and marks a missed poll STA if (!String(url).startsWith('/api/flights')) { return { ok: true, status: 200, json: async () => ({ ac: [] }) }; } - const states = - openskyPoll++ === 0 - ? [ - [ - icao24, - 'DAL123 ', - 'United States', - nowSec, - nowSec, - -97.6, - 30.3, - 10_668, - false, - 250, - 95, - 5, - null, - 10_700, - null, - null, - null, - 5, - ], - ] - : []; + const states = openskyPoll++ === 0 + ? [[ + icao24, 'DAL123 ', 'United States', nowSec, nowSec, + -97.6, 30.3, 10_668, false, 250, 95, 5, null, 10_700, + null, null, null, 5, + ]] + : []; return { ok: true, status: 200, @@ -413,18 +348,14 @@ test('real civil track path creates no native label and publishes every cached h 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 }, @@ -463,12 +394,9 @@ test('real civil track path creates no native label and publishes every cached h 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 { @@ -480,15 +408,13 @@ test('real civil track path creates no native label and publishes every cached h billboardCollection: { show: true, remove() {} }, viewer, tracked: false, - history: [ - { - time: now, - epochMs: Date.now(), - position, - velocity: 250, - track: 95, - }, - ], + history: [{ + time: now, + epochMs: Date.now(), + position, + velocity: 250, + track: 95, + }], meta: { callsign: 'N12345', altitude: 10_668, @@ -511,19 +437,11 @@ test('real civil track path creates no native label and publishes every cached h }, }); - assert.equal( - flightsLayer.trackById(icao24, { origin: 'programmatic' }), - true, - ); + assert.equal(flightsLayer.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: 'N12345 · FL350 · 486 kts', details: ['TEST AIR · A320', 'AUS → LAX'], @@ -533,44 +451,17 @@ test('real civil track path creates no native label and publishes every cached h const initialAppliedFrames = appliedFrames; const initialCancelledFlights = cancelledFlights; assert.equal(flightsLayer.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(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(flightsLayer.refocusTrackedById('different-flight'), false); - assert.equal( - flightsLayer.refocusTrackedById(icao24, { origin: 'voice' }), - true, - ); - assert.equal( - selectionEvents.at(-1)?.origin, - 'voice', - 'same-target refocus forwards explicit authority', - ); + assert.equal(flightsLayer.refocusTrackedById(icao24, { origin: 'voice' }), true); + assert.equal(selectionEvents.at(-1)?.origin, 'voice', 'same-target refocus forwards explicit authority'); assert.equal(flightsLayer.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]; @@ -581,11 +472,7 @@ test('real civil track path creates no native label and publishes every cached h 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'); flightsLayer.setParams( { selectedFlightsTrackingId: 'late001' }, @@ -593,17 +480,10 @@ test('real civil track path creates no native label and publishes every cached h ); assert.equal(_pendingFlightTrackingRestoreForTest(), 'late001'); assert.equal(flightsLayer.trackById(icao24, { origin: 'user' }), true); - assert.equal( - _pendingFlightTrackingRestoreForTest(), - null, - 'new user selection cancels stale restore', - ); + assert.equal(_pendingFlightTrackingRestoreForTest(), null, 'new user selection cancels stale restore'); _addFlightTrackingCandidateForTest({ icao24: 'late001', - billboard: { - ...billboard, - position: Cesium.Cartesian3.fromDegrees(-97.5, 30.4, 9_000), - }, + billboard: { ...billboard, position: Cesium.Cartesian3.fromDegrees(-97.5, 30.4, 9_000) }, meta: { ...FULL_INFO, callsign: 'LATE1', rawLat: 30.4, rawLon: -97.5 }, history: [], }); @@ -617,17 +497,10 @@ test('real civil track path creates no native label and publishes every cached h ); assert.equal(_pendingFlightTrackingRestoreForTest(), 'late002'); flightsLayer.stopTracking({ origin: 'user' }); - assert.equal( - _pendingFlightTrackingRestoreForTest(), - null, - 'explicit clear cancels stale restore', - ); + assert.equal(_pendingFlightTrackingRestoreForTest(), null, 'explicit clear cancels stale restore'); _addFlightTrackingCandidateForTest({ icao24: 'late002', - billboard: { - ...billboard, - position: Cesium.Cartesian3.fromDegrees(-97.4, 30.5, 8_000), - }, + billboard: { ...billboard, position: Cesium.Cartesian3.fromDegrees(-97.4, 30.5, 8_000) }, meta: { ...FULL_INFO, callsign: 'LATE2', rawLat: 30.5, rawLon: -97.4 }, history: [], }); @@ -647,10 +520,7 @@ test('real civil track path creates no native label and publishes every cached h ); _addFlightTrackingCandidateForTest({ 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: [], }); @@ -664,10 +534,7 @@ test('real civil track path creates no native label and publishes every cached h assert.equal(_pendingFlightTrackingRestoreForTest(), 'late004'); _addFlightTrackingCandidateForTest({ 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: [], }); @@ -678,10 +545,7 @@ test('real civil track path creates no native label and publishes every cached h { selectedFlightsTrackingId: 'late005' }, { origin: 'share-restore' }, ); - assert.equal( - flightsLayer.trackById(icao24, { origin: 'programmatic' }), - true, - ); + assert.equal(flightsLayer.trackById(icao24, { origin: 'programmatic' }), true); assert.equal( _pendingFlightTrackingRestoreForTest(), 'late005', @@ -689,10 +553,7 @@ test('real civil track path creates no native label and publishes every cached h ); _addFlightTrackingCandidateForTest({ icao24: 'late005', - billboard: { - ...billboard, - position: Cesium.Cartesian3.fromDegrees(-97.1, 30.8, 5_000), - }, + billboard: { ...billboard, position: Cesium.Cartesian3.fromDegrees(-97.1, 30.8, 5_000) }, meta: { ...FULL_INFO, callsign: 'LATE5', rawLat: 30.8, rawLon: -97.1 }, history: [], }); @@ -727,11 +588,7 @@ function seedLabelContact({ callsign, registration, tracked = false }) { _setTrackedFlightRefreshStateForTest({ icao24: LABEL_ICAO, entity: null, - billboard: { - position: LABEL_POSITION, - color: Cesium.Color.WHITE, - show: true, - }, + billboard: { position: LABEL_POSITION, color: Cesium.Color.WHITE, show: true }, billboardCollection: { show: true, remove() {} }, viewer: { camera: { positionCartographic: null }, scene: {} }, tracked, @@ -789,11 +646,7 @@ test('civil label chain: a blank callsign falls back to the registration, not th const labels = labelsFor({ callsign: ' ', registration: 'N123AB ' }); for (const [surface, value] of Object.entries(labels)) { if (surface === '_identity') continue; - assert.equal( - value, - 'N123AB', - `${surface} must show the registration, not ${LABEL_ICAO}`, - ); + assert.equal(value, 'N123AB', `${surface} must show the registration, not ${LABEL_ICAO}`); } }); @@ -802,11 +655,7 @@ test('civil label chain: no callsign and no registration still reads as the ICAO const labels = labelsFor({ callsign: null, registration }); for (const [surface, value] of Object.entries(labels)) { if (surface === '_identity') continue; - assert.equal( - value, - LABEL_ICAO, - `${surface} must fall through to the hex`, - ); + assert.equal(value, LABEL_ICAO, `${surface} must fall through to the hex`); } } }); @@ -830,11 +679,7 @@ test('civil label chain: identity stays icao24 while the label moves', () => { ]) { const { _identity } = labelsFor(fixture); for (const [key, value] of Object.entries(_identity)) { - assert.equal( - value, - LABEL_ICAO, - `${key} identity must remain the ICAO hex`, - ); + assert.equal(value, LABEL_ICAO, `${key} identity must remain the ICAO hex`); } } }); @@ -881,11 +726,7 @@ test('civil label chain: the awareness selection EVENT uses the canonical chain' `selection event label for ${JSON.stringify(fixture)}`, ); // Identity must NOT move with the label. - assert.equal( - detail.id, - LABEL_ICAO, - 'selection event identity stays the ICAO hex', - ); + assert.equal(detail.id, LABEL_ICAO, 'selection event identity stays the ICAO hex'); assert.equal(detail.layerId, 'flights'); } }); @@ -896,11 +737,7 @@ test('civil tracked readout: a callsign-less enriched contact reads as its regis _setTrackedFlightRefreshStateForTest({ icao24: LABEL_ICAO, entity, - billboard: { - position: LABEL_POSITION, - color: Cesium.Color.WHITE, - show: false, - }, + billboard: { position: LABEL_POSITION, color: Cesium.Color.WHITE, show: false }, billboardCollection: { show: false, remove() {} }, viewer: { camera: { positionCartographic: null }, scene: {} }, meta: { @@ -921,16 +758,10 @@ test('civil tracked readout: a callsign-less enriched contact reads as its regis const realFetch = globalThis.fetch; globalThis.fetch = async () => ({ - ok: true, - status: 200, - headers: { get: () => null }, - json: async () => ({ time: 0, states: [] }), + ok: true, status: 200, headers: { get: () => null }, json: async () => ({ time: 0, states: [] }), }); try { - await flightsLayer.update({ - camera: { positionCartographic: null }, - scene: {}, - }); + await flightsLayer.update({ camera: { positionCartographic: null }, scene: {} }); assert.match(entity.gevLabelModel.title, /^N123AB\b/); assert.doesNotMatch( [entity.gevLabelModel.title, ...entity.gevLabelModel.details].join(' · '), @@ -1010,8 +841,7 @@ test('military suppression exempts the contact this layer is still restoring', ( // contacts only, never when a 3D model owns the visual (T7 one-shot). // --------------------------------------------------------------------------- -const _floorCarto = (pos) => - Cesium.Cartographic.fromCartesian(pos, Cesium.Ellipsoid.WGS84); +const _floorCarto = (pos) => Cesium.Cartographic.fromCartesian(pos, Cesium.Ellipsoid.WGS84); test('a newly seen taxiing aircraft starts on the floor under its delayed position', async () => { // A grounded contact is drawn at its delayed position lifted onto the floor @@ -1116,16 +946,10 @@ test('display floor: a grounded contact that drifted onto a higher cell is lifte // Fix cell floor was 124.7; the display has taxied into a 140.2 m cell. reportMeshFloorCell(30.2004, -97.6604, 140.2); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); - const out = _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - ); + const out = _floorGroundedDisplayPositionForTest({ onGround: true }, pos, false); const c = _floorCarto(out); - assert.ok( - Math.abs(c.height - (140.2 + GROUND_FLOOR_LIFT_M)) < 0.05, - `height ${c.height} should be 140.2 + lift`, - ); + assert.ok(Math.abs(c.height - (140.2 + GROUND_FLOOR_LIFT_M)) < 0.05, + `height ${c.height} should be 140.2 + lift`); // Lifted straight up — the drifted lat/lon is preserved exactly. assert.ok(Math.abs(Cesium.Math.toDegrees(c.latitude) - 30.2004) < 1e-6); assert.ok(Math.abs(Cesium.Math.toDegrees(c.longitude) + 97.6604) < 1e-6); @@ -1137,11 +961,7 @@ test('display floor: an AIRBORNE contact over the same cell is never lifted', () setMeshFloorPreferred(true); reportMeshFloorCell(30.2004, -97.6604, 140.2); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); - const out = _floorGroundedDisplayPositionForTest( - { onGround: false }, - pos, - false, - ); + const out = _floorGroundedDisplayPositionForTest({ onGround: false }, pos, false); assert.equal(out, pos, 'airborne display positions pass through untouched'); }); @@ -1151,16 +971,8 @@ test('display floor: T7 — a model-owned grounded contact keeps its billboard d setMeshFloorPreferred(true); reportMeshFloorCell(30.2004, -97.6604, 140.2); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); - const out = _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - true, - ); - assert.equal( - out, - pos, - 'moving it would drag groundSnap past its 50 m re-sample threshold', - ); + const out = _floorGroundedDisplayPositionForTest({ onGround: true }, pos, true); + assert.equal(out, pos, 'moving it would drag groundSnap past its 50 m re-sample threshold'); }); test('display floor: a contact already above the floor is passed through unchanged', () => { @@ -1169,11 +981,7 @@ test('display floor: a contact already above the floor is passed through unchang setMeshFloorPreferred(true); reportMeshFloorCell(30.2004, -97.6604, 124.7); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 140.2); - const out = _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - ); + const out = _floorGroundedDisplayPositionForTest({ onGround: true }, pos, false); assert.equal(out, pos, 'no rebuild when nothing needs lifting'); }); @@ -1182,11 +990,7 @@ test('display floor: a cold display cell leaves the contact where it is', () => _clearMeshFloorCellsForTest(); setMeshFloorPreferred(true); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); - const out = _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - ); + const out = _floorGroundedDisplayPositionForTest({ onGround: true }, pos, false); assert.equal(out, pos, 'no floor data → no clamp (never invent a surface)'); }); @@ -1197,11 +1001,7 @@ test('display floor: mesh cells are ignored outside the google-3d regime', () => reportMeshFloorCell(30.2004, -97.6604, 140.2); setMeshFloorPreferred(false); // globe stack: the DEM IS the rendered terrain const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); - const out = _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - ); + const out = _floorGroundedDisplayPositionForTest({ onGround: true }, pos, false); assert.equal(out, pos); setMeshFloorPreferred(true); }); @@ -1217,23 +1017,14 @@ test('display floor: jitter across a cell edge does not alternate the floor', () reportMeshFloorCell(30.2014, -97.6604, 146); const at = (lat) => { const out = _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, lat, 120), - false, - 'jitter', + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, lat, 120), false, 'jitter', ); return _floorCarto(out).height; }; const settled = at(30.2004); // squarely inside the low cell - assert.ok( - Math.abs(settled - (140 + GROUND_FLOOR_LIFT_M)) < 0.05, - `settled ${settled}`, - ); + assert.ok(Math.abs(settled - (140 + GROUND_FLOOR_LIFT_M)) < 0.05, `settled ${settled}`); // 30.2006 belongs to the HIGH cell but is only ~11 m past the boundary. - assert.ok( - Math.abs(at(30.2006) - settled) < 0.05, - 'held the low cell across the edge', - ); + assert.ok(Math.abs(at(30.2006) - settled) < 0.05, 'held the low cell across the edge'); assert.ok(Math.abs(at(30.2004) - settled) < 0.05, 'and back again — no pop'); }); @@ -1243,21 +1034,12 @@ test('display floor: a real crossing does adopt the next cell', () => { setMeshFloorPreferred(true); reportMeshFloorCell(30.2004, -97.6604, 140); reportMeshFloorCell(30.2014, -97.6604, 146); - const at = (lat) => - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, lat, 120), - false, - 'crossing', - ), - ).height; + const at = (lat) => _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, lat, 120), false, 'crossing', + )).height; at(30.2004); const crossed = at(30.2014); // properly into the next cell - assert.ok( - Math.abs(crossed - (146 + GROUND_FLOOR_LIFT_M)) < 0.05, - `crossed ${crossed}`, - ); + assert.ok(Math.abs(crossed - (146 + GROUND_FLOOR_LIFT_M)) < 0.05, `crossed ${crossed}`); }); // --- F7: no rebuild when nothing changed ----------------------------------- @@ -1272,28 +1054,10 @@ test('display floor: a clamped stationary contact reuses its previous output', ( setMeshFloorPreferred(true); reportMeshFloorCell(30.2004, -97.6604, 140); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 120); - const first = _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'parked', - ); - const second = _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'parked', - ); - assert.notEqual( - first, - pos, - 'it really is clamped (not the pass-through case)', - ); - assert.equal( - second, - first, - 'same input + same floor ⇒ the very same object, no rebuild', - ); + const first = _floorGroundedDisplayPositionForTest({ onGround: true }, pos, false, 'parked'); + const second = _floorGroundedDisplayPositionForTest({ onGround: true }, pos, false, 'parked'); + assert.notEqual(first, pos, 'it really is clamped (not the pass-through case)'); + assert.equal(second, first, 'same input + same floor ⇒ the very same object, no rebuild'); }); test('display floor: the cached output is dropped when the floor changes under it', () => { @@ -1302,31 +1066,14 @@ test('display floor: the cached output is dropped when the floor changes under i setMeshFloorPreferred(true); reportMeshFloorCell(30.2004, -97.6604, 140); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 120); - const before = _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'warming', - ), - ).height; + const before = _floorCarto(_floorGroundedDisplayPositionForTest({ onGround: true }, pos, false, 'warming')).height; // A late mesh sample raises this cell (a fresh module state stands in for the // one-shot latch, which never overwrites). _clearMeshFloorCellsForTest(); reportMeshFloorCell(30.2004, -97.6604, 175); - const after = _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'warming', - ), - ).height; + const after = _floorCarto(_floorGroundedDisplayPositionForTest({ onGround: true }, pos, false, 'warming')).height; assert.ok(Math.abs(before - 141.5) < 0.05, `before ${before}`); - assert.ok( - Math.abs(after - 176.5) < 0.05, - `after ${after} — a stale cache would still read 141.5`, - ); + assert.ok(Math.abs(after - 176.5) < 0.05, `after ${after} — a stale cache would still read 141.5`); }); // --- F8: hold the last known floor through a floor-data gap ---------------- @@ -1346,28 +1093,18 @@ test('display floor: a cold cell HOLDS the last floor that resolved for this con reportMeshFloorCell(30.2004, -97.6604, 140.2); // On the warm cell: clamped onto the floor as usual. const warm = _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7), - false, - 'held', - ); - assert.ok( - Math.abs(_floorCarto(warm).height - (140.2 + GROUND_FLOOR_LIFT_M)) < 0.05, + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7), false, 'held', ); + assert.ok(Math.abs(_floorCarto(warm).height - (140.2 + GROUND_FLOOR_LIFT_M)) < 0.05); // Taxis ~550 m onto a cell nothing has resolved, still at the geoid-ish // height the poll path handed it. Pre-fix this passed straight through — // buried. const cold = _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, 30.2054, 124.7), - false, - 'held', + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, 30.2054, 124.7), false, 'held', ); const h = _floorCarto(cold).height; - assert.ok( - Math.abs(h - (140.2 + GROUND_FLOOR_LIFT_M)) < 0.05, - `held ${h} — a cold cell must hold 140.2 + lift, never the 124.7 it arrived with`, - ); + assert.ok(Math.abs(h - (140.2 + GROUND_FLOOR_LIFT_M)) < 0.05, + `held ${h} — a cold cell must hold 140.2 + lift, never the 124.7 it arrived with`); }); test('display floor: with NO prior anywhere the contact is left exactly where it is', () => { @@ -1375,17 +1112,8 @@ test('display floor: with NO prior anywhere the contact is left exactly where it _clearMeshFloorCellsForTest(); setMeshFloorPreferred(true); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); - const out = _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'fresh', - ); - assert.equal( - out, - pos, - 'nothing measured anywhere ⇒ nothing invented — same object, no clamp', - ); + const out = _floorGroundedDisplayPositionForTest({ onGround: true }, pos, false, 'fresh'); + assert.equal(out, pos, 'nothing measured anywhere ⇒ nothing invented — same object, no clamp'); }); test('display floor: a fresh contact on a cold cell adopts a resolved NEIGHBOUR', () => { @@ -1398,16 +1126,11 @@ test('display floor: a fresh contact on a cold cell adopts a resolved NEIGHBOUR' reportMeshFloorCell(30.2014, -97.6604, 141.0); reportMeshFloorCell(30.1994, -97.6604, 152.0); const out = _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7), - false, - 'neighbour', + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7), false, 'neighbour', ); const h = _floorCarto(out).height; - assert.ok( - Math.abs(h - (141.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `neighbour hold ${h} — expected the lower of the two resolved neighbours`, - ); + assert.ok(Math.abs(h - (141.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + `neighbour hold ${h} — expected the lower of the two resolved neighbours`); }); // Drives a stationary contact at the production 80 ms fleet cadence and reports @@ -1415,28 +1138,16 @@ test('display floor: a fresh contact on a cold cell adopts a resolved NEIGHBOUR' // `mutate` runs before the tick at that index, so a target can be moved // mid-approach. function _driveEase(id, pos, fromMs, ticks, mutate = () => {}) { - let previous = _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - id, - fromMs, - ), - ).height; + let previous = _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, pos, false, id, fromMs, + )).height; const opened = previous; let maxStepM = 0; for (let i = 1; i <= ticks; i += 1) { mutate(i); - const h = _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - id, - fromMs + i * 80, - ), - ).height; + const h = _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, pos, false, id, fromMs + i * 80, + )).height; maxStepM = Math.max(maxStepM, previous - h); previous = h; } @@ -1455,18 +1166,10 @@ test('display floor: a hold released onto a LOWER floor eases down, never snaps' if (i === 1) reportMeshFloorCell(30.2004, -97.6604, 160.0); // its own cell resolves lower }); const totalDropM = r.opened - (160.0 + GROUND_FLOOR_LIFT_M); - assert.ok( - Math.abs(r.opened - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `opened at ${r.opened}`, - ); - assert.ok( - r.maxStepM <= totalDropM * 0.25, - `largest single-tick drop ${r.maxStepM.toFixed(1)} m of ${totalDropM.toFixed(1)} m — a snap is the whole drop at once`, - ); - assert.ok( - Math.abs(r.settled - (160.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `settled at ${r.settled}`, - ); + assert.ok(Math.abs(r.opened - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05, `opened at ${r.opened}`); + assert.ok(r.maxStepM <= totalDropM * 0.25, + `largest single-tick drop ${r.maxStepM.toFixed(1)} m of ${totalDropM.toFixed(1)} m — a snap is the whole drop at once`); + assert.ok(Math.abs(r.settled - (160.0 + GROUND_FLOOR_LIFT_M)) < 0.05, `settled at ${r.settled}`); }); test('display floor: a hold released onto a HIGHER floor rises immediately', () => { @@ -1474,26 +1177,17 @@ test('display floor: a hold released onto a HIGHER floor rises immediately', () _clearMeshFloorCellsForTest(); setMeshFloorPreferred(true); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); - const at = (ms) => - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'rise', - ms, - ), - ).height; + const at = (ms) => _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, pos, false, 'rise', ms, + )).height; reportMeshFloorCell(30.2014, -97.6604, 160.0); reportMeshFloorCell(30.1994, -97.6604, 190.0); assert.ok(Math.abs(at(1000) - (160.0 + GROUND_FLOOR_LIFT_M)) < 0.05); reportMeshFloorCell(30.2004, -97.6604, 205.0); // No easing UP: an eased rise is time spent under the mesh, which is the // whole failure this path exists to prevent. - assert.ok( - Math.abs(at(1001) - (205.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - 'the next tick is already on the higher floor', - ); + assert.ok(Math.abs(at(1001) - (205.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + 'the next tick is already on the higher floor'); }); // --- F9: the hold is a live state machine, not a one-shot ------------------ @@ -1507,24 +1201,17 @@ test('display floor: a STATIONARY unresolved contact still adopts a floor that a _clearMeshFloorCellsForTest(); setMeshFloorPreferred(true); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); - const at = (ms) => - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'parked-cold', - ms, - ); + const at = (ms) => _floorGroundedDisplayPositionForTest( + { onGround: true }, pos, false, 'parked-cold', ms, + ); assert.equal(at(1000), pos, 'nothing anywhere yet — passes through'); // The poll's warm batch lands the cells NEXT DOOR (this contact's own cell // is still cold, so only the hold chain can see them). reportMeshFloorCell(30.2014, -97.6604, 138.0); reportMeshFloorCell(30.1994, -97.6604, 160.0); const adopted = _floorCarto(at(1600)).height; // past the probe throttle - assert.ok( - Math.abs(adopted - (138.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `stationary contact ${adopted} — a memoized unresolved answer would still read 124.7`, - ); + assert.ok(Math.abs(adopted - (138.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + `stationary contact ${adopted} — a memoized unresolved answer would still read 124.7`); }); test('display floor: a stationary contact FOLLOWS the neighbourhood as it fills in', () => { @@ -1532,16 +1219,9 @@ test('display floor: a stationary contact FOLLOWS the neighbourhood as it fills _clearMeshFloorCellsForTest(); setMeshFloorPreferred(true); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 100); - const at = (ms) => - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'upgrade', - ms, - ), - ).height; + const at = (ms) => _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, pos, false, 'upgrade', ms, + )).height; reportMeshFloorCell(30.2014, -97.6604, 200.0); reportMeshFloorCell(30.1994, -97.6604, 240.0); assert.ok(Math.abs(at(1000) - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05); @@ -1551,10 +1231,8 @@ test('display floor: a stationary contact FOLLOWS the neighbourhood as it fills reportMeshFloorCell(30.2004, -97.6614, 120.0); let h = 0; for (let ms = 1600; ms <= 6000; ms += 80) h = at(ms); - assert.ok( - Math.abs(h - (120.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `followed to ${h} — a borrowed floor must not outlive a better one`, - ); + assert.ok(Math.abs(h - (120.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + `followed to ${h} — a borrowed floor must not outlive a better one`); }); test('display floor: an on_ground FLAP mid-takeoff-roll never dips below the runway', () => { @@ -1568,35 +1246,23 @@ test('display floor: an on_ground FLAP mid-takeoff-roll never dips below the run // the geoid. Deleting the hold on the airborne poll made that switch visible: // the contact came back grounded with no prior, outrunning its own floor // cells at 23 m/s, and sat under the runway (owner sighting, VIR138M). - const GROUND = -28.5, - GEOID = -32.5, - LON = -73.78; + const GROUND = -28.5, GEOID = -32.5, LON = -73.78; reportMeshFloorCell(40.64, LON, GROUND); // only the cell it STARTED on is warm let lat = 40.64; const grounded = []; for (let i = 0; i <= 22; i += 1) { const onGround = i !== 10; // one airborne poll mid-roll const out = _floorGroundedDisplayPositionForTest( - { onGround }, - Cesium.Cartesian3.fromDegrees(LON, lat, GEOID), - false, - 'VIR138M', - 1000 + i * 80, + { onGround }, Cesium.Cartesian3.fromDegrees(LON, lat, GEOID), false, 'VIR138M', 1000 + i * 80, ); if (onGround) grounded.push(_floorCarto(out).height); lat += 0.00021; // ~23 m per tick at 45 kt } const dipped = grounded.filter((h) => h < GROUND - 0.05); - assert.deepEqual( - dipped, - [], - `${dipped.length} of ${grounded.length} grounded ticks rendered below the runway`, - ); - assert.ok( - Math.abs(grounded[grounded.length - 1] - (GROUND + GROUND_FLOOR_LIFT_M)) < - 0.05, - 'and it is still standing on the floor it held, not on nothing', - ); + assert.deepEqual(dipped, [], + `${dipped.length} of ${grounded.length} grounded ticks rendered below the runway`); + assert.ok(Math.abs(grounded[grounded.length - 1] - (GROUND + GROUND_FLOOR_LIFT_M)) < 0.05, + 'and it is still standing on the floor it held, not on nothing'); }); test('display floor: the rehydration seed EXPIRES on its own age, with NO calls in between', () => { @@ -1606,45 +1272,20 @@ test('display floor: the rehydration seed EXPIRES on its own age, with NO calls const field = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 100); reportMeshFloorCell(30.2014, -97.6604, 200.0); reportMeshFloorCell(30.1994, -97.6604, 240.0); - assert.ok( - Math.abs( - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - field, - false, - 'longhaul', - 1000, - ), - ).height - - (200.0 + GROUND_FLOOR_LIFT_M), - ) < 0.05, - 'holding 200 m before departure', - ); + assert.ok(Math.abs(_floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, field, false, 'longhaul', 1000, + )).height - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05, 'holding 200 m before departure'); // ONE airborne tick parks the seed, and then this contact is not seen again // for 198 s: off the poll on a cruise, outside the corridor radius, tab // hidden. Nothing runs in between, so the retire path never gets a second // chance to expire it — the age has to be judged where it comes BACK. // An earlier cut checked expiry only on the retire path and cleared // `retiredMs` before validating it, so this reused the 200 m floor. - _floorGroundedDisplayPositionForTest( - { onGround: false }, - field, - false, - 'longhaul', - 2000, - ); + _floorGroundedDisplayPositionForTest({ onGround: false }, field, false, 'longhaul', 2000); _clearMeshFloorCellsForTest(); // a different airport: nothing warm here const back = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 100); assert.equal( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - back, - false, - 'longhaul', - 200000, - ), - back, + _floorGroundedDisplayPositionForTest({ onGround: true }, back, false, 'longhaul', 200000), back, 'an expired seed is gone — no floor is invented from a 198-second-old measurement', ); }); @@ -1656,40 +1297,15 @@ test('display floor: the seed expires the same way when the contact keeps report const field = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 100); reportMeshFloorCell(30.2014, -97.6604, 200.0); reportMeshFloorCell(30.1994, -97.6604, 240.0); - _floorGroundedDisplayPositionForTest( - { onGround: true }, - field, - false, - 'cruise', - 1000, - ); + _floorGroundedDisplayPositionForTest({ onGround: true }, field, false, 'cruise', 1000); // The other half of the same clock: an airborne contact that IS still being // ticked. The window is the window either way. - _floorGroundedDisplayPositionForTest( - { onGround: false }, - field, - false, - 'cruise', - 2000, - ); - _floorGroundedDisplayPositionForTest( - { onGround: false }, - field, - false, - 'cruise', - 200000, - ); + _floorGroundedDisplayPositionForTest({ onGround: false }, field, false, 'cruise', 2000); + _floorGroundedDisplayPositionForTest({ onGround: false }, field, false, 'cruise', 200000); _clearMeshFloorCellsForTest(); const back = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 100); assert.equal( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - back, - false, - 'cruise', - 201000, - ), - back, + _floorGroundedDisplayPositionForTest({ onGround: true }, back, false, 'cruise', 201000), back, 'no memory of the departure field survives the grace window', ); }); @@ -1705,28 +1321,10 @@ test('display floor: a rehydrated seed ranks BELOW fresh neighbour evidence', () setMeshFloorPreferred(true); const departed = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 50); reportMeshFloorCell(30.2004, -97.6604, 200.0); - assert.ok( - Math.abs( - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - departed, - false, - 'short-hop', - 1000, - ), - ).height - - (200.0 + GROUND_FLOOR_LIFT_M), - ) < 0.05, - 'standing on its own 200 m cell', - ); - _floorGroundedDisplayPositionForTest( - { onGround: false }, - departed, - false, - 'short-hop', - 2000, - ); + assert.ok(Math.abs(_floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, departed, false, 'short-hop', 1000, + )).height - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05, 'standing on its own 200 m cell'); + _floorGroundedDisplayPositionForTest({ onGround: false }, departed, false, 'short-hop', 2000); // Re-grounds 0.56 km away — INSIDE HELD_FLOOR_MAX_DRIFT_KM, so the drift // bound does not save us here; the tier order has to. Its own cell is cold, // but two adjacent cells have since resolved, and they are 100 m of ground. @@ -1734,19 +1332,11 @@ test('display floor: a rehydrated seed ranks BELOW fresh neighbour evidence', () reportMeshFloorCell(30.2044, -97.6604, 100.0); reportMeshFloorCell(30.2064, -97.6604, 105.0); const moved = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2054, 50); - const rendered = _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - moved, - false, - 'short-hop', - 3000, - ), - ).height; - assert.ok( - Math.abs(rendered - (100.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `rendered at ${rendered} — a seed that outranked the neighbourhood read 201.5, 100 m in the air`, - ); + const rendered = _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, moved, false, 'short-hop', 3000, + )).height; + assert.ok(Math.abs(rendered - (100.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + `rendered at ${rendered} — a seed that outranked the neighbourhood read 201.5, 100 m in the air`); }); test('display floor: with nothing fresh to contradict it, the seed still answers', () => { @@ -1758,34 +1348,14 @@ test('display floor: with nothing fresh to contradict it, the seed still answers // warmed either. Demoting the seed must not mean discarding it. const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 50); reportMeshFloorCell(30.2004, -97.6604, 200.0); - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'flap', - 1000, - ); - _floorGroundedDisplayPositionForTest( - { onGround: false }, - pos, - false, - 'flap', - 2000, - ); + _floorGroundedDisplayPositionForTest({ onGround: true }, pos, false, 'flap', 1000); + _floorGroundedDisplayPositionForTest({ onGround: false }, pos, false, 'flap', 2000); _clearMeshFloorCellsForTest(); // the whole neighbourhood goes cold - const held = _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, 30.2014, 50), - false, - 'flap', - 3000, - ), - ).height; - assert.ok( - Math.abs(held - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `held ${held} — with no measurement anywhere the seed is the best thing available`, - ); + const held = _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, 30.2014, 50), false, 'flap', 3000, + )).height; + assert.ok(Math.abs(held - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + `held ${held} — with no measurement anywhere the seed is the best thing available`); }); test('display floor: re-measuring its own cell makes a seeded contact authoritative again', () => { @@ -1794,56 +1364,24 @@ test('display floor: re-measuring its own cell makes a seeded contact authoritat setMeshFloorPreferred(true); const pad = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 100); reportMeshFloorCell(30.2004, -97.6604, 300.0); - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pad, - false, - 'relatch', - 1000, - ); - _floorGroundedDisplayPositionForTest( - { onGround: false }, - pad, - false, - 'relatch', - 2000, - ); + _floorGroundedDisplayPositionForTest({ onGround: true }, pad, false, 'relatch', 1000); + _floorGroundedDisplayPositionForTest({ onGround: false }, pad, false, 'relatch', 2000); // Re-grounds on its own, still-warm 300 m cell: that is a live reading, so the // demotion is over and the ordinary tier order applies again. - assert.ok( - Math.abs( - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pad, - false, - 'relatch', - 3000, - ), - ).height - - (300.0 + GROUND_FLOOR_LIFT_M), - ) < 0.05, - 'back on its own measured cell', - ); + assert.ok(Math.abs(_floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, pad, false, 'relatch', 3000, + )).height - (300.0 + GROUND_FLOOR_LIFT_M)) < 0.05, 'back on its own measured cell'); // It now taxis 220 m onto a cold cell whose two adjacent cells read 100 m. An // own floor it measured while standing here outranks a borrowed one — the // seed flag has to have been cleared by that reading, or the contact drops // 200 m onto someone else's ground. reportMeshFloorCell(30.2014, -97.6604, 100.0); reportMeshFloorCell(30.2034, -97.6604, 100.0); - const taxied = _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, 30.2024, 100), - false, - 'relatch', - 4000, - ), - ).height; - assert.ok( - Math.abs(taxied - (300.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `taxied at ${taxied} — a still-seeded contact would have taken the 100 m neighbours`, - ); + const taxied = _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, 30.2024, 100), false, 'relatch', 4000, + )).height; + assert.ok(Math.abs(taxied - (300.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + `taxied at ${taxied} — a still-seeded contact would have taken the 100 m neighbours`); }); test('display floor: taking off and landing elsewhere does NOT inherit the old field floor', () => { @@ -1853,62 +1391,25 @@ test('display floor: taking off and landing elsewhere does NOT inherit the old f const field = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); reportMeshFloorCell(30.2004, -97.6604, 200.0); // 1. grounded on a 200 m floor. - assert.ok( - Math.abs( - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - field, - false, - 'trip', - 1000, - ), - ).height - - (200.0 + GROUND_FLOOR_LIFT_M), - ) < 0.05, - ); + assert.ok(Math.abs(_floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, field, false, 'trip', 1000, + )).height - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05); // 2. it taxis ~330 m onto ground nothing has resolved, so 200 m is now being // HELD — this is the state that must not survive the trip. const heldAt = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2034, 124.7); - assert.ok( - Math.abs( - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - heldAt, - false, - 'trip', - 1500, - ), - ).height - - (200.0 + GROUND_FLOOR_LIFT_M), - ) < 0.05, - 'the hold is active before takeoff', - ); + assert.ok(Math.abs(_floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, heldAt, false, 'trip', 1500, + )).height - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05, 'the hold is active before takeoff'); // 3. airborne — the hold belongs to the grounded-billboard regime, not to the // contact, so leaving that regime must retire it. - _floorGroundedDisplayPositionForTest( - { onGround: false }, - heldAt, - false, - 'trip', - 2000, - ); + _floorGroundedDisplayPositionForTest({ onGround: false }, heldAt, false, 'trip', 2000); // 4. lands at another field on a real 100 m floor. reportMeshFloorCell(31.5004, -97.6604, 100.0); - const landed = _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, 31.5004, 60), - false, - 'trip', - 3000, - ), - ).height; - assert.ok( - Math.abs(landed - (100.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `landed at ${landed} — a stale hold would arrive at 201.5 and ease down from it`, - ); + const landed = _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, 31.5004, 60), false, 'trip', 3000, + )).height; + assert.ok(Math.abs(landed - (100.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + `landed at ${landed} — a stale hold would arrive at 201.5 and ease down from it`); }); test('display floor: a model-owned interval retires the hold too', () => { @@ -1917,47 +1418,21 @@ test('display floor: a model-owned interval retires the hold too', () => { setMeshFloorPreferred(true); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); reportMeshFloorCell(30.2004, -97.6604, 200.0); - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'handoff', - 1000, - ); + _floorGroundedDisplayPositionForTest({ onGround: true }, pos, false, 'handoff', 1000); // It moves onto unresolved ground, so 200 m is being HELD. const heldAt = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2034, 124.7); - _floorGroundedDisplayPositionForTest( - { onGround: true }, - heldAt, - false, - 'handoff', - 1500, - ); + _floorGroundedDisplayPositionForTest({ onGround: true }, heldAt, false, 'handoff', 1500); // T7: the 3D model takes the visual. Our state is retired (nothing visual is // touched — groundSnap's one-shot input is untouched either way). - _floorGroundedDisplayPositionForTest( - { onGround: true }, - heldAt, - true, - 'handoff', - 2000, - ); + _floorGroundedDisplayPositionForTest({ onGround: true }, heldAt, true, 'handoff', 2000); // The billboard resumes over ground that has since resolved LOWER. A stale // heldActive would read this as a hold release and ease; it is a fresh start. reportMeshFloorCell(30.2034, -97.6604, 150.0); - const back = _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - heldAt, - false, - 'handoff', - 3000, - ), - ).height; - assert.ok( - Math.abs(back - (150.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `resumed at ${back} — no ease, because there was no hold to release`, - ); + const back = _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, heldAt, false, 'handoff', 3000, + )).height; + assert.ok(Math.abs(back - (150.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + `resumed at ${back} — no ease, because there was no hold to release`); }); test('display floor: a model handoff back onto a COLD cell takes the neighbourhood, not the seed', () => { @@ -1970,44 +1445,18 @@ test('display floor: a model handoff back onto a COLD cell takes the neighbourho // a floor it measured 330 m ago. Two adjacent cells have resolved at 150 m. const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 124.7); reportMeshFloorCell(30.2004, -97.6604, 200.0); - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'cold-handoff', - 1000, - ); + _floorGroundedDisplayPositionForTest({ onGround: true }, pos, false, 'cold-handoff', 1000); const heldAt = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2034, 124.7); - _floorGroundedDisplayPositionForTest( - { onGround: true }, - heldAt, - false, - 'cold-handoff', - 1500, - ); - _floorGroundedDisplayPositionForTest( - { onGround: true }, - heldAt, - true, - 'cold-handoff', - 2000, - ); + _floorGroundedDisplayPositionForTest({ onGround: true }, heldAt, false, 'cold-handoff', 1500); + _floorGroundedDisplayPositionForTest({ onGround: true }, heldAt, true, 'cold-handoff', 2000); _clearMeshFloorCellsForTest(); reportMeshFloorCell(30.2024, -97.6604, 150.0); reportMeshFloorCell(30.2044, -97.6604, 155.0); - const back = _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - heldAt, - false, - 'cold-handoff', - 3000, - ), - ).height; - assert.ok( - Math.abs(back - (150.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `resumed at ${back} — a seed that outranked the neighbourhood would resume at 201.5`, - ); + const back = _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, heldAt, false, 'cold-handoff', 3000, + )).height; + assert.ok(Math.abs(back - (150.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + `resumed at ${back} — a seed that outranked the neighbourhood would resume at 201.5`); }); // --- F10: no contact waits on another -------------------------------------- @@ -2030,28 +1479,15 @@ test('display floor: every grounded contact is floored on the tick it asks', () } const heights = []; for (let i = 0; i < N; i += 1) { - heights.push( - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.66, latOf(i), 100), - false, - `q${i}`, - 1000, - ), - ).height, - ); + heights.push(_floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.66, latOf(i), 100), false, `q${i}`, 1000, + )).height); } const unfloored = heights - .map((h, i) => - Math.abs(h - (300 + i + GROUND_FLOOR_LIFT_M)) < 0.05 ? null : i, - ) + .map((h, i) => (Math.abs(h - (300 + i + GROUND_FLOOR_LIFT_M)) < 0.05 ? null : i)) .filter((i) => i != null); - assert.deepEqual( - unfloored, - [], - 'a global budget would leave the tail of the fleet on the geoid for later ticks', - ); + assert.deepEqual(unfloored, [], + 'a global budget would leave the tail of the fleet on the geoid for later ticks'); }); test('display floor: a re-probe that finds a LOWER neighbour eases down, never snaps', () => { @@ -2068,14 +1504,9 @@ test('display floor: a re-probe that finds a LOWER neighbour eases down, never s if (i === 8) reportMeshFloorCell(30.2004, -97.6614, 120.0); // past the 500 ms probe throttle }); const totalDropM = r.opened - target; - assert.ok( - r.maxStepM <= totalDropM * 0.25, - `largest single-tick drop ${r.maxStepM.toFixed(1)} m of ${totalDropM.toFixed(1)} m`, - ); - assert.ok( - Math.abs(r.settled - target) < 0.05, - `settled at ${r.settled}, expected ${target}`, - ); + assert.ok(r.maxStepM <= totalDropM * 0.25, + `largest single-tick drop ${r.maxStepM.toFixed(1)} m of ${totalDropM.toFixed(1)} m`); + assert.ok(Math.abs(r.settled - target) < 0.05, `settled at ${r.settled}, expected ${target}`); }); test('display floor: a SECOND lower re-latch mid-approach retargets without a jump', () => { @@ -2095,14 +1526,9 @@ test('display floor: a SECOND lower re-latch mid-approach retargets without a ju if (i === 16) reportMeshFloorCell(30.2004, -97.6594, 100.0); // -> 100 m, mid-approach }); const totalDropM = r.opened - target; - assert.ok( - r.maxStepM <= totalDropM * 0.25, - `largest single-tick drop ${r.maxStepM.toFixed(1)} m of ${totalDropM.toFixed(1)} m — the retarget must not be a seam`, - ); - assert.ok( - Math.abs(r.settled - target) < 0.05, - `settled at ${r.settled}, expected ${target}`, - ); + assert.ok(r.maxStepM <= totalDropM * 0.25, + `largest single-tick drop ${r.maxStepM.toFixed(1)} m of ${totalDropM.toFixed(1)} m — the retarget must not be a seam`); + assert.ok(Math.abs(r.settled - target) < 0.05, `settled at ${r.settled}, expected ${target}`); }); test('display floor: a DELAYED tick cannot close more of the gap than a prompt one', () => { @@ -2110,16 +1536,9 @@ test('display floor: a DELAYED tick cannot close more of the gap than a prompt o _clearMeshFloorCellsForTest(); setMeshFloorPreferred(true); const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 100); - const at = (ms) => - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'stall', - ms, - ), - ).height; + const at = (ms) => _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, pos, false, 'stall', ms, + )).height; reportMeshFloorCell(30.2014, -97.6604, 400.0); reportMeshFloorCell(30.1994, -97.6604, 440.0); // borrowed floor = 400 m const opened = at(1000); @@ -2138,10 +1557,8 @@ test('display floor: a DELAYED tick cannot close more of the gap than a prompt o maxStepM = Math.max(maxStepM, previous - h); previous = h; } - assert.ok( - maxStepM <= totalDropM * 0.25, - `largest step ${maxStepM.toFixed(1)} m of ${totalDropM.toFixed(1)} m across a stalled schedule`, - ); + assert.ok(maxStepM <= totalDropM * 0.25, + `largest step ${maxStepM.toFixed(1)} m of ${totalDropM.toFixed(1)} m across a stalled schedule`); assert.ok(previous < opened, 'and it is still making progress downward'); }); @@ -2152,16 +1569,9 @@ test('display floor: a floor rising MID-APPROACH is taken whole, on that tick', const pos = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 100); reportMeshFloorCell(30.2014, -97.6604, 400.0); reportMeshFloorCell(30.1994, -97.6604, 440.0); // borrowed floor = 400 m - const at = (ms) => - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - pos, - false, - 'riseMid', - ms, - ), - ).height; + const at = (ms) => _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, pos, false, 'riseMid', ms, + )).height; at(1000); reportMeshFloorCell(30.2004, -97.6604, 150.0); // own cell resolves far below let h = 0; @@ -2172,10 +1582,8 @@ test('display floor: a floor rising MID-APPROACH is taken whole, on that tick', _clearMeshFloorCellsForTest(); reportMeshFloorCell(30.2004, -97.6604, 500.0); const risen = at(1000 + 5 * 80); - assert.ok( - Math.abs(risen - (500.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - `rose to ${risen} — an eased rise is time spent under the mesh`, - ); + assert.ok(Math.abs(risen - (500.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + `rose to ${risen} — an eased rise is time spent under the mesh`); }); test('display floor: a held floor is dropped once the contact leaves the ground it describes', () => { @@ -2183,38 +1591,18 @@ test('display floor: a held floor is dropped once the contact leaves the ground _clearMeshFloorCellsForTest(); setMeshFloorPreferred(true); reportMeshFloorCell(30.2004, -97.6604, 200.0); - const at = (lat, ms) => - _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, lat, 124.7), - false, - 'drift', - ms, - ); - assert.ok( - Math.abs( - _floorCarto(at(30.2004, 1000)).height - (200.0 + GROUND_FLOOR_LIFT_M), - ) < 0.05, + const at = (lat, ms) => _floorGroundedDisplayPositionForTest( + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, lat, 124.7), false, 'drift', ms, ); + assert.ok(Math.abs(_floorCarto(at(30.2004, 1000)).height - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05); // ~670 m on: cold cell, but that is inside one poll's rollout. Hold stands. - assert.ok( - Math.abs( - _floorCarto(at(30.2064, 2000)).height - (200.0 + GROUND_FLOOR_LIFT_M), - ) < 0.05, - 'inside the drift bound the held floor still describes the ground', - ); + assert.ok(Math.abs(_floorCarto(at(30.2064, 2000)).height - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + 'inside the drift bound the held floor still describes the ground'); // ~3.3 km on: the held value is no longer a measurement of anywhere this // contact has been, so it is dropped rather than stretched. const far = Cesium.Cartesian3.fromDegrees(-97.6604, 30.2304, 124.7); assert.equal( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - far, - false, - 'drift', - 3000, - ), - far, + _floorGroundedDisplayPositionForTest({ onGround: true }, far, false, 'drift', 3000), far, 'past the drift bound: back to no clamp, not a stretched one', ); }); @@ -2228,21 +1616,12 @@ test('display floor: an ordinary cell-to-cell floor DROP keeps its existing timi // untouched by this round. reportMeshFloorCell(30.2004, -97.6604, 200.0); reportMeshFloorCell(30.2044, -97.6604, 160.0); - const at = (lat, ms) => - _floorCarto( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, lat, 124.7), - false, - 'plain', - ms, - ), - ).height; + const at = (lat, ms) => _floorCarto(_floorGroundedDisplayPositionForTest( + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, lat, 124.7), false, 'plain', ms, + )).height; assert.ok(Math.abs(at(30.2004, 1000) - (200.0 + GROUND_FLOOR_LIFT_M)) < 0.05); - assert.ok( - Math.abs(at(30.2044, 1050) - (160.0 + GROUND_FLOOR_LIFT_M)) < 0.05, - 'the very next tick is on the new cell — no ease was introduced here', - ); + assert.ok(Math.abs(at(30.2044, 1050) - (160.0 + GROUND_FLOOR_LIFT_M)) < 0.05, + 'the very next tick is on the new cell — no ease was introduced here'); }); test('display floor: the hold never applies to an airborne contact or a modelled one', () => { @@ -2254,23 +1633,11 @@ test('display floor: the hold never applies to an airborne contact or a modelled _floorGroundedDisplayPositionForTest({ onGround: true }, on, false, 'gates'); const cold = Cesium.Cartesian3.fromDegrees(-97.6704, 30.2104, 124.7); assert.equal( - _floorGroundedDisplayPositionForTest( - { onGround: false }, - cold, - false, - 'gates', - ), - cold, + _floorGroundedDisplayPositionForTest({ onGround: false }, cold, false, 'gates'), cold, 'airborne heights belong to the fix-time clamp, held floor or not', ); assert.equal( - _floorGroundedDisplayPositionForTest( - { onGround: true }, - cold, - true, - 'gates', - ), - cold, + _floorGroundedDisplayPositionForTest({ onGround: true }, cold, true, 'gates'), cold, 'T7: a model-owned contact keeps its billboard datum, held floor or not', ); }); @@ -2281,21 +1648,11 @@ test('display floor: two contacts on the same cell get their own outputs', () => setMeshFloorPreferred(true); reportMeshFloorCell(30.2004, -97.6604, 140); const a = _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 120), - false, - 'A', + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.6604, 30.2004, 120), false, 'A', ); const b = _floorGroundedDisplayPositionForTest( - { onGround: true }, - Cesium.Cartesian3.fromDegrees(-97.66041, 30.20041, 121), - false, - 'B', - ); - assert.notEqual( - a, - b, - 'a shared scratch would hand both contacts the same object', + { onGround: true }, Cesium.Cartesian3.fromDegrees(-97.66041, 30.20041, 121), false, 'B', ); + assert.notEqual(a, b, 'a shared scratch would hand both contacts the same object'); assert.ok(Math.abs(_floorCarto(a).height - _floorCarto(b).height) < 0.05); }); diff --git a/src/data/militaryFlights.test.mjs b/src/data/militaryFlights.test.mjs index cd29c1ebd..9e6e4aea8 100644 --- a/src/data/militaryFlights.test.mjs +++ b/src/data/militaryFlights.test.mjs @@ -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'); }); @@ -105,14 +101,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', () => { @@ -121,8 +111,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`); } }); @@ -134,17 +123,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 }); @@ -174,22 +158,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; } @@ -235,26 +208,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', + }] : [], }), }); @@ -279,10 +247,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, @@ -320,18 +285,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); @@ -358,18 +316,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 }, @@ -408,12 +362,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 { @@ -425,15 +376,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', @@ -443,72 +392,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]; @@ -519,40 +430,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( @@ -561,25 +455,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' }, @@ -594,18 +478,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' }, @@ -614,27 +492,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', @@ -642,18 +511,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(); @@ -698,47 +561,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', () => { @@ -751,35 +588,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), }); @@ -802,49 +622,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'); }); diff --git a/src/layers/flights/rendering.js b/src/layers/flights/rendering.js index e84e05e01..a58587100 100644 --- a/src/layers/flights/rendering.js +++ b/src/layers/flights/rendering.js @@ -58,23 +58,23 @@ export function createRendering({ /** True while this contact's latest poll broadcast an emergency code. */ - function _isEmergency(icao24) { - return isEmergency(flightState.records.data.get(icao24)?.emergency); + function _isEmergency(icao24, info = flightState.records.data.get(icao24)) { + return isEmergency(info?.emergency); } /** Fleet (untracked) billboard tint: red while broadcasting an emergency, * amber for known-military, white otherwise. * Ground traffic gets NO special tint (owner verdict 2026-07-03 field test). */ - function _fleetBillboardColor(icao24) { - if (_isEmergency(icao24)) return EMERGENCY_TINT; + function _fleetBillboardColor(icao24, info) { + if (_isEmergency(icao24, info)) return EMERGENCY_TINT; return isMilitaryIcao(icao24) ? MIL_TINT : Cesium.Color.WHITE; } /** Cockpit far-contact dot tint: the emergency red outranks the category tint. */ - function _cockpitContactColor(icao24) { - if (_isEmergency(icao24)) return EMERGENCY_TINT; + function _cockpitContactColor(icao24, info) { + if (_isEmergency(icao24, info)) return EMERGENCY_TINT; return isMilitaryIcao(icao24) ? MIL_TINT : COCKPIT_CIVILIAN_COLOR; } @@ -1024,8 +1024,8 @@ export function createRendering({ flightState._cockpitNearContacts.has(icao24); const layerBaseColor = flightState._cockpitContactMode && !isCockpitNear - ? _cockpitContactColor(icao24) - : _fleetBillboardColor(icao24); + ? _cockpitContactColor(icao24, info) + : _fleetBillboardColor(icao24, info); const baseColor = layerBaseColor; const treatment = applyAircraftBillboardTreatment({ billboard: bb, diff --git a/src/layers/military/rendering.js b/src/layers/military/rendering.js index 48265a777..2beb81bc5 100644 --- a/src/layers/military/rendering.js +++ b/src/layers/military/rendering.js @@ -197,10 +197,11 @@ export function createRendering({ /** Fleet (untracked) tint: red while the latest poll broadcast an emergency, * amber otherwise. Cockpit far-contact dots use it too. */ - function _fleetBillboardColor(icao24) { - return isEmergency(flightState.records.data.get(icao24)?.emergency) - ? EMERGENCY_ICON_COLOR - : MIL_ICON_COLOR; + function _fleetBillboardColor( + icao24, + info = flightState.records.data.get(icao24), + ) { + return isEmergency(info?.emergency) ? EMERGENCY_ICON_COLOR : MIL_ICON_COLOR; } /** The FLEET's 3D-model regime: models3d enabled AND the camera zoomed in past the altitude @@ -976,7 +977,7 @@ export function createRendering({ const isCockpitNear = flightState._cockpitContactMode && flightState._cockpitNearContacts.has(icao24); - const baseColor = _fleetBillboardColor(icao24); + const baseColor = _fleetBillboardColor(icao24, info); const treatment = applyAircraftBillboardTreatment({ billboard: bb, baseScale: diff --git a/src/layers/military/snapshotRenderer.js b/src/layers/military/snapshotRenderer.js index 58eb57972..b9e54c173 100644 --- a/src/layers/military/snapshotRenderer.js +++ b/src/layers/military/snapshotRenderer.js @@ -7,12 +7,7 @@ import { } from '../../data/motionModel.js'; import { aircraftIcon } from '../../data/aircraftIcons.js'; import { POSITION_HISTORY_LIMIT } from './recordPolicy.js'; -import { - BILLBOARD_SCALE, - GROUND_SCALE, - TRACKED_ICON_COLOR, - MIL_ICON_COLOR, -} from './policy.js'; +import { BILLBOARD_SCALE, GROUND_SCALE, TRACKED_ICON_COLOR } from './policy.js'; /** Apply military record changes to Cesium resources and existing follow operations. */ export function createMilitarySnapshotRenderer({ @@ -161,7 +156,9 @@ export function createMilitarySnapshotRenderer({ // Screen-projected rotation lands on the next fleet tick. rotation: 0, alignedAxis: Cesium.Cartesian3.ZERO, - color: isTracked ? TRACKED_ICON_COLOR : MIL_ICON_COLOR, + color: isTracked + ? TRACKED_ICON_COLOR + : rendering._fleetBillboardColor(icao24, meta), sizeInMeters: false, scaleByDistance: rendering._normalBillboardScaleByDistance(), // Grounded/near-surface planes sit at/below the photoreal tile diff --git a/src/layers/military/tracking.js b/src/layers/military/tracking.js index b0cfb0368..5723ac39e 100644 --- a/src/layers/military/tracking.js +++ b/src/layers/military/tracking.js @@ -21,7 +21,6 @@ import { TRAIL_COLOR, BILLBOARD_SCALE, GROUND_SCALE, - MIL_ICON_COLOR, AMBER_TRANSPARENT, TRACKED_ICON_COLOR, } from './policy.js'; @@ -780,12 +779,16 @@ export function createTracking({ bb.width = 20; bb.height = 20; // Ground-aware restore (a plane untracked while taxiing comes back at - // ground scale; tint is full amber on the ground and in the air). + // ground scale; the fleet tint is amber, or red while broadcasting an + // emergency, on the ground and in the air). bb.scale = BILLBOARD_SCALE * (CLASS_SCALE_2D[meta?.klass] || 1) * (meta?.onGround ? GROUND_SCALE : 1); - bb.color = MIL_ICON_COLOR; + bb.color = parts.rendering._fleetBillboardColor( + flightState._trackedIcao, + meta, + ); bb.rotation = flightState._lastTrackedRotation; } flightState._lastCamPoseSig = ''; // force a fleet rotation pass on the next tick diff --git a/src/ui/styles/cockpit.css b/src/ui/styles/cockpit.css index df4e599e4..bed0c1772 100644 --- a/src/ui/styles/cockpit.css +++ b/src/ui/styles/cockpit.css @@ -43,6 +43,8 @@ #cockpit-hud { --cockpit-accent: #00dcff; + /* Matches EMERGENCY_TINT_CSS in src/data/aircraftEmergency.js. */ + --cockpit-emergency: #ff4444; --cockpit-side-panel-width: min(340px, 28vw); /* HUD bottom offset + tallest scaled telemetry block + 20px clear border. */ --cockpit-bottom-rail-safe: calc(clamp(84px, 11vh, 118px) + 100px); @@ -497,7 +499,8 @@ body.cockpit-mode:has(#left-panel-stack > .panel-collapsible:not(.collapsed)) letter-spacing: 0.16em; } .cockpit-aircraft-meta.emergency { - color: #ff4444; + color: var(--cockpit-emergency); + font-weight: 600; } .cockpit-mode-chip { margin-top: 2px; diff --git a/src/ui/styles/cyber.css b/src/ui/styles/cyber.css index 79ce004e1..c8f5b139f 100644 --- a/src/ui/styles/cyber.css +++ b/src/ui/styles/cyber.css @@ -2273,7 +2273,7 @@ :root[data-ui-theme='cyber'] body.cockpit-mode .cockpit-aircraft-meta.emergency { - color: #ff4444; + color: var(--cyber-red-bright); } :root[data-ui-theme='cyber'] body.cockpit-mode .cockpit-vision-controls {