fix(satellites): stop matching a mission to a namesake from another year

The mission orbit lookup reads each loaded satellite's launch year from its
international designator, but satellite.js never parses that field, so the
year check never ran and a new payload could take an older namesake's orbit
and position. The loaded catalog now keeps the designator from the TLE line.
This commit is contained in:
Aditya committed 2026-10-28 17:15:48 +05:30
1 parent a4d3701c3c
commit 46b3864d61
4 files changed
+38

No files matched your search

+2
View File
@@ -114,6 +114,8 @@ export function createCatalog({ state: layerState, services, parts, source }) {
const entry = entries[i];
const satrec = twoline2satrec(entry.line1, entry.line2);
if (!satrec || satrec.error !== 0) continue;
// satellite.js omits the designator the launch-year match reads.
satrec.intldesg = entry.line1.slice(9, 17).trim();
const noradId = noradIdFromSatnum(satrec.satnum);
if (layerState._catalog.has(noradId)) continue; // core catalog keeps priority
const pos = parts.orbits.propagatePosition(satrec, now);
+2
View File
@@ -119,6 +119,8 @@ export function createIngestion({
for (const entry of allEntries) {
const satrec = twoline2satrec(entry.line1, entry.line2);
if (!satrec || satrec.error !== 0) continue;
// satellite.js omits the designator the launch-year match reads.
satrec.intldesg = entry.line1.slice(9, 17).trim();
const noradId = noradIdFromSatnum(satrec.satnum);
if (seen.has(noradId)) continue;
+20
View File
@@ -2,6 +2,7 @@ import test from 'node:test';
import assert from 'node:assert/strict';
import { parseTleText } from '../../sources/tle.js';
import { createIngestion } from './ingestion.js';
import { createOrbits } from './orbits.js';
// The ISS set (1 January 2024) with its catalog field swapped per satellite.
const ISS_LINE1 =
@@ -78,3 +79,22 @@ test('every Alpha-5 satellite keeps its own catalog entry through dedupe', async
assert.deepEqual([...state._points.keys()], [100000, 110001, 339999]);
assert.equal(state._count, 3);
});
test('a mission payload does not match a loaded satellite from another launch year', async (t) => {
t.mock.method(console, 'log', () => {});
t.mock.method(console, 'warn', () => {});
// SXM-11, international designator 2026-148A.
const { state, update } = ingestionFor(
[
'SXM-11',
'1 69728U 26148A 26208.65166678 .00000015 00000+0 00000+0 0 9990',
'2 69728 0.0784 264.9826 0001797 240.4588 273.9506 1.00271527 440',
].join('\n'),
);
await update();
const { getSatelliteOrbitTrack } = createOrbits({ state });
const track = (launchTime) =>
getSatelliteOrbitTrack('Sirius SXM-11', { launchTime });
assert.equal(track('2026-06-29T02:25:00Z')?.noradId, 69728);
assert.equal(track('2030-06-29T02:25:00Z'), null);
});
+14
View File
@@ -235,6 +235,20 @@ test('dense provenance belongs to its admitted records and clears on removal', a
);
});
test('dense extras keep their launch year for mission orbit matching', async (t) => {
const f = await fixture(t);
await f.update();
f.state._params.catalog = 'dense';
assert.equal((await f.parts.catalog._loadDenseCatalog()).status, 'ready');
// Every fixture set carries the 1998-067A designator.
const track = (launchTime) =>
f.parts.orbits.getSatelliteOrbitTrack('OWNED ORBIT FIXTURE 25551', {
launchTime,
});
assert.equal(track('1998-11-20T06:40:00Z')?.noradId, 25551);
assert.equal(track('2026-06-29T02:25:00Z'), null);
});
test('cancelling dense replacement preserves admitted stale extras until rebuild', async (t) => {
const f = await fixture(t);
await f.update();