mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
Merge branch 'main' into feat/voice-geometry
# Conflicts: # SECURITY.md # scripts/format-scope.json # src/app/constructCatalog.test.mjs # src/app/sourceComposition.test.mjs # src/firstRunExperience.test.mjs # src/i18n/locales/ar.json # src/i18n/locales/de.json # src/i18n/locales/es.json # src/i18n/locales/fr.json # src/i18n/locales/it.json # src/i18n/locales/ja.json # src/i18n/locales/pt.json # src/i18n/locales/ru.json # src/i18n/locales/uk.json # src/i18n/locales/zh-CN.json # src/i18n/locales/zh-Hant.json # src/voice/actionSchemas.test.mjs
This commit is contained in:
commit
51637a5e96
56 files changed
+1834
-208
No files matched your search
@@ -28,6 +28,29 @@ function services() {
|
||||
};
|
||||
}
|
||||
|
||||
// The application's own scene services, as src/app/layers/militaryFlights.js
|
||||
// composes them; only the surface samplers and registry are stubbed.
|
||||
async function sceneServices() {
|
||||
return {
|
||||
picking: await import('../../data/pickRegistry.js'),
|
||||
sprites: await import('../../data/spriteOrder.js'),
|
||||
trails: await import('../../data/trailRenderer.js'),
|
||||
aircraftPresentation: await import('../../data/tr3bRegistry.js'),
|
||||
camera: await import('../../data/trackedCamera.js'),
|
||||
labels: await import('../../data/detectionDraw.js'),
|
||||
geoid: await import('../../data/geoid.js'),
|
||||
focus: await import('../../data/focusDeemphasis.js'),
|
||||
readout: await import('../../data/trackedReadout.js'),
|
||||
context: await import('../../data/contextStore.js'),
|
||||
render: await import('../../renderGovernor.js'),
|
||||
recession: await import('../../data/aircraftRecession.js'),
|
||||
groundFloor: await import('../../data/groundFloor.js'),
|
||||
groundSnap: await import('../../data/groundSnap.js'),
|
||||
meshFloor: { sampleMeshFloorCells: () => {} },
|
||||
militaryRegistry: { registerMilitaryIcaos: () => {} },
|
||||
};
|
||||
}
|
||||
|
||||
test('military layer instances isolate policy and restoration state without requesting a source', async () => {
|
||||
let requests = 0;
|
||||
const source = {
|
||||
@@ -122,26 +145,7 @@ test('a newly seen military contact appears at its delayed position, not ahead o
|
||||
// the next fleet tick jumped it back.
|
||||
const Cesium = await import('cesium');
|
||||
const { RENDER_DELAY_SEC } = await import('./policy.js');
|
||||
// The application's own scene services, as src/app/layers/militaryFlights.js
|
||||
// composes them; only the surface samplers and registry are stubbed.
|
||||
const supplied = {
|
||||
picking: await import('../../data/pickRegistry.js'),
|
||||
sprites: await import('../../data/spriteOrder.js'),
|
||||
trails: await import('../../data/trailRenderer.js'),
|
||||
aircraftPresentation: await import('../../data/tr3bRegistry.js'),
|
||||
camera: await import('../../data/trackedCamera.js'),
|
||||
labels: await import('../../data/detectionDraw.js'),
|
||||
geoid: await import('../../data/geoid.js'),
|
||||
focus: await import('../../data/focusDeemphasis.js'),
|
||||
readout: await import('../../data/trackedReadout.js'),
|
||||
context: await import('../../data/contextStore.js'),
|
||||
render: await import('../../renderGovernor.js'),
|
||||
recession: await import('../../data/aircraftRecession.js'),
|
||||
groundFloor: await import('../../data/groundFloor.js'),
|
||||
groundSnap: await import('../../data/groundSnap.js'),
|
||||
meshFloor: { sampleMeshFloorCells: () => {} },
|
||||
militaryRegistry: { registerMilitaryIcaos: () => {} },
|
||||
};
|
||||
const supplied = await sceneServices();
|
||||
const fixLon = -97.6;
|
||||
const fixLat = 30.3;
|
||||
const speedMps = 200;
|
||||
@@ -213,6 +217,55 @@ test('a newly seen military contact appears at its delayed position, not ahead o
|
||||
);
|
||||
});
|
||||
|
||||
test('a vanished military contact fades while position-less rows keep theirs', async () => {
|
||||
// About a third of a live adsb.lol /v2/mil fetch is contacts without a
|
||||
// position. Those rows marked every snapshot partial, so an aircraft that
|
||||
// left the feed stayed at full opacity for five minutes.
|
||||
const Cesium = await import('cesium');
|
||||
const { readsbSnapshot } = await import('../../sources/live/aircraft.js');
|
||||
const nowMs = Date.now();
|
||||
const row = (hex, lon) => ({
|
||||
hex,
|
||||
lat: 30.3,
|
||||
lon,
|
||||
alt_baro: 20_000,
|
||||
gs: 300,
|
||||
track: 90,
|
||||
seen_pos: 1,
|
||||
});
|
||||
const polls = [
|
||||
[row('ae0001', -97.6), row('ae0002', -96.6), row('ae0003', -95.6)],
|
||||
[row('ae0001', -97.5), { hex: 'ae0003' }],
|
||||
];
|
||||
// Every poll also lists a contact that never has a position.
|
||||
for (const poll of polls) poll.push({ hex: 'ae0009', type: 'mode_s' });
|
||||
const layer = createMilitaryFlightLayer({
|
||||
services: await sceneServices(),
|
||||
source: {
|
||||
label: 'Fixture aircraft',
|
||||
getSnapshot: async () =>
|
||||
readsbSnapshot(
|
||||
{ ac: polls.shift() },
|
||||
{ observedAtMs: nowMs, now: nowMs },
|
||||
),
|
||||
},
|
||||
});
|
||||
const viewer = { camera: { positionCartographic: null }, scene: {} };
|
||||
layer.testing._setTrackedMilitaryRefreshStateForTest({
|
||||
icao24: 'seed00',
|
||||
entity: null,
|
||||
meta: { rawLat: 0, rawLon: 0, onGround: false },
|
||||
billboard: { show: false, position: Cesium.Cartesian3.fromDegrees(0, 0) },
|
||||
billboardCollection: { show: true, add: (options) => ({ ...options }) },
|
||||
viewer,
|
||||
tracked: false,
|
||||
});
|
||||
await layer.update(viewer);
|
||||
await layer.update(viewer);
|
||||
assert.equal(layer.findByQuery('ae0002')?.stale, true, 'vanished: fading');
|
||||
assert.equal(layer.findByQuery('ae0003')?.stale, false, 'still listed');
|
||||
});
|
||||
|
||||
test('voice context reads a track that rounds to north as 0°, not 360°', () => {
|
||||
const stubs = services();
|
||||
stubs.aircraftPresentation = { tr3bTypeLabel: (id, type) => type };
|
||||
|
||||
@@ -187,9 +187,12 @@ export class MilitaryFlightRecords {
|
||||
fixEpochMs: aircraft.positionTimeMs ?? observedAtMs,
|
||||
};
|
||||
}
|
||||
absence(id, { complete, likelyLanded }) {
|
||||
absence(id, { complete, retainIds, likelyLanded }) {
|
||||
// Partial admissions and position-less rows do not prove absence, but
|
||||
// retention remains bounded; a likely-landed position-less contact
|
||||
// still takes the landed limit.
|
||||
if (
|
||||
!complete &&
|
||||
(!complete || (retainIds?.has(id) && !likelyLanded)) &&
|
||||
Date.now() - (this.data.get(id)?.observedReceiptMs ?? 0) < 300000
|
||||
)
|
||||
return 'retain';
|
||||
|
||||
@@ -99,6 +99,30 @@ test('military records keep bounded partial retention and release metadata only
|
||||
);
|
||||
});
|
||||
|
||||
test('complete military snapshots age out vanished aircraft and retain position-less ones unless landed', () => {
|
||||
const records = store();
|
||||
for (const id of ['aaa001', 'aaa002', 'aaa003'])
|
||||
records.receive(observation({ id, reference: id }), context());
|
||||
// aaa001 and aaa003 are still listed without a position; aaa002 vanished.
|
||||
const absent = (id, likelyLanded = false) =>
|
||||
records.absence(id, {
|
||||
complete: true,
|
||||
retainIds: new Set(['aaa001', 'aaa003']),
|
||||
likelyLanded,
|
||||
});
|
||||
assert.deepEqual(
|
||||
[absent('aaa002'), absent('aaa002'), absent('aaa002')],
|
||||
['stale', 'stale', 'remove'],
|
||||
);
|
||||
assert.equal(absent('aaa001'), 'retain');
|
||||
assert.equal(records.missingPolls.has('aaa001'), false);
|
||||
// A landed contact that lost its position takes the landed fast-cull.
|
||||
assert.equal(absent('aaa003', true), 'remove');
|
||||
// Position-less retention stays bounded like partial admission.
|
||||
records.data.get('aaa001').observedReceiptMs = Date.now() - 300001;
|
||||
assert.equal(absent('aaa001'), 'stale');
|
||||
});
|
||||
|
||||
test('military emergency status follows the latest poll instead of sticking', () => {
|
||||
const records = store();
|
||||
const first = records.receive(
|
||||
|
||||
@@ -259,6 +259,7 @@ export function createMilitarySnapshotRenderer({
|
||||
if (currentIcaos.has(icao24)) continue;
|
||||
const absence = records.absence(icao24, {
|
||||
complete: snapshot.complete,
|
||||
retainIds: snapshot.retainIds,
|
||||
likelyLanded: queries._likelyLanded(icao24),
|
||||
});
|
||||
if (absence === 'retain') continue;
|
||||
|
||||
Reference in new issue
Block a user