mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
Merge branch 'main' into codex/shared-geodesy
This commit is contained in:
commit
aa52ab2eca
17 files changed
+419
-152
No files matched your search
@@ -219,8 +219,8 @@ export const COURSE_SLEW_DT_MAX_SEC = 0.25;
|
||||
// adsbdb enrichment (best-effort, fail-silent). Bounded fan-out: max 4
|
||||
// concurrent requests, dispatches dripped ≥ENRICH_DISPATCH_GAP_MS apart
|
||||
// (≤5/s — adsbdb is a free community API; the dev-server proxy additionally
|
||||
// caches per-key on disk forever, negative results included, so repeat
|
||||
// sessions never re-hit adsbdb). Each key is requested at most once per
|
||||
// caches successful and 404 responses per key for 24 hours, so later sessions
|
||||
// may refresh them). Each key is requested at most once per
|
||||
// session. Priority jobs (tracked plane, model-eligible planes) jump the
|
||||
// queue; the ambient fleet sweep (below) fills the back at poll cadence.
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -243,7 +243,7 @@ export const ENRICH_DISPATCH_GAP_MS = 200;
|
||||
// of drip, so the backlog can't outgrow a poll and re-sorts fresh), and
|
||||
// - a ROLLING token-bucket budget (below) bounding the sustained ambient
|
||||
// request rate (repeat sessions resolve instantly from the proxy's
|
||||
// permanent disk cache).
|
||||
// 24-hour disk cache).
|
||||
// Fail-silent by contract: the sweep never throws into the poll loop, never
|
||||
// blocks rendering, and never touches tracking state. When a type answer
|
||||
// lands, _requestTypeEnrichment's callback swaps the billboard glyph + scale
|
||||
@@ -257,7 +257,7 @@ export const ENRICH_DISPATCH_GAP_MS = 200;
|
||||
// ENRICH_AMBIENT_REFILL_TOKENS every ENRICH_AMBIENT_REFILL_WINDOW_MS, clamped
|
||||
// at the ceiling (no banking beyond one bucket). Numbers: 150 / 5 min sustains
|
||||
// 0.5 req/s worst case — an order of magnitude under the 5/s drip that (with
|
||||
// the 4-concurrent limit + the proxy's permanent disk cache) is the REAL
|
||||
// the 4-concurrent limit + the proxy's 24-hour disk cache) is the REAL
|
||||
// politeness bound on adsbdb; the 300 ceiling preserves the old first-look
|
||||
// burst so a fresh region still classifies quickly.
|
||||
/** Bucket ceiling: max ambient tokens held at once (= the initial burst). */
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as Cesium from 'cesium';
|
||||
import { twoline2satrec } from 'satellite.js';
|
||||
import { noradIdFromSatnum } from '../../sources/tle.js';
|
||||
import {
|
||||
DENSE_REFRESH_FRAMES,
|
||||
DENSE_GROUP_PATH,
|
||||
@@ -113,7 +114,7 @@ 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;
|
||||
const noradId = Number(satrec.satnum);
|
||||
const noradId = noradIdFromSatnum(satrec.satnum);
|
||||
if (layerState._catalog.has(noradId)) continue; // core catalog keeps priority
|
||||
const pos = parts.orbits.propagatePosition(satrec, now);
|
||||
if (!pos) continue;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { twoline2satrec } from 'satellite.js';
|
||||
import * as Cesium from 'cesium';
|
||||
import { msg } from '../../i18n/index.js';
|
||||
import { noradIdFromSatnum } from '../../sources/tle.js';
|
||||
import { CATALOG_GROUPS, ISS_NORAD, POINT_STYLES } from './policy.js';
|
||||
|
||||
export function createIngestion({
|
||||
@@ -111,7 +112,7 @@ export function createIngestion({
|
||||
const satrec = twoline2satrec(entry.line1, entry.line2);
|
||||
if (!satrec || satrec.error !== 0) continue;
|
||||
|
||||
const noradId = Number(satrec.satnum);
|
||||
const noradId = noradIdFromSatnum(satrec.satnum);
|
||||
if (seen.has(noradId)) continue;
|
||||
seen.add(noradId);
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { parseTleText } from '../../sources/tle.js';
|
||||
import { createIngestion } from './ingestion.js';
|
||||
|
||||
// The ISS set (1 January 2024) with its catalog field swapped per satellite.
|
||||
const ISS_LINE1 =
|
||||
'1 25544U 98067A 24001.50000000 .00016717 00000-0 30370-3 0 9993';
|
||||
const ISS_LINE2 =
|
||||
'2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.50377579432323';
|
||||
|
||||
function tleSet(name, field) {
|
||||
return [
|
||||
name,
|
||||
ISS_LINE1.replace('25544', field),
|
||||
ISS_LINE2.replace('25544', field),
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function ingestionFor(catalogText) {
|
||||
const state = {
|
||||
_trackingRefreshEpoch: 0,
|
||||
_activeUpdateControllers: new Set(),
|
||||
_pointCollection: {
|
||||
removeAll() {},
|
||||
add: (point) => point,
|
||||
},
|
||||
_points: new Map(),
|
||||
_orbitPaths: new Map(),
|
||||
_catalog: new Map(),
|
||||
_detectionObjects: new Map(),
|
||||
_denseLoadToken: 0,
|
||||
_catalogRevision: 0,
|
||||
_params: { catalog: 'core', showOrbits: false },
|
||||
};
|
||||
const parts = {
|
||||
orbits: {
|
||||
parseTLE: parseTleText,
|
||||
propagatePosition: () => ({ longitude: 0, latitude: 0, altitude: 4e5 }),
|
||||
},
|
||||
labels: { _syncIssOverlay() {} },
|
||||
controls: { _pointStyleFor: () => ({}) },
|
||||
rendering: { _showOrbitPath() {} },
|
||||
tracking: {
|
||||
async _reconcileTrackedSubjectContext() {},
|
||||
_applyPendingTrackingRestore() {},
|
||||
},
|
||||
catalog: {},
|
||||
};
|
||||
const source = {
|
||||
async readGroup() {
|
||||
return { ok: true, text: catalogText };
|
||||
},
|
||||
};
|
||||
const { methods } = createIngestion({ state, services: {}, parts, source });
|
||||
return { state, update: () => methods.update({ scene: {} }) };
|
||||
}
|
||||
|
||||
test('every Alpha-5 satellite keeps its own catalog entry through dedupe', async (t) => {
|
||||
t.mock.method(console, 'log', () => {});
|
||||
t.mock.method(console, 'warn', () => {});
|
||||
const { state, update } = ingestionFor(
|
||||
[
|
||||
tleSet('FIRST', 'A0000'),
|
||||
tleSet('SECOND', 'B0001'),
|
||||
tleSet('THIRD', 'Z9999'),
|
||||
].join('\n'),
|
||||
);
|
||||
await update();
|
||||
assert.deepEqual(
|
||||
[...state._catalog].map(([id, entry]) => [id, entry.name]),
|
||||
[
|
||||
[100000, 'FIRST'],
|
||||
[110001, 'SECOND'],
|
||||
[339999, 'THIRD'],
|
||||
],
|
||||
);
|
||||
assert.deepEqual([...state._points.keys()], [100000, 110001, 339999]);
|
||||
assert.equal(state._count, 3);
|
||||
});
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
twoline2satrec,
|
||||
} from 'satellite.js';
|
||||
import { findNextSatellitePass } from '../../data/satellitePass.js';
|
||||
import { parseTleText } from '../../sources/tle.js';
|
||||
import { noradIdFromSatnum, parseTleText } from '../../sources/tle.js';
|
||||
import { ORBIT_PATH_STEPS, ISS_NORAD } from './policy.js';
|
||||
|
||||
export function createOrbits({ state: layerState, services, parts, source }) {
|
||||
@@ -284,7 +284,7 @@ export function createOrbits({ state: layerState, services, parts, source }) {
|
||||
const current = propagatePosition(satrec, referenceDate);
|
||||
if (!current) return null;
|
||||
return {
|
||||
noradId: Number(satrec.satnum),
|
||||
noradId: noradIdFromSatnum(satrec.satnum),
|
||||
name: String(name || '').trim(),
|
||||
current,
|
||||
periodSec: orbitalPeriodSeconds(satrec),
|
||||
|
||||
Reference in new issue
Block a user