diff --git a/src/data/flights.test.mjs b/src/data/flights.test.mjs index 6f2544034..b09dc7014 100644 --- a/src/data/flights.test.mjs +++ b/src/data/flights.test.mjs @@ -18,8 +18,10 @@ 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, @@ -33,7 +35,10 @@ 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'); @@ -55,7 +60,10 @@ 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', @@ -90,7 +98,8 @@ 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`); } }); @@ -98,14 +107,31 @@ 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); @@ -120,17 +146,25 @@ 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(); @@ -155,14 +189,27 @@ 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; } @@ -187,7 +234,10 @@ test('a newly seen aircraft appears at its delayed position, not ahead of it', a _setTrackedFlightRefreshStateForTest({ icao24: 'seed00', entity: null, - billboard: { show: false, position: Cesium.Cartesian3.fromDegrees(0, 0, 0) }, + billboard: { + show: false, + position: Cesium.Cartesian3.fromDegrees(0, 0, 0), + }, billboardCollection, viewer, meta: { rawLat: 0, rawLon: 0, onGround: false }, @@ -209,11 +259,28 @@ test('a newly seen aircraft appears at its delayed position, not ahead of it', a headers: { get: () => null }, json: async () => ({ time: nowSec, - states: [[ - icao24, 'NEW123 ', 'United States', nowSec - 2, nowSec - 2, - fixLon, fixLat, 10_668, false, speedMps, 90, 0, null, 10_700, - null, null, null, 5, - ]], + states: [ + [ + icao24, + 'NEW123 ', + 'United States', + nowSec - 2, + nowSec - 2, + fixLon, + fixLat, + 10_668, + false, + speedMps, + 90, + 0, + null, + 10_700, + null, + null, + null, + 5, + ], + ], }), }; }; @@ -229,7 +296,10 @@ test('a newly seen aircraft appears at its delayed position, not ahead of it', a ); // Eastbound, so the delayed position is west of the fix by the travel // between the delayed time and the fix (about 28 s). - assert.ok(Cesium.Math.toDegrees(shown.longitude) < fixLon, 'drawn behind the fix'); + assert.ok( + Cesium.Math.toDegrees(shown.longitude) < fixLon, + 'drawn behind the fix', + ); assert.ok( behindM > speedMps * 25 && behindM < speedMps * 31, `expected about ${speedMps * 28} m behind the fix, got ${Math.round(behindM)} m`, @@ -277,13 +347,31 @@ 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, @@ -322,14 +410,18 @@ 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 }, @@ -368,9 +460,12 @@ 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 { @@ -382,13 +477,15 @@ 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, @@ -411,11 +508,19 @@ 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'], @@ -425,17 +530,44 @@ 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]; @@ -446,7 +578,11 @@ 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' }, @@ -454,10 +590,17 @@ 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: [], }); @@ -471,10 +614,17 @@ 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: [], }); @@ -494,7 +644,10 @@ 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: [], }); @@ -508,7 +661,10 @@ 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: [], }); @@ -519,7 +675,10 @@ 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', @@ -527,7 +686,10 @@ 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: [], }); @@ -562,7 +724,11 @@ 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, @@ -620,7 +786,11 @@ 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}`, + ); } }); @@ -629,7 +799,11 @@ 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`, + ); } } }); @@ -653,7 +827,11 @@ 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`, + ); } } }); @@ -700,7 +878,11 @@ 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'); } }); @@ -711,7 +893,11 @@ 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: { @@ -732,10 +918,16 @@ 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(' · '), @@ -815,7 +1007,104 @@ 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 + // under it. Creating the billboard without that lift started it on the + // floor of its reported fix, then the next fleet tick raised it. + _clearDisplayFloorStateForTest(); + _clearMeshFloorCellsForTest(); + setMeshFloorPreferred(true); + const added = []; + const billboardCollection = { + show: false, + add(options) { + const billboard = { ...options }; + added.push(billboard); + return billboard; + }, + remove() {}, + }; + const viewer = { camera: { positionCartographic: null }, scene: {} }; + _setTrackedFlightRefreshStateForTest({ + icao24: 'seed00', + entity: null, + billboard: { + show: false, + position: Cesium.Cartesian3.fromDegrees(0, 0, 0), + }, + billboardCollection, + viewer, + meta: { rawLat: 0, rawLon: 0, onGround: false }, + tracked: false, + }); + const icao24 = 'taxi01'; + const fixLon = -97.66; + const fixLat = 30.2; + const speedMps = 10; + // The reported fix sits on a 100 m floor; the apron it taxied from is higher. + reportMeshFloorCell(fixLat, fixLon, 100); + for (let k = 1; k <= 40; k += 1) { + reportMeshFloorCell(fixLat, fixLon - k * 0.0001, 131.5); + } + const nowSec = Math.floor(Date.now() / 1000); + const realFetch = globalThis.fetch; + globalThis.fetch = async (url) => { + if (!String(url).startsWith('/api/flights')) { + return { ok: true, status: 200, json: async () => ({ ac: [] }) }; + } + return { + ok: true, + status: 200, + headers: { get: () => null }, + json: async () => ({ + time: nowSec, + states: [ + [ + icao24, + 'TAXI01 ', + 'United States', + nowSec - 2, + nowSec - 2, + fixLon, + fixLat, + null, + true, + speedMps, + 90, + 0, + null, + null, + null, + null, + null, + 5, + ], + ], + }), + }; + }; + try { + await flightsLayer.update(viewer); + const billboard = added.find((entry) => entry.id === icao24); + assert.ok(billboard, 'the new contact gets a billboard'); + const shown = _floorCarto(billboard.position); + assert.ok( + Cesium.Math.toDegrees(shown.longitude) < fixLon, + 'drawn behind the fix', + ); + assert.ok( + Math.abs(shown.height - (131.5 + GROUND_FLOOR_LIFT_M)) < 0.05, + `height ${shown.height} should be the delayed position's floor, 131.5 + lift`, + ); + } finally { + globalThis.fetch = realFetch; + _clearMeshFloorCellsForTest(); + _clearDisplayFloorStateForTest(); + } +}); test('display floor: a grounded contact that drifted onto a higher cell is lifted', () => { _clearDisplayFloorStateForTest(); @@ -824,10 +1113,16 @@ 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); @@ -839,7 +1134,11 @@ 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'); }); @@ -849,8 +1148,16 @@ 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', () => { @@ -859,7 +1166,11 @@ 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'); }); @@ -868,7 +1179,11 @@ 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)'); }); @@ -879,7 +1194,11 @@ 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); }); @@ -895,14 +1214,23 @@ 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'); }); @@ -912,12 +1240,21 @@ 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 ----------------------------------- @@ -932,10 +1269,28 @@ 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', () => { @@ -944,14 +1299,31 @@ 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 ---------------- @@ -971,18 +1343,28 @@ 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', + { 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, ); - 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', () => { @@ -990,8 +1372,17 @@ 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', () => { @@ -1004,11 +1395,16 @@ 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 @@ -1016,16 +1412,28 @@ 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; } @@ -1044,10 +1452,18 @@ 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', () => { @@ -1055,17 +1471,26 @@ 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 ------------------ @@ -1079,17 +1504,24 @@ 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', () => { @@ -1097,9 +1529,16 @@ 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); @@ -1109,8 +1548,10 @@ 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', () => { @@ -1124,23 +1565,35 @@ 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', () => { @@ -1150,20 +1603,45 @@ 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', ); }); @@ -1175,15 +1653,40 @@ 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', ); }); @@ -1199,10 +1702,28 @@ 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. @@ -1210,11 +1731,19 @@ 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', () => { @@ -1226,14 +1755,34 @@ 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', () => { @@ -1242,24 +1791,56 @@ 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', () => { @@ -1269,25 +1850,62 @@ 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', () => { @@ -1296,21 +1914,47 @@ 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', () => { @@ -1323,18 +1967,44 @@ 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 -------------------------------------- @@ -1357,15 +2027,28 @@ 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', () => { @@ -1382,9 +2065,14 @@ 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', () => { @@ -1404,9 +2092,14 @@ 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', () => { @@ -1414,9 +2107,16 @@ 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); @@ -1435,8 +2135,10 @@ 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'); }); @@ -1447,9 +2149,16 @@ 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; @@ -1460,8 +2169,10 @@ 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', () => { @@ -1469,18 +2180,38 @@ 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, + 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, ); - 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', ); }); @@ -1494,12 +2225,21 @@ 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', () => { @@ -1511,11 +2251,23 @@ 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', ); }); @@ -1526,11 +2278,21 @@ 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', + { 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.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/layers/flights/snapshotRenderer.js b/src/layers/flights/snapshotRenderer.js index 369fb4af2..f6e74b125 100644 --- a/src/layers/flights/snapshotRenderer.js +++ b/src/layers/flights/snapshotRenderer.js @@ -181,10 +181,20 @@ export function createFlightSnapshotRenderer({ } else { // Start where the fleet pass will draw it: the fleet renders behind // real time, so a fresh contact's displayed position is its fix - // projected back to that delayed time. Creating it at the raw fix drew - // it ahead until the next fleet tick snapped it back. + // projected back to that delayed time, and a grounded contact is then + // lifted onto the floor under that delayed position. Creating it at + // the raw fix drew it ahead until the next fleet tick snapped it back; + // skipping the floor started a taxiing contact low and then raised it. + const delayed = motion._deadReckon(icao24, new Cesium.Cartesian3()); const displayed = - motion._deadReckon(icao24, new Cesium.Cartesian3()) || position; + (delayed && + motion._floorGroundedDisplayPosition( + icao24, + flightState.records.data.get(icao24) || meta, + delayed, + rendering._modelOwnsVisual(icao24), + )) || + position; const bb = flightState._billboardCollection.add({ position: displayed, image: aircraftIcon(rendering._iconKind(icao24, meta.klass)),