mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +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
+40
-5
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* qa-gnss.mjs — headless proof for the GNSS Interference layer.
|
||||
* qa-gnss.mjs — headless proof for the GNSS Integrity layer
|
||||
* (id `gnss-interference`): low navigation accuracy reported by ADS-B.
|
||||
*
|
||||
* Drives the REAL app in headless Chromium against a dev server (keyless).
|
||||
* The live section reads adsb.lol through `/api/gnss-integrity` (60 s server
|
||||
@@ -16,6 +17,8 @@
|
||||
* with 1 degraded is low (first bad discounted), a clean cell at sea
|
||||
* is low, a 2-aircraft cell is withheld; `stale: true` reaches
|
||||
* getStats. Every band is drawn and low is shown over land and sea.
|
||||
* The row reads as low navigation accuracy, and stats carry the
|
||||
* machine-readable provenance (GEV classifier, gpsjam bands, window).
|
||||
* (iii) FAILURE — the route stubbed to 502: stats.error is set and the
|
||||
* cells already on the globe are kept.
|
||||
* (iv) DISABLE — the layer's data source is hidden.
|
||||
@@ -108,13 +111,19 @@ async function enableAndSettle(page, { timeoutS = 30, refresh = false } = {}) {
|
||||
if (stats.count > 0 || stats.error) break;
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
}
|
||||
const legend = layer
|
||||
.getRowControls()
|
||||
.legend.map(({ label, count }) => ({ label, count }));
|
||||
const rowControls = layer.getRowControls().legend;
|
||||
const legend = rowControls.map(({ label, count }) => ({ label, count }));
|
||||
const blurb = rowControls[0]?.blurb ?? '';
|
||||
const rowName =
|
||||
document.querySelector(`[data-layer-id="${id}"] .data-name`)
|
||||
?.textContent ?? '';
|
||||
const ds = window.__godsEyeView.viewer.dataSources.getByName(id)[0];
|
||||
return {
|
||||
stats,
|
||||
legend,
|
||||
blurb,
|
||||
rowName,
|
||||
name: layer.name,
|
||||
entities: ds?.entities.values.length ?? -1,
|
||||
shown: ds?.show ?? null,
|
||||
};
|
||||
@@ -217,7 +226,7 @@ function fixturePayload() {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('\nGNSS Interference proof (qa-gnss)');
|
||||
console.log('\nGNSS Integrity proof (qa-gnss)');
|
||||
console.log(` App URL : ${APP_URL}\n`);
|
||||
try {
|
||||
const res = await fetch(APP_URL);
|
||||
@@ -346,6 +355,32 @@ async function main() {
|
||||
band('Over') === 1 && band('2') === 1 && band('Under') === 2,
|
||||
JSON.stringify(f.legend),
|
||||
);
|
||||
const prov = f.stats.provenance;
|
||||
record(
|
||||
'PROVENANCE: GEV classifier, gpsjam bands and visited-view window are machine-readable',
|
||||
prov?.classifier?.id === 'gev-nic-nacp-v1' &&
|
||||
prov.classifier.definedBy === 'GEV' &&
|
||||
prov.classifier.validated === false &&
|
||||
prov.aggregation?.source === 'https://gpsjam.org/faq' &&
|
||||
JSON.stringify(prov.aggregation.bands) === '[0.02,0.1]' &&
|
||||
prov.window?.minutes === 30 &&
|
||||
prov.window.scope === 'visited-view' &&
|
||||
prov.interpretation?.validated === false,
|
||||
JSON.stringify(prov?.classifier),
|
||||
);
|
||||
record(
|
||||
'WORDING: the row leads with low navigation accuracy, interference only as the hedged reading',
|
||||
f.name === 'GNSS Integrity' &&
|
||||
f.rowName.includes('GNSS Integrity') &&
|
||||
!/interference/i.test(f.rowName) &&
|
||||
f.legend.every(({ label }) => label.endsWith('low accuracy')) &&
|
||||
f.blurb.startsWith(
|
||||
'Share of ADS-B aircraft reporting low navigation accuracy',
|
||||
) &&
|
||||
/GEV threshold/.test(f.blurb) &&
|
||||
/Suspected jamming or spoofing is one possible cause/.test(f.blurb),
|
||||
`name=${JSON.stringify(f.name)}`,
|
||||
);
|
||||
record(
|
||||
'BANDS: stale snapshot is reported',
|
||||
f.stats.stale === true,
|
||||
|
||||
Reference in new issue
Block a user