diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 5c2e52c58..4fa9edd5b 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -4257,8 +4257,11 @@ down | auth-failed` (plus the unchanged `missing-key`/`unsupported`) with > advances dead reckoning from the host frame. > - **Scheduled route plausibility:** Aircraft route labels and selected-route > context reject the distant backwards, forwards and antipodal extensions of -> an airport pair's great circle. The existing 130 km endpoint admission and -> 200 km cross-track tolerance remain; a projected fix outside the finite +> an airport pair's great circle. The existing 130 km endpoint admission +> remains; the cross-track tolerance is 200 km or 15% of the route's length, +> whichever is larger (JFK–SFO allows 623 km, as airways and winds take a +> coast-to-coast flight several hundred kilometres off the great circle); a +> projected fix outside the finite > origin-to-destination minor arc also has a generous 200 km extension at > either end. Missing coordinates and coincident or antipodal endpoint pairs > retain their loose, unjudgeable behavior; vertical-trend checks are unchanged. diff --git a/src/data/routePlausible.js b/src/data/routePlausible.js index d52b44ef8..7b2cffc06 100644 --- a/src/data/routePlausible.js +++ b/src/data/routePlausible.js @@ -46,7 +46,13 @@ function alongTrackKm(position, origin, destination) { } const NEAR_ENDPOINT_KM = 130; +/** Least allowed distance off the great circle, and how far past either + * endpoint the plane may be along it. */ const CROSS_TRACK_KM = 200; +/** On a long route the allowed distance off the great circle grows to this + * share of its length: airways and winds take a coast-to-coast flight several + * hundred kilometres off it (JFK–SFO: 623 km). */ +const CROSS_TRACK_ROUTE_FRACTION = 0.15; const LOW_ALT_M = 3700; // ~12 000 ft const VERT_TREND_MPS = 2; // ~400 fpm const LOCAL_AIRPORT_KM = 150; @@ -90,6 +96,9 @@ export function routePlausible({ // existing loose check when the segment cannot be judged reliably. const segmentKnown = routeKm > 0.001 && routeKm < Math.PI * EARTH_RADIUS_KM - 0.001; + const corridorKm = segmentKnown + ? Math.max(CROSS_TRACK_KM, CROSS_TRACK_ROUTE_FRACTION * routeKm) + : CROSS_TRACK_KM; geomOk = Math.abs( crossTrackKm( @@ -100,7 +109,7 @@ export function routePlausible({ destination.lat, destination.lon, ), - ) < CROSS_TRACK_KM && + ) < corridorKm && (!segmentKnown || (segmentAlongKm >= -CROSS_TRACK_KM && segmentAlongKm <= routeKm + CROSS_TRACK_KM)); diff --git a/src/data/routePlausible.test.mjs b/src/data/routePlausible.test.mjs index aa59a0c18..f4ca0c573 100644 --- a/src/data/routePlausible.test.mjs +++ b/src/data/routePlausible.test.mjs @@ -27,6 +27,24 @@ test('plane in London with an SFO→LAX route: implausible', () => { }), false); }); +test('a long route allows a wider corridor; a short one does not', () => { + // JFK→SFO over Kansas, 384 km south of the great circle. + assert.equal(routePlausible({ + latDeg: 38.21, lonDeg: -99.69, altitudeM: 10370, verticalRateMps: 0, + origin: JFK, destination: SFO, + }), true); + // 516 km off the 543 km SFO→LAX route is still the wrong route. + assert.equal(routePlausible({ + latDeg: 37.9, lonDeg: -115.0, altitudeM: 10000, verticalRateMps: 0, + origin: SFO, destination: LAX, + }), false); + // 960 km off, past 15 % of the 4,152 km JFK→SFO route, is still wrong. + assert.equal(routePlausible({ + latDeg: 33.0, lonDeg: -99.69, altitudeM: 10000, verticalRateMps: 0, + origin: JFK, destination: SFO, + }), false); +}); + test('vertical trend: low climbing plane near SFO — origin must be local', () => { const nearSfo = { latDeg: 37.7, lonDeg: -122.4, altitudeM: 2500 }; // JFK→SFO passes geometry (near destination), but a CLIMBING plane here just