ootk 7.0.2 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -8
- package/dist/main.cjs +1807 -634
- package/dist/main.d.cts +1181 -142
- package/dist/main.d.ts +1181 -142
- package/dist/main.js +1791 -635
- package/package.json +36 -12
package/dist/main.cjs
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// node_modules/.pnpm/tsup@8.5.1_postcss@8.5.23_tsx@4.23.1_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
|
|
4
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
5
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
6
|
+
|
|
3
7
|
// src/errors/index.ts
|
|
4
8
|
var OotkError = class extends Error {
|
|
5
9
|
constructor(message) {
|
|
@@ -23,6 +27,8 @@ var ValidationError = class extends OotkError {
|
|
|
23
27
|
this.value = value;
|
|
24
28
|
this.name = "ValidationError";
|
|
25
29
|
}
|
|
30
|
+
field;
|
|
31
|
+
value;
|
|
26
32
|
};
|
|
27
33
|
var ParseError = class extends OotkError {
|
|
28
34
|
/**
|
|
@@ -37,6 +43,8 @@ var ParseError = class extends OotkError {
|
|
|
37
43
|
this.line = line;
|
|
38
44
|
this.name = "ParseError";
|
|
39
45
|
}
|
|
46
|
+
format;
|
|
47
|
+
line;
|
|
40
48
|
};
|
|
41
49
|
var PropagationError = class extends OotkError {
|
|
42
50
|
/**
|
|
@@ -49,6 +57,7 @@ var PropagationError = class extends OotkError {
|
|
|
49
57
|
this.epoch = epoch;
|
|
50
58
|
this.name = "PropagationError";
|
|
51
59
|
}
|
|
60
|
+
epoch;
|
|
52
61
|
};
|
|
53
62
|
var OrbitDeterminationError = class extends OotkError {
|
|
54
63
|
/**
|
|
@@ -61,6 +70,7 @@ var OrbitDeterminationError = class extends OotkError {
|
|
|
61
70
|
this.algorithm = algorithm;
|
|
62
71
|
this.name = "OrbitDeterminationError";
|
|
63
72
|
}
|
|
73
|
+
algorithm;
|
|
64
74
|
};
|
|
65
75
|
|
|
66
76
|
// src/enums/AngularDiameterMethod.ts
|
|
@@ -155,6 +165,7 @@ var SensorType = /* @__PURE__ */ ((SensorType2) => {
|
|
|
155
165
|
SensorType2["LASER_RANGING"] = "LASER_RANGING";
|
|
156
166
|
SensorType2["PASSIVE_RF"] = "PASSIVE_RF";
|
|
157
167
|
SensorType2["BISTATIC_RADIO_TELESCOPE"] = "BISTATIC_RADIO_TELESCOPE";
|
|
168
|
+
SensorType2["TT_C_ANTENNA"] = "TT_C_ANTENNA";
|
|
158
169
|
return SensorType2;
|
|
159
170
|
})(SensorType || {});
|
|
160
171
|
|
|
@@ -225,6 +236,7 @@ var SpaceObjectType = /* @__PURE__ */ ((SpaceObjectType2) => {
|
|
|
225
236
|
SpaceObjectType2[SpaceObjectType2["DWARF_PLANET"] = 36] = "DWARF_PLANET";
|
|
226
237
|
SpaceObjectType2[SpaceObjectType2["MOON"] = 37] = "MOON";
|
|
227
238
|
SpaceObjectType2[SpaceObjectType2["DYNAMIC_GROUND_OBJECT"] = 38] = "DYNAMIC_GROUND_OBJECT";
|
|
239
|
+
SpaceObjectType2[SpaceObjectType2["ASTEROID"] = 39] = "ASTEROID";
|
|
228
240
|
SpaceObjectType2[SpaceObjectType2["MAX_SPACE_OBJECT_TYPE"] = 40] = "MAX_SPACE_OBJECT_TYPE";
|
|
229
241
|
return SpaceObjectType2;
|
|
230
242
|
})(SpaceObjectType || {});
|
|
@@ -278,6 +290,7 @@ var Epoch = class {
|
|
|
278
290
|
throw new ValidationError("Epoch posix time must be a valid number", "posix", posix);
|
|
279
291
|
}
|
|
280
292
|
}
|
|
293
|
+
posix;
|
|
281
294
|
toString() {
|
|
282
295
|
return this.toDateTime().toISOString();
|
|
283
296
|
}
|
|
@@ -440,7 +453,8 @@ function matchHalfPlane(angle, match) {
|
|
|
440
453
|
return Math.abs(d1) < Math.abs(d2) ? a1 : a2;
|
|
441
454
|
}
|
|
442
455
|
function wrapAngle(theta) {
|
|
443
|
-
const
|
|
456
|
+
const tau = 2 * Math.PI;
|
|
457
|
+
const result = ((theta + Math.PI) % tau + tau) % tau - Math.PI;
|
|
444
458
|
if (result === -Math.PI) {
|
|
445
459
|
return Math.PI;
|
|
446
460
|
}
|
|
@@ -449,7 +463,7 @@ function wrapAngle(theta) {
|
|
|
449
463
|
function angularDistanceCosine_(lam1, phi1, lam2, phi2) {
|
|
450
464
|
const a = Math.sin(phi1) * Math.sin(phi2);
|
|
451
465
|
const b = Math.cos(phi1) * Math.cos(phi2) * Math.cos(lam2 - lam1);
|
|
452
|
-
return Math.acos(a + b);
|
|
466
|
+
return Math.acos(Math.min(1, Math.max(-1, a + b)));
|
|
453
467
|
}
|
|
454
468
|
function angularDistanceHaversine_(lam1, phi1, lam2, phi2) {
|
|
455
469
|
const dlam = lam2 - lam1;
|
|
@@ -634,6 +648,7 @@ var spaceObjTypeStrMap_ = {
|
|
|
634
648
|
[36 /* DWARF_PLANET */]: "Dwarf Planet",
|
|
635
649
|
[37 /* MOON */]: "Moon",
|
|
636
650
|
[38 /* DYNAMIC_GROUND_OBJECT */]: "Dynamic Ground Object",
|
|
651
|
+
[39 /* ASTEROID */]: "Asteroid",
|
|
637
652
|
[40 /* MAX_SPACE_OBJECT_TYPE */]: "Max Space Object Type"
|
|
638
653
|
};
|
|
639
654
|
var spaceObjType2Str = (spaceObjType) => spaceObjTypeStrMap_[spaceObjType] || "Unknown";
|
|
@@ -1815,6 +1830,8 @@ var EpochGPS = class _EpochGPS {
|
|
|
1815
1830
|
throw new ValidationError("GPS seconds must be between 0 and 604799", "seconds", seconds);
|
|
1816
1831
|
}
|
|
1817
1832
|
}
|
|
1833
|
+
week;
|
|
1834
|
+
seconds;
|
|
1818
1835
|
/** Cached GPS reference epoch (1980-01-06T00:00:00.000Z) */
|
|
1819
1836
|
static reference_ = null;
|
|
1820
1837
|
/**
|
|
@@ -1950,6 +1967,7 @@ var Vector = class _Vector {
|
|
|
1950
1967
|
this.elements = elements;
|
|
1951
1968
|
this.length = elements.length;
|
|
1952
1969
|
}
|
|
1970
|
+
elements;
|
|
1953
1971
|
/**
|
|
1954
1972
|
* The length of the vector.
|
|
1955
1973
|
*/
|
|
@@ -2713,6 +2731,9 @@ var Vector3D = class _Vector3D {
|
|
|
2713
2731
|
this.y = y;
|
|
2714
2732
|
this.z = z;
|
|
2715
2733
|
}
|
|
2734
|
+
x;
|
|
2735
|
+
y;
|
|
2736
|
+
z;
|
|
2716
2737
|
/**
|
|
2717
2738
|
* Create a new Vector3D object from the first three elements of a Vector
|
|
2718
2739
|
* object.
|
|
@@ -3220,6 +3241,131 @@ var Sgp4Error = class _Sgp4Error extends Error {
|
|
|
3220
3241
|
}
|
|
3221
3242
|
};
|
|
3222
3243
|
|
|
3244
|
+
// src/sgp4/sgp4-wasm-backend.ts
|
|
3245
|
+
var WASM_KEY_FAILED = -1n;
|
|
3246
|
+
var defineHidden_ = (obj, key, value) => {
|
|
3247
|
+
Object.defineProperty(obj, key, {
|
|
3248
|
+
value,
|
|
3249
|
+
writable: true,
|
|
3250
|
+
configurable: true,
|
|
3251
|
+
enumerable: false
|
|
3252
|
+
});
|
|
3253
|
+
};
|
|
3254
|
+
var backend = null;
|
|
3255
|
+
var satKeyCache = /* @__PURE__ */ new Map();
|
|
3256
|
+
var syntheticSatnumCounter = 0;
|
|
3257
|
+
var wasmAttachedCount = 0;
|
|
3258
|
+
var wasmFallbackCount = 0;
|
|
3259
|
+
var SYNTHETIC_SATNUM_LETTERS = "VWXYZ";
|
|
3260
|
+
var synthesizeSatnum_ = () => {
|
|
3261
|
+
const n = syntheticSatnumCounter++;
|
|
3262
|
+
const letterIdx = Math.floor(n / 1e4);
|
|
3263
|
+
if (letterIdx >= SYNTHETIC_SATNUM_LETTERS.length) {
|
|
3264
|
+
return null;
|
|
3265
|
+
}
|
|
3266
|
+
return `${SYNTHETIC_SATNUM_LETTERS[letterIdx]}${(n % 1e4).toString().padStart(4, "0")}`;
|
|
3267
|
+
};
|
|
3268
|
+
var tryAddWithSyntheticSatnum_ = (wasm, line1, line2) => {
|
|
3269
|
+
const satnumField = line1.substring(2, 7);
|
|
3270
|
+
const classification = line1.charAt(7);
|
|
3271
|
+
const isSatnumMeaningless = satnumField.trim() === "" || satnumField === "00000";
|
|
3272
|
+
const isNonStandardClassification = classification !== "U" && classification !== "C" && classification !== "S";
|
|
3273
|
+
if (!isSatnumMeaningless && !isNonStandardClassification) {
|
|
3274
|
+
return null;
|
|
3275
|
+
}
|
|
3276
|
+
const satnum = synthesizeSatnum_();
|
|
3277
|
+
if (satnum === null) {
|
|
3278
|
+
return null;
|
|
3279
|
+
}
|
|
3280
|
+
const syntheticLine1 = `${line1.slice(0, 2)}${satnum}U${line1.slice(8)}`;
|
|
3281
|
+
const syntheticLine2 = `${line2.slice(0, 2)}${satnum}${line2.slice(7)}`;
|
|
3282
|
+
try {
|
|
3283
|
+
const satKey = wasm.addSat(syntheticLine1, syntheticLine2);
|
|
3284
|
+
wasm.initSats([satKey]);
|
|
3285
|
+
return satKey;
|
|
3286
|
+
} catch {
|
|
3287
|
+
return null;
|
|
3288
|
+
}
|
|
3289
|
+
};
|
|
3290
|
+
var setSgp4WasmBackend = (wasm) => {
|
|
3291
|
+
if (!wasm.isLoaded) {
|
|
3292
|
+
throw new Error("Sgp4 wasm backend must be loaded before use. Call load() first.");
|
|
3293
|
+
}
|
|
3294
|
+
backend = wasm;
|
|
3295
|
+
};
|
|
3296
|
+
var clearSgp4WasmBackend = () => {
|
|
3297
|
+
backend = null;
|
|
3298
|
+
satKeyCache.clear();
|
|
3299
|
+
syntheticSatnumCounter = 0;
|
|
3300
|
+
wasmAttachedCount = 0;
|
|
3301
|
+
wasmFallbackCount = 0;
|
|
3302
|
+
};
|
|
3303
|
+
var getSgp4WasmBackendStats = () => ({
|
|
3304
|
+
attached: wasmAttachedCount,
|
|
3305
|
+
fallback: wasmFallbackCount
|
|
3306
|
+
});
|
|
3307
|
+
var isSgp4WasmBackendActive = () => backend !== null;
|
|
3308
|
+
var stashTleLines = (satrec, line1, line2) => {
|
|
3309
|
+
defineHidden_(satrec, "wasmTleLine1", line1);
|
|
3310
|
+
defineHidden_(satrec, "wasmTleLine2", line2);
|
|
3311
|
+
};
|
|
3312
|
+
var ensureSatKey_ = (wasm, satrec) => {
|
|
3313
|
+
const existing = satrec.wasmSatKey;
|
|
3314
|
+
if (existing !== void 0) {
|
|
3315
|
+
return existing > 0n ? existing : null;
|
|
3316
|
+
}
|
|
3317
|
+
const line1 = satrec.wasmTleLine1;
|
|
3318
|
+
const line2 = satrec.wasmTleLine2;
|
|
3319
|
+
if (!line1 || !line2) {
|
|
3320
|
+
defineHidden_(satrec, "wasmSatKey", WASM_KEY_FAILED);
|
|
3321
|
+
wasmFallbackCount++;
|
|
3322
|
+
return null;
|
|
3323
|
+
}
|
|
3324
|
+
const cacheKey = `${line1}
|
|
3325
|
+
${line2}`;
|
|
3326
|
+
const cached = satKeyCache.get(cacheKey);
|
|
3327
|
+
if (cached !== void 0) {
|
|
3328
|
+
defineHidden_(satrec, "wasmSatKey", cached);
|
|
3329
|
+
return cached;
|
|
3330
|
+
}
|
|
3331
|
+
try {
|
|
3332
|
+
const satKey = wasm.addSat(line1, line2);
|
|
3333
|
+
wasm.initSats([satKey]);
|
|
3334
|
+
satKeyCache.set(cacheKey, satKey);
|
|
3335
|
+
defineHidden_(satrec, "wasmSatKey", satKey);
|
|
3336
|
+
wasmAttachedCount++;
|
|
3337
|
+
return satKey;
|
|
3338
|
+
} catch {
|
|
3339
|
+
const syntheticKey = tryAddWithSyntheticSatnum_(wasm, line1, line2);
|
|
3340
|
+
if (syntheticKey !== null) {
|
|
3341
|
+
satKeyCache.set(cacheKey, syntheticKey);
|
|
3342
|
+
defineHidden_(satrec, "wasmSatKey", syntheticKey);
|
|
3343
|
+
wasmAttachedCount++;
|
|
3344
|
+
return syntheticKey;
|
|
3345
|
+
}
|
|
3346
|
+
defineHidden_(satrec, "wasmSatKey", WASM_KEY_FAILED);
|
|
3347
|
+
wasmFallbackCount++;
|
|
3348
|
+
return null;
|
|
3349
|
+
}
|
|
3350
|
+
};
|
|
3351
|
+
var tryPropagateWasm = (satrec, tsince) => {
|
|
3352
|
+
if (!backend || satrec.init) {
|
|
3353
|
+
return null;
|
|
3354
|
+
}
|
|
3355
|
+
const satKey = ensureSatKey_(backend, satrec);
|
|
3356
|
+
if (satKey === null) {
|
|
3357
|
+
return null;
|
|
3358
|
+
}
|
|
3359
|
+
const result = backend.propagateOnePosVelFast(satKey, tsince);
|
|
3360
|
+
if (result.err !== 0) {
|
|
3361
|
+
return { position: false, velocity: false };
|
|
3362
|
+
}
|
|
3363
|
+
return {
|
|
3364
|
+
position: result.position,
|
|
3365
|
+
velocity: result.velocity
|
|
3366
|
+
};
|
|
3367
|
+
};
|
|
3368
|
+
|
|
3223
3369
|
// src/sgp4/sgp4.ts
|
|
3224
3370
|
var fasx2 = 0.13130908;
|
|
3225
3371
|
var fasx4 = 2.8843198;
|
|
@@ -3234,6 +3380,42 @@ var stepp = 720;
|
|
|
3234
3380
|
var stepn = -720;
|
|
3235
3381
|
var step2 = 259200;
|
|
3236
3382
|
var Sgp4 = class _Sgp4 {
|
|
3383
|
+
/**
|
|
3384
|
+
* Routes all subsequent `createSatrec`/`propagate` calls in this JavaScript
|
|
3385
|
+
* context through the USSF Astro Standards SGP4 wasm build instead of the
|
|
3386
|
+
* pure-TypeScript implementation. Pass a loaded `Sgp4Wasm` or `Sgp4XpWasm`
|
|
3387
|
+
* instance (see `src/external/`).
|
|
3388
|
+
*
|
|
3389
|
+
* satKeys are attached to satrecs lazily on first propagation, so the
|
|
3390
|
+
* backend can be activated before or after satrecs are created. Satrecs
|
|
3391
|
+
* without TLE lines (e.g. built from an OMM) and TLEs the wasm library
|
|
3392
|
+
* rejects fall back to the TypeScript implementation permanently.
|
|
3393
|
+
* @param wasm A loaded wasm SGP4 instance.
|
|
3394
|
+
*/
|
|
3395
|
+
static useWasmBackend(wasm) {
|
|
3396
|
+
setSgp4WasmBackend(wasm);
|
|
3397
|
+
}
|
|
3398
|
+
/**
|
|
3399
|
+
* Restores the pure-TypeScript SGP4 implementation.
|
|
3400
|
+
*/
|
|
3401
|
+
static clearWasmBackend() {
|
|
3402
|
+
clearSgp4WasmBackend();
|
|
3403
|
+
}
|
|
3404
|
+
/**
|
|
3405
|
+
* Whether propagation is currently routed through a wasm backend.
|
|
3406
|
+
*/
|
|
3407
|
+
static get isWasmBackendActive() {
|
|
3408
|
+
return isSgp4WasmBackendActive();
|
|
3409
|
+
}
|
|
3410
|
+
/**
|
|
3411
|
+
* Diagnostic counters for the wasm backend: distinct TLEs attached to the
|
|
3412
|
+
* wasm registry vs permanently fallen back to the TypeScript
|
|
3413
|
+
* implementation. A growing fallback count means part of the catalog is
|
|
3414
|
+
* still propagating in TypeScript.
|
|
3415
|
+
*/
|
|
3416
|
+
static get wasmBackendStats() {
|
|
3417
|
+
return getSgp4WasmBackendStats();
|
|
3418
|
+
}
|
|
3237
3419
|
/*
|
|
3238
3420
|
* -----------------------------------------------------------------------------
|
|
3239
3421
|
*
|
|
@@ -3393,6 +3575,7 @@ var Sgp4 = class _Sgp4 {
|
|
|
3393
3575
|
xno: satrec.no,
|
|
3394
3576
|
xnodeo: satrec.nodeo
|
|
3395
3577
|
});
|
|
3578
|
+
stashTleLines(satrec, tleLine1, tleLine2);
|
|
3396
3579
|
return satrec;
|
|
3397
3580
|
}
|
|
3398
3581
|
static createSatrecFromOmm(omm, whichconst = "wgs72" /* wgs72 */, opsmode = "i" /* IMPROVED */) {
|
|
@@ -3910,6 +4093,10 @@ var Sgp4 = class _Sgp4 {
|
|
|
3910
4093
|
*----------------------------------------------------------------------------
|
|
3911
4094
|
*/
|
|
3912
4095
|
static propagate(satrec, tsince) {
|
|
4096
|
+
const wasmResult = tryPropagateWasm(satrec, tsince);
|
|
4097
|
+
if (wasmResult !== null) {
|
|
4098
|
+
return wasmResult;
|
|
4099
|
+
}
|
|
3913
4100
|
const { xke, j2, j3oj2, vkmpersec } = satrec;
|
|
3914
4101
|
satrec.t = tsince;
|
|
3915
4102
|
satrec.error = 0 /* NO_ERROR */;
|
|
@@ -6320,7 +6507,7 @@ function uv2azel(u, v, coneHalfAngle) {
|
|
|
6320
6507
|
throw new ValidationError("v must be between -1 and 1", "v", v);
|
|
6321
6508
|
}
|
|
6322
6509
|
const alpha = Math.asin(u);
|
|
6323
|
-
const beta = Math.asin(v);
|
|
6510
|
+
const beta = Math.asin(0 - v);
|
|
6324
6511
|
const az = alpha / 90 * (coneHalfAngle * RAD2DEG);
|
|
6325
6512
|
const el = beta / 90 * (coneHalfAngle * RAD2DEG);
|
|
6326
6513
|
return { az, el };
|
|
@@ -6354,6 +6541,8 @@ var StateCovariance = class _StateCovariance {
|
|
|
6354
6541
|
this.matrix = matrix;
|
|
6355
6542
|
this.frame = frame;
|
|
6356
6543
|
}
|
|
6544
|
+
matrix;
|
|
6545
|
+
frame;
|
|
6357
6546
|
// / Create a new [StateCovariance] object from 1-sigma values.
|
|
6358
6547
|
static fromSigmas(sigmas, frame) {
|
|
6359
6548
|
const n = sigmas.length;
|
|
@@ -6556,7 +6745,7 @@ var ClassicalElements = class _ClassicalElements {
|
|
|
6556
6745
|
}
|
|
6557
6746
|
const pos = state.position;
|
|
6558
6747
|
const vel = state.velocity;
|
|
6559
|
-
const a =
|
|
6748
|
+
const a = 1 / (2 / pos.magnitude() - vel.magnitude() ** 2 / mu);
|
|
6560
6749
|
const eVecA = pos.scale(vel.magnitude() ** 2 - mu / pos.magnitude());
|
|
6561
6750
|
const eVecB = vel.scale(pos.dot(vel));
|
|
6562
6751
|
const eVec = eVecA.subtract(eVecB).scale(1 / mu);
|
|
@@ -6685,7 +6874,7 @@ var ClassicalElements = class _ClassicalElements {
|
|
|
6685
6874
|
*/
|
|
6686
6875
|
getOrbitRegime() {
|
|
6687
6876
|
const n = this.revsPerDay;
|
|
6688
|
-
const p = this.period
|
|
6877
|
+
const p = this.period;
|
|
6689
6878
|
if (n >= 0.99 && n <= 1.01 && this.eccentricity < 0.01) {
|
|
6690
6879
|
return "Geosynchronous Orbit" /* GEO */;
|
|
6691
6880
|
}
|
|
@@ -6753,13 +6942,14 @@ var ClassicalElements = class _ClassicalElements {
|
|
|
6753
6942
|
let maInit = eaInit - this.eccentricity * Math.sin(eaInit);
|
|
6754
6943
|
maInit = matchHalfPlane(maInit, eaInit);
|
|
6755
6944
|
const maFinal = (maInit + n * delta) % TAU;
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6945
|
+
const ecc = this.eccentricity;
|
|
6946
|
+
let eaFinal = ecc > 0.8 ? Math.PI * Math.sign(maFinal || 1) : maFinal;
|
|
6947
|
+
for (let iter = 0; iter < 50; iter++) {
|
|
6948
|
+
const step = (eaFinal - ecc * Math.sin(eaFinal) - maFinal) / (1 - ecc * Math.cos(eaFinal));
|
|
6949
|
+
eaFinal -= step;
|
|
6950
|
+
if (Math.abs(step) < 1e-12) {
|
|
6760
6951
|
break;
|
|
6761
6952
|
}
|
|
6762
|
-
eaFinal = eaTemp;
|
|
6763
6953
|
}
|
|
6764
6954
|
const cosEaFinal = Math.cos(eaFinal);
|
|
6765
6955
|
let vFinal = Math.acos(clamp((cosEaFinal - this.eccentricity) / (1 - this.eccentricity * cosEaFinal), -1, 1));
|
|
@@ -7189,13 +7379,21 @@ var GroundObject = class extends BaseObject {
|
|
|
7189
7379
|
}
|
|
7190
7380
|
/**
|
|
7191
7381
|
* Calculates the Earth-Centered Inertial (ECI) position vector of the ground object at a given date.
|
|
7192
|
-
*
|
|
7382
|
+
*
|
|
7383
|
+
* Uses the WGS84 ellipsoid (the same {@link llaRad2ecef} position that `ecef()` and
|
|
7384
|
+
* `Satellite.rae()` use), rotated into TEME by GMST. It deliberately does not use the
|
|
7385
|
+
* spherical-Earth `lla2eci()`: at mid latitudes that puts the observer ~20 km away from
|
|
7386
|
+
* its WGS84 position, which skewed `Satellite.dopplerFactor()` by up to ~0.2 km/s of range
|
|
7387
|
+
* rate near closest approach.
|
|
7388
|
+
*
|
|
7389
|
+
* The result is in TEME (GMST rotation only), the frame `Satellite.eci()` returns. For J2000
|
|
7390
|
+
* use {@link toJ2000}, which also applies precession and nutation.
|
|
7193
7391
|
* @param date The date for which to calculate the ECI position vector. Defaults to the current date.
|
|
7194
7392
|
* @returns The ECI position vector of the ground object.
|
|
7195
7393
|
*/
|
|
7196
7394
|
eci(date = /* @__PURE__ */ new Date()) {
|
|
7197
7395
|
const { gmst } = calcGmst(date);
|
|
7198
|
-
return
|
|
7396
|
+
return ecef2eci(llaRad2ecef(this.llaRad()), gmst);
|
|
7199
7397
|
}
|
|
7200
7398
|
/**
|
|
7201
7399
|
* Returns the latitude, longitude, and altitude of the GroundObject.
|
|
@@ -7236,18 +7434,16 @@ var GroundObject = class extends BaseObject {
|
|
|
7236
7434
|
}
|
|
7237
7435
|
/**
|
|
7238
7436
|
* Converts the ground position to J2000 inertial coordinates.
|
|
7239
|
-
*
|
|
7437
|
+
*
|
|
7438
|
+
* The site is placed on the WGS84 ellipsoid in ITRF and rotated into J2000 with the full
|
|
7439
|
+
* precession, nutation and sidereal-time chain ({@link ITRF.toJ2000}), so it is in the same
|
|
7440
|
+
* frame as `Satellite.toJ2000()`. The velocity is the Earth-rotation velocity (omega x r,
|
|
7441
|
+
* ~0.46 km/s at the equator): a ground site is fixed in ITRF, not in inertial space.
|
|
7240
7442
|
* @param date - The date for the conversion (defaults to now)
|
|
7241
7443
|
* @returns J2000 state vector
|
|
7242
7444
|
*/
|
|
7243
7445
|
toJ2000(date = /* @__PURE__ */ new Date()) {
|
|
7244
|
-
|
|
7245
|
-
const position = lla2eci(this.llaRad(), gmst);
|
|
7246
|
-
return new J2000(
|
|
7247
|
-
EpochUTC.fromDateTime(date),
|
|
7248
|
-
new Vector3D(position.x, position.y, position.z),
|
|
7249
|
-
new Vector3D(0, 0, 0)
|
|
7250
|
-
);
|
|
7446
|
+
return this.toGeodetic().toITRF(EpochUTC.fromDateTime(date)).toJ2000();
|
|
7251
7447
|
}
|
|
7252
7448
|
// ==================== Component Management ====================
|
|
7253
7449
|
/**
|
|
@@ -7794,7 +7990,7 @@ var TEME = class _TEME extends StateVector {
|
|
|
7794
7990
|
}
|
|
7795
7991
|
};
|
|
7796
7992
|
|
|
7797
|
-
// node_modules/astronomy-engine/esm/astronomy.js
|
|
7993
|
+
// node_modules/.pnpm/astronomy-engine@2.1.19/node_modules/astronomy-engine/esm/astronomy.js
|
|
7798
7994
|
var C_AUDAY = 173.1446326846693;
|
|
7799
7995
|
var KM_PER_AU2 = 14959787069098932e-8;
|
|
7800
7996
|
var DEG2RAD2 = 0.017453292519943295;
|
|
@@ -9861,7 +10057,7 @@ function CalcPluto(time, helio) {
|
|
|
9861
10057
|
r = ra.mul(1 - ramp).add(rb.mul(ramp));
|
|
9862
10058
|
v = va.mul(1 - ramp).add(vb.mul(ramp));
|
|
9863
10059
|
}
|
|
9864
|
-
|
|
10060
|
+
{
|
|
9865
10061
|
if (!bary)
|
|
9866
10062
|
bary = new major_bodies_t(time.tt);
|
|
9867
10063
|
r = r.sub(bary.Sun.r);
|
|
@@ -9874,7 +10070,7 @@ function HelioVector(body, date) {
|
|
|
9874
10070
|
if (body in vsop)
|
|
9875
10071
|
return CalcVsop(vsop[body], time);
|
|
9876
10072
|
if (body === Body.Pluto) {
|
|
9877
|
-
const p = CalcPluto(time
|
|
10073
|
+
const p = CalcPluto(time);
|
|
9878
10074
|
return new Vector2(p.x, p.y, p.z, time);
|
|
9879
10075
|
}
|
|
9880
10076
|
if (body === Body.Sun)
|
|
@@ -9970,38 +10166,6 @@ function GeoVector(body, date, aberration) {
|
|
|
9970
10166
|
function ExportState(terse, time) {
|
|
9971
10167
|
return new StateVector2(terse.r.x, terse.r.y, terse.r.z, terse.v.x, terse.v.y, terse.v.z, time);
|
|
9972
10168
|
}
|
|
9973
|
-
function BaryState(body, date) {
|
|
9974
|
-
const time = MakeTime(date);
|
|
9975
|
-
if (body === Body.SSB) {
|
|
9976
|
-
return new StateVector2(0, 0, 0, 0, 0, 0, time);
|
|
9977
|
-
}
|
|
9978
|
-
if (body === Body.Pluto) {
|
|
9979
|
-
return CalcPluto(time, false);
|
|
9980
|
-
}
|
|
9981
|
-
const bary = new major_bodies_t(time.tt);
|
|
9982
|
-
switch (body) {
|
|
9983
|
-
case Body.Sun:
|
|
9984
|
-
return ExportState(bary.Sun, time);
|
|
9985
|
-
case Body.Jupiter:
|
|
9986
|
-
return ExportState(bary.Jupiter, time);
|
|
9987
|
-
case Body.Saturn:
|
|
9988
|
-
return ExportState(bary.Saturn, time);
|
|
9989
|
-
case Body.Uranus:
|
|
9990
|
-
return ExportState(bary.Uranus, time);
|
|
9991
|
-
case Body.Neptune:
|
|
9992
|
-
return ExportState(bary.Neptune, time);
|
|
9993
|
-
case Body.Moon:
|
|
9994
|
-
case Body.EMB:
|
|
9995
|
-
const earth = CalcVsopPosVel(vsop[Body.Earth], time.tt);
|
|
9996
|
-
const state = body === Body.Moon ? GeoMoonState(time) : GeoEmbState(time);
|
|
9997
|
-
return new StateVector2(state.x + bary.Sun.r.x + earth.r.x, state.y + bary.Sun.r.y + earth.r.y, state.z + bary.Sun.r.z + earth.r.z, state.vx + bary.Sun.v.x + earth.v.x, state.vy + bary.Sun.v.y + earth.v.y, state.vz + bary.Sun.v.z + earth.v.z, time);
|
|
9998
|
-
}
|
|
9999
|
-
if (body in vsop) {
|
|
10000
|
-
const planet = CalcVsopPosVel(vsop[body], time.tt);
|
|
10001
|
-
return new StateVector2(bary.Sun.r.x + planet.r.x, bary.Sun.r.y + planet.r.y, bary.Sun.r.z + planet.r.z, bary.Sun.v.x + planet.v.x, bary.Sun.v.y + planet.v.y, bary.Sun.v.z + planet.v.z, time);
|
|
10002
|
-
}
|
|
10003
|
-
throw `BaryState: Unsupported body "${body}"`;
|
|
10004
|
-
}
|
|
10005
10169
|
function HelioState(body, date) {
|
|
10006
10170
|
const time = MakeTime(date);
|
|
10007
10171
|
switch (body) {
|
|
@@ -10021,7 +10185,7 @@ function HelioState(body, date) {
|
|
|
10021
10185
|
const planet = CalcVsopPosVel(vsop[body], time.tt);
|
|
10022
10186
|
return ExportState(planet, time);
|
|
10023
10187
|
case Body.Pluto:
|
|
10024
|
-
return CalcPluto(time
|
|
10188
|
+
return CalcPluto(time);
|
|
10025
10189
|
case Body.Moon:
|
|
10026
10190
|
case Body.EMB:
|
|
10027
10191
|
const earth = CalcVsopPosVel(vsop.Earth, time.tt);
|
|
@@ -10342,6 +10506,11 @@ function SearchRiseSet(body, observer, direction, dateStart, limitDays, metersAb
|
|
|
10342
10506
|
const altitude = dip - REFRACTION_NEAR_HORIZON * atmos.density;
|
|
10343
10507
|
return InternalSearchAltitude(body, observer, direction, dateStart, limitDays, body_radius_au, altitude);
|
|
10344
10508
|
}
|
|
10509
|
+
function SearchAltitude(body, observer, direction, dateStart, limitDays, altitude) {
|
|
10510
|
+
if (!Number.isFinite(altitude) || altitude < -90 || altitude > 90)
|
|
10511
|
+
throw `Invalid altitude angle: ${altitude}`;
|
|
10512
|
+
return InternalSearchAltitude(body, observer, direction, dateStart, limitDays, 0, altitude);
|
|
10513
|
+
}
|
|
10345
10514
|
var AscentInfo = class {
|
|
10346
10515
|
constructor(tx, ty, ax, ay) {
|
|
10347
10516
|
this.tx = tx;
|
|
@@ -10604,7 +10773,9 @@ var CelestialBody = class extends BaseObject {
|
|
|
10604
10773
|
* Gets rise, transit, and set times for this body as seen from a ground location.
|
|
10605
10774
|
* @param observer - The ground observer location
|
|
10606
10775
|
* @param date - The starting date for the search
|
|
10607
|
-
* @param minElevation -
|
|
10776
|
+
* @param minElevation - Elevation threshold in degrees (default 0). At 0 the times are the
|
|
10777
|
+
* usual rise/set (upper limb on the horizon, with refraction); otherwise they are when the
|
|
10778
|
+
* body's centre crosses that geometric elevation.
|
|
10608
10779
|
* @returns Rise, transit, and set times (null if body doesn't rise/set)
|
|
10609
10780
|
*/
|
|
10610
10781
|
getRiseSetTimes(observer, date = /* @__PURE__ */ new Date(), minElevation = 0) {
|
|
@@ -10613,8 +10784,10 @@ var CelestialBody = class extends BaseObject {
|
|
|
10613
10784
|
}
|
|
10614
10785
|
const obs = new Observer(observer.lat, observer.lon, observer.alt * 1e3);
|
|
10615
10786
|
const time = MakeTime(date);
|
|
10616
|
-
const
|
|
10617
|
-
const
|
|
10787
|
+
const body = this.astronomyBody_;
|
|
10788
|
+
const search = (direction) => minElevation === 0 ? SearchRiseSet(body, obs, direction, time, 1) : SearchAltitude(body, obs, direction, time, 1, minElevation);
|
|
10789
|
+
const riseTime = search(1);
|
|
10790
|
+
const setTime = search(-1);
|
|
10618
10791
|
let transitTime = null;
|
|
10619
10792
|
if (riseTime && setTime) {
|
|
10620
10793
|
const riseMs = riseTime.date.getTime();
|
|
@@ -10633,14 +10806,17 @@ var CelestialBody = class extends BaseObject {
|
|
|
10633
10806
|
}
|
|
10634
10807
|
/**
|
|
10635
10808
|
* Calculates the angular diameter of this body as seen from a given position.
|
|
10636
|
-
* @param observerPos - The observer's position in km
|
|
10809
|
+
* @param observerPos - The observer's position in km (J2000, at `date`)
|
|
10810
|
+
* @param date - The time of `observerPos` (defaults to now). The body is placed at this
|
|
10811
|
+
* time; without it a past or future observer position was paired with the body's
|
|
10812
|
+
* position now.
|
|
10637
10813
|
* @returns Angular diameter in radians
|
|
10638
10814
|
*/
|
|
10639
|
-
getAngularDiameter(observerPos) {
|
|
10815
|
+
getAngularDiameter(observerPos, date = /* @__PURE__ */ new Date()) {
|
|
10640
10816
|
if (!this.radius) {
|
|
10641
10817
|
throw new Error(`${this.name} does not have a defined radius`);
|
|
10642
10818
|
}
|
|
10643
|
-
const distance = observerPos.subtract(this.eci()).magnitude();
|
|
10819
|
+
const distance = observerPos.subtract(this.eci(date)).magnitude();
|
|
10644
10820
|
return 2 * Math.atan(this.radius / distance);
|
|
10645
10821
|
}
|
|
10646
10822
|
/**
|
|
@@ -10781,16 +10957,16 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
10781
10957
|
return this.geoVectorToKm(Body.Sun, date);
|
|
10782
10958
|
}
|
|
10783
10959
|
/**
|
|
10784
|
-
* Gets the Sun's apparent position (corrected for light travel time).
|
|
10960
|
+
* Gets the Sun's apparent position (corrected for light travel time and aberration).
|
|
10961
|
+
*
|
|
10962
|
+
* Same as {@link eci}: astronomy-engine's `GeoVector` already applies the light-time and
|
|
10963
|
+
* aberration corrections. (This used to roll the time back by another ~499 s, putting the
|
|
10964
|
+
* Sun a further ~20 arcsec along its path.)
|
|
10785
10965
|
* @param date - The date/time for the position calculation
|
|
10786
|
-
* @returns Position vector in kilometers
|
|
10966
|
+
* @returns Position vector in kilometers (J2000)
|
|
10787
10967
|
*/
|
|
10788
10968
|
eciApparent(date = /* @__PURE__ */ new Date()) {
|
|
10789
|
-
|
|
10790
|
-
const distance = pos.magnitude();
|
|
10791
|
-
const lightTimeSec = distance / cKmPerSec;
|
|
10792
|
-
const adjustedDate = new Date(date.getTime() - lightTimeSec * 1e3);
|
|
10793
|
-
return this.eci(adjustedDate);
|
|
10969
|
+
return this.eci(date);
|
|
10794
10970
|
}
|
|
10795
10971
|
/**
|
|
10796
10972
|
* Gets the Sun's position in heliocentric coordinates.
|
|
@@ -10855,8 +11031,13 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
10855
11031
|
// ==================== Satellite Shadow Methods ====================
|
|
10856
11032
|
/**
|
|
10857
11033
|
* Determines if a satellite is in Earth's shadow.
|
|
11034
|
+
*
|
|
11035
|
+
* The test is a cylinder-plus-cone that tracks the umbra boundary closely (within ~0.01 deg
|
|
11036
|
+
* of the `lightingRatio` umbra edge in LEO); a satellite in the penumbra counts as lit. Use
|
|
11037
|
+
* {@link lightingRatio} when partial illumination matters.
|
|
10858
11038
|
* @param epoch - The epoch for the calculation
|
|
10859
|
-
* @param satPos - The satellite's
|
|
11039
|
+
* @param satPos - The satellite's position in J2000 (the frame of `eci()`), km; a TEME
|
|
11040
|
+
* position from `Satellite.eci()` is ~0.3 deg off in 2026
|
|
10860
11041
|
* @example
|
|
10861
11042
|
* ```typescript
|
|
10862
11043
|
* import { Sun, Satellite, EpochUTC, Vector3D, Kilometers, Seconds } from 'ootk';
|
|
@@ -10865,14 +11046,8 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
10865
11046
|
* const now = new Date();
|
|
10866
11047
|
* const epoch = new EpochUTC((now.getTime() / 1000) as Seconds);
|
|
10867
11048
|
*
|
|
10868
|
-
* const
|
|
10869
|
-
*
|
|
10870
|
-
* const satPos = new Vector3D<Kilometers>(
|
|
10871
|
-
* pv.position.x as Kilometers,
|
|
10872
|
-
* pv.position.y as Kilometers,
|
|
10873
|
-
* pv.position.z as Kilometers
|
|
10874
|
-
* );
|
|
10875
|
-
*
|
|
11049
|
+
* const satPos = satellite.toJ2000(now).position; // J2000, like Sun.eci()
|
|
11050
|
+
* {
|
|
10876
11051
|
* const inShadow = Sun.shadow(epoch, satPos);
|
|
10877
11052
|
* console.log(inShadow ? 'Satellite is in eclipse' : 'Satellite is sunlit');
|
|
10878
11053
|
*
|
|
@@ -10948,11 +11123,12 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
10948
11123
|
}
|
|
10949
11124
|
/**
|
|
10950
11125
|
* Calculates the Sun's angular diameter as seen from a position.
|
|
10951
|
-
* @param obsPos - Observer position in kilometers
|
|
11126
|
+
* @param obsPos - Observer position in kilometers (J2000, at `date`)
|
|
11127
|
+
* @param date - The time of `obsPos` (defaults to now)
|
|
10952
11128
|
* @returns Angular diameter in radians
|
|
10953
11129
|
*/
|
|
10954
|
-
diameter(obsPos) {
|
|
10955
|
-
const sunPos = this.eci();
|
|
11130
|
+
diameter(obsPos, date = /* @__PURE__ */ new Date()) {
|
|
11131
|
+
const sunPos = this.eci(date);
|
|
10956
11132
|
const distance = obsPos.subtract(sunPos).magnitude();
|
|
10957
11133
|
return 2 * Math.asin(_SunBody.RADIUS / distance);
|
|
10958
11134
|
}
|
|
@@ -10997,7 +11173,7 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
10997
11173
|
if (isNaN(lon)) {
|
|
10998
11174
|
throw new Error("longitude missing");
|
|
10999
11175
|
}
|
|
11000
|
-
const date = dateVal instanceof Date ? dateVal :
|
|
11176
|
+
const date = new Date(dateVal instanceof Date ? dateVal.getTime() : dateVal);
|
|
11001
11177
|
if (isUtc) {
|
|
11002
11178
|
date.setUTCHours(12, 0, 0, 0);
|
|
11003
11179
|
} else {
|
|
@@ -11021,15 +11197,19 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
11021
11197
|
}
|
|
11022
11198
|
/**
|
|
11023
11199
|
* Gets sunrise and sunset times using astronomy-engine.
|
|
11200
|
+
*
|
|
11201
|
+
* The times are when the Sun's centre crosses `minElevation` (geometric altitude). The
|
|
11202
|
+
* default -0.833 deg is the standard sunrise/sunset definition (refraction plus the solar
|
|
11203
|
+
* semidiameter); -6, -12 and -18 give civil, nautical and astronomical twilight.
|
|
11024
11204
|
* @param observer - Ground observer location
|
|
11025
11205
|
* @param date - The date for calculation
|
|
11026
|
-
* @param minElevation -
|
|
11206
|
+
* @param minElevation - Elevation of the Sun's centre in degrees (default -0.833 for standard sunrise)
|
|
11027
11207
|
*/
|
|
11028
11208
|
getSunriseSunset(observer, date = /* @__PURE__ */ new Date(), minElevation = -0.833) {
|
|
11029
11209
|
const obs = new Observer(observer.lat, observer.lon, observer.alt * 1e3);
|
|
11030
11210
|
const time = MakeTime(date);
|
|
11031
|
-
const sunrise =
|
|
11032
|
-
const sunset =
|
|
11211
|
+
const sunrise = SearchAltitude(Body.Sun, obs, 1, time, 1, minElevation);
|
|
11212
|
+
const sunset = SearchAltitude(Body.Sun, obs, -1, time, 1, minElevation);
|
|
11033
11213
|
return {
|
|
11034
11214
|
sunrise: sunrise?.date ?? null,
|
|
11035
11215
|
sunset: sunset?.date ?? null
|
|
@@ -11308,6 +11488,9 @@ var SolarRadiationPressure = class _SolarRadiationPressure extends Force {
|
|
|
11308
11488
|
this.area = area;
|
|
11309
11489
|
this.reflectCoeff = reflectCoeff;
|
|
11310
11490
|
}
|
|
11491
|
+
mass;
|
|
11492
|
+
area;
|
|
11493
|
+
reflectCoeff;
|
|
11311
11494
|
// / Solar pressure _(N/m²)_;
|
|
11312
11495
|
static _kRef = 456e-8 * KM_PER_AU ** 2;
|
|
11313
11496
|
acceleration(state) {
|
|
@@ -11542,7 +11725,7 @@ var MoonBody = class _MoonBody extends CelestialBody {
|
|
|
11542
11725
|
if (!phase) {
|
|
11543
11726
|
phase = _MoonBody.moonCycles_[0];
|
|
11544
11727
|
}
|
|
11545
|
-
let type
|
|
11728
|
+
let type;
|
|
11546
11729
|
if (next === nextNewMoon) {
|
|
11547
11730
|
type = "newMoon";
|
|
11548
11731
|
} else if (next === nextFirstQuarter) {
|
|
@@ -11690,6 +11873,8 @@ var ThirdBodyGravity = class _ThirdBodyGravity {
|
|
|
11690
11873
|
this.moon = moon;
|
|
11691
11874
|
this.sun = sun;
|
|
11692
11875
|
}
|
|
11876
|
+
moon;
|
|
11877
|
+
sun;
|
|
11693
11878
|
static moonGravity_(state) {
|
|
11694
11879
|
const rMoon = Moon.eci(state.epoch.toDateTime());
|
|
11695
11880
|
const aNum = rMoon.subtract(state.position);
|
|
@@ -12096,29 +12281,40 @@ var FormatTle = class _FormatTle {
|
|
|
12096
12281
|
* Format a value in TLE exponential notation for BSTAR or mean motion ddot.
|
|
12097
12282
|
* Format: "sNNNNN±N" (8 chars) where mantissa has implied leading decimal point.
|
|
12098
12283
|
* Example: 0.00017507 → " 17507-3" (i.e., .17507 × 10^-3)
|
|
12284
|
+
*
|
|
12285
|
+
* The result is ALWAYS exactly 8 characters. The field has room for a 5-digit
|
|
12286
|
+
* mantissa and a single-digit exponent, so values needing more are folded into
|
|
12287
|
+
* what the format can express: magnitudes below 1e-10 become zero (the usual
|
|
12288
|
+
* convention for a drag term too small to write), and the 1e9-and-up case is
|
|
12289
|
+
* saturated. Anything longer would push every later column of TLE line 1 to the
|
|
12290
|
+
* right, which silently corrupts ephemeris type, element number and checksum.
|
|
12099
12291
|
* @param value - The value to format
|
|
12100
12292
|
* @returns Formatted 8-character string
|
|
12101
12293
|
*/
|
|
12102
12294
|
static formatTleExponential(value) {
|
|
12103
|
-
if (value === 0) {
|
|
12295
|
+
if (value === 0 || !Number.isFinite(value)) {
|
|
12104
12296
|
return " 00000+0";
|
|
12105
12297
|
}
|
|
12106
12298
|
const sign2 = value >= 0 ? " " : "-";
|
|
12107
12299
|
const absVal = Math.abs(value);
|
|
12108
12300
|
let exponent = Math.floor(Math.log10(absVal)) + 1;
|
|
12109
|
-
let mantissa = absVal / 10 ** exponent;
|
|
12110
|
-
|
|
12111
|
-
|
|
12301
|
+
let mantissa = Math.round(absVal / 10 ** exponent * 1e5);
|
|
12302
|
+
while (mantissa < 1e4) {
|
|
12303
|
+
exponent--;
|
|
12304
|
+
mantissa = Math.round(absVal / 10 ** exponent * 1e5);
|
|
12305
|
+
}
|
|
12306
|
+
while (mantissa >= 1e5) {
|
|
12112
12307
|
exponent++;
|
|
12308
|
+
mantissa = Math.round(absVal / 10 ** exponent * 1e5);
|
|
12113
12309
|
}
|
|
12114
|
-
if (
|
|
12115
|
-
|
|
12116
|
-
|
|
12310
|
+
if (exponent < -9) {
|
|
12311
|
+
return " 00000+0";
|
|
12312
|
+
}
|
|
12313
|
+
if (exponent > 9) {
|
|
12314
|
+
return `${sign2}99999+9`;
|
|
12117
12315
|
}
|
|
12118
|
-
const mantissaStr = Math.round(mantissa * 1e5).toString().padStart(5, "0");
|
|
12119
12316
|
const expSign = exponent >= 0 ? "+" : "-";
|
|
12120
|
-
|
|
12121
|
-
return sign2 + mantissaStr + expSign + expStr;
|
|
12317
|
+
return sign2 + mantissa.toString() + expSign + Math.abs(exponent).toString();
|
|
12122
12318
|
}
|
|
12123
12319
|
/**
|
|
12124
12320
|
* Compute TLE line checksum (modulo 10 sum of digits, '-' counts as 1).
|
|
@@ -12295,20 +12491,8 @@ ${this.line2}`;
|
|
|
12295
12491
|
}
|
|
12296
12492
|
static calcElsetAge(tle1, nowInput, outputUnits = "days") {
|
|
12297
12493
|
nowInput ??= /* @__PURE__ */ new Date();
|
|
12298
|
-
const
|
|
12299
|
-
const
|
|
12300
|
-
const epochYearShort = parseInt(tle1.substring(18, 20), 10);
|
|
12301
|
-
const epochDayOfYear = parseFloat(tle1.substring(20, 32));
|
|
12302
|
-
let epochYearFull;
|
|
12303
|
-
if (epochYearShort <= currentYearShort) {
|
|
12304
|
-
epochYearFull = 2e3 + epochYearShort;
|
|
12305
|
-
} else {
|
|
12306
|
-
epochYearFull = 1900 + epochYearShort;
|
|
12307
|
-
}
|
|
12308
|
-
const epochJday = epochDayOfYear + epochYearFull * 365;
|
|
12309
|
-
const currentJday = getDayOfYear(nowInput) + currentYearFull * 365;
|
|
12310
|
-
const currentTime = (nowInput.getUTCHours() * 3600 + nowInput.getUTCMinutes() * 60 + nowInput.getUTCSeconds()) / 86400;
|
|
12311
|
-
const daysOld = currentJday + currentTime - epochJday;
|
|
12494
|
+
const epochMs = _Tle.parseEpoch_(tle1.substring(18, 32)).toDateTime().getTime();
|
|
12495
|
+
const daysOld = (nowInput.getTime() - epochMs) / 864e5;
|
|
12312
12496
|
switch (outputUnits) {
|
|
12313
12497
|
case "hours":
|
|
12314
12498
|
return daysOld * 24;
|
|
@@ -12991,6 +13175,8 @@ var RkCheckpoint = class {
|
|
|
12991
13175
|
this.cacheState = cacheState;
|
|
12992
13176
|
this.stepSize = stepSize;
|
|
12993
13177
|
}
|
|
13178
|
+
cacheState;
|
|
13179
|
+
stepSize;
|
|
12994
13180
|
};
|
|
12995
13181
|
|
|
12996
13182
|
// src/propagator/RkResult.ts
|
|
@@ -13001,6 +13187,9 @@ var RkResult = class {
|
|
|
13001
13187
|
this.error = error;
|
|
13002
13188
|
this.newStep = newStep;
|
|
13003
13189
|
}
|
|
13190
|
+
state;
|
|
13191
|
+
error;
|
|
13192
|
+
newStep;
|
|
13004
13193
|
};
|
|
13005
13194
|
|
|
13006
13195
|
// src/interpolator/CubicSpline.ts
|
|
@@ -13014,6 +13203,12 @@ var CubicSpline = class {
|
|
|
13014
13203
|
this.p1 = p1;
|
|
13015
13204
|
this.m1 = m1;
|
|
13016
13205
|
}
|
|
13206
|
+
t0;
|
|
13207
|
+
p0;
|
|
13208
|
+
m0;
|
|
13209
|
+
t1;
|
|
13210
|
+
p1;
|
|
13211
|
+
m1;
|
|
13017
13212
|
// / Interpolate position at the provided time [t] _(POSIX seconds)_.
|
|
13018
13213
|
position_(t) {
|
|
13019
13214
|
const t2 = t * t;
|
|
@@ -13052,9 +13247,8 @@ var Interpolator = class {
|
|
|
13052
13247
|
* cached value range.
|
|
13053
13248
|
*/
|
|
13054
13249
|
inWindow(epoch) {
|
|
13055
|
-
const
|
|
13056
|
-
|
|
13057
|
-
return start <= epoch && epoch <= stop;
|
|
13250
|
+
const window = this.window();
|
|
13251
|
+
return window.start.posix <= epoch.posix && epoch.posix <= window.end.posix;
|
|
13058
13252
|
}
|
|
13059
13253
|
/*
|
|
13060
13254
|
* Calculate the start/stop epoch between this and another [Interpolator].
|
|
@@ -13062,13 +13256,15 @@ var Interpolator = class {
|
|
|
13062
13256
|
* Returns `null` if there is no overlap between interpolators.
|
|
13063
13257
|
*/
|
|
13064
13258
|
overlap(interpolator) {
|
|
13065
|
-
const
|
|
13066
|
-
const
|
|
13067
|
-
const
|
|
13068
|
-
const
|
|
13259
|
+
const a = this.window();
|
|
13260
|
+
const b = interpolator.window();
|
|
13261
|
+
const x1 = a.start.posix;
|
|
13262
|
+
const x2 = a.end.posix;
|
|
13263
|
+
const y1 = b.start.posix;
|
|
13264
|
+
const y2 = b.end.posix;
|
|
13069
13265
|
if (x1 <= y2 && y1 <= x2) {
|
|
13070
|
-
const e1 = new EpochUTC(Math.max(x1
|
|
13071
|
-
const e2 = new EpochUTC(Math.min(x2
|
|
13266
|
+
const e1 = new EpochUTC(Math.max(x1, y1));
|
|
13267
|
+
const e2 = new EpochUTC(Math.min(x2, y2));
|
|
13072
13268
|
return new EpochWindow(e1, e2);
|
|
13073
13269
|
}
|
|
13074
13270
|
return null;
|
|
@@ -13089,6 +13285,7 @@ var CubicSplineInterpolator = class _CubicSplineInterpolator extends StateInterp
|
|
|
13089
13285
|
super();
|
|
13090
13286
|
this.splines_ = splines_;
|
|
13091
13287
|
}
|
|
13288
|
+
splines_;
|
|
13092
13289
|
static fromEphemeris(ephemeris) {
|
|
13093
13290
|
const splines = [];
|
|
13094
13291
|
for (let i = 0; i < ephemeris.length - 1; i++) {
|
|
@@ -13288,6 +13485,7 @@ var VerletBlendInterpolator = class _VerletBlendInterpolator extends StateInterp
|
|
|
13288
13485
|
super();
|
|
13289
13486
|
this.ephemeris = ephemeris;
|
|
13290
13487
|
}
|
|
13488
|
+
ephemeris;
|
|
13291
13489
|
get sizeBytes() {
|
|
13292
13490
|
return 64 * 7 * this.ephemeris.length / 8;
|
|
13293
13491
|
}
|
|
@@ -13483,7 +13681,7 @@ var Propagator = class {
|
|
|
13483
13681
|
ephemeris(start, stop, interval = 60) {
|
|
13484
13682
|
const output = [this.propagate(start)];
|
|
13485
13683
|
let tempEpoch = start;
|
|
13486
|
-
while (tempEpoch <= stop) {
|
|
13684
|
+
while (tempEpoch.posix <= stop.posix) {
|
|
13487
13685
|
tempEpoch = tempEpoch.roll(interval);
|
|
13488
13686
|
output.push(this.propagate(tempEpoch));
|
|
13489
13687
|
}
|
|
@@ -13657,6 +13855,9 @@ var RungeKuttaAdaptive = class _RungeKuttaAdaptive extends Propagator {
|
|
|
13657
13855
|
this._cacheState = this.initState_;
|
|
13658
13856
|
this.tolerance_ = Math.max(_RungeKuttaAdaptive._minTolerance, Math.abs(tolerance_));
|
|
13659
13857
|
}
|
|
13858
|
+
initState_;
|
|
13859
|
+
forceModel_;
|
|
13860
|
+
tolerance_;
|
|
13660
13861
|
// / Initial state vector.
|
|
13661
13862
|
_cacheState;
|
|
13662
13863
|
_checkpoints = [];
|
|
@@ -13776,7 +13977,7 @@ var RungeKuttaAdaptive = class _RungeKuttaAdaptive extends Propagator {
|
|
|
13776
13977
|
let tState = this.propagate(maneuver.start);
|
|
13777
13978
|
this.forceModel_.loadManeuver(maneuver);
|
|
13778
13979
|
const ephemeris = [tState];
|
|
13779
|
-
while (tState.epoch < maneuver.stop) {
|
|
13980
|
+
while (tState.epoch.posix < maneuver.stop.posix) {
|
|
13780
13981
|
const step = Math.min(maneuver.stop.difference(tState.epoch), interval);
|
|
13781
13982
|
tState = this.propagate(tState.epoch.roll(step));
|
|
13782
13983
|
ephemeris.push(tState);
|
|
@@ -13785,13 +13986,13 @@ var RungeKuttaAdaptive = class _RungeKuttaAdaptive extends Propagator {
|
|
|
13785
13986
|
return ephemeris;
|
|
13786
13987
|
}
|
|
13787
13988
|
ephemerisManeuver(start, finish, maneuvers, interval = 60) {
|
|
13788
|
-
const tMvr = maneuvers.filter((mvr) => mvr.start >= start || mvr.stop <= finish);
|
|
13989
|
+
const tMvr = maneuvers.filter((mvr) => mvr.start.posix >= start.posix || mvr.stop.posix <= finish.posix);
|
|
13789
13990
|
const ephemeris = [];
|
|
13790
|
-
if (tMvr[0].start > start) {
|
|
13991
|
+
if (tMvr[0].start.posix > start.posix) {
|
|
13791
13992
|
ephemeris.push(this.propagate(start));
|
|
13792
13993
|
}
|
|
13793
13994
|
for (const mvr of tMvr) {
|
|
13794
|
-
while (this._cacheState.epoch < mvr.start) {
|
|
13995
|
+
while (this._cacheState.epoch.posix < mvr.start.posix) {
|
|
13795
13996
|
const step = Math.min(mvr.start.difference(this._cacheState.epoch), interval);
|
|
13796
13997
|
this.propagate(this._cacheState.epoch.roll(step));
|
|
13797
13998
|
if (this._cacheState.epoch.posix !== mvr.start.posix) {
|
|
@@ -14390,6 +14591,7 @@ var AccessWindow = class _AccessWindow {
|
|
|
14390
14591
|
};
|
|
14391
14592
|
|
|
14392
14593
|
// src/objects/AccessCalculator.ts
|
|
14594
|
+
var CIVIL_TWILIGHT_DEG = -6;
|
|
14393
14595
|
var AccessCalculator = class _AccessCalculator {
|
|
14394
14596
|
/** Default calculation time step in milliseconds (10 seconds) */
|
|
14395
14597
|
static DEFAULT_STEP_MS_ = 1e4;
|
|
@@ -14530,31 +14732,20 @@ var AccessCalculator = class _AccessCalculator {
|
|
|
14530
14732
|
return false;
|
|
14531
14733
|
}
|
|
14532
14734
|
if (constraints.requireSunlit) {
|
|
14533
|
-
|
|
14534
|
-
|
|
14735
|
+
let satPos;
|
|
14736
|
+
try {
|
|
14737
|
+
satPos = target.toJ2000(date).position;
|
|
14738
|
+
} catch {
|
|
14535
14739
|
return false;
|
|
14536
14740
|
}
|
|
14537
14741
|
const epoch = EpochUTC.fromDateTime(date);
|
|
14538
|
-
const satPos = new Vector3D(
|
|
14539
|
-
eci.position.x,
|
|
14540
|
-
eci.position.y,
|
|
14541
|
-
eci.position.z
|
|
14542
|
-
);
|
|
14543
14742
|
if (Sun.shadow(epoch, satPos)) {
|
|
14544
14743
|
return false;
|
|
14545
14744
|
}
|
|
14546
14745
|
}
|
|
14547
14746
|
if (constraints.requireObserverDark) {
|
|
14548
|
-
|
|
14549
|
-
|
|
14550
|
-
const sunTimes = Sun.getTimes(date, lla.lat, lla.lon, altMeters);
|
|
14551
|
-
const currentTime = date.getTime();
|
|
14552
|
-
const civilDusk = sunTimes.civilDusk?.getTime();
|
|
14553
|
-
const civilDawn = sunTimes.civilDawn?.getTime();
|
|
14554
|
-
if (civilDusk && civilDawn) {
|
|
14555
|
-
if (currentTime >= civilDawn && currentTime <= civilDusk) {
|
|
14556
|
-
return false;
|
|
14557
|
-
}
|
|
14747
|
+
if (Sun.getAzEl(observer, date, false).el >= CIVIL_TWILIGHT_DEG) {
|
|
14748
|
+
return false;
|
|
14558
14749
|
}
|
|
14559
14750
|
}
|
|
14560
14751
|
return true;
|
|
@@ -14734,38 +14925,35 @@ var DynamicGroundObject = class _DynamicGroundObject extends GroundObject {
|
|
|
14734
14925
|
}
|
|
14735
14926
|
/**
|
|
14736
14927
|
* Gets the ECI (Earth-Centered Inertial) position at a specific time.
|
|
14928
|
+
*
|
|
14929
|
+
* The WGS84 position from {@link getEcef} rotated into TEME by GMST, the same model
|
|
14930
|
+
* `GroundObject.eci()` uses (not the spherical-Earth `lla2eci()`).
|
|
14737
14931
|
* @param time - The time to get position for
|
|
14738
|
-
* @returns ECI position vector, or null if time is outside waypoint range
|
|
14932
|
+
* @returns ECI (TEME) position vector, or null if time is outside waypoint range
|
|
14739
14933
|
*/
|
|
14740
14934
|
getEci(time) {
|
|
14741
|
-
const
|
|
14742
|
-
if (!
|
|
14935
|
+
const ecef = this.getEcef(time);
|
|
14936
|
+
if (!ecef) {
|
|
14743
14937
|
return null;
|
|
14744
14938
|
}
|
|
14745
|
-
const llaRad = {
|
|
14746
|
-
lat: lla.lat * DEG2RAD,
|
|
14747
|
-
lon: lla.lon * DEG2RAD,
|
|
14748
|
-
alt: lla.alt
|
|
14749
|
-
};
|
|
14750
14939
|
const { gmst } = calcGmst(time);
|
|
14751
|
-
return
|
|
14940
|
+
return ecef2eci(ecef, gmst);
|
|
14752
14941
|
}
|
|
14753
14942
|
/**
|
|
14754
14943
|
* Converts position at a specific time to J2000 inertial coordinates.
|
|
14755
|
-
*
|
|
14944
|
+
*
|
|
14945
|
+
* The WGS84 position is rotated from ITRF into J2000 with precession, nutation and
|
|
14946
|
+
* sidereal time, as in `GroundObject.toJ2000()`. The velocity is the Earth-rotation
|
|
14947
|
+
* velocity (omega x r); the platform's own motion between waypoints is not included.
|
|
14756
14948
|
* @param time - The time for the conversion
|
|
14757
14949
|
* @returns J2000 state vector, or null if time is outside waypoint range
|
|
14758
14950
|
*/
|
|
14759
14951
|
getJ2000(time) {
|
|
14760
|
-
const
|
|
14761
|
-
if (!
|
|
14952
|
+
const geodetic = this.getGeodetic(time);
|
|
14953
|
+
if (!geodetic) {
|
|
14762
14954
|
return null;
|
|
14763
14955
|
}
|
|
14764
|
-
return
|
|
14765
|
-
EpochUTC.fromDateTime(time),
|
|
14766
|
-
new Vector3D(eci.x, eci.y, eci.z),
|
|
14767
|
-
new Vector3D(0, 0, 0)
|
|
14768
|
-
);
|
|
14956
|
+
return geodetic.toITRF(EpochUTC.fromDateTime(time)).toJ2000();
|
|
14769
14957
|
}
|
|
14770
14958
|
/**
|
|
14771
14959
|
* Converts position at a specific time to Geodetic coordinates.
|
|
@@ -16125,10 +16313,17 @@ var RAE = class _RAE {
|
|
|
16125
16313
|
this.azRateRad = azRateRad;
|
|
16126
16314
|
this.elRateRad = elRateRad;
|
|
16127
16315
|
}
|
|
16316
|
+
epoch;
|
|
16317
|
+
rng;
|
|
16318
|
+
azRad;
|
|
16319
|
+
elRad;
|
|
16320
|
+
rngRate;
|
|
16321
|
+
azRateRad;
|
|
16322
|
+
elRateRad;
|
|
16128
16323
|
// / Create a new [Razel] object, using degrees for the angular values.
|
|
16129
16324
|
static fromDegrees(epoch, range, azimuth, elevation, rangeRate, azimuthRate, elevationRate) {
|
|
16130
|
-
const azimuthRateRad = azimuthRate ? azimuthRate * DEG2RAD : void 0;
|
|
16131
|
-
const elevationRateRad = elevationRate ? elevationRate * DEG2RAD : void 0;
|
|
16325
|
+
const azimuthRateRad = typeof azimuthRate === "number" ? azimuthRate * DEG2RAD : void 0;
|
|
16326
|
+
const elevationRateRad = typeof elevationRate === "number" ? elevationRate * DEG2RAD : void 0;
|
|
16132
16327
|
return new _RAE(
|
|
16133
16328
|
epoch,
|
|
16134
16329
|
range,
|
|
@@ -16201,14 +16396,14 @@ var RAE = class _RAE {
|
|
|
16201
16396
|
* @returns The azimuth rate in degrees per second, or undefined if it is not available.
|
|
16202
16397
|
*/
|
|
16203
16398
|
get azRate() {
|
|
16204
|
-
return this.azRateRad ? this.azRateRad * RAD2DEG : void 0;
|
|
16399
|
+
return typeof this.azRateRad === "number" ? this.azRateRad * RAD2DEG : void 0;
|
|
16205
16400
|
}
|
|
16206
16401
|
/**
|
|
16207
16402
|
* Gets the elevation rate in degrees per second.
|
|
16208
16403
|
* @returns The elevation rate in degrees per second, or undefined if the elevation rate is not set.
|
|
16209
16404
|
*/
|
|
16210
16405
|
get elRate() {
|
|
16211
|
-
return this.elRateRad ? this.elRateRad * RAD2DEG : void 0;
|
|
16406
|
+
return typeof this.elRateRad === "number" ? this.elRateRad * RAD2DEG : void 0;
|
|
16212
16407
|
}
|
|
16213
16408
|
toString() {
|
|
16214
16409
|
return [
|
|
@@ -16380,13 +16575,13 @@ var KeplerPropagator = class extends Propagator {
|
|
|
16380
16575
|
return [this.cacheState_];
|
|
16381
16576
|
}
|
|
16382
16577
|
ephemerisManeuver(start, finish, maneuvers, interval = 60) {
|
|
16383
|
-
const tMvr = maneuvers.slice(0).filter((mvr) => mvr.center >= start
|
|
16578
|
+
const tMvr = maneuvers.slice(0).filter((mvr) => mvr.center.posix >= start.posix && mvr.center.posix <= finish.posix);
|
|
16384
16579
|
const ephemeris = [];
|
|
16385
|
-
if (tMvr[0].start > start) {
|
|
16580
|
+
if (tMvr.length === 0 || tMvr[0].start.posix > start.posix) {
|
|
16386
16581
|
ephemeris.push(this.propagate(start));
|
|
16387
16582
|
}
|
|
16388
16583
|
for (const mvr of tMvr) {
|
|
16389
|
-
while (this.cacheState_.epoch < mvr.center) {
|
|
16584
|
+
while (this.cacheState_.epoch.posix < mvr.center.posix) {
|
|
16390
16585
|
const step = Math.min(mvr.center.difference(this.cacheState_.epoch), interval);
|
|
16391
16586
|
this.propagate(this.cacheState_.epoch.roll(step));
|
|
16392
16587
|
if (this.cacheState_.epoch.posix !== mvr.center.posix) {
|
|
@@ -16395,7 +16590,7 @@ var KeplerPropagator = class extends Propagator {
|
|
|
16395
16590
|
}
|
|
16396
16591
|
ephemeris.push(...this.maneuver(mvr));
|
|
16397
16592
|
}
|
|
16398
|
-
while (this.cacheState_.epoch < finish) {
|
|
16593
|
+
while (this.cacheState_.epoch.posix < finish.posix) {
|
|
16399
16594
|
const step = Math.min(finish.difference(this.cacheState_.epoch), interval);
|
|
16400
16595
|
this.propagate(this.cacheState_.epoch.roll(step));
|
|
16401
16596
|
ephemeris.push(this.cacheState_);
|
|
@@ -16411,6 +16606,7 @@ var KeplerPropagator = class extends Propagator {
|
|
|
16411
16606
|
}
|
|
16412
16607
|
restore(index) {
|
|
16413
16608
|
this.cacheState_ = this.checkpoints_[index];
|
|
16609
|
+
this.elements_ = this.cacheState_.toClassicalElements();
|
|
16414
16610
|
}
|
|
16415
16611
|
};
|
|
16416
16612
|
|
|
@@ -16434,6 +16630,11 @@ var RungeKutta4Propagator = class extends Propagator {
|
|
|
16434
16630
|
this.checkpoints_ = checkpoints_;
|
|
16435
16631
|
this.stepSize_ = Math.abs(stepSize_);
|
|
16436
16632
|
}
|
|
16633
|
+
initState_;
|
|
16634
|
+
forceModel_;
|
|
16635
|
+
stepSize_;
|
|
16636
|
+
cacheState_;
|
|
16637
|
+
checkpoints_;
|
|
16437
16638
|
// / Set the integrator step size to the provided number of [seconds].
|
|
16438
16639
|
setStepSize(seconds) {
|
|
16439
16640
|
this.stepSize_ = Math.abs(seconds);
|
|
@@ -16443,13 +16644,13 @@ var RungeKutta4Propagator = class extends Propagator {
|
|
|
16443
16644
|
this.forceModel_ = forceModel;
|
|
16444
16645
|
}
|
|
16445
16646
|
ephemerisManeuver(start, finish, maneuvers, interval = 60) {
|
|
16446
|
-
const tMvr = maneuvers.slice(0).filter((mvr) => mvr.start <= finish && mvr.stop >= start);
|
|
16647
|
+
const tMvr = maneuvers.slice(0).filter((mvr) => mvr.start.posix <= finish.posix && mvr.stop.posix >= start.posix);
|
|
16447
16648
|
const ephemeris = [];
|
|
16448
|
-
if (tMvr.length === 0 || tMvr[0].start > start) {
|
|
16649
|
+
if (tMvr.length === 0 || tMvr[0].start.posix > start.posix) {
|
|
16449
16650
|
ephemeris.push(this.propagate(start));
|
|
16450
16651
|
}
|
|
16451
16652
|
for (const mvr of tMvr) {
|
|
16452
|
-
while (this.cacheState_.epoch < mvr.start) {
|
|
16653
|
+
while (this.cacheState_.epoch.posix < mvr.start.posix) {
|
|
16453
16654
|
const step = Math.min(mvr.start.difference(this.cacheState_.epoch), interval);
|
|
16454
16655
|
this.propagate(this.cacheState_.epoch.roll(step));
|
|
16455
16656
|
if (this.cacheState_.epoch.posix !== mvr.start.posix) {
|
|
@@ -16473,7 +16674,7 @@ var RungeKutta4Propagator = class extends Propagator {
|
|
|
16473
16674
|
let tState = this.propagate(maneuver.start);
|
|
16474
16675
|
this.forceModel_.loadManeuver(maneuver);
|
|
16475
16676
|
const ephemeris = [tState];
|
|
16476
|
-
while (tState.epoch < maneuver.stop) {
|
|
16677
|
+
while (tState.epoch.posix < maneuver.stop.posix) {
|
|
16477
16678
|
const step = Math.min(maneuver.stop.difference(tState.epoch), interval);
|
|
16478
16679
|
tState = this.propagate(tState.epoch.roll(step));
|
|
16479
16680
|
ephemeris.push(tState);
|
|
@@ -16545,6 +16746,7 @@ var Sgp4Propagator = class extends Propagator {
|
|
|
16545
16746
|
this.tle_ = tle_;
|
|
16546
16747
|
this.cacheState_ = tle_.state.toJ2000();
|
|
16547
16748
|
}
|
|
16749
|
+
tle_;
|
|
16548
16750
|
cacheState_;
|
|
16549
16751
|
checkpoints_ = [];
|
|
16550
16752
|
/**
|
|
@@ -17028,8 +17230,13 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
17028
17230
|
return rae.az;
|
|
17029
17231
|
}
|
|
17030
17232
|
/**
|
|
17031
|
-
* Calculates the RAE (Range, Azimuth, Elevation) values for a given sensor
|
|
17032
|
-
* current time is used.
|
|
17233
|
+
* Calculates the RAE (Range, Azimuth, Elevation) values and their rates for a given sensor
|
|
17234
|
+
* and date. If no date is provided, the current time is used.
|
|
17235
|
+
*
|
|
17236
|
+
* The rates are central differences of `rae()` over +/-0.5 s, stored in the units the
|
|
17237
|
+
* {@link RAE} class expects: km/s for range rate and rad/s for the angle rates (read them
|
|
17238
|
+
* in deg/s with `azRate` / `elRate`). The azimuth change is wrapped so a pass through
|
|
17239
|
+
* north does not produce a 360 deg jump.
|
|
17033
17240
|
* @variation expanded
|
|
17034
17241
|
* @param observer - The observer's position on the ground.
|
|
17035
17242
|
* @param date - The date at which to calculate the RAE values. Optional, defaults to the current date.
|
|
@@ -17040,14 +17247,18 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
17040
17247
|
if (!rae) {
|
|
17041
17248
|
return null;
|
|
17042
17249
|
}
|
|
17043
|
-
const
|
|
17044
|
-
|
|
17250
|
+
const halfStepMs = 500;
|
|
17251
|
+
const raeBefore = this.rae(observer, new Date(date.getTime() - halfStepMs));
|
|
17252
|
+
const raeAfter = this.rae(observer, new Date(date.getTime() + halfStepMs));
|
|
17253
|
+
if (!raeBefore || !raeAfter) {
|
|
17045
17254
|
return null;
|
|
17046
17255
|
}
|
|
17256
|
+
const dt = 2 * halfStepMs / 1e3;
|
|
17257
|
+
const rangeRate = (raeAfter.rng - raeBefore.rng) / dt;
|
|
17258
|
+
const dAz = ((raeAfter.az - raeBefore.az + 180) % 360 + 360) % 360 - 180;
|
|
17259
|
+
const azimuthRate = dAz / dt * DEG2RAD;
|
|
17260
|
+
const elevationRate = (raeAfter.el - raeBefore.el) / dt * DEG2RAD;
|
|
17047
17261
|
const epoch = new EpochUTC(date.getTime() / 1e3);
|
|
17048
|
-
const rangeRate = rae2.rng - rae.rng;
|
|
17049
|
-
const azimuthRate = rae2.az - rae.az;
|
|
17050
|
-
const elevationRate = rae2.el - rae.el;
|
|
17051
17262
|
return new RAE(
|
|
17052
17263
|
epoch,
|
|
17053
17264
|
rae.rng,
|
|
@@ -17711,11 +17922,12 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
17711
17922
|
/**
|
|
17712
17923
|
* Calculates the angle between this satellite, another satellite, and the Sun.
|
|
17713
17924
|
*
|
|
17714
|
-
* Returns the angle at
|
|
17715
|
-
*
|
|
17925
|
+
* Returns the angle at the OTHER satellite between its vector to the Sun and its vector
|
|
17926
|
+
* to this satellite: the solar phase angle of `other` as seen from this satellite
|
|
17927
|
+
* (0 = fully lit face toward this satellite, pi = backlit).
|
|
17716
17928
|
*
|
|
17717
17929
|
* @param other - The other satellite.
|
|
17718
|
-
* @param sunPosition - The Sun's
|
|
17930
|
+
* @param sunPosition - The Sun's position in TEME (the frame of `eci()`), km.
|
|
17719
17931
|
* @param date - The date for the calculation.
|
|
17720
17932
|
* @returns The angle in radians.
|
|
17721
17933
|
* @throws Error if positions are undefined.
|
|
@@ -17758,11 +17970,12 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
17758
17970
|
* ```
|
|
17759
17971
|
*/
|
|
17760
17972
|
getSunStatus(date = /* @__PURE__ */ new Date()) {
|
|
17761
|
-
|
|
17762
|
-
|
|
17973
|
+
let satPos;
|
|
17974
|
+
try {
|
|
17975
|
+
satPos = this.toJ2000(date).position;
|
|
17976
|
+
} catch {
|
|
17763
17977
|
return -1 /* UNKNOWN */;
|
|
17764
17978
|
}
|
|
17765
|
-
const satPos = new Vector3D(pv.position.x, pv.position.y, pv.position.z);
|
|
17766
17979
|
const sunPos = Sun.eci(date);
|
|
17767
17980
|
const ratio = Sun.lightingRatio(satPos, sunPos);
|
|
17768
17981
|
if (ratio === 0) {
|
|
@@ -18230,6 +18443,43 @@ var FieldOfView = class _FieldOfView {
|
|
|
18230
18443
|
const targetDir = direction.normalize();
|
|
18231
18444
|
return this.isDirectionWithinCone(targetDir);
|
|
18232
18445
|
}
|
|
18446
|
+
/**
|
|
18447
|
+
* Samples a unit direction on or inside the FOV cone.
|
|
18448
|
+
*
|
|
18449
|
+
* The cone cross-section is parameterized like a standard 2D ellipse in
|
|
18450
|
+
* angular (gnomonic) space: x = halfAngle * cos(t), y = minorHalfAngle * sin(t),
|
|
18451
|
+
* where x is the off-boresight angle along the major axis (u) and y along the
|
|
18452
|
+
* minor axis (v). Uniform steps in t produce well-distributed boundary points
|
|
18453
|
+
* even for highly eccentric fans (e.g., an 80° x 1° fence radar), unlike
|
|
18454
|
+
* uniform steps in the geometric position angle which clump at the fan tips.
|
|
18455
|
+
*
|
|
18456
|
+
* Points sampled with radialFraction < 1 always satisfy the elliptical cone
|
|
18457
|
+
* containment check; radialFraction = 1 lies exactly on the boundary.
|
|
18458
|
+
*
|
|
18459
|
+
* @param t - Parametric ellipse angle in radians. t=0 points along the major
|
|
18460
|
+
* axis (+u), t=PI/2 along the minor axis (+v). Note this is NOT the
|
|
18461
|
+
* geometric position angle around the boresight.
|
|
18462
|
+
* @param radialFraction - 0 = boresight, 1 = cone boundary (default: 1)
|
|
18463
|
+
* @returns Unit direction vector in the FOV's reference frame (ENU for topocentric)
|
|
18464
|
+
*/
|
|
18465
|
+
directionAt(t, radialFraction = 1) {
|
|
18466
|
+
const x = radialFraction * this.halfAngleRad_ * Math.cos(t);
|
|
18467
|
+
const y = radialFraction * this.minorHalfAngleRad_ * Math.sin(t);
|
|
18468
|
+
const theta = Math.hypot(x, y);
|
|
18469
|
+
const { b, u, v } = this.boresightFrame_;
|
|
18470
|
+
if (theta < EPS) {
|
|
18471
|
+
return b;
|
|
18472
|
+
}
|
|
18473
|
+
const cosTheta = Math.cos(theta);
|
|
18474
|
+
const sinTheta = Math.sin(theta);
|
|
18475
|
+
const rx = x / theta;
|
|
18476
|
+
const ry = y / theta;
|
|
18477
|
+
return new Vector3D(
|
|
18478
|
+
cosTheta * b.x + sinTheta * (rx * u.x + ry * v.x),
|
|
18479
|
+
cosTheta * b.y + sinTheta * (rx * u.y + ry * v.y),
|
|
18480
|
+
cosTheta * b.z + sinTheta * (rx * u.z + ry * v.z)
|
|
18481
|
+
);
|
|
18482
|
+
}
|
|
18233
18483
|
/**
|
|
18234
18484
|
* Gets the effective minimum elevation at a given azimuth.
|
|
18235
18485
|
* Considers all applicable elevation masks and returns the most restrictive.
|
|
@@ -18639,13 +18889,13 @@ var VisualizationHelpers = class {
|
|
|
18639
18889
|
const points = [];
|
|
18640
18890
|
for (let i = 0; i < samples; i++) {
|
|
18641
18891
|
const phi = 2 * Math.PI * i / samples;
|
|
18642
|
-
const
|
|
18643
|
-
|
|
18644
|
-
|
|
18892
|
+
const x = fov.halfAngle * Math.cos(phi) * DEG2RAD;
|
|
18893
|
+
const y = fov.minorHalfAngle * Math.sin(phi) * DEG2RAD;
|
|
18894
|
+
const theta = Math.hypot(x, y);
|
|
18645
18895
|
const cosTheta = Math.cos(theta);
|
|
18646
18896
|
const sinTheta = Math.sin(theta);
|
|
18647
|
-
const cosPhi =
|
|
18648
|
-
const sinPhi =
|
|
18897
|
+
const cosPhi = theta > 0 ? x / theta : 1;
|
|
18898
|
+
const sinPhi = theta > 0 ? y / theta : 0;
|
|
18649
18899
|
const dir = {
|
|
18650
18900
|
x: b.x * cosTheta + u.x * sinTheta * cosPhi + v.x * sinTheta * sinPhi,
|
|
18651
18901
|
y: b.y * cosTheta + u.y * sinTheta * cosPhi + v.y * sinTheta * sinPhi,
|
|
@@ -18835,7 +19085,7 @@ var PlanetBody = class _PlanetBody extends CelestialBody {
|
|
|
18835
19085
|
*/
|
|
18836
19086
|
velocity(date = /* @__PURE__ */ new Date()) {
|
|
18837
19087
|
const time = MakeTime(date);
|
|
18838
|
-
const state =
|
|
19088
|
+
const state = HelioState(this.planetData_.body, time);
|
|
18839
19089
|
return new Vector3D(
|
|
18840
19090
|
state.vx * AU_PER_DAY_TO_KM_PER_S,
|
|
18841
19091
|
state.vy * AU_PER_DAY_TO_KM_PER_S,
|
|
@@ -18914,7 +19164,7 @@ var EphemerisBody = class _EphemerisBody extends CelestialBody {
|
|
|
18914
19164
|
if (!params.ephemeris || params.ephemeris.length === 0) {
|
|
18915
19165
|
throw new Error("Ephemeris array cannot be empty");
|
|
18916
19166
|
}
|
|
18917
|
-
let spaceObjectType
|
|
19167
|
+
let spaceObjectType;
|
|
18918
19168
|
switch (params.bodyType) {
|
|
18919
19169
|
case "dwarf_planet" /* DWARF_PLANET */:
|
|
18920
19170
|
spaceObjectType = 36 /* DWARF_PLANET */;
|
|
@@ -19289,6 +19539,11 @@ var Thrust = class {
|
|
|
19289
19539
|
crosstrack * 1e-3
|
|
19290
19540
|
);
|
|
19291
19541
|
}
|
|
19542
|
+
center;
|
|
19543
|
+
radial;
|
|
19544
|
+
intrack;
|
|
19545
|
+
crosstrack;
|
|
19546
|
+
durationRate;
|
|
19292
19547
|
deltaV;
|
|
19293
19548
|
get magnitude() {
|
|
19294
19549
|
return this.deltaV.magnitude() * 1e3;
|
|
@@ -19327,6 +19582,9 @@ var Hill = class _Hill {
|
|
|
19327
19582
|
this.semimajorAxis_ = semimajorAxis;
|
|
19328
19583
|
this.meanMotion_ = Earth.smaToMeanMotion(this.semimajorAxis_);
|
|
19329
19584
|
}
|
|
19585
|
+
epoch;
|
|
19586
|
+
position;
|
|
19587
|
+
velocity;
|
|
19330
19588
|
semimajorAxis_;
|
|
19331
19589
|
meanMotion_;
|
|
19332
19590
|
static fromState(origin, radialPosition, intrackPosition, nodeVelocity, nodeOffsetTime) {
|
|
@@ -19503,7 +19761,7 @@ var Hill = class _Hill {
|
|
|
19503
19761
|
postManeuvers = postManeuvers.slice();
|
|
19504
19762
|
let output = preManeuvers;
|
|
19505
19763
|
output.sort((a, b) => a.center.difference(b.center));
|
|
19506
|
-
output = output.filter((mvr) => mvr.center >= this.epoch && mvr.center >= pivot);
|
|
19764
|
+
output = output.filter((mvr) => mvr.center.posix >= this.epoch.posix && mvr.center.posix >= pivot.posix);
|
|
19507
19765
|
for (const mvr of output) {
|
|
19508
19766
|
state = state.maneuver(mvr);
|
|
19509
19767
|
}
|
|
@@ -19613,6 +19871,13 @@ var RadecTopocentric = class _RadecTopocentric {
|
|
|
19613
19871
|
this.declinationRate = declinationRate;
|
|
19614
19872
|
this.rangeRate = rangeRate;
|
|
19615
19873
|
}
|
|
19874
|
+
epoch;
|
|
19875
|
+
rightAscension;
|
|
19876
|
+
declination;
|
|
19877
|
+
range;
|
|
19878
|
+
rightAscensionRate;
|
|
19879
|
+
declinationRate;
|
|
19880
|
+
rangeRate;
|
|
19616
19881
|
/**
|
|
19617
19882
|
* Create a new RadecTopocentric object, using degrees for the angular values.
|
|
19618
19883
|
* @param epoch UTC epoch.
|
|
@@ -19625,8 +19890,8 @@ var RadecTopocentric = class _RadecTopocentric {
|
|
|
19625
19890
|
* @returns A new RadecTopocentric object.
|
|
19626
19891
|
*/
|
|
19627
19892
|
static fromDegrees(epoch, rightAscensionDegrees, declinationDegrees, range, rightAscensionRateDegrees, declinationRateDegrees, rangeRate) {
|
|
19628
|
-
const rightAscensionRate = rightAscensionRateDegrees ? rightAscensionRateDegrees * DEG2RAD : null;
|
|
19629
|
-
const declinationRate = declinationRateDegrees ? declinationRateDegrees * DEG2RAD : null;
|
|
19893
|
+
const rightAscensionRate = typeof rightAscensionRateDegrees === "number" ? rightAscensionRateDegrees * DEG2RAD : null;
|
|
19894
|
+
const declinationRate = typeof declinationRateDegrees === "number" ? declinationRateDegrees * DEG2RAD : null;
|
|
19630
19895
|
const normalizedRA = ((rightAscensionDegrees * DEG2RAD + Math.PI) % TAU + TAU) % TAU - Math.PI;
|
|
19631
19896
|
return new _RadecTopocentric(
|
|
19632
19897
|
epoch,
|
|
@@ -19696,14 +19961,14 @@ var RadecTopocentric = class _RadecTopocentric {
|
|
|
19696
19961
|
* @returns The right ascension rate in degrees per second, or null if it is not available.
|
|
19697
19962
|
*/
|
|
19698
19963
|
get rightAscensionRateDegrees() {
|
|
19699
|
-
return this.rightAscensionRate ? this.rightAscensionRate * RAD2DEG : null;
|
|
19964
|
+
return typeof this.rightAscensionRate === "number" ? this.rightAscensionRate * RAD2DEG : null;
|
|
19700
19965
|
}
|
|
19701
19966
|
/**
|
|
19702
19967
|
* Gets the rate of change of declination in degrees per second.
|
|
19703
19968
|
* @returns The rate of change of declination in degrees per second, or null if the declination rate is not defined.
|
|
19704
19969
|
*/
|
|
19705
19970
|
get declinationRateDegrees() {
|
|
19706
|
-
return this.declinationRate ? this.declinationRate * RAD2DEG : null;
|
|
19971
|
+
return typeof this.declinationRate === "number" ? this.declinationRate * RAD2DEG : null;
|
|
19707
19972
|
}
|
|
19708
19973
|
/**
|
|
19709
19974
|
* Return the position relative to the observer site.
|
|
@@ -19727,7 +19992,7 @@ var RadecTopocentric = class _RadecTopocentric {
|
|
|
19727
19992
|
* @returns A Vector3D object.
|
|
19728
19993
|
*/
|
|
19729
19994
|
velocity(site, range, rangeRate) {
|
|
19730
|
-
if (
|
|
19995
|
+
if (typeof this.rightAscensionRate !== "number" || typeof this.declinationRate !== "number") {
|
|
19731
19996
|
throw new Error("Velocity unsolvable, missing ra/dec rates.");
|
|
19732
19997
|
}
|
|
19733
19998
|
const r = range ?? this.range ?? 1;
|
|
@@ -19782,6 +20047,9 @@ var ObservationOptical = class _ObservationOptical extends Observation {
|
|
|
19782
20047
|
this.observation = observation;
|
|
19783
20048
|
this.noise_ = noise_;
|
|
19784
20049
|
}
|
|
20050
|
+
site_;
|
|
20051
|
+
observation;
|
|
20052
|
+
noise_;
|
|
19785
20053
|
// / Inertial site location.
|
|
19786
20054
|
get site() {
|
|
19787
20055
|
return this.site_;
|
|
@@ -19872,6 +20140,9 @@ var ObservationRadar = class _ObservationRadar extends Observation {
|
|
|
19872
20140
|
this.observation = observation;
|
|
19873
20141
|
this.noise_ = noise_;
|
|
19874
20142
|
}
|
|
20143
|
+
site_;
|
|
20144
|
+
observation;
|
|
20145
|
+
noise_;
|
|
19875
20146
|
// / Default noise matrix.
|
|
19876
20147
|
static defaultNoise = _ObservationRadar.noiseFromSigmas(0.32, 0.015 * DEG2RAD, 0.015 * DEG2RAD);
|
|
19877
20148
|
get epoch() {
|
|
@@ -19952,6 +20223,13 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
19952
20223
|
this.declinationRate = declinationRate;
|
|
19953
20224
|
this.rangeRate = rangeRate;
|
|
19954
20225
|
}
|
|
20226
|
+
epoch;
|
|
20227
|
+
rightAscension;
|
|
20228
|
+
declination;
|
|
20229
|
+
range;
|
|
20230
|
+
rightAscensionRate;
|
|
20231
|
+
declinationRate;
|
|
20232
|
+
rangeRate;
|
|
19955
20233
|
/**
|
|
19956
20234
|
* Creates a RadecGeocentric object from the given parameters in degrees.
|
|
19957
20235
|
* @param epoch - The epoch in UTC.
|
|
@@ -19964,8 +20242,8 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
19964
20242
|
* @returns A new RadecGeocentric object.
|
|
19965
20243
|
*/
|
|
19966
20244
|
static fromDegrees(epoch, rightAscensionDegrees, declinationDegrees, range, rightAscensionRateDegrees, declinationRateDegrees, rangeRate) {
|
|
19967
|
-
const rightAscensionRate = rightAscensionRateDegrees ? rightAscensionRateDegrees * DEG2RAD : null;
|
|
19968
|
-
const declinationRate = declinationRateDegrees ? declinationRateDegrees * DEG2RAD : null;
|
|
20245
|
+
const rightAscensionRate = typeof rightAscensionRateDegrees === "number" ? rightAscensionRateDegrees * DEG2RAD : null;
|
|
20246
|
+
const declinationRate = typeof declinationRateDegrees === "number" ? declinationRateDegrees * DEG2RAD : null;
|
|
19969
20247
|
return new _RadecGeocentric(
|
|
19970
20248
|
epoch,
|
|
19971
20249
|
rightAscensionDegrees * DEG2RAD,
|
|
@@ -20002,7 +20280,7 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
20002
20280
|
const declinationRate = (vK - rangeRate * (rK / rMag)) / rIJMag;
|
|
20003
20281
|
return new _RadecGeocentric(
|
|
20004
20282
|
state.epoch,
|
|
20005
|
-
rightAscension % TAU,
|
|
20283
|
+
(rightAscension % TAU + TAU) % TAU,
|
|
20006
20284
|
declination,
|
|
20007
20285
|
rMag,
|
|
20008
20286
|
rightAscensionRate,
|
|
@@ -20015,7 +20293,8 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
20015
20293
|
* @returns The right ascension in degrees.
|
|
20016
20294
|
*/
|
|
20017
20295
|
get rightAscensionDegrees() {
|
|
20018
|
-
|
|
20296
|
+
const deg = this.rightAscension * RAD2DEG;
|
|
20297
|
+
return (deg % 360 + 360) % 360;
|
|
20019
20298
|
}
|
|
20020
20299
|
/**
|
|
20021
20300
|
* Gets the declination in degrees.
|
|
@@ -20029,14 +20308,14 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
20029
20308
|
* @returns The right ascension rate in degrees per second, or null if it is not available.
|
|
20030
20309
|
*/
|
|
20031
20310
|
get rightAscensionRateDegrees() {
|
|
20032
|
-
return this.rightAscensionRate ? this.rightAscensionRate * RAD2DEG : null;
|
|
20311
|
+
return typeof this.rightAscensionRate === "number" ? this.rightAscensionRate * RAD2DEG : null;
|
|
20033
20312
|
}
|
|
20034
20313
|
/**
|
|
20035
20314
|
* Gets the rate of change of declination in degrees per second.
|
|
20036
20315
|
* @returns The rate of change of declination in degrees per second, or null if not available.
|
|
20037
20316
|
*/
|
|
20038
20317
|
get declinationRateDegrees() {
|
|
20039
|
-
return this.declinationRate ? this.declinationRate * RAD2DEG : null;
|
|
20318
|
+
return typeof this.declinationRate === "number" ? this.declinationRate * RAD2DEG : null;
|
|
20040
20319
|
}
|
|
20041
20320
|
/**
|
|
20042
20321
|
* Calculates the position vector in geocentric coordinates.
|
|
@@ -20056,7 +20335,7 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
20056
20335
|
* @throws Error if the right ascension rate or declination rate is missing.
|
|
20057
20336
|
*/
|
|
20058
20337
|
velocity(range, rangeRate) {
|
|
20059
|
-
if (
|
|
20338
|
+
if (typeof this.rightAscensionRate !== "number" || typeof this.declinationRate !== "number") {
|
|
20060
20339
|
throw new Error("Velocity unsolvable, missing ra/dec rates.");
|
|
20061
20340
|
}
|
|
20062
20341
|
const r = range ?? this.range ?? 1;
|
|
@@ -20474,12 +20753,143 @@ var RandomGaussianSource = class {
|
|
|
20474
20753
|
}
|
|
20475
20754
|
};
|
|
20476
20755
|
|
|
20756
|
+
// src/external/Sgp4WasmTypes.ts
|
|
20757
|
+
var Sgp4WasmLogLevel = /* @__PURE__ */ ((Sgp4WasmLogLevel3) => {
|
|
20758
|
+
Sgp4WasmLogLevel3[Sgp4WasmLogLevel3["None"] = 0] = "None";
|
|
20759
|
+
Sgp4WasmLogLevel3[Sgp4WasmLogLevel3["Info"] = 1] = "Info";
|
|
20760
|
+
Sgp4WasmLogLevel3[Sgp4WasmLogLevel3["Warn"] = 2] = "Warn";
|
|
20761
|
+
Sgp4WasmLogLevel3[Sgp4WasmLogLevel3["Error"] = 3] = "Error";
|
|
20762
|
+
return Sgp4WasmLogLevel3;
|
|
20763
|
+
})(Sgp4WasmLogLevel || {});
|
|
20764
|
+
var Sgp4WasmError = class extends Error {
|
|
20765
|
+
constructor(message, code) {
|
|
20766
|
+
super(message);
|
|
20767
|
+
this.code = code;
|
|
20768
|
+
this.name = "Sgp4WasmError";
|
|
20769
|
+
}
|
|
20770
|
+
code;
|
|
20771
|
+
};
|
|
20772
|
+
var SGP4_WASM_DYN_ARR_ERRORS = {
|
|
20773
|
+
[-1]: "Error allocating space for the dynamic array",
|
|
20774
|
+
[-2]: "Dynamic array is already initialized",
|
|
20775
|
+
[-3]: "The requested size is too large",
|
|
20776
|
+
[-4]: "The requested size is negative"
|
|
20777
|
+
};
|
|
20778
|
+
var SGP4_WASM_ADD_SAT_ERRORS = {
|
|
20779
|
+
[-1]: "Dynamic array has not been allocated",
|
|
20780
|
+
[-2]: "Dynamic array is out of space (call reallocDynArr)",
|
|
20781
|
+
[-3]: "Failed to determine satKey from line 1",
|
|
20782
|
+
[-4]: "Failed to parse line 1 or line 2"
|
|
20783
|
+
};
|
|
20784
|
+
|
|
20785
|
+
// src/propagator/Sgp4WasmPropagator.ts
|
|
20786
|
+
var Sgp4WasmPropagator = class extends Propagator {
|
|
20787
|
+
constructor(wasm_, tle_) {
|
|
20788
|
+
super();
|
|
20789
|
+
this.wasm_ = wasm_;
|
|
20790
|
+
this.tle_ = tle_;
|
|
20791
|
+
if (!wasm_.isLoaded) {
|
|
20792
|
+
throw new Sgp4WasmError("Sgp4WasmPropagator requires a loaded Sgp4Wasm instance. Call load() first.");
|
|
20793
|
+
}
|
|
20794
|
+
this.satKey_ = wasm_.addSat(tle_.line1, tle_.line2);
|
|
20795
|
+
wasm_.initSats([this.satKey_]);
|
|
20796
|
+
this.cacheState_ = this.propagate(tle_.epoch);
|
|
20797
|
+
}
|
|
20798
|
+
wasm_;
|
|
20799
|
+
tle_;
|
|
20800
|
+
satKey_;
|
|
20801
|
+
cacheState_;
|
|
20802
|
+
checkpoints_ = [];
|
|
20803
|
+
/**
|
|
20804
|
+
* The Astro Standards satKey backing this propagator.
|
|
20805
|
+
*/
|
|
20806
|
+
get satKey() {
|
|
20807
|
+
return this.satKey_;
|
|
20808
|
+
}
|
|
20809
|
+
/**
|
|
20810
|
+
* Gets the state of the propagator in the J2000 coordinate system.
|
|
20811
|
+
* @returns The J2000 state of the propagator.
|
|
20812
|
+
*/
|
|
20813
|
+
get state() {
|
|
20814
|
+
return this.cacheState_;
|
|
20815
|
+
}
|
|
20816
|
+
/**
|
|
20817
|
+
* Calculates the ephemeris maneuver using the SGP4 propagator.
|
|
20818
|
+
* @param start The start epoch in UTC.
|
|
20819
|
+
* @param finish The finish epoch in UTC.
|
|
20820
|
+
* @param maneuvers The array of thrust maneuvers.
|
|
20821
|
+
* @param interval The time interval in seconds.
|
|
20822
|
+
*/
|
|
20823
|
+
ephemerisManeuver(_start, _finish, _maneuvers, _interval = 60) {
|
|
20824
|
+
throw new Error("Maneuvers cannot be modelled with SGP4.");
|
|
20825
|
+
}
|
|
20826
|
+
/**
|
|
20827
|
+
* Performs a maneuver with the given thrust.
|
|
20828
|
+
* @param maneuver - The thrust maneuver to perform.
|
|
20829
|
+
* @param interval - The time interval for the maneuver (default: 60.0 seconds).
|
|
20830
|
+
* @throws Error if maneuvers cannot be modeled with SGP4.
|
|
20831
|
+
*/
|
|
20832
|
+
maneuver(_maneuver, _interval = 60) {
|
|
20833
|
+
throw new Error("Maneuvers cannot be modelled with SGP4.");
|
|
20834
|
+
}
|
|
20835
|
+
/**
|
|
20836
|
+
* Propagates the state to a specified epoch in J2000 coordinates.
|
|
20837
|
+
* @param epoch - The epoch in UTC format.
|
|
20838
|
+
* @returns The propagated state in J2000 coordinates.
|
|
20839
|
+
*/
|
|
20840
|
+
propagate(epoch) {
|
|
20841
|
+
const state = this.wasm_.propagateEpoch(this.satKey_, epoch);
|
|
20842
|
+
if (state.err !== 0) {
|
|
20843
|
+
throw new Sgp4WasmError(`SGP4 propagation failed for satKey ${this.satKey_} (code ${state.err})`, state.err);
|
|
20844
|
+
}
|
|
20845
|
+
this.cacheState_ = new TEME(epoch, state.position, state.velocity).toJ2000();
|
|
20846
|
+
return this.cacheState_;
|
|
20847
|
+
}
|
|
20848
|
+
/**
|
|
20849
|
+
* Resets the state of the propagator to the TLE epoch state.
|
|
20850
|
+
*/
|
|
20851
|
+
reset() {
|
|
20852
|
+
this.cacheState_ = this.propagate(this.tle_.epoch);
|
|
20853
|
+
}
|
|
20854
|
+
/**
|
|
20855
|
+
* Saves the current state of the propagator and returns the index of the checkpoint.
|
|
20856
|
+
* @returns The index of the checkpoint.
|
|
20857
|
+
*/
|
|
20858
|
+
checkpoint() {
|
|
20859
|
+
this.checkpoints_.push(this.cacheState_);
|
|
20860
|
+
return this.checkpoints_.length - 1;
|
|
20861
|
+
}
|
|
20862
|
+
/**
|
|
20863
|
+
* Clears all the checkpoints in the propagator.
|
|
20864
|
+
*/
|
|
20865
|
+
clearCheckpoints() {
|
|
20866
|
+
this.checkpoints_ = [];
|
|
20867
|
+
}
|
|
20868
|
+
/**
|
|
20869
|
+
* Restores the state of the propagator to a previously saved checkpoint.
|
|
20870
|
+
* @param index - The index of the checkpoint to restore.
|
|
20871
|
+
*/
|
|
20872
|
+
restore(index) {
|
|
20873
|
+
this.cacheState_ = this.checkpoints_[index];
|
|
20874
|
+
}
|
|
20875
|
+
/**
|
|
20876
|
+
* Removes the satellite from the backing wasm instance's SGP4 and TLE
|
|
20877
|
+
* registries.
|
|
20878
|
+
*/
|
|
20879
|
+
dispose() {
|
|
20880
|
+
this.wasm_.uninitSats([this.satKey_]);
|
|
20881
|
+
this.wasm_.removeSats([this.satKey_]);
|
|
20882
|
+
}
|
|
20883
|
+
};
|
|
20884
|
+
|
|
20477
20885
|
// src/observation/PropagatorPairs.ts
|
|
20478
20886
|
var PropagatorPairs = class {
|
|
20479
20887
|
constructor(posStep_, velStep_) {
|
|
20480
20888
|
this.posStep_ = posStep_;
|
|
20481
20889
|
this.velStep_ = velStep_;
|
|
20482
20890
|
}
|
|
20891
|
+
posStep_;
|
|
20892
|
+
velStep_;
|
|
20483
20893
|
_high = Array(6).fill(null);
|
|
20484
20894
|
_low = Array(6).fill(null);
|
|
20485
20895
|
set(index, high, low) {
|
|
@@ -20513,6 +20923,9 @@ var BatchLeastSquaresResult = class {
|
|
|
20513
20923
|
this.covariance = covariance2;
|
|
20514
20924
|
this.rms = rms;
|
|
20515
20925
|
}
|
|
20926
|
+
state;
|
|
20927
|
+
covariance;
|
|
20928
|
+
rms;
|
|
20516
20929
|
};
|
|
20517
20930
|
|
|
20518
20931
|
// src/orbit-determination/BatchLeastSquaresOD.ts
|
|
@@ -20543,6 +20956,12 @@ var BatchLeastSquaresOD = class _BatchLeastSquaresOD {
|
|
|
20543
20956
|
this.propagator_ = new RungeKutta89Propagator(this.apriori_, this.forceModel_);
|
|
20544
20957
|
this.nominal_ = this.propagator_.propagate(this.start_);
|
|
20545
20958
|
}
|
|
20959
|
+
observations_;
|
|
20960
|
+
apriori_;
|
|
20961
|
+
forceModel_;
|
|
20962
|
+
posStep_;
|
|
20963
|
+
velStep_;
|
|
20964
|
+
fastDerivatives_;
|
|
20546
20965
|
/** Propagator pair cache, for generating observation Jacobians. */
|
|
20547
20966
|
propPairs_;
|
|
20548
20967
|
/** Nominal state propagator. */
|
|
@@ -20643,6 +21062,7 @@ var GibbsIOD = class _GibbsIOD {
|
|
|
20643
21062
|
constructor(mu = Earth.mu) {
|
|
20644
21063
|
this.mu = mu;
|
|
20645
21064
|
}
|
|
21065
|
+
mu;
|
|
20646
21066
|
/** Abort solve if position plane exceeds this value. */
|
|
20647
21067
|
static coplanarThreshold_ = 5 * DEG2RAD;
|
|
20648
21068
|
/**
|
|
@@ -20701,6 +21121,7 @@ var HerrickGibbsIOD = class {
|
|
|
20701
21121
|
constructor(mu = Earth.mu) {
|
|
20702
21122
|
this.mu = mu;
|
|
20703
21123
|
}
|
|
21124
|
+
mu;
|
|
20704
21125
|
// / Attempt to create a state estimate from three inertial position vectors.
|
|
20705
21126
|
solve(r1, t1, r2, t2, r3, t3) {
|
|
20706
21127
|
const dt31 = t3.difference(t1);
|
|
@@ -20961,6 +21382,7 @@ var LambertIOD = class _LambertIOD {
|
|
|
20961
21382
|
constructor(mu = Earth.mu) {
|
|
20962
21383
|
this.mu = mu;
|
|
20963
21384
|
}
|
|
21385
|
+
mu;
|
|
20964
21386
|
/**
|
|
20965
21387
|
* Try to guess the short path argument given an [interceptor] and
|
|
20966
21388
|
* [target] state.
|
|
@@ -21216,10 +21638,9 @@ var GoodingIOD = class _GoodingIOD {
|
|
|
21216
21638
|
* @returns Orbit estimate referenced to the epoch of the second observation
|
|
21217
21639
|
*/
|
|
21218
21640
|
estimate(o1, o2, o3, rho1init = null, rho3init = null, nRev = 0, direction = true) {
|
|
21219
|
-
let orbit = null;
|
|
21220
21641
|
if (rho1init === null || rho3init === null) {
|
|
21221
21642
|
const gaussIod = new GaussIOD(this._mu);
|
|
21222
|
-
orbit = gaussIod.estimate(o1, o2, o3);
|
|
21643
|
+
const orbit = gaussIod.estimate(o1, o2, o3);
|
|
21223
21644
|
if (orbit === null) {
|
|
21224
21645
|
throw new OrbitDeterminationError("Gauss IOD failed to provide initial estimate for Gooding IOD", "Gooding");
|
|
21225
21646
|
}
|
|
@@ -21648,6 +22069,11 @@ var LevenbergMarquardtResult = class {
|
|
|
21648
22069
|
this.iterations = iterations;
|
|
21649
22070
|
this.converged = converged;
|
|
21650
22071
|
}
|
|
22072
|
+
state;
|
|
22073
|
+
covariance;
|
|
22074
|
+
rms;
|
|
22075
|
+
iterations;
|
|
22076
|
+
converged;
|
|
21651
22077
|
};
|
|
21652
22078
|
|
|
21653
22079
|
// src/orbit-determination/LevenbergMarquardtOD.ts
|
|
@@ -21678,6 +22104,12 @@ var LevenbergMarquardtOD = class _LevenbergMarquardtOD {
|
|
|
21678
22104
|
const propagator = new RungeKutta89Propagator(this.apriori_, this.forceModel_);
|
|
21679
22105
|
this.nominal_ = propagator.propagate(this.start_);
|
|
21680
22106
|
}
|
|
22107
|
+
observations_;
|
|
22108
|
+
apriori_;
|
|
22109
|
+
forceModel_;
|
|
22110
|
+
posStep_;
|
|
22111
|
+
velStep_;
|
|
22112
|
+
fastDerivatives_;
|
|
21681
22113
|
/** Propagator pair cache, for generating observation Jacobians. */
|
|
21682
22114
|
propPairs_;
|
|
21683
22115
|
/** State estimate during solve. */
|
|
@@ -21836,6 +22268,8 @@ var SimplexEntry = class _SimplexEntry {
|
|
|
21836
22268
|
this.x_ = new Vector(points);
|
|
21837
22269
|
this.score = this.f_(points);
|
|
21838
22270
|
}
|
|
22271
|
+
f_;
|
|
22272
|
+
points;
|
|
21839
22273
|
/**
|
|
21840
22274
|
* The cost function score at this vertex.
|
|
21841
22275
|
*
|
|
@@ -22114,6 +22548,18 @@ var ModifiedGoodingIOD = class {
|
|
|
22114
22548
|
};
|
|
22115
22549
|
return scoreFn;
|
|
22116
22550
|
}
|
|
22551
|
+
/**
|
|
22552
|
+
* Solves for a J2000 state at the middle observation's epoch: a Gooding three-line-of-sight
|
|
22553
|
+
* estimate refined by a downhill simplex over all observations with a two-body force model.
|
|
22554
|
+
*
|
|
22555
|
+
* `tolerance` ends the simplex once the spread of the summed line-of-sight error (radians)
|
|
22556
|
+
* or of the vertices falls below it.
|
|
22557
|
+
* @param observations - Optical observations, in time order (at least 3)
|
|
22558
|
+
* @param r0 - Range guess for the first observation (km)
|
|
22559
|
+
* @param rN - Range guess for the last observation (km)
|
|
22560
|
+
* @param options - Solver options
|
|
22561
|
+
* @returns The J2000 state at the middle observation's epoch
|
|
22562
|
+
*/
|
|
22117
22563
|
solve(observations, r0, rN, {
|
|
22118
22564
|
nRev = 0,
|
|
22119
22565
|
direction = true,
|
|
@@ -22577,6 +23023,8 @@ var ConjunctionAssessment = class {
|
|
|
22577
23023
|
this.primaryProp = this.createPropagator(primary, false);
|
|
22578
23024
|
this.secondaryProp = this.createPropagator(secondary, false);
|
|
22579
23025
|
}
|
|
23026
|
+
primary;
|
|
23027
|
+
secondary;
|
|
22580
23028
|
primaryProp;
|
|
22581
23029
|
secondaryProp;
|
|
22582
23030
|
primaryCovSample;
|
|
@@ -23811,7 +24259,7 @@ var PhasedArrayRadar = class _PhasedArrayRadar extends RadarSensor {
|
|
|
23811
24259
|
this.validateFace_(face);
|
|
23812
24260
|
const azRad = az * DEG2RAD;
|
|
23813
24261
|
const elRad = el * DEG2RAD;
|
|
23814
|
-
const azDiff = azRad - this.boresightAzRad(face);
|
|
24262
|
+
const azDiff = wrapAngle(azRad - this.boresightAzRad(face));
|
|
23815
24263
|
const elDiff = elRad - this.boresightElRad(face);
|
|
23816
24264
|
return azel2uv(azDiff, elDiff, this.beamwidthRad);
|
|
23817
24265
|
}
|
|
@@ -23905,9 +24353,7 @@ var PhasedArrayRadar = class _PhasedArrayRadar extends RadarSensor {
|
|
|
23905
24353
|
getVisibleFaces(az, el, maxAngle = 60) {
|
|
23906
24354
|
const visibleFaces = [];
|
|
23907
24355
|
for (let face = 0; face < this.faceCount; face++) {
|
|
23908
|
-
const
|
|
23909
|
-
const elDiff = Math.abs(el - this.boresightEl[face]);
|
|
23910
|
-
const angularDist = Math.sqrt(azDiff * azDiff + elDiff * elDiff);
|
|
24356
|
+
const angularDist = this.angleFromBoresight_(az, el, face);
|
|
23911
24357
|
if (angularDist <= maxAngle) {
|
|
23912
24358
|
visibleFaces.push(face);
|
|
23913
24359
|
}
|
|
@@ -23924,9 +24370,7 @@ var PhasedArrayRadar = class _PhasedArrayRadar extends RadarSensor {
|
|
|
23924
24370
|
let bestFace = -1;
|
|
23925
24371
|
let minDist = Infinity;
|
|
23926
24372
|
for (let face = 0; face < this.faceCount; face++) {
|
|
23927
|
-
const
|
|
23928
|
-
const elDiff = el - this.boresightEl[face];
|
|
23929
|
-
const dist = Math.sqrt(azDiff * azDiff + elDiff * elDiff);
|
|
24373
|
+
const dist = this.angleFromBoresight_(az, el, face);
|
|
23930
24374
|
if (dist < minDist && dist < 90) {
|
|
23931
24375
|
minDist = dist;
|
|
23932
24376
|
bestFace = face;
|
|
@@ -23934,6 +24378,24 @@ var PhasedArrayRadar = class _PhasedArrayRadar extends RadarSensor {
|
|
|
23934
24378
|
}
|
|
23935
24379
|
return bestFace;
|
|
23936
24380
|
}
|
|
24381
|
+
/**
|
|
24382
|
+
* Great-circle angle between a look direction and a face's boresight, in degrees.
|
|
24383
|
+
* (The face checks used sqrt(dAz^2 + dEl^2) with no azimuth wrap: 359 deg missed a 0 deg
|
|
24384
|
+
* face, and azimuth differences were not shrunk by cos(elevation).)
|
|
24385
|
+
* @param az - Target azimuth in degrees
|
|
24386
|
+
* @param el - Target elevation in degrees
|
|
24387
|
+
* @param face - Face index
|
|
24388
|
+
* @returns Angle from boresight in degrees
|
|
24389
|
+
*/
|
|
24390
|
+
angleFromBoresight_(az, el, face) {
|
|
24391
|
+
return angularDistance(
|
|
24392
|
+
az * DEG2RAD,
|
|
24393
|
+
el * DEG2RAD,
|
|
24394
|
+
this.boresightAz[face] * DEG2RAD,
|
|
24395
|
+
this.boresightEl[face] * DEG2RAD,
|
|
24396
|
+
1 /* Haversine */
|
|
24397
|
+
) * RAD2DEG;
|
|
24398
|
+
}
|
|
23937
24399
|
// ==================== Serialization ====================
|
|
23938
24400
|
serializeSpecific() {
|
|
23939
24401
|
return {
|
|
@@ -24444,20 +24906,97 @@ var PassiveRFSensor = class _PassiveRFSensor extends Sensor {
|
|
|
24444
24906
|
}
|
|
24445
24907
|
};
|
|
24446
24908
|
|
|
24447
|
-
// src/
|
|
24448
|
-
|
|
24449
|
-
|
|
24450
|
-
|
|
24451
|
-
|
|
24452
|
-
|
|
24453
|
-
|
|
24909
|
+
// src/sensor/TtcAntenna.ts
|
|
24910
|
+
var TtcAntenna = class _TtcAntenna extends Sensor {
|
|
24911
|
+
/** Antenna dish diameter in meters */
|
|
24912
|
+
antennaDiameter;
|
|
24913
|
+
/** Antenna gain in dB */
|
|
24914
|
+
antennaGain;
|
|
24915
|
+
/** Whether the station supports two-way ranging */
|
|
24916
|
+
hasRanging;
|
|
24917
|
+
/** Whether the station can uplink commands */
|
|
24918
|
+
hasUplink;
|
|
24919
|
+
constructor(params) {
|
|
24920
|
+
const paramsWithType = {
|
|
24921
|
+
...params,
|
|
24922
|
+
sensorType: "TT_C_ANTENNA" /* TT_C_ANTENNA */
|
|
24923
|
+
};
|
|
24924
|
+
super(paramsWithType);
|
|
24925
|
+
this.antennaDiameter = params.antennaDiameter;
|
|
24926
|
+
this.antennaGain = params.antennaGain;
|
|
24927
|
+
this.hasRanging = params.hasRanging ?? true;
|
|
24928
|
+
this.hasUplink = params.hasUplink ?? true;
|
|
24454
24929
|
}
|
|
24455
24930
|
/**
|
|
24456
|
-
*
|
|
24457
|
-
*
|
|
24458
|
-
*
|
|
24459
|
-
* -
|
|
24460
|
-
*
|
|
24931
|
+
* Creates an angle observation (Ra/Dec) of a cooperative target.
|
|
24932
|
+
* Angles come from antenna autotrack while locked to the target's downlink.
|
|
24933
|
+
* @param target - The space object to observe
|
|
24934
|
+
* @param date - Time of observation (defaults to now)
|
|
24935
|
+
* @returns ObservationOptical or null if target not in FOV
|
|
24936
|
+
*/
|
|
24937
|
+
observe(target, date = /* @__PURE__ */ new Date()) {
|
|
24938
|
+
if (!this.canObserve(target, date)) {
|
|
24939
|
+
return null;
|
|
24940
|
+
}
|
|
24941
|
+
const sensorJ2000 = this.getJ2000(date);
|
|
24942
|
+
const targetJ2000 = target.toJ2000(date);
|
|
24943
|
+
const radec = RadecTopocentric.fromStateVector(targetJ2000, sensorJ2000);
|
|
24944
|
+
return new ObservationOptical(sensorJ2000, radec);
|
|
24945
|
+
}
|
|
24946
|
+
serializeSpecific() {
|
|
24947
|
+
return {
|
|
24948
|
+
antennaDiameter: this.antennaDiameter,
|
|
24949
|
+
antennaGain: this.antennaGain,
|
|
24950
|
+
hasRanging: this.hasRanging,
|
|
24951
|
+
hasUplink: this.hasUplink
|
|
24952
|
+
};
|
|
24953
|
+
}
|
|
24954
|
+
/**
|
|
24955
|
+
* Creates a deep copy of this TT&C antenna.
|
|
24956
|
+
* The cloned sensor will not have a parent assigned.
|
|
24957
|
+
* @returns A new TtcAntenna instance with the same properties
|
|
24958
|
+
*/
|
|
24959
|
+
clone() {
|
|
24960
|
+
return new _TtcAntenna({
|
|
24961
|
+
id: this.id,
|
|
24962
|
+
name: this.name,
|
|
24963
|
+
sensorType: this.sensorType,
|
|
24964
|
+
fieldOfView: this.fieldOfView.serialize(),
|
|
24965
|
+
antennaDiameter: this.antennaDiameter,
|
|
24966
|
+
antennaGain: this.antennaGain,
|
|
24967
|
+
hasRanging: this.hasRanging,
|
|
24968
|
+
hasUplink: this.hasUplink,
|
|
24969
|
+
shortName: this.shortName,
|
|
24970
|
+
system: this.system,
|
|
24971
|
+
country: this.country,
|
|
24972
|
+
operator: this.operator,
|
|
24973
|
+
dwellTime: this.dwellTime,
|
|
24974
|
+
freqBand: this.freqBand,
|
|
24975
|
+
isVolumetric: this.isVolumetric,
|
|
24976
|
+
url: this.url,
|
|
24977
|
+
metadata: this.metadata ? { ...this.metadata } : void 0
|
|
24978
|
+
});
|
|
24979
|
+
}
|
|
24980
|
+
};
|
|
24981
|
+
|
|
24982
|
+
// src/parsers/HorizonsParser.ts
|
|
24983
|
+
function julianDateToDate(jd) {
|
|
24984
|
+
const unixMs = (jd - 24405875e-1) * 864e5;
|
|
24985
|
+
return new Date(unixMs);
|
|
24986
|
+
}
|
|
24987
|
+
function tdbDateToEpochUtc(tdb) {
|
|
24988
|
+
const j2000Posix = 946728e3;
|
|
24989
|
+
return EpochUTC.fromJ2000TTSeconds(tdb.getTime() / 1e3 - j2000Posix);
|
|
24990
|
+
}
|
|
24991
|
+
var HorizonsParser = class {
|
|
24992
|
+
constructor() {
|
|
24993
|
+
}
|
|
24994
|
+
/**
|
|
24995
|
+
* Parses NASA Horizons vector table format.
|
|
24996
|
+
*
|
|
24997
|
+
* Expects output from Horizons with settings:
|
|
24998
|
+
* - VECTORS (type 2 or 3)
|
|
24999
|
+
* - Output in km and km/s
|
|
24461
25000
|
*
|
|
24462
25001
|
* @param data - Raw Horizons output text
|
|
24463
25002
|
* @returns Parsed ephemeris data
|
|
@@ -24480,22 +25019,34 @@ var HorizonsParser = class {
|
|
|
24480
25019
|
let referenceFrame = "";
|
|
24481
25020
|
for (let i = 0; i < lines.length; i++) {
|
|
24482
25021
|
const line = lines[i].trim();
|
|
24483
|
-
|
|
24484
|
-
|
|
24485
|
-
|
|
24486
|
-
|
|
24487
|
-
|
|
24488
|
-
|
|
24489
|
-
|
|
24490
|
-
|
|
24491
|
-
|
|
24492
|
-
|
|
24493
|
-
|
|
24494
|
-
|
|
24495
|
-
|
|
24496
|
-
|
|
24497
|
-
|
|
24498
|
-
|
|
25022
|
+
const meta = this.matchMetadataLine_(line);
|
|
25023
|
+
if (meta) {
|
|
25024
|
+
switch (meta.label) {
|
|
25025
|
+
case "Target body name":
|
|
25026
|
+
targetName = this.stripNameSuffixes_(meta.value);
|
|
25027
|
+
metadata.targetName = targetName;
|
|
25028
|
+
break;
|
|
25029
|
+
case "Center body name":
|
|
25030
|
+
centerBody = meta.value;
|
|
25031
|
+
metadata.centerBody = centerBody;
|
|
25032
|
+
break;
|
|
25033
|
+
case "Center-site name":
|
|
25034
|
+
if (!centerBody) {
|
|
25035
|
+
centerBody = meta.value;
|
|
25036
|
+
metadata.centerBody = centerBody;
|
|
25037
|
+
}
|
|
25038
|
+
break;
|
|
25039
|
+
case "Output type":
|
|
25040
|
+
metadata.outputType = meta.value;
|
|
25041
|
+
break;
|
|
25042
|
+
case "Reference frame":
|
|
25043
|
+
referenceFrame = meta.value;
|
|
25044
|
+
metadata.referenceFrame = referenceFrame;
|
|
25045
|
+
break;
|
|
25046
|
+
case "Output units":
|
|
25047
|
+
metadata.outputUnits = meta.value;
|
|
25048
|
+
break;
|
|
25049
|
+
}
|
|
24499
25050
|
}
|
|
24500
25051
|
if (line === "$$SOE") {
|
|
24501
25052
|
inData = true;
|
|
@@ -24540,10 +25091,12 @@ var HorizonsParser = class {
|
|
|
24540
25091
|
let observerLocation = "";
|
|
24541
25092
|
for (let i = 0; i < lines.length; i++) {
|
|
24542
25093
|
const line = lines[i].trim();
|
|
24543
|
-
|
|
24544
|
-
|
|
24545
|
-
|
|
24546
|
-
|
|
25094
|
+
const targetRaw = this.extractLabeledValue_(line, "Target body name");
|
|
25095
|
+
const siteRaw = this.extractLabeledValue_(line, "Center-site name");
|
|
25096
|
+
if (targetRaw !== null) {
|
|
25097
|
+
targetName = this.stripNameSuffixes_(targetRaw);
|
|
25098
|
+
} else if (siteRaw !== null) {
|
|
25099
|
+
observerLocation = siteRaw;
|
|
24547
25100
|
}
|
|
24548
25101
|
if (line === "$$SOE") {
|
|
24549
25102
|
inData = true;
|
|
@@ -24567,23 +25120,51 @@ var HorizonsParser = class {
|
|
|
24567
25120
|
};
|
|
24568
25121
|
}
|
|
24569
25122
|
// ==================== Private Helper Methods ====================
|
|
25123
|
+
/** Metadata labels recognized in Horizons output headers. */
|
|
25124
|
+
static metadataLabels_ = [
|
|
25125
|
+
"Target body name",
|
|
25126
|
+
"Center body name",
|
|
25127
|
+
"Center-site name",
|
|
25128
|
+
"Output type",
|
|
25129
|
+
"Reference frame",
|
|
25130
|
+
"Output units"
|
|
25131
|
+
];
|
|
24570
25132
|
/**
|
|
24571
|
-
* Extracts a value from a
|
|
25133
|
+
* Extracts a metadata value from a `Label : value` line, tolerating the
|
|
25134
|
+
* column-aligned padding Horizons inserts before the colon (e.g.
|
|
25135
|
+
* "Output units : KM-S"). Returns null if the line does not carry the
|
|
25136
|
+
* given label.
|
|
24572
25137
|
*/
|
|
24573
|
-
static
|
|
24574
|
-
|
|
24575
|
-
|
|
25138
|
+
static extractLabeledValue_(line, label) {
|
|
25139
|
+
if (!line.startsWith(label)) {
|
|
25140
|
+
return null;
|
|
25141
|
+
}
|
|
25142
|
+
const rest = line.substring(label.length);
|
|
25143
|
+
const colonIdx = rest.indexOf(":");
|
|
25144
|
+
if (colonIdx === -1 || rest.substring(0, colonIdx).trim().length > 0) {
|
|
25145
|
+
return null;
|
|
25146
|
+
}
|
|
25147
|
+
return rest.substring(colonIdx + 1).trim().split(/\s{2,}/u)[0].trim();
|
|
25148
|
+
}
|
|
25149
|
+
/** Returns the first recognized metadata label/value on the line, if any. */
|
|
25150
|
+
static matchMetadataLine_(line) {
|
|
25151
|
+
for (const label of this.metadataLabels_) {
|
|
25152
|
+
const value = this.extractLabeledValue_(line, label);
|
|
25153
|
+
if (value !== null) {
|
|
25154
|
+
return { label, value };
|
|
25155
|
+
}
|
|
25156
|
+
}
|
|
25157
|
+
return null;
|
|
24576
25158
|
}
|
|
24577
25159
|
/**
|
|
24578
|
-
*
|
|
25160
|
+
* Strips parenthetical descriptors and IDs from a target body name.
|
|
24579
25161
|
*
|
|
24580
25162
|
* Horizons format examples:
|
|
24581
|
-
* - "1 Ceres
|
|
24582
|
-
* - "Artemis II (spacecraft) (-1024)
|
|
24583
|
-
* - "Mars (499)"
|
|
25163
|
+
* - "1 Ceres" → "1 Ceres"
|
|
25164
|
+
* - "Artemis II (spacecraft) (-1024)" → "Artemis II"
|
|
25165
|
+
* - "Mars (499)" → "Mars"
|
|
24584
25166
|
*/
|
|
24585
|
-
static
|
|
24586
|
-
const raw = this.extractValue_(line, key);
|
|
25167
|
+
static stripNameSuffixes_(raw) {
|
|
24587
25168
|
const parenIdx = raw.search(/\s\(/u);
|
|
24588
25169
|
if (parenIdx > 0) {
|
|
24589
25170
|
return raw.substring(0, parenIdx).trim();
|
|
@@ -24604,7 +25185,7 @@ var HorizonsParser = class {
|
|
|
24604
25185
|
if (isNaN(jd)) {
|
|
24605
25186
|
return null;
|
|
24606
25187
|
}
|
|
24607
|
-
const epoch2 =
|
|
25188
|
+
const epoch2 = tdbDateToEpochUtc(julianDateToDate(jd));
|
|
24608
25189
|
const x2 = parseFloat(parts[1]);
|
|
24609
25190
|
const y2 = parseFloat(parts[2]);
|
|
24610
25191
|
const z2 = parseFloat(parts[3]);
|
|
@@ -24652,7 +25233,8 @@ var HorizonsParser = class {
|
|
|
24652
25233
|
};
|
|
24653
25234
|
const month = monthMap[monthStr] ?? 0;
|
|
24654
25235
|
const date = new Date(Date.UTC(year, month, day, hour, minute, Math.floor(second), second % 1 * 1e3));
|
|
24655
|
-
const
|
|
25236
|
+
const isUt = /\bUTC?\b/u.test(line.slice(dateMatch[0].length));
|
|
25237
|
+
const epoch = isUt ? EpochUTC.fromDateTime(date) : tdbDateToEpochUtc(date);
|
|
24656
25238
|
let nextIndex = currentIndex + 1;
|
|
24657
25239
|
if (nextIndex >= allLines.length) {
|
|
24658
25240
|
return null;
|
|
@@ -26570,7 +27152,7 @@ var Transmitter = class _Transmitter extends CommunicationDevice {
|
|
|
26570
27152
|
const distance = this.getDistanceTo(receiver, date);
|
|
26571
27153
|
const fspl = calculateFspl(distance, this.frequency);
|
|
26572
27154
|
const receivedPower = this.eirp - fspl + receiver.antenna.gain - receiver.lineLoss;
|
|
26573
|
-
const noiseFloorDbw =
|
|
27155
|
+
const noiseFloorDbw = receiver.noiseFloor;
|
|
26574
27156
|
const snr = receivedPower - noiseFloorDbw;
|
|
26575
27157
|
return {
|
|
26576
27158
|
eirp: this.eirp,
|
|
@@ -27310,6 +27892,11 @@ var ChebyshevCoefficients = class _ChebyshevCoefficients {
|
|
|
27310
27892
|
this.cyd_ = _ChebyshevCoefficients._derivative(a, b, cy_);
|
|
27311
27893
|
this.czd_ = _ChebyshevCoefficients._derivative(a, b, cz_);
|
|
27312
27894
|
}
|
|
27895
|
+
a;
|
|
27896
|
+
b;
|
|
27897
|
+
cx_;
|
|
27898
|
+
cy_;
|
|
27899
|
+
cz_;
|
|
27313
27900
|
cxd_;
|
|
27314
27901
|
cyd_;
|
|
27315
27902
|
czd_;
|
|
@@ -27641,6 +28228,43 @@ var PlaneChangeBurn = class _PlaneChangeBurn {
|
|
|
27641
28228
|
}
|
|
27642
28229
|
};
|
|
27643
28230
|
|
|
28231
|
+
// src/maneuver/PropellantBudget.ts
|
|
28232
|
+
var PropellantBudget = class _PropellantBudget {
|
|
28233
|
+
/** Standard gravity used by the specific-impulse definition (m/s^2). */
|
|
28234
|
+
static g0 = 9.80665;
|
|
28235
|
+
/**
|
|
28236
|
+
* Mass remaining after an impulsive burn: `m * exp(-dv / (isp * g0))`.
|
|
28237
|
+
* @param massKg Total mass before the burn. (kg)
|
|
28238
|
+
* @param deltaVMs Delta-V of the burn. (m/s)
|
|
28239
|
+
* @param ispS Specific impulse of the propulsion system. (s)
|
|
28240
|
+
* @returns Total mass after the burn. (kg)
|
|
28241
|
+
*/
|
|
28242
|
+
static massAfterBurn(massKg, deltaVMs, ispS) {
|
|
28243
|
+
return massKg * Math.exp(-deltaVMs / (ispS * _PropellantBudget.g0));
|
|
28244
|
+
}
|
|
28245
|
+
/**
|
|
28246
|
+
* Propellant consumed by an impulsive burn.
|
|
28247
|
+
* @param massKg Total mass before the burn. (kg)
|
|
28248
|
+
* @param deltaVMs Delta-V of the burn. (m/s)
|
|
28249
|
+
* @param ispS Specific impulse of the propulsion system. (s)
|
|
28250
|
+
* @returns Propellant mass consumed. (kg)
|
|
28251
|
+
*/
|
|
28252
|
+
static propellantUsed(massKg, deltaVMs, ispS) {
|
|
28253
|
+
return massKg - _PropellantBudget.massAfterBurn(massKg, deltaVMs, ispS);
|
|
28254
|
+
}
|
|
28255
|
+
/**
|
|
28256
|
+
* Delta-V available before the propellant is exhausted:
|
|
28257
|
+
* `isp * g0 * ln(m / dry)`.
|
|
28258
|
+
* @param massKg Current total mass. (kg)
|
|
28259
|
+
* @param dryMassKg Dry mass (no propellant). (kg)
|
|
28260
|
+
* @param ispS Specific impulse of the propulsion system. (s)
|
|
28261
|
+
* @returns Remaining delta-V. (m/s)
|
|
28262
|
+
*/
|
|
28263
|
+
static deltaVRemaining(massKg, dryMassKg, ispS) {
|
|
28264
|
+
return ispS * _PropellantBudget.g0 * Math.log(massKg / dryMassKg);
|
|
28265
|
+
}
|
|
28266
|
+
};
|
|
28267
|
+
|
|
27644
28268
|
// src/maneuver/Waypoint.ts
|
|
27645
28269
|
var Waypoint = class _Waypoint {
|
|
27646
28270
|
epoch;
|
|
@@ -27829,6 +28453,11 @@ var TwoBurnOrbitTransfer = class _TwoBurnOrbitTransfer {
|
|
|
27829
28453
|
this.vTransB = vTransB;
|
|
27830
28454
|
this.tTrans = tTrans;
|
|
27831
28455
|
}
|
|
28456
|
+
vInit;
|
|
28457
|
+
vFinal;
|
|
28458
|
+
vTransA;
|
|
28459
|
+
vTransB;
|
|
28460
|
+
tTrans;
|
|
27832
28461
|
/**
|
|
27833
28462
|
* Calculates the parameters for a Hohmann transfer orbit between two circular orbits.
|
|
27834
28463
|
* @param rInit The initial radius of the orbit. (km)
|
|
@@ -27843,6 +28472,55 @@ var TwoBurnOrbitTransfer = class _TwoBurnOrbitTransfer {
|
|
|
27843
28472
|
const tTrans = Math.PI * Math.sqrt((rInit + rFinal) ** 3 / (8 * Earth.mu));
|
|
27844
28473
|
return new _TwoBurnOrbitTransfer(vInit, vFinal, vTransA, vTransB, tTrans);
|
|
27845
28474
|
}
|
|
28475
|
+
/**
|
|
28476
|
+
* Calculates a Hohmann transfer between two circular orbits with an optional
|
|
28477
|
+
* inclination change folded into one of the two burns as a single combined
|
|
28478
|
+
* impulse.
|
|
28479
|
+
*
|
|
28480
|
+
* The plane change is cheapest where the orbital velocity is lowest. For a
|
|
28481
|
+
* raising transfer that is the second burn (at rFinal); for a lowering
|
|
28482
|
+
* transfer it is the first burn (at rInit). The plane change is auto-assigned
|
|
28483
|
+
* to the cheaper burn and the assignment is reported in the result. A
|
|
28484
|
+
* combined burn costs `sqrt(v1^2 + v2^2 - 2 * v1 * v2 * cos(deltaInc))`
|
|
28485
|
+
* (law of cosines between the pre- and post-burn velocity vectors).
|
|
28486
|
+
* @param rInit The initial orbit radius. (km)
|
|
28487
|
+
* @param rFinal The final orbit radius. (km)
|
|
28488
|
+
* @param deltaIncRad The inclination change. (rad)
|
|
28489
|
+
* @returns Per-burn delta-V magnitudes, total, transfer time, and the plane-change assignment.
|
|
28490
|
+
*/
|
|
28491
|
+
static hohmannTransferWithPlaneChange(rInit, rFinal, deltaIncRad) {
|
|
28492
|
+
const vInit = Math.sqrt(Earth.mu / rInit);
|
|
28493
|
+
const vFinal = Math.sqrt(Earth.mu / rFinal);
|
|
28494
|
+
const vTransInit = Math.sqrt(Earth.mu * (2 / rInit - 2 / (rInit + rFinal)));
|
|
28495
|
+
const vTransFinal = Math.sqrt(Earth.mu * (2 / rFinal - 2 / (rInit + rFinal)));
|
|
28496
|
+
const tTrans = Math.PI * Math.sqrt((rInit + rFinal) ** 3 / (8 * Earth.mu));
|
|
28497
|
+
const combined = (v1, v2) => Math.sqrt(v1 ** 2 + v2 ** 2 - 2 * v1 * v2 * Math.cos(deltaIncRad));
|
|
28498
|
+
let planeChangeBurn;
|
|
28499
|
+
let deltaV1;
|
|
28500
|
+
let deltaV2;
|
|
28501
|
+
if (deltaIncRad === 0) {
|
|
28502
|
+
planeChangeBurn = "none";
|
|
28503
|
+
deltaV1 = Math.abs(vTransInit - vInit);
|
|
28504
|
+
deltaV2 = Math.abs(vFinal - vTransFinal);
|
|
28505
|
+
} else if (rFinal >= rInit) {
|
|
28506
|
+
planeChangeBurn = "burn2";
|
|
28507
|
+
deltaV1 = Math.abs(vTransInit - vInit);
|
|
28508
|
+
deltaV2 = combined(vTransFinal, vFinal);
|
|
28509
|
+
} else {
|
|
28510
|
+
planeChangeBurn = "burn1";
|
|
28511
|
+
deltaV1 = combined(vInit, vTransInit);
|
|
28512
|
+
deltaV2 = Math.abs(vFinal - vTransFinal);
|
|
28513
|
+
}
|
|
28514
|
+
return {
|
|
28515
|
+
vInit,
|
|
28516
|
+
vFinal,
|
|
28517
|
+
deltaV1,
|
|
28518
|
+
deltaV2,
|
|
28519
|
+
deltaVTotal: deltaV1 + deltaV2,
|
|
28520
|
+
tTrans,
|
|
28521
|
+
planeChangeBurn
|
|
28522
|
+
};
|
|
28523
|
+
}
|
|
27846
28524
|
// / Return the total delta-velocity magnitude for both maneuvers _(km/s)_.
|
|
27847
28525
|
get deltaV() {
|
|
27848
28526
|
return Math.abs(this.vTransA) + Math.abs(this.vTransB);
|
|
@@ -27881,6 +28559,7 @@ var ChebyshevCompressor = class _ChebyshevCompressor {
|
|
|
27881
28559
|
constructor(interpolator_) {
|
|
27882
28560
|
this.interpolator_ = interpolator_;
|
|
27883
28561
|
}
|
|
28562
|
+
interpolator_;
|
|
27884
28563
|
/** Returns the cosine of π times x. */
|
|
27885
28564
|
static cosPi_(x) {
|
|
27886
28565
|
return Math.cos(Math.PI * x);
|
|
@@ -28121,6 +28800,8 @@ var SimpleLinearRegression = class _SimpleLinearRegression {
|
|
|
28121
28800
|
this.ys = ys;
|
|
28122
28801
|
this.update();
|
|
28123
28802
|
}
|
|
28803
|
+
xs;
|
|
28804
|
+
ys;
|
|
28124
28805
|
/** Line slope (m in y = mx + b). */
|
|
28125
28806
|
slope_ = 0;
|
|
28126
28807
|
/** Y-axis intercept (b in y = mx + b). */
|
|
@@ -29319,8 +30000,7 @@ var LaunchTrajectoryGenerator = class _LaunchTrajectoryGenerator {
|
|
|
29319
30000
|
azimuthRad,
|
|
29320
30001
|
dr
|
|
29321
30002
|
);
|
|
29322
|
-
const
|
|
29323
|
-
const pos = _LaunchTrajectoryGenerator.geodeticToEci_(lat, lon, alt, gmst);
|
|
30003
|
+
const pos = _LaunchTrajectoryGenerator.geodeticToJ2000_(lat, lon, alt, epoch);
|
|
29324
30004
|
positions.push({ epoch, pos });
|
|
29325
30005
|
}
|
|
29326
30006
|
for (let i = 0; i < positions.length; i++) {
|
|
@@ -29418,20 +30098,17 @@ var LaunchTrajectoryGenerator = class _LaunchTrajectoryGenerator {
|
|
|
29418
30098
|
return { lat, lon };
|
|
29419
30099
|
}
|
|
29420
30100
|
/**
|
|
29421
|
-
* Convert geodetic coordinates to
|
|
29422
|
-
*
|
|
30101
|
+
* Convert geodetic coordinates to a J2000 position.
|
|
30102
|
+
*
|
|
30103
|
+
* The point is placed on the WGS84 ellipsoid in ITRF and rotated into J2000 with
|
|
30104
|
+
* precession, nutation and sidereal time, so the trajectory starts on the same launch
|
|
30105
|
+
* site a `GroundObject` at those coordinates reports. (A spherical Earth rotated by GMST
|
|
30106
|
+
* alone, the earlier model, is a TEME position on the wrong surface: the pad sat ~17 km
|
|
30107
|
+
* off on the sphere plus up to ~40 km of precession and nutation since J2000.)
|
|
29423
30108
|
*/
|
|
29424
|
-
static
|
|
29425
|
-
const
|
|
29426
|
-
|
|
29427
|
-
const cosLon = Math.cos(lonRad + gmst);
|
|
29428
|
-
const sinLon = Math.sin(lonRad + gmst);
|
|
29429
|
-
const r = Earth.radiusMean + altKm;
|
|
29430
|
-
return new Vector3D(
|
|
29431
|
-
r * cosLat * cosLon,
|
|
29432
|
-
r * cosLat * sinLon,
|
|
29433
|
-
r * sinLat
|
|
29434
|
-
);
|
|
30109
|
+
static geodeticToJ2000_(latRad, lonRad, altKm, epoch) {
|
|
30110
|
+
const lonWrapped = Math.atan2(Math.sin(lonRad), Math.cos(lonRad));
|
|
30111
|
+
return new Geodetic(latRad, lonWrapped, altKm).toITRF(epoch).toJ2000().position;
|
|
29435
30112
|
}
|
|
29436
30113
|
/**
|
|
29437
30114
|
* Compute the orbital velocity vector at the insertion point.
|
|
@@ -29552,390 +30229,175 @@ var LaunchTrajectoryGenerator = class _LaunchTrajectoryGenerator {
|
|
|
29552
30229
|
}
|
|
29553
30230
|
};
|
|
29554
30231
|
|
|
29555
|
-
// src/orbit-design/
|
|
29556
|
-
var
|
|
29557
|
-
|
|
29558
|
-
|
|
29559
|
-
|
|
29560
|
-
|
|
29561
|
-
|
|
29562
|
-
|
|
29563
|
-
|
|
29564
|
-
// degrees
|
|
29565
|
-
sat_;
|
|
29566
|
-
goalParams_;
|
|
29567
|
-
now_;
|
|
29568
|
-
goalDirection_;
|
|
29569
|
-
debugCallback_;
|
|
29570
|
-
currentParams_;
|
|
29571
|
-
lastLatitude_ = null;
|
|
29572
|
-
currentDirection_ = null;
|
|
29573
|
-
constructor(sat, goalLat, goalLon, goalDirection, now, goalAlt, raanOffset = 0, options) {
|
|
29574
|
-
this.sat_ = sat;
|
|
29575
|
-
this.now_ = now;
|
|
29576
|
-
this.goalDirection_ = goalDirection;
|
|
29577
|
-
this.debugCallback_ = options?.debugCallback;
|
|
29578
|
-
this.goalParams_ = {
|
|
29579
|
-
meanAnomaly: 0,
|
|
29580
|
-
argOfPerigee: sat.argOfPerigee,
|
|
29581
|
-
raan: sat.rightAscension + raanOffset,
|
|
29582
|
-
altitude: goalAlt || 0,
|
|
29583
|
-
latitude: goalLat,
|
|
29584
|
-
longitude: goalLon
|
|
30232
|
+
// src/orbit-design/LaunchWindow.ts
|
|
30233
|
+
var LaunchWindowFinder = class _LaunchWindowFinder {
|
|
30234
|
+
options_;
|
|
30235
|
+
constructor(options) {
|
|
30236
|
+
this.options_ = {
|
|
30237
|
+
searchHours: 24,
|
|
30238
|
+
coarseStepSec: 60,
|
|
30239
|
+
fineStepSec: 1,
|
|
30240
|
+
...options
|
|
29585
30241
|
};
|
|
29586
|
-
this.currentParams_ = this.getCurrentOrbitParams_();
|
|
29587
30242
|
}
|
|
29588
30243
|
/**
|
|
29589
|
-
*
|
|
29590
|
-
*
|
|
30244
|
+
* Whether the site can inject directly into the desired inclination: the
|
|
30245
|
+
* orbit plane must reach the site's latitude, i.e. |sin(lat)| <= sin(inc).
|
|
29591
30246
|
*/
|
|
29592
|
-
|
|
29593
|
-
|
|
29594
|
-
|
|
29595
|
-
|
|
29596
|
-
this.debug_(`Original perigee position: ${perigeeParams.latitude} - ${perigeeParams.longitude} - ${perigeeParams.altitude}`);
|
|
29597
|
-
this.updateOrbitWithoutDirectionCheck_({
|
|
29598
|
-
meanAnomaly: 0
|
|
29599
|
-
});
|
|
29600
|
-
this.debug_(`Positioned at initial perigee: ${this.currentParams_.latitude} - ${this.currentParams_.longitude} - ${this.currentParams_.altitude}`);
|
|
29601
|
-
let bestArgPerigee = this.currentParams_.argOfPerigee;
|
|
29602
|
-
let bestLatError = Infinity;
|
|
29603
|
-
for (let argPer = 0; argPer < 360; argPer += 0.25) {
|
|
29604
|
-
this.updateOrbitWithoutDirectionCheck_({
|
|
29605
|
-
meanAnomaly: 0,
|
|
29606
|
-
argOfPerigee: argPer
|
|
29607
|
-
});
|
|
29608
|
-
const latError = Math.abs(this.currentParams_.latitude - perigeeParams.latitude);
|
|
29609
|
-
if (latError < bestLatError) {
|
|
29610
|
-
bestLatError = latError;
|
|
29611
|
-
bestArgPerigee = argPer;
|
|
29612
|
-
this.debug_(`New best arg perigee: ${argPer} with lat error ${latError}`);
|
|
29613
|
-
}
|
|
29614
|
-
if (latError <= _OrbitFinder.MAX_LAT_ERROR) {
|
|
29615
|
-
break;
|
|
29616
|
-
}
|
|
29617
|
-
}
|
|
29618
|
-
this.updateOrbitWithoutDirectionCheck_({
|
|
29619
|
-
meanAnomaly: 0,
|
|
29620
|
-
argOfPerigee: bestArgPerigee
|
|
29621
|
-
});
|
|
29622
|
-
this.debug_(`After arg perigee adjustment: ${this.currentParams_.latitude} - ${this.currentParams_.longitude} - ${this.currentParams_.altitude}`);
|
|
29623
|
-
this.lastLatitude_ = null;
|
|
29624
|
-
this.currentDirection_ = null;
|
|
29625
|
-
const finalMeanAResult = this.meanACalcLoop_(this.goalDirection_);
|
|
29626
|
-
if (finalMeanAResult !== 1 /* Success */) {
|
|
29627
|
-
return ["Error", "Failed to find final target position"];
|
|
29628
|
-
}
|
|
29629
|
-
const successfulDirection = this.currentDirection_;
|
|
29630
|
-
const newRaan = this.linearSearchRaan_();
|
|
29631
|
-
this.currentDirection_ = successfulDirection;
|
|
29632
|
-
const finalSuccess = this.updateOrbit_({
|
|
29633
|
-
meanAnomaly: this.currentParams_.meanAnomaly,
|
|
29634
|
-
raan: newRaan,
|
|
29635
|
-
argOfPerigee: bestArgPerigee
|
|
29636
|
-
});
|
|
29637
|
-
if (!finalSuccess) {
|
|
29638
|
-
return ["Error", "Final position adjustment failed"];
|
|
29639
|
-
}
|
|
29640
|
-
this.debug_(`Final position: ${this.currentParams_.latitude}, ${this.currentParams_.longitude}, ${this.currentParams_.altitude}`);
|
|
29641
|
-
this.debug_(`Target altitude: ${this.goalParams_.altitude}, Current altitude: ${this.currentParams_.altitude}`);
|
|
29642
|
-
} else {
|
|
29643
|
-
const result = this.meanACalcLoop_(this.goalDirection_);
|
|
29644
|
-
if (result !== 1 /* Success */) {
|
|
29645
|
-
return ["Error", `Failed to find solution with ${this.goalDirection_} bound direction`];
|
|
29646
|
-
}
|
|
29647
|
-
const meanAnomalySuccess = this.updateOrbit_({
|
|
29648
|
-
meanAnomaly: this.currentParams_.meanAnomaly
|
|
29649
|
-
});
|
|
29650
|
-
if (!meanAnomalySuccess) {
|
|
29651
|
-
return ["Error", "Mean anomaly adjustment resulted in incorrect direction"];
|
|
29652
|
-
}
|
|
29653
|
-
const successfulDirection = this.currentDirection_;
|
|
29654
|
-
const newRaan = this.linearSearchRaan_();
|
|
29655
|
-
this.currentDirection_ = successfulDirection;
|
|
29656
|
-
const combinedSuccess = this.updateOrbit_({
|
|
29657
|
-
meanAnomaly: this.currentParams_.meanAnomaly,
|
|
29658
|
-
raan: newRaan
|
|
29659
|
-
});
|
|
29660
|
-
if (!combinedSuccess) {
|
|
29661
|
-
return ["Error", "Combined mean anomaly and RAAN adjustment failed"];
|
|
29662
|
-
}
|
|
29663
|
-
}
|
|
29664
|
-
return [
|
|
29665
|
-
this.generateTle1_(),
|
|
29666
|
-
this.generateTle2_(this.currentParams_)
|
|
29667
|
-
];
|
|
29668
|
-
} catch (error) {
|
|
29669
|
-
this.debug_(`Error in rotateOrbitToLatLon: ${error.message}`);
|
|
29670
|
-
return ["Error", error.message];
|
|
29671
|
-
}
|
|
29672
|
-
}
|
|
29673
|
-
debug_(message) {
|
|
29674
|
-
if (this.debugCallback_) {
|
|
29675
|
-
this.debugCallback_(message);
|
|
30247
|
+
isGeometryPossible() {
|
|
30248
|
+
const sinInc = Math.sin(this.options_.inclination * DEG2RAD);
|
|
30249
|
+
if (sinInc === 0) {
|
|
30250
|
+
return this.options_.siteLat === 0;
|
|
29676
30251
|
}
|
|
30252
|
+
return Math.abs(Math.sin(this.options_.siteLat * DEG2RAD) / sinInc) <= 1;
|
|
29677
30253
|
}
|
|
29678
|
-
|
|
29679
|
-
|
|
29680
|
-
|
|
29681
|
-
|
|
29682
|
-
|
|
29683
|
-
|
|
29684
|
-
|
|
29685
|
-
|
|
29686
|
-
|
|
29687
|
-
|
|
29688
|
-
const
|
|
29689
|
-
|
|
29690
|
-
|
|
29691
|
-
}
|
|
29692
|
-
const positionEci = Sgp4.propagate(this.sat_.satrec, m).position;
|
|
29693
|
-
const { lat, lon, alt } = eci2lla(positionEci, gmst);
|
|
29694
|
-
return {
|
|
29695
|
-
meanAnomaly: this.sat_.meanAnomaly,
|
|
29696
|
-
argOfPerigee: this.sat_.argOfPerigee,
|
|
29697
|
-
raan: this.sat_.rightAscension,
|
|
29698
|
-
altitude: alt,
|
|
29699
|
-
latitude: lat,
|
|
29700
|
-
longitude: lon
|
|
29701
|
-
};
|
|
30254
|
+
/**
|
|
30255
|
+
* RAAN of the (only) plane with the configured inclination that contains the
|
|
30256
|
+
* launch site at the given time on the configured leg. (deg, 0-360)
|
|
30257
|
+
*/
|
|
30258
|
+
achievableRaan(time) {
|
|
30259
|
+
const inc = this.options_.inclination * DEG2RAD;
|
|
30260
|
+
const lat = this.options_.siteLat * DEG2RAD;
|
|
30261
|
+
const sinU = Math.sin(lat) / Math.sin(inc);
|
|
30262
|
+
const u = this.options_.direction === "N" ? Math.asin(sinU) : Math.PI - Math.asin(sinU);
|
|
30263
|
+
const nodeToSite = Math.atan2(Math.sin(u) * Math.cos(inc), Math.cos(u));
|
|
30264
|
+
const gmst = Sgp4.gstime(_LaunchWindowFinder.julianDate_(time));
|
|
30265
|
+
const lst = gmst + this.options_.siteLon * DEG2RAD;
|
|
30266
|
+
return _LaunchWindowFinder.normalizeDeg_((lst - nodeToSite) * RAD2DEG);
|
|
29702
30267
|
}
|
|
29703
|
-
|
|
29704
|
-
|
|
29705
|
-
|
|
29706
|
-
|
|
30268
|
+
/**
|
|
30269
|
+
* Scans the search window for the launch time minimizing |ΔRAAN| to the
|
|
30270
|
+
* target plane, then refines around the best coarse sample. Returns null
|
|
30271
|
+
* when the geometry is impossible (inclination below site latitude).
|
|
30272
|
+
*/
|
|
30273
|
+
findBestLaunchTime() {
|
|
30274
|
+
if (!this.isGeometryPossible()) {
|
|
29707
30275
|
return null;
|
|
29708
30276
|
}
|
|
29709
|
-
|
|
29710
|
-
|
|
29711
|
-
|
|
29712
|
-
|
|
29713
|
-
this.debug_(`Current latitude: ${this.lastLatitude_} - New latitude: ${newLat} - Direction: ${direction}`);
|
|
29714
|
-
this.lastLatitude_ = newLat;
|
|
29715
|
-
this.debug_(`New direction: ${direction}`);
|
|
29716
|
-
return direction;
|
|
29717
|
-
}
|
|
29718
|
-
isCorrectDirection_() {
|
|
29719
|
-
this.debug_(`Current direction: ${this.currentDirection_} - Goal direction: ${this.goalDirection_}`);
|
|
29720
|
-
return this.currentDirection_ === this.goalDirection_;
|
|
29721
|
-
}
|
|
29722
|
-
updateOrbit_(newParams) {
|
|
29723
|
-
const tle1 = this.generateTle1_();
|
|
29724
|
-
const tle2 = this.generateTle2_(newParams);
|
|
29725
|
-
const satrec = Sgp4.createSatrec(tle1, tle2);
|
|
29726
|
-
if (!satrec) {
|
|
29727
|
-
throw new Error("Invalid orbit parameters");
|
|
29728
|
-
}
|
|
29729
|
-
const { m, gmst } = this.calculateTimeVariables_(this.now_, satrec);
|
|
29730
|
-
if (m === null) {
|
|
29731
|
-
throw new Error("Invalid time variables");
|
|
29732
|
-
}
|
|
29733
|
-
const positionEci = Sgp4.propagate(satrec, m).position;
|
|
29734
|
-
const { lat, lon, alt } = eci2lla(positionEci, gmst);
|
|
29735
|
-
const newDirection = this.determineDirection_(lat);
|
|
29736
|
-
if (newDirection !== null) {
|
|
29737
|
-
this.currentDirection_ = newDirection;
|
|
29738
|
-
}
|
|
29739
|
-
this.currentParams_ = {
|
|
29740
|
-
...this.currentParams_,
|
|
29741
|
-
...newParams,
|
|
29742
|
-
latitude: lat,
|
|
29743
|
-
longitude: lon,
|
|
29744
|
-
altitude: alt
|
|
29745
|
-
};
|
|
29746
|
-
return this.isCorrectDirection_();
|
|
29747
|
-
}
|
|
29748
|
-
meanACalcLoop_(direction) {
|
|
29749
|
-
const startVal = direction === "N" ? 0 : 180;
|
|
29750
|
-
const endVal = direction === "N" ? 360 : 540;
|
|
29751
|
-
for (let posVal = startVal * 10; posVal < endVal * 10; posVal += 0.25) {
|
|
29752
|
-
const normalizedVal = posVal % (360 * 10);
|
|
29753
|
-
const result = this.meanACalc_(normalizedVal, this.now_);
|
|
29754
|
-
if (result === 1 /* Success */) {
|
|
29755
|
-
if (this.currentDirection_ !== direction) {
|
|
29756
|
-
posVal += 20;
|
|
29757
|
-
} else {
|
|
29758
|
-
return 1 /* Success */;
|
|
29759
|
-
}
|
|
29760
|
-
}
|
|
29761
|
-
if (result === 3 /* Far */) {
|
|
29762
|
-
posVal += 100;
|
|
29763
|
-
}
|
|
29764
|
-
if (result === 2 /* Error */) {
|
|
29765
|
-
return 2 /* Error */;
|
|
29766
|
-
}
|
|
29767
|
-
}
|
|
29768
|
-
return 0 /* Near */;
|
|
29769
|
-
}
|
|
29770
|
-
meanACalc_(meana, now) {
|
|
29771
|
-
meana /= 10;
|
|
29772
|
-
meana %= 360;
|
|
29773
|
-
const tle1 = this.generateTle1_();
|
|
29774
|
-
const tle2 = this.generateTle2_({ meanAnomaly: meana });
|
|
29775
|
-
const satrec = Sgp4.createSatrec(tle1, tle2);
|
|
29776
|
-
if (!satrec) {
|
|
29777
|
-
return 2 /* Error */;
|
|
29778
|
-
}
|
|
29779
|
-
const { m, gmst } = this.calculateTimeVariables_(now, satrec);
|
|
29780
|
-
if (m === null) {
|
|
29781
|
-
return 2 /* Error */;
|
|
29782
|
-
}
|
|
29783
|
-
const positionEci = Sgp4.propagate(satrec, m).position;
|
|
29784
|
-
const { lat } = eci2lla(positionEci, gmst);
|
|
29785
|
-
if (this.lastLatitude_ !== null) {
|
|
29786
|
-
if (Math.abs(lat - this.lastLatitude_) > 1e-3) {
|
|
29787
|
-
this.currentDirection_ = lat > this.lastLatitude_ ? "N" : "S";
|
|
29788
|
-
}
|
|
29789
|
-
}
|
|
29790
|
-
this.lastLatitude_ = lat;
|
|
29791
|
-
if (Math.abs(lat - this.goalParams_.latitude) <= _OrbitFinder.MAX_LAT_ERROR) {
|
|
29792
|
-
if (this.currentDirection_ === this.goalDirection_) {
|
|
29793
|
-
this.currentParams_.meanAnomaly = meana;
|
|
29794
|
-
return 1 /* Success */;
|
|
29795
|
-
}
|
|
29796
|
-
}
|
|
29797
|
-
if (Math.abs(lat - this.goalParams_.latitude) > 11) {
|
|
29798
|
-
return 3 /* Far */;
|
|
29799
|
-
}
|
|
29800
|
-
return 0 /* Near */;
|
|
29801
|
-
}
|
|
29802
|
-
linearSearchRaan_() {
|
|
29803
|
-
let bestValue = this.currentParams_.raan;
|
|
30277
|
+
const startMs = this.options_.startTime.getTime();
|
|
30278
|
+
const endMs = startMs + this.options_.searchHours * 3600 * 1e3;
|
|
30279
|
+
const coarseMs = this.options_.coarseStepSec * 1e3;
|
|
30280
|
+
let bestMs = startMs;
|
|
29804
30281
|
let bestError = Infinity;
|
|
29805
|
-
for (let
|
|
29806
|
-
this.
|
|
29807
|
-
|
|
29808
|
-
if (error < Math.abs(bestError)) {
|
|
30282
|
+
for (let ms = startMs; ms <= endMs; ms += coarseMs) {
|
|
30283
|
+
const error = Math.abs(this.raanErrorAt_(new Date(ms)));
|
|
30284
|
+
if (error < bestError) {
|
|
29809
30285
|
bestError = error;
|
|
29810
|
-
|
|
29811
|
-
}
|
|
29812
|
-
if (error < _OrbitFinder.MAX_LON_ERROR) {
|
|
29813
|
-
for (let fineRaan = bestValue - 5; fineRaan <= bestValue + 5; fineRaan += _OrbitFinder.FINE_STEP) {
|
|
29814
|
-
const normalizedRaan = (fineRaan % 360 + 360) % 360;
|
|
29815
|
-
this.updateOrbit_({ raan: normalizedRaan });
|
|
29816
|
-
const fineError = Math.abs(this.calculateError_("raan"));
|
|
29817
|
-
if (fineError < error) {
|
|
29818
|
-
bestValue = normalizedRaan;
|
|
29819
|
-
bestError = fineError;
|
|
29820
|
-
}
|
|
29821
|
-
}
|
|
29822
|
-
return bestValue;
|
|
30286
|
+
bestMs = ms;
|
|
29823
30287
|
}
|
|
29824
30288
|
}
|
|
29825
|
-
|
|
29826
|
-
|
|
29827
|
-
|
|
29828
|
-
|
|
29829
|
-
|
|
29830
|
-
|
|
29831
|
-
|
|
29832
|
-
|
|
29833
|
-
}
|
|
29834
|
-
return diff;
|
|
29835
|
-
}
|
|
29836
|
-
calculateError_(param) {
|
|
29837
|
-
switch (param) {
|
|
29838
|
-
case "meanAnomaly":
|
|
29839
|
-
return this.currentParams_.latitude - this.goalParams_.latitude;
|
|
29840
|
-
case "raan":
|
|
29841
|
-
return this.normalizeAngleDifference_(
|
|
29842
|
-
this.currentParams_.longitude,
|
|
29843
|
-
this.goalParams_.longitude
|
|
29844
|
-
);
|
|
29845
|
-
case "argOfPerigee":
|
|
29846
|
-
this.debug_(`Current altitude: ${this.currentParams_.altitude} - Goal altitude: ${this.goalParams_.altitude}`);
|
|
29847
|
-
return this.currentParams_.altitude - this.goalParams_.altitude;
|
|
29848
|
-
default:
|
|
29849
|
-
return 0;
|
|
29850
|
-
}
|
|
29851
|
-
}
|
|
29852
|
-
updateOrbitWithoutDirectionCheck_(newParams) {
|
|
29853
|
-
const tle1 = this.generateTle1_();
|
|
29854
|
-
const tle2 = this.generateTle2_(newParams);
|
|
29855
|
-
const satrec = Sgp4.createSatrec(tle1, tle2);
|
|
29856
|
-
if (!satrec) {
|
|
29857
|
-
throw new Error("Invalid orbit parameters");
|
|
29858
|
-
}
|
|
29859
|
-
const { m, gmst } = this.calculateTimeVariables_(this.now_, satrec);
|
|
29860
|
-
if (m === null) {
|
|
29861
|
-
throw new Error("Invalid time variables");
|
|
29862
|
-
}
|
|
29863
|
-
const positionEci = Sgp4.propagate(satrec, m).position;
|
|
29864
|
-
const { lat, lon, alt } = eci2lla(positionEci, gmst);
|
|
29865
|
-
this.currentParams_ = {
|
|
29866
|
-
...this.currentParams_,
|
|
29867
|
-
...newParams,
|
|
29868
|
-
latitude: lat,
|
|
29869
|
-
longitude: lon,
|
|
29870
|
-
altitude: alt
|
|
29871
|
-
};
|
|
29872
|
-
}
|
|
29873
|
-
findPerigeePosition_() {
|
|
29874
|
-
let lowestAltitude = Infinity;
|
|
29875
|
-
let perigeeParams = null;
|
|
29876
|
-
for (let meanA = 0; meanA < 360; meanA += 0.05) {
|
|
29877
|
-
const tle1 = this.generateTle1_();
|
|
29878
|
-
const tle2 = this.generateTle2_({ meanAnomaly: meanA });
|
|
29879
|
-
const satrec = Sgp4.createSatrec(tle1, tle2);
|
|
29880
|
-
if (!satrec) {
|
|
29881
|
-
continue;
|
|
29882
|
-
}
|
|
29883
|
-
const { m, gmst } = this.calculateTimeVariables_(this.now_, satrec);
|
|
29884
|
-
if (m === null) {
|
|
29885
|
-
throw new Error("Invalid time variables");
|
|
29886
|
-
}
|
|
29887
|
-
const positionEci = Sgp4.propagate(satrec, m).position;
|
|
29888
|
-
const { lat, lon, alt } = eci2lla(positionEci, gmst);
|
|
29889
|
-
if (alt < lowestAltitude) {
|
|
29890
|
-
lowestAltitude = alt;
|
|
29891
|
-
perigeeParams = {
|
|
29892
|
-
meanAnomaly: meanA,
|
|
29893
|
-
argOfPerigee: this.currentParams_.argOfPerigee,
|
|
29894
|
-
raan: this.currentParams_.raan,
|
|
29895
|
-
altitude: alt,
|
|
29896
|
-
latitude: lat,
|
|
29897
|
-
longitude: lon
|
|
29898
|
-
};
|
|
30289
|
+
const fineMs = this.options_.fineStepSec * 1e3;
|
|
30290
|
+
const refineStart = Math.max(startMs, bestMs - coarseMs);
|
|
30291
|
+
const refineEnd = Math.min(endMs, bestMs + coarseMs);
|
|
30292
|
+
for (let ms = refineStart; ms <= refineEnd; ms += fineMs) {
|
|
30293
|
+
const error = Math.abs(this.raanErrorAt_(new Date(ms)));
|
|
30294
|
+
if (error < bestError) {
|
|
30295
|
+
bestError = error;
|
|
30296
|
+
bestMs = ms;
|
|
29899
30297
|
}
|
|
29900
30298
|
}
|
|
29901
|
-
|
|
29902
|
-
|
|
29903
|
-
|
|
29904
|
-
|
|
29905
|
-
|
|
30299
|
+
const time = new Date(bestMs);
|
|
30300
|
+
const achievableRaan = this.achievableRaan(time);
|
|
30301
|
+
const targetRaan = this.targetRaanAt_(time);
|
|
30302
|
+
return {
|
|
30303
|
+
time,
|
|
30304
|
+
achievableRaan,
|
|
30305
|
+
targetRaan,
|
|
30306
|
+
raanError: _LaunchWindowFinder.normalize180_(achievableRaan - targetRaan)
|
|
30307
|
+
};
|
|
29906
30308
|
}
|
|
29907
30309
|
/**
|
|
29908
|
-
*
|
|
29909
|
-
*
|
|
29910
|
-
*
|
|
29911
|
-
* the full 9-digit canonical value, which would push the TLE past 69 chars
|
|
29912
|
-
* and break Sgp4.createSatrec. The input tle1 always has a valid 5-char
|
|
29913
|
-
* satnum at columns 2-6, and SGP4 itself doesn't care what value lives
|
|
29914
|
-
* there as long as line 1 and line 2 agree — so use that.
|
|
30310
|
+
* Mean RAAN of a satellite at a given time: the TLE epoch value advanced by
|
|
30311
|
+
* the secular nodal drift (J2). Suitable as the `targetRaan` function so the
|
|
30312
|
+
* target plane's regression over the search window is accounted for.
|
|
29915
30313
|
*/
|
|
29916
|
-
|
|
29917
|
-
|
|
30314
|
+
static meanRaanAt(satrec, time) {
|
|
30315
|
+
const minutesSinceEpoch = (_LaunchWindowFinder.julianDate_(time) - satrec.jdsatepoch) * MINUTES_PER_DAY;
|
|
30316
|
+
return _LaunchWindowFinder.normalizeDeg_((satrec.nodeo + satrec.nodedot * minutesSinceEpoch) * RAD2DEG);
|
|
29918
30317
|
}
|
|
29919
|
-
|
|
29920
|
-
|
|
30318
|
+
targetRaanAt_(time) {
|
|
30319
|
+
const target = this.options_.targetRaan;
|
|
30320
|
+
return typeof target === "function" ? target(time) : target;
|
|
29921
30321
|
}
|
|
29922
|
-
|
|
29923
|
-
|
|
29924
|
-
|
|
29925
|
-
|
|
29926
|
-
|
|
29927
|
-
|
|
29928
|
-
|
|
29929
|
-
|
|
29930
|
-
|
|
29931
|
-
|
|
29932
|
-
const
|
|
29933
|
-
|
|
29934
|
-
const meanMo = this.sat_.tle2.substring(52, 63);
|
|
29935
|
-
return `2 ${this.tleSatNum_()} ${inc} ${raan} ${ecc} ${argPer} ${meanA} ${meanMo} 10`;
|
|
30322
|
+
raanErrorAt_(time) {
|
|
30323
|
+
return _LaunchWindowFinder.normalize180_(this.achievableRaan(time) - this.targetRaanAt_(time));
|
|
30324
|
+
}
|
|
30325
|
+
static julianDate_(time) {
|
|
30326
|
+
return jday(time.getUTCFullYear(), time.getUTCMonth() + 1, time.getUTCDate(), time.getUTCHours(), time.getUTCMinutes(), time.getUTCSeconds()) + time.getUTCMilliseconds() * MILLISECONDS_TO_DAYS;
|
|
30327
|
+
}
|
|
30328
|
+
static normalizeDeg_(deg) {
|
|
30329
|
+
return (deg % 360 + 360) % 360;
|
|
30330
|
+
}
|
|
30331
|
+
static normalize180_(deg) {
|
|
30332
|
+
const normalized = (deg % 360 + 360) % 360;
|
|
30333
|
+
return normalized >= 180 ? normalized - 360 : normalized;
|
|
29936
30334
|
}
|
|
29937
30335
|
};
|
|
29938
30336
|
|
|
30337
|
+
// src/orbit-design/GroundTrackStateVector.ts
|
|
30338
|
+
var WGS84_A = 6378.137;
|
|
30339
|
+
var WGS84_F = 1 / 298.257223563;
|
|
30340
|
+
var WGS84_E2 = 2 * WGS84_F - WGS84_F * WGS84_F;
|
|
30341
|
+
var MIN_ECCENTRICITY = 1e-3;
|
|
30342
|
+
var groundTrackStateVector = (opts) => {
|
|
30343
|
+
const { semimajorAxisKm: a, inclinationRad: inc, latRad, lonRad, gmstRad, direction } = opts;
|
|
30344
|
+
const e = Math.max(opts.eccentricity, MIN_ECCENTRICITY);
|
|
30345
|
+
const rPerigee = a * (1 - e);
|
|
30346
|
+
const vPerigee = Math.sqrt(Earth.mu / a * ((1 + e) / (1 - e)));
|
|
30347
|
+
const sinLat = Math.sin(latRad);
|
|
30348
|
+
const cosLat = Math.cos(latRad);
|
|
30349
|
+
const nPrime = WGS84_A / Math.sqrt(1 - WGS84_E2 * sinLat * sinLat);
|
|
30350
|
+
const mPrime = nPrime * (1 - WGS84_E2);
|
|
30351
|
+
const c = cosLat * cosLat;
|
|
30352
|
+
const s = sinLat * sinLat;
|
|
30353
|
+
const bCoef = c * nPrime + s * mPrime;
|
|
30354
|
+
const cCoef = c * nPrime * nPrime + s * mPrime * mPrime - rPerigee * rPerigee;
|
|
30355
|
+
const disc = bCoef * bCoef - cCoef;
|
|
30356
|
+
if (disc < 0) {
|
|
30357
|
+
return null;
|
|
30358
|
+
}
|
|
30359
|
+
const height = -bCoef + Math.sqrt(disc);
|
|
30360
|
+
const cosLonE = Math.cos(lonRad);
|
|
30361
|
+
const sinLonE = Math.sin(lonRad);
|
|
30362
|
+
const xEcef = (nPrime + height) * cosLat * cosLonE;
|
|
30363
|
+
const yEcef = (nPrime + height) * cosLat * sinLonE;
|
|
30364
|
+
const zEcef = (mPrime + height) * sinLat;
|
|
30365
|
+
const cosG = Math.cos(gmstRad);
|
|
30366
|
+
const sinG = Math.sin(gmstRad);
|
|
30367
|
+
const position = {
|
|
30368
|
+
x: xEcef * cosG - yEcef * sinG,
|
|
30369
|
+
y: xEcef * sinG + yEcef * cosG,
|
|
30370
|
+
z: zEcef
|
|
30371
|
+
};
|
|
30372
|
+
const rMag = Math.hypot(position.x, position.y, position.z);
|
|
30373
|
+
const rHat = { x: position.x / rMag, y: position.y / rMag, z: position.z / rMag };
|
|
30374
|
+
const geocentricLat = Math.asin(rHat.z);
|
|
30375
|
+
const cosGeoLat = Math.cos(geocentricLat);
|
|
30376
|
+
const sinAz = Math.cos(inc) / cosGeoLat;
|
|
30377
|
+
if (sinAz < -1 || sinAz > 1) {
|
|
30378
|
+
return null;
|
|
30379
|
+
}
|
|
30380
|
+
const cosAz = (direction === "N" ? 1 : -1) * Math.sqrt(Math.max(0, 1 - sinAz * sinAz));
|
|
30381
|
+
const eastMag = Math.hypot(rHat.x, rHat.y);
|
|
30382
|
+
const eastHat = eastMag > 1e-9 ? { x: -rHat.y / eastMag, y: rHat.x / eastMag, z: 0 } : { x: 1, y: 0, z: 0 };
|
|
30383
|
+
const northHat = {
|
|
30384
|
+
x: rHat.y * eastHat.z - rHat.z * eastHat.y,
|
|
30385
|
+
y: rHat.z * eastHat.x - rHat.x * eastHat.z,
|
|
30386
|
+
z: rHat.x * eastHat.y - rHat.y * eastHat.x
|
|
30387
|
+
};
|
|
30388
|
+
const velocity = {
|
|
30389
|
+
x: vPerigee * (sinAz * eastHat.x + cosAz * northHat.x),
|
|
30390
|
+
y: vPerigee * (sinAz * eastHat.y + cosAz * northHat.y),
|
|
30391
|
+
z: vPerigee * (sinAz * eastHat.z + cosAz * northHat.z)
|
|
30392
|
+
};
|
|
30393
|
+
return { position, velocity };
|
|
30394
|
+
};
|
|
30395
|
+
var semimajorAxisFromMeanMotion = (meanMotionRevPerDay) => {
|
|
30396
|
+
const n = meanMotionRevPerDay * 2 * Math.PI / 86400;
|
|
30397
|
+
return (Earth.mu / (n * n)) ** (1 / 3);
|
|
30398
|
+
};
|
|
30399
|
+
var perigeeSpeed = (semimajorAxisKm, eccentricity) => Math.sqrt(Earth.mu / semimajorAxisKm * ((1 + eccentricity) / (1 - eccentricity)));
|
|
30400
|
+
|
|
29939
30401
|
// src/orbit-design/RepeatGroundTrack.ts
|
|
29940
30402
|
var SUN_SYNC_PRECESSION_RAD_PER_SEC = 0.9856 * DEG2RAD / secondsPerDay;
|
|
29941
30403
|
var RepeatGroundTrack = class _RepeatGroundTrack {
|
|
@@ -30222,6 +30684,688 @@ var RepeatGroundTrack = class _RepeatGroundTrack {
|
|
|
30222
30684
|
return a;
|
|
30223
30685
|
}
|
|
30224
30686
|
};
|
|
30687
|
+
|
|
30688
|
+
// src/external/Sgp4WasmBase.ts
|
|
30689
|
+
var DS50_UTC_JD_EPOCH = 24332815e-1;
|
|
30690
|
+
var ERR_SAT_TEXT_BYTES = 512;
|
|
30691
|
+
var Sgp4WasmBase = class _Sgp4WasmBase {
|
|
30692
|
+
module_ = null;
|
|
30693
|
+
fs_ = null;
|
|
30694
|
+
bindings_ = null;
|
|
30695
|
+
loadPromise_ = null;
|
|
30696
|
+
isDynArrInitialized_ = false;
|
|
30697
|
+
scratchKeyPtr_ = 0;
|
|
30698
|
+
scratchResultPtr_ = 0;
|
|
30699
|
+
/**
|
|
30700
|
+
* Converts a UTC epoch to the Astro Standards ds50UTC time system
|
|
30701
|
+
* (days since 1949 December 31, 00:00 UTC).
|
|
30702
|
+
* @param epoch The epoch to convert.
|
|
30703
|
+
* @returns Days since 1950 UTC.
|
|
30704
|
+
*/
|
|
30705
|
+
static toDs50Utc(epoch) {
|
|
30706
|
+
return epoch.toJulianDate() - DS50_UTC_JD_EPOCH;
|
|
30707
|
+
}
|
|
30708
|
+
/**
|
|
30709
|
+
* Whether the wasm runtime has been loaded and initialized.
|
|
30710
|
+
*/
|
|
30711
|
+
get isLoaded() {
|
|
30712
|
+
return this.module_ !== null;
|
|
30713
|
+
}
|
|
30714
|
+
/**
|
|
30715
|
+
* Raw Emscripten Module escape hatch (`_malloc`, `ccall`, heap views, ...).
|
|
30716
|
+
* @throws {Sgp4WasmError} If the module is not loaded yet.
|
|
30717
|
+
*/
|
|
30718
|
+
get module() {
|
|
30719
|
+
if (!this.module_) {
|
|
30720
|
+
throw new Sgp4WasmError(`${this.constructor.name} is not loaded. Call load() first.`);
|
|
30721
|
+
}
|
|
30722
|
+
return this.module_;
|
|
30723
|
+
}
|
|
30724
|
+
/**
|
|
30725
|
+
* Raw Emscripten virtual filesystem escape hatch.
|
|
30726
|
+
* @throws {Sgp4WasmError} If the module is not loaded yet.
|
|
30727
|
+
*/
|
|
30728
|
+
get fs() {
|
|
30729
|
+
if (!this.fs_) {
|
|
30730
|
+
throw new Sgp4WasmError(`${this.constructor.name} is not loaded. Call load() first.`);
|
|
30731
|
+
}
|
|
30732
|
+
return this.fs_;
|
|
30733
|
+
}
|
|
30734
|
+
/**
|
|
30735
|
+
* Loads and initializes the wasm runtime. Idempotent — repeat calls return
|
|
30736
|
+
* the same in-flight or completed load.
|
|
30737
|
+
* @param options Artifact sources and stdout/stderr hooks. In Node both
|
|
30738
|
+
* artifacts default to files next to this module; in the browser both must
|
|
30739
|
+
* be provided (typically URLs to statically served copies).
|
|
30740
|
+
* @returns This instance, once the runtime is initialized.
|
|
30741
|
+
*/
|
|
30742
|
+
load(options = {}) {
|
|
30743
|
+
this.loadPromise_ ??= this.loadInternal_(options).catch((err) => {
|
|
30744
|
+
this.loadPromise_ = null;
|
|
30745
|
+
throw err;
|
|
30746
|
+
});
|
|
30747
|
+
return this.loadPromise_;
|
|
30748
|
+
}
|
|
30749
|
+
/**
|
|
30750
|
+
* Frees the dynamic array (if initialized) and drops all references to the
|
|
30751
|
+
* wasm runtime so it can be garbage collected. Note Emscripten linear
|
|
30752
|
+
* memory cannot be truly freed; a subsequent load() evaluates a fresh,
|
|
30753
|
+
* isolated runtime.
|
|
30754
|
+
*/
|
|
30755
|
+
dispose() {
|
|
30756
|
+
if (this.isDynArrInitialized_ && this.bindings_) {
|
|
30757
|
+
this.bindings_.freeDynArr();
|
|
30758
|
+
}
|
|
30759
|
+
if (this.module_ && this.scratchKeyPtr_) {
|
|
30760
|
+
this.module_._free(this.scratchKeyPtr_);
|
|
30761
|
+
this.module_._free(this.scratchResultPtr_);
|
|
30762
|
+
}
|
|
30763
|
+
this.scratchKeyPtr_ = 0;
|
|
30764
|
+
this.scratchResultPtr_ = 0;
|
|
30765
|
+
this.isDynArrInitialized_ = false;
|
|
30766
|
+
this.module_ = null;
|
|
30767
|
+
this.fs_ = null;
|
|
30768
|
+
this.bindings_ = null;
|
|
30769
|
+
this.loadPromise_ = null;
|
|
30770
|
+
}
|
|
30771
|
+
// TLE tree management
|
|
30772
|
+
/**
|
|
30773
|
+
* Adds a single TLE to the TLE tree (`TleAddSatFrLines_wasm`).
|
|
30774
|
+
* @param line1 Line 1 of the TLE.
|
|
30775
|
+
* @param line2 Line 2 of the TLE.
|
|
30776
|
+
* @returns The satKey of the added TLE.
|
|
30777
|
+
*/
|
|
30778
|
+
addSat(line1, line2) {
|
|
30779
|
+
const satKey = this.fn_().tleAddSatFrLines(line1, line2);
|
|
30780
|
+
if (satKey <= 0n) {
|
|
30781
|
+
throw new Sgp4WasmError(`TleAddSatFrLines_wasm failed for line1 "${line1}" (code ${satKey})`, Number(satKey));
|
|
30782
|
+
}
|
|
30783
|
+
return satKey;
|
|
30784
|
+
}
|
|
30785
|
+
/**
|
|
30786
|
+
* Adds any number of TLEs from newline-separated text
|
|
30787
|
+
* (`TleAddSatsFrLines_wasm`).
|
|
30788
|
+
*
|
|
30789
|
+
* The library adds TLEs individually: entries it fails to parse come back
|
|
30790
|
+
* as `0n` in the returned array (positions match the input order), while
|
|
30791
|
+
* the rest are added normally.
|
|
30792
|
+
* @param tleText Newline-separated TLE lines.
|
|
30793
|
+
* @returns One satKey per input TLE; `0n` marks a failed entry.
|
|
30794
|
+
*/
|
|
30795
|
+
addSats(tleText) {
|
|
30796
|
+
const m = this.module;
|
|
30797
|
+
const numSats = _Sgp4WasmBase.countTles_(tleText);
|
|
30798
|
+
if (numSats === 0) {
|
|
30799
|
+
return [];
|
|
30800
|
+
}
|
|
30801
|
+
const textBytes = new TextEncoder().encode(tleText);
|
|
30802
|
+
return this.withMalloc_(textBytes.length + 1, (textPtr) => this.withMalloc_(numSats * 8, (keysPtr) => this.withMalloc_(ERR_SAT_TEXT_BYTES, (errPtr) => {
|
|
30803
|
+
m.HEAPU8.set(textBytes, textPtr);
|
|
30804
|
+
m.HEAPU8[textPtr + textBytes.length] = 0;
|
|
30805
|
+
m.HEAPU8.fill(0, errPtr, errPtr + ERR_SAT_TEXT_BYTES);
|
|
30806
|
+
const ret = this.fn_().tleAddSatsFrLines(textPtr, keysPtr, errPtr);
|
|
30807
|
+
if (ret !== 0) {
|
|
30808
|
+
const errLine = m.UTF8ToString(errPtr, ERR_SAT_TEXT_BYTES);
|
|
30809
|
+
throw new Sgp4WasmError(`TleAddSatsFrLines_wasm failed (code ${ret}); offending TLE line 1: "${errLine}"`, ret);
|
|
30810
|
+
}
|
|
30811
|
+
return this.readI64Array_(keysPtr, numSats);
|
|
30812
|
+
})));
|
|
30813
|
+
}
|
|
30814
|
+
/**
|
|
30815
|
+
* Adds any number of TLEs through the Emscripten virtual filesystem
|
|
30816
|
+
* (`TleLoadFileVFS_wasm`). The text is written to the VFS `inFile`, and
|
|
30817
|
+
* the satKeys the library writes to `outFile` are read back.
|
|
30818
|
+
* @param tleText Newline-separated TLE lines.
|
|
30819
|
+
* @returns The satKeys of all added TLEs.
|
|
30820
|
+
*/
|
|
30821
|
+
loadTlesVfs(tleText) {
|
|
30822
|
+
this.fn_();
|
|
30823
|
+
const vfs = this.fs;
|
|
30824
|
+
try {
|
|
30825
|
+
vfs.writeFile("inFile", tleText);
|
|
30826
|
+
const ret = this.fn_().tleLoadFileVfs("inFile");
|
|
30827
|
+
if (ret <= 0) {
|
|
30828
|
+
throw new Sgp4WasmError(`TleLoadFileVFS_wasm failed (code ${ret})`, ret);
|
|
30829
|
+
}
|
|
30830
|
+
const outText = vfs.readFile("outFile", { encoding: "utf8" });
|
|
30831
|
+
return outText.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => BigInt(line));
|
|
30832
|
+
} finally {
|
|
30833
|
+
for (const file of ["inFile", "outFile"]) {
|
|
30834
|
+
if (vfs.analyzePath(file).exists) {
|
|
30835
|
+
vfs.unlink(file);
|
|
30836
|
+
}
|
|
30837
|
+
}
|
|
30838
|
+
}
|
|
30839
|
+
}
|
|
30840
|
+
/**
|
|
30841
|
+
* Removes TLEs from the TLE tree (`TleRemoveSats_wasm`).
|
|
30842
|
+
* @param satKeys The satKeys to remove.
|
|
30843
|
+
*/
|
|
30844
|
+
removeSats(satKeys) {
|
|
30845
|
+
this.runKeysWithErrSat_((keysPtr, numKeys, errPtr) => this.fn_().tleRemoveSats(keysPtr, numKeys, errPtr), satKeys, "TleRemoveSats_wasm");
|
|
30846
|
+
}
|
|
30847
|
+
// SGP4 object lifecycle
|
|
30848
|
+
/**
|
|
30849
|
+
* Initializes SGP4 objects for previously added TLEs
|
|
30850
|
+
* (`Sgp4InitSats_wasm`). Must be called after adding TLEs and before
|
|
30851
|
+
* propagating them.
|
|
30852
|
+
* @param satKeys The satKeys to initialize.
|
|
30853
|
+
*/
|
|
30854
|
+
initSats(satKeys) {
|
|
30855
|
+
this.runKeysWithErrSat_((keysPtr, numKeys, errPtr) => this.fn_().sgp4InitSats(keysPtr, numKeys, errPtr), satKeys, "Sgp4InitSats_wasm");
|
|
30856
|
+
}
|
|
30857
|
+
/**
|
|
30858
|
+
* Removes initialized SGP4 objects (`Sgp4RemoveSats_wasm`).
|
|
30859
|
+
* @param satKeys The satKeys to remove.
|
|
30860
|
+
*/
|
|
30861
|
+
uninitSats(satKeys) {
|
|
30862
|
+
this.runKeysWithErrSat_((keysPtr, numKeys, errPtr) => this.fn_().sgp4RemoveSats(keysPtr, numKeys, errPtr), satKeys, "Sgp4RemoveSats_wasm");
|
|
30863
|
+
}
|
|
30864
|
+
// Propagation by satKey
|
|
30865
|
+
/**
|
|
30866
|
+
* Propagates satKeys with geodetic output (`Sgp4Prop_wasm`).
|
|
30867
|
+
* @param satKeys The satKeys to propagate (must be initialized).
|
|
30868
|
+
* @param startTimeMse Start time in minutes since each TLE's epoch.
|
|
30869
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
30870
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
30871
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
30872
|
+
*/
|
|
30873
|
+
propagate(satKeys, startTimeMse, propsPerSat, stepSizeMin) {
|
|
30874
|
+
const buf = this.propagateKeys_(
|
|
30875
|
+
(...args) => this.fn_().sgp4Prop(...args),
|
|
30876
|
+
satKeys,
|
|
30877
|
+
startTimeMse,
|
|
30878
|
+
propsPerSat,
|
|
30879
|
+
stepSizeMin,
|
|
30880
|
+
11,
|
|
30881
|
+
"Sgp4Prop_wasm"
|
|
30882
|
+
);
|
|
30883
|
+
return this.parseRecords_(buf, satKeys.length, propsPerSat, 11);
|
|
30884
|
+
}
|
|
30885
|
+
/**
|
|
30886
|
+
* Propagates satKeys, position/velocity only (`Sgp4PropPosVel_wasm`).
|
|
30887
|
+
* @param satKeys The satKeys to propagate (must be initialized).
|
|
30888
|
+
* @param startTimeMse Start time in minutes since each TLE's epoch.
|
|
30889
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
30890
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
30891
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
30892
|
+
*/
|
|
30893
|
+
propagatePosVel(satKeys, startTimeMse, propsPerSat, stepSizeMin) {
|
|
30894
|
+
const buf = this.propagateKeys_(
|
|
30895
|
+
(...args) => this.fn_().sgp4PropPosVel(...args),
|
|
30896
|
+
satKeys,
|
|
30897
|
+
startTimeMse,
|
|
30898
|
+
propsPerSat,
|
|
30899
|
+
stepSizeMin,
|
|
30900
|
+
8,
|
|
30901
|
+
"Sgp4PropPosVel_wasm"
|
|
30902
|
+
);
|
|
30903
|
+
return this.parseRecords_(buf, satKeys.length, propsPerSat, 8);
|
|
30904
|
+
}
|
|
30905
|
+
/**
|
|
30906
|
+
* Propagates satKeys from an absolute time with geodetic output
|
|
30907
|
+
* (`Sgp4PropDs50Utc_wasm`).
|
|
30908
|
+
* @param satKeys The satKeys to propagate (must be initialized).
|
|
30909
|
+
* @param startTimeDs50Utc Start time in days since 1950 UTC (see {@link toDs50Utc}).
|
|
30910
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
30911
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
30912
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
30913
|
+
*/
|
|
30914
|
+
propagateDs50Utc(satKeys, startTimeDs50Utc, propsPerSat, stepSizeMin) {
|
|
30915
|
+
const buf = this.propagateKeys_(
|
|
30916
|
+
(...args) => this.fn_().sgp4PropDs50Utc(...args),
|
|
30917
|
+
satKeys,
|
|
30918
|
+
startTimeDs50Utc,
|
|
30919
|
+
propsPerSat,
|
|
30920
|
+
stepSizeMin,
|
|
30921
|
+
11,
|
|
30922
|
+
"Sgp4PropDs50Utc_wasm"
|
|
30923
|
+
);
|
|
30924
|
+
return this.parseRecords_(buf, satKeys.length, propsPerSat, 11);
|
|
30925
|
+
}
|
|
30926
|
+
/**
|
|
30927
|
+
* Propagates satKeys from an absolute time, position/velocity only
|
|
30928
|
+
* (`Sgp4PropDs50UtcPosVel_wasm`).
|
|
30929
|
+
* @param satKeys The satKeys to propagate (must be initialized).
|
|
30930
|
+
* @param startTimeDs50Utc Start time in days since 1950 UTC (see {@link toDs50Utc}).
|
|
30931
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
30932
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
30933
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
30934
|
+
*/
|
|
30935
|
+
propagateDs50UtcPosVel(satKeys, startTimeDs50Utc, propsPerSat, stepSizeMin) {
|
|
30936
|
+
const buf = this.propagateKeys_(
|
|
30937
|
+
(...args) => this.fn_().sgp4PropDs50UtcPosVel(...args),
|
|
30938
|
+
satKeys,
|
|
30939
|
+
startTimeDs50Utc,
|
|
30940
|
+
propsPerSat,
|
|
30941
|
+
stepSizeMin,
|
|
30942
|
+
8,
|
|
30943
|
+
"Sgp4PropDs50UtcPosVel_wasm"
|
|
30944
|
+
);
|
|
30945
|
+
return this.parseRecords_(buf, satKeys.length, propsPerSat, 8);
|
|
30946
|
+
}
|
|
30947
|
+
/**
|
|
30948
|
+
* Convenience single-satellite, single-step propagation.
|
|
30949
|
+
* @param satKey The satKey to propagate (must be initialized).
|
|
30950
|
+
* @param minutesSinceEpoch Minutes since the TLE epoch.
|
|
30951
|
+
* @returns The propagated state.
|
|
30952
|
+
*/
|
|
30953
|
+
propagateOne(satKey, minutesSinceEpoch) {
|
|
30954
|
+
return this.propagate([satKey], minutesSinceEpoch, 1, 0)[0][0];
|
|
30955
|
+
}
|
|
30956
|
+
/**
|
|
30957
|
+
* Convenience single-satellite propagation to an absolute UTC epoch.
|
|
30958
|
+
* @param satKey The satKey to propagate (must be initialized).
|
|
30959
|
+
* @param epoch The UTC epoch to propagate to.
|
|
30960
|
+
* @returns The propagated state.
|
|
30961
|
+
*/
|
|
30962
|
+
propagateEpoch(satKey, epoch) {
|
|
30963
|
+
return this.propagateDs50Utc([satKey], _Sgp4WasmBase.toDs50Utc(epoch), 1, 0)[0][0];
|
|
30964
|
+
}
|
|
30965
|
+
/**
|
|
30966
|
+
* Hot-path single-satellite propagation (`Sgp4PropPosVel_wasm`) that reuses
|
|
30967
|
+
* persistent scratch buffers instead of allocating per call, and returns
|
|
30968
|
+
* plain numbers instead of Vector3D instances. Intended for per-frame use
|
|
30969
|
+
* (e.g. as a drop-in backend for `Sgp4.propagate`).
|
|
30970
|
+
* @param satKey The satKey to propagate (must be initialized).
|
|
30971
|
+
* @param minutesSinceEpoch Minutes since the TLE epoch.
|
|
30972
|
+
* @returns Plain TEME position/velocity plus the per-record error code; a
|
|
30973
|
+
* nonzero function-level failure is reported as `err` too.
|
|
30974
|
+
*/
|
|
30975
|
+
propagateOnePosVelFast(satKey, minutesSinceEpoch) {
|
|
30976
|
+
this.fn_();
|
|
30977
|
+
const m = this.module;
|
|
30978
|
+
if (!this.scratchKeyPtr_) {
|
|
30979
|
+
this.scratchKeyPtr_ = m._malloc(8);
|
|
30980
|
+
this.scratchResultPtr_ = m._malloc(8 * 8);
|
|
30981
|
+
}
|
|
30982
|
+
m.HEAP64[this.scratchKeyPtr_ >> 3] = satKey;
|
|
30983
|
+
const ret = m._Sgp4PropPosVel_wasm(this.scratchKeyPtr_, 1, minutesSinceEpoch, 1, 0, this.scratchResultPtr_);
|
|
30984
|
+
if (ret !== 0) {
|
|
30985
|
+
return { err: ret, position: { x: 0, y: 0, z: 0 }, velocity: { x: 0, y: 0, z: 0 } };
|
|
30986
|
+
}
|
|
30987
|
+
const heap = m.HEAPF64;
|
|
30988
|
+
const off = this.scratchResultPtr_ / 8;
|
|
30989
|
+
return {
|
|
30990
|
+
err: heap[off],
|
|
30991
|
+
position: { x: heap[off + 2], y: heap[off + 3], z: heap[off + 4] },
|
|
30992
|
+
velocity: { x: heap[off + 5], y: heap[off + 6], z: heap[off + 7] }
|
|
30993
|
+
};
|
|
30994
|
+
}
|
|
30995
|
+
// Dynamic array batch path
|
|
30996
|
+
/**
|
|
30997
|
+
* Initializes the module-global dynamic array of SGP4 objects
|
|
30998
|
+
* (`InitDynArr_wasm`).
|
|
30999
|
+
* @param initialSize Initial capacity of the array.
|
|
31000
|
+
*/
|
|
31001
|
+
initDynArr(initialSize) {
|
|
31002
|
+
const ret = this.fn_().initDynArr(initialSize);
|
|
31003
|
+
if (ret !== 0) {
|
|
31004
|
+
throw new Sgp4WasmError(`InitDynArr_wasm failed (code ${ret}): ${SGP4_WASM_DYN_ARR_ERRORS[ret] ?? "unknown error"}`, ret);
|
|
31005
|
+
}
|
|
31006
|
+
this.isDynArrInitialized_ = true;
|
|
31007
|
+
}
|
|
31008
|
+
/**
|
|
31009
|
+
* Re-allocates the dynamic array to a new capacity
|
|
31010
|
+
* (`ReallocateDynArr_wasm`).
|
|
31011
|
+
* @param newSize New capacity of the array.
|
|
31012
|
+
*/
|
|
31013
|
+
reallocDynArr(newSize) {
|
|
31014
|
+
const ret = this.fn_().reallocDynArr(newSize);
|
|
31015
|
+
if (ret !== 0) {
|
|
31016
|
+
throw new Sgp4WasmError(`ReallocateDynArr_wasm failed (code ${ret}): ${SGP4_WASM_DYN_ARR_ERRORS[ret] ?? "unknown error"}`, ret);
|
|
31017
|
+
}
|
|
31018
|
+
}
|
|
31019
|
+
/**
|
|
31020
|
+
* Frees the dynamic array (`FreeDynArr_wasm`).
|
|
31021
|
+
*/
|
|
31022
|
+
freeDynArr() {
|
|
31023
|
+
this.fn_().freeDynArr();
|
|
31024
|
+
this.isDynArrInitialized_ = false;
|
|
31025
|
+
}
|
|
31026
|
+
/**
|
|
31027
|
+
* Current size of the dynamic array (`GetDynArrSize_wasm`).
|
|
31028
|
+
*/
|
|
31029
|
+
get dynArrSize() {
|
|
31030
|
+
return this.fn_().getDynArrSize();
|
|
31031
|
+
}
|
|
31032
|
+
/**
|
|
31033
|
+
* Adds a TLE to the dynamic array (`AddSatToDynArr_wasm`).
|
|
31034
|
+
* @param line1 Line 1 of the TLE.
|
|
31035
|
+
* @param line2 Line 2 of the TLE.
|
|
31036
|
+
* @returns The dynamic array index of the added satellite.
|
|
31037
|
+
*/
|
|
31038
|
+
addSatToDynArr(line1, line2) {
|
|
31039
|
+
const index = this.fn_().addSatToDynArr(line1, line2);
|
|
31040
|
+
if (index < 0) {
|
|
31041
|
+
throw new Sgp4WasmError(`AddSatToDynArr_wasm failed (code ${index}): ${SGP4_WASM_ADD_SAT_ERRORS[index] ?? "unknown error"}`, index);
|
|
31042
|
+
}
|
|
31043
|
+
return index;
|
|
31044
|
+
}
|
|
31045
|
+
/**
|
|
31046
|
+
* Propagates dynamic array entries, position/velocity only
|
|
31047
|
+
* (`Sgp4PropDynArrPosVel_wasm`).
|
|
31048
|
+
* @param indexes Dynamic array indices to propagate.
|
|
31049
|
+
* @param startTimeMse Start time in minutes since each TLE's epoch.
|
|
31050
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
31051
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
31052
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
31053
|
+
*/
|
|
31054
|
+
propagateDynArrPosVel(indexes, startTimeMse, propsPerSat, stepSizeMin) {
|
|
31055
|
+
const buf = this.propagateIndexes_(
|
|
31056
|
+
(...args) => this.fn_().sgp4PropDynArrPosVel(...args),
|
|
31057
|
+
indexes,
|
|
31058
|
+
startTimeMse,
|
|
31059
|
+
propsPerSat,
|
|
31060
|
+
stepSizeMin,
|
|
31061
|
+
8
|
|
31062
|
+
);
|
|
31063
|
+
return this.parseRecords_(buf, indexes.length, propsPerSat, 8);
|
|
31064
|
+
}
|
|
31065
|
+
/**
|
|
31066
|
+
* Propagates dynamic array entries from an absolute time with geodetic
|
|
31067
|
+
* output (`Sgp4PropDs50UtcDynArr_wasm`).
|
|
31068
|
+
* @param indexes Dynamic array indices to propagate.
|
|
31069
|
+
* @param startTimeDs50Utc Start time in days since 1950 UTC (see {@link toDs50Utc}).
|
|
31070
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
31071
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
31072
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
31073
|
+
*/
|
|
31074
|
+
propagateDs50UtcDynArr(indexes, startTimeDs50Utc, propsPerSat, stepSizeMin) {
|
|
31075
|
+
const buf = this.propagateIndexes_(
|
|
31076
|
+
(...args) => this.fn_().sgp4PropDs50UtcDynArr(...args),
|
|
31077
|
+
indexes,
|
|
31078
|
+
startTimeDs50Utc,
|
|
31079
|
+
propsPerSat,
|
|
31080
|
+
stepSizeMin,
|
|
31081
|
+
11
|
|
31082
|
+
);
|
|
31083
|
+
return this.parseRecords_(buf, indexes.length, propsPerSat, 11);
|
|
31084
|
+
}
|
|
31085
|
+
/**
|
|
31086
|
+
* Propagates dynamic array entries from an absolute time,
|
|
31087
|
+
* position/velocity only (`Sgp4PropDs50UtcDynArrPosVel_wasm`).
|
|
31088
|
+
* @param indexes Dynamic array indices to propagate.
|
|
31089
|
+
* @param startTimeDs50Utc Start time in days since 1950 UTC (see {@link toDs50Utc}).
|
|
31090
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
31091
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
31092
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
31093
|
+
*/
|
|
31094
|
+
propagateDs50UtcDynArrPosVel(indexes, startTimeDs50Utc, propsPerSat, stepSizeMin) {
|
|
31095
|
+
const buf = this.propagateIndexes_(
|
|
31096
|
+
(...args) => this.fn_().sgp4PropDs50UtcDynArrPosVel(...args),
|
|
31097
|
+
indexes,
|
|
31098
|
+
startTimeDs50Utc,
|
|
31099
|
+
propsPerSat,
|
|
31100
|
+
stepSizeMin,
|
|
31101
|
+
8
|
|
31102
|
+
);
|
|
31103
|
+
return this.parseRecords_(buf, indexes.length, propsPerSat, 8);
|
|
31104
|
+
}
|
|
31105
|
+
// Logging
|
|
31106
|
+
/**
|
|
31107
|
+
* Sets the wasm library's logging level (`SetLogLevel_wasm`).
|
|
31108
|
+
* @param level The new log level.
|
|
31109
|
+
*/
|
|
31110
|
+
setLogLevel(level) {
|
|
31111
|
+
const ret = this.fn_().setLogLevel(level);
|
|
31112
|
+
if (ret !== 0) {
|
|
31113
|
+
throw new Sgp4WasmError(`SetLogLevel_wasm failed (code ${ret})`, ret);
|
|
31114
|
+
}
|
|
31115
|
+
}
|
|
31116
|
+
// Internals
|
|
31117
|
+
/**
|
|
31118
|
+
* Counts the TLEs in newline-separated text by counting line-1 rows.
|
|
31119
|
+
* @param tleText Newline-separated TLE lines.
|
|
31120
|
+
* @returns Number of TLEs.
|
|
31121
|
+
*/
|
|
31122
|
+
static countTles_(tleText) {
|
|
31123
|
+
return tleText.split(/\r?\n/u).filter((line) => line.trim().startsWith("1 ")).length;
|
|
31124
|
+
}
|
|
31125
|
+
static isHttpUrl_(source) {
|
|
31126
|
+
const href = source instanceof URL ? source.href : source;
|
|
31127
|
+
return /^https?:\/\//u.test(href);
|
|
31128
|
+
}
|
|
31129
|
+
fn_() {
|
|
31130
|
+
if (!this.bindings_) {
|
|
31131
|
+
throw new Sgp4WasmError(`${this.constructor.name} is not loaded. Call load() first.`);
|
|
31132
|
+
}
|
|
31133
|
+
return this.bindings_;
|
|
31134
|
+
}
|
|
31135
|
+
async loadInternal_(options) {
|
|
31136
|
+
const isNode = typeof process !== "undefined" && Boolean(process.versions?.node);
|
|
31137
|
+
const glueText = await this.resolveGlueText_(options.glue, isNode);
|
|
31138
|
+
const wasmBinary = await this.resolveWasmBinary_(options.wasm, isNode);
|
|
31139
|
+
let requireFn;
|
|
31140
|
+
let dirname;
|
|
31141
|
+
if (isNode) {
|
|
31142
|
+
const nodeModule = await import(
|
|
31143
|
+
/* webpackIgnore: true */
|
|
31144
|
+
'module'
|
|
31145
|
+
);
|
|
31146
|
+
requireFn = nodeModule.createRequire(importMetaUrl);
|
|
31147
|
+
dirname = ".";
|
|
31148
|
+
}
|
|
31149
|
+
let markReady = () => void 0;
|
|
31150
|
+
let markFailed = () => void 0;
|
|
31151
|
+
const runtimeReady = new Promise((resolve, reject) => {
|
|
31152
|
+
markReady = resolve;
|
|
31153
|
+
markFailed = reject;
|
|
31154
|
+
});
|
|
31155
|
+
const seed = {
|
|
31156
|
+
wasmBinary,
|
|
31157
|
+
onRuntimeInitialized: () => markReady(),
|
|
31158
|
+
onAbort: (reason) => markFailed(new Sgp4WasmError(`${this.constructor.name} wasm runtime aborted: ${reason}`))
|
|
31159
|
+
};
|
|
31160
|
+
if (options.print) {
|
|
31161
|
+
seed.print = options.print;
|
|
31162
|
+
}
|
|
31163
|
+
if (options.printErr) {
|
|
31164
|
+
seed.printErr = options.printErr;
|
|
31165
|
+
}
|
|
31166
|
+
const evaluateGlue = new Function("Module", "require", "__dirname", `${glueText};
|
|
31167
|
+
return { Module: Module, FS: FS };`);
|
|
31168
|
+
const { Module, FS } = evaluateGlue(seed, requireFn, dirname);
|
|
31169
|
+
await runtimeReady;
|
|
31170
|
+
this.module_ = Module;
|
|
31171
|
+
this.fs_ = FS;
|
|
31172
|
+
this.bindings_ = _Sgp4WasmBase.buildBindings_(Module);
|
|
31173
|
+
return this;
|
|
31174
|
+
}
|
|
31175
|
+
static buildBindings_(m) {
|
|
31176
|
+
const num = "number";
|
|
31177
|
+
return {
|
|
31178
|
+
tleAddSatFrLines: m.cwrap("TleAddSatFrLines_wasm", num, ["string", "string"]),
|
|
31179
|
+
tleAddSatsFrLines: m.cwrap("TleAddSatsFrLines_wasm", num, [num, num, num]),
|
|
31180
|
+
tleLoadFileVfs: m.cwrap("TleLoadFileVFS_wasm", num, ["string"]),
|
|
31181
|
+
tleRemoveSats: m.cwrap("TleRemoveSats_wasm", num, [num, num, num]),
|
|
31182
|
+
sgp4InitSats: m.cwrap("Sgp4InitSats_wasm", num, [num, num, num]),
|
|
31183
|
+
sgp4RemoveSats: m.cwrap("Sgp4RemoveSats_wasm", num, [num, num, num]),
|
|
31184
|
+
sgp4Prop: m.cwrap("Sgp4Prop_wasm", num, [num, num, num, num, num, num]),
|
|
31185
|
+
sgp4PropPosVel: m.cwrap("Sgp4PropPosVel_wasm", num, [num, num, num, num, num, num]),
|
|
31186
|
+
sgp4PropDs50Utc: m.cwrap("Sgp4PropDs50Utc_wasm", num, [num, num, num, num, num, num]),
|
|
31187
|
+
sgp4PropDs50UtcPosVel: m.cwrap("Sgp4PropDs50UtcPosVel_wasm", num, [num, num, num, num, num, num]),
|
|
31188
|
+
initDynArr: m.cwrap("InitDynArr_wasm", num, [num]),
|
|
31189
|
+
reallocDynArr: m.cwrap("ReallocateDynArr_wasm", num, [num]),
|
|
31190
|
+
freeDynArr: m.cwrap("FreeDynArr_wasm", null, []),
|
|
31191
|
+
getDynArrSize: m.cwrap("GetDynArrSize_wasm", num, []),
|
|
31192
|
+
addSatToDynArr: m.cwrap("AddSatToDynArr_wasm", num, ["string", "string"]),
|
|
31193
|
+
sgp4PropDynArrPosVel: m.cwrap("Sgp4PropDynArrPosVel_wasm", null, [num, num, num, num, num, num]),
|
|
31194
|
+
sgp4PropDs50UtcDynArr: m.cwrap("Sgp4PropDs50UtcDynArr_wasm", null, [num, num, num, num, num, num]),
|
|
31195
|
+
sgp4PropDs50UtcDynArrPosVel: m.cwrap("Sgp4PropDs50UtcDynArrPosVel_wasm", null, [num, num, num, num, num, num]),
|
|
31196
|
+
setLogLevel: m.cwrap("SetLogLevel_wasm", num, [num])
|
|
31197
|
+
};
|
|
31198
|
+
}
|
|
31199
|
+
async resolveGlueText_(source, isNode) {
|
|
31200
|
+
if (source && typeof source === "object" && "text" in source) {
|
|
31201
|
+
return source.text;
|
|
31202
|
+
}
|
|
31203
|
+
const resolved = this.resolveDefaultSource_(source, this.defaultGlueFile_, isNode);
|
|
31204
|
+
if (isNode && !_Sgp4WasmBase.isHttpUrl_(resolved)) {
|
|
31205
|
+
return await this.readNodeFile_(resolved, "utf8");
|
|
31206
|
+
}
|
|
31207
|
+
const response = await fetch(resolved);
|
|
31208
|
+
if (!response.ok) {
|
|
31209
|
+
throw this.missingArtifactError_(resolved.toString(), `HTTP ${response.status}`);
|
|
31210
|
+
}
|
|
31211
|
+
return response.text();
|
|
31212
|
+
}
|
|
31213
|
+
async resolveWasmBinary_(source, isNode) {
|
|
31214
|
+
if (source instanceof Uint8Array || source instanceof ArrayBuffer) {
|
|
31215
|
+
return source;
|
|
31216
|
+
}
|
|
31217
|
+
const resolved = this.resolveDefaultSource_(source, this.defaultWasmFile_, isNode);
|
|
31218
|
+
if (isNode && !_Sgp4WasmBase.isHttpUrl_(resolved)) {
|
|
31219
|
+
return await this.readNodeFile_(resolved, null);
|
|
31220
|
+
}
|
|
31221
|
+
const response = await fetch(resolved);
|
|
31222
|
+
if (!response.ok) {
|
|
31223
|
+
throw this.missingArtifactError_(resolved.toString(), `HTTP ${response.status}`);
|
|
31224
|
+
}
|
|
31225
|
+
return response.arrayBuffer();
|
|
31226
|
+
}
|
|
31227
|
+
resolveDefaultSource_(source, defaultFile, isNode) {
|
|
31228
|
+
if (source) {
|
|
31229
|
+
return source;
|
|
31230
|
+
}
|
|
31231
|
+
if (!isNode || typeof importMetaUrl !== "string") {
|
|
31232
|
+
throw new Sgp4WasmError(
|
|
31233
|
+
`${this.constructor.name}.load() requires explicit glue/wasm sources in this environment. Serve the ${defaultFile} artifact as a static asset and pass its URL via LoadOptions.`
|
|
31234
|
+
);
|
|
31235
|
+
}
|
|
31236
|
+
return new URL(defaultFile, importMetaUrl);
|
|
31237
|
+
}
|
|
31238
|
+
async readNodeFile_(source, encoding) {
|
|
31239
|
+
const fsPromises = await import(
|
|
31240
|
+
/* webpackIgnore: true */
|
|
31241
|
+
'fs/promises'
|
|
31242
|
+
);
|
|
31243
|
+
try {
|
|
31244
|
+
return encoding ? await fsPromises.readFile(source, encoding) : await fsPromises.readFile(source);
|
|
31245
|
+
} catch (err) {
|
|
31246
|
+
throw this.missingArtifactError_(source.toString(), err.message);
|
|
31247
|
+
}
|
|
31248
|
+
}
|
|
31249
|
+
missingArtifactError_(location, cause) {
|
|
31250
|
+
return new Sgp4WasmError(
|
|
31251
|
+
`Failed to load Sgp4Prop artifact at ${location} (${cause}). The Sgp4Prop WebAssembly artifacts are part of the license-restricted USSF Astro Standards "C Sgp4Prop WebAssembly" distribution available from space-track.org. Download it and place ${this.defaultGlueFile_}/${this.defaultWasmFile_} in ootk's src/external/ directory, or pass explicit sources via LoadOptions.`
|
|
31252
|
+
);
|
|
31253
|
+
}
|
|
31254
|
+
// Memory marshalling
|
|
31255
|
+
withMalloc_(bytes, fn) {
|
|
31256
|
+
const m = this.module;
|
|
31257
|
+
const ptr = m._malloc(Math.max(bytes, 1));
|
|
31258
|
+
try {
|
|
31259
|
+
return fn(ptr);
|
|
31260
|
+
} finally {
|
|
31261
|
+
m._free(ptr);
|
|
31262
|
+
}
|
|
31263
|
+
}
|
|
31264
|
+
withI64Array_(values, fn) {
|
|
31265
|
+
return this.withMalloc_(values.length * 8, (ptr) => {
|
|
31266
|
+
const m = this.module;
|
|
31267
|
+
values.forEach((value, i) => m.setValue(ptr + i * 8, BigInt(value), "i64"));
|
|
31268
|
+
return fn(ptr);
|
|
31269
|
+
});
|
|
31270
|
+
}
|
|
31271
|
+
withI32Array_(values, fn) {
|
|
31272
|
+
return this.withMalloc_(values.length * 4, (ptr) => {
|
|
31273
|
+
const m = this.module;
|
|
31274
|
+
values.forEach((value, i) => m.setValue(ptr + i * 4, value, "i32"));
|
|
31275
|
+
return fn(ptr);
|
|
31276
|
+
});
|
|
31277
|
+
}
|
|
31278
|
+
readI64Array_(ptr, count) {
|
|
31279
|
+
const m = this.module;
|
|
31280
|
+
const out = [];
|
|
31281
|
+
for (let i = 0; i < count; i++) {
|
|
31282
|
+
out.push(m.getValue(ptr + i * 8, "i64"));
|
|
31283
|
+
}
|
|
31284
|
+
return out;
|
|
31285
|
+
}
|
|
31286
|
+
/**
|
|
31287
|
+
* Allocates a result buffer of `count` doubles, runs `fn`, and copies the
|
|
31288
|
+
* buffer out. The HEAPF64 view is re-read from the module after the call
|
|
31289
|
+
* because wasm memory growth invalidates cached views.
|
|
31290
|
+
*/
|
|
31291
|
+
withF64Result_(count, fn) {
|
|
31292
|
+
return this.withMalloc_(count * 8, (ptr) => {
|
|
31293
|
+
fn(ptr);
|
|
31294
|
+
return this.module.HEAPF64.slice(ptr / 8, ptr / 8 + count);
|
|
31295
|
+
});
|
|
31296
|
+
}
|
|
31297
|
+
runKeysWithErrSat_(op, satKeys, opName) {
|
|
31298
|
+
this.withI64Array_(satKeys, (keysPtr) => this.withMalloc_(8, (errPtr) => {
|
|
31299
|
+
const m = this.module;
|
|
31300
|
+
m.setValue(errPtr, 0n, "i64");
|
|
31301
|
+
const ret = op(keysPtr, satKeys.length, errPtr);
|
|
31302
|
+
if (ret !== 0) {
|
|
31303
|
+
const errKey = m.getValue(errPtr, "i64");
|
|
31304
|
+
throw new Sgp4WasmError(`${opName} failed (code ${ret}); offending satKey: ${errKey}`, ret);
|
|
31305
|
+
}
|
|
31306
|
+
}));
|
|
31307
|
+
}
|
|
31308
|
+
propagateKeys_(op, satKeys, start, propsPerSat, stepMin, width, opName) {
|
|
31309
|
+
return this.withI64Array_(satKeys, (keysPtr) => this.withF64Result_(satKeys.length * propsPerSat * width, (resultPtr) => {
|
|
31310
|
+
const ret = op(keysPtr, satKeys.length, start, propsPerSat, stepMin, resultPtr);
|
|
31311
|
+
if (ret !== 0) {
|
|
31312
|
+
throw new Sgp4WasmError(`${opName} failed (code ${ret})`, ret);
|
|
31313
|
+
}
|
|
31314
|
+
}));
|
|
31315
|
+
}
|
|
31316
|
+
propagateIndexes_(op, indexes, start, propsPerSat, stepMin, width) {
|
|
31317
|
+
return this.withI32Array_(indexes, (idxPtr) => this.withF64Result_(indexes.length * propsPerSat * width, (resultPtr) => {
|
|
31318
|
+
op(idxPtr, indexes.length, start, propsPerSat, stepMin, resultPtr);
|
|
31319
|
+
}));
|
|
31320
|
+
}
|
|
31321
|
+
/**
|
|
31322
|
+
* Parses a copied-out result buffer into per-satellite, per-step records.
|
|
31323
|
+
* Records are 8 doubles wide (err, time, pos, vel) or 11 doubles wide
|
|
31324
|
+
* (plus lat, lon, height) — the distribution docs' "7 per record"
|
|
31325
|
+
* precondition is stale.
|
|
31326
|
+
*/
|
|
31327
|
+
parseRecords_(buf, numKeys, propsPerSat, width) {
|
|
31328
|
+
const results = [];
|
|
31329
|
+
for (let satIndex = 0; satIndex < numKeys; satIndex++) {
|
|
31330
|
+
const satRecords = [];
|
|
31331
|
+
for (let stepIndex = 0; stepIndex < propsPerSat; stepIndex++) {
|
|
31332
|
+
const off = (satIndex * propsPerSat + stepIndex) * width;
|
|
31333
|
+
const record = {
|
|
31334
|
+
err: buf[off],
|
|
31335
|
+
time: buf[off + 1],
|
|
31336
|
+
position: new Vector3D(buf[off + 2], buf[off + 3], buf[off + 4]),
|
|
31337
|
+
velocity: new Vector3D(
|
|
31338
|
+
buf[off + 5],
|
|
31339
|
+
buf[off + 6],
|
|
31340
|
+
buf[off + 7]
|
|
31341
|
+
)
|
|
31342
|
+
};
|
|
31343
|
+
if (width === 11) {
|
|
31344
|
+
record.llh = {
|
|
31345
|
+
lat: buf[off + 8],
|
|
31346
|
+
lon: buf[off + 9],
|
|
31347
|
+
height: buf[off + 10]
|
|
31348
|
+
};
|
|
31349
|
+
}
|
|
31350
|
+
satRecords.push(record);
|
|
31351
|
+
}
|
|
31352
|
+
results.push(satRecords);
|
|
31353
|
+
}
|
|
31354
|
+
return results;
|
|
31355
|
+
}
|
|
31356
|
+
};
|
|
31357
|
+
|
|
31358
|
+
// src/external/Sgp4Wasm.ts
|
|
31359
|
+
var Sgp4Wasm = class extends Sgp4WasmBase {
|
|
31360
|
+
defaultGlueFile_ = "Sgp4Prop.js";
|
|
31361
|
+
defaultWasmFile_ = "Sgp4Prop.wasm";
|
|
31362
|
+
};
|
|
31363
|
+
|
|
31364
|
+
// src/external/Sgp4XpWasm.ts
|
|
31365
|
+
var Sgp4XpWasm = class extends Sgp4WasmBase {
|
|
31366
|
+
defaultGlueFile_ = "Sgp4Prop.xp.js";
|
|
31367
|
+
defaultWasmFile_ = "Sgp4Prop.xp.wasm";
|
|
31368
|
+
};
|
|
30225
31369
|
/**
|
|
30226
31370
|
* @author Theodore Kruczek
|
|
30227
31371
|
* @license AGPL-3.0-or-later
|
|
@@ -30395,6 +31539,22 @@ var RepeatGroundTrack = class _RepeatGroundTrack {
|
|
|
30395
31539
|
* You should have received a copy of the GNU Affero General Public License along with
|
|
30396
31540
|
* Orbital Object ToolKit. If not, see <http://www.gnu.org/licenses/>.
|
|
30397
31541
|
*/
|
|
31542
|
+
/**
|
|
31543
|
+
* @author Theodore Kruczek
|
|
31544
|
+
* @license AGPL-3.0-or-later
|
|
31545
|
+
* @copyright (c) 2026 Kruczek Labs LLC
|
|
31546
|
+
*
|
|
31547
|
+
* Orbital Object ToolKit is free software: you can redistribute it and/or modify it under the
|
|
31548
|
+
* terms of the GNU Affero General Public License as published by the Free Software
|
|
31549
|
+
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
31550
|
+
*
|
|
31551
|
+
* Orbital Object ToolKit is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
31552
|
+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
31553
|
+
* See the GNU Affero General Public License for more details.
|
|
31554
|
+
*
|
|
31555
|
+
* You should have received a copy of the GNU Affero General Public License along with
|
|
31556
|
+
* Orbital Object ToolKit. If not, see <http://www.gnu.org/licenses/>.
|
|
31557
|
+
*/
|
|
30398
31558
|
/*! Bundled license information:
|
|
30399
31559
|
|
|
30400
31560
|
astronomy-engine/esm/astronomy.js:
|
|
@@ -30518,6 +31678,7 @@ exports.LambertIOD = LambertIOD;
|
|
|
30518
31678
|
exports.LandObject = LandObject;
|
|
30519
31679
|
exports.LaserRangingSensor = LaserRangingSensor;
|
|
30520
31680
|
exports.LaunchTrajectoryGenerator = LaunchTrajectoryGenerator;
|
|
31681
|
+
exports.LaunchWindowFinder = LaunchWindowFinder;
|
|
30521
31682
|
exports.LevenbergMarquardtOD = LevenbergMarquardtOD;
|
|
30522
31683
|
exports.LevenbergMarquardtResult = LevenbergMarquardtResult;
|
|
30523
31684
|
exports.MILLISECONDS_PER_DAY = MILLISECONDS_PER_DAY;
|
|
@@ -30544,7 +31705,6 @@ exports.OmmParser = OmmParser;
|
|
|
30544
31705
|
exports.OotkError = OotkError;
|
|
30545
31706
|
exports.OpticalSensor = OpticalSensor;
|
|
30546
31707
|
exports.OrbitDeterminationError = OrbitDeterminationError;
|
|
30547
|
-
exports.OrbitFinder = OrbitFinder;
|
|
30548
31708
|
exports.OrbitRegime = OrbitRegime;
|
|
30549
31709
|
exports.PI = PI;
|
|
30550
31710
|
exports.ParseError = ParseError;
|
|
@@ -30560,6 +31720,7 @@ exports.ProbabilityOfCollision = ProbabilityOfCollision;
|
|
|
30560
31720
|
exports.PropagationError = PropagationError;
|
|
30561
31721
|
exports.Propagator = Propagator;
|
|
30562
31722
|
exports.PropagatorType = PropagatorType;
|
|
31723
|
+
exports.PropellantBudget = PropellantBudget;
|
|
30563
31724
|
exports.Quaternion = Quaternion;
|
|
30564
31725
|
exports.RAD2DEG = RAD2DEG;
|
|
30565
31726
|
exports.RADIUS_OF_EARTH = RADIUS_OF_EARTH;
|
|
@@ -30580,6 +31741,8 @@ exports.RkResult = RkResult;
|
|
|
30580
31741
|
exports.RungeKutta4Propagator = RungeKutta4Propagator;
|
|
30581
31742
|
exports.RungeKutta89Propagator = RungeKutta89Propagator;
|
|
30582
31743
|
exports.RungeKuttaAdaptive = RungeKuttaAdaptive;
|
|
31744
|
+
exports.SGP4_WASM_ADD_SAT_ERRORS = SGP4_WASM_ADD_SAT_ERRORS;
|
|
31745
|
+
exports.SGP4_WASM_DYN_ARR_ERRORS = SGP4_WASM_DYN_ARR_ERRORS;
|
|
30583
31746
|
exports.SLR_WAVELENGTHS = SLR_WAVELENGTHS;
|
|
30584
31747
|
exports.SPEED_OF_LIGHT_KM_S = SPEED_OF_LIGHT_KM_S;
|
|
30585
31748
|
exports.Satellite = Satellite;
|
|
@@ -30594,6 +31757,12 @@ exports.Sgp4Error = Sgp4Error;
|
|
|
30594
31757
|
exports.Sgp4ErrorCode = Sgp4ErrorCode;
|
|
30595
31758
|
exports.Sgp4OpsMode = Sgp4OpsMode;
|
|
30596
31759
|
exports.Sgp4Propagator = Sgp4Propagator;
|
|
31760
|
+
exports.Sgp4Wasm = Sgp4Wasm;
|
|
31761
|
+
exports.Sgp4WasmBase = Sgp4WasmBase;
|
|
31762
|
+
exports.Sgp4WasmError = Sgp4WasmError;
|
|
31763
|
+
exports.Sgp4WasmLogLevel = Sgp4WasmLogLevel;
|
|
31764
|
+
exports.Sgp4WasmPropagator = Sgp4WasmPropagator;
|
|
31765
|
+
exports.Sgp4XpWasm = Sgp4XpWasm;
|
|
30597
31766
|
exports.SimpleLinearRegression = SimpleLinearRegression;
|
|
30598
31767
|
exports.SolarRadiationPressure = SolarRadiationPressure;
|
|
30599
31768
|
exports.SolarSystem = SolarSystem;
|
|
@@ -30614,6 +31783,7 @@ exports.TimeStamped = TimeStamped;
|
|
|
30614
31783
|
exports.Tle = Tle;
|
|
30615
31784
|
exports.Transmitter = Transmitter;
|
|
30616
31785
|
exports.Transponder = Transponder;
|
|
31786
|
+
exports.TtcAntenna = TtcAntenna;
|
|
30617
31787
|
exports.TwoBurnOrbitTransfer = TwoBurnOrbitTransfer;
|
|
30618
31788
|
exports.Uranus = Uranus;
|
|
30619
31789
|
exports.ValidationError = ValidationError;
|
|
@@ -30677,6 +31847,7 @@ exports.getDegLat = getDegLat;
|
|
|
30677
31847
|
exports.getDegLon = getDegLon;
|
|
30678
31848
|
exports.getRadLat = getRadLat;
|
|
30679
31849
|
exports.getRadLon = getRadLon;
|
|
31850
|
+
exports.groundTrackStateVector = groundTrackStateVector;
|
|
30680
31851
|
exports.halfPi = halfPi;
|
|
30681
31852
|
exports.isLeapYear = isLeapYear;
|
|
30682
31853
|
exports.jacobian = jacobian;
|
|
@@ -30699,6 +31870,7 @@ exports.normalizeAngle = normalizeAngle;
|
|
|
30699
31870
|
exports.observationDerivative = observationDerivative;
|
|
30700
31871
|
exports.observationNoiseFromSigmas = observationNoiseFromSigmas;
|
|
30701
31872
|
exports.parseCenterBody = parseCenterBody;
|
|
31873
|
+
exports.perigeeSpeed = perigeeSpeed;
|
|
30702
31874
|
exports.rad2deg = rad2deg;
|
|
30703
31875
|
exports.radecToPosition = radecToPosition;
|
|
30704
31876
|
exports.radecToVelocity = radecToVelocity;
|
|
@@ -30718,6 +31890,7 @@ exports.sech = sech;
|
|
|
30718
31890
|
exports.secondsPerDay = secondsPerDay;
|
|
30719
31891
|
exports.secondsPerSiderealDay = secondsPerSiderealDay;
|
|
30720
31892
|
exports.secondsPerWeek = secondsPerWeek;
|
|
31893
|
+
exports.semimajorAxisFromMeanMotion = semimajorAxisFromMeanMotion;
|
|
30721
31894
|
exports.sez2rae = sez2rae;
|
|
30722
31895
|
exports.sign = sign;
|
|
30723
31896
|
exports.spaceObjType2Str = spaceObjType2Str;
|