refactor: separate civil-flight records and rendering

This commit is contained in:
Sameh Khamis committed 2026-09-14 15:55:37 -07:00
1 parent a658e63fed
commit 4cb7334da5
27 files changed
+1113 -819

No files matched your search

+313
View File
@@ -0,0 +1,313 @@
import * as Cesium from 'cesium';
import {
turnRateFromFixHistory,
liftRepeatedGroundFix,
synthesizeForwardKinematicsFix,
} from '../../data/motionModel.js';
import { aircraftIcon } from '../../data/aircraftIcons.js';
import { POSITION_HISTORY_LIMIT } from './recordPolicy.js';
/** Apply reconciled aircraft records to Cesium resources and follow state. */
export function createFlightSnapshotRenderer({
flightState,
records,
militaryRegistry,
groundFloor,
meshFloor,
rendering,
tracking,
motion,
enrichment,
queries,
}) {
const { refreshMilitaryRegistryIfStale, isMilitaryIcao } = militaryRegistry;
const { warmGroundFloor } = groundFloor;
const { sampleMeshFloorCells } = meshFloor;
return function applySnapshot(snapshot, viewer) {
const currentIcaos = new Set();
const acceptedSnapshotIcaos = new Set();
// Field-test round 3 (2026-07-06, Austin fleet-underground): viewer
// subpoint + collected floor cells for the viewer-proximate low-contact
// clamp below — one carto read per poll, one batch warm after the loop.
const viewerCarto =
(viewer || flightState._viewer)?.camera?.positionCartographic || null;
const viewerLatDeg = viewerCarto
? Cesium.Math.toDegrees(viewerCarto.latitude)
: null;
const viewerLonDeg = viewerCarto
? Cesium.Math.toDegrees(viewerCarto.longitude)
: null;
const floorWarmPoints = [];
// Classification and display policy consume source-independent observations.
refreshMilitaryRegistryIfStale();
for (const observation of snapshot.records) {
const icao24 = observation.id;
acceptedSnapshotIcaos.add(icao24);
// Known-military aircraft: the dedicated military layer wins
// (icon + track + click) while it is enabled — suppress the
// OpenSky duplicate entirely (except a currently tracked one,
// which hands off on untrack).
const isMil = isMilitaryIcao(icao24);
if (isMil && tracking._militaryLayerSuppresses(icao24)) {
const dupe = flightState._billboards.get(icao24);
if (dupe) {
flightState._billboardCollection.remove(dupe);
flightState._billboards.delete(icao24);
rendering._releaseModel(icao24); // military-suppression: drop any 3D model too
records.forget(icao24);
flightState._cullPositions.delete(icao24);
flightState._positionHistory.delete(icao24);
flightState._displayCourse.delete(icao24);
flightState._groundSnap.forget(icao24);
}
continue;
}
currentIcaos.add(icao24);
const { prevMeta, meta, groundFlipped, fixEpochMs } = records.receive(
observation,
{
viewerLatDeg,
viewerLonDeg,
trackedId: flightState._trackedIcao,
floorWarmPoints,
},
);
const position = Cesium.Cartesian3.fromDegrees(
meta.rawLon,
meta.rawLat,
meta.renderAltitudeM,
);
// Either flip retires the previous one-shot ground sample.
if (groundFlipped) flightState._groundSnap.forget(icao24);
flightState._cullPositions.set(
icao24,
meta.renderAltitudeM < 10
? Cesium.Cartesian3.fromDegrees(meta.rawLon, meta.rawLat, 12)
: null,
);
const isTracked = icao24 === flightState._trackedIcao;
// Append to position history stamped with the FEED's fix epoch
// (time_position), not client receipt time — OpenSky positions arrive
// 5-15s stale and receipt-time stamping is what caused the
// back/forward oscillation. Only append when the fix actually
// advances, so stale repeats don't create zero-dt segments.
const fixTime = Cesium.JulianDate.fromDate(new Date(fixEpochMs));
if (!flightState._positionHistory.has(icao24)) {
flightState._positionHistory.set(icao24, []);
}
const history = flightState._positionHistory.get(icao24);
const newest = history[history.length - 1];
if (!newest || Cesium.JulianDate.greaterThan(fixTime, newest.time)) {
// Per-fix kinematics: the fix's own velocity/track ride along so the
// extrapolation paths use the values that BELONG to the fix they
// extend, not whatever the latest poll reported.
history.push({
time: fixTime,
epochMs: fixEpochMs,
position: position.clone(),
velocity: meta.velocity,
track: meta.true_track,
});
if (history.length > POSITION_HISTORY_LIMIT) {
history.shift();
}
// Turn rate from the fix-track history — computed once per new fix
// (≤5 samples), consumed by the extrapolation paths at tick rate.
meta.turnRateDps = turnRateFromFixHistory(history);
// Trail accumulation is separate from the 5-fix DR history (PRD F1)
// so the visible trail keeps growing while tracked. Ground traffic
// appends nothing — a touchdown freezes the existing trail.
// Round 2 (owner): ground traffic appends too — taxi history stays
// live after touchdown (grounded flights positions are already
// surface-clamped via the surfaceM chain, so the ground leg drapes).
if (isTracked) tracking._appendTrailFix(position.clone());
} else {
const modelOwnsGroundVisual = rendering._modelOwnsVisual(icao24);
if (!modelOwnsGroundVisual) {
liftRepeatedGroundFix(newest, position, meta.onGround);
}
// Apply fresh kinematics only from a forward synthetic fix. Mutating
// the historical fix reprojects the entire stale interval and snaps
// the rendered aircraft when course or speed changes late.
const kinematicsChanged =
newest.velocity !== meta.velocity || newest.track !== meta.true_track;
if (kinematicsChanged) {
const synthetic = synthesizeForwardKinematicsFix(newest, {
epochMs: Date.now(),
velocity: meta.velocity,
track: meta.true_track,
turnRateDps: meta.turnRateDps,
});
if (synthetic) {
history.push(synthetic);
if (history.length > POSITION_HISTORY_LIMIT) history.shift();
meta.turnRateDps = turnRateFromFixHistory(history);
if (isTracked) tracking._appendTrailFix(synthetic.position.clone());
}
}
}
if (flightState._billboards.has(icao24)) {
const bb = flightState._billboards.get(icao24);
// Reclassify if the category resolved/changed (first extended poll);
// a ground flip (landing/takeoff) re-scales the SAME billboard in
// place — the transition is a restyle, never a removal.
// Round 5: depth policy is uniform (always depth-test-free, see
// _groundDepthDistance) — nothing to flip on landing/takeoff.
// Position AND rotation are owned by the fleet pass (_fleetTick);
// course changes land on the next rotation pass (forced below).
if (!isTracked) {
// Refresh affiliation hue without clobbering the tick-owned
// freshness × focus × horizon alpha composition.
bb.color = rendering
._fleetBillboardColor(icao24)
.withAlpha(bb.color?.alpha ?? 1);
}
if (
prevMeta?.klass !== meta.klass ||
groundFlipped ||
flightState._cockpitContactMode
) {
rendering._applyFleetBillboardPresentation(icao24, bb);
}
// Poll-path class change (category updates): same model resync rule
// as the enrichment path — the class's GLB/scale may have changed.
if (prevMeta?.klass !== meta.klass) rendering._syncModelToClass(icao24);
} else {
const bb = flightState._billboardCollection.add({
position,
image: aircraftIcon(rendering._iconKind(icao24, meta.klass)),
width: isTracked ? 24 : 20,
height: isTracked ? 24 : 20,
scale: rendering._fleetBillboardScale(icao24, meta.klass),
// Screen-projected rotation lands on the next fleet tick.
rotation: 0,
alignedAxis: Cesium.Cartesian3.ZERO,
color: isTracked
? Cesium.Color.CYAN
: rendering._fleetBillboardColor(icao24),
sizeInMeters: false,
scaleByDistance: rendering._normalBillboardScaleByDistance(),
// Grounded/near-surface planes sit at/below the photoreal tile
// surface — render them depth-test-free so they never vanish up
// close (_groundDepthDistance).
disableDepthTestDistance: rendering._groundDepthDistance(),
id: icao24,
show: !isTracked, // hidden if currently tracked (entity replaces it)
});
flightState._billboards.set(icao24, bb);
rendering._applyFleetBillboardPresentation(icao24, bb);
}
// Takeoff while TRACKED: ground traffic drew no trail, so start one
// from the fresh airborne history (touchdown needs no action — the
// append gate above simply freezes the existing trail).
if (isTracked && groundFlipped && !meta.onGround)
tracking._startTrail(icao24);
// If this is the tracked aircraft, update label text
// (position updates automatically via dead-reckoning CallbackProperty)
if (isTracked && flightState._trackedEntity) {
tracking._updateTrackedLabelModel(icao24);
}
}
// Remove aircraft only after MISSING_POLL_LIMIT consecutive absences.
// OpenSky routinely drops aircraft for a single poll; immediate removal
// made planes blink and yanked the camera off actively tracked flights.
// EXCEPTION: likely-landed planes (last fix low + slow) get only
// LANDED_MISSING_POLL_LIMIT — their disappearance means "landed", not a
// feed gap, and the full grace left phantom planes parked at airports.
for (const [icao24, bb] of flightState._billboards) {
if (currentIcaos.has(icao24)) continue;
const absence = records.absence(icao24, {
complete: snapshot.complete,
likelyLanded: queries._likelyLanded(icao24),
});
if (absence === 'retain') continue;
if (absence === 'stale') {
if (icao24 === flightState._trackedIcao && flightState._trackedEntity) {
// Honest readout: the tracked plane has no faded billboard (its
// entity owns the visual), so refresh the label — with icao24 now
// in _missingPolls, _trackedLabelText appends the STALE cue so
// last-known velocity/altitude aren't presented as live.
tracking._updateTrackedLabelModel(icao24);
}
continue;
}
// If the tracked flight is truly gone, clear tracking BEFORE deleting
// its state (M3 ordering, keep it): teardown reads the maps this loop
// is about to delete (billboard restore, DR cache reset), and we must
// never leave the camera mid-follow with stale tracking state. The
// camera is then RELEASED IN PLACE — it stays where the follow left
// it, fully free (owner decision 2026-07-02: no overview flyTo).
if (icao24 === flightState._trackedIcao) {
tracking._clearTracking(false, { evicted: true });
}
flightState._billboardCollection.remove(bb);
flightState._billboards.delete(icao24);
rendering._releaseModel(icao24); // aged-out aircraft: drop its 3D model (no orphan / cap leak)
records.forget(icao24);
flightState._cullPositions.delete(icao24);
flightState._positionHistory.delete(icao24);
flightState._displayCourse.delete(icao24);
flightState._groundSnap.forget(icao24);
flightState._displayFloorState.delete(icao24);
}
// Fresh courses arrived — force a rotation pass on the next fleet tick
flightState._lastCamPoseSig = '';
// Ambient type enrichment: give ON-SCREEN planes real types (bounded
// sweep — see _sweepAmbientEnrichment; internally fail-silent).
enrichment._sweepAmbientEnrichment();
// 2026-08-19: the loop above only ever collects FIX cells, but a grounded
// contact renders across every cell its dead-reckoned position drifts
// through. Add those too, so the display clamp has data where the contact
// actually is instead of silently passing.
motion._collectDisplayCorridorCells(
floorWarmPoints,
viewerLatDeg,
viewerLonDeg,
);
// Field-test round 3: one batch warm of the viewer-proximate low-contact
// floor cells collected in the loop (fire-and-forget, single-flight;
// read synchronously by NEXT poll's clamp — the military-layer pattern).
warmGroundFloor(floorWarmPoints);
// Round 4: sample the RENDERED mesh for those same cells (one-shot per
// cell, budget-capped, viewer-proximate, google-3d regime only). Own
// billboards/models are excluded so a vertical probe can't land on an
// aircraft instead of the pavement.
sampleMeshFloorCells(flightState._viewer?.scene, floorWarmPoints, {
excludeObjects: [
...flightState._billboards.values(),
...flightState._models.values(),
flightState._trackedModel,
].filter(Boolean),
viewerLat: viewerLatDeg,
viewerLon: viewerLonDeg,
});
// Round 5: the grounded exact-key warm that used to live here is gone —
// see the note where _warmGroundedAircraftSurfaceCache was removed. The
// viewer-proximate coarse warm + mesh sampler above cover everything
// whose height is actually visible.
// Round 6: re-floor STALE grounded contacts. A parked plane whose
// transponder went quiet stops receiving poll updates, so a floor that
// warms AFTER its last fix never applied — it sat frozen at the geoid
// (ATL verify: FFT4347 at −30.7 m, 305 m under the apron, forever).
// Grounded contacts are static, so lifting the stored fix + billboard
// in place is safe (the DR extrapolates a zero-velocity fix).
motion._refloorStaleGroundedContacts(currentIcaos);
return { count: flightState._billboards.size, ids: acceptedSnapshotIcaos };
};
}