mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
The first formal release. - One-click install via Pinokio; keyless boot lands on a live Esri World Imagery satellite globe with keyless terrain, with automatic OSM fallback and graceful degradation when terrain is unavailable. - Provider Settings (the POWER UP panel): add, replace, or remove API keys inside the app; credential files made owner-only before any secret is written; external keys shown read-only; the panel refuses shared or proxied servers. - Keyless capability responses for the optional HUD summary and place search. - Aircraft-identity voice answers cover operator, type, and route, and say so plainly when enrichment is unavailable. - README rewritten keyless-first; concise 0.1.0 changelog section; CI workflow included. Gates: 2,672 unit tests pass / 0 fail, build clean, tracking regression 108/108, map-source tray QA pass, setup doctor ready.
97 lines
5.1 KiB
JavaScript
97 lines
5.1 KiB
JavaScript
// src/data/renderAltitude.js — pure priority-chain helper for aircraft render
|
|
// altitude (docs/plans/2026-07-05-entity-height-datum-fix.md Task 6).
|
|
//
|
|
// The globe needs ELLIPSOIDAL height (h = H + N). OpenSky's `geo_altitude`
|
|
// (state-vector index 13) is already WGS84 geometric/ellipsoidal — the
|
|
// correct value to hand `Cartesian3.fromDegrees(lon, lat, h)` verbatim.
|
|
// `baro_altitude` (index 7) is barometric/MSL-referenced (and, unlike a true
|
|
// GPS-derived geo_altitude, also carries non-standard-pressure QNH error) —
|
|
// it is only a VISUAL FALLBACK when geo_altitude is missing, corrected by
|
|
// adding the local geoid undulation N (src/data/geoid.js) so it at least
|
|
// lands close to the ellipsoid instead of sinking into high-elevation
|
|
// terrain. This module makes NO claim that baro+N is geometrically exact.
|
|
//
|
|
// Kept as a small standalone module (rather than inlined in flights.js) so
|
|
// it can be unit-tested in isolation and reused verbatim by the military
|
|
// flights layer (Task 7).
|
|
|
|
/**
|
|
* @param {object} params
|
|
* @param {number|null|undefined} params.geoAltM - OpenSky geo_altitude (m, WGS84 ellipsoidal), if reported.
|
|
* @param {number|null|undefined} params.baroAltM - Barometric/MSL altitude (m), if reported.
|
|
* @param {boolean} params.onGround - OpenSky on_ground flag.
|
|
* @param {number|null|undefined} params.surfaceM - Cached ellipsoidal ground height at this lat/lon (Task 3's cachedEllipsoidalGround), if warm.
|
|
* @param {number|null|undefined} [params.geoidN] - Geoid undulation N (m) at this lat/lon; treated as 0 when absent.
|
|
* @returns {number|null} Ellipsoidal render height in metres, or `null` (sentinel) when
|
|
* none of the above are usable — the caller applies its OWN existing sticky/default
|
|
* fallback (this helper never invents one, so it can't drift from the caller's policy).
|
|
*/
|
|
export function pickRenderAltitudeM({ geoAltM, baroAltM, onGround, surfaceM, geoidN }) {
|
|
if (onGround && Number.isFinite(surfaceM)) {
|
|
return surfaceM;
|
|
}
|
|
if (Number.isFinite(geoAltM)) {
|
|
return geoAltM;
|
|
}
|
|
if (Number.isFinite(baroAltM)) {
|
|
return baroAltM + (Number.isFinite(geoidN) ? geoidN : 0);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Ground-surface reuse for a TAXIING on-ground aircraft.
|
|
*
|
|
* The ellipsoidal-ground cache (Task 3) keys by exact 5-decimal-rounded
|
|
* lat/lon (~1.1 m). The warm-up batch resolves the CURRENT poll's fix for a
|
|
* LATER poll, but an aircraft that moves more than ~1 m between polls (any
|
|
* taxiing traffic — polls are tens of seconds apart) rounds to a fresh key
|
|
* every poll, so `cachedEllipsoidalGround(currentLat, currentLon)` misses
|
|
* indefinitely and a no-baro/no-geo aircraft would stick at the 0 m sentinel
|
|
* while it moves. LAST poll's fix, however, WAS resolved by last poll's warm
|
|
* batch, so its cached ground is available now. Airport aprons/taxiways are
|
|
* ~flat, so the previous fix's ellipsoidal ground is a good stand-in for the
|
|
* current one — far better than sinking to 0 m at an elevated field.
|
|
*
|
|
* @param {number|null|undefined} currentM - Ground at the CURRENT poll fix (may be a cache miss → null).
|
|
* @param {number|null|undefined} previousM - Ground at the PREVIOUS poll fix (warmed last poll), if any.
|
|
* @returns {number|null} The first finite of [current, previous], else null (sentinel).
|
|
*/
|
|
export function reuseGroundedSurfaceM(currentM, previousM) {
|
|
if (Number.isFinite(currentM)) return currentM;
|
|
if (Number.isFinite(previousM)) return previousM;
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Last-resort ground prior for a grounded contact whose floor cells are all
|
|
* cold and which reports NO altitude at all.
|
|
*
|
|
* The geoid is a crude stand-in: at a sea-level field it is the local ground to
|
|
* within metres, at an inland field it is tens of metres BELOW the rendered
|
|
* mesh. It earns its place only for a contact nothing else can describe — a
|
|
* first sighting, before any cell has warmed (the documented "born-grounded
|
|
* first poll" residual).
|
|
*
|
|
* It must NOT outrank a render height the contact already had. That was the
|
|
* 2026-08-21 defect: four Re:Earth refreshes in a row timed out, a parked
|
|
* contact's cells went cold, and this guess overwrote a height that had been
|
|
* sitting correctly on the mesh — dropping it through the ground until the
|
|
* proxy recovered. With `priorRenderM` present the caller's existing sticky
|
|
* fallback holds that height instead, which is what the owner asked for:
|
|
* "hold the last known altitude until a fresh one comes in."
|
|
*
|
|
* @param {object} params
|
|
* @param {number|null|undefined} params.geoAltM - Reported geo_altitude.
|
|
* @param {number|null|undefined} params.baroAltM - Reported baro_altitude.
|
|
* @param {number|null|undefined} params.priorRenderM - Render height this
|
|
* contact already had, if any.
|
|
* @param {number|null|undefined} params.geoidN - Geoid undulation N here.
|
|
* @returns {number|null} The geoid guess, or null when it must not be used.
|
|
*/
|
|
export function geoidSurfaceLastResortM({ geoAltM, baroAltM, priorRenderM, geoidN }) {
|
|
if (Number.isFinite(geoAltM) || Number.isFinite(baroAltM)) return null;
|
|
if (Number.isFinite(priorRenderM)) return null;
|
|
return Number.isFinite(geoidN) ? geoidN : null;
|
|
}
|