fix(satellites): recover TLE parsing after incomplete sets

This commit is contained in:
Huanyi Xie committed 2026-10-05 13:02:20 +03:00
1 parent e1cc7afacf
commit 1c3748e3bd
4 files changed
+32 -2

No files matched your search

+4
View File
@@ -2,6 +2,10 @@
## [Unreleased] ## [Unreleased]
- Keep later satellites when a TLE catalog entry is missing an orbital line.
The parser skips the incomplete set and resumes at the next complete named
set, instead of losing the rest of the group (#906).
## [0.2.1] — 2026-10-02 — God's Eye View through Your Agent (with security fixes and other features) ## [0.2.1] — 2026-10-02 — God's Eye View through Your Agent (with security fixes and other features)
- Ship each bundled data pack once. The region, marine, admin-boundary, - Ship each bundled data pack once. The region, marine, admin-boundary,
+4
View File
@@ -1049,6 +1049,10 @@ The Node-only `gods-eye-view/server/providers/space` export supplies factories;
`sources/space` supplies fixed upstream URL builders with no I/O or environment `sources/space` supplies fixed upstream URL builders with no I/O or environment
access. Callers retain validation, transport and response policy. access. Callers retain validation, transport and response policy.
The shared TLE text parser skips incomplete named sets and resumes at the next
complete set. A missing orbital line does not discard the later satellites in
the catalog; satellite layers and next-pass tools use the same parser.
## Build configuration and local provider boundaries ## Build configuration and local provider boundaries
`vite.config.js` delegates to `server/standalone/vite.config.js`, which loads `vite.config.js` delegates to `server/standalone/vite.config.js`, which loads
+3 -2
View File
@@ -1,6 +1,6 @@
/** /**
* Parse three-line TLE catalog text into `{ name, line1, line2 }` entries. * Parse three-line TLE catalog text into `{ name, line1, line2 }` entries.
* Blocks whose second and third lines are not TLE lines 1 and 2 are skipped. * Malformed sets are skipped without dropping the valid sets that follow.
*/ */
export function parseTleText(text) { export function parseTleText(text) {
const lines = String(text) const lines = String(text)
@@ -9,12 +9,13 @@ export function parseTleText(text) {
.map((line) => line.trim()) .map((line) => line.trim())
.filter((line) => line.length > 0); .filter((line) => line.length > 0);
const result = []; const result = [];
for (let i = 0; i < lines.length - 2; i += 3) { for (let i = 0; i < lines.length - 2; i++) {
const name = lines[i]; const name = lines[i];
const line1 = lines[i + 1]; const line1 = lines[i + 1];
const line2 = lines[i + 2]; const line2 = lines[i + 2];
if (line1.startsWith('1 ') && line2.startsWith('2 ')) { if (line1.startsWith('1 ') && line2.startsWith('2 ')) {
result.push({ name, line1, line2 }); result.push({ name, line1, line2 });
i += 2;
} }
} }
return result; return result;
+21
View File
@@ -30,6 +30,27 @@ test('TLE text parses into named entries with catalog numbers', () => {
assert.equal(tleCatalogNumber('1 xx'), null); assert.equal(tleCatalogNumber('1 xx'), null);
}); });
for (const missingLine of [1, 2]) {
test(`TLE parsing resumes after a set missing line ${missingLine}`, () => {
const sets = Array.from({ length: 8 }, (_, i) => {
const lines = TLE.trim().split('\n').slice(0, 3);
lines[0] = `SAT ${i}`;
return lines;
});
sets[1].splice(missingLine, 1);
const entries = parseTleText(sets.flat().join('\r\n'));
assert.deepEqual(
entries.map((entry) => entry.name),
['SAT 0', 'SAT 2', 'SAT 3', 'SAT 4', 'SAT 5', 'SAT 6', 'SAT 7'],
);
assert.ok(
entries.every(
(entry) => entry.line1.startsWith('1 ') && entry.line2.startsWith('2 '),
),
);
});
}
test('the next pass defaults to the ISS and reports times, peak and direction', async () => { test('the next pass defaults to the ISS and reports times, peak and direction', async () => {
const catalog = composeCatalog({ const catalog = composeCatalog({
tools: coreTools, tools: coreTools,