ootk 7.0.3 → 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.js
CHANGED
|
@@ -21,6 +21,8 @@ var ValidationError = class extends OotkError {
|
|
|
21
21
|
this.value = value;
|
|
22
22
|
this.name = "ValidationError";
|
|
23
23
|
}
|
|
24
|
+
field;
|
|
25
|
+
value;
|
|
24
26
|
};
|
|
25
27
|
var ParseError = class extends OotkError {
|
|
26
28
|
/**
|
|
@@ -35,6 +37,8 @@ var ParseError = class extends OotkError {
|
|
|
35
37
|
this.line = line;
|
|
36
38
|
this.name = "ParseError";
|
|
37
39
|
}
|
|
40
|
+
format;
|
|
41
|
+
line;
|
|
38
42
|
};
|
|
39
43
|
var PropagationError = class extends OotkError {
|
|
40
44
|
/**
|
|
@@ -47,6 +51,7 @@ var PropagationError = class extends OotkError {
|
|
|
47
51
|
this.epoch = epoch;
|
|
48
52
|
this.name = "PropagationError";
|
|
49
53
|
}
|
|
54
|
+
epoch;
|
|
50
55
|
};
|
|
51
56
|
var OrbitDeterminationError = class extends OotkError {
|
|
52
57
|
/**
|
|
@@ -59,6 +64,7 @@ var OrbitDeterminationError = class extends OotkError {
|
|
|
59
64
|
this.algorithm = algorithm;
|
|
60
65
|
this.name = "OrbitDeterminationError";
|
|
61
66
|
}
|
|
67
|
+
algorithm;
|
|
62
68
|
};
|
|
63
69
|
|
|
64
70
|
// src/enums/AngularDiameterMethod.ts
|
|
@@ -153,6 +159,7 @@ var SensorType = /* @__PURE__ */ ((SensorType2) => {
|
|
|
153
159
|
SensorType2["LASER_RANGING"] = "LASER_RANGING";
|
|
154
160
|
SensorType2["PASSIVE_RF"] = "PASSIVE_RF";
|
|
155
161
|
SensorType2["BISTATIC_RADIO_TELESCOPE"] = "BISTATIC_RADIO_TELESCOPE";
|
|
162
|
+
SensorType2["TT_C_ANTENNA"] = "TT_C_ANTENNA";
|
|
156
163
|
return SensorType2;
|
|
157
164
|
})(SensorType || {});
|
|
158
165
|
|
|
@@ -223,6 +230,7 @@ var SpaceObjectType = /* @__PURE__ */ ((SpaceObjectType2) => {
|
|
|
223
230
|
SpaceObjectType2[SpaceObjectType2["DWARF_PLANET"] = 36] = "DWARF_PLANET";
|
|
224
231
|
SpaceObjectType2[SpaceObjectType2["MOON"] = 37] = "MOON";
|
|
225
232
|
SpaceObjectType2[SpaceObjectType2["DYNAMIC_GROUND_OBJECT"] = 38] = "DYNAMIC_GROUND_OBJECT";
|
|
233
|
+
SpaceObjectType2[SpaceObjectType2["ASTEROID"] = 39] = "ASTEROID";
|
|
226
234
|
SpaceObjectType2[SpaceObjectType2["MAX_SPACE_OBJECT_TYPE"] = 40] = "MAX_SPACE_OBJECT_TYPE";
|
|
227
235
|
return SpaceObjectType2;
|
|
228
236
|
})(SpaceObjectType || {});
|
|
@@ -276,6 +284,7 @@ var Epoch = class {
|
|
|
276
284
|
throw new ValidationError("Epoch posix time must be a valid number", "posix", posix);
|
|
277
285
|
}
|
|
278
286
|
}
|
|
287
|
+
posix;
|
|
279
288
|
toString() {
|
|
280
289
|
return this.toDateTime().toISOString();
|
|
281
290
|
}
|
|
@@ -438,7 +447,8 @@ function matchHalfPlane(angle, match) {
|
|
|
438
447
|
return Math.abs(d1) < Math.abs(d2) ? a1 : a2;
|
|
439
448
|
}
|
|
440
449
|
function wrapAngle(theta) {
|
|
441
|
-
const
|
|
450
|
+
const tau = 2 * Math.PI;
|
|
451
|
+
const result = ((theta + Math.PI) % tau + tau) % tau - Math.PI;
|
|
442
452
|
if (result === -Math.PI) {
|
|
443
453
|
return Math.PI;
|
|
444
454
|
}
|
|
@@ -447,7 +457,7 @@ function wrapAngle(theta) {
|
|
|
447
457
|
function angularDistanceCosine_(lam1, phi1, lam2, phi2) {
|
|
448
458
|
const a = Math.sin(phi1) * Math.sin(phi2);
|
|
449
459
|
const b = Math.cos(phi1) * Math.cos(phi2) * Math.cos(lam2 - lam1);
|
|
450
|
-
return Math.acos(a + b);
|
|
460
|
+
return Math.acos(Math.min(1, Math.max(-1, a + b)));
|
|
451
461
|
}
|
|
452
462
|
function angularDistanceHaversine_(lam1, phi1, lam2, phi2) {
|
|
453
463
|
const dlam = lam2 - lam1;
|
|
@@ -632,6 +642,7 @@ var spaceObjTypeStrMap_ = {
|
|
|
632
642
|
[36 /* DWARF_PLANET */]: "Dwarf Planet",
|
|
633
643
|
[37 /* MOON */]: "Moon",
|
|
634
644
|
[38 /* DYNAMIC_GROUND_OBJECT */]: "Dynamic Ground Object",
|
|
645
|
+
[39 /* ASTEROID */]: "Asteroid",
|
|
635
646
|
[40 /* MAX_SPACE_OBJECT_TYPE */]: "Max Space Object Type"
|
|
636
647
|
};
|
|
637
648
|
var spaceObjType2Str = (spaceObjType) => spaceObjTypeStrMap_[spaceObjType] || "Unknown";
|
|
@@ -1813,6 +1824,8 @@ var EpochGPS = class _EpochGPS {
|
|
|
1813
1824
|
throw new ValidationError("GPS seconds must be between 0 and 604799", "seconds", seconds);
|
|
1814
1825
|
}
|
|
1815
1826
|
}
|
|
1827
|
+
week;
|
|
1828
|
+
seconds;
|
|
1816
1829
|
/** Cached GPS reference epoch (1980-01-06T00:00:00.000Z) */
|
|
1817
1830
|
static reference_ = null;
|
|
1818
1831
|
/**
|
|
@@ -1948,6 +1961,7 @@ var Vector = class _Vector {
|
|
|
1948
1961
|
this.elements = elements;
|
|
1949
1962
|
this.length = elements.length;
|
|
1950
1963
|
}
|
|
1964
|
+
elements;
|
|
1951
1965
|
/**
|
|
1952
1966
|
* The length of the vector.
|
|
1953
1967
|
*/
|
|
@@ -2711,6 +2725,9 @@ var Vector3D = class _Vector3D {
|
|
|
2711
2725
|
this.y = y;
|
|
2712
2726
|
this.z = z;
|
|
2713
2727
|
}
|
|
2728
|
+
x;
|
|
2729
|
+
y;
|
|
2730
|
+
z;
|
|
2714
2731
|
/**
|
|
2715
2732
|
* Create a new Vector3D object from the first three elements of a Vector
|
|
2716
2733
|
* object.
|
|
@@ -3218,6 +3235,131 @@ var Sgp4Error = class _Sgp4Error extends Error {
|
|
|
3218
3235
|
}
|
|
3219
3236
|
};
|
|
3220
3237
|
|
|
3238
|
+
// src/sgp4/sgp4-wasm-backend.ts
|
|
3239
|
+
var WASM_KEY_FAILED = -1n;
|
|
3240
|
+
var defineHidden_ = (obj, key, value) => {
|
|
3241
|
+
Object.defineProperty(obj, key, {
|
|
3242
|
+
value,
|
|
3243
|
+
writable: true,
|
|
3244
|
+
configurable: true,
|
|
3245
|
+
enumerable: false
|
|
3246
|
+
});
|
|
3247
|
+
};
|
|
3248
|
+
var backend = null;
|
|
3249
|
+
var satKeyCache = /* @__PURE__ */ new Map();
|
|
3250
|
+
var syntheticSatnumCounter = 0;
|
|
3251
|
+
var wasmAttachedCount = 0;
|
|
3252
|
+
var wasmFallbackCount = 0;
|
|
3253
|
+
var SYNTHETIC_SATNUM_LETTERS = "VWXYZ";
|
|
3254
|
+
var synthesizeSatnum_ = () => {
|
|
3255
|
+
const n = syntheticSatnumCounter++;
|
|
3256
|
+
const letterIdx = Math.floor(n / 1e4);
|
|
3257
|
+
if (letterIdx >= SYNTHETIC_SATNUM_LETTERS.length) {
|
|
3258
|
+
return null;
|
|
3259
|
+
}
|
|
3260
|
+
return `${SYNTHETIC_SATNUM_LETTERS[letterIdx]}${(n % 1e4).toString().padStart(4, "0")}`;
|
|
3261
|
+
};
|
|
3262
|
+
var tryAddWithSyntheticSatnum_ = (wasm, line1, line2) => {
|
|
3263
|
+
const satnumField = line1.substring(2, 7);
|
|
3264
|
+
const classification = line1.charAt(7);
|
|
3265
|
+
const isSatnumMeaningless = satnumField.trim() === "" || satnumField === "00000";
|
|
3266
|
+
const isNonStandardClassification = classification !== "U" && classification !== "C" && classification !== "S";
|
|
3267
|
+
if (!isSatnumMeaningless && !isNonStandardClassification) {
|
|
3268
|
+
return null;
|
|
3269
|
+
}
|
|
3270
|
+
const satnum = synthesizeSatnum_();
|
|
3271
|
+
if (satnum === null) {
|
|
3272
|
+
return null;
|
|
3273
|
+
}
|
|
3274
|
+
const syntheticLine1 = `${line1.slice(0, 2)}${satnum}U${line1.slice(8)}`;
|
|
3275
|
+
const syntheticLine2 = `${line2.slice(0, 2)}${satnum}${line2.slice(7)}`;
|
|
3276
|
+
try {
|
|
3277
|
+
const satKey = wasm.addSat(syntheticLine1, syntheticLine2);
|
|
3278
|
+
wasm.initSats([satKey]);
|
|
3279
|
+
return satKey;
|
|
3280
|
+
} catch {
|
|
3281
|
+
return null;
|
|
3282
|
+
}
|
|
3283
|
+
};
|
|
3284
|
+
var setSgp4WasmBackend = (wasm) => {
|
|
3285
|
+
if (!wasm.isLoaded) {
|
|
3286
|
+
throw new Error("Sgp4 wasm backend must be loaded before use. Call load() first.");
|
|
3287
|
+
}
|
|
3288
|
+
backend = wasm;
|
|
3289
|
+
};
|
|
3290
|
+
var clearSgp4WasmBackend = () => {
|
|
3291
|
+
backend = null;
|
|
3292
|
+
satKeyCache.clear();
|
|
3293
|
+
syntheticSatnumCounter = 0;
|
|
3294
|
+
wasmAttachedCount = 0;
|
|
3295
|
+
wasmFallbackCount = 0;
|
|
3296
|
+
};
|
|
3297
|
+
var getSgp4WasmBackendStats = () => ({
|
|
3298
|
+
attached: wasmAttachedCount,
|
|
3299
|
+
fallback: wasmFallbackCount
|
|
3300
|
+
});
|
|
3301
|
+
var isSgp4WasmBackendActive = () => backend !== null;
|
|
3302
|
+
var stashTleLines = (satrec, line1, line2) => {
|
|
3303
|
+
defineHidden_(satrec, "wasmTleLine1", line1);
|
|
3304
|
+
defineHidden_(satrec, "wasmTleLine2", line2);
|
|
3305
|
+
};
|
|
3306
|
+
var ensureSatKey_ = (wasm, satrec) => {
|
|
3307
|
+
const existing = satrec.wasmSatKey;
|
|
3308
|
+
if (existing !== void 0) {
|
|
3309
|
+
return existing > 0n ? existing : null;
|
|
3310
|
+
}
|
|
3311
|
+
const line1 = satrec.wasmTleLine1;
|
|
3312
|
+
const line2 = satrec.wasmTleLine2;
|
|
3313
|
+
if (!line1 || !line2) {
|
|
3314
|
+
defineHidden_(satrec, "wasmSatKey", WASM_KEY_FAILED);
|
|
3315
|
+
wasmFallbackCount++;
|
|
3316
|
+
return null;
|
|
3317
|
+
}
|
|
3318
|
+
const cacheKey = `${line1}
|
|
3319
|
+
${line2}`;
|
|
3320
|
+
const cached = satKeyCache.get(cacheKey);
|
|
3321
|
+
if (cached !== void 0) {
|
|
3322
|
+
defineHidden_(satrec, "wasmSatKey", cached);
|
|
3323
|
+
return cached;
|
|
3324
|
+
}
|
|
3325
|
+
try {
|
|
3326
|
+
const satKey = wasm.addSat(line1, line2);
|
|
3327
|
+
wasm.initSats([satKey]);
|
|
3328
|
+
satKeyCache.set(cacheKey, satKey);
|
|
3329
|
+
defineHidden_(satrec, "wasmSatKey", satKey);
|
|
3330
|
+
wasmAttachedCount++;
|
|
3331
|
+
return satKey;
|
|
3332
|
+
} catch {
|
|
3333
|
+
const syntheticKey = tryAddWithSyntheticSatnum_(wasm, line1, line2);
|
|
3334
|
+
if (syntheticKey !== null) {
|
|
3335
|
+
satKeyCache.set(cacheKey, syntheticKey);
|
|
3336
|
+
defineHidden_(satrec, "wasmSatKey", syntheticKey);
|
|
3337
|
+
wasmAttachedCount++;
|
|
3338
|
+
return syntheticKey;
|
|
3339
|
+
}
|
|
3340
|
+
defineHidden_(satrec, "wasmSatKey", WASM_KEY_FAILED);
|
|
3341
|
+
wasmFallbackCount++;
|
|
3342
|
+
return null;
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
var tryPropagateWasm = (satrec, tsince) => {
|
|
3346
|
+
if (!backend || satrec.init) {
|
|
3347
|
+
return null;
|
|
3348
|
+
}
|
|
3349
|
+
const satKey = ensureSatKey_(backend, satrec);
|
|
3350
|
+
if (satKey === null) {
|
|
3351
|
+
return null;
|
|
3352
|
+
}
|
|
3353
|
+
const result = backend.propagateOnePosVelFast(satKey, tsince);
|
|
3354
|
+
if (result.err !== 0) {
|
|
3355
|
+
return { position: false, velocity: false };
|
|
3356
|
+
}
|
|
3357
|
+
return {
|
|
3358
|
+
position: result.position,
|
|
3359
|
+
velocity: result.velocity
|
|
3360
|
+
};
|
|
3361
|
+
};
|
|
3362
|
+
|
|
3221
3363
|
// src/sgp4/sgp4.ts
|
|
3222
3364
|
var fasx2 = 0.13130908;
|
|
3223
3365
|
var fasx4 = 2.8843198;
|
|
@@ -3232,6 +3374,42 @@ var stepp = 720;
|
|
|
3232
3374
|
var stepn = -720;
|
|
3233
3375
|
var step2 = 259200;
|
|
3234
3376
|
var Sgp4 = class _Sgp4 {
|
|
3377
|
+
/**
|
|
3378
|
+
* Routes all subsequent `createSatrec`/`propagate` calls in this JavaScript
|
|
3379
|
+
* context through the USSF Astro Standards SGP4 wasm build instead of the
|
|
3380
|
+
* pure-TypeScript implementation. Pass a loaded `Sgp4Wasm` or `Sgp4XpWasm`
|
|
3381
|
+
* instance (see `src/external/`).
|
|
3382
|
+
*
|
|
3383
|
+
* satKeys are attached to satrecs lazily on first propagation, so the
|
|
3384
|
+
* backend can be activated before or after satrecs are created. Satrecs
|
|
3385
|
+
* without TLE lines (e.g. built from an OMM) and TLEs the wasm library
|
|
3386
|
+
* rejects fall back to the TypeScript implementation permanently.
|
|
3387
|
+
* @param wasm A loaded wasm SGP4 instance.
|
|
3388
|
+
*/
|
|
3389
|
+
static useWasmBackend(wasm) {
|
|
3390
|
+
setSgp4WasmBackend(wasm);
|
|
3391
|
+
}
|
|
3392
|
+
/**
|
|
3393
|
+
* Restores the pure-TypeScript SGP4 implementation.
|
|
3394
|
+
*/
|
|
3395
|
+
static clearWasmBackend() {
|
|
3396
|
+
clearSgp4WasmBackend();
|
|
3397
|
+
}
|
|
3398
|
+
/**
|
|
3399
|
+
* Whether propagation is currently routed through a wasm backend.
|
|
3400
|
+
*/
|
|
3401
|
+
static get isWasmBackendActive() {
|
|
3402
|
+
return isSgp4WasmBackendActive();
|
|
3403
|
+
}
|
|
3404
|
+
/**
|
|
3405
|
+
* Diagnostic counters for the wasm backend: distinct TLEs attached to the
|
|
3406
|
+
* wasm registry vs permanently fallen back to the TypeScript
|
|
3407
|
+
* implementation. A growing fallback count means part of the catalog is
|
|
3408
|
+
* still propagating in TypeScript.
|
|
3409
|
+
*/
|
|
3410
|
+
static get wasmBackendStats() {
|
|
3411
|
+
return getSgp4WasmBackendStats();
|
|
3412
|
+
}
|
|
3235
3413
|
/*
|
|
3236
3414
|
* -----------------------------------------------------------------------------
|
|
3237
3415
|
*
|
|
@@ -3391,6 +3569,7 @@ var Sgp4 = class _Sgp4 {
|
|
|
3391
3569
|
xno: satrec.no,
|
|
3392
3570
|
xnodeo: satrec.nodeo
|
|
3393
3571
|
});
|
|
3572
|
+
stashTleLines(satrec, tleLine1, tleLine2);
|
|
3394
3573
|
return satrec;
|
|
3395
3574
|
}
|
|
3396
3575
|
static createSatrecFromOmm(omm, whichconst = "wgs72" /* wgs72 */, opsmode = "i" /* IMPROVED */) {
|
|
@@ -3908,6 +4087,10 @@ var Sgp4 = class _Sgp4 {
|
|
|
3908
4087
|
*----------------------------------------------------------------------------
|
|
3909
4088
|
*/
|
|
3910
4089
|
static propagate(satrec, tsince) {
|
|
4090
|
+
const wasmResult = tryPropagateWasm(satrec, tsince);
|
|
4091
|
+
if (wasmResult !== null) {
|
|
4092
|
+
return wasmResult;
|
|
4093
|
+
}
|
|
3911
4094
|
const { xke, j2, j3oj2, vkmpersec } = satrec;
|
|
3912
4095
|
satrec.t = tsince;
|
|
3913
4096
|
satrec.error = 0 /* NO_ERROR */;
|
|
@@ -6318,7 +6501,7 @@ function uv2azel(u, v, coneHalfAngle) {
|
|
|
6318
6501
|
throw new ValidationError("v must be between -1 and 1", "v", v);
|
|
6319
6502
|
}
|
|
6320
6503
|
const alpha = Math.asin(u);
|
|
6321
|
-
const beta = Math.asin(v);
|
|
6504
|
+
const beta = Math.asin(0 - v);
|
|
6322
6505
|
const az = alpha / 90 * (coneHalfAngle * RAD2DEG);
|
|
6323
6506
|
const el = beta / 90 * (coneHalfAngle * RAD2DEG);
|
|
6324
6507
|
return { az, el };
|
|
@@ -6352,6 +6535,8 @@ var StateCovariance = class _StateCovariance {
|
|
|
6352
6535
|
this.matrix = matrix;
|
|
6353
6536
|
this.frame = frame;
|
|
6354
6537
|
}
|
|
6538
|
+
matrix;
|
|
6539
|
+
frame;
|
|
6355
6540
|
// / Create a new [StateCovariance] object from 1-sigma values.
|
|
6356
6541
|
static fromSigmas(sigmas, frame) {
|
|
6357
6542
|
const n = sigmas.length;
|
|
@@ -6554,7 +6739,7 @@ var ClassicalElements = class _ClassicalElements {
|
|
|
6554
6739
|
}
|
|
6555
6740
|
const pos = state.position;
|
|
6556
6741
|
const vel = state.velocity;
|
|
6557
|
-
const a =
|
|
6742
|
+
const a = 1 / (2 / pos.magnitude() - vel.magnitude() ** 2 / mu);
|
|
6558
6743
|
const eVecA = pos.scale(vel.magnitude() ** 2 - mu / pos.magnitude());
|
|
6559
6744
|
const eVecB = vel.scale(pos.dot(vel));
|
|
6560
6745
|
const eVec = eVecA.subtract(eVecB).scale(1 / mu);
|
|
@@ -6683,7 +6868,7 @@ var ClassicalElements = class _ClassicalElements {
|
|
|
6683
6868
|
*/
|
|
6684
6869
|
getOrbitRegime() {
|
|
6685
6870
|
const n = this.revsPerDay;
|
|
6686
|
-
const p = this.period
|
|
6871
|
+
const p = this.period;
|
|
6687
6872
|
if (n >= 0.99 && n <= 1.01 && this.eccentricity < 0.01) {
|
|
6688
6873
|
return "Geosynchronous Orbit" /* GEO */;
|
|
6689
6874
|
}
|
|
@@ -6751,13 +6936,14 @@ var ClassicalElements = class _ClassicalElements {
|
|
|
6751
6936
|
let maInit = eaInit - this.eccentricity * Math.sin(eaInit);
|
|
6752
6937
|
maInit = matchHalfPlane(maInit, eaInit);
|
|
6753
6938
|
const maFinal = (maInit + n * delta) % TAU;
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6939
|
+
const ecc = this.eccentricity;
|
|
6940
|
+
let eaFinal = ecc > 0.8 ? Math.PI * Math.sign(maFinal || 1) : maFinal;
|
|
6941
|
+
for (let iter = 0; iter < 50; iter++) {
|
|
6942
|
+
const step = (eaFinal - ecc * Math.sin(eaFinal) - maFinal) / (1 - ecc * Math.cos(eaFinal));
|
|
6943
|
+
eaFinal -= step;
|
|
6944
|
+
if (Math.abs(step) < 1e-12) {
|
|
6758
6945
|
break;
|
|
6759
6946
|
}
|
|
6760
|
-
eaFinal = eaTemp;
|
|
6761
6947
|
}
|
|
6762
6948
|
const cosEaFinal = Math.cos(eaFinal);
|
|
6763
6949
|
let vFinal = Math.acos(clamp((cosEaFinal - this.eccentricity) / (1 - this.eccentricity * cosEaFinal), -1, 1));
|
|
@@ -7187,13 +7373,21 @@ var GroundObject = class extends BaseObject {
|
|
|
7187
7373
|
}
|
|
7188
7374
|
/**
|
|
7189
7375
|
* Calculates the Earth-Centered Inertial (ECI) position vector of the ground object at a given date.
|
|
7190
|
-
*
|
|
7376
|
+
*
|
|
7377
|
+
* Uses the WGS84 ellipsoid (the same {@link llaRad2ecef} position that `ecef()` and
|
|
7378
|
+
* `Satellite.rae()` use), rotated into TEME by GMST. It deliberately does not use the
|
|
7379
|
+
* spherical-Earth `lla2eci()`: at mid latitudes that puts the observer ~20 km away from
|
|
7380
|
+
* its WGS84 position, which skewed `Satellite.dopplerFactor()` by up to ~0.2 km/s of range
|
|
7381
|
+
* rate near closest approach.
|
|
7382
|
+
*
|
|
7383
|
+
* The result is in TEME (GMST rotation only), the frame `Satellite.eci()` returns. For J2000
|
|
7384
|
+
* use {@link toJ2000}, which also applies precession and nutation.
|
|
7191
7385
|
* @param date The date for which to calculate the ECI position vector. Defaults to the current date.
|
|
7192
7386
|
* @returns The ECI position vector of the ground object.
|
|
7193
7387
|
*/
|
|
7194
7388
|
eci(date = /* @__PURE__ */ new Date()) {
|
|
7195
7389
|
const { gmst } = calcGmst(date);
|
|
7196
|
-
return
|
|
7390
|
+
return ecef2eci(llaRad2ecef(this.llaRad()), gmst);
|
|
7197
7391
|
}
|
|
7198
7392
|
/**
|
|
7199
7393
|
* Returns the latitude, longitude, and altitude of the GroundObject.
|
|
@@ -7234,18 +7428,16 @@ var GroundObject = class extends BaseObject {
|
|
|
7234
7428
|
}
|
|
7235
7429
|
/**
|
|
7236
7430
|
* Converts the ground position to J2000 inertial coordinates.
|
|
7237
|
-
*
|
|
7431
|
+
*
|
|
7432
|
+
* The site is placed on the WGS84 ellipsoid in ITRF and rotated into J2000 with the full
|
|
7433
|
+
* precession, nutation and sidereal-time chain ({@link ITRF.toJ2000}), so it is in the same
|
|
7434
|
+
* frame as `Satellite.toJ2000()`. The velocity is the Earth-rotation velocity (omega x r,
|
|
7435
|
+
* ~0.46 km/s at the equator): a ground site is fixed in ITRF, not in inertial space.
|
|
7238
7436
|
* @param date - The date for the conversion (defaults to now)
|
|
7239
7437
|
* @returns J2000 state vector
|
|
7240
7438
|
*/
|
|
7241
7439
|
toJ2000(date = /* @__PURE__ */ new Date()) {
|
|
7242
|
-
|
|
7243
|
-
const position = lla2eci(this.llaRad(), gmst);
|
|
7244
|
-
return new J2000(
|
|
7245
|
-
EpochUTC.fromDateTime(date),
|
|
7246
|
-
new Vector3D(position.x, position.y, position.z),
|
|
7247
|
-
new Vector3D(0, 0, 0)
|
|
7248
|
-
);
|
|
7440
|
+
return this.toGeodetic().toITRF(EpochUTC.fromDateTime(date)).toJ2000();
|
|
7249
7441
|
}
|
|
7250
7442
|
// ==================== Component Management ====================
|
|
7251
7443
|
/**
|
|
@@ -7792,7 +7984,7 @@ var TEME = class _TEME extends StateVector {
|
|
|
7792
7984
|
}
|
|
7793
7985
|
};
|
|
7794
7986
|
|
|
7795
|
-
// node_modules/astronomy-engine/esm/astronomy.js
|
|
7987
|
+
// node_modules/.pnpm/astronomy-engine@2.1.19/node_modules/astronomy-engine/esm/astronomy.js
|
|
7796
7988
|
var C_AUDAY = 173.1446326846693;
|
|
7797
7989
|
var KM_PER_AU2 = 14959787069098932e-8;
|
|
7798
7990
|
var DEG2RAD2 = 0.017453292519943295;
|
|
@@ -9859,7 +10051,7 @@ function CalcPluto(time, helio) {
|
|
|
9859
10051
|
r = ra.mul(1 - ramp).add(rb.mul(ramp));
|
|
9860
10052
|
v = va.mul(1 - ramp).add(vb.mul(ramp));
|
|
9861
10053
|
}
|
|
9862
|
-
|
|
10054
|
+
{
|
|
9863
10055
|
if (!bary)
|
|
9864
10056
|
bary = new major_bodies_t(time.tt);
|
|
9865
10057
|
r = r.sub(bary.Sun.r);
|
|
@@ -9872,7 +10064,7 @@ function HelioVector(body, date) {
|
|
|
9872
10064
|
if (body in vsop)
|
|
9873
10065
|
return CalcVsop(vsop[body], time);
|
|
9874
10066
|
if (body === Body.Pluto) {
|
|
9875
|
-
const p = CalcPluto(time
|
|
10067
|
+
const p = CalcPluto(time);
|
|
9876
10068
|
return new Vector2(p.x, p.y, p.z, time);
|
|
9877
10069
|
}
|
|
9878
10070
|
if (body === Body.Sun)
|
|
@@ -9968,38 +10160,6 @@ function GeoVector(body, date, aberration) {
|
|
|
9968
10160
|
function ExportState(terse, time) {
|
|
9969
10161
|
return new StateVector2(terse.r.x, terse.r.y, terse.r.z, terse.v.x, terse.v.y, terse.v.z, time);
|
|
9970
10162
|
}
|
|
9971
|
-
function BaryState(body, date) {
|
|
9972
|
-
const time = MakeTime(date);
|
|
9973
|
-
if (body === Body.SSB) {
|
|
9974
|
-
return new StateVector2(0, 0, 0, 0, 0, 0, time);
|
|
9975
|
-
}
|
|
9976
|
-
if (body === Body.Pluto) {
|
|
9977
|
-
return CalcPluto(time, false);
|
|
9978
|
-
}
|
|
9979
|
-
const bary = new major_bodies_t(time.tt);
|
|
9980
|
-
switch (body) {
|
|
9981
|
-
case Body.Sun:
|
|
9982
|
-
return ExportState(bary.Sun, time);
|
|
9983
|
-
case Body.Jupiter:
|
|
9984
|
-
return ExportState(bary.Jupiter, time);
|
|
9985
|
-
case Body.Saturn:
|
|
9986
|
-
return ExportState(bary.Saturn, time);
|
|
9987
|
-
case Body.Uranus:
|
|
9988
|
-
return ExportState(bary.Uranus, time);
|
|
9989
|
-
case Body.Neptune:
|
|
9990
|
-
return ExportState(bary.Neptune, time);
|
|
9991
|
-
case Body.Moon:
|
|
9992
|
-
case Body.EMB:
|
|
9993
|
-
const earth = CalcVsopPosVel(vsop[Body.Earth], time.tt);
|
|
9994
|
-
const state = body === Body.Moon ? GeoMoonState(time) : GeoEmbState(time);
|
|
9995
|
-
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);
|
|
9996
|
-
}
|
|
9997
|
-
if (body in vsop) {
|
|
9998
|
-
const planet = CalcVsopPosVel(vsop[body], time.tt);
|
|
9999
|
-
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);
|
|
10000
|
-
}
|
|
10001
|
-
throw `BaryState: Unsupported body "${body}"`;
|
|
10002
|
-
}
|
|
10003
10163
|
function HelioState(body, date) {
|
|
10004
10164
|
const time = MakeTime(date);
|
|
10005
10165
|
switch (body) {
|
|
@@ -10019,7 +10179,7 @@ function HelioState(body, date) {
|
|
|
10019
10179
|
const planet = CalcVsopPosVel(vsop[body], time.tt);
|
|
10020
10180
|
return ExportState(planet, time);
|
|
10021
10181
|
case Body.Pluto:
|
|
10022
|
-
return CalcPluto(time
|
|
10182
|
+
return CalcPluto(time);
|
|
10023
10183
|
case Body.Moon:
|
|
10024
10184
|
case Body.EMB:
|
|
10025
10185
|
const earth = CalcVsopPosVel(vsop.Earth, time.tt);
|
|
@@ -10340,6 +10500,11 @@ function SearchRiseSet(body, observer, direction, dateStart, limitDays, metersAb
|
|
|
10340
10500
|
const altitude = dip - REFRACTION_NEAR_HORIZON * atmos.density;
|
|
10341
10501
|
return InternalSearchAltitude(body, observer, direction, dateStart, limitDays, body_radius_au, altitude);
|
|
10342
10502
|
}
|
|
10503
|
+
function SearchAltitude(body, observer, direction, dateStart, limitDays, altitude) {
|
|
10504
|
+
if (!Number.isFinite(altitude) || altitude < -90 || altitude > 90)
|
|
10505
|
+
throw `Invalid altitude angle: ${altitude}`;
|
|
10506
|
+
return InternalSearchAltitude(body, observer, direction, dateStart, limitDays, 0, altitude);
|
|
10507
|
+
}
|
|
10343
10508
|
var AscentInfo = class {
|
|
10344
10509
|
constructor(tx, ty, ax, ay) {
|
|
10345
10510
|
this.tx = tx;
|
|
@@ -10602,7 +10767,9 @@ var CelestialBody = class extends BaseObject {
|
|
|
10602
10767
|
* Gets rise, transit, and set times for this body as seen from a ground location.
|
|
10603
10768
|
* @param observer - The ground observer location
|
|
10604
10769
|
* @param date - The starting date for the search
|
|
10605
|
-
* @param minElevation -
|
|
10770
|
+
* @param minElevation - Elevation threshold in degrees (default 0). At 0 the times are the
|
|
10771
|
+
* usual rise/set (upper limb on the horizon, with refraction); otherwise they are when the
|
|
10772
|
+
* body's centre crosses that geometric elevation.
|
|
10606
10773
|
* @returns Rise, transit, and set times (null if body doesn't rise/set)
|
|
10607
10774
|
*/
|
|
10608
10775
|
getRiseSetTimes(observer, date = /* @__PURE__ */ new Date(), minElevation = 0) {
|
|
@@ -10611,8 +10778,10 @@ var CelestialBody = class extends BaseObject {
|
|
|
10611
10778
|
}
|
|
10612
10779
|
const obs = new Observer(observer.lat, observer.lon, observer.alt * 1e3);
|
|
10613
10780
|
const time = MakeTime(date);
|
|
10614
|
-
const
|
|
10615
|
-
const
|
|
10781
|
+
const body = this.astronomyBody_;
|
|
10782
|
+
const search = (direction) => minElevation === 0 ? SearchRiseSet(body, obs, direction, time, 1) : SearchAltitude(body, obs, direction, time, 1, minElevation);
|
|
10783
|
+
const riseTime = search(1);
|
|
10784
|
+
const setTime = search(-1);
|
|
10616
10785
|
let transitTime = null;
|
|
10617
10786
|
if (riseTime && setTime) {
|
|
10618
10787
|
const riseMs = riseTime.date.getTime();
|
|
@@ -10631,14 +10800,17 @@ var CelestialBody = class extends BaseObject {
|
|
|
10631
10800
|
}
|
|
10632
10801
|
/**
|
|
10633
10802
|
* Calculates the angular diameter of this body as seen from a given position.
|
|
10634
|
-
* @param observerPos - The observer's position in km
|
|
10803
|
+
* @param observerPos - The observer's position in km (J2000, at `date`)
|
|
10804
|
+
* @param date - The time of `observerPos` (defaults to now). The body is placed at this
|
|
10805
|
+
* time; without it a past or future observer position was paired with the body's
|
|
10806
|
+
* position now.
|
|
10635
10807
|
* @returns Angular diameter in radians
|
|
10636
10808
|
*/
|
|
10637
|
-
getAngularDiameter(observerPos) {
|
|
10809
|
+
getAngularDiameter(observerPos, date = /* @__PURE__ */ new Date()) {
|
|
10638
10810
|
if (!this.radius) {
|
|
10639
10811
|
throw new Error(`${this.name} does not have a defined radius`);
|
|
10640
10812
|
}
|
|
10641
|
-
const distance = observerPos.subtract(this.eci()).magnitude();
|
|
10813
|
+
const distance = observerPos.subtract(this.eci(date)).magnitude();
|
|
10642
10814
|
return 2 * Math.atan(this.radius / distance);
|
|
10643
10815
|
}
|
|
10644
10816
|
/**
|
|
@@ -10779,16 +10951,16 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
10779
10951
|
return this.geoVectorToKm(Body.Sun, date);
|
|
10780
10952
|
}
|
|
10781
10953
|
/**
|
|
10782
|
-
* Gets the Sun's apparent position (corrected for light travel time).
|
|
10954
|
+
* Gets the Sun's apparent position (corrected for light travel time and aberration).
|
|
10955
|
+
*
|
|
10956
|
+
* Same as {@link eci}: astronomy-engine's `GeoVector` already applies the light-time and
|
|
10957
|
+
* aberration corrections. (This used to roll the time back by another ~499 s, putting the
|
|
10958
|
+
* Sun a further ~20 arcsec along its path.)
|
|
10783
10959
|
* @param date - The date/time for the position calculation
|
|
10784
|
-
* @returns Position vector in kilometers
|
|
10960
|
+
* @returns Position vector in kilometers (J2000)
|
|
10785
10961
|
*/
|
|
10786
10962
|
eciApparent(date = /* @__PURE__ */ new Date()) {
|
|
10787
|
-
|
|
10788
|
-
const distance = pos.magnitude();
|
|
10789
|
-
const lightTimeSec = distance / cKmPerSec;
|
|
10790
|
-
const adjustedDate = new Date(date.getTime() - lightTimeSec * 1e3);
|
|
10791
|
-
return this.eci(adjustedDate);
|
|
10963
|
+
return this.eci(date);
|
|
10792
10964
|
}
|
|
10793
10965
|
/**
|
|
10794
10966
|
* Gets the Sun's position in heliocentric coordinates.
|
|
@@ -10853,8 +11025,13 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
10853
11025
|
// ==================== Satellite Shadow Methods ====================
|
|
10854
11026
|
/**
|
|
10855
11027
|
* Determines if a satellite is in Earth's shadow.
|
|
11028
|
+
*
|
|
11029
|
+
* The test is a cylinder-plus-cone that tracks the umbra boundary closely (within ~0.01 deg
|
|
11030
|
+
* of the `lightingRatio` umbra edge in LEO); a satellite in the penumbra counts as lit. Use
|
|
11031
|
+
* {@link lightingRatio} when partial illumination matters.
|
|
10856
11032
|
* @param epoch - The epoch for the calculation
|
|
10857
|
-
* @param satPos - The satellite's
|
|
11033
|
+
* @param satPos - The satellite's position in J2000 (the frame of `eci()`), km; a TEME
|
|
11034
|
+
* position from `Satellite.eci()` is ~0.3 deg off in 2026
|
|
10858
11035
|
* @example
|
|
10859
11036
|
* ```typescript
|
|
10860
11037
|
* import { Sun, Satellite, EpochUTC, Vector3D, Kilometers, Seconds } from 'ootk';
|
|
@@ -10863,14 +11040,8 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
10863
11040
|
* const now = new Date();
|
|
10864
11041
|
* const epoch = new EpochUTC((now.getTime() / 1000) as Seconds);
|
|
10865
11042
|
*
|
|
10866
|
-
* const
|
|
10867
|
-
*
|
|
10868
|
-
* const satPos = new Vector3D<Kilometers>(
|
|
10869
|
-
* pv.position.x as Kilometers,
|
|
10870
|
-
* pv.position.y as Kilometers,
|
|
10871
|
-
* pv.position.z as Kilometers
|
|
10872
|
-
* );
|
|
10873
|
-
*
|
|
11043
|
+
* const satPos = satellite.toJ2000(now).position; // J2000, like Sun.eci()
|
|
11044
|
+
* {
|
|
10874
11045
|
* const inShadow = Sun.shadow(epoch, satPos);
|
|
10875
11046
|
* console.log(inShadow ? 'Satellite is in eclipse' : 'Satellite is sunlit');
|
|
10876
11047
|
*
|
|
@@ -10946,11 +11117,12 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
10946
11117
|
}
|
|
10947
11118
|
/**
|
|
10948
11119
|
* Calculates the Sun's angular diameter as seen from a position.
|
|
10949
|
-
* @param obsPos - Observer position in kilometers
|
|
11120
|
+
* @param obsPos - Observer position in kilometers (J2000, at `date`)
|
|
11121
|
+
* @param date - The time of `obsPos` (defaults to now)
|
|
10950
11122
|
* @returns Angular diameter in radians
|
|
10951
11123
|
*/
|
|
10952
|
-
diameter(obsPos) {
|
|
10953
|
-
const sunPos = this.eci();
|
|
11124
|
+
diameter(obsPos, date = /* @__PURE__ */ new Date()) {
|
|
11125
|
+
const sunPos = this.eci(date);
|
|
10954
11126
|
const distance = obsPos.subtract(sunPos).magnitude();
|
|
10955
11127
|
return 2 * Math.asin(_SunBody.RADIUS / distance);
|
|
10956
11128
|
}
|
|
@@ -10995,7 +11167,7 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
10995
11167
|
if (isNaN(lon)) {
|
|
10996
11168
|
throw new Error("longitude missing");
|
|
10997
11169
|
}
|
|
10998
|
-
const date = dateVal instanceof Date ? dateVal :
|
|
11170
|
+
const date = new Date(dateVal instanceof Date ? dateVal.getTime() : dateVal);
|
|
10999
11171
|
if (isUtc) {
|
|
11000
11172
|
date.setUTCHours(12, 0, 0, 0);
|
|
11001
11173
|
} else {
|
|
@@ -11019,15 +11191,19 @@ var SunBody = class _SunBody extends CelestialBody {
|
|
|
11019
11191
|
}
|
|
11020
11192
|
/**
|
|
11021
11193
|
* Gets sunrise and sunset times using astronomy-engine.
|
|
11194
|
+
*
|
|
11195
|
+
* The times are when the Sun's centre crosses `minElevation` (geometric altitude). The
|
|
11196
|
+
* default -0.833 deg is the standard sunrise/sunset definition (refraction plus the solar
|
|
11197
|
+
* semidiameter); -6, -12 and -18 give civil, nautical and astronomical twilight.
|
|
11022
11198
|
* @param observer - Ground observer location
|
|
11023
11199
|
* @param date - The date for calculation
|
|
11024
|
-
* @param minElevation -
|
|
11200
|
+
* @param minElevation - Elevation of the Sun's centre in degrees (default -0.833 for standard sunrise)
|
|
11025
11201
|
*/
|
|
11026
11202
|
getSunriseSunset(observer, date = /* @__PURE__ */ new Date(), minElevation = -0.833) {
|
|
11027
11203
|
const obs = new Observer(observer.lat, observer.lon, observer.alt * 1e3);
|
|
11028
11204
|
const time = MakeTime(date);
|
|
11029
|
-
const sunrise =
|
|
11030
|
-
const sunset =
|
|
11205
|
+
const sunrise = SearchAltitude(Body.Sun, obs, 1, time, 1, minElevation);
|
|
11206
|
+
const sunset = SearchAltitude(Body.Sun, obs, -1, time, 1, minElevation);
|
|
11031
11207
|
return {
|
|
11032
11208
|
sunrise: sunrise?.date ?? null,
|
|
11033
11209
|
sunset: sunset?.date ?? null
|
|
@@ -11306,6 +11482,9 @@ var SolarRadiationPressure = class _SolarRadiationPressure extends Force {
|
|
|
11306
11482
|
this.area = area;
|
|
11307
11483
|
this.reflectCoeff = reflectCoeff;
|
|
11308
11484
|
}
|
|
11485
|
+
mass;
|
|
11486
|
+
area;
|
|
11487
|
+
reflectCoeff;
|
|
11309
11488
|
// / Solar pressure _(N/m²)_;
|
|
11310
11489
|
static _kRef = 456e-8 * KM_PER_AU ** 2;
|
|
11311
11490
|
acceleration(state) {
|
|
@@ -11540,7 +11719,7 @@ var MoonBody = class _MoonBody extends CelestialBody {
|
|
|
11540
11719
|
if (!phase) {
|
|
11541
11720
|
phase = _MoonBody.moonCycles_[0];
|
|
11542
11721
|
}
|
|
11543
|
-
let type
|
|
11722
|
+
let type;
|
|
11544
11723
|
if (next === nextNewMoon) {
|
|
11545
11724
|
type = "newMoon";
|
|
11546
11725
|
} else if (next === nextFirstQuarter) {
|
|
@@ -11688,6 +11867,8 @@ var ThirdBodyGravity = class _ThirdBodyGravity {
|
|
|
11688
11867
|
this.moon = moon;
|
|
11689
11868
|
this.sun = sun;
|
|
11690
11869
|
}
|
|
11870
|
+
moon;
|
|
11871
|
+
sun;
|
|
11691
11872
|
static moonGravity_(state) {
|
|
11692
11873
|
const rMoon = Moon.eci(state.epoch.toDateTime());
|
|
11693
11874
|
const aNum = rMoon.subtract(state.position);
|
|
@@ -12094,29 +12275,40 @@ var FormatTle = class _FormatTle {
|
|
|
12094
12275
|
* Format a value in TLE exponential notation for BSTAR or mean motion ddot.
|
|
12095
12276
|
* Format: "sNNNNN±N" (8 chars) where mantissa has implied leading decimal point.
|
|
12096
12277
|
* Example: 0.00017507 → " 17507-3" (i.e., .17507 × 10^-3)
|
|
12278
|
+
*
|
|
12279
|
+
* The result is ALWAYS exactly 8 characters. The field has room for a 5-digit
|
|
12280
|
+
* mantissa and a single-digit exponent, so values needing more are folded into
|
|
12281
|
+
* what the format can express: magnitudes below 1e-10 become zero (the usual
|
|
12282
|
+
* convention for a drag term too small to write), and the 1e9-and-up case is
|
|
12283
|
+
* saturated. Anything longer would push every later column of TLE line 1 to the
|
|
12284
|
+
* right, which silently corrupts ephemeris type, element number and checksum.
|
|
12097
12285
|
* @param value - The value to format
|
|
12098
12286
|
* @returns Formatted 8-character string
|
|
12099
12287
|
*/
|
|
12100
12288
|
static formatTleExponential(value) {
|
|
12101
|
-
if (value === 0) {
|
|
12289
|
+
if (value === 0 || !Number.isFinite(value)) {
|
|
12102
12290
|
return " 00000+0";
|
|
12103
12291
|
}
|
|
12104
12292
|
const sign2 = value >= 0 ? " " : "-";
|
|
12105
12293
|
const absVal = Math.abs(value);
|
|
12106
12294
|
let exponent = Math.floor(Math.log10(absVal)) + 1;
|
|
12107
|
-
let mantissa = absVal / 10 ** exponent;
|
|
12108
|
-
|
|
12109
|
-
|
|
12295
|
+
let mantissa = Math.round(absVal / 10 ** exponent * 1e5);
|
|
12296
|
+
while (mantissa < 1e4) {
|
|
12297
|
+
exponent--;
|
|
12298
|
+
mantissa = Math.round(absVal / 10 ** exponent * 1e5);
|
|
12299
|
+
}
|
|
12300
|
+
while (mantissa >= 1e5) {
|
|
12110
12301
|
exponent++;
|
|
12302
|
+
mantissa = Math.round(absVal / 10 ** exponent * 1e5);
|
|
12111
12303
|
}
|
|
12112
|
-
if (
|
|
12113
|
-
|
|
12114
|
-
|
|
12304
|
+
if (exponent < -9) {
|
|
12305
|
+
return " 00000+0";
|
|
12306
|
+
}
|
|
12307
|
+
if (exponent > 9) {
|
|
12308
|
+
return `${sign2}99999+9`;
|
|
12115
12309
|
}
|
|
12116
|
-
const mantissaStr = Math.round(mantissa * 1e5).toString().padStart(5, "0");
|
|
12117
12310
|
const expSign = exponent >= 0 ? "+" : "-";
|
|
12118
|
-
|
|
12119
|
-
return sign2 + mantissaStr + expSign + expStr;
|
|
12311
|
+
return sign2 + mantissa.toString() + expSign + Math.abs(exponent).toString();
|
|
12120
12312
|
}
|
|
12121
12313
|
/**
|
|
12122
12314
|
* Compute TLE line checksum (modulo 10 sum of digits, '-' counts as 1).
|
|
@@ -12293,20 +12485,8 @@ ${this.line2}`;
|
|
|
12293
12485
|
}
|
|
12294
12486
|
static calcElsetAge(tle1, nowInput, outputUnits = "days") {
|
|
12295
12487
|
nowInput ??= /* @__PURE__ */ new Date();
|
|
12296
|
-
const
|
|
12297
|
-
const
|
|
12298
|
-
const epochYearShort = parseInt(tle1.substring(18, 20), 10);
|
|
12299
|
-
const epochDayOfYear = parseFloat(tle1.substring(20, 32));
|
|
12300
|
-
let epochYearFull;
|
|
12301
|
-
if (epochYearShort <= currentYearShort) {
|
|
12302
|
-
epochYearFull = 2e3 + epochYearShort;
|
|
12303
|
-
} else {
|
|
12304
|
-
epochYearFull = 1900 + epochYearShort;
|
|
12305
|
-
}
|
|
12306
|
-
const epochJday = epochDayOfYear + epochYearFull * 365;
|
|
12307
|
-
const currentJday = getDayOfYear(nowInput) + currentYearFull * 365;
|
|
12308
|
-
const currentTime = (nowInput.getUTCHours() * 3600 + nowInput.getUTCMinutes() * 60 + nowInput.getUTCSeconds()) / 86400;
|
|
12309
|
-
const daysOld = currentJday + currentTime - epochJday;
|
|
12488
|
+
const epochMs = _Tle.parseEpoch_(tle1.substring(18, 32)).toDateTime().getTime();
|
|
12489
|
+
const daysOld = (nowInput.getTime() - epochMs) / 864e5;
|
|
12310
12490
|
switch (outputUnits) {
|
|
12311
12491
|
case "hours":
|
|
12312
12492
|
return daysOld * 24;
|
|
@@ -12989,6 +13169,8 @@ var RkCheckpoint = class {
|
|
|
12989
13169
|
this.cacheState = cacheState;
|
|
12990
13170
|
this.stepSize = stepSize;
|
|
12991
13171
|
}
|
|
13172
|
+
cacheState;
|
|
13173
|
+
stepSize;
|
|
12992
13174
|
};
|
|
12993
13175
|
|
|
12994
13176
|
// src/propagator/RkResult.ts
|
|
@@ -12999,6 +13181,9 @@ var RkResult = class {
|
|
|
12999
13181
|
this.error = error;
|
|
13000
13182
|
this.newStep = newStep;
|
|
13001
13183
|
}
|
|
13184
|
+
state;
|
|
13185
|
+
error;
|
|
13186
|
+
newStep;
|
|
13002
13187
|
};
|
|
13003
13188
|
|
|
13004
13189
|
// src/interpolator/CubicSpline.ts
|
|
@@ -13012,6 +13197,12 @@ var CubicSpline = class {
|
|
|
13012
13197
|
this.p1 = p1;
|
|
13013
13198
|
this.m1 = m1;
|
|
13014
13199
|
}
|
|
13200
|
+
t0;
|
|
13201
|
+
p0;
|
|
13202
|
+
m0;
|
|
13203
|
+
t1;
|
|
13204
|
+
p1;
|
|
13205
|
+
m1;
|
|
13015
13206
|
// / Interpolate position at the provided time [t] _(POSIX seconds)_.
|
|
13016
13207
|
position_(t) {
|
|
13017
13208
|
const t2 = t * t;
|
|
@@ -13050,9 +13241,8 @@ var Interpolator = class {
|
|
|
13050
13241
|
* cached value range.
|
|
13051
13242
|
*/
|
|
13052
13243
|
inWindow(epoch) {
|
|
13053
|
-
const
|
|
13054
|
-
|
|
13055
|
-
return start <= epoch && epoch <= stop;
|
|
13244
|
+
const window = this.window();
|
|
13245
|
+
return window.start.posix <= epoch.posix && epoch.posix <= window.end.posix;
|
|
13056
13246
|
}
|
|
13057
13247
|
/*
|
|
13058
13248
|
* Calculate the start/stop epoch between this and another [Interpolator].
|
|
@@ -13060,13 +13250,15 @@ var Interpolator = class {
|
|
|
13060
13250
|
* Returns `null` if there is no overlap between interpolators.
|
|
13061
13251
|
*/
|
|
13062
13252
|
overlap(interpolator) {
|
|
13063
|
-
const
|
|
13064
|
-
const
|
|
13065
|
-
const
|
|
13066
|
-
const
|
|
13253
|
+
const a = this.window();
|
|
13254
|
+
const b = interpolator.window();
|
|
13255
|
+
const x1 = a.start.posix;
|
|
13256
|
+
const x2 = a.end.posix;
|
|
13257
|
+
const y1 = b.start.posix;
|
|
13258
|
+
const y2 = b.end.posix;
|
|
13067
13259
|
if (x1 <= y2 && y1 <= x2) {
|
|
13068
|
-
const e1 = new EpochUTC(Math.max(x1
|
|
13069
|
-
const e2 = new EpochUTC(Math.min(x2
|
|
13260
|
+
const e1 = new EpochUTC(Math.max(x1, y1));
|
|
13261
|
+
const e2 = new EpochUTC(Math.min(x2, y2));
|
|
13070
13262
|
return new EpochWindow(e1, e2);
|
|
13071
13263
|
}
|
|
13072
13264
|
return null;
|
|
@@ -13087,6 +13279,7 @@ var CubicSplineInterpolator = class _CubicSplineInterpolator extends StateInterp
|
|
|
13087
13279
|
super();
|
|
13088
13280
|
this.splines_ = splines_;
|
|
13089
13281
|
}
|
|
13282
|
+
splines_;
|
|
13090
13283
|
static fromEphemeris(ephemeris) {
|
|
13091
13284
|
const splines = [];
|
|
13092
13285
|
for (let i = 0; i < ephemeris.length - 1; i++) {
|
|
@@ -13286,6 +13479,7 @@ var VerletBlendInterpolator = class _VerletBlendInterpolator extends StateInterp
|
|
|
13286
13479
|
super();
|
|
13287
13480
|
this.ephemeris = ephemeris;
|
|
13288
13481
|
}
|
|
13482
|
+
ephemeris;
|
|
13289
13483
|
get sizeBytes() {
|
|
13290
13484
|
return 64 * 7 * this.ephemeris.length / 8;
|
|
13291
13485
|
}
|
|
@@ -13481,7 +13675,7 @@ var Propagator = class {
|
|
|
13481
13675
|
ephemeris(start, stop, interval = 60) {
|
|
13482
13676
|
const output = [this.propagate(start)];
|
|
13483
13677
|
let tempEpoch = start;
|
|
13484
|
-
while (tempEpoch <= stop) {
|
|
13678
|
+
while (tempEpoch.posix <= stop.posix) {
|
|
13485
13679
|
tempEpoch = tempEpoch.roll(interval);
|
|
13486
13680
|
output.push(this.propagate(tempEpoch));
|
|
13487
13681
|
}
|
|
@@ -13655,6 +13849,9 @@ var RungeKuttaAdaptive = class _RungeKuttaAdaptive extends Propagator {
|
|
|
13655
13849
|
this._cacheState = this.initState_;
|
|
13656
13850
|
this.tolerance_ = Math.max(_RungeKuttaAdaptive._minTolerance, Math.abs(tolerance_));
|
|
13657
13851
|
}
|
|
13852
|
+
initState_;
|
|
13853
|
+
forceModel_;
|
|
13854
|
+
tolerance_;
|
|
13658
13855
|
// / Initial state vector.
|
|
13659
13856
|
_cacheState;
|
|
13660
13857
|
_checkpoints = [];
|
|
@@ -13774,7 +13971,7 @@ var RungeKuttaAdaptive = class _RungeKuttaAdaptive extends Propagator {
|
|
|
13774
13971
|
let tState = this.propagate(maneuver.start);
|
|
13775
13972
|
this.forceModel_.loadManeuver(maneuver);
|
|
13776
13973
|
const ephemeris = [tState];
|
|
13777
|
-
while (tState.epoch < maneuver.stop) {
|
|
13974
|
+
while (tState.epoch.posix < maneuver.stop.posix) {
|
|
13778
13975
|
const step = Math.min(maneuver.stop.difference(tState.epoch), interval);
|
|
13779
13976
|
tState = this.propagate(tState.epoch.roll(step));
|
|
13780
13977
|
ephemeris.push(tState);
|
|
@@ -13783,13 +13980,13 @@ var RungeKuttaAdaptive = class _RungeKuttaAdaptive extends Propagator {
|
|
|
13783
13980
|
return ephemeris;
|
|
13784
13981
|
}
|
|
13785
13982
|
ephemerisManeuver(start, finish, maneuvers, interval = 60) {
|
|
13786
|
-
const tMvr = maneuvers.filter((mvr) => mvr.start >= start || mvr.stop <= finish);
|
|
13983
|
+
const tMvr = maneuvers.filter((mvr) => mvr.start.posix >= start.posix || mvr.stop.posix <= finish.posix);
|
|
13787
13984
|
const ephemeris = [];
|
|
13788
|
-
if (tMvr[0].start > start) {
|
|
13985
|
+
if (tMvr[0].start.posix > start.posix) {
|
|
13789
13986
|
ephemeris.push(this.propagate(start));
|
|
13790
13987
|
}
|
|
13791
13988
|
for (const mvr of tMvr) {
|
|
13792
|
-
while (this._cacheState.epoch < mvr.start) {
|
|
13989
|
+
while (this._cacheState.epoch.posix < mvr.start.posix) {
|
|
13793
13990
|
const step = Math.min(mvr.start.difference(this._cacheState.epoch), interval);
|
|
13794
13991
|
this.propagate(this._cacheState.epoch.roll(step));
|
|
13795
13992
|
if (this._cacheState.epoch.posix !== mvr.start.posix) {
|
|
@@ -14388,6 +14585,7 @@ var AccessWindow = class _AccessWindow {
|
|
|
14388
14585
|
};
|
|
14389
14586
|
|
|
14390
14587
|
// src/objects/AccessCalculator.ts
|
|
14588
|
+
var CIVIL_TWILIGHT_DEG = -6;
|
|
14391
14589
|
var AccessCalculator = class _AccessCalculator {
|
|
14392
14590
|
/** Default calculation time step in milliseconds (10 seconds) */
|
|
14393
14591
|
static DEFAULT_STEP_MS_ = 1e4;
|
|
@@ -14528,31 +14726,20 @@ var AccessCalculator = class _AccessCalculator {
|
|
|
14528
14726
|
return false;
|
|
14529
14727
|
}
|
|
14530
14728
|
if (constraints.requireSunlit) {
|
|
14531
|
-
|
|
14532
|
-
|
|
14729
|
+
let satPos;
|
|
14730
|
+
try {
|
|
14731
|
+
satPos = target.toJ2000(date).position;
|
|
14732
|
+
} catch {
|
|
14533
14733
|
return false;
|
|
14534
14734
|
}
|
|
14535
14735
|
const epoch = EpochUTC.fromDateTime(date);
|
|
14536
|
-
const satPos = new Vector3D(
|
|
14537
|
-
eci.position.x,
|
|
14538
|
-
eci.position.y,
|
|
14539
|
-
eci.position.z
|
|
14540
|
-
);
|
|
14541
14736
|
if (Sun.shadow(epoch, satPos)) {
|
|
14542
14737
|
return false;
|
|
14543
14738
|
}
|
|
14544
14739
|
}
|
|
14545
14740
|
if (constraints.requireObserverDark) {
|
|
14546
|
-
|
|
14547
|
-
|
|
14548
|
-
const sunTimes = Sun.getTimes(date, lla.lat, lla.lon, altMeters);
|
|
14549
|
-
const currentTime = date.getTime();
|
|
14550
|
-
const civilDusk = sunTimes.civilDusk?.getTime();
|
|
14551
|
-
const civilDawn = sunTimes.civilDawn?.getTime();
|
|
14552
|
-
if (civilDusk && civilDawn) {
|
|
14553
|
-
if (currentTime >= civilDawn && currentTime <= civilDusk) {
|
|
14554
|
-
return false;
|
|
14555
|
-
}
|
|
14741
|
+
if (Sun.getAzEl(observer, date, false).el >= CIVIL_TWILIGHT_DEG) {
|
|
14742
|
+
return false;
|
|
14556
14743
|
}
|
|
14557
14744
|
}
|
|
14558
14745
|
return true;
|
|
@@ -14732,38 +14919,35 @@ var DynamicGroundObject = class _DynamicGroundObject extends GroundObject {
|
|
|
14732
14919
|
}
|
|
14733
14920
|
/**
|
|
14734
14921
|
* Gets the ECI (Earth-Centered Inertial) position at a specific time.
|
|
14922
|
+
*
|
|
14923
|
+
* The WGS84 position from {@link getEcef} rotated into TEME by GMST, the same model
|
|
14924
|
+
* `GroundObject.eci()` uses (not the spherical-Earth `lla2eci()`).
|
|
14735
14925
|
* @param time - The time to get position for
|
|
14736
|
-
* @returns ECI position vector, or null if time is outside waypoint range
|
|
14926
|
+
* @returns ECI (TEME) position vector, or null if time is outside waypoint range
|
|
14737
14927
|
*/
|
|
14738
14928
|
getEci(time) {
|
|
14739
|
-
const
|
|
14740
|
-
if (!
|
|
14929
|
+
const ecef = this.getEcef(time);
|
|
14930
|
+
if (!ecef) {
|
|
14741
14931
|
return null;
|
|
14742
14932
|
}
|
|
14743
|
-
const llaRad = {
|
|
14744
|
-
lat: lla.lat * DEG2RAD,
|
|
14745
|
-
lon: lla.lon * DEG2RAD,
|
|
14746
|
-
alt: lla.alt
|
|
14747
|
-
};
|
|
14748
14933
|
const { gmst } = calcGmst(time);
|
|
14749
|
-
return
|
|
14934
|
+
return ecef2eci(ecef, gmst);
|
|
14750
14935
|
}
|
|
14751
14936
|
/**
|
|
14752
14937
|
* Converts position at a specific time to J2000 inertial coordinates.
|
|
14753
|
-
*
|
|
14938
|
+
*
|
|
14939
|
+
* The WGS84 position is rotated from ITRF into J2000 with precession, nutation and
|
|
14940
|
+
* sidereal time, as in `GroundObject.toJ2000()`. The velocity is the Earth-rotation
|
|
14941
|
+
* velocity (omega x r); the platform's own motion between waypoints is not included.
|
|
14754
14942
|
* @param time - The time for the conversion
|
|
14755
14943
|
* @returns J2000 state vector, or null if time is outside waypoint range
|
|
14756
14944
|
*/
|
|
14757
14945
|
getJ2000(time) {
|
|
14758
|
-
const
|
|
14759
|
-
if (!
|
|
14946
|
+
const geodetic = this.getGeodetic(time);
|
|
14947
|
+
if (!geodetic) {
|
|
14760
14948
|
return null;
|
|
14761
14949
|
}
|
|
14762
|
-
return
|
|
14763
|
-
EpochUTC.fromDateTime(time),
|
|
14764
|
-
new Vector3D(eci.x, eci.y, eci.z),
|
|
14765
|
-
new Vector3D(0, 0, 0)
|
|
14766
|
-
);
|
|
14950
|
+
return geodetic.toITRF(EpochUTC.fromDateTime(time)).toJ2000();
|
|
14767
14951
|
}
|
|
14768
14952
|
/**
|
|
14769
14953
|
* Converts position at a specific time to Geodetic coordinates.
|
|
@@ -16123,10 +16307,17 @@ var RAE = class _RAE {
|
|
|
16123
16307
|
this.azRateRad = azRateRad;
|
|
16124
16308
|
this.elRateRad = elRateRad;
|
|
16125
16309
|
}
|
|
16310
|
+
epoch;
|
|
16311
|
+
rng;
|
|
16312
|
+
azRad;
|
|
16313
|
+
elRad;
|
|
16314
|
+
rngRate;
|
|
16315
|
+
azRateRad;
|
|
16316
|
+
elRateRad;
|
|
16126
16317
|
// / Create a new [Razel] object, using degrees for the angular values.
|
|
16127
16318
|
static fromDegrees(epoch, range, azimuth, elevation, rangeRate, azimuthRate, elevationRate) {
|
|
16128
|
-
const azimuthRateRad = azimuthRate ? azimuthRate * DEG2RAD : void 0;
|
|
16129
|
-
const elevationRateRad = elevationRate ? elevationRate * DEG2RAD : void 0;
|
|
16319
|
+
const azimuthRateRad = typeof azimuthRate === "number" ? azimuthRate * DEG2RAD : void 0;
|
|
16320
|
+
const elevationRateRad = typeof elevationRate === "number" ? elevationRate * DEG2RAD : void 0;
|
|
16130
16321
|
return new _RAE(
|
|
16131
16322
|
epoch,
|
|
16132
16323
|
range,
|
|
@@ -16199,14 +16390,14 @@ var RAE = class _RAE {
|
|
|
16199
16390
|
* @returns The azimuth rate in degrees per second, or undefined if it is not available.
|
|
16200
16391
|
*/
|
|
16201
16392
|
get azRate() {
|
|
16202
|
-
return this.azRateRad ? this.azRateRad * RAD2DEG : void 0;
|
|
16393
|
+
return typeof this.azRateRad === "number" ? this.azRateRad * RAD2DEG : void 0;
|
|
16203
16394
|
}
|
|
16204
16395
|
/**
|
|
16205
16396
|
* Gets the elevation rate in degrees per second.
|
|
16206
16397
|
* @returns The elevation rate in degrees per second, or undefined if the elevation rate is not set.
|
|
16207
16398
|
*/
|
|
16208
16399
|
get elRate() {
|
|
16209
|
-
return this.elRateRad ? this.elRateRad * RAD2DEG : void 0;
|
|
16400
|
+
return typeof this.elRateRad === "number" ? this.elRateRad * RAD2DEG : void 0;
|
|
16210
16401
|
}
|
|
16211
16402
|
toString() {
|
|
16212
16403
|
return [
|
|
@@ -16378,13 +16569,13 @@ var KeplerPropagator = class extends Propagator {
|
|
|
16378
16569
|
return [this.cacheState_];
|
|
16379
16570
|
}
|
|
16380
16571
|
ephemerisManeuver(start, finish, maneuvers, interval = 60) {
|
|
16381
|
-
const tMvr = maneuvers.slice(0).filter((mvr) => mvr.center >= start
|
|
16572
|
+
const tMvr = maneuvers.slice(0).filter((mvr) => mvr.center.posix >= start.posix && mvr.center.posix <= finish.posix);
|
|
16382
16573
|
const ephemeris = [];
|
|
16383
|
-
if (tMvr[0].start > start) {
|
|
16574
|
+
if (tMvr.length === 0 || tMvr[0].start.posix > start.posix) {
|
|
16384
16575
|
ephemeris.push(this.propagate(start));
|
|
16385
16576
|
}
|
|
16386
16577
|
for (const mvr of tMvr) {
|
|
16387
|
-
while (this.cacheState_.epoch < mvr.center) {
|
|
16578
|
+
while (this.cacheState_.epoch.posix < mvr.center.posix) {
|
|
16388
16579
|
const step = Math.min(mvr.center.difference(this.cacheState_.epoch), interval);
|
|
16389
16580
|
this.propagate(this.cacheState_.epoch.roll(step));
|
|
16390
16581
|
if (this.cacheState_.epoch.posix !== mvr.center.posix) {
|
|
@@ -16393,7 +16584,7 @@ var KeplerPropagator = class extends Propagator {
|
|
|
16393
16584
|
}
|
|
16394
16585
|
ephemeris.push(...this.maneuver(mvr));
|
|
16395
16586
|
}
|
|
16396
|
-
while (this.cacheState_.epoch < finish) {
|
|
16587
|
+
while (this.cacheState_.epoch.posix < finish.posix) {
|
|
16397
16588
|
const step = Math.min(finish.difference(this.cacheState_.epoch), interval);
|
|
16398
16589
|
this.propagate(this.cacheState_.epoch.roll(step));
|
|
16399
16590
|
ephemeris.push(this.cacheState_);
|
|
@@ -16409,6 +16600,7 @@ var KeplerPropagator = class extends Propagator {
|
|
|
16409
16600
|
}
|
|
16410
16601
|
restore(index) {
|
|
16411
16602
|
this.cacheState_ = this.checkpoints_[index];
|
|
16603
|
+
this.elements_ = this.cacheState_.toClassicalElements();
|
|
16412
16604
|
}
|
|
16413
16605
|
};
|
|
16414
16606
|
|
|
@@ -16432,6 +16624,11 @@ var RungeKutta4Propagator = class extends Propagator {
|
|
|
16432
16624
|
this.checkpoints_ = checkpoints_;
|
|
16433
16625
|
this.stepSize_ = Math.abs(stepSize_);
|
|
16434
16626
|
}
|
|
16627
|
+
initState_;
|
|
16628
|
+
forceModel_;
|
|
16629
|
+
stepSize_;
|
|
16630
|
+
cacheState_;
|
|
16631
|
+
checkpoints_;
|
|
16435
16632
|
// / Set the integrator step size to the provided number of [seconds].
|
|
16436
16633
|
setStepSize(seconds) {
|
|
16437
16634
|
this.stepSize_ = Math.abs(seconds);
|
|
@@ -16441,13 +16638,13 @@ var RungeKutta4Propagator = class extends Propagator {
|
|
|
16441
16638
|
this.forceModel_ = forceModel;
|
|
16442
16639
|
}
|
|
16443
16640
|
ephemerisManeuver(start, finish, maneuvers, interval = 60) {
|
|
16444
|
-
const tMvr = maneuvers.slice(0).filter((mvr) => mvr.start <= finish && mvr.stop >= start);
|
|
16641
|
+
const tMvr = maneuvers.slice(0).filter((mvr) => mvr.start.posix <= finish.posix && mvr.stop.posix >= start.posix);
|
|
16445
16642
|
const ephemeris = [];
|
|
16446
|
-
if (tMvr.length === 0 || tMvr[0].start > start) {
|
|
16643
|
+
if (tMvr.length === 0 || tMvr[0].start.posix > start.posix) {
|
|
16447
16644
|
ephemeris.push(this.propagate(start));
|
|
16448
16645
|
}
|
|
16449
16646
|
for (const mvr of tMvr) {
|
|
16450
|
-
while (this.cacheState_.epoch < mvr.start) {
|
|
16647
|
+
while (this.cacheState_.epoch.posix < mvr.start.posix) {
|
|
16451
16648
|
const step = Math.min(mvr.start.difference(this.cacheState_.epoch), interval);
|
|
16452
16649
|
this.propagate(this.cacheState_.epoch.roll(step));
|
|
16453
16650
|
if (this.cacheState_.epoch.posix !== mvr.start.posix) {
|
|
@@ -16471,7 +16668,7 @@ var RungeKutta4Propagator = class extends Propagator {
|
|
|
16471
16668
|
let tState = this.propagate(maneuver.start);
|
|
16472
16669
|
this.forceModel_.loadManeuver(maneuver);
|
|
16473
16670
|
const ephemeris = [tState];
|
|
16474
|
-
while (tState.epoch < maneuver.stop) {
|
|
16671
|
+
while (tState.epoch.posix < maneuver.stop.posix) {
|
|
16475
16672
|
const step = Math.min(maneuver.stop.difference(tState.epoch), interval);
|
|
16476
16673
|
tState = this.propagate(tState.epoch.roll(step));
|
|
16477
16674
|
ephemeris.push(tState);
|
|
@@ -16543,6 +16740,7 @@ var Sgp4Propagator = class extends Propagator {
|
|
|
16543
16740
|
this.tle_ = tle_;
|
|
16544
16741
|
this.cacheState_ = tle_.state.toJ2000();
|
|
16545
16742
|
}
|
|
16743
|
+
tle_;
|
|
16546
16744
|
cacheState_;
|
|
16547
16745
|
checkpoints_ = [];
|
|
16548
16746
|
/**
|
|
@@ -17026,8 +17224,13 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
17026
17224
|
return rae.az;
|
|
17027
17225
|
}
|
|
17028
17226
|
/**
|
|
17029
|
-
* Calculates the RAE (Range, Azimuth, Elevation) values for a given sensor
|
|
17030
|
-
* current time is used.
|
|
17227
|
+
* Calculates the RAE (Range, Azimuth, Elevation) values and their rates for a given sensor
|
|
17228
|
+
* and date. If no date is provided, the current time is used.
|
|
17229
|
+
*
|
|
17230
|
+
* The rates are central differences of `rae()` over +/-0.5 s, stored in the units the
|
|
17231
|
+
* {@link RAE} class expects: km/s for range rate and rad/s for the angle rates (read them
|
|
17232
|
+
* in deg/s with `azRate` / `elRate`). The azimuth change is wrapped so a pass through
|
|
17233
|
+
* north does not produce a 360 deg jump.
|
|
17031
17234
|
* @variation expanded
|
|
17032
17235
|
* @param observer - The observer's position on the ground.
|
|
17033
17236
|
* @param date - The date at which to calculate the RAE values. Optional, defaults to the current date.
|
|
@@ -17038,14 +17241,18 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
17038
17241
|
if (!rae) {
|
|
17039
17242
|
return null;
|
|
17040
17243
|
}
|
|
17041
|
-
const
|
|
17042
|
-
|
|
17244
|
+
const halfStepMs = 500;
|
|
17245
|
+
const raeBefore = this.rae(observer, new Date(date.getTime() - halfStepMs));
|
|
17246
|
+
const raeAfter = this.rae(observer, new Date(date.getTime() + halfStepMs));
|
|
17247
|
+
if (!raeBefore || !raeAfter) {
|
|
17043
17248
|
return null;
|
|
17044
17249
|
}
|
|
17250
|
+
const dt = 2 * halfStepMs / 1e3;
|
|
17251
|
+
const rangeRate = (raeAfter.rng - raeBefore.rng) / dt;
|
|
17252
|
+
const dAz = ((raeAfter.az - raeBefore.az + 180) % 360 + 360) % 360 - 180;
|
|
17253
|
+
const azimuthRate = dAz / dt * DEG2RAD;
|
|
17254
|
+
const elevationRate = (raeAfter.el - raeBefore.el) / dt * DEG2RAD;
|
|
17045
17255
|
const epoch = new EpochUTC(date.getTime() / 1e3);
|
|
17046
|
-
const rangeRate = rae2.rng - rae.rng;
|
|
17047
|
-
const azimuthRate = rae2.az - rae.az;
|
|
17048
|
-
const elevationRate = rae2.el - rae.el;
|
|
17049
17256
|
return new RAE(
|
|
17050
17257
|
epoch,
|
|
17051
17258
|
rae.rng,
|
|
@@ -17709,11 +17916,12 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
17709
17916
|
/**
|
|
17710
17917
|
* Calculates the angle between this satellite, another satellite, and the Sun.
|
|
17711
17918
|
*
|
|
17712
|
-
* Returns the angle at
|
|
17713
|
-
*
|
|
17919
|
+
* Returns the angle at the OTHER satellite between its vector to the Sun and its vector
|
|
17920
|
+
* to this satellite: the solar phase angle of `other` as seen from this satellite
|
|
17921
|
+
* (0 = fully lit face toward this satellite, pi = backlit).
|
|
17714
17922
|
*
|
|
17715
17923
|
* @param other - The other satellite.
|
|
17716
|
-
* @param sunPosition - The Sun's
|
|
17924
|
+
* @param sunPosition - The Sun's position in TEME (the frame of `eci()`), km.
|
|
17717
17925
|
* @param date - The date for the calculation.
|
|
17718
17926
|
* @returns The angle in radians.
|
|
17719
17927
|
* @throws Error if positions are undefined.
|
|
@@ -17756,11 +17964,12 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
17756
17964
|
* ```
|
|
17757
17965
|
*/
|
|
17758
17966
|
getSunStatus(date = /* @__PURE__ */ new Date()) {
|
|
17759
|
-
|
|
17760
|
-
|
|
17967
|
+
let satPos;
|
|
17968
|
+
try {
|
|
17969
|
+
satPos = this.toJ2000(date).position;
|
|
17970
|
+
} catch {
|
|
17761
17971
|
return -1 /* UNKNOWN */;
|
|
17762
17972
|
}
|
|
17763
|
-
const satPos = new Vector3D(pv.position.x, pv.position.y, pv.position.z);
|
|
17764
17973
|
const sunPos = Sun.eci(date);
|
|
17765
17974
|
const ratio = Sun.lightingRatio(satPos, sunPos);
|
|
17766
17975
|
if (ratio === 0) {
|
|
@@ -18228,6 +18437,43 @@ var FieldOfView = class _FieldOfView {
|
|
|
18228
18437
|
const targetDir = direction.normalize();
|
|
18229
18438
|
return this.isDirectionWithinCone(targetDir);
|
|
18230
18439
|
}
|
|
18440
|
+
/**
|
|
18441
|
+
* Samples a unit direction on or inside the FOV cone.
|
|
18442
|
+
*
|
|
18443
|
+
* The cone cross-section is parameterized like a standard 2D ellipse in
|
|
18444
|
+
* angular (gnomonic) space: x = halfAngle * cos(t), y = minorHalfAngle * sin(t),
|
|
18445
|
+
* where x is the off-boresight angle along the major axis (u) and y along the
|
|
18446
|
+
* minor axis (v). Uniform steps in t produce well-distributed boundary points
|
|
18447
|
+
* even for highly eccentric fans (e.g., an 80° x 1° fence radar), unlike
|
|
18448
|
+
* uniform steps in the geometric position angle which clump at the fan tips.
|
|
18449
|
+
*
|
|
18450
|
+
* Points sampled with radialFraction < 1 always satisfy the elliptical cone
|
|
18451
|
+
* containment check; radialFraction = 1 lies exactly on the boundary.
|
|
18452
|
+
*
|
|
18453
|
+
* @param t - Parametric ellipse angle in radians. t=0 points along the major
|
|
18454
|
+
* axis (+u), t=PI/2 along the minor axis (+v). Note this is NOT the
|
|
18455
|
+
* geometric position angle around the boresight.
|
|
18456
|
+
* @param radialFraction - 0 = boresight, 1 = cone boundary (default: 1)
|
|
18457
|
+
* @returns Unit direction vector in the FOV's reference frame (ENU for topocentric)
|
|
18458
|
+
*/
|
|
18459
|
+
directionAt(t, radialFraction = 1) {
|
|
18460
|
+
const x = radialFraction * this.halfAngleRad_ * Math.cos(t);
|
|
18461
|
+
const y = radialFraction * this.minorHalfAngleRad_ * Math.sin(t);
|
|
18462
|
+
const theta = Math.hypot(x, y);
|
|
18463
|
+
const { b, u, v } = this.boresightFrame_;
|
|
18464
|
+
if (theta < EPS) {
|
|
18465
|
+
return b;
|
|
18466
|
+
}
|
|
18467
|
+
const cosTheta = Math.cos(theta);
|
|
18468
|
+
const sinTheta = Math.sin(theta);
|
|
18469
|
+
const rx = x / theta;
|
|
18470
|
+
const ry = y / theta;
|
|
18471
|
+
return new Vector3D(
|
|
18472
|
+
cosTheta * b.x + sinTheta * (rx * u.x + ry * v.x),
|
|
18473
|
+
cosTheta * b.y + sinTheta * (rx * u.y + ry * v.y),
|
|
18474
|
+
cosTheta * b.z + sinTheta * (rx * u.z + ry * v.z)
|
|
18475
|
+
);
|
|
18476
|
+
}
|
|
18231
18477
|
/**
|
|
18232
18478
|
* Gets the effective minimum elevation at a given azimuth.
|
|
18233
18479
|
* Considers all applicable elevation masks and returns the most restrictive.
|
|
@@ -18637,13 +18883,13 @@ var VisualizationHelpers = class {
|
|
|
18637
18883
|
const points = [];
|
|
18638
18884
|
for (let i = 0; i < samples; i++) {
|
|
18639
18885
|
const phi = 2 * Math.PI * i / samples;
|
|
18640
|
-
const
|
|
18641
|
-
|
|
18642
|
-
|
|
18886
|
+
const x = fov.halfAngle * Math.cos(phi) * DEG2RAD;
|
|
18887
|
+
const y = fov.minorHalfAngle * Math.sin(phi) * DEG2RAD;
|
|
18888
|
+
const theta = Math.hypot(x, y);
|
|
18643
18889
|
const cosTheta = Math.cos(theta);
|
|
18644
18890
|
const sinTheta = Math.sin(theta);
|
|
18645
|
-
const cosPhi =
|
|
18646
|
-
const sinPhi =
|
|
18891
|
+
const cosPhi = theta > 0 ? x / theta : 1;
|
|
18892
|
+
const sinPhi = theta > 0 ? y / theta : 0;
|
|
18647
18893
|
const dir = {
|
|
18648
18894
|
x: b.x * cosTheta + u.x * sinTheta * cosPhi + v.x * sinTheta * sinPhi,
|
|
18649
18895
|
y: b.y * cosTheta + u.y * sinTheta * cosPhi + v.y * sinTheta * sinPhi,
|
|
@@ -18833,7 +19079,7 @@ var PlanetBody = class _PlanetBody extends CelestialBody {
|
|
|
18833
19079
|
*/
|
|
18834
19080
|
velocity(date = /* @__PURE__ */ new Date()) {
|
|
18835
19081
|
const time = MakeTime(date);
|
|
18836
|
-
const state =
|
|
19082
|
+
const state = HelioState(this.planetData_.body, time);
|
|
18837
19083
|
return new Vector3D(
|
|
18838
19084
|
state.vx * AU_PER_DAY_TO_KM_PER_S,
|
|
18839
19085
|
state.vy * AU_PER_DAY_TO_KM_PER_S,
|
|
@@ -18912,7 +19158,7 @@ var EphemerisBody = class _EphemerisBody extends CelestialBody {
|
|
|
18912
19158
|
if (!params.ephemeris || params.ephemeris.length === 0) {
|
|
18913
19159
|
throw new Error("Ephemeris array cannot be empty");
|
|
18914
19160
|
}
|
|
18915
|
-
let spaceObjectType
|
|
19161
|
+
let spaceObjectType;
|
|
18916
19162
|
switch (params.bodyType) {
|
|
18917
19163
|
case "dwarf_planet" /* DWARF_PLANET */:
|
|
18918
19164
|
spaceObjectType = 36 /* DWARF_PLANET */;
|
|
@@ -19287,6 +19533,11 @@ var Thrust = class {
|
|
|
19287
19533
|
crosstrack * 1e-3
|
|
19288
19534
|
);
|
|
19289
19535
|
}
|
|
19536
|
+
center;
|
|
19537
|
+
radial;
|
|
19538
|
+
intrack;
|
|
19539
|
+
crosstrack;
|
|
19540
|
+
durationRate;
|
|
19290
19541
|
deltaV;
|
|
19291
19542
|
get magnitude() {
|
|
19292
19543
|
return this.deltaV.magnitude() * 1e3;
|
|
@@ -19325,6 +19576,9 @@ var Hill = class _Hill {
|
|
|
19325
19576
|
this.semimajorAxis_ = semimajorAxis;
|
|
19326
19577
|
this.meanMotion_ = Earth.smaToMeanMotion(this.semimajorAxis_);
|
|
19327
19578
|
}
|
|
19579
|
+
epoch;
|
|
19580
|
+
position;
|
|
19581
|
+
velocity;
|
|
19328
19582
|
semimajorAxis_;
|
|
19329
19583
|
meanMotion_;
|
|
19330
19584
|
static fromState(origin, radialPosition, intrackPosition, nodeVelocity, nodeOffsetTime) {
|
|
@@ -19501,7 +19755,7 @@ var Hill = class _Hill {
|
|
|
19501
19755
|
postManeuvers = postManeuvers.slice();
|
|
19502
19756
|
let output = preManeuvers;
|
|
19503
19757
|
output.sort((a, b) => a.center.difference(b.center));
|
|
19504
|
-
output = output.filter((mvr) => mvr.center >= this.epoch && mvr.center >= pivot);
|
|
19758
|
+
output = output.filter((mvr) => mvr.center.posix >= this.epoch.posix && mvr.center.posix >= pivot.posix);
|
|
19505
19759
|
for (const mvr of output) {
|
|
19506
19760
|
state = state.maneuver(mvr);
|
|
19507
19761
|
}
|
|
@@ -19611,6 +19865,13 @@ var RadecTopocentric = class _RadecTopocentric {
|
|
|
19611
19865
|
this.declinationRate = declinationRate;
|
|
19612
19866
|
this.rangeRate = rangeRate;
|
|
19613
19867
|
}
|
|
19868
|
+
epoch;
|
|
19869
|
+
rightAscension;
|
|
19870
|
+
declination;
|
|
19871
|
+
range;
|
|
19872
|
+
rightAscensionRate;
|
|
19873
|
+
declinationRate;
|
|
19874
|
+
rangeRate;
|
|
19614
19875
|
/**
|
|
19615
19876
|
* Create a new RadecTopocentric object, using degrees for the angular values.
|
|
19616
19877
|
* @param epoch UTC epoch.
|
|
@@ -19623,8 +19884,8 @@ var RadecTopocentric = class _RadecTopocentric {
|
|
|
19623
19884
|
* @returns A new RadecTopocentric object.
|
|
19624
19885
|
*/
|
|
19625
19886
|
static fromDegrees(epoch, rightAscensionDegrees, declinationDegrees, range, rightAscensionRateDegrees, declinationRateDegrees, rangeRate) {
|
|
19626
|
-
const rightAscensionRate = rightAscensionRateDegrees ? rightAscensionRateDegrees * DEG2RAD : null;
|
|
19627
|
-
const declinationRate = declinationRateDegrees ? declinationRateDegrees * DEG2RAD : null;
|
|
19887
|
+
const rightAscensionRate = typeof rightAscensionRateDegrees === "number" ? rightAscensionRateDegrees * DEG2RAD : null;
|
|
19888
|
+
const declinationRate = typeof declinationRateDegrees === "number" ? declinationRateDegrees * DEG2RAD : null;
|
|
19628
19889
|
const normalizedRA = ((rightAscensionDegrees * DEG2RAD + Math.PI) % TAU + TAU) % TAU - Math.PI;
|
|
19629
19890
|
return new _RadecTopocentric(
|
|
19630
19891
|
epoch,
|
|
@@ -19694,14 +19955,14 @@ var RadecTopocentric = class _RadecTopocentric {
|
|
|
19694
19955
|
* @returns The right ascension rate in degrees per second, or null if it is not available.
|
|
19695
19956
|
*/
|
|
19696
19957
|
get rightAscensionRateDegrees() {
|
|
19697
|
-
return this.rightAscensionRate ? this.rightAscensionRate * RAD2DEG : null;
|
|
19958
|
+
return typeof this.rightAscensionRate === "number" ? this.rightAscensionRate * RAD2DEG : null;
|
|
19698
19959
|
}
|
|
19699
19960
|
/**
|
|
19700
19961
|
* Gets the rate of change of declination in degrees per second.
|
|
19701
19962
|
* @returns The rate of change of declination in degrees per second, or null if the declination rate is not defined.
|
|
19702
19963
|
*/
|
|
19703
19964
|
get declinationRateDegrees() {
|
|
19704
|
-
return this.declinationRate ? this.declinationRate * RAD2DEG : null;
|
|
19965
|
+
return typeof this.declinationRate === "number" ? this.declinationRate * RAD2DEG : null;
|
|
19705
19966
|
}
|
|
19706
19967
|
/**
|
|
19707
19968
|
* Return the position relative to the observer site.
|
|
@@ -19725,7 +19986,7 @@ var RadecTopocentric = class _RadecTopocentric {
|
|
|
19725
19986
|
* @returns A Vector3D object.
|
|
19726
19987
|
*/
|
|
19727
19988
|
velocity(site, range, rangeRate) {
|
|
19728
|
-
if (
|
|
19989
|
+
if (typeof this.rightAscensionRate !== "number" || typeof this.declinationRate !== "number") {
|
|
19729
19990
|
throw new Error("Velocity unsolvable, missing ra/dec rates.");
|
|
19730
19991
|
}
|
|
19731
19992
|
const r = range ?? this.range ?? 1;
|
|
@@ -19780,6 +20041,9 @@ var ObservationOptical = class _ObservationOptical extends Observation {
|
|
|
19780
20041
|
this.observation = observation;
|
|
19781
20042
|
this.noise_ = noise_;
|
|
19782
20043
|
}
|
|
20044
|
+
site_;
|
|
20045
|
+
observation;
|
|
20046
|
+
noise_;
|
|
19783
20047
|
// / Inertial site location.
|
|
19784
20048
|
get site() {
|
|
19785
20049
|
return this.site_;
|
|
@@ -19870,6 +20134,9 @@ var ObservationRadar = class _ObservationRadar extends Observation {
|
|
|
19870
20134
|
this.observation = observation;
|
|
19871
20135
|
this.noise_ = noise_;
|
|
19872
20136
|
}
|
|
20137
|
+
site_;
|
|
20138
|
+
observation;
|
|
20139
|
+
noise_;
|
|
19873
20140
|
// / Default noise matrix.
|
|
19874
20141
|
static defaultNoise = _ObservationRadar.noiseFromSigmas(0.32, 0.015 * DEG2RAD, 0.015 * DEG2RAD);
|
|
19875
20142
|
get epoch() {
|
|
@@ -19950,6 +20217,13 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
19950
20217
|
this.declinationRate = declinationRate;
|
|
19951
20218
|
this.rangeRate = rangeRate;
|
|
19952
20219
|
}
|
|
20220
|
+
epoch;
|
|
20221
|
+
rightAscension;
|
|
20222
|
+
declination;
|
|
20223
|
+
range;
|
|
20224
|
+
rightAscensionRate;
|
|
20225
|
+
declinationRate;
|
|
20226
|
+
rangeRate;
|
|
19953
20227
|
/**
|
|
19954
20228
|
* Creates a RadecGeocentric object from the given parameters in degrees.
|
|
19955
20229
|
* @param epoch - The epoch in UTC.
|
|
@@ -19962,8 +20236,8 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
19962
20236
|
* @returns A new RadecGeocentric object.
|
|
19963
20237
|
*/
|
|
19964
20238
|
static fromDegrees(epoch, rightAscensionDegrees, declinationDegrees, range, rightAscensionRateDegrees, declinationRateDegrees, rangeRate) {
|
|
19965
|
-
const rightAscensionRate = rightAscensionRateDegrees ? rightAscensionRateDegrees * DEG2RAD : null;
|
|
19966
|
-
const declinationRate = declinationRateDegrees ? declinationRateDegrees * DEG2RAD : null;
|
|
20239
|
+
const rightAscensionRate = typeof rightAscensionRateDegrees === "number" ? rightAscensionRateDegrees * DEG2RAD : null;
|
|
20240
|
+
const declinationRate = typeof declinationRateDegrees === "number" ? declinationRateDegrees * DEG2RAD : null;
|
|
19967
20241
|
return new _RadecGeocentric(
|
|
19968
20242
|
epoch,
|
|
19969
20243
|
rightAscensionDegrees * DEG2RAD,
|
|
@@ -20000,7 +20274,7 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
20000
20274
|
const declinationRate = (vK - rangeRate * (rK / rMag)) / rIJMag;
|
|
20001
20275
|
return new _RadecGeocentric(
|
|
20002
20276
|
state.epoch,
|
|
20003
|
-
rightAscension % TAU,
|
|
20277
|
+
(rightAscension % TAU + TAU) % TAU,
|
|
20004
20278
|
declination,
|
|
20005
20279
|
rMag,
|
|
20006
20280
|
rightAscensionRate,
|
|
@@ -20013,7 +20287,8 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
20013
20287
|
* @returns The right ascension in degrees.
|
|
20014
20288
|
*/
|
|
20015
20289
|
get rightAscensionDegrees() {
|
|
20016
|
-
|
|
20290
|
+
const deg = this.rightAscension * RAD2DEG;
|
|
20291
|
+
return (deg % 360 + 360) % 360;
|
|
20017
20292
|
}
|
|
20018
20293
|
/**
|
|
20019
20294
|
* Gets the declination in degrees.
|
|
@@ -20027,14 +20302,14 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
20027
20302
|
* @returns The right ascension rate in degrees per second, or null if it is not available.
|
|
20028
20303
|
*/
|
|
20029
20304
|
get rightAscensionRateDegrees() {
|
|
20030
|
-
return this.rightAscensionRate ? this.rightAscensionRate * RAD2DEG : null;
|
|
20305
|
+
return typeof this.rightAscensionRate === "number" ? this.rightAscensionRate * RAD2DEG : null;
|
|
20031
20306
|
}
|
|
20032
20307
|
/**
|
|
20033
20308
|
* Gets the rate of change of declination in degrees per second.
|
|
20034
20309
|
* @returns The rate of change of declination in degrees per second, or null if not available.
|
|
20035
20310
|
*/
|
|
20036
20311
|
get declinationRateDegrees() {
|
|
20037
|
-
return this.declinationRate ? this.declinationRate * RAD2DEG : null;
|
|
20312
|
+
return typeof this.declinationRate === "number" ? this.declinationRate * RAD2DEG : null;
|
|
20038
20313
|
}
|
|
20039
20314
|
/**
|
|
20040
20315
|
* Calculates the position vector in geocentric coordinates.
|
|
@@ -20054,7 +20329,7 @@ var RadecGeocentric = class _RadecGeocentric {
|
|
|
20054
20329
|
* @throws Error if the right ascension rate or declination rate is missing.
|
|
20055
20330
|
*/
|
|
20056
20331
|
velocity(range, rangeRate) {
|
|
20057
|
-
if (
|
|
20332
|
+
if (typeof this.rightAscensionRate !== "number" || typeof this.declinationRate !== "number") {
|
|
20058
20333
|
throw new Error("Velocity unsolvable, missing ra/dec rates.");
|
|
20059
20334
|
}
|
|
20060
20335
|
const r = range ?? this.range ?? 1;
|
|
@@ -20472,12 +20747,143 @@ var RandomGaussianSource = class {
|
|
|
20472
20747
|
}
|
|
20473
20748
|
};
|
|
20474
20749
|
|
|
20750
|
+
// src/external/Sgp4WasmTypes.ts
|
|
20751
|
+
var Sgp4WasmLogLevel = /* @__PURE__ */ ((Sgp4WasmLogLevel3) => {
|
|
20752
|
+
Sgp4WasmLogLevel3[Sgp4WasmLogLevel3["None"] = 0] = "None";
|
|
20753
|
+
Sgp4WasmLogLevel3[Sgp4WasmLogLevel3["Info"] = 1] = "Info";
|
|
20754
|
+
Sgp4WasmLogLevel3[Sgp4WasmLogLevel3["Warn"] = 2] = "Warn";
|
|
20755
|
+
Sgp4WasmLogLevel3[Sgp4WasmLogLevel3["Error"] = 3] = "Error";
|
|
20756
|
+
return Sgp4WasmLogLevel3;
|
|
20757
|
+
})(Sgp4WasmLogLevel || {});
|
|
20758
|
+
var Sgp4WasmError = class extends Error {
|
|
20759
|
+
constructor(message, code) {
|
|
20760
|
+
super(message);
|
|
20761
|
+
this.code = code;
|
|
20762
|
+
this.name = "Sgp4WasmError";
|
|
20763
|
+
}
|
|
20764
|
+
code;
|
|
20765
|
+
};
|
|
20766
|
+
var SGP4_WASM_DYN_ARR_ERRORS = {
|
|
20767
|
+
[-1]: "Error allocating space for the dynamic array",
|
|
20768
|
+
[-2]: "Dynamic array is already initialized",
|
|
20769
|
+
[-3]: "The requested size is too large",
|
|
20770
|
+
[-4]: "The requested size is negative"
|
|
20771
|
+
};
|
|
20772
|
+
var SGP4_WASM_ADD_SAT_ERRORS = {
|
|
20773
|
+
[-1]: "Dynamic array has not been allocated",
|
|
20774
|
+
[-2]: "Dynamic array is out of space (call reallocDynArr)",
|
|
20775
|
+
[-3]: "Failed to determine satKey from line 1",
|
|
20776
|
+
[-4]: "Failed to parse line 1 or line 2"
|
|
20777
|
+
};
|
|
20778
|
+
|
|
20779
|
+
// src/propagator/Sgp4WasmPropagator.ts
|
|
20780
|
+
var Sgp4WasmPropagator = class extends Propagator {
|
|
20781
|
+
constructor(wasm_, tle_) {
|
|
20782
|
+
super();
|
|
20783
|
+
this.wasm_ = wasm_;
|
|
20784
|
+
this.tle_ = tle_;
|
|
20785
|
+
if (!wasm_.isLoaded) {
|
|
20786
|
+
throw new Sgp4WasmError("Sgp4WasmPropagator requires a loaded Sgp4Wasm instance. Call load() first.");
|
|
20787
|
+
}
|
|
20788
|
+
this.satKey_ = wasm_.addSat(tle_.line1, tle_.line2);
|
|
20789
|
+
wasm_.initSats([this.satKey_]);
|
|
20790
|
+
this.cacheState_ = this.propagate(tle_.epoch);
|
|
20791
|
+
}
|
|
20792
|
+
wasm_;
|
|
20793
|
+
tle_;
|
|
20794
|
+
satKey_;
|
|
20795
|
+
cacheState_;
|
|
20796
|
+
checkpoints_ = [];
|
|
20797
|
+
/**
|
|
20798
|
+
* The Astro Standards satKey backing this propagator.
|
|
20799
|
+
*/
|
|
20800
|
+
get satKey() {
|
|
20801
|
+
return this.satKey_;
|
|
20802
|
+
}
|
|
20803
|
+
/**
|
|
20804
|
+
* Gets the state of the propagator in the J2000 coordinate system.
|
|
20805
|
+
* @returns The J2000 state of the propagator.
|
|
20806
|
+
*/
|
|
20807
|
+
get state() {
|
|
20808
|
+
return this.cacheState_;
|
|
20809
|
+
}
|
|
20810
|
+
/**
|
|
20811
|
+
* Calculates the ephemeris maneuver using the SGP4 propagator.
|
|
20812
|
+
* @param start The start epoch in UTC.
|
|
20813
|
+
* @param finish The finish epoch in UTC.
|
|
20814
|
+
* @param maneuvers The array of thrust maneuvers.
|
|
20815
|
+
* @param interval The time interval in seconds.
|
|
20816
|
+
*/
|
|
20817
|
+
ephemerisManeuver(_start, _finish, _maneuvers, _interval = 60) {
|
|
20818
|
+
throw new Error("Maneuvers cannot be modelled with SGP4.");
|
|
20819
|
+
}
|
|
20820
|
+
/**
|
|
20821
|
+
* Performs a maneuver with the given thrust.
|
|
20822
|
+
* @param maneuver - The thrust maneuver to perform.
|
|
20823
|
+
* @param interval - The time interval for the maneuver (default: 60.0 seconds).
|
|
20824
|
+
* @throws Error if maneuvers cannot be modeled with SGP4.
|
|
20825
|
+
*/
|
|
20826
|
+
maneuver(_maneuver, _interval = 60) {
|
|
20827
|
+
throw new Error("Maneuvers cannot be modelled with SGP4.");
|
|
20828
|
+
}
|
|
20829
|
+
/**
|
|
20830
|
+
* Propagates the state to a specified epoch in J2000 coordinates.
|
|
20831
|
+
* @param epoch - The epoch in UTC format.
|
|
20832
|
+
* @returns The propagated state in J2000 coordinates.
|
|
20833
|
+
*/
|
|
20834
|
+
propagate(epoch) {
|
|
20835
|
+
const state = this.wasm_.propagateEpoch(this.satKey_, epoch);
|
|
20836
|
+
if (state.err !== 0) {
|
|
20837
|
+
throw new Sgp4WasmError(`SGP4 propagation failed for satKey ${this.satKey_} (code ${state.err})`, state.err);
|
|
20838
|
+
}
|
|
20839
|
+
this.cacheState_ = new TEME(epoch, state.position, state.velocity).toJ2000();
|
|
20840
|
+
return this.cacheState_;
|
|
20841
|
+
}
|
|
20842
|
+
/**
|
|
20843
|
+
* Resets the state of the propagator to the TLE epoch state.
|
|
20844
|
+
*/
|
|
20845
|
+
reset() {
|
|
20846
|
+
this.cacheState_ = this.propagate(this.tle_.epoch);
|
|
20847
|
+
}
|
|
20848
|
+
/**
|
|
20849
|
+
* Saves the current state of the propagator and returns the index of the checkpoint.
|
|
20850
|
+
* @returns The index of the checkpoint.
|
|
20851
|
+
*/
|
|
20852
|
+
checkpoint() {
|
|
20853
|
+
this.checkpoints_.push(this.cacheState_);
|
|
20854
|
+
return this.checkpoints_.length - 1;
|
|
20855
|
+
}
|
|
20856
|
+
/**
|
|
20857
|
+
* Clears all the checkpoints in the propagator.
|
|
20858
|
+
*/
|
|
20859
|
+
clearCheckpoints() {
|
|
20860
|
+
this.checkpoints_ = [];
|
|
20861
|
+
}
|
|
20862
|
+
/**
|
|
20863
|
+
* Restores the state of the propagator to a previously saved checkpoint.
|
|
20864
|
+
* @param index - The index of the checkpoint to restore.
|
|
20865
|
+
*/
|
|
20866
|
+
restore(index) {
|
|
20867
|
+
this.cacheState_ = this.checkpoints_[index];
|
|
20868
|
+
}
|
|
20869
|
+
/**
|
|
20870
|
+
* Removes the satellite from the backing wasm instance's SGP4 and TLE
|
|
20871
|
+
* registries.
|
|
20872
|
+
*/
|
|
20873
|
+
dispose() {
|
|
20874
|
+
this.wasm_.uninitSats([this.satKey_]);
|
|
20875
|
+
this.wasm_.removeSats([this.satKey_]);
|
|
20876
|
+
}
|
|
20877
|
+
};
|
|
20878
|
+
|
|
20475
20879
|
// src/observation/PropagatorPairs.ts
|
|
20476
20880
|
var PropagatorPairs = class {
|
|
20477
20881
|
constructor(posStep_, velStep_) {
|
|
20478
20882
|
this.posStep_ = posStep_;
|
|
20479
20883
|
this.velStep_ = velStep_;
|
|
20480
20884
|
}
|
|
20885
|
+
posStep_;
|
|
20886
|
+
velStep_;
|
|
20481
20887
|
_high = Array(6).fill(null);
|
|
20482
20888
|
_low = Array(6).fill(null);
|
|
20483
20889
|
set(index, high, low) {
|
|
@@ -20511,6 +20917,9 @@ var BatchLeastSquaresResult = class {
|
|
|
20511
20917
|
this.covariance = covariance2;
|
|
20512
20918
|
this.rms = rms;
|
|
20513
20919
|
}
|
|
20920
|
+
state;
|
|
20921
|
+
covariance;
|
|
20922
|
+
rms;
|
|
20514
20923
|
};
|
|
20515
20924
|
|
|
20516
20925
|
// src/orbit-determination/BatchLeastSquaresOD.ts
|
|
@@ -20541,6 +20950,12 @@ var BatchLeastSquaresOD = class _BatchLeastSquaresOD {
|
|
|
20541
20950
|
this.propagator_ = new RungeKutta89Propagator(this.apriori_, this.forceModel_);
|
|
20542
20951
|
this.nominal_ = this.propagator_.propagate(this.start_);
|
|
20543
20952
|
}
|
|
20953
|
+
observations_;
|
|
20954
|
+
apriori_;
|
|
20955
|
+
forceModel_;
|
|
20956
|
+
posStep_;
|
|
20957
|
+
velStep_;
|
|
20958
|
+
fastDerivatives_;
|
|
20544
20959
|
/** Propagator pair cache, for generating observation Jacobians. */
|
|
20545
20960
|
propPairs_;
|
|
20546
20961
|
/** Nominal state propagator. */
|
|
@@ -20641,6 +21056,7 @@ var GibbsIOD = class _GibbsIOD {
|
|
|
20641
21056
|
constructor(mu = Earth.mu) {
|
|
20642
21057
|
this.mu = mu;
|
|
20643
21058
|
}
|
|
21059
|
+
mu;
|
|
20644
21060
|
/** Abort solve if position plane exceeds this value. */
|
|
20645
21061
|
static coplanarThreshold_ = 5 * DEG2RAD;
|
|
20646
21062
|
/**
|
|
@@ -20699,6 +21115,7 @@ var HerrickGibbsIOD = class {
|
|
|
20699
21115
|
constructor(mu = Earth.mu) {
|
|
20700
21116
|
this.mu = mu;
|
|
20701
21117
|
}
|
|
21118
|
+
mu;
|
|
20702
21119
|
// / Attempt to create a state estimate from three inertial position vectors.
|
|
20703
21120
|
solve(r1, t1, r2, t2, r3, t3) {
|
|
20704
21121
|
const dt31 = t3.difference(t1);
|
|
@@ -20959,6 +21376,7 @@ var LambertIOD = class _LambertIOD {
|
|
|
20959
21376
|
constructor(mu = Earth.mu) {
|
|
20960
21377
|
this.mu = mu;
|
|
20961
21378
|
}
|
|
21379
|
+
mu;
|
|
20962
21380
|
/**
|
|
20963
21381
|
* Try to guess the short path argument given an [interceptor] and
|
|
20964
21382
|
* [target] state.
|
|
@@ -21214,10 +21632,9 @@ var GoodingIOD = class _GoodingIOD {
|
|
|
21214
21632
|
* @returns Orbit estimate referenced to the epoch of the second observation
|
|
21215
21633
|
*/
|
|
21216
21634
|
estimate(o1, o2, o3, rho1init = null, rho3init = null, nRev = 0, direction = true) {
|
|
21217
|
-
let orbit = null;
|
|
21218
21635
|
if (rho1init === null || rho3init === null) {
|
|
21219
21636
|
const gaussIod = new GaussIOD(this._mu);
|
|
21220
|
-
orbit = gaussIod.estimate(o1, o2, o3);
|
|
21637
|
+
const orbit = gaussIod.estimate(o1, o2, o3);
|
|
21221
21638
|
if (orbit === null) {
|
|
21222
21639
|
throw new OrbitDeterminationError("Gauss IOD failed to provide initial estimate for Gooding IOD", "Gooding");
|
|
21223
21640
|
}
|
|
@@ -21646,6 +22063,11 @@ var LevenbergMarquardtResult = class {
|
|
|
21646
22063
|
this.iterations = iterations;
|
|
21647
22064
|
this.converged = converged;
|
|
21648
22065
|
}
|
|
22066
|
+
state;
|
|
22067
|
+
covariance;
|
|
22068
|
+
rms;
|
|
22069
|
+
iterations;
|
|
22070
|
+
converged;
|
|
21649
22071
|
};
|
|
21650
22072
|
|
|
21651
22073
|
// src/orbit-determination/LevenbergMarquardtOD.ts
|
|
@@ -21676,6 +22098,12 @@ var LevenbergMarquardtOD = class _LevenbergMarquardtOD {
|
|
|
21676
22098
|
const propagator = new RungeKutta89Propagator(this.apriori_, this.forceModel_);
|
|
21677
22099
|
this.nominal_ = propagator.propagate(this.start_);
|
|
21678
22100
|
}
|
|
22101
|
+
observations_;
|
|
22102
|
+
apriori_;
|
|
22103
|
+
forceModel_;
|
|
22104
|
+
posStep_;
|
|
22105
|
+
velStep_;
|
|
22106
|
+
fastDerivatives_;
|
|
21679
22107
|
/** Propagator pair cache, for generating observation Jacobians. */
|
|
21680
22108
|
propPairs_;
|
|
21681
22109
|
/** State estimate during solve. */
|
|
@@ -21834,6 +22262,8 @@ var SimplexEntry = class _SimplexEntry {
|
|
|
21834
22262
|
this.x_ = new Vector(points);
|
|
21835
22263
|
this.score = this.f_(points);
|
|
21836
22264
|
}
|
|
22265
|
+
f_;
|
|
22266
|
+
points;
|
|
21837
22267
|
/**
|
|
21838
22268
|
* The cost function score at this vertex.
|
|
21839
22269
|
*
|
|
@@ -22112,6 +22542,18 @@ var ModifiedGoodingIOD = class {
|
|
|
22112
22542
|
};
|
|
22113
22543
|
return scoreFn;
|
|
22114
22544
|
}
|
|
22545
|
+
/**
|
|
22546
|
+
* Solves for a J2000 state at the middle observation's epoch: a Gooding three-line-of-sight
|
|
22547
|
+
* estimate refined by a downhill simplex over all observations with a two-body force model.
|
|
22548
|
+
*
|
|
22549
|
+
* `tolerance` ends the simplex once the spread of the summed line-of-sight error (radians)
|
|
22550
|
+
* or of the vertices falls below it.
|
|
22551
|
+
* @param observations - Optical observations, in time order (at least 3)
|
|
22552
|
+
* @param r0 - Range guess for the first observation (km)
|
|
22553
|
+
* @param rN - Range guess for the last observation (km)
|
|
22554
|
+
* @param options - Solver options
|
|
22555
|
+
* @returns The J2000 state at the middle observation's epoch
|
|
22556
|
+
*/
|
|
22115
22557
|
solve(observations, r0, rN, {
|
|
22116
22558
|
nRev = 0,
|
|
22117
22559
|
direction = true,
|
|
@@ -22575,6 +23017,8 @@ var ConjunctionAssessment = class {
|
|
|
22575
23017
|
this.primaryProp = this.createPropagator(primary, false);
|
|
22576
23018
|
this.secondaryProp = this.createPropagator(secondary, false);
|
|
22577
23019
|
}
|
|
23020
|
+
primary;
|
|
23021
|
+
secondary;
|
|
22578
23022
|
primaryProp;
|
|
22579
23023
|
secondaryProp;
|
|
22580
23024
|
primaryCovSample;
|
|
@@ -23809,7 +24253,7 @@ var PhasedArrayRadar = class _PhasedArrayRadar extends RadarSensor {
|
|
|
23809
24253
|
this.validateFace_(face);
|
|
23810
24254
|
const azRad = az * DEG2RAD;
|
|
23811
24255
|
const elRad = el * DEG2RAD;
|
|
23812
|
-
const azDiff = azRad - this.boresightAzRad(face);
|
|
24256
|
+
const azDiff = wrapAngle(azRad - this.boresightAzRad(face));
|
|
23813
24257
|
const elDiff = elRad - this.boresightElRad(face);
|
|
23814
24258
|
return azel2uv(azDiff, elDiff, this.beamwidthRad);
|
|
23815
24259
|
}
|
|
@@ -23903,9 +24347,7 @@ var PhasedArrayRadar = class _PhasedArrayRadar extends RadarSensor {
|
|
|
23903
24347
|
getVisibleFaces(az, el, maxAngle = 60) {
|
|
23904
24348
|
const visibleFaces = [];
|
|
23905
24349
|
for (let face = 0; face < this.faceCount; face++) {
|
|
23906
|
-
const
|
|
23907
|
-
const elDiff = Math.abs(el - this.boresightEl[face]);
|
|
23908
|
-
const angularDist = Math.sqrt(azDiff * azDiff + elDiff * elDiff);
|
|
24350
|
+
const angularDist = this.angleFromBoresight_(az, el, face);
|
|
23909
24351
|
if (angularDist <= maxAngle) {
|
|
23910
24352
|
visibleFaces.push(face);
|
|
23911
24353
|
}
|
|
@@ -23922,9 +24364,7 @@ var PhasedArrayRadar = class _PhasedArrayRadar extends RadarSensor {
|
|
|
23922
24364
|
let bestFace = -1;
|
|
23923
24365
|
let minDist = Infinity;
|
|
23924
24366
|
for (let face = 0; face < this.faceCount; face++) {
|
|
23925
|
-
const
|
|
23926
|
-
const elDiff = el - this.boresightEl[face];
|
|
23927
|
-
const dist = Math.sqrt(azDiff * azDiff + elDiff * elDiff);
|
|
24367
|
+
const dist = this.angleFromBoresight_(az, el, face);
|
|
23928
24368
|
if (dist < minDist && dist < 90) {
|
|
23929
24369
|
minDist = dist;
|
|
23930
24370
|
bestFace = face;
|
|
@@ -23932,6 +24372,24 @@ var PhasedArrayRadar = class _PhasedArrayRadar extends RadarSensor {
|
|
|
23932
24372
|
}
|
|
23933
24373
|
return bestFace;
|
|
23934
24374
|
}
|
|
24375
|
+
/**
|
|
24376
|
+
* Great-circle angle between a look direction and a face's boresight, in degrees.
|
|
24377
|
+
* (The face checks used sqrt(dAz^2 + dEl^2) with no azimuth wrap: 359 deg missed a 0 deg
|
|
24378
|
+
* face, and azimuth differences were not shrunk by cos(elevation).)
|
|
24379
|
+
* @param az - Target azimuth in degrees
|
|
24380
|
+
* @param el - Target elevation in degrees
|
|
24381
|
+
* @param face - Face index
|
|
24382
|
+
* @returns Angle from boresight in degrees
|
|
24383
|
+
*/
|
|
24384
|
+
angleFromBoresight_(az, el, face) {
|
|
24385
|
+
return angularDistance(
|
|
24386
|
+
az * DEG2RAD,
|
|
24387
|
+
el * DEG2RAD,
|
|
24388
|
+
this.boresightAz[face] * DEG2RAD,
|
|
24389
|
+
this.boresightEl[face] * DEG2RAD,
|
|
24390
|
+
1 /* Haversine */
|
|
24391
|
+
) * RAD2DEG;
|
|
24392
|
+
}
|
|
23935
24393
|
// ==================== Serialization ====================
|
|
23936
24394
|
serializeSpecific() {
|
|
23937
24395
|
return {
|
|
@@ -24442,21 +24900,98 @@ var PassiveRFSensor = class _PassiveRFSensor extends Sensor {
|
|
|
24442
24900
|
}
|
|
24443
24901
|
};
|
|
24444
24902
|
|
|
24445
|
-
// src/
|
|
24446
|
-
|
|
24447
|
-
|
|
24448
|
-
|
|
24449
|
-
|
|
24450
|
-
|
|
24451
|
-
|
|
24903
|
+
// src/sensor/TtcAntenna.ts
|
|
24904
|
+
var TtcAntenna = class _TtcAntenna extends Sensor {
|
|
24905
|
+
/** Antenna dish diameter in meters */
|
|
24906
|
+
antennaDiameter;
|
|
24907
|
+
/** Antenna gain in dB */
|
|
24908
|
+
antennaGain;
|
|
24909
|
+
/** Whether the station supports two-way ranging */
|
|
24910
|
+
hasRanging;
|
|
24911
|
+
/** Whether the station can uplink commands */
|
|
24912
|
+
hasUplink;
|
|
24913
|
+
constructor(params) {
|
|
24914
|
+
const paramsWithType = {
|
|
24915
|
+
...params,
|
|
24916
|
+
sensorType: "TT_C_ANTENNA" /* TT_C_ANTENNA */
|
|
24917
|
+
};
|
|
24918
|
+
super(paramsWithType);
|
|
24919
|
+
this.antennaDiameter = params.antennaDiameter;
|
|
24920
|
+
this.antennaGain = params.antennaGain;
|
|
24921
|
+
this.hasRanging = params.hasRanging ?? true;
|
|
24922
|
+
this.hasUplink = params.hasUplink ?? true;
|
|
24452
24923
|
}
|
|
24453
24924
|
/**
|
|
24454
|
-
*
|
|
24455
|
-
*
|
|
24456
|
-
*
|
|
24457
|
-
* -
|
|
24458
|
-
*
|
|
24459
|
-
|
|
24925
|
+
* Creates an angle observation (Ra/Dec) of a cooperative target.
|
|
24926
|
+
* Angles come from antenna autotrack while locked to the target's downlink.
|
|
24927
|
+
* @param target - The space object to observe
|
|
24928
|
+
* @param date - Time of observation (defaults to now)
|
|
24929
|
+
* @returns ObservationOptical or null if target not in FOV
|
|
24930
|
+
*/
|
|
24931
|
+
observe(target, date = /* @__PURE__ */ new Date()) {
|
|
24932
|
+
if (!this.canObserve(target, date)) {
|
|
24933
|
+
return null;
|
|
24934
|
+
}
|
|
24935
|
+
const sensorJ2000 = this.getJ2000(date);
|
|
24936
|
+
const targetJ2000 = target.toJ2000(date);
|
|
24937
|
+
const radec = RadecTopocentric.fromStateVector(targetJ2000, sensorJ2000);
|
|
24938
|
+
return new ObservationOptical(sensorJ2000, radec);
|
|
24939
|
+
}
|
|
24940
|
+
serializeSpecific() {
|
|
24941
|
+
return {
|
|
24942
|
+
antennaDiameter: this.antennaDiameter,
|
|
24943
|
+
antennaGain: this.antennaGain,
|
|
24944
|
+
hasRanging: this.hasRanging,
|
|
24945
|
+
hasUplink: this.hasUplink
|
|
24946
|
+
};
|
|
24947
|
+
}
|
|
24948
|
+
/**
|
|
24949
|
+
* Creates a deep copy of this TT&C antenna.
|
|
24950
|
+
* The cloned sensor will not have a parent assigned.
|
|
24951
|
+
* @returns A new TtcAntenna instance with the same properties
|
|
24952
|
+
*/
|
|
24953
|
+
clone() {
|
|
24954
|
+
return new _TtcAntenna({
|
|
24955
|
+
id: this.id,
|
|
24956
|
+
name: this.name,
|
|
24957
|
+
sensorType: this.sensorType,
|
|
24958
|
+
fieldOfView: this.fieldOfView.serialize(),
|
|
24959
|
+
antennaDiameter: this.antennaDiameter,
|
|
24960
|
+
antennaGain: this.antennaGain,
|
|
24961
|
+
hasRanging: this.hasRanging,
|
|
24962
|
+
hasUplink: this.hasUplink,
|
|
24963
|
+
shortName: this.shortName,
|
|
24964
|
+
system: this.system,
|
|
24965
|
+
country: this.country,
|
|
24966
|
+
operator: this.operator,
|
|
24967
|
+
dwellTime: this.dwellTime,
|
|
24968
|
+
freqBand: this.freqBand,
|
|
24969
|
+
isVolumetric: this.isVolumetric,
|
|
24970
|
+
url: this.url,
|
|
24971
|
+
metadata: this.metadata ? { ...this.metadata } : void 0
|
|
24972
|
+
});
|
|
24973
|
+
}
|
|
24974
|
+
};
|
|
24975
|
+
|
|
24976
|
+
// src/parsers/HorizonsParser.ts
|
|
24977
|
+
function julianDateToDate(jd) {
|
|
24978
|
+
const unixMs = (jd - 24405875e-1) * 864e5;
|
|
24979
|
+
return new Date(unixMs);
|
|
24980
|
+
}
|
|
24981
|
+
function tdbDateToEpochUtc(tdb) {
|
|
24982
|
+
const j2000Posix = 946728e3;
|
|
24983
|
+
return EpochUTC.fromJ2000TTSeconds(tdb.getTime() / 1e3 - j2000Posix);
|
|
24984
|
+
}
|
|
24985
|
+
var HorizonsParser = class {
|
|
24986
|
+
constructor() {
|
|
24987
|
+
}
|
|
24988
|
+
/**
|
|
24989
|
+
* Parses NASA Horizons vector table format.
|
|
24990
|
+
*
|
|
24991
|
+
* Expects output from Horizons with settings:
|
|
24992
|
+
* - VECTORS (type 2 or 3)
|
|
24993
|
+
* - Output in km and km/s
|
|
24994
|
+
*
|
|
24460
24995
|
* @param data - Raw Horizons output text
|
|
24461
24996
|
* @returns Parsed ephemeris data
|
|
24462
24997
|
*/
|
|
@@ -24478,22 +25013,34 @@ var HorizonsParser = class {
|
|
|
24478
25013
|
let referenceFrame = "";
|
|
24479
25014
|
for (let i = 0; i < lines.length; i++) {
|
|
24480
25015
|
const line = lines[i].trim();
|
|
24481
|
-
|
|
24482
|
-
|
|
24483
|
-
|
|
24484
|
-
|
|
24485
|
-
|
|
24486
|
-
|
|
24487
|
-
|
|
24488
|
-
|
|
24489
|
-
|
|
24490
|
-
|
|
24491
|
-
|
|
24492
|
-
|
|
24493
|
-
|
|
24494
|
-
|
|
24495
|
-
|
|
24496
|
-
|
|
25016
|
+
const meta = this.matchMetadataLine_(line);
|
|
25017
|
+
if (meta) {
|
|
25018
|
+
switch (meta.label) {
|
|
25019
|
+
case "Target body name":
|
|
25020
|
+
targetName = this.stripNameSuffixes_(meta.value);
|
|
25021
|
+
metadata.targetName = targetName;
|
|
25022
|
+
break;
|
|
25023
|
+
case "Center body name":
|
|
25024
|
+
centerBody = meta.value;
|
|
25025
|
+
metadata.centerBody = centerBody;
|
|
25026
|
+
break;
|
|
25027
|
+
case "Center-site name":
|
|
25028
|
+
if (!centerBody) {
|
|
25029
|
+
centerBody = meta.value;
|
|
25030
|
+
metadata.centerBody = centerBody;
|
|
25031
|
+
}
|
|
25032
|
+
break;
|
|
25033
|
+
case "Output type":
|
|
25034
|
+
metadata.outputType = meta.value;
|
|
25035
|
+
break;
|
|
25036
|
+
case "Reference frame":
|
|
25037
|
+
referenceFrame = meta.value;
|
|
25038
|
+
metadata.referenceFrame = referenceFrame;
|
|
25039
|
+
break;
|
|
25040
|
+
case "Output units":
|
|
25041
|
+
metadata.outputUnits = meta.value;
|
|
25042
|
+
break;
|
|
25043
|
+
}
|
|
24497
25044
|
}
|
|
24498
25045
|
if (line === "$$SOE") {
|
|
24499
25046
|
inData = true;
|
|
@@ -24538,10 +25085,12 @@ var HorizonsParser = class {
|
|
|
24538
25085
|
let observerLocation = "";
|
|
24539
25086
|
for (let i = 0; i < lines.length; i++) {
|
|
24540
25087
|
const line = lines[i].trim();
|
|
24541
|
-
|
|
24542
|
-
|
|
24543
|
-
|
|
24544
|
-
|
|
25088
|
+
const targetRaw = this.extractLabeledValue_(line, "Target body name");
|
|
25089
|
+
const siteRaw = this.extractLabeledValue_(line, "Center-site name");
|
|
25090
|
+
if (targetRaw !== null) {
|
|
25091
|
+
targetName = this.stripNameSuffixes_(targetRaw);
|
|
25092
|
+
} else if (siteRaw !== null) {
|
|
25093
|
+
observerLocation = siteRaw;
|
|
24545
25094
|
}
|
|
24546
25095
|
if (line === "$$SOE") {
|
|
24547
25096
|
inData = true;
|
|
@@ -24565,23 +25114,51 @@ var HorizonsParser = class {
|
|
|
24565
25114
|
};
|
|
24566
25115
|
}
|
|
24567
25116
|
// ==================== Private Helper Methods ====================
|
|
25117
|
+
/** Metadata labels recognized in Horizons output headers. */
|
|
25118
|
+
static metadataLabels_ = [
|
|
25119
|
+
"Target body name",
|
|
25120
|
+
"Center body name",
|
|
25121
|
+
"Center-site name",
|
|
25122
|
+
"Output type",
|
|
25123
|
+
"Reference frame",
|
|
25124
|
+
"Output units"
|
|
25125
|
+
];
|
|
24568
25126
|
/**
|
|
24569
|
-
* Extracts a value from a
|
|
25127
|
+
* Extracts a metadata value from a `Label : value` line, tolerating the
|
|
25128
|
+
* column-aligned padding Horizons inserts before the colon (e.g.
|
|
25129
|
+
* "Output units : KM-S"). Returns null if the line does not carry the
|
|
25130
|
+
* given label.
|
|
24570
25131
|
*/
|
|
24571
|
-
static
|
|
24572
|
-
|
|
24573
|
-
|
|
25132
|
+
static extractLabeledValue_(line, label) {
|
|
25133
|
+
if (!line.startsWith(label)) {
|
|
25134
|
+
return null;
|
|
25135
|
+
}
|
|
25136
|
+
const rest = line.substring(label.length);
|
|
25137
|
+
const colonIdx = rest.indexOf(":");
|
|
25138
|
+
if (colonIdx === -1 || rest.substring(0, colonIdx).trim().length > 0) {
|
|
25139
|
+
return null;
|
|
25140
|
+
}
|
|
25141
|
+
return rest.substring(colonIdx + 1).trim().split(/\s{2,}/u)[0].trim();
|
|
25142
|
+
}
|
|
25143
|
+
/** Returns the first recognized metadata label/value on the line, if any. */
|
|
25144
|
+
static matchMetadataLine_(line) {
|
|
25145
|
+
for (const label of this.metadataLabels_) {
|
|
25146
|
+
const value = this.extractLabeledValue_(line, label);
|
|
25147
|
+
if (value !== null) {
|
|
25148
|
+
return { label, value };
|
|
25149
|
+
}
|
|
25150
|
+
}
|
|
25151
|
+
return null;
|
|
24574
25152
|
}
|
|
24575
25153
|
/**
|
|
24576
|
-
*
|
|
25154
|
+
* Strips parenthetical descriptors and IDs from a target body name.
|
|
24577
25155
|
*
|
|
24578
25156
|
* Horizons format examples:
|
|
24579
|
-
* - "1 Ceres
|
|
24580
|
-
* - "Artemis II (spacecraft) (-1024)
|
|
24581
|
-
* - "Mars (499)"
|
|
25157
|
+
* - "1 Ceres" → "1 Ceres"
|
|
25158
|
+
* - "Artemis II (spacecraft) (-1024)" → "Artemis II"
|
|
25159
|
+
* - "Mars (499)" → "Mars"
|
|
24582
25160
|
*/
|
|
24583
|
-
static
|
|
24584
|
-
const raw = this.extractValue_(line, key);
|
|
25161
|
+
static stripNameSuffixes_(raw) {
|
|
24585
25162
|
const parenIdx = raw.search(/\s\(/u);
|
|
24586
25163
|
if (parenIdx > 0) {
|
|
24587
25164
|
return raw.substring(0, parenIdx).trim();
|
|
@@ -24602,7 +25179,7 @@ var HorizonsParser = class {
|
|
|
24602
25179
|
if (isNaN(jd)) {
|
|
24603
25180
|
return null;
|
|
24604
25181
|
}
|
|
24605
|
-
const epoch2 =
|
|
25182
|
+
const epoch2 = tdbDateToEpochUtc(julianDateToDate(jd));
|
|
24606
25183
|
const x2 = parseFloat(parts[1]);
|
|
24607
25184
|
const y2 = parseFloat(parts[2]);
|
|
24608
25185
|
const z2 = parseFloat(parts[3]);
|
|
@@ -24650,7 +25227,8 @@ var HorizonsParser = class {
|
|
|
24650
25227
|
};
|
|
24651
25228
|
const month = monthMap[monthStr] ?? 0;
|
|
24652
25229
|
const date = new Date(Date.UTC(year, month, day, hour, minute, Math.floor(second), second % 1 * 1e3));
|
|
24653
|
-
const
|
|
25230
|
+
const isUt = /\bUTC?\b/u.test(line.slice(dateMatch[0].length));
|
|
25231
|
+
const epoch = isUt ? EpochUTC.fromDateTime(date) : tdbDateToEpochUtc(date);
|
|
24654
25232
|
let nextIndex = currentIndex + 1;
|
|
24655
25233
|
if (nextIndex >= allLines.length) {
|
|
24656
25234
|
return null;
|
|
@@ -26568,7 +27146,7 @@ var Transmitter = class _Transmitter extends CommunicationDevice {
|
|
|
26568
27146
|
const distance = this.getDistanceTo(receiver, date);
|
|
26569
27147
|
const fspl = calculateFspl(distance, this.frequency);
|
|
26570
27148
|
const receivedPower = this.eirp - fspl + receiver.antenna.gain - receiver.lineLoss;
|
|
26571
|
-
const noiseFloorDbw =
|
|
27149
|
+
const noiseFloorDbw = receiver.noiseFloor;
|
|
26572
27150
|
const snr = receivedPower - noiseFloorDbw;
|
|
26573
27151
|
return {
|
|
26574
27152
|
eirp: this.eirp,
|
|
@@ -27308,6 +27886,11 @@ var ChebyshevCoefficients = class _ChebyshevCoefficients {
|
|
|
27308
27886
|
this.cyd_ = _ChebyshevCoefficients._derivative(a, b, cy_);
|
|
27309
27887
|
this.czd_ = _ChebyshevCoefficients._derivative(a, b, cz_);
|
|
27310
27888
|
}
|
|
27889
|
+
a;
|
|
27890
|
+
b;
|
|
27891
|
+
cx_;
|
|
27892
|
+
cy_;
|
|
27893
|
+
cz_;
|
|
27311
27894
|
cxd_;
|
|
27312
27895
|
cyd_;
|
|
27313
27896
|
czd_;
|
|
@@ -27639,6 +28222,43 @@ var PlaneChangeBurn = class _PlaneChangeBurn {
|
|
|
27639
28222
|
}
|
|
27640
28223
|
};
|
|
27641
28224
|
|
|
28225
|
+
// src/maneuver/PropellantBudget.ts
|
|
28226
|
+
var PropellantBudget = class _PropellantBudget {
|
|
28227
|
+
/** Standard gravity used by the specific-impulse definition (m/s^2). */
|
|
28228
|
+
static g0 = 9.80665;
|
|
28229
|
+
/**
|
|
28230
|
+
* Mass remaining after an impulsive burn: `m * exp(-dv / (isp * g0))`.
|
|
28231
|
+
* @param massKg Total mass before the burn. (kg)
|
|
28232
|
+
* @param deltaVMs Delta-V of the burn. (m/s)
|
|
28233
|
+
* @param ispS Specific impulse of the propulsion system. (s)
|
|
28234
|
+
* @returns Total mass after the burn. (kg)
|
|
28235
|
+
*/
|
|
28236
|
+
static massAfterBurn(massKg, deltaVMs, ispS) {
|
|
28237
|
+
return massKg * Math.exp(-deltaVMs / (ispS * _PropellantBudget.g0));
|
|
28238
|
+
}
|
|
28239
|
+
/**
|
|
28240
|
+
* Propellant consumed by an impulsive burn.
|
|
28241
|
+
* @param massKg Total mass before the burn. (kg)
|
|
28242
|
+
* @param deltaVMs Delta-V of the burn. (m/s)
|
|
28243
|
+
* @param ispS Specific impulse of the propulsion system. (s)
|
|
28244
|
+
* @returns Propellant mass consumed. (kg)
|
|
28245
|
+
*/
|
|
28246
|
+
static propellantUsed(massKg, deltaVMs, ispS) {
|
|
28247
|
+
return massKg - _PropellantBudget.massAfterBurn(massKg, deltaVMs, ispS);
|
|
28248
|
+
}
|
|
28249
|
+
/**
|
|
28250
|
+
* Delta-V available before the propellant is exhausted:
|
|
28251
|
+
* `isp * g0 * ln(m / dry)`.
|
|
28252
|
+
* @param massKg Current total mass. (kg)
|
|
28253
|
+
* @param dryMassKg Dry mass (no propellant). (kg)
|
|
28254
|
+
* @param ispS Specific impulse of the propulsion system. (s)
|
|
28255
|
+
* @returns Remaining delta-V. (m/s)
|
|
28256
|
+
*/
|
|
28257
|
+
static deltaVRemaining(massKg, dryMassKg, ispS) {
|
|
28258
|
+
return ispS * _PropellantBudget.g0 * Math.log(massKg / dryMassKg);
|
|
28259
|
+
}
|
|
28260
|
+
};
|
|
28261
|
+
|
|
27642
28262
|
// src/maneuver/Waypoint.ts
|
|
27643
28263
|
var Waypoint = class _Waypoint {
|
|
27644
28264
|
epoch;
|
|
@@ -27827,6 +28447,11 @@ var TwoBurnOrbitTransfer = class _TwoBurnOrbitTransfer {
|
|
|
27827
28447
|
this.vTransB = vTransB;
|
|
27828
28448
|
this.tTrans = tTrans;
|
|
27829
28449
|
}
|
|
28450
|
+
vInit;
|
|
28451
|
+
vFinal;
|
|
28452
|
+
vTransA;
|
|
28453
|
+
vTransB;
|
|
28454
|
+
tTrans;
|
|
27830
28455
|
/**
|
|
27831
28456
|
* Calculates the parameters for a Hohmann transfer orbit between two circular orbits.
|
|
27832
28457
|
* @param rInit The initial radius of the orbit. (km)
|
|
@@ -27841,6 +28466,55 @@ var TwoBurnOrbitTransfer = class _TwoBurnOrbitTransfer {
|
|
|
27841
28466
|
const tTrans = Math.PI * Math.sqrt((rInit + rFinal) ** 3 / (8 * Earth.mu));
|
|
27842
28467
|
return new _TwoBurnOrbitTransfer(vInit, vFinal, vTransA, vTransB, tTrans);
|
|
27843
28468
|
}
|
|
28469
|
+
/**
|
|
28470
|
+
* Calculates a Hohmann transfer between two circular orbits with an optional
|
|
28471
|
+
* inclination change folded into one of the two burns as a single combined
|
|
28472
|
+
* impulse.
|
|
28473
|
+
*
|
|
28474
|
+
* The plane change is cheapest where the orbital velocity is lowest. For a
|
|
28475
|
+
* raising transfer that is the second burn (at rFinal); for a lowering
|
|
28476
|
+
* transfer it is the first burn (at rInit). The plane change is auto-assigned
|
|
28477
|
+
* to the cheaper burn and the assignment is reported in the result. A
|
|
28478
|
+
* combined burn costs `sqrt(v1^2 + v2^2 - 2 * v1 * v2 * cos(deltaInc))`
|
|
28479
|
+
* (law of cosines between the pre- and post-burn velocity vectors).
|
|
28480
|
+
* @param rInit The initial orbit radius. (km)
|
|
28481
|
+
* @param rFinal The final orbit radius. (km)
|
|
28482
|
+
* @param deltaIncRad The inclination change. (rad)
|
|
28483
|
+
* @returns Per-burn delta-V magnitudes, total, transfer time, and the plane-change assignment.
|
|
28484
|
+
*/
|
|
28485
|
+
static hohmannTransferWithPlaneChange(rInit, rFinal, deltaIncRad) {
|
|
28486
|
+
const vInit = Math.sqrt(Earth.mu / rInit);
|
|
28487
|
+
const vFinal = Math.sqrt(Earth.mu / rFinal);
|
|
28488
|
+
const vTransInit = Math.sqrt(Earth.mu * (2 / rInit - 2 / (rInit + rFinal)));
|
|
28489
|
+
const vTransFinal = Math.sqrt(Earth.mu * (2 / rFinal - 2 / (rInit + rFinal)));
|
|
28490
|
+
const tTrans = Math.PI * Math.sqrt((rInit + rFinal) ** 3 / (8 * Earth.mu));
|
|
28491
|
+
const combined = (v1, v2) => Math.sqrt(v1 ** 2 + v2 ** 2 - 2 * v1 * v2 * Math.cos(deltaIncRad));
|
|
28492
|
+
let planeChangeBurn;
|
|
28493
|
+
let deltaV1;
|
|
28494
|
+
let deltaV2;
|
|
28495
|
+
if (deltaIncRad === 0) {
|
|
28496
|
+
planeChangeBurn = "none";
|
|
28497
|
+
deltaV1 = Math.abs(vTransInit - vInit);
|
|
28498
|
+
deltaV2 = Math.abs(vFinal - vTransFinal);
|
|
28499
|
+
} else if (rFinal >= rInit) {
|
|
28500
|
+
planeChangeBurn = "burn2";
|
|
28501
|
+
deltaV1 = Math.abs(vTransInit - vInit);
|
|
28502
|
+
deltaV2 = combined(vTransFinal, vFinal);
|
|
28503
|
+
} else {
|
|
28504
|
+
planeChangeBurn = "burn1";
|
|
28505
|
+
deltaV1 = combined(vInit, vTransInit);
|
|
28506
|
+
deltaV2 = Math.abs(vFinal - vTransFinal);
|
|
28507
|
+
}
|
|
28508
|
+
return {
|
|
28509
|
+
vInit,
|
|
28510
|
+
vFinal,
|
|
28511
|
+
deltaV1,
|
|
28512
|
+
deltaV2,
|
|
28513
|
+
deltaVTotal: deltaV1 + deltaV2,
|
|
28514
|
+
tTrans,
|
|
28515
|
+
planeChangeBurn
|
|
28516
|
+
};
|
|
28517
|
+
}
|
|
27844
28518
|
// / Return the total delta-velocity magnitude for both maneuvers _(km/s)_.
|
|
27845
28519
|
get deltaV() {
|
|
27846
28520
|
return Math.abs(this.vTransA) + Math.abs(this.vTransB);
|
|
@@ -27879,6 +28553,7 @@ var ChebyshevCompressor = class _ChebyshevCompressor {
|
|
|
27879
28553
|
constructor(interpolator_) {
|
|
27880
28554
|
this.interpolator_ = interpolator_;
|
|
27881
28555
|
}
|
|
28556
|
+
interpolator_;
|
|
27882
28557
|
/** Returns the cosine of π times x. */
|
|
27883
28558
|
static cosPi_(x) {
|
|
27884
28559
|
return Math.cos(Math.PI * x);
|
|
@@ -28119,6 +28794,8 @@ var SimpleLinearRegression = class _SimpleLinearRegression {
|
|
|
28119
28794
|
this.ys = ys;
|
|
28120
28795
|
this.update();
|
|
28121
28796
|
}
|
|
28797
|
+
xs;
|
|
28798
|
+
ys;
|
|
28122
28799
|
/** Line slope (m in y = mx + b). */
|
|
28123
28800
|
slope_ = 0;
|
|
28124
28801
|
/** Y-axis intercept (b in y = mx + b). */
|
|
@@ -29317,8 +29994,7 @@ var LaunchTrajectoryGenerator = class _LaunchTrajectoryGenerator {
|
|
|
29317
29994
|
azimuthRad,
|
|
29318
29995
|
dr
|
|
29319
29996
|
);
|
|
29320
|
-
const
|
|
29321
|
-
const pos = _LaunchTrajectoryGenerator.geodeticToEci_(lat, lon, alt, gmst);
|
|
29997
|
+
const pos = _LaunchTrajectoryGenerator.geodeticToJ2000_(lat, lon, alt, epoch);
|
|
29322
29998
|
positions.push({ epoch, pos });
|
|
29323
29999
|
}
|
|
29324
30000
|
for (let i = 0; i < positions.length; i++) {
|
|
@@ -29416,20 +30092,17 @@ var LaunchTrajectoryGenerator = class _LaunchTrajectoryGenerator {
|
|
|
29416
30092
|
return { lat, lon };
|
|
29417
30093
|
}
|
|
29418
30094
|
/**
|
|
29419
|
-
* Convert geodetic coordinates to
|
|
29420
|
-
*
|
|
30095
|
+
* Convert geodetic coordinates to a J2000 position.
|
|
30096
|
+
*
|
|
30097
|
+
* The point is placed on the WGS84 ellipsoid in ITRF and rotated into J2000 with
|
|
30098
|
+
* precession, nutation and sidereal time, so the trajectory starts on the same launch
|
|
30099
|
+
* site a `GroundObject` at those coordinates reports. (A spherical Earth rotated by GMST
|
|
30100
|
+
* alone, the earlier model, is a TEME position on the wrong surface: the pad sat ~17 km
|
|
30101
|
+
* off on the sphere plus up to ~40 km of precession and nutation since J2000.)
|
|
29421
30102
|
*/
|
|
29422
|
-
static
|
|
29423
|
-
const
|
|
29424
|
-
|
|
29425
|
-
const cosLon = Math.cos(lonRad + gmst);
|
|
29426
|
-
const sinLon = Math.sin(lonRad + gmst);
|
|
29427
|
-
const r = Earth.radiusMean + altKm;
|
|
29428
|
-
return new Vector3D(
|
|
29429
|
-
r * cosLat * cosLon,
|
|
29430
|
-
r * cosLat * sinLon,
|
|
29431
|
-
r * sinLat
|
|
29432
|
-
);
|
|
30103
|
+
static geodeticToJ2000_(latRad, lonRad, altKm, epoch) {
|
|
30104
|
+
const lonWrapped = Math.atan2(Math.sin(lonRad), Math.cos(lonRad));
|
|
30105
|
+
return new Geodetic(latRad, lonWrapped, altKm).toITRF(epoch).toJ2000().position;
|
|
29433
30106
|
}
|
|
29434
30107
|
/**
|
|
29435
30108
|
* Compute the orbital velocity vector at the insertion point.
|
|
@@ -29550,389 +30223,174 @@ var LaunchTrajectoryGenerator = class _LaunchTrajectoryGenerator {
|
|
|
29550
30223
|
}
|
|
29551
30224
|
};
|
|
29552
30225
|
|
|
29553
|
-
// src/orbit-design/
|
|
29554
|
-
var
|
|
29555
|
-
|
|
29556
|
-
|
|
29557
|
-
|
|
29558
|
-
|
|
29559
|
-
|
|
29560
|
-
|
|
29561
|
-
|
|
29562
|
-
// degrees
|
|
29563
|
-
sat_;
|
|
29564
|
-
goalParams_;
|
|
29565
|
-
now_;
|
|
29566
|
-
goalDirection_;
|
|
29567
|
-
debugCallback_;
|
|
29568
|
-
currentParams_;
|
|
29569
|
-
lastLatitude_ = null;
|
|
29570
|
-
currentDirection_ = null;
|
|
29571
|
-
constructor(sat, goalLat, goalLon, goalDirection, now, goalAlt, raanOffset = 0, options) {
|
|
29572
|
-
this.sat_ = sat;
|
|
29573
|
-
this.now_ = now;
|
|
29574
|
-
this.goalDirection_ = goalDirection;
|
|
29575
|
-
this.debugCallback_ = options?.debugCallback;
|
|
29576
|
-
this.goalParams_ = {
|
|
29577
|
-
meanAnomaly: 0,
|
|
29578
|
-
argOfPerigee: sat.argOfPerigee,
|
|
29579
|
-
raan: sat.rightAscension + raanOffset,
|
|
29580
|
-
altitude: goalAlt || 0,
|
|
29581
|
-
latitude: goalLat,
|
|
29582
|
-
longitude: goalLon
|
|
30226
|
+
// src/orbit-design/LaunchWindow.ts
|
|
30227
|
+
var LaunchWindowFinder = class _LaunchWindowFinder {
|
|
30228
|
+
options_;
|
|
30229
|
+
constructor(options) {
|
|
30230
|
+
this.options_ = {
|
|
30231
|
+
searchHours: 24,
|
|
30232
|
+
coarseStepSec: 60,
|
|
30233
|
+
fineStepSec: 1,
|
|
30234
|
+
...options
|
|
29583
30235
|
};
|
|
29584
|
-
this.currentParams_ = this.getCurrentOrbitParams_();
|
|
29585
30236
|
}
|
|
29586
30237
|
/**
|
|
29587
|
-
*
|
|
29588
|
-
*
|
|
30238
|
+
* Whether the site can inject directly into the desired inclination: the
|
|
30239
|
+
* orbit plane must reach the site's latitude, i.e. |sin(lat)| <= sin(inc).
|
|
29589
30240
|
*/
|
|
29590
|
-
|
|
29591
|
-
|
|
29592
|
-
|
|
29593
|
-
|
|
29594
|
-
this.debug_(`Original perigee position: ${perigeeParams.latitude} - ${perigeeParams.longitude} - ${perigeeParams.altitude}`);
|
|
29595
|
-
this.updateOrbitWithoutDirectionCheck_({
|
|
29596
|
-
meanAnomaly: 0
|
|
29597
|
-
});
|
|
29598
|
-
this.debug_(`Positioned at initial perigee: ${this.currentParams_.latitude} - ${this.currentParams_.longitude} - ${this.currentParams_.altitude}`);
|
|
29599
|
-
let bestArgPerigee = this.currentParams_.argOfPerigee;
|
|
29600
|
-
let bestLatError = Infinity;
|
|
29601
|
-
for (let argPer = 0; argPer < 360; argPer += 0.25) {
|
|
29602
|
-
this.updateOrbitWithoutDirectionCheck_({
|
|
29603
|
-
meanAnomaly: 0,
|
|
29604
|
-
argOfPerigee: argPer
|
|
29605
|
-
});
|
|
29606
|
-
const latError = Math.abs(this.currentParams_.latitude - perigeeParams.latitude);
|
|
29607
|
-
if (latError < bestLatError) {
|
|
29608
|
-
bestLatError = latError;
|
|
29609
|
-
bestArgPerigee = argPer;
|
|
29610
|
-
this.debug_(`New best arg perigee: ${argPer} with lat error ${latError}`);
|
|
29611
|
-
}
|
|
29612
|
-
if (latError <= _OrbitFinder.MAX_LAT_ERROR) {
|
|
29613
|
-
break;
|
|
29614
|
-
}
|
|
29615
|
-
}
|
|
29616
|
-
this.updateOrbitWithoutDirectionCheck_({
|
|
29617
|
-
meanAnomaly: 0,
|
|
29618
|
-
argOfPerigee: bestArgPerigee
|
|
29619
|
-
});
|
|
29620
|
-
this.debug_(`After arg perigee adjustment: ${this.currentParams_.latitude} - ${this.currentParams_.longitude} - ${this.currentParams_.altitude}`);
|
|
29621
|
-
this.lastLatitude_ = null;
|
|
29622
|
-
this.currentDirection_ = null;
|
|
29623
|
-
const finalMeanAResult = this.meanACalcLoop_(this.goalDirection_);
|
|
29624
|
-
if (finalMeanAResult !== 1 /* Success */) {
|
|
29625
|
-
return ["Error", "Failed to find final target position"];
|
|
29626
|
-
}
|
|
29627
|
-
const successfulDirection = this.currentDirection_;
|
|
29628
|
-
const newRaan = this.linearSearchRaan_();
|
|
29629
|
-
this.currentDirection_ = successfulDirection;
|
|
29630
|
-
const finalSuccess = this.updateOrbit_({
|
|
29631
|
-
meanAnomaly: this.currentParams_.meanAnomaly,
|
|
29632
|
-
raan: newRaan,
|
|
29633
|
-
argOfPerigee: bestArgPerigee
|
|
29634
|
-
});
|
|
29635
|
-
if (!finalSuccess) {
|
|
29636
|
-
return ["Error", "Final position adjustment failed"];
|
|
29637
|
-
}
|
|
29638
|
-
this.debug_(`Final position: ${this.currentParams_.latitude}, ${this.currentParams_.longitude}, ${this.currentParams_.altitude}`);
|
|
29639
|
-
this.debug_(`Target altitude: ${this.goalParams_.altitude}, Current altitude: ${this.currentParams_.altitude}`);
|
|
29640
|
-
} else {
|
|
29641
|
-
const result = this.meanACalcLoop_(this.goalDirection_);
|
|
29642
|
-
if (result !== 1 /* Success */) {
|
|
29643
|
-
return ["Error", `Failed to find solution with ${this.goalDirection_} bound direction`];
|
|
29644
|
-
}
|
|
29645
|
-
const meanAnomalySuccess = this.updateOrbit_({
|
|
29646
|
-
meanAnomaly: this.currentParams_.meanAnomaly
|
|
29647
|
-
});
|
|
29648
|
-
if (!meanAnomalySuccess) {
|
|
29649
|
-
return ["Error", "Mean anomaly adjustment resulted in incorrect direction"];
|
|
29650
|
-
}
|
|
29651
|
-
const successfulDirection = this.currentDirection_;
|
|
29652
|
-
const newRaan = this.linearSearchRaan_();
|
|
29653
|
-
this.currentDirection_ = successfulDirection;
|
|
29654
|
-
const combinedSuccess = this.updateOrbit_({
|
|
29655
|
-
meanAnomaly: this.currentParams_.meanAnomaly,
|
|
29656
|
-
raan: newRaan
|
|
29657
|
-
});
|
|
29658
|
-
if (!combinedSuccess) {
|
|
29659
|
-
return ["Error", "Combined mean anomaly and RAAN adjustment failed"];
|
|
29660
|
-
}
|
|
29661
|
-
}
|
|
29662
|
-
return [
|
|
29663
|
-
this.generateTle1_(),
|
|
29664
|
-
this.generateTle2_(this.currentParams_)
|
|
29665
|
-
];
|
|
29666
|
-
} catch (error) {
|
|
29667
|
-
this.debug_(`Error in rotateOrbitToLatLon: ${error.message}`);
|
|
29668
|
-
return ["Error", error.message];
|
|
29669
|
-
}
|
|
29670
|
-
}
|
|
29671
|
-
debug_(message) {
|
|
29672
|
-
if (this.debugCallback_) {
|
|
29673
|
-
this.debugCallback_(message);
|
|
30241
|
+
isGeometryPossible() {
|
|
30242
|
+
const sinInc = Math.sin(this.options_.inclination * DEG2RAD);
|
|
30243
|
+
if (sinInc === 0) {
|
|
30244
|
+
return this.options_.siteLat === 0;
|
|
29674
30245
|
}
|
|
30246
|
+
return Math.abs(Math.sin(this.options_.siteLat * DEG2RAD) / sinInc) <= 1;
|
|
29675
30247
|
}
|
|
29676
|
-
|
|
29677
|
-
|
|
29678
|
-
|
|
29679
|
-
|
|
29680
|
-
|
|
29681
|
-
|
|
29682
|
-
|
|
29683
|
-
|
|
29684
|
-
|
|
29685
|
-
|
|
29686
|
-
const
|
|
29687
|
-
|
|
29688
|
-
|
|
29689
|
-
}
|
|
29690
|
-
const positionEci = Sgp4.propagate(this.sat_.satrec, m).position;
|
|
29691
|
-
const { lat, lon, alt } = eci2lla(positionEci, gmst);
|
|
29692
|
-
return {
|
|
29693
|
-
meanAnomaly: this.sat_.meanAnomaly,
|
|
29694
|
-
argOfPerigee: this.sat_.argOfPerigee,
|
|
29695
|
-
raan: this.sat_.rightAscension,
|
|
29696
|
-
altitude: alt,
|
|
29697
|
-
latitude: lat,
|
|
29698
|
-
longitude: lon
|
|
29699
|
-
};
|
|
30248
|
+
/**
|
|
30249
|
+
* RAAN of the (only) plane with the configured inclination that contains the
|
|
30250
|
+
* launch site at the given time on the configured leg. (deg, 0-360)
|
|
30251
|
+
*/
|
|
30252
|
+
achievableRaan(time) {
|
|
30253
|
+
const inc = this.options_.inclination * DEG2RAD;
|
|
30254
|
+
const lat = this.options_.siteLat * DEG2RAD;
|
|
30255
|
+
const sinU = Math.sin(lat) / Math.sin(inc);
|
|
30256
|
+
const u = this.options_.direction === "N" ? Math.asin(sinU) : Math.PI - Math.asin(sinU);
|
|
30257
|
+
const nodeToSite = Math.atan2(Math.sin(u) * Math.cos(inc), Math.cos(u));
|
|
30258
|
+
const gmst = Sgp4.gstime(_LaunchWindowFinder.julianDate_(time));
|
|
30259
|
+
const lst = gmst + this.options_.siteLon * DEG2RAD;
|
|
30260
|
+
return _LaunchWindowFinder.normalizeDeg_((lst - nodeToSite) * RAD2DEG);
|
|
29700
30261
|
}
|
|
29701
|
-
|
|
29702
|
-
|
|
29703
|
-
|
|
29704
|
-
|
|
30262
|
+
/**
|
|
30263
|
+
* Scans the search window for the launch time minimizing |ΔRAAN| to the
|
|
30264
|
+
* target plane, then refines around the best coarse sample. Returns null
|
|
30265
|
+
* when the geometry is impossible (inclination below site latitude).
|
|
30266
|
+
*/
|
|
30267
|
+
findBestLaunchTime() {
|
|
30268
|
+
if (!this.isGeometryPossible()) {
|
|
29705
30269
|
return null;
|
|
29706
30270
|
}
|
|
29707
|
-
|
|
29708
|
-
|
|
29709
|
-
|
|
29710
|
-
|
|
29711
|
-
this.debug_(`Current latitude: ${this.lastLatitude_} - New latitude: ${newLat} - Direction: ${direction}`);
|
|
29712
|
-
this.lastLatitude_ = newLat;
|
|
29713
|
-
this.debug_(`New direction: ${direction}`);
|
|
29714
|
-
return direction;
|
|
29715
|
-
}
|
|
29716
|
-
isCorrectDirection_() {
|
|
29717
|
-
this.debug_(`Current direction: ${this.currentDirection_} - Goal direction: ${this.goalDirection_}`);
|
|
29718
|
-
return this.currentDirection_ === this.goalDirection_;
|
|
29719
|
-
}
|
|
29720
|
-
updateOrbit_(newParams) {
|
|
29721
|
-
const tle1 = this.generateTle1_();
|
|
29722
|
-
const tle2 = this.generateTle2_(newParams);
|
|
29723
|
-
const satrec = Sgp4.createSatrec(tle1, tle2);
|
|
29724
|
-
if (!satrec) {
|
|
29725
|
-
throw new Error("Invalid orbit parameters");
|
|
29726
|
-
}
|
|
29727
|
-
const { m, gmst } = this.calculateTimeVariables_(this.now_, satrec);
|
|
29728
|
-
if (m === null) {
|
|
29729
|
-
throw new Error("Invalid time variables");
|
|
29730
|
-
}
|
|
29731
|
-
const positionEci = Sgp4.propagate(satrec, m).position;
|
|
29732
|
-
const { lat, lon, alt } = eci2lla(positionEci, gmst);
|
|
29733
|
-
const newDirection = this.determineDirection_(lat);
|
|
29734
|
-
if (newDirection !== null) {
|
|
29735
|
-
this.currentDirection_ = newDirection;
|
|
29736
|
-
}
|
|
29737
|
-
this.currentParams_ = {
|
|
29738
|
-
...this.currentParams_,
|
|
29739
|
-
...newParams,
|
|
29740
|
-
latitude: lat,
|
|
29741
|
-
longitude: lon,
|
|
29742
|
-
altitude: alt
|
|
29743
|
-
};
|
|
29744
|
-
return this.isCorrectDirection_();
|
|
29745
|
-
}
|
|
29746
|
-
meanACalcLoop_(direction) {
|
|
29747
|
-
const startVal = direction === "N" ? 0 : 180;
|
|
29748
|
-
const endVal = direction === "N" ? 360 : 540;
|
|
29749
|
-
for (let posVal = startVal * 10; posVal < endVal * 10; posVal += 0.25) {
|
|
29750
|
-
const normalizedVal = posVal % (360 * 10);
|
|
29751
|
-
const result = this.meanACalc_(normalizedVal, this.now_);
|
|
29752
|
-
if (result === 1 /* Success */) {
|
|
29753
|
-
if (this.currentDirection_ !== direction) {
|
|
29754
|
-
posVal += 20;
|
|
29755
|
-
} else {
|
|
29756
|
-
return 1 /* Success */;
|
|
29757
|
-
}
|
|
29758
|
-
}
|
|
29759
|
-
if (result === 3 /* Far */) {
|
|
29760
|
-
posVal += 100;
|
|
29761
|
-
}
|
|
29762
|
-
if (result === 2 /* Error */) {
|
|
29763
|
-
return 2 /* Error */;
|
|
29764
|
-
}
|
|
29765
|
-
}
|
|
29766
|
-
return 0 /* Near */;
|
|
29767
|
-
}
|
|
29768
|
-
meanACalc_(meana, now) {
|
|
29769
|
-
meana /= 10;
|
|
29770
|
-
meana %= 360;
|
|
29771
|
-
const tle1 = this.generateTle1_();
|
|
29772
|
-
const tle2 = this.generateTle2_({ meanAnomaly: meana });
|
|
29773
|
-
const satrec = Sgp4.createSatrec(tle1, tle2);
|
|
29774
|
-
if (!satrec) {
|
|
29775
|
-
return 2 /* Error */;
|
|
29776
|
-
}
|
|
29777
|
-
const { m, gmst } = this.calculateTimeVariables_(now, satrec);
|
|
29778
|
-
if (m === null) {
|
|
29779
|
-
return 2 /* Error */;
|
|
29780
|
-
}
|
|
29781
|
-
const positionEci = Sgp4.propagate(satrec, m).position;
|
|
29782
|
-
const { lat } = eci2lla(positionEci, gmst);
|
|
29783
|
-
if (this.lastLatitude_ !== null) {
|
|
29784
|
-
if (Math.abs(lat - this.lastLatitude_) > 1e-3) {
|
|
29785
|
-
this.currentDirection_ = lat > this.lastLatitude_ ? "N" : "S";
|
|
29786
|
-
}
|
|
29787
|
-
}
|
|
29788
|
-
this.lastLatitude_ = lat;
|
|
29789
|
-
if (Math.abs(lat - this.goalParams_.latitude) <= _OrbitFinder.MAX_LAT_ERROR) {
|
|
29790
|
-
if (this.currentDirection_ === this.goalDirection_) {
|
|
29791
|
-
this.currentParams_.meanAnomaly = meana;
|
|
29792
|
-
return 1 /* Success */;
|
|
29793
|
-
}
|
|
29794
|
-
}
|
|
29795
|
-
if (Math.abs(lat - this.goalParams_.latitude) > 11) {
|
|
29796
|
-
return 3 /* Far */;
|
|
29797
|
-
}
|
|
29798
|
-
return 0 /* Near */;
|
|
29799
|
-
}
|
|
29800
|
-
linearSearchRaan_() {
|
|
29801
|
-
let bestValue = this.currentParams_.raan;
|
|
30271
|
+
const startMs = this.options_.startTime.getTime();
|
|
30272
|
+
const endMs = startMs + this.options_.searchHours * 3600 * 1e3;
|
|
30273
|
+
const coarseMs = this.options_.coarseStepSec * 1e3;
|
|
30274
|
+
let bestMs = startMs;
|
|
29802
30275
|
let bestError = Infinity;
|
|
29803
|
-
for (let
|
|
29804
|
-
this.
|
|
29805
|
-
|
|
29806
|
-
if (error < Math.abs(bestError)) {
|
|
30276
|
+
for (let ms = startMs; ms <= endMs; ms += coarseMs) {
|
|
30277
|
+
const error = Math.abs(this.raanErrorAt_(new Date(ms)));
|
|
30278
|
+
if (error < bestError) {
|
|
29807
30279
|
bestError = error;
|
|
29808
|
-
|
|
29809
|
-
}
|
|
29810
|
-
if (error < _OrbitFinder.MAX_LON_ERROR) {
|
|
29811
|
-
for (let fineRaan = bestValue - 5; fineRaan <= bestValue + 5; fineRaan += _OrbitFinder.FINE_STEP) {
|
|
29812
|
-
const normalizedRaan = (fineRaan % 360 + 360) % 360;
|
|
29813
|
-
this.updateOrbit_({ raan: normalizedRaan });
|
|
29814
|
-
const fineError = Math.abs(this.calculateError_("raan"));
|
|
29815
|
-
if (fineError < error) {
|
|
29816
|
-
bestValue = normalizedRaan;
|
|
29817
|
-
bestError = fineError;
|
|
29818
|
-
}
|
|
29819
|
-
}
|
|
29820
|
-
return bestValue;
|
|
30280
|
+
bestMs = ms;
|
|
29821
30281
|
}
|
|
29822
30282
|
}
|
|
29823
|
-
|
|
29824
|
-
|
|
29825
|
-
|
|
29826
|
-
|
|
29827
|
-
|
|
29828
|
-
|
|
29829
|
-
|
|
29830
|
-
|
|
29831
|
-
}
|
|
29832
|
-
return diff;
|
|
29833
|
-
}
|
|
29834
|
-
calculateError_(param) {
|
|
29835
|
-
switch (param) {
|
|
29836
|
-
case "meanAnomaly":
|
|
29837
|
-
return this.currentParams_.latitude - this.goalParams_.latitude;
|
|
29838
|
-
case "raan":
|
|
29839
|
-
return this.normalizeAngleDifference_(
|
|
29840
|
-
this.currentParams_.longitude,
|
|
29841
|
-
this.goalParams_.longitude
|
|
29842
|
-
);
|
|
29843
|
-
case "argOfPerigee":
|
|
29844
|
-
this.debug_(`Current altitude: ${this.currentParams_.altitude} - Goal altitude: ${this.goalParams_.altitude}`);
|
|
29845
|
-
return this.currentParams_.altitude - this.goalParams_.altitude;
|
|
29846
|
-
default:
|
|
29847
|
-
return 0;
|
|
29848
|
-
}
|
|
29849
|
-
}
|
|
29850
|
-
updateOrbitWithoutDirectionCheck_(newParams) {
|
|
29851
|
-
const tle1 = this.generateTle1_();
|
|
29852
|
-
const tle2 = this.generateTle2_(newParams);
|
|
29853
|
-
const satrec = Sgp4.createSatrec(tle1, tle2);
|
|
29854
|
-
if (!satrec) {
|
|
29855
|
-
throw new Error("Invalid orbit parameters");
|
|
29856
|
-
}
|
|
29857
|
-
const { m, gmst } = this.calculateTimeVariables_(this.now_, satrec);
|
|
29858
|
-
if (m === null) {
|
|
29859
|
-
throw new Error("Invalid time variables");
|
|
29860
|
-
}
|
|
29861
|
-
const positionEci = Sgp4.propagate(satrec, m).position;
|
|
29862
|
-
const { lat, lon, alt } = eci2lla(positionEci, gmst);
|
|
29863
|
-
this.currentParams_ = {
|
|
29864
|
-
...this.currentParams_,
|
|
29865
|
-
...newParams,
|
|
29866
|
-
latitude: lat,
|
|
29867
|
-
longitude: lon,
|
|
29868
|
-
altitude: alt
|
|
29869
|
-
};
|
|
29870
|
-
}
|
|
29871
|
-
findPerigeePosition_() {
|
|
29872
|
-
let lowestAltitude = Infinity;
|
|
29873
|
-
let perigeeParams = null;
|
|
29874
|
-
for (let meanA = 0; meanA < 360; meanA += 0.05) {
|
|
29875
|
-
const tle1 = this.generateTle1_();
|
|
29876
|
-
const tle2 = this.generateTle2_({ meanAnomaly: meanA });
|
|
29877
|
-
const satrec = Sgp4.createSatrec(tle1, tle2);
|
|
29878
|
-
if (!satrec) {
|
|
29879
|
-
continue;
|
|
29880
|
-
}
|
|
29881
|
-
const { m, gmst } = this.calculateTimeVariables_(this.now_, satrec);
|
|
29882
|
-
if (m === null) {
|
|
29883
|
-
throw new Error("Invalid time variables");
|
|
29884
|
-
}
|
|
29885
|
-
const positionEci = Sgp4.propagate(satrec, m).position;
|
|
29886
|
-
const { lat, lon, alt } = eci2lla(positionEci, gmst);
|
|
29887
|
-
if (alt < lowestAltitude) {
|
|
29888
|
-
lowestAltitude = alt;
|
|
29889
|
-
perigeeParams = {
|
|
29890
|
-
meanAnomaly: meanA,
|
|
29891
|
-
argOfPerigee: this.currentParams_.argOfPerigee,
|
|
29892
|
-
raan: this.currentParams_.raan,
|
|
29893
|
-
altitude: alt,
|
|
29894
|
-
latitude: lat,
|
|
29895
|
-
longitude: lon
|
|
29896
|
-
};
|
|
30283
|
+
const fineMs = this.options_.fineStepSec * 1e3;
|
|
30284
|
+
const refineStart = Math.max(startMs, bestMs - coarseMs);
|
|
30285
|
+
const refineEnd = Math.min(endMs, bestMs + coarseMs);
|
|
30286
|
+
for (let ms = refineStart; ms <= refineEnd; ms += fineMs) {
|
|
30287
|
+
const error = Math.abs(this.raanErrorAt_(new Date(ms)));
|
|
30288
|
+
if (error < bestError) {
|
|
30289
|
+
bestError = error;
|
|
30290
|
+
bestMs = ms;
|
|
29897
30291
|
}
|
|
29898
30292
|
}
|
|
29899
|
-
|
|
29900
|
-
|
|
29901
|
-
|
|
29902
|
-
|
|
29903
|
-
|
|
30293
|
+
const time = new Date(bestMs);
|
|
30294
|
+
const achievableRaan = this.achievableRaan(time);
|
|
30295
|
+
const targetRaan = this.targetRaanAt_(time);
|
|
30296
|
+
return {
|
|
30297
|
+
time,
|
|
30298
|
+
achievableRaan,
|
|
30299
|
+
targetRaan,
|
|
30300
|
+
raanError: _LaunchWindowFinder.normalize180_(achievableRaan - targetRaan)
|
|
30301
|
+
};
|
|
29904
30302
|
}
|
|
29905
30303
|
/**
|
|
29906
|
-
*
|
|
29907
|
-
*
|
|
29908
|
-
*
|
|
29909
|
-
* the full 9-digit canonical value, which would push the TLE past 69 chars
|
|
29910
|
-
* and break Sgp4.createSatrec. The input tle1 always has a valid 5-char
|
|
29911
|
-
* satnum at columns 2-6, and SGP4 itself doesn't care what value lives
|
|
29912
|
-
* there as long as line 1 and line 2 agree — so use that.
|
|
30304
|
+
* Mean RAAN of a satellite at a given time: the TLE epoch value advanced by
|
|
30305
|
+
* the secular nodal drift (J2). Suitable as the `targetRaan` function so the
|
|
30306
|
+
* target plane's regression over the search window is accounted for.
|
|
29913
30307
|
*/
|
|
29914
|
-
|
|
29915
|
-
|
|
30308
|
+
static meanRaanAt(satrec, time) {
|
|
30309
|
+
const minutesSinceEpoch = (_LaunchWindowFinder.julianDate_(time) - satrec.jdsatepoch) * MINUTES_PER_DAY;
|
|
30310
|
+
return _LaunchWindowFinder.normalizeDeg_((satrec.nodeo + satrec.nodedot * minutesSinceEpoch) * RAD2DEG);
|
|
29916
30311
|
}
|
|
29917
|
-
|
|
29918
|
-
|
|
30312
|
+
targetRaanAt_(time) {
|
|
30313
|
+
const target = this.options_.targetRaan;
|
|
30314
|
+
return typeof target === "function" ? target(time) : target;
|
|
29919
30315
|
}
|
|
29920
|
-
|
|
29921
|
-
|
|
29922
|
-
|
|
29923
|
-
|
|
29924
|
-
|
|
29925
|
-
|
|
29926
|
-
|
|
29927
|
-
|
|
29928
|
-
|
|
29929
|
-
|
|
29930
|
-
const
|
|
29931
|
-
|
|
29932
|
-
|
|
29933
|
-
|
|
30316
|
+
raanErrorAt_(time) {
|
|
30317
|
+
return _LaunchWindowFinder.normalize180_(this.achievableRaan(time) - this.targetRaanAt_(time));
|
|
30318
|
+
}
|
|
30319
|
+
static julianDate_(time) {
|
|
30320
|
+
return jday(time.getUTCFullYear(), time.getUTCMonth() + 1, time.getUTCDate(), time.getUTCHours(), time.getUTCMinutes(), time.getUTCSeconds()) + time.getUTCMilliseconds() * MILLISECONDS_TO_DAYS;
|
|
30321
|
+
}
|
|
30322
|
+
static normalizeDeg_(deg) {
|
|
30323
|
+
return (deg % 360 + 360) % 360;
|
|
30324
|
+
}
|
|
30325
|
+
static normalize180_(deg) {
|
|
30326
|
+
const normalized = (deg % 360 + 360) % 360;
|
|
30327
|
+
return normalized >= 180 ? normalized - 360 : normalized;
|
|
30328
|
+
}
|
|
30329
|
+
};
|
|
30330
|
+
|
|
30331
|
+
// src/orbit-design/GroundTrackStateVector.ts
|
|
30332
|
+
var WGS84_A = 6378.137;
|
|
30333
|
+
var WGS84_F = 1 / 298.257223563;
|
|
30334
|
+
var WGS84_E2 = 2 * WGS84_F - WGS84_F * WGS84_F;
|
|
30335
|
+
var MIN_ECCENTRICITY = 1e-3;
|
|
30336
|
+
var groundTrackStateVector = (opts) => {
|
|
30337
|
+
const { semimajorAxisKm: a, inclinationRad: inc, latRad, lonRad, gmstRad, direction } = opts;
|
|
30338
|
+
const e = Math.max(opts.eccentricity, MIN_ECCENTRICITY);
|
|
30339
|
+
const rPerigee = a * (1 - e);
|
|
30340
|
+
const vPerigee = Math.sqrt(Earth.mu / a * ((1 + e) / (1 - e)));
|
|
30341
|
+
const sinLat = Math.sin(latRad);
|
|
30342
|
+
const cosLat = Math.cos(latRad);
|
|
30343
|
+
const nPrime = WGS84_A / Math.sqrt(1 - WGS84_E2 * sinLat * sinLat);
|
|
30344
|
+
const mPrime = nPrime * (1 - WGS84_E2);
|
|
30345
|
+
const c = cosLat * cosLat;
|
|
30346
|
+
const s = sinLat * sinLat;
|
|
30347
|
+
const bCoef = c * nPrime + s * mPrime;
|
|
30348
|
+
const cCoef = c * nPrime * nPrime + s * mPrime * mPrime - rPerigee * rPerigee;
|
|
30349
|
+
const disc = bCoef * bCoef - cCoef;
|
|
30350
|
+
if (disc < 0) {
|
|
30351
|
+
return null;
|
|
30352
|
+
}
|
|
30353
|
+
const height = -bCoef + Math.sqrt(disc);
|
|
30354
|
+
const cosLonE = Math.cos(lonRad);
|
|
30355
|
+
const sinLonE = Math.sin(lonRad);
|
|
30356
|
+
const xEcef = (nPrime + height) * cosLat * cosLonE;
|
|
30357
|
+
const yEcef = (nPrime + height) * cosLat * sinLonE;
|
|
30358
|
+
const zEcef = (mPrime + height) * sinLat;
|
|
30359
|
+
const cosG = Math.cos(gmstRad);
|
|
30360
|
+
const sinG = Math.sin(gmstRad);
|
|
30361
|
+
const position = {
|
|
30362
|
+
x: xEcef * cosG - yEcef * sinG,
|
|
30363
|
+
y: xEcef * sinG + yEcef * cosG,
|
|
30364
|
+
z: zEcef
|
|
30365
|
+
};
|
|
30366
|
+
const rMag = Math.hypot(position.x, position.y, position.z);
|
|
30367
|
+
const rHat = { x: position.x / rMag, y: position.y / rMag, z: position.z / rMag };
|
|
30368
|
+
const geocentricLat = Math.asin(rHat.z);
|
|
30369
|
+
const cosGeoLat = Math.cos(geocentricLat);
|
|
30370
|
+
const sinAz = Math.cos(inc) / cosGeoLat;
|
|
30371
|
+
if (sinAz < -1 || sinAz > 1) {
|
|
30372
|
+
return null;
|
|
29934
30373
|
}
|
|
30374
|
+
const cosAz = (direction === "N" ? 1 : -1) * Math.sqrt(Math.max(0, 1 - sinAz * sinAz));
|
|
30375
|
+
const eastMag = Math.hypot(rHat.x, rHat.y);
|
|
30376
|
+
const eastHat = eastMag > 1e-9 ? { x: -rHat.y / eastMag, y: rHat.x / eastMag, z: 0 } : { x: 1, y: 0, z: 0 };
|
|
30377
|
+
const northHat = {
|
|
30378
|
+
x: rHat.y * eastHat.z - rHat.z * eastHat.y,
|
|
30379
|
+
y: rHat.z * eastHat.x - rHat.x * eastHat.z,
|
|
30380
|
+
z: rHat.x * eastHat.y - rHat.y * eastHat.x
|
|
30381
|
+
};
|
|
30382
|
+
const velocity = {
|
|
30383
|
+
x: vPerigee * (sinAz * eastHat.x + cosAz * northHat.x),
|
|
30384
|
+
y: vPerigee * (sinAz * eastHat.y + cosAz * northHat.y),
|
|
30385
|
+
z: vPerigee * (sinAz * eastHat.z + cosAz * northHat.z)
|
|
30386
|
+
};
|
|
30387
|
+
return { position, velocity };
|
|
30388
|
+
};
|
|
30389
|
+
var semimajorAxisFromMeanMotion = (meanMotionRevPerDay) => {
|
|
30390
|
+
const n = meanMotionRevPerDay * 2 * Math.PI / 86400;
|
|
30391
|
+
return (Earth.mu / (n * n)) ** (1 / 3);
|
|
29935
30392
|
};
|
|
30393
|
+
var perigeeSpeed = (semimajorAxisKm, eccentricity) => Math.sqrt(Earth.mu / semimajorAxisKm * ((1 + eccentricity) / (1 - eccentricity)));
|
|
29936
30394
|
|
|
29937
30395
|
// src/orbit-design/RepeatGroundTrack.ts
|
|
29938
30396
|
var SUN_SYNC_PRECESSION_RAD_PER_SEC = 0.9856 * DEG2RAD / secondsPerDay;
|
|
@@ -30220,6 +30678,688 @@ var RepeatGroundTrack = class _RepeatGroundTrack {
|
|
|
30220
30678
|
return a;
|
|
30221
30679
|
}
|
|
30222
30680
|
};
|
|
30681
|
+
|
|
30682
|
+
// src/external/Sgp4WasmBase.ts
|
|
30683
|
+
var DS50_UTC_JD_EPOCH = 24332815e-1;
|
|
30684
|
+
var ERR_SAT_TEXT_BYTES = 512;
|
|
30685
|
+
var Sgp4WasmBase = class _Sgp4WasmBase {
|
|
30686
|
+
module_ = null;
|
|
30687
|
+
fs_ = null;
|
|
30688
|
+
bindings_ = null;
|
|
30689
|
+
loadPromise_ = null;
|
|
30690
|
+
isDynArrInitialized_ = false;
|
|
30691
|
+
scratchKeyPtr_ = 0;
|
|
30692
|
+
scratchResultPtr_ = 0;
|
|
30693
|
+
/**
|
|
30694
|
+
* Converts a UTC epoch to the Astro Standards ds50UTC time system
|
|
30695
|
+
* (days since 1949 December 31, 00:00 UTC).
|
|
30696
|
+
* @param epoch The epoch to convert.
|
|
30697
|
+
* @returns Days since 1950 UTC.
|
|
30698
|
+
*/
|
|
30699
|
+
static toDs50Utc(epoch) {
|
|
30700
|
+
return epoch.toJulianDate() - DS50_UTC_JD_EPOCH;
|
|
30701
|
+
}
|
|
30702
|
+
/**
|
|
30703
|
+
* Whether the wasm runtime has been loaded and initialized.
|
|
30704
|
+
*/
|
|
30705
|
+
get isLoaded() {
|
|
30706
|
+
return this.module_ !== null;
|
|
30707
|
+
}
|
|
30708
|
+
/**
|
|
30709
|
+
* Raw Emscripten Module escape hatch (`_malloc`, `ccall`, heap views, ...).
|
|
30710
|
+
* @throws {Sgp4WasmError} If the module is not loaded yet.
|
|
30711
|
+
*/
|
|
30712
|
+
get module() {
|
|
30713
|
+
if (!this.module_) {
|
|
30714
|
+
throw new Sgp4WasmError(`${this.constructor.name} is not loaded. Call load() first.`);
|
|
30715
|
+
}
|
|
30716
|
+
return this.module_;
|
|
30717
|
+
}
|
|
30718
|
+
/**
|
|
30719
|
+
* Raw Emscripten virtual filesystem escape hatch.
|
|
30720
|
+
* @throws {Sgp4WasmError} If the module is not loaded yet.
|
|
30721
|
+
*/
|
|
30722
|
+
get fs() {
|
|
30723
|
+
if (!this.fs_) {
|
|
30724
|
+
throw new Sgp4WasmError(`${this.constructor.name} is not loaded. Call load() first.`);
|
|
30725
|
+
}
|
|
30726
|
+
return this.fs_;
|
|
30727
|
+
}
|
|
30728
|
+
/**
|
|
30729
|
+
* Loads and initializes the wasm runtime. Idempotent — repeat calls return
|
|
30730
|
+
* the same in-flight or completed load.
|
|
30731
|
+
* @param options Artifact sources and stdout/stderr hooks. In Node both
|
|
30732
|
+
* artifacts default to files next to this module; in the browser both must
|
|
30733
|
+
* be provided (typically URLs to statically served copies).
|
|
30734
|
+
* @returns This instance, once the runtime is initialized.
|
|
30735
|
+
*/
|
|
30736
|
+
load(options = {}) {
|
|
30737
|
+
this.loadPromise_ ??= this.loadInternal_(options).catch((err) => {
|
|
30738
|
+
this.loadPromise_ = null;
|
|
30739
|
+
throw err;
|
|
30740
|
+
});
|
|
30741
|
+
return this.loadPromise_;
|
|
30742
|
+
}
|
|
30743
|
+
/**
|
|
30744
|
+
* Frees the dynamic array (if initialized) and drops all references to the
|
|
30745
|
+
* wasm runtime so it can be garbage collected. Note Emscripten linear
|
|
30746
|
+
* memory cannot be truly freed; a subsequent load() evaluates a fresh,
|
|
30747
|
+
* isolated runtime.
|
|
30748
|
+
*/
|
|
30749
|
+
dispose() {
|
|
30750
|
+
if (this.isDynArrInitialized_ && this.bindings_) {
|
|
30751
|
+
this.bindings_.freeDynArr();
|
|
30752
|
+
}
|
|
30753
|
+
if (this.module_ && this.scratchKeyPtr_) {
|
|
30754
|
+
this.module_._free(this.scratchKeyPtr_);
|
|
30755
|
+
this.module_._free(this.scratchResultPtr_);
|
|
30756
|
+
}
|
|
30757
|
+
this.scratchKeyPtr_ = 0;
|
|
30758
|
+
this.scratchResultPtr_ = 0;
|
|
30759
|
+
this.isDynArrInitialized_ = false;
|
|
30760
|
+
this.module_ = null;
|
|
30761
|
+
this.fs_ = null;
|
|
30762
|
+
this.bindings_ = null;
|
|
30763
|
+
this.loadPromise_ = null;
|
|
30764
|
+
}
|
|
30765
|
+
// TLE tree management
|
|
30766
|
+
/**
|
|
30767
|
+
* Adds a single TLE to the TLE tree (`TleAddSatFrLines_wasm`).
|
|
30768
|
+
* @param line1 Line 1 of the TLE.
|
|
30769
|
+
* @param line2 Line 2 of the TLE.
|
|
30770
|
+
* @returns The satKey of the added TLE.
|
|
30771
|
+
*/
|
|
30772
|
+
addSat(line1, line2) {
|
|
30773
|
+
const satKey = this.fn_().tleAddSatFrLines(line1, line2);
|
|
30774
|
+
if (satKey <= 0n) {
|
|
30775
|
+
throw new Sgp4WasmError(`TleAddSatFrLines_wasm failed for line1 "${line1}" (code ${satKey})`, Number(satKey));
|
|
30776
|
+
}
|
|
30777
|
+
return satKey;
|
|
30778
|
+
}
|
|
30779
|
+
/**
|
|
30780
|
+
* Adds any number of TLEs from newline-separated text
|
|
30781
|
+
* (`TleAddSatsFrLines_wasm`).
|
|
30782
|
+
*
|
|
30783
|
+
* The library adds TLEs individually: entries it fails to parse come back
|
|
30784
|
+
* as `0n` in the returned array (positions match the input order), while
|
|
30785
|
+
* the rest are added normally.
|
|
30786
|
+
* @param tleText Newline-separated TLE lines.
|
|
30787
|
+
* @returns One satKey per input TLE; `0n` marks a failed entry.
|
|
30788
|
+
*/
|
|
30789
|
+
addSats(tleText) {
|
|
30790
|
+
const m = this.module;
|
|
30791
|
+
const numSats = _Sgp4WasmBase.countTles_(tleText);
|
|
30792
|
+
if (numSats === 0) {
|
|
30793
|
+
return [];
|
|
30794
|
+
}
|
|
30795
|
+
const textBytes = new TextEncoder().encode(tleText);
|
|
30796
|
+
return this.withMalloc_(textBytes.length + 1, (textPtr) => this.withMalloc_(numSats * 8, (keysPtr) => this.withMalloc_(ERR_SAT_TEXT_BYTES, (errPtr) => {
|
|
30797
|
+
m.HEAPU8.set(textBytes, textPtr);
|
|
30798
|
+
m.HEAPU8[textPtr + textBytes.length] = 0;
|
|
30799
|
+
m.HEAPU8.fill(0, errPtr, errPtr + ERR_SAT_TEXT_BYTES);
|
|
30800
|
+
const ret = this.fn_().tleAddSatsFrLines(textPtr, keysPtr, errPtr);
|
|
30801
|
+
if (ret !== 0) {
|
|
30802
|
+
const errLine = m.UTF8ToString(errPtr, ERR_SAT_TEXT_BYTES);
|
|
30803
|
+
throw new Sgp4WasmError(`TleAddSatsFrLines_wasm failed (code ${ret}); offending TLE line 1: "${errLine}"`, ret);
|
|
30804
|
+
}
|
|
30805
|
+
return this.readI64Array_(keysPtr, numSats);
|
|
30806
|
+
})));
|
|
30807
|
+
}
|
|
30808
|
+
/**
|
|
30809
|
+
* Adds any number of TLEs through the Emscripten virtual filesystem
|
|
30810
|
+
* (`TleLoadFileVFS_wasm`). The text is written to the VFS `inFile`, and
|
|
30811
|
+
* the satKeys the library writes to `outFile` are read back.
|
|
30812
|
+
* @param tleText Newline-separated TLE lines.
|
|
30813
|
+
* @returns The satKeys of all added TLEs.
|
|
30814
|
+
*/
|
|
30815
|
+
loadTlesVfs(tleText) {
|
|
30816
|
+
this.fn_();
|
|
30817
|
+
const vfs = this.fs;
|
|
30818
|
+
try {
|
|
30819
|
+
vfs.writeFile("inFile", tleText);
|
|
30820
|
+
const ret = this.fn_().tleLoadFileVfs("inFile");
|
|
30821
|
+
if (ret <= 0) {
|
|
30822
|
+
throw new Sgp4WasmError(`TleLoadFileVFS_wasm failed (code ${ret})`, ret);
|
|
30823
|
+
}
|
|
30824
|
+
const outText = vfs.readFile("outFile", { encoding: "utf8" });
|
|
30825
|
+
return outText.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => BigInt(line));
|
|
30826
|
+
} finally {
|
|
30827
|
+
for (const file of ["inFile", "outFile"]) {
|
|
30828
|
+
if (vfs.analyzePath(file).exists) {
|
|
30829
|
+
vfs.unlink(file);
|
|
30830
|
+
}
|
|
30831
|
+
}
|
|
30832
|
+
}
|
|
30833
|
+
}
|
|
30834
|
+
/**
|
|
30835
|
+
* Removes TLEs from the TLE tree (`TleRemoveSats_wasm`).
|
|
30836
|
+
* @param satKeys The satKeys to remove.
|
|
30837
|
+
*/
|
|
30838
|
+
removeSats(satKeys) {
|
|
30839
|
+
this.runKeysWithErrSat_((keysPtr, numKeys, errPtr) => this.fn_().tleRemoveSats(keysPtr, numKeys, errPtr), satKeys, "TleRemoveSats_wasm");
|
|
30840
|
+
}
|
|
30841
|
+
// SGP4 object lifecycle
|
|
30842
|
+
/**
|
|
30843
|
+
* Initializes SGP4 objects for previously added TLEs
|
|
30844
|
+
* (`Sgp4InitSats_wasm`). Must be called after adding TLEs and before
|
|
30845
|
+
* propagating them.
|
|
30846
|
+
* @param satKeys The satKeys to initialize.
|
|
30847
|
+
*/
|
|
30848
|
+
initSats(satKeys) {
|
|
30849
|
+
this.runKeysWithErrSat_((keysPtr, numKeys, errPtr) => this.fn_().sgp4InitSats(keysPtr, numKeys, errPtr), satKeys, "Sgp4InitSats_wasm");
|
|
30850
|
+
}
|
|
30851
|
+
/**
|
|
30852
|
+
* Removes initialized SGP4 objects (`Sgp4RemoveSats_wasm`).
|
|
30853
|
+
* @param satKeys The satKeys to remove.
|
|
30854
|
+
*/
|
|
30855
|
+
uninitSats(satKeys) {
|
|
30856
|
+
this.runKeysWithErrSat_((keysPtr, numKeys, errPtr) => this.fn_().sgp4RemoveSats(keysPtr, numKeys, errPtr), satKeys, "Sgp4RemoveSats_wasm");
|
|
30857
|
+
}
|
|
30858
|
+
// Propagation by satKey
|
|
30859
|
+
/**
|
|
30860
|
+
* Propagates satKeys with geodetic output (`Sgp4Prop_wasm`).
|
|
30861
|
+
* @param satKeys The satKeys to propagate (must be initialized).
|
|
30862
|
+
* @param startTimeMse Start time in minutes since each TLE's epoch.
|
|
30863
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
30864
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
30865
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
30866
|
+
*/
|
|
30867
|
+
propagate(satKeys, startTimeMse, propsPerSat, stepSizeMin) {
|
|
30868
|
+
const buf = this.propagateKeys_(
|
|
30869
|
+
(...args) => this.fn_().sgp4Prop(...args),
|
|
30870
|
+
satKeys,
|
|
30871
|
+
startTimeMse,
|
|
30872
|
+
propsPerSat,
|
|
30873
|
+
stepSizeMin,
|
|
30874
|
+
11,
|
|
30875
|
+
"Sgp4Prop_wasm"
|
|
30876
|
+
);
|
|
30877
|
+
return this.parseRecords_(buf, satKeys.length, propsPerSat, 11);
|
|
30878
|
+
}
|
|
30879
|
+
/**
|
|
30880
|
+
* Propagates satKeys, position/velocity only (`Sgp4PropPosVel_wasm`).
|
|
30881
|
+
* @param satKeys The satKeys to propagate (must be initialized).
|
|
30882
|
+
* @param startTimeMse Start time in minutes since each TLE's epoch.
|
|
30883
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
30884
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
30885
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
30886
|
+
*/
|
|
30887
|
+
propagatePosVel(satKeys, startTimeMse, propsPerSat, stepSizeMin) {
|
|
30888
|
+
const buf = this.propagateKeys_(
|
|
30889
|
+
(...args) => this.fn_().sgp4PropPosVel(...args),
|
|
30890
|
+
satKeys,
|
|
30891
|
+
startTimeMse,
|
|
30892
|
+
propsPerSat,
|
|
30893
|
+
stepSizeMin,
|
|
30894
|
+
8,
|
|
30895
|
+
"Sgp4PropPosVel_wasm"
|
|
30896
|
+
);
|
|
30897
|
+
return this.parseRecords_(buf, satKeys.length, propsPerSat, 8);
|
|
30898
|
+
}
|
|
30899
|
+
/**
|
|
30900
|
+
* Propagates satKeys from an absolute time with geodetic output
|
|
30901
|
+
* (`Sgp4PropDs50Utc_wasm`).
|
|
30902
|
+
* @param satKeys The satKeys to propagate (must be initialized).
|
|
30903
|
+
* @param startTimeDs50Utc Start time in days since 1950 UTC (see {@link toDs50Utc}).
|
|
30904
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
30905
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
30906
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
30907
|
+
*/
|
|
30908
|
+
propagateDs50Utc(satKeys, startTimeDs50Utc, propsPerSat, stepSizeMin) {
|
|
30909
|
+
const buf = this.propagateKeys_(
|
|
30910
|
+
(...args) => this.fn_().sgp4PropDs50Utc(...args),
|
|
30911
|
+
satKeys,
|
|
30912
|
+
startTimeDs50Utc,
|
|
30913
|
+
propsPerSat,
|
|
30914
|
+
stepSizeMin,
|
|
30915
|
+
11,
|
|
30916
|
+
"Sgp4PropDs50Utc_wasm"
|
|
30917
|
+
);
|
|
30918
|
+
return this.parseRecords_(buf, satKeys.length, propsPerSat, 11);
|
|
30919
|
+
}
|
|
30920
|
+
/**
|
|
30921
|
+
* Propagates satKeys from an absolute time, position/velocity only
|
|
30922
|
+
* (`Sgp4PropDs50UtcPosVel_wasm`).
|
|
30923
|
+
* @param satKeys The satKeys to propagate (must be initialized).
|
|
30924
|
+
* @param startTimeDs50Utc Start time in days since 1950 UTC (see {@link toDs50Utc}).
|
|
30925
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
30926
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
30927
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
30928
|
+
*/
|
|
30929
|
+
propagateDs50UtcPosVel(satKeys, startTimeDs50Utc, propsPerSat, stepSizeMin) {
|
|
30930
|
+
const buf = this.propagateKeys_(
|
|
30931
|
+
(...args) => this.fn_().sgp4PropDs50UtcPosVel(...args),
|
|
30932
|
+
satKeys,
|
|
30933
|
+
startTimeDs50Utc,
|
|
30934
|
+
propsPerSat,
|
|
30935
|
+
stepSizeMin,
|
|
30936
|
+
8,
|
|
30937
|
+
"Sgp4PropDs50UtcPosVel_wasm"
|
|
30938
|
+
);
|
|
30939
|
+
return this.parseRecords_(buf, satKeys.length, propsPerSat, 8);
|
|
30940
|
+
}
|
|
30941
|
+
/**
|
|
30942
|
+
* Convenience single-satellite, single-step propagation.
|
|
30943
|
+
* @param satKey The satKey to propagate (must be initialized).
|
|
30944
|
+
* @param minutesSinceEpoch Minutes since the TLE epoch.
|
|
30945
|
+
* @returns The propagated state.
|
|
30946
|
+
*/
|
|
30947
|
+
propagateOne(satKey, minutesSinceEpoch) {
|
|
30948
|
+
return this.propagate([satKey], minutesSinceEpoch, 1, 0)[0][0];
|
|
30949
|
+
}
|
|
30950
|
+
/**
|
|
30951
|
+
* Convenience single-satellite propagation to an absolute UTC epoch.
|
|
30952
|
+
* @param satKey The satKey to propagate (must be initialized).
|
|
30953
|
+
* @param epoch The UTC epoch to propagate to.
|
|
30954
|
+
* @returns The propagated state.
|
|
30955
|
+
*/
|
|
30956
|
+
propagateEpoch(satKey, epoch) {
|
|
30957
|
+
return this.propagateDs50Utc([satKey], _Sgp4WasmBase.toDs50Utc(epoch), 1, 0)[0][0];
|
|
30958
|
+
}
|
|
30959
|
+
/**
|
|
30960
|
+
* Hot-path single-satellite propagation (`Sgp4PropPosVel_wasm`) that reuses
|
|
30961
|
+
* persistent scratch buffers instead of allocating per call, and returns
|
|
30962
|
+
* plain numbers instead of Vector3D instances. Intended for per-frame use
|
|
30963
|
+
* (e.g. as a drop-in backend for `Sgp4.propagate`).
|
|
30964
|
+
* @param satKey The satKey to propagate (must be initialized).
|
|
30965
|
+
* @param minutesSinceEpoch Minutes since the TLE epoch.
|
|
30966
|
+
* @returns Plain TEME position/velocity plus the per-record error code; a
|
|
30967
|
+
* nonzero function-level failure is reported as `err` too.
|
|
30968
|
+
*/
|
|
30969
|
+
propagateOnePosVelFast(satKey, minutesSinceEpoch) {
|
|
30970
|
+
this.fn_();
|
|
30971
|
+
const m = this.module;
|
|
30972
|
+
if (!this.scratchKeyPtr_) {
|
|
30973
|
+
this.scratchKeyPtr_ = m._malloc(8);
|
|
30974
|
+
this.scratchResultPtr_ = m._malloc(8 * 8);
|
|
30975
|
+
}
|
|
30976
|
+
m.HEAP64[this.scratchKeyPtr_ >> 3] = satKey;
|
|
30977
|
+
const ret = m._Sgp4PropPosVel_wasm(this.scratchKeyPtr_, 1, minutesSinceEpoch, 1, 0, this.scratchResultPtr_);
|
|
30978
|
+
if (ret !== 0) {
|
|
30979
|
+
return { err: ret, position: { x: 0, y: 0, z: 0 }, velocity: { x: 0, y: 0, z: 0 } };
|
|
30980
|
+
}
|
|
30981
|
+
const heap = m.HEAPF64;
|
|
30982
|
+
const off = this.scratchResultPtr_ / 8;
|
|
30983
|
+
return {
|
|
30984
|
+
err: heap[off],
|
|
30985
|
+
position: { x: heap[off + 2], y: heap[off + 3], z: heap[off + 4] },
|
|
30986
|
+
velocity: { x: heap[off + 5], y: heap[off + 6], z: heap[off + 7] }
|
|
30987
|
+
};
|
|
30988
|
+
}
|
|
30989
|
+
// Dynamic array batch path
|
|
30990
|
+
/**
|
|
30991
|
+
* Initializes the module-global dynamic array of SGP4 objects
|
|
30992
|
+
* (`InitDynArr_wasm`).
|
|
30993
|
+
* @param initialSize Initial capacity of the array.
|
|
30994
|
+
*/
|
|
30995
|
+
initDynArr(initialSize) {
|
|
30996
|
+
const ret = this.fn_().initDynArr(initialSize);
|
|
30997
|
+
if (ret !== 0) {
|
|
30998
|
+
throw new Sgp4WasmError(`InitDynArr_wasm failed (code ${ret}): ${SGP4_WASM_DYN_ARR_ERRORS[ret] ?? "unknown error"}`, ret);
|
|
30999
|
+
}
|
|
31000
|
+
this.isDynArrInitialized_ = true;
|
|
31001
|
+
}
|
|
31002
|
+
/**
|
|
31003
|
+
* Re-allocates the dynamic array to a new capacity
|
|
31004
|
+
* (`ReallocateDynArr_wasm`).
|
|
31005
|
+
* @param newSize New capacity of the array.
|
|
31006
|
+
*/
|
|
31007
|
+
reallocDynArr(newSize) {
|
|
31008
|
+
const ret = this.fn_().reallocDynArr(newSize);
|
|
31009
|
+
if (ret !== 0) {
|
|
31010
|
+
throw new Sgp4WasmError(`ReallocateDynArr_wasm failed (code ${ret}): ${SGP4_WASM_DYN_ARR_ERRORS[ret] ?? "unknown error"}`, ret);
|
|
31011
|
+
}
|
|
31012
|
+
}
|
|
31013
|
+
/**
|
|
31014
|
+
* Frees the dynamic array (`FreeDynArr_wasm`).
|
|
31015
|
+
*/
|
|
31016
|
+
freeDynArr() {
|
|
31017
|
+
this.fn_().freeDynArr();
|
|
31018
|
+
this.isDynArrInitialized_ = false;
|
|
31019
|
+
}
|
|
31020
|
+
/**
|
|
31021
|
+
* Current size of the dynamic array (`GetDynArrSize_wasm`).
|
|
31022
|
+
*/
|
|
31023
|
+
get dynArrSize() {
|
|
31024
|
+
return this.fn_().getDynArrSize();
|
|
31025
|
+
}
|
|
31026
|
+
/**
|
|
31027
|
+
* Adds a TLE to the dynamic array (`AddSatToDynArr_wasm`).
|
|
31028
|
+
* @param line1 Line 1 of the TLE.
|
|
31029
|
+
* @param line2 Line 2 of the TLE.
|
|
31030
|
+
* @returns The dynamic array index of the added satellite.
|
|
31031
|
+
*/
|
|
31032
|
+
addSatToDynArr(line1, line2) {
|
|
31033
|
+
const index = this.fn_().addSatToDynArr(line1, line2);
|
|
31034
|
+
if (index < 0) {
|
|
31035
|
+
throw new Sgp4WasmError(`AddSatToDynArr_wasm failed (code ${index}): ${SGP4_WASM_ADD_SAT_ERRORS[index] ?? "unknown error"}`, index);
|
|
31036
|
+
}
|
|
31037
|
+
return index;
|
|
31038
|
+
}
|
|
31039
|
+
/**
|
|
31040
|
+
* Propagates dynamic array entries, position/velocity only
|
|
31041
|
+
* (`Sgp4PropDynArrPosVel_wasm`).
|
|
31042
|
+
* @param indexes Dynamic array indices to propagate.
|
|
31043
|
+
* @param startTimeMse Start time in minutes since each TLE's epoch.
|
|
31044
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
31045
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
31046
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
31047
|
+
*/
|
|
31048
|
+
propagateDynArrPosVel(indexes, startTimeMse, propsPerSat, stepSizeMin) {
|
|
31049
|
+
const buf = this.propagateIndexes_(
|
|
31050
|
+
(...args) => this.fn_().sgp4PropDynArrPosVel(...args),
|
|
31051
|
+
indexes,
|
|
31052
|
+
startTimeMse,
|
|
31053
|
+
propsPerSat,
|
|
31054
|
+
stepSizeMin,
|
|
31055
|
+
8
|
|
31056
|
+
);
|
|
31057
|
+
return this.parseRecords_(buf, indexes.length, propsPerSat, 8);
|
|
31058
|
+
}
|
|
31059
|
+
/**
|
|
31060
|
+
* Propagates dynamic array entries from an absolute time with geodetic
|
|
31061
|
+
* output (`Sgp4PropDs50UtcDynArr_wasm`).
|
|
31062
|
+
* @param indexes Dynamic array indices to propagate.
|
|
31063
|
+
* @param startTimeDs50Utc Start time in days since 1950 UTC (see {@link toDs50Utc}).
|
|
31064
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
31065
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
31066
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
31067
|
+
*/
|
|
31068
|
+
propagateDs50UtcDynArr(indexes, startTimeDs50Utc, propsPerSat, stepSizeMin) {
|
|
31069
|
+
const buf = this.propagateIndexes_(
|
|
31070
|
+
(...args) => this.fn_().sgp4PropDs50UtcDynArr(...args),
|
|
31071
|
+
indexes,
|
|
31072
|
+
startTimeDs50Utc,
|
|
31073
|
+
propsPerSat,
|
|
31074
|
+
stepSizeMin,
|
|
31075
|
+
11
|
|
31076
|
+
);
|
|
31077
|
+
return this.parseRecords_(buf, indexes.length, propsPerSat, 11);
|
|
31078
|
+
}
|
|
31079
|
+
/**
|
|
31080
|
+
* Propagates dynamic array entries from an absolute time,
|
|
31081
|
+
* position/velocity only (`Sgp4PropDs50UtcDynArrPosVel_wasm`).
|
|
31082
|
+
* @param indexes Dynamic array indices to propagate.
|
|
31083
|
+
* @param startTimeDs50Utc Start time in days since 1950 UTC (see {@link toDs50Utc}).
|
|
31084
|
+
* @param propsPerSat Number of propagations per satellite.
|
|
31085
|
+
* @param stepSizeMin Step size in minutes between propagations.
|
|
31086
|
+
* @returns Records indexed as `[satIndex][stepIndex]`.
|
|
31087
|
+
*/
|
|
31088
|
+
propagateDs50UtcDynArrPosVel(indexes, startTimeDs50Utc, propsPerSat, stepSizeMin) {
|
|
31089
|
+
const buf = this.propagateIndexes_(
|
|
31090
|
+
(...args) => this.fn_().sgp4PropDs50UtcDynArrPosVel(...args),
|
|
31091
|
+
indexes,
|
|
31092
|
+
startTimeDs50Utc,
|
|
31093
|
+
propsPerSat,
|
|
31094
|
+
stepSizeMin,
|
|
31095
|
+
8
|
|
31096
|
+
);
|
|
31097
|
+
return this.parseRecords_(buf, indexes.length, propsPerSat, 8);
|
|
31098
|
+
}
|
|
31099
|
+
// Logging
|
|
31100
|
+
/**
|
|
31101
|
+
* Sets the wasm library's logging level (`SetLogLevel_wasm`).
|
|
31102
|
+
* @param level The new log level.
|
|
31103
|
+
*/
|
|
31104
|
+
setLogLevel(level) {
|
|
31105
|
+
const ret = this.fn_().setLogLevel(level);
|
|
31106
|
+
if (ret !== 0) {
|
|
31107
|
+
throw new Sgp4WasmError(`SetLogLevel_wasm failed (code ${ret})`, ret);
|
|
31108
|
+
}
|
|
31109
|
+
}
|
|
31110
|
+
// Internals
|
|
31111
|
+
/**
|
|
31112
|
+
* Counts the TLEs in newline-separated text by counting line-1 rows.
|
|
31113
|
+
* @param tleText Newline-separated TLE lines.
|
|
31114
|
+
* @returns Number of TLEs.
|
|
31115
|
+
*/
|
|
31116
|
+
static countTles_(tleText) {
|
|
31117
|
+
return tleText.split(/\r?\n/u).filter((line) => line.trim().startsWith("1 ")).length;
|
|
31118
|
+
}
|
|
31119
|
+
static isHttpUrl_(source) {
|
|
31120
|
+
const href = source instanceof URL ? source.href : source;
|
|
31121
|
+
return /^https?:\/\//u.test(href);
|
|
31122
|
+
}
|
|
31123
|
+
fn_() {
|
|
31124
|
+
if (!this.bindings_) {
|
|
31125
|
+
throw new Sgp4WasmError(`${this.constructor.name} is not loaded. Call load() first.`);
|
|
31126
|
+
}
|
|
31127
|
+
return this.bindings_;
|
|
31128
|
+
}
|
|
31129
|
+
async loadInternal_(options) {
|
|
31130
|
+
const isNode = typeof process !== "undefined" && Boolean(process.versions?.node);
|
|
31131
|
+
const glueText = await this.resolveGlueText_(options.glue, isNode);
|
|
31132
|
+
const wasmBinary = await this.resolveWasmBinary_(options.wasm, isNode);
|
|
31133
|
+
let requireFn;
|
|
31134
|
+
let dirname;
|
|
31135
|
+
if (isNode) {
|
|
31136
|
+
const nodeModule = await import(
|
|
31137
|
+
/* webpackIgnore: true */
|
|
31138
|
+
'module'
|
|
31139
|
+
);
|
|
31140
|
+
requireFn = nodeModule.createRequire(import.meta.url);
|
|
31141
|
+
dirname = ".";
|
|
31142
|
+
}
|
|
31143
|
+
let markReady = () => void 0;
|
|
31144
|
+
let markFailed = () => void 0;
|
|
31145
|
+
const runtimeReady = new Promise((resolve, reject) => {
|
|
31146
|
+
markReady = resolve;
|
|
31147
|
+
markFailed = reject;
|
|
31148
|
+
});
|
|
31149
|
+
const seed = {
|
|
31150
|
+
wasmBinary,
|
|
31151
|
+
onRuntimeInitialized: () => markReady(),
|
|
31152
|
+
onAbort: (reason) => markFailed(new Sgp4WasmError(`${this.constructor.name} wasm runtime aborted: ${reason}`))
|
|
31153
|
+
};
|
|
31154
|
+
if (options.print) {
|
|
31155
|
+
seed.print = options.print;
|
|
31156
|
+
}
|
|
31157
|
+
if (options.printErr) {
|
|
31158
|
+
seed.printErr = options.printErr;
|
|
31159
|
+
}
|
|
31160
|
+
const evaluateGlue = new Function("Module", "require", "__dirname", `${glueText};
|
|
31161
|
+
return { Module: Module, FS: FS };`);
|
|
31162
|
+
const { Module, FS } = evaluateGlue(seed, requireFn, dirname);
|
|
31163
|
+
await runtimeReady;
|
|
31164
|
+
this.module_ = Module;
|
|
31165
|
+
this.fs_ = FS;
|
|
31166
|
+
this.bindings_ = _Sgp4WasmBase.buildBindings_(Module);
|
|
31167
|
+
return this;
|
|
31168
|
+
}
|
|
31169
|
+
static buildBindings_(m) {
|
|
31170
|
+
const num = "number";
|
|
31171
|
+
return {
|
|
31172
|
+
tleAddSatFrLines: m.cwrap("TleAddSatFrLines_wasm", num, ["string", "string"]),
|
|
31173
|
+
tleAddSatsFrLines: m.cwrap("TleAddSatsFrLines_wasm", num, [num, num, num]),
|
|
31174
|
+
tleLoadFileVfs: m.cwrap("TleLoadFileVFS_wasm", num, ["string"]),
|
|
31175
|
+
tleRemoveSats: m.cwrap("TleRemoveSats_wasm", num, [num, num, num]),
|
|
31176
|
+
sgp4InitSats: m.cwrap("Sgp4InitSats_wasm", num, [num, num, num]),
|
|
31177
|
+
sgp4RemoveSats: m.cwrap("Sgp4RemoveSats_wasm", num, [num, num, num]),
|
|
31178
|
+
sgp4Prop: m.cwrap("Sgp4Prop_wasm", num, [num, num, num, num, num, num]),
|
|
31179
|
+
sgp4PropPosVel: m.cwrap("Sgp4PropPosVel_wasm", num, [num, num, num, num, num, num]),
|
|
31180
|
+
sgp4PropDs50Utc: m.cwrap("Sgp4PropDs50Utc_wasm", num, [num, num, num, num, num, num]),
|
|
31181
|
+
sgp4PropDs50UtcPosVel: m.cwrap("Sgp4PropDs50UtcPosVel_wasm", num, [num, num, num, num, num, num]),
|
|
31182
|
+
initDynArr: m.cwrap("InitDynArr_wasm", num, [num]),
|
|
31183
|
+
reallocDynArr: m.cwrap("ReallocateDynArr_wasm", num, [num]),
|
|
31184
|
+
freeDynArr: m.cwrap("FreeDynArr_wasm", null, []),
|
|
31185
|
+
getDynArrSize: m.cwrap("GetDynArrSize_wasm", num, []),
|
|
31186
|
+
addSatToDynArr: m.cwrap("AddSatToDynArr_wasm", num, ["string", "string"]),
|
|
31187
|
+
sgp4PropDynArrPosVel: m.cwrap("Sgp4PropDynArrPosVel_wasm", null, [num, num, num, num, num, num]),
|
|
31188
|
+
sgp4PropDs50UtcDynArr: m.cwrap("Sgp4PropDs50UtcDynArr_wasm", null, [num, num, num, num, num, num]),
|
|
31189
|
+
sgp4PropDs50UtcDynArrPosVel: m.cwrap("Sgp4PropDs50UtcDynArrPosVel_wasm", null, [num, num, num, num, num, num]),
|
|
31190
|
+
setLogLevel: m.cwrap("SetLogLevel_wasm", num, [num])
|
|
31191
|
+
};
|
|
31192
|
+
}
|
|
31193
|
+
async resolveGlueText_(source, isNode) {
|
|
31194
|
+
if (source && typeof source === "object" && "text" in source) {
|
|
31195
|
+
return source.text;
|
|
31196
|
+
}
|
|
31197
|
+
const resolved = this.resolveDefaultSource_(source, this.defaultGlueFile_, isNode);
|
|
31198
|
+
if (isNode && !_Sgp4WasmBase.isHttpUrl_(resolved)) {
|
|
31199
|
+
return await this.readNodeFile_(resolved, "utf8");
|
|
31200
|
+
}
|
|
31201
|
+
const response = await fetch(resolved);
|
|
31202
|
+
if (!response.ok) {
|
|
31203
|
+
throw this.missingArtifactError_(resolved.toString(), `HTTP ${response.status}`);
|
|
31204
|
+
}
|
|
31205
|
+
return response.text();
|
|
31206
|
+
}
|
|
31207
|
+
async resolveWasmBinary_(source, isNode) {
|
|
31208
|
+
if (source instanceof Uint8Array || source instanceof ArrayBuffer) {
|
|
31209
|
+
return source;
|
|
31210
|
+
}
|
|
31211
|
+
const resolved = this.resolveDefaultSource_(source, this.defaultWasmFile_, isNode);
|
|
31212
|
+
if (isNode && !_Sgp4WasmBase.isHttpUrl_(resolved)) {
|
|
31213
|
+
return await this.readNodeFile_(resolved, null);
|
|
31214
|
+
}
|
|
31215
|
+
const response = await fetch(resolved);
|
|
31216
|
+
if (!response.ok) {
|
|
31217
|
+
throw this.missingArtifactError_(resolved.toString(), `HTTP ${response.status}`);
|
|
31218
|
+
}
|
|
31219
|
+
return response.arrayBuffer();
|
|
31220
|
+
}
|
|
31221
|
+
resolveDefaultSource_(source, defaultFile, isNode) {
|
|
31222
|
+
if (source) {
|
|
31223
|
+
return source;
|
|
31224
|
+
}
|
|
31225
|
+
if (!isNode || typeof import.meta.url !== "string") {
|
|
31226
|
+
throw new Sgp4WasmError(
|
|
31227
|
+
`${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.`
|
|
31228
|
+
);
|
|
31229
|
+
}
|
|
31230
|
+
return new URL(defaultFile, import.meta.url);
|
|
31231
|
+
}
|
|
31232
|
+
async readNodeFile_(source, encoding) {
|
|
31233
|
+
const fsPromises = await import(
|
|
31234
|
+
/* webpackIgnore: true */
|
|
31235
|
+
'fs/promises'
|
|
31236
|
+
);
|
|
31237
|
+
try {
|
|
31238
|
+
return encoding ? await fsPromises.readFile(source, encoding) : await fsPromises.readFile(source);
|
|
31239
|
+
} catch (err) {
|
|
31240
|
+
throw this.missingArtifactError_(source.toString(), err.message);
|
|
31241
|
+
}
|
|
31242
|
+
}
|
|
31243
|
+
missingArtifactError_(location, cause) {
|
|
31244
|
+
return new Sgp4WasmError(
|
|
31245
|
+
`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.`
|
|
31246
|
+
);
|
|
31247
|
+
}
|
|
31248
|
+
// Memory marshalling
|
|
31249
|
+
withMalloc_(bytes, fn) {
|
|
31250
|
+
const m = this.module;
|
|
31251
|
+
const ptr = m._malloc(Math.max(bytes, 1));
|
|
31252
|
+
try {
|
|
31253
|
+
return fn(ptr);
|
|
31254
|
+
} finally {
|
|
31255
|
+
m._free(ptr);
|
|
31256
|
+
}
|
|
31257
|
+
}
|
|
31258
|
+
withI64Array_(values, fn) {
|
|
31259
|
+
return this.withMalloc_(values.length * 8, (ptr) => {
|
|
31260
|
+
const m = this.module;
|
|
31261
|
+
values.forEach((value, i) => m.setValue(ptr + i * 8, BigInt(value), "i64"));
|
|
31262
|
+
return fn(ptr);
|
|
31263
|
+
});
|
|
31264
|
+
}
|
|
31265
|
+
withI32Array_(values, fn) {
|
|
31266
|
+
return this.withMalloc_(values.length * 4, (ptr) => {
|
|
31267
|
+
const m = this.module;
|
|
31268
|
+
values.forEach((value, i) => m.setValue(ptr + i * 4, value, "i32"));
|
|
31269
|
+
return fn(ptr);
|
|
31270
|
+
});
|
|
31271
|
+
}
|
|
31272
|
+
readI64Array_(ptr, count) {
|
|
31273
|
+
const m = this.module;
|
|
31274
|
+
const out = [];
|
|
31275
|
+
for (let i = 0; i < count; i++) {
|
|
31276
|
+
out.push(m.getValue(ptr + i * 8, "i64"));
|
|
31277
|
+
}
|
|
31278
|
+
return out;
|
|
31279
|
+
}
|
|
31280
|
+
/**
|
|
31281
|
+
* Allocates a result buffer of `count` doubles, runs `fn`, and copies the
|
|
31282
|
+
* buffer out. The HEAPF64 view is re-read from the module after the call
|
|
31283
|
+
* because wasm memory growth invalidates cached views.
|
|
31284
|
+
*/
|
|
31285
|
+
withF64Result_(count, fn) {
|
|
31286
|
+
return this.withMalloc_(count * 8, (ptr) => {
|
|
31287
|
+
fn(ptr);
|
|
31288
|
+
return this.module.HEAPF64.slice(ptr / 8, ptr / 8 + count);
|
|
31289
|
+
});
|
|
31290
|
+
}
|
|
31291
|
+
runKeysWithErrSat_(op, satKeys, opName) {
|
|
31292
|
+
this.withI64Array_(satKeys, (keysPtr) => this.withMalloc_(8, (errPtr) => {
|
|
31293
|
+
const m = this.module;
|
|
31294
|
+
m.setValue(errPtr, 0n, "i64");
|
|
31295
|
+
const ret = op(keysPtr, satKeys.length, errPtr);
|
|
31296
|
+
if (ret !== 0) {
|
|
31297
|
+
const errKey = m.getValue(errPtr, "i64");
|
|
31298
|
+
throw new Sgp4WasmError(`${opName} failed (code ${ret}); offending satKey: ${errKey}`, ret);
|
|
31299
|
+
}
|
|
31300
|
+
}));
|
|
31301
|
+
}
|
|
31302
|
+
propagateKeys_(op, satKeys, start, propsPerSat, stepMin, width, opName) {
|
|
31303
|
+
return this.withI64Array_(satKeys, (keysPtr) => this.withF64Result_(satKeys.length * propsPerSat * width, (resultPtr) => {
|
|
31304
|
+
const ret = op(keysPtr, satKeys.length, start, propsPerSat, stepMin, resultPtr);
|
|
31305
|
+
if (ret !== 0) {
|
|
31306
|
+
throw new Sgp4WasmError(`${opName} failed (code ${ret})`, ret);
|
|
31307
|
+
}
|
|
31308
|
+
}));
|
|
31309
|
+
}
|
|
31310
|
+
propagateIndexes_(op, indexes, start, propsPerSat, stepMin, width) {
|
|
31311
|
+
return this.withI32Array_(indexes, (idxPtr) => this.withF64Result_(indexes.length * propsPerSat * width, (resultPtr) => {
|
|
31312
|
+
op(idxPtr, indexes.length, start, propsPerSat, stepMin, resultPtr);
|
|
31313
|
+
}));
|
|
31314
|
+
}
|
|
31315
|
+
/**
|
|
31316
|
+
* Parses a copied-out result buffer into per-satellite, per-step records.
|
|
31317
|
+
* Records are 8 doubles wide (err, time, pos, vel) or 11 doubles wide
|
|
31318
|
+
* (plus lat, lon, height) — the distribution docs' "7 per record"
|
|
31319
|
+
* precondition is stale.
|
|
31320
|
+
*/
|
|
31321
|
+
parseRecords_(buf, numKeys, propsPerSat, width) {
|
|
31322
|
+
const results = [];
|
|
31323
|
+
for (let satIndex = 0; satIndex < numKeys; satIndex++) {
|
|
31324
|
+
const satRecords = [];
|
|
31325
|
+
for (let stepIndex = 0; stepIndex < propsPerSat; stepIndex++) {
|
|
31326
|
+
const off = (satIndex * propsPerSat + stepIndex) * width;
|
|
31327
|
+
const record = {
|
|
31328
|
+
err: buf[off],
|
|
31329
|
+
time: buf[off + 1],
|
|
31330
|
+
position: new Vector3D(buf[off + 2], buf[off + 3], buf[off + 4]),
|
|
31331
|
+
velocity: new Vector3D(
|
|
31332
|
+
buf[off + 5],
|
|
31333
|
+
buf[off + 6],
|
|
31334
|
+
buf[off + 7]
|
|
31335
|
+
)
|
|
31336
|
+
};
|
|
31337
|
+
if (width === 11) {
|
|
31338
|
+
record.llh = {
|
|
31339
|
+
lat: buf[off + 8],
|
|
31340
|
+
lon: buf[off + 9],
|
|
31341
|
+
height: buf[off + 10]
|
|
31342
|
+
};
|
|
31343
|
+
}
|
|
31344
|
+
satRecords.push(record);
|
|
31345
|
+
}
|
|
31346
|
+
results.push(satRecords);
|
|
31347
|
+
}
|
|
31348
|
+
return results;
|
|
31349
|
+
}
|
|
31350
|
+
};
|
|
31351
|
+
|
|
31352
|
+
// src/external/Sgp4Wasm.ts
|
|
31353
|
+
var Sgp4Wasm = class extends Sgp4WasmBase {
|
|
31354
|
+
defaultGlueFile_ = "Sgp4Prop.js";
|
|
31355
|
+
defaultWasmFile_ = "Sgp4Prop.wasm";
|
|
31356
|
+
};
|
|
31357
|
+
|
|
31358
|
+
// src/external/Sgp4XpWasm.ts
|
|
31359
|
+
var Sgp4XpWasm = class extends Sgp4WasmBase {
|
|
31360
|
+
defaultGlueFile_ = "Sgp4Prop.xp.js";
|
|
31361
|
+
defaultWasmFile_ = "Sgp4Prop.xp.wasm";
|
|
31362
|
+
};
|
|
30223
31363
|
/**
|
|
30224
31364
|
* @author Theodore Kruczek
|
|
30225
31365
|
* @license AGPL-3.0-or-later
|
|
@@ -30393,6 +31533,22 @@ var RepeatGroundTrack = class _RepeatGroundTrack {
|
|
|
30393
31533
|
* You should have received a copy of the GNU Affero General Public License along with
|
|
30394
31534
|
* Orbital Object ToolKit. If not, see <http://www.gnu.org/licenses/>.
|
|
30395
31535
|
*/
|
|
31536
|
+
/**
|
|
31537
|
+
* @author Theodore Kruczek
|
|
31538
|
+
* @license AGPL-3.0-or-later
|
|
31539
|
+
* @copyright (c) 2026 Kruczek Labs LLC
|
|
31540
|
+
*
|
|
31541
|
+
* Orbital Object ToolKit is free software: you can redistribute it and/or modify it under the
|
|
31542
|
+
* terms of the GNU Affero General Public License as published by the Free Software
|
|
31543
|
+
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
31544
|
+
*
|
|
31545
|
+
* Orbital Object ToolKit is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
31546
|
+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
31547
|
+
* See the GNU Affero General Public License for more details.
|
|
31548
|
+
*
|
|
31549
|
+
* You should have received a copy of the GNU Affero General Public License along with
|
|
31550
|
+
* Orbital Object ToolKit. If not, see <http://www.gnu.org/licenses/>.
|
|
31551
|
+
*/
|
|
30396
31552
|
/*! Bundled license information:
|
|
30397
31553
|
|
|
30398
31554
|
astronomy-engine/esm/astronomy.js:
|
|
@@ -30431,4 +31587,4 @@ astronomy-engine/esm/astronomy.js:
|
|
|
30431
31587
|
*)
|
|
30432
31588
|
*/
|
|
30433
31589
|
|
|
30434
|
-
export { AccessCalculator, AngularDiameterMethod, AngularDistanceMethod, Antenna, AtmosphericDrag, BaseObject, BatchLeastSquaresOD, BatchLeastSquaresResult, Beacon, BoxMuller, CatalogScreener, CatalogSource, CdmExporter, CdmParser, CelestialBody, CelestialBodyType, CenterBody, CenterBodyMu, ChebyshevCoefficients, ChebyshevCompressor, ChebyshevInterpolator, ClassicalElements, CommDeviceType, CommLink, CommunicationDevice, ConjunctionAssessment, ConjunctionEvent, ConstellationGenerator, ContactScheduler, ContactSelectionStrategy, CovarianceFrame, CovarianceRealism, CovarianceSample, CubicSpline, CubicSplineInterpolator, DEFAULT_INTERPOLATOR, DEFAULT_LAGRANGE_ORDER, DEG2RAD, DataHandler, DormandPrince54Propagator, DownhillSimplex, DynamicGroundObject, Earth, EarthGravity, EphemerisBody, EphemerisSatellite, Epoch, EpochGPS, EpochTAI, EpochTDB, EpochTT, EpochUTC, EpochWindow, EquinoctialElements, EulerAngles, FieldInterpolator, FieldOfView, Force, ForceModel, FormatTle, FovFrame, FovShape, Geodetic, GibbsIOD, GoldenSection, GoodingIOD, Gravity, GroundObject, GroundStation, HerrickGibbsIOD, Hill, History, HorizonsParser, ITRF, Interpolator, InterpolatorType, J2000, Jupiter, KM_PER_AU, KeplerPropagator, LagrangeInterpolator, LambertIOD, LandObject, LaserRangingSensor, LaunchTrajectoryGenerator, LevenbergMarquardtOD, LevenbergMarquardtResult, MILLISECONDS_PER_DAY, MILLISECONDS_PER_SECOND, MILLISECONDS_TO_DAYS, MINUTES_PER_DAY, MS_PER_DAY, Marker, Mars, Matrix, MechanicalRadar, Mercury, ModifiedGoodingIOD, ModulationType, Moon, MoonBody, Neptune, Observation, ObservationOptical, ObservationRadar, OdmExporter, OemParser, OmmParser, OotkError, OpticalSensor, OrbitDeterminationError,
|
|
31590
|
+
export { AccessCalculator, AngularDiameterMethod, AngularDistanceMethod, Antenna, AtmosphericDrag, BaseObject, BatchLeastSquaresOD, BatchLeastSquaresResult, Beacon, BoxMuller, CatalogScreener, CatalogSource, CdmExporter, CdmParser, CelestialBody, CelestialBodyType, CenterBody, CenterBodyMu, ChebyshevCoefficients, ChebyshevCompressor, ChebyshevInterpolator, ClassicalElements, CommDeviceType, CommLink, CommunicationDevice, ConjunctionAssessment, ConjunctionEvent, ConstellationGenerator, ContactScheduler, ContactSelectionStrategy, CovarianceFrame, CovarianceRealism, CovarianceSample, CubicSpline, CubicSplineInterpolator, DEFAULT_INTERPOLATOR, DEFAULT_LAGRANGE_ORDER, DEG2RAD, DataHandler, DormandPrince54Propagator, DownhillSimplex, DynamicGroundObject, Earth, EarthGravity, EphemerisBody, EphemerisSatellite, Epoch, EpochGPS, EpochTAI, EpochTDB, EpochTT, EpochUTC, EpochWindow, EquinoctialElements, EulerAngles, FieldInterpolator, FieldOfView, Force, ForceModel, FormatTle, FovFrame, FovShape, Geodetic, GibbsIOD, GoldenSection, GoodingIOD, Gravity, GroundObject, GroundStation, HerrickGibbsIOD, Hill, History, HorizonsParser, ITRF, Interpolator, InterpolatorType, J2000, Jupiter, KM_PER_AU, KeplerPropagator, LagrangeInterpolator, LambertIOD, LandObject, LaserRangingSensor, LaunchTrajectoryGenerator, LaunchWindowFinder, LevenbergMarquardtOD, LevenbergMarquardtResult, MILLISECONDS_PER_DAY, MILLISECONDS_PER_SECOND, MILLISECONDS_TO_DAYS, MINUTES_PER_DAY, MS_PER_DAY, Marker, Mars, Matrix, MechanicalRadar, Mercury, ModifiedGoodingIOD, ModulationType, Moon, MoonBody, Neptune, Observation, ObservationOptical, ObservationRadar, OdmExporter, OemParser, OmmParser, OotkError, OpticalSensor, OrbitDeterminationError, OrbitRegime, PI, ParseError, PassType, PassiveRFSensor, PayloadStatus, PhasedArrayRadar, PlaneChangeBurn, PlanetBody, Pluto, PolynomialRegression, ProbabilityOfCollision, PropagationError, Propagator, PropagatorType, PropellantBudget, Quaternion, RAD2DEG, RADIUS_OF_EARTH, RAE, RCS_VMAG_ESTIMATE_MAX, RCS_VMAG_ESTIMATE_MIN, RIC, RadarSensor, RadecGeocentric, RadecTopocentric, Random, RandomGaussianSource, Receiver, RelativeState, RepeatGroundTrack, RkCheckpoint, RkResult, RungeKutta4Propagator, RungeKutta89Propagator, RungeKuttaAdaptive, SGP4_WASM_ADD_SAT_ERRORS, SGP4_WASM_DYN_ARR_ERRORS, SLR_WAVELENGTHS, SPEED_OF_LIGHT_KM_S, Satellite, Saturn, ScheduledContact, ScreeningFilter, SegmentedLagrangeInterpolator, Sensor, SensorType, Sgp4, Sgp4Error, Sgp4ErrorCode, Sgp4OpsMode, Sgp4Propagator, Sgp4Wasm, Sgp4WasmBase, Sgp4WasmError, Sgp4WasmLogLevel, Sgp4WasmPropagator, Sgp4XpWasm, SimpleLinearRegression, SolarRadiationPressure, SolarSystem, SpaceObject, SpaceObjectType, Star, StateCovariance, StateInterpolator, StateVector, Sun, SunBody, SunStatus, TAU, TEME, ThirdBodyGravity, Thrust, TimeStamped, Tle, Transmitter, Transponder, TtcAntenna, TwoBurnOrbitTransfer, Uranus, ValidationError, Vector, Vector3D, Venus, VerletBlendInterpolator, VisualizationHelpers, Waypoint, ZoomValue, acoth, acsch, angularDiameter, angularDistance, angularVelocityOfEarth, array2d, asec2rad, asech, azel2uv, bodyTypeLookup, boresightFrameFromAzElRoll, cKmPerMs, cKmPerSec, cMPerSec, calcGmst, calcIncFromAz, calcInertAz, calculateFspl, calculatePropagationDelay, cappedScreeningCovarianceFromTle, clamp, concat, copySign, covariance, createCovarianceFromTle, createSampleCovarianceFromTle, createVec, csch, dbmToDbw, dbwToDbm, dbwToWatts, deg2rad, derivative, dopplerFactor, earthGravityParam, ecef2eci, ecef2enu, ecef2rae, ecefRad2rae, eci2ecef, eci2lla, eci2rae, enu2rf, estimateRcs, estimateVmagFromRcs, evalPoly, factorial, gamma, getDayOfYear, getDegLat, getDegLon, getRadLat, getRadLon, groundTrackStateVector, halfPi, isLeapYear, jacobian, jday, linearDistance, linearInterpolate, lla2ecef, lla2eci, lla2sez, llaRad2ecef, log10, mag2db, masec2rad, matchHalfPlane, mean, msec2sec, newtonM, newtonNu, normalizeAngle, observationDerivative, observationNoiseFromSigmas, parseCenterBody, perigeeSpeed, rad2deg, radecToPosition, radecToVelocity, rae2ecef, rae2eci, rae2enu, rae2raeOffBoresight, rae2ruv, rae2sez, relativeVelocity, rv2tle, scaleAndClampRicSigmas, sec2day, sec2deg, sec2min, sech, secondsPerDay, secondsPerSiderealDay, secondsPerWeek, semimajorAxisFromMeanMotion, sez2rae, sign, spaceObjType2Str, std, temp4, toPrecision, ttasec2rad, uv2azel, wattsToDbw, wrapAngle, x2o3 };
|