mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
feat(layers): highlight aircraft broadcasting a transponder emergency
Flights and Military Flights tint aircraft red while the latest poll carries squawk 7500/7600/7700 or an adsb.lol ADS-B emergency status (general/unlawful/nordo/downed). Minimum-fuel and lifeguard priorities are reported but not tinted. The Cockpit HUD aircraft-meta line names the broadcast code in red. The status is not sticky: a cleared code stops highlighting on the next refresh. The analyst/voice query engine gains squawk, emergency and emergencyKind fields. No new source or request: OpenSky state vectors already carry the squawk and adsb.lol/readsb carries both fields. Built by the Hex project (Crew Leo Agile dev team). Co-Authored-By: Claude <[email protected]>
This commit is contained in:
1 parent
9beadbda63
commit
eb7f059750
26 files changed
+1174
-107
No files matched your search
@@ -7,6 +7,7 @@ export {
|
||||
ERROR_BACKOFF_INTERVAL,
|
||||
} from './recordPolicy.js';
|
||||
import * as Cesium from 'cesium';
|
||||
import { EMERGENCY_TINT_CSS } from '../../data/aircraftEmergency.js';
|
||||
|
||||
export const FOCUS_EVIDENCE_DEV = import.meta.env?.DEV === true;
|
||||
|
||||
@@ -14,6 +15,11 @@ export const FOCUS_EVIDENCE_DEV = import.meta.env?.DEV === true;
|
||||
|
||||
export const MIL_TINT = Cesium.Color.fromCssColorString('#FFB800');
|
||||
|
||||
/** Red tint for aircraft broadcasting an emergency (7500/7600/7700 or ADS-B status). */
|
||||
|
||||
export const EMERGENCY_TINT =
|
||||
Cesium.Color.fromCssColorString(EMERGENCY_TINT_CSS);
|
||||
|
||||
// --- Ground traffic (owner reversal 2026-07-03: "absolutely we should see planes
|
||||
// taxiing and landing") -----------------------------------------------------------
|
||||
// Present-but-grounded planes are RENDERED instead of being skipped: same class
|
||||
|
||||
@@ -167,6 +167,9 @@ export function createQueries({
|
||||
flightState.records.missingPolls.get(icao24) ||
|
||||
flightState.feed._backoff,
|
||||
),
|
||||
// This poll's transponder broadcast (not sticky; see records.js).
|
||||
squawk: info?.squawk ?? null,
|
||||
emergency: info?.emergency ? { ...info.emergency } : null,
|
||||
airline: info?.airline ?? null,
|
||||
// CLASS label follows the TR-3B conversion so every downstream card
|
||||
// (cockpit, Contacts, analyst) agrees with the triangle on screen.
|
||||
@@ -219,7 +222,9 @@ export function createQueries({
|
||||
* lon: number|null, altitudeM: number|null, speedMps: number|null,
|
||||
* heading: number|null, verticalRateMps: number|null, onGround: boolean,
|
||||
* military: boolean, aircraftClass: string|null, originCountry: string|null,
|
||||
* operator: string|null, routeOrigin: string|null, routeDestination: string|null}}
|
||||
* operator: string|null, squawk: string|null, emergency: boolean,
|
||||
* emergencyKind: string|null, routeOrigin: string|null,
|
||||
* routeDestination: string|null}}
|
||||
*/
|
||||
|
||||
function mapAnalystRecord(
|
||||
@@ -253,6 +258,12 @@ export function createQueries({
|
||||
aircraftClass: tr3bAircraftClass(icao24, text(info?.klass)),
|
||||
originCountry: text(info?.originCountry),
|
||||
operator: text(info?.airline),
|
||||
squawk: text(info?.squawk),
|
||||
// True while this poll broadcast an emergency (7500/7600/7700 or the
|
||||
// ADS-B status); priority statuses (lifeguard, minfuel) report the kind
|
||||
// without the flag.
|
||||
emergency: info?.emergency?.severity === 'emergency',
|
||||
emergencyKind: text(info?.emergency?.kind),
|
||||
routeOrigin: routeOk ? text(info?.route?.origin?.code) : null,
|
||||
routeDestination: routeOk ? text(info?.route?.destination?.code) : null,
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
pickRenderAltitudeM,
|
||||
} from '../../data/renderAltitude.js';
|
||||
import { classifyAircraft } from '../../data/aircraftClass.js';
|
||||
import { aircraftEmergency } from '../../data/aircraftEmergency.js';
|
||||
import {
|
||||
approxDistanceKm,
|
||||
GROUND_FLOOR_CLAMP_RADIUS_KM,
|
||||
@@ -226,6 +227,10 @@ export class FlightRecords {
|
||||
// like callsign so a transient blank row doesn't blank the field.
|
||||
originCountry:
|
||||
stickyText(origin_country, prevMeta?.originCountry) || null,
|
||||
// Per poll, NOT sticky: an emergency code clears when the crew resets
|
||||
// it, so a blank squawk must not hold a stale 7700 on screen.
|
||||
squawk: observation.squawk ?? null,
|
||||
emergency: aircraftEmergency(observation),
|
||||
// OpenSky distinguishes the last position epoch from the last
|
||||
// transponder message. The fleet coast horizon uses this actual
|
||||
// contact time so a temporarily old position does not hard-freeze
|
||||
|
||||
@@ -106,3 +106,13 @@ test('ground transitions preserve last render height while a surface cell is col
|
||||
assert.equal(next.meta.renderAltitudeM, 1040);
|
||||
assert.equal(next.meta.wasAirborne, true);
|
||||
});
|
||||
|
||||
test('civil emergency status follows the latest poll instead of sticking', () => {
|
||||
const store = records();
|
||||
const first = store.receive(observation({ squawk: '7700' }), view());
|
||||
assert.equal(first.meta.squawk, '7700');
|
||||
assert.equal(first.meta.emergency.kind, 'general');
|
||||
const next = store.receive(observation({ squawk: '2000' }), view());
|
||||
assert.equal(next.meta.squawk, '2000');
|
||||
assert.equal(next.meta.emergency, null);
|
||||
});
|
||||
@@ -3,6 +3,7 @@ import { cyberSonarBaseAlpha } from '../../cyberSonar.js';
|
||||
import { selectModelEligible } from '../../data/modelEligibility.js';
|
||||
import { civilAircraftModelSpec } from './modelSpec.js';
|
||||
import { CLASS_SCALE_2D } from '../../data/aircraftClass.js';
|
||||
import { isEmergency } from '../../data/aircraftEmergency.js';
|
||||
import { cockpitContactDotImage } from '../../data/cockpitContactDot.js';
|
||||
import { aircraftIcon, TRACKED_ICON_PX } from '../../data/aircraftIcons.js';
|
||||
import {
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
import { limitCourseStep, courseSlewCapDps } from '../../data/motionModel.js';
|
||||
import {
|
||||
MIL_TINT,
|
||||
EMERGENCY_TINT,
|
||||
GROUND_SCALE,
|
||||
COCKPIT_CONTACT_SIZE_PX,
|
||||
COCKPIT_CIVILIAN_COLOR,
|
||||
@@ -54,13 +56,28 @@ export function createRendering({
|
||||
const { applyAircraftBillboardTreatment, applyAircraftModelTreatment } =
|
||||
services.recession;
|
||||
|
||||
/** Fleet (untracked) billboard tint: amber for known-military, white otherwise.
|
||||
/** True while this contact's latest poll broadcast an emergency code. */
|
||||
|
||||
function _isEmergency(icao24) {
|
||||
return isEmergency(flightState.records.data.get(icao24)?.emergency);
|
||||
}
|
||||
|
||||
/** Fleet (untracked) billboard tint: red while broadcasting an emergency,
|
||||
* amber for known-military, white otherwise.
|
||||
* Ground traffic gets NO special tint (owner verdict 2026-07-03 field test). */
|
||||
|
||||
function _fleetBillboardColor(icao24) {
|
||||
if (_isEmergency(icao24)) return EMERGENCY_TINT;
|
||||
return isMilitaryIcao(icao24) ? MIL_TINT : Cesium.Color.WHITE;
|
||||
}
|
||||
|
||||
/** Cockpit far-contact dot tint: the emergency red outranks the category tint. */
|
||||
|
||||
function _cockpitContactColor(icao24) {
|
||||
if (_isEmergency(icao24)) return EMERGENCY_TINT;
|
||||
return isMilitaryIcao(icao24) ? MIL_TINT : COCKPIT_CIVILIAN_COLOR;
|
||||
}
|
||||
|
||||
/** Fleet billboard scale: per-class scale, ×GROUND_SCALE while grounded. */
|
||||
|
||||
function _fleetBillboardScale(icao24, klass) {
|
||||
@@ -129,9 +146,7 @@ export function createRendering({
|
||||
bb.height = COCKPIT_CONTACT_SIZE_PX;
|
||||
bb.scale = limbScale;
|
||||
bb.scaleByDistance = _cockpitBillboardScaleByDistance();
|
||||
bb.color = (
|
||||
isMilitaryIcao(icao24) ? MIL_TINT : COCKPIT_CIVILIAN_COLOR
|
||||
).withAlpha(freshnessAlpha);
|
||||
bb.color = _cockpitContactColor(icao24).withAlpha(freshnessAlpha);
|
||||
bb.rotation = 0;
|
||||
return;
|
||||
}
|
||||
@@ -162,7 +177,7 @@ export function createRendering({
|
||||
|
||||
function _modelColor(icao24) {
|
||||
if (icao24 === flightState._trackedIcao) return Cesium.Color.CYAN;
|
||||
return isMilitaryIcao(icao24) ? MIL_TINT : Cesium.Color.WHITE;
|
||||
return _fleetBillboardColor(icao24);
|
||||
}
|
||||
|
||||
/** The FLEET's 3D-model regime: models3d enabled AND the camera zoomed in past the altitude
|
||||
@@ -1009,9 +1024,7 @@ export function createRendering({
|
||||
flightState._cockpitNearContacts.has(icao24);
|
||||
const layerBaseColor =
|
||||
flightState._cockpitContactMode && !isCockpitNear
|
||||
? isMilitaryIcao(icao24)
|
||||
? MIL_TINT
|
||||
: COCKPIT_CIVILIAN_COLOR
|
||||
? _cockpitContactColor(icao24)
|
||||
: _fleetBillboardColor(icao24);
|
||||
const baseColor = layerBaseColor;
|
||||
const treatment = applyAircraftBillboardTreatment({
|
||||
|
||||
@@ -6,6 +6,7 @@ export {
|
||||
ERROR_BACKOFF_INTERVAL,
|
||||
} from './recordPolicy.js';
|
||||
import * as Cesium from 'cesium';
|
||||
import { EMERGENCY_TINT_CSS } from '../../data/aircraftEmergency.js';
|
||||
|
||||
/**
|
||||
* @module militaryFlights
|
||||
@@ -40,6 +41,11 @@ export const MIL_ICON_COLOR = Cesium.Color.fromCssColorString('#FFB800');
|
||||
|
||||
export const TRACKED_ICON_COLOR = Cesium.Color.fromCssColorString('#FFD166');
|
||||
|
||||
/** @constant {Cesium.Color} Red tint for aircraft broadcasting an emergency (mirror of flights) */
|
||||
|
||||
export const EMERGENCY_ICON_COLOR =
|
||||
Cesium.Color.fromCssColorString(EMERGENCY_TINT_CSS);
|
||||
|
||||
// --- Ground traffic (owner reversal 2026-07-03; mirror of flights.js) ---------------
|
||||
// adsb.lol/readsb flags ground traffic with alt_baro === "ground" (no separate
|
||||
// boolean). Such aircraft are RENDERED instead of floating at the 3 km altitude
|
||||
|
||||
@@ -133,6 +133,9 @@ export function createQueries({
|
||||
flightState.records.missingPolls.get(icao24) ||
|
||||
flightState.feed._backoff,
|
||||
),
|
||||
// This poll's transponder broadcast (not sticky; see records.js).
|
||||
squawk: info?.squawk ?? null,
|
||||
emergency: info?.emergency ? { ...info.emergency } : null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -156,7 +159,8 @@ export function createQueries({
|
||||
* lon: number|null, altitudeM: number|null, speedMps: number|null,
|
||||
* heading: number|null, verticalRateMps: number|null, onGround: boolean,
|
||||
* military: boolean, aircraftClass: string|null, originCountry: null,
|
||||
* operator: string|null, routeOrigin: null, routeDestination: null}}
|
||||
* operator: string|null, squawk: string|null, emergency: boolean,
|
||||
* emergencyKind: string|null, routeOrigin: null, routeDestination: null}}
|
||||
*/
|
||||
|
||||
function mapAnalystRecord(icao24, info) {
|
||||
@@ -187,6 +191,12 @@ export function createQueries({
|
||||
aircraftClass: tr3bAircraftClass(icao24, text(info?.klass)),
|
||||
originCountry: null,
|
||||
operator: text(info?.operator),
|
||||
squawk: text(info?.squawk),
|
||||
// True while this poll broadcast an emergency (7500/7600/7700 or the
|
||||
// ADS-B status); priority statuses (lifeguard, minfuel) report the kind
|
||||
// without the flag.
|
||||
emergency: info?.emergency?.severity === 'emergency',
|
||||
emergencyKind: text(info?.emergency?.kind),
|
||||
routeOrigin: null,
|
||||
routeDestination: null,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { pickRenderAltitudeM } from '../../data/renderAltitude.js';
|
||||
import { stickyText, stickyNumber } from '../../data/aircraftMeta.js';
|
||||
import { classifyAircraft } from '../../data/aircraftClass.js';
|
||||
import { aircraftEmergency } from '../../data/aircraftEmergency.js';
|
||||
import {
|
||||
GROUND_FLOOR_WARM_MAX_ALT_M,
|
||||
LANDED_MISSING_POLL_LIMIT,
|
||||
@@ -161,6 +162,9 @@ export class MilitaryFlightRecords {
|
||||
null,
|
||||
),
|
||||
turnRateDps: prevMeta?.turnRateDps || 0,
|
||||
// Per poll, NOT sticky (mirror of flights): a cleared code clears here.
|
||||
squawk: aircraft.squawk ?? null,
|
||||
emergency: aircraftEmergency(aircraft),
|
||||
onGround,
|
||||
// Round 7: sticky airborne history (see _likelyLanded).
|
||||
wasAirborne: prevMeta?.wasAirborne === true || !onGround,
|
||||
|
||||
@@ -98,3 +98,19 @@ test('military records keep bounded partial retention and release metadata only
|
||||
'remove',
|
||||
);
|
||||
});
|
||||
|
||||
test('military emergency status follows the latest poll instead of sticking', () => {
|
||||
const records = store();
|
||||
const first = records.receive(
|
||||
observation({ squawk: '1234', emergency: 'minfuel' }),
|
||||
context(),
|
||||
);
|
||||
assert.equal(first.meta.squawk, '1234');
|
||||
assert.equal(first.meta.emergency.kind, 'minfuel');
|
||||
assert.equal(first.meta.emergency.severity, 'priority');
|
||||
const next = records.receive(
|
||||
observation({ squawk: '1234', emergency: 'none' }),
|
||||
context(),
|
||||
);
|
||||
assert.equal(next.meta.emergency, null);
|
||||
});
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
screenProjectedRotation,
|
||||
} from '../../data/iconOrientation.js';
|
||||
import { limitCourseStep, courseSlewCapDps } from '../../data/motionModel.js';
|
||||
import { isEmergency } from '../../data/aircraftEmergency.js';
|
||||
import {
|
||||
PLANE_MODEL_SCALE,
|
||||
PLANE_MODEL_URL,
|
||||
@@ -33,6 +34,7 @@ import {
|
||||
COCKPIT_CONTACT_SIZE_PX,
|
||||
MIL_ICON_COLOR,
|
||||
TRACKED_ICON_COLOR,
|
||||
EMERGENCY_ICON_COLOR,
|
||||
MODEL_ALT_CEIL_M,
|
||||
MODEL_MAX_ALL,
|
||||
MODEL_MAX,
|
||||
@@ -159,7 +161,7 @@ export function createRendering({
|
||||
bb.height = COCKPIT_CONTACT_SIZE_PX;
|
||||
bb.scale = limbScale;
|
||||
bb.scaleByDistance = _cockpitBillboardScaleByDistance();
|
||||
bb.color = MIL_ICON_COLOR.withAlpha(freshnessAlpha);
|
||||
bb.color = _fleetBillboardColor(icao24).withAlpha(freshnessAlpha);
|
||||
bb.rotation = 0;
|
||||
return;
|
||||
}
|
||||
@@ -173,7 +175,7 @@ export function createRendering({
|
||||
bb.height = icao24 === flightState._trackedIcao ? 24 : 20;
|
||||
bb.scale = _militaryBillboardScale(icao24) * limbScale;
|
||||
bb.scaleByDistance = _normalBillboardScaleByDistance();
|
||||
bb.color = MIL_ICON_COLOR.withAlpha(cyberSonarBaseAlpha(bb));
|
||||
bb.color = _fleetBillboardColor(icao24).withAlpha(cyberSonarBaseAlpha(bb));
|
||||
}
|
||||
|
||||
/** Sprite kind for one contact's billboard. Identity for every aircraft except
|
||||
@@ -189,7 +191,16 @@ export function createRendering({
|
||||
|
||||
function _modelColor(icao24) {
|
||||
if (icao24 === flightState._trackedIcao) return TRACKED_ICON_COLOR;
|
||||
return MIL_ICON_COLOR;
|
||||
return _fleetBillboardColor(icao24);
|
||||
}
|
||||
|
||||
/** Fleet (untracked) tint: red while the latest poll broadcast an emergency,
|
||||
* amber otherwise. Cockpit far-contact dots use it too. */
|
||||
|
||||
function _fleetBillboardColor(icao24) {
|
||||
return isEmergency(flightState.records.data.get(icao24)?.emergency)
|
||||
? EMERGENCY_ICON_COLOR
|
||||
: MIL_ICON_COLOR;
|
||||
}
|
||||
|
||||
/** The FLEET's 3D-model regime: models3d enabled AND the camera zoomed in past the altitude
|
||||
@@ -965,7 +976,7 @@ export function createRendering({
|
||||
const isCockpitNear =
|
||||
flightState._cockpitContactMode &&
|
||||
flightState._cockpitNearContacts.has(icao24);
|
||||
const baseColor = MIL_ICON_COLOR;
|
||||
const baseColor = _fleetBillboardColor(icao24);
|
||||
const treatment = applyAircraftBillboardTreatment({
|
||||
billboard: bb,
|
||||
baseScale:
|
||||
@@ -1082,6 +1093,7 @@ export function createRendering({
|
||||
_applyFleetBillboardPresentation,
|
||||
_iconKind,
|
||||
_modelColor,
|
||||
_fleetBillboardColor,
|
||||
_modelRegimeActive,
|
||||
_modelCap,
|
||||
_modelAddDistM,
|
||||
|
||||
Reference in new issue
Block a user