mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
fix(gnss): separate GEV integrity classifier from GPSJAM aggregation; machine-readable provenance
Review on #855: the per-aircraft rule (NIC < 7 || NACp < 8, borrowed from the US 14 CFR 91.227 ADS-B Out minima) is GEV-defined, not gpsjam's, and gpsjam's map shows low navigation accuracy, not necessarily jamming. - Primary cell state is now `navigation-integrity-anomaly` (medium/high) or `nominal`; `suspected-gnss-interference` is a secondary `interpretation` marked validated: false. - GNSS_PROVENANCE / gnssProvenance({ windowMs }) records the classifier (gev-nic-nacp-v1, GEV, unvalidated), the gpsjam aggregation (formula, bands [0.02, 0.1], FAQ source) and the GEV 30-minute visited-view window, built from the constants the code applies. Exposed as layer.provenance and getStats().provenance; the proxy tags responses with `classifier`. - Visible name "GNSS Integrity", legend "... low accuracy", blurb leads with low navigation accuracy and states the threshold is a GEV choice. - Layer id `gnss-interference` and share token `0` unchanged. - Voice aliases add "gnss integrity", "navigation integrity", "low navigation accuracy"; the interference aliases stay. - Docs describe the method as GEV threshold + gpsjam aggregation. - Tests: provenance object, renamed state/label, proxy classifier tag; qa-gnss asserts provenance and wording in the real app. Hex project - Crew Leo Agile dev team. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
40b06a24d4
commit
6d4a2a02fe
16 files changed
+325
-67
No files matched your search
+19
-11
@@ -2,9 +2,12 @@ import * as Cesium from 'cesium';
|
||||
import {
|
||||
GNSS_LEVEL_COLORS,
|
||||
GNSS_MIN_AIRCRAFT,
|
||||
GNSS_NACP_THRESHOLD,
|
||||
GNSS_NIC_THRESHOLD,
|
||||
GNSS_WINDOW_MS,
|
||||
accumulateGnssObservations,
|
||||
binGnssCells,
|
||||
gnssProvenance,
|
||||
} from './records.js';
|
||||
export * from './records.js';
|
||||
export { createAdsbGnssSource } from './source.js';
|
||||
@@ -12,9 +15,9 @@ export { createAdsbGnssSource } from './source.js';
|
||||
/** Low stays visible over land so coverage reads apart from no data. */
|
||||
const FILL_ALPHA = Object.freeze({ low: 0.3, medium: 0.42, high: 0.55 });
|
||||
const LEVEL_LABELS = Object.freeze({
|
||||
low: 'Under 2% degraded',
|
||||
medium: '2–10% degraded',
|
||||
high: 'Over 10% degraded',
|
||||
low: 'Under 2% low accuracy',
|
||||
medium: '2–10% low accuracy',
|
||||
high: 'Over 10% low accuracy',
|
||||
});
|
||||
|
||||
/** Degrees at the centre of the current view, or null before the camera settles. */
|
||||
@@ -34,18 +37,19 @@ export function defaultGnssViewAnchor(viewer) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Own one GNSS-interference display: rolling integrity observations around
|
||||
* the views the user visits, binned into cells coloured by the share of
|
||||
* aircraft reporting degraded navigation integrity.
|
||||
* Own one GNSS navigation-integrity display: rolling integrity observations
|
||||
* around the views the user visits, binned into cells coloured by the share
|
||||
* of aircraft reporting low navigation accuracy. The layer id keeps its
|
||||
* original `gnss-interference` spelling because share links depend on it.
|
||||
*/
|
||||
export function createGnssInterferenceLayer({
|
||||
export function createGnssIntegrityLayer({
|
||||
source,
|
||||
viewAnchor = defaultGnssViewAnchor,
|
||||
now = () => Date.now(),
|
||||
windowMs = GNSS_WINDOW_MS,
|
||||
} = {}) {
|
||||
if (typeof source?.getSnapshot !== 'function')
|
||||
throw new TypeError('GNSS interference requires a snapshot source');
|
||||
throw new TypeError('GNSS integrity layer requires a snapshot source');
|
||||
let _viewer = null;
|
||||
let _dataSource = null;
|
||||
let _request = null;
|
||||
@@ -56,6 +60,7 @@ export function createGnssInterferenceLayer({
|
||||
let _lastError = null;
|
||||
let _stale = false;
|
||||
const _observations = new Map();
|
||||
const provenance = gnssProvenance({ windowMs });
|
||||
|
||||
function render() {
|
||||
// Only the id and band change what is drawn; counts alone do not.
|
||||
@@ -91,14 +96,16 @@ export function createGnssInterferenceLayer({
|
||||
|
||||
const layer = {
|
||||
id: 'gnss-interference',
|
||||
name: 'GNSS Interference',
|
||||
name: 'GNSS Integrity',
|
||||
icon: '📡',
|
||||
source: 'adsb.lol',
|
||||
/** Where each part of the method comes from; see records.js. */
|
||||
provenance,
|
||||
updateInterval: 60000,
|
||||
|
||||
init(viewer) {
|
||||
if (_viewer)
|
||||
throw new Error('GNSS interference layer is already initialized');
|
||||
throw new Error('GNSS integrity layer is already initialized');
|
||||
_viewer = viewer;
|
||||
_dataSource = new Cesium.CustomDataSource('gnss-interference');
|
||||
_dataSource.show = false;
|
||||
@@ -179,7 +186,7 @@ export function createGnssInterferenceLayer({
|
||||
count: _cells.filter((cell) => cell.level === level).length,
|
||||
...(index === 0
|
||||
? {
|
||||
blurb: `Share of ADS-B aircraft reporting low navigation integrity (NIC/NACp) over the last 30 minutes, around the views you visit. Cells with fewer than ${GNSS_MIN_AIRCRAFT} aircraft are not drawn, so blank areas mean no data, not clean GNSS. An inference of jamming or spoofing, not a detection.`,
|
||||
blurb: `Share of ADS-B aircraft reporting low navigation accuracy over the last ${provenance.window.minutes} minutes, around the views you visit. An aircraft counts when it reports NIC < ${GNSS_NIC_THRESHOLD} or NACp < ${GNSS_NACP_THRESHOLD} or GPS loss: a GEV threshold borrowed from US ADS-B Out minima, not a published interference test. Cells use gpsjam.org's formula and 2% / 10% bands. Cells with fewer than ${GNSS_MIN_AIRCRAFT} aircraft are not drawn, so blank areas mean no data, not clean GNSS. Medium and high cells are navigation-integrity anomalies. Suspected jamming or spoofing is one possible cause, not a detection: avionics faults also report low values.`,
|
||||
}
|
||||
: {}),
|
||||
})),
|
||||
@@ -192,6 +199,7 @@ export function createGnssInterferenceLayer({
|
||||
lastUpdate: _lastUpdate,
|
||||
error: _lastError,
|
||||
stale: _stale,
|
||||
provenance,
|
||||
// Load-bearing: without an explicit boolean, layerFeedState reads the
|
||||
// adsb.lol source name as a flights fallback (src/data/feedState.js).
|
||||
fallback: false,
|
||||
|
||||
Reference in new issue
Block a user