ootk 6.0.4 → 6.0.7
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/dist/main.cjs +462 -46
- package/dist/main.d.cts +307 -17
- package/dist/main.d.ts +307 -17
- package/dist/main.js +457 -47
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -77,6 +77,7 @@ var AngularDistanceMethod = /* @__PURE__ */ ((AngularDistanceMethod2) => {
|
|
|
77
77
|
|
|
78
78
|
// src/enums/CatalogSource.ts
|
|
79
79
|
var CatalogSource = /* @__PURE__ */ ((CatalogSource2) => {
|
|
80
|
+
CatalogSource2["UNKNOWN"] = "unknown";
|
|
80
81
|
CatalogSource2["USSF"] = "spacetrack";
|
|
81
82
|
CatalogSource2["CELESTRAK"] = "celestrak";
|
|
82
83
|
CatalogSource2["CELESTRAK_SUP"] = "celestrak-sup";
|
|
@@ -298,7 +299,7 @@ var Epoch = class {
|
|
|
298
299
|
const currentDateObj = this.toDateTime();
|
|
299
300
|
const epochYear = currentDateObj.getUTCFullYear().toString().slice(2, 4);
|
|
300
301
|
const epochDay = this.getDayOfYear_(currentDateObj);
|
|
301
|
-
const timeOfDay = (currentDateObj.getUTCHours() *
|
|
302
|
+
const timeOfDay = (currentDateObj.getUTCHours() * 3600 + currentDateObj.getUTCMinutes() * 60 + currentDateObj.getUTCSeconds() + currentDateObj.getUTCMilliseconds() / 1e3) / 86400;
|
|
302
303
|
const epochDayStr = (epochDay + timeOfDay).toFixed(8).padStart(12, "0");
|
|
303
304
|
return {
|
|
304
305
|
epochYr: epochYear,
|
|
@@ -2243,6 +2244,32 @@ function createSampleCovarianceFromTle(tleLine1, tleLine2, frame = "ric" /* RIC
|
|
|
2243
2244
|
const sample = new CovarianceSample(j2000State, covariance2, tle);
|
|
2244
2245
|
return frame === "ric" /* RIC */ ? sample.desampleRIC() : sample.desampleJ2000();
|
|
2245
2246
|
}
|
|
2247
|
+
var DEFAULT_SCREENING_SIGMA_CAPS = [1200, 1e3, 5e3];
|
|
2248
|
+
function cappedScreeningCovarianceFromTle(tleLine1, tleLine2, confidenceLevel, caps = DEFAULT_SCREENING_SIGMA_CAPS) {
|
|
2249
|
+
let elements;
|
|
2250
|
+
try {
|
|
2251
|
+
elements = createSampleCovarianceFromTle(tleLine1, tleLine2).matrix.elements;
|
|
2252
|
+
} catch {
|
|
2253
|
+
return buildFallbackScreeningCovariance_(caps);
|
|
2254
|
+
}
|
|
2255
|
+
const radialVar = elements[0][0];
|
|
2256
|
+
const intrackVar = elements[1][1];
|
|
2257
|
+
const crosstrackVar = elements[2][2];
|
|
2258
|
+
elements[0][0] = Math.min(Math.sqrt(radialVar) * confidenceLevel, caps[0]);
|
|
2259
|
+
elements[1][1] = Math.min(Math.sqrt(crosstrackVar) * confidenceLevel, caps[1]);
|
|
2260
|
+
elements[2][2] = Math.min(Math.sqrt(intrackVar) * confidenceLevel, caps[2]);
|
|
2261
|
+
if (!elements[0][0] || !elements[1][1] || !elements[2][2]) {
|
|
2262
|
+
return buildFallbackScreeningCovariance_(caps);
|
|
2263
|
+
}
|
|
2264
|
+
return new StateCovariance(new Matrix(elements), "eci" /* ECI */);
|
|
2265
|
+
}
|
|
2266
|
+
function buildFallbackScreeningCovariance_(caps) {
|
|
2267
|
+
const fallback = Array.from({ length: 6 }, () => new Array(6).fill(0));
|
|
2268
|
+
fallback[0][0] = caps[0];
|
|
2269
|
+
fallback[1][1] = caps[1];
|
|
2270
|
+
fallback[2][2] = caps[2];
|
|
2271
|
+
return new StateCovariance(new Matrix(fallback), "eci" /* ECI */);
|
|
2272
|
+
}
|
|
2246
2273
|
|
|
2247
2274
|
// src/utils/jacobian.ts
|
|
2248
2275
|
var jacobian = (f, m, x0, step = 1e-5) => {
|
|
@@ -2269,6 +2296,17 @@ function linearDistance(pos1, pos2) {
|
|
|
2269
2296
|
return Math.sqrt((pos1.x - pos2.x) ** 2 + (pos1.y - pos2.y) ** 2 + (pos1.z - pos2.z) ** 2);
|
|
2270
2297
|
}
|
|
2271
2298
|
|
|
2299
|
+
// src/utils/photometry.ts
|
|
2300
|
+
var RCS_VMAG_ESTIMATE_MIN = -5;
|
|
2301
|
+
var RCS_VMAG_ESTIMATE_MAX = 15;
|
|
2302
|
+
function estimateVmagFromRcs(rcs) {
|
|
2303
|
+
if (typeof rcs !== "number" || !Number.isFinite(rcs) || rcs <= 0) {
|
|
2304
|
+
return null;
|
|
2305
|
+
}
|
|
2306
|
+
const vmag = -1.3 - 2.5 * Math.log10(rcs);
|
|
2307
|
+
return Math.min(Math.max(vmag, RCS_VMAG_ESTIMATE_MIN), RCS_VMAG_ESTIMATE_MAX);
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2272
2310
|
// src/operations/Vector3D.ts
|
|
2273
2311
|
var Vector3D = class _Vector3D {
|
|
2274
2312
|
constructor(x, y, z) {
|
|
@@ -3253,7 +3291,7 @@ var Random = class {
|
|
|
3253
3291
|
}
|
|
3254
3292
|
};
|
|
3255
3293
|
|
|
3256
|
-
//
|
|
3294
|
+
// node_modules/astronomy-engine/esm/astronomy.js
|
|
3257
3295
|
var C_AUDAY = 173.1446326846693;
|
|
3258
3296
|
var KM_PER_AU2 = 14959787069098932e-8;
|
|
3259
3297
|
var DEG2RAD2 = 0.017453292519943295;
|
|
@@ -9026,6 +9064,7 @@ var OemParser = class _OemParser {
|
|
|
9026
9064
|
let currentBlock = null;
|
|
9027
9065
|
let currentMetadata = {};
|
|
9028
9066
|
let metadataComments = [];
|
|
9067
|
+
let userDefined = {};
|
|
9029
9068
|
let inMeta = false;
|
|
9030
9069
|
let inData = false;
|
|
9031
9070
|
let inCovariance = false;
|
|
@@ -9034,6 +9073,7 @@ var OemParser = class _OemParser {
|
|
|
9034
9073
|
if (line === "META_START") {
|
|
9035
9074
|
currentMetadata = {};
|
|
9036
9075
|
metadataComments = [];
|
|
9076
|
+
userDefined = {};
|
|
9037
9077
|
inMeta = true;
|
|
9038
9078
|
continue;
|
|
9039
9079
|
}
|
|
@@ -9041,6 +9081,9 @@ var OemParser = class _OemParser {
|
|
|
9041
9081
|
if (metadataComments.length > 0) {
|
|
9042
9082
|
currentMetadata.COMMENT = metadataComments;
|
|
9043
9083
|
}
|
|
9084
|
+
if (Object.keys(userDefined).length > 0) {
|
|
9085
|
+
currentMetadata.USER_DEFINED = userDefined;
|
|
9086
|
+
}
|
|
9044
9087
|
currentBlock = {
|
|
9045
9088
|
metadata: currentMetadata,
|
|
9046
9089
|
ephemeris: []
|
|
@@ -9079,7 +9122,9 @@ var OemParser = class _OemParser {
|
|
|
9079
9122
|
const eqIndex = line.indexOf("=");
|
|
9080
9123
|
const key = line.substring(0, eqIndex).trim();
|
|
9081
9124
|
const value = line.substring(eqIndex + 1).trim();
|
|
9082
|
-
if (key
|
|
9125
|
+
if (key.startsWith("USER_DEFINED_")) {
|
|
9126
|
+
userDefined[key.substring(13)] = value;
|
|
9127
|
+
} else if (key === "INTERPOLATION_DEGREE") {
|
|
9083
9128
|
currentMetadata[key] = parseInt(value, 10);
|
|
9084
9129
|
} else {
|
|
9085
9130
|
currentMetadata[key] = value;
|
|
@@ -9847,7 +9892,10 @@ var FormatTle = class _FormatTle {
|
|
|
9847
9892
|
*/
|
|
9848
9893
|
static createTle(tleParams) {
|
|
9849
9894
|
const { inc, meanmo, rasc, argPe, meana, ecen, epochyr, epochday, intl } = tleParams;
|
|
9850
|
-
|
|
9895
|
+
let scc = Tle.convert6DigitToA5(tleParams.scc);
|
|
9896
|
+
if (/^\d{1,4}$/u.test(scc)) {
|
|
9897
|
+
scc = scc.padStart(5, "0");
|
|
9898
|
+
}
|
|
9851
9899
|
const epochYrStr = String(epochyr).padStart(2, "0");
|
|
9852
9900
|
const epochdayStr = Number(epochday).toFixed(8).padStart(12, "0");
|
|
9853
9901
|
const incStr = _FormatTle.inclination(inc);
|
|
@@ -10360,7 +10408,7 @@ var Sgp4 = class _Sgp4 {
|
|
|
10360
10408
|
error: 0 /* NO_ERROR */
|
|
10361
10409
|
};
|
|
10362
10410
|
const xpdotp = 1440 / (2 * PI);
|
|
10363
|
-
satrec.satnum = tleLine1.substring(2, 7);
|
|
10411
|
+
satrec.satnum = tleLine1.charAt(7) === "V" ? "0" : tleLine1.substring(2, 7);
|
|
10364
10412
|
satrec.epochyr = parseInt(tleLine1.substring(18, 20));
|
|
10365
10413
|
satrec.epochdays = parseFloat(tleLine1.substring(20, 32));
|
|
10366
10414
|
satrec.ndot = parseFloat(tleLine1.substring(33, 43));
|
|
@@ -13878,7 +13926,7 @@ var Tle = class _Tle {
|
|
|
13878
13926
|
this.line1 = line1;
|
|
13879
13927
|
this.line2 = line2;
|
|
13880
13928
|
this.epoch = _Tle.parseEpoch_(line1.substring(18, 32));
|
|
13881
|
-
this.satnum =
|
|
13929
|
+
this.satnum = _Tle.satNum(this.line1);
|
|
13882
13930
|
this.satrec_ = Sgp4.createSatrec(line1, line2, gravConst, opsMode);
|
|
13883
13931
|
}
|
|
13884
13932
|
toString() {
|
|
@@ -13967,7 +14015,7 @@ ${this.line2}`;
|
|
|
13967
14015
|
epochYearFull = 1900 + epochYearShort;
|
|
13968
14016
|
}
|
|
13969
14017
|
const epochJday = epochDayOfYear + epochYearFull * 365;
|
|
13970
|
-
const currentJday = getDayOfYear() + currentYearFull * 365;
|
|
14018
|
+
const currentJday = getDayOfYear(nowInput) + currentYearFull * 365;
|
|
13971
14019
|
const currentTime = (nowInput.getUTCHours() * 3600 + nowInput.getUTCMinutes() * 60 + nowInput.getUTCSeconds()) / 86400;
|
|
13972
14020
|
const daysOld = currentJday + currentTime - epochJday;
|
|
13973
14021
|
switch (outputUnits) {
|
|
@@ -14115,7 +14163,7 @@ ${this.line2}`;
|
|
|
14115
14163
|
const BSTAR_PART_3 = _Tle.bstar_.start + 6;
|
|
14116
14164
|
const BSTAR_PART_4 = _Tle.bstar_.stop - 1;
|
|
14117
14165
|
const bstarSymbol = tleLine1.substring(_Tle.bstar_.start, BSTAR_PART_2);
|
|
14118
|
-
|
|
14166
|
+
const mantissaFraction = tleLine1.substring(BSTAR_PART_2, BSTAR_PART_3);
|
|
14119
14167
|
const exponentSymbol = tleLine1.substring(BSTAR_PART_3, BSTAR_PART_4);
|
|
14120
14168
|
let exponent = parseInt(tleLine1.substring(BSTAR_PART_4, _Tle.bstar_.stop));
|
|
14121
14169
|
if (exponentSymbol === "-") {
|
|
@@ -14123,12 +14171,16 @@ ${this.line2}`;
|
|
|
14123
14171
|
} else if (exponentSymbol !== "+" && exponentSymbol !== " " && exponentSymbol !== "0") {
|
|
14124
14172
|
throw new ParseError(`Invalid BSTAR exponent symbol: ${exponentSymbol}`, "TLE");
|
|
14125
14173
|
}
|
|
14126
|
-
|
|
14174
|
+
let sign2 = 1;
|
|
14175
|
+
let integerPart = "0";
|
|
14127
14176
|
if (bstarSymbol === "-") {
|
|
14128
|
-
|
|
14129
|
-
} else if (bstarSymbol === "+" || bstarSymbol === " " || bstarSymbol === "0") ; else {
|
|
14177
|
+
sign2 = -1;
|
|
14178
|
+
} else if (bstarSymbol === "+" || bstarSymbol === " " || bstarSymbol === "0") ; else if (/^\d$/u.test(bstarSymbol)) {
|
|
14179
|
+
integerPart = bstarSymbol;
|
|
14180
|
+
} else {
|
|
14130
14181
|
throw new ParseError(`Invalid BSTAR symbol: ${bstarSymbol}`, "TLE");
|
|
14131
14182
|
}
|
|
14183
|
+
const bstar1 = sign2 * parseFloat(`${integerPart}.${mantissaFraction}`) * 10 ** exponent;
|
|
14132
14184
|
return toPrecision(bstar1, 14);
|
|
14133
14185
|
}
|
|
14134
14186
|
/**
|
|
@@ -14183,22 +14235,25 @@ ${this.line2}`;
|
|
|
14183
14235
|
}
|
|
14184
14236
|
/**
|
|
14185
14237
|
* Private value - used by United States Space Force to reference the orbit model used to generate the Tle. Will
|
|
14186
|
-
* always be seen as zero externally (e.g. by "us", unless you are "them" - in which case, hello!).
|
|
14238
|
+
* almost always be seen as zero externally (e.g. by "us", unless you are "them" - in which case, hello!).
|
|
14187
14239
|
*
|
|
14188
|
-
*
|
|
14189
|
-
*
|
|
14240
|
+
* A value of 1 is tolerated in addition to 0: the field is informational and does not change how SGP4/SDP4
|
|
14241
|
+
* propagate the element set, and a handful of archival TLEs carry a 1 (originally an SGP marker).
|
|
14242
|
+
*
|
|
14243
|
+
* A value of 4 indicates the SGP4-XP model. Until that source code is released there is no way to support that
|
|
14244
|
+
* format in JavaScript or TypeScript, so it is rejected explicitly. Any other value is treated as malformed.
|
|
14190
14245
|
* @example 0
|
|
14191
14246
|
* @param tleLine1 The first line of the Tle to parse.
|
|
14192
|
-
* @returns The ephemeris type.
|
|
14247
|
+
* @returns The ephemeris type (0 or 1).
|
|
14193
14248
|
*/
|
|
14194
14249
|
static ephemerisType(tleLine1) {
|
|
14195
14250
|
const ephemerisType = parseInt(tleLine1.substring(_Tle.ephemerisType_.start, _Tle.ephemerisType_.stop));
|
|
14196
|
-
if (ephemerisType !== 0 && ephemerisType !== 4) {
|
|
14197
|
-
throw new ParseError(`Invalid ephemeris type: ${ephemerisType}`, "TLE");
|
|
14198
|
-
}
|
|
14199
14251
|
if (ephemerisType === 4) {
|
|
14200
14252
|
throw new ParseError("SGP4-XP ephemeris type is not supported", "TLE");
|
|
14201
14253
|
}
|
|
14254
|
+
if (ephemerisType !== 0 && ephemerisType !== 1) {
|
|
14255
|
+
throw new ParseError(`Invalid ephemeris type: ${ephemerisType}`, "TLE");
|
|
14256
|
+
}
|
|
14202
14257
|
return ephemerisType;
|
|
14203
14258
|
}
|
|
14204
14259
|
/**
|
|
@@ -14429,6 +14484,9 @@ ${this.line2}`;
|
|
|
14429
14484
|
*/
|
|
14430
14485
|
static satNum(tleLine) {
|
|
14431
14486
|
const satNumStr = tleLine.substring(_Tle.satNum_.start, _Tle.satNum_.stop);
|
|
14487
|
+
if (satNumStr.trim().length === 0) {
|
|
14488
|
+
return NaN;
|
|
14489
|
+
}
|
|
14432
14490
|
const sixDigitSatNum = _Tle.convertA5to6Digit(satNumStr);
|
|
14433
14491
|
return parseInt(sixDigitSatNum);
|
|
14434
14492
|
}
|
|
@@ -14519,16 +14577,23 @@ ${this.line2}`;
|
|
|
14519
14577
|
static parse(tleLine1, tleLine2) {
|
|
14520
14578
|
const line1 = _Tle.parseLine1(tleLine1);
|
|
14521
14579
|
const line2 = _Tle.parseLine2(tleLine2);
|
|
14522
|
-
|
|
14580
|
+
const isVimpel = line1.classification === "V";
|
|
14581
|
+
if (!isVimpel && line1.satNum !== line2.satNum) {
|
|
14582
|
+
console.info("Line 1 satNum:", line1.satNum);
|
|
14583
|
+
console.info("Line 2 satNum:", line2.satNum);
|
|
14523
14584
|
throw new ParseError("Satellite numbers do not match between TLE lines", "TLE");
|
|
14524
14585
|
}
|
|
14525
|
-
if (line1.satNumRaw !== line2.satNumRaw) {
|
|
14586
|
+
if (!isVimpel && line1.satNumRaw !== line2.satNumRaw) {
|
|
14587
|
+
console.info("Line 1 satNumRaw:", line1.satNumRaw);
|
|
14588
|
+
console.info("Line 2 satNumRaw:", line2.satNumRaw);
|
|
14526
14589
|
throw new ParseError("Raw satellite numbers do not match between TLE lines", "TLE");
|
|
14527
14590
|
}
|
|
14528
14591
|
if (line1.lineNumber1 !== 1) {
|
|
14592
|
+
console.info("Line 1 line number:", line1.lineNumber1);
|
|
14529
14593
|
throw new ParseError("First TLE line number must be 1", "TLE");
|
|
14530
14594
|
}
|
|
14531
14595
|
if (line2.lineNumber2 !== 2) {
|
|
14596
|
+
console.info("Line 2 line number:", line2.lineNumber2);
|
|
14532
14597
|
throw new ParseError("Second TLE line number must be 2", "TLE");
|
|
14533
14598
|
}
|
|
14534
14599
|
return {
|
|
@@ -14559,10 +14624,11 @@ ${this.line2}`;
|
|
|
14559
14624
|
static parseAll(tleLine1, tleLine2) {
|
|
14560
14625
|
const line1 = _Tle.parseLine1(tleLine1);
|
|
14561
14626
|
const line2 = _Tle.parseLine2(tleLine2);
|
|
14562
|
-
|
|
14627
|
+
const isVimpel = line1.classification === "V";
|
|
14628
|
+
if (!isVimpel && line1.satNum !== line2.satNum) {
|
|
14563
14629
|
throw new ParseError("Satellite numbers do not match between TLE lines", "TLE");
|
|
14564
14630
|
}
|
|
14565
|
-
if (line1.satNumRaw !== line2.satNumRaw) {
|
|
14631
|
+
if (!isVimpel && line1.satNumRaw !== line2.satNumRaw) {
|
|
14566
14632
|
throw new ParseError("Raw satellite numbers do not match between TLE lines", "TLE");
|
|
14567
14633
|
}
|
|
14568
14634
|
if (line1.lineNumber1 !== 1) {
|
|
@@ -14574,9 +14640,44 @@ ${this.line2}`;
|
|
|
14574
14640
|
return { ...line1, ...line2 };
|
|
14575
14641
|
}
|
|
14576
14642
|
/**
|
|
14577
|
-
*
|
|
14578
|
-
*
|
|
14579
|
-
* @
|
|
14643
|
+
* Classifies a satellite catalog number by representation. See {@link SatNumKind}.
|
|
14644
|
+
* Pure function; no side effects.
|
|
14645
|
+
* @param sccNum The catalog number string.
|
|
14646
|
+
* @returns The {@link SatNumKind} describing the input.
|
|
14647
|
+
*/
|
|
14648
|
+
static classifySatNum(sccNum) {
|
|
14649
|
+
if (typeof sccNum !== "string") {
|
|
14650
|
+
return "invalid";
|
|
14651
|
+
}
|
|
14652
|
+
const trimmed = sccNum.trim();
|
|
14653
|
+
if (trimmed.length === 0) {
|
|
14654
|
+
return "invalid";
|
|
14655
|
+
}
|
|
14656
|
+
const first = trimmed[0];
|
|
14657
|
+
if (trimmed.length === 5 && first in _Tle.alpha5_) {
|
|
14658
|
+
return /^\d{4}$/u.test(trimmed.slice(1)) ? "alpha5" : "invalid";
|
|
14659
|
+
}
|
|
14660
|
+
if (!/^\d+$/u.test(trimmed)) {
|
|
14661
|
+
return "invalid";
|
|
14662
|
+
}
|
|
14663
|
+
if (trimmed.length <= 5) {
|
|
14664
|
+
return "numeric5";
|
|
14665
|
+
}
|
|
14666
|
+
if (trimmed.length === 6) {
|
|
14667
|
+
return parseInt(trimmed, 10) <= 339999 ? "numeric6" : "extended";
|
|
14668
|
+
}
|
|
14669
|
+
return "extended";
|
|
14670
|
+
}
|
|
14671
|
+
/**
|
|
14672
|
+
* Converts a 6-digit numeric SCC number to its 5-character alpha-5 form.
|
|
14673
|
+
*
|
|
14674
|
+
* Inputs shorter than 6 chars or already in alpha-5 form pass through unchanged.
|
|
14675
|
+
*
|
|
14676
|
+
* @param sccNum The SCC number to convert.
|
|
14677
|
+
* @returns The 5-character alpha-5 representation.
|
|
14678
|
+
* @throws {ValidationError} If `sccNum` exceeds the alpha-5 range (numeric value > 339 999,
|
|
14679
|
+
* or length > 6). For such IDs the canonical value should be kept on
|
|
14680
|
+
* `Satellite.sccNum` and the TLE column populated with the last 5 digits.
|
|
14580
14681
|
*/
|
|
14581
14682
|
static convert6DigitToA5(sccNum) {
|
|
14582
14683
|
if (sccNum.length < 6) {
|
|
@@ -14585,9 +14686,27 @@ ${this.line2}`;
|
|
|
14585
14686
|
if (typeof sccNum[0] !== "string") {
|
|
14586
14687
|
throw new ParseError("Invalid SCC number format", "TLE");
|
|
14587
14688
|
}
|
|
14689
|
+
if (sccNum.length > 6) {
|
|
14690
|
+
throw new ValidationError(
|
|
14691
|
+
"SCC number exceeds TLE alpha-5 capacity (max 339999); use Satellite.sccNum for the canonical ID",
|
|
14692
|
+
"sccNum",
|
|
14693
|
+
sccNum
|
|
14694
|
+
);
|
|
14695
|
+
}
|
|
14588
14696
|
if (RegExp(/[A-Z]/iu, "u").test(sccNum[0])) {
|
|
14589
14697
|
return sccNum;
|
|
14590
14698
|
}
|
|
14699
|
+
const numericValue = parseInt(sccNum, 10);
|
|
14700
|
+
if (isNaN(numericValue)) {
|
|
14701
|
+
throw new ValidationError("SCC number must be numeric", "sccNum", sccNum);
|
|
14702
|
+
}
|
|
14703
|
+
if (numericValue > 339999) {
|
|
14704
|
+
throw new ValidationError(
|
|
14705
|
+
"SCC number exceeds TLE alpha-5 capacity (max 339999); use Satellite.sccNum for the canonical ID",
|
|
14706
|
+
"sccNum",
|
|
14707
|
+
sccNum
|
|
14708
|
+
);
|
|
14709
|
+
}
|
|
14591
14710
|
const rest = sccNum.slice(2, 6);
|
|
14592
14711
|
let first = parseInt(`${sccNum[0]}${sccNum[1]}`);
|
|
14593
14712
|
const iPlus = first >= 18 ? 1 : 0;
|
|
@@ -14596,9 +14715,17 @@ ${this.line2}`;
|
|
|
14596
14715
|
return `${String.fromCharCode(first + 55)}${rest}`;
|
|
14597
14716
|
}
|
|
14598
14717
|
/**
|
|
14599
|
-
* Converts a 5-
|
|
14600
|
-
*
|
|
14601
|
-
*
|
|
14718
|
+
* Converts a 5-character alpha-5 SCC number to its 6-digit numeric form.
|
|
14719
|
+
*
|
|
14720
|
+
* Inputs shorter than 5 chars pass through unchanged. Numeric inputs without
|
|
14721
|
+
* an alpha-5 leading letter pass through unchanged (5-digit numeric, in-range
|
|
14722
|
+
* 6-digit numeric, and extended 7+ digit numeric IDs are all identity).
|
|
14723
|
+
*
|
|
14724
|
+
* @param sccNum The SCC number to convert.
|
|
14725
|
+
* @returns The 6-digit numeric representation, or the input itself if it
|
|
14726
|
+
* is already a numeric form.
|
|
14727
|
+
* @throws {ValidationError} If `sccNum` is malformed: 6-digit numeric whose
|
|
14728
|
+
* value exceeds 339 999, or contains stray letters in a non-leading position.
|
|
14602
14729
|
*/
|
|
14603
14730
|
static convertA5to6Digit(sccNum) {
|
|
14604
14731
|
if (sccNum.length < 5) {
|
|
@@ -14609,10 +14736,33 @@ ${this.line2}`;
|
|
|
14609
14736
|
throw new ParseError("Invalid SCC number format", "TLE");
|
|
14610
14737
|
}
|
|
14611
14738
|
if (values[0] in _Tle.alpha5_) {
|
|
14739
|
+
if (!/^\d+$/u.test(sccNum.slice(1))) {
|
|
14740
|
+
throw new ValidationError(
|
|
14741
|
+
"Alpha-5 SCC number must have 4 trailing digits",
|
|
14742
|
+
"sccNum",
|
|
14743
|
+
sccNum
|
|
14744
|
+
);
|
|
14745
|
+
}
|
|
14612
14746
|
const firstLetter = values[0];
|
|
14613
14747
|
values[0] = _Tle.alpha5_[firstLetter];
|
|
14748
|
+
return values.join("");
|
|
14749
|
+
}
|
|
14750
|
+
if (!/^\s*\d+\s*$/u.test(sccNum)) {
|
|
14751
|
+
throw new ValidationError(
|
|
14752
|
+
"SCC number must be alpha-5 or all-numeric",
|
|
14753
|
+
"sccNum",
|
|
14754
|
+
sccNum
|
|
14755
|
+
);
|
|
14756
|
+
}
|
|
14757
|
+
const trimmed = sccNum.trim();
|
|
14758
|
+
if (trimmed.length === 6 && parseInt(trimmed, 10) > 339999) {
|
|
14759
|
+
throw new ValidationError(
|
|
14760
|
+
"6-digit SCC number exceeds TLE alpha-5 capacity (max 339999)",
|
|
14761
|
+
"sccNum",
|
|
14762
|
+
sccNum
|
|
14763
|
+
);
|
|
14614
14764
|
}
|
|
14615
|
-
return
|
|
14765
|
+
return sccNum;
|
|
14616
14766
|
}
|
|
14617
14767
|
};
|
|
14618
14768
|
|
|
@@ -16495,11 +16645,15 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
16495
16645
|
period;
|
|
16496
16646
|
rightAscension;
|
|
16497
16647
|
satrec;
|
|
16498
|
-
/** The satellite catalog number
|
|
16648
|
+
/** The canonical satellite catalog number. May be a 5-digit numeric, alpha-5,
|
|
16649
|
+
* 6-digit numeric, or an extended (7+ digit) ID such as CelesTrak supplemental
|
|
16650
|
+
* 9-digit IDs. */
|
|
16499
16651
|
sccNum;
|
|
16500
|
-
/** The 5
|
|
16652
|
+
/** The 5-character alpha-5 representation, or `null` when {@link sccNum} is
|
|
16653
|
+
* an extended ID that exceeds the alpha-5 capacity (max numeric value 339 999). */
|
|
16501
16654
|
sccNum5;
|
|
16502
|
-
/** The 6
|
|
16655
|
+
/** The 6-digit numeric representation, or `null` when {@link sccNum} is
|
|
16656
|
+
* an extended ID that exceeds the alpha-5 capacity (max numeric value 339 999). */
|
|
16503
16657
|
sccNum6;
|
|
16504
16658
|
tle1;
|
|
16505
16659
|
tle2;
|
|
@@ -16681,13 +16835,57 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
16681
16835
|
return new _Satellite({ omm, name: name ?? omm.OBJECT_NAME });
|
|
16682
16836
|
}
|
|
16683
16837
|
// ==================== TLE/OMM Parsing ====================
|
|
16838
|
+
/**
|
|
16839
|
+
* Converts an OMM international designator (e.g. `"2026-114A"`) to the
|
|
16840
|
+
* TLE intl-des column format (cols 10-17, e.g. `"26114A"`). Inputs that
|
|
16841
|
+
* don't match the OMM `YYYY-NNNL...` pattern pass through unchanged.
|
|
16842
|
+
*/
|
|
16843
|
+
static ommObjectIdToTleIntlDes_(objectId) {
|
|
16844
|
+
if (!objectId) {
|
|
16845
|
+
return "";
|
|
16846
|
+
}
|
|
16847
|
+
const match = objectId.match(/^\d{4}-(?<rest>\d{3}[A-Z]{1,3})$/u);
|
|
16848
|
+
if (match?.groups?.rest) {
|
|
16849
|
+
return objectId.slice(2, 4) + match.groups.rest;
|
|
16850
|
+
}
|
|
16851
|
+
return objectId;
|
|
16852
|
+
}
|
|
16853
|
+
/**
|
|
16854
|
+
* Normalizes {@link sccNum} to the display-canonical numeric form and derives
|
|
16855
|
+
* {@link sccNum5} and {@link sccNum6}. The class invariant is that
|
|
16856
|
+
* `Satellite.sccNum` is always numeric — never an alpha-5 string. Alpha-5
|
|
16857
|
+
* inputs ("T0001") are converted to their 6-digit numeric equivalent
|
|
16858
|
+
* ("270001"); the alpha-5 form is preserved on {@link sccNum5}.
|
|
16859
|
+
*
|
|
16860
|
+
* Extended (7+ digit) IDs that exceed the alpha-5 capacity (max 339 999)
|
|
16861
|
+
* leave sccNum5/sccNum6 set to `null`.
|
|
16862
|
+
*
|
|
16863
|
+
* Invalid input (empty string, malformed token) is passed through unchanged
|
|
16864
|
+
* so callers can store placeholder sccNums on notional / debris stubs.
|
|
16865
|
+
*/
|
|
16866
|
+
assignAlpha5Forms_() {
|
|
16867
|
+
try {
|
|
16868
|
+
this.sccNum = Tle.convertA5to6Digit(this.sccNum);
|
|
16869
|
+
} catch {
|
|
16870
|
+
}
|
|
16871
|
+
if (/^0+\d/u.test(this.sccNum)) {
|
|
16872
|
+
this.sccNum = this.sccNum.replace(/^0+/u, "");
|
|
16873
|
+
}
|
|
16874
|
+
const kind = Tle.classifySatNum(this.sccNum);
|
|
16875
|
+
if (kind === "numeric5" || kind === "numeric6") {
|
|
16876
|
+
this.sccNum5 = Tle.convert6DigitToA5(this.sccNum);
|
|
16877
|
+
this.sccNum6 = this.sccNum;
|
|
16878
|
+
} else {
|
|
16879
|
+
this.sccNum5 = null;
|
|
16880
|
+
this.sccNum6 = null;
|
|
16881
|
+
}
|
|
16882
|
+
}
|
|
16684
16883
|
parseTleAndUpdateOrbit_(tle1, tle2, sccNum) {
|
|
16685
16884
|
const tleData = Tle.parse(tle1, tle2);
|
|
16686
16885
|
this.tle1 = tle1;
|
|
16687
16886
|
this.tle2 = tle2;
|
|
16688
16887
|
this.sccNum = sccNum ?? tleData.satNum.toString();
|
|
16689
|
-
this.
|
|
16690
|
-
this.sccNum6 = Tle.convertA5to6Digit(this.sccNum5);
|
|
16888
|
+
this.assignAlpha5Forms_();
|
|
16691
16889
|
this.intlDes = tleData.intlDes;
|
|
16692
16890
|
this.epochYear = tleData.epochYear;
|
|
16693
16891
|
this.epochDay = tleData.epochDay;
|
|
@@ -16710,8 +16908,7 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
16710
16908
|
parseOmmAndUpdateOrbit_(omm) {
|
|
16711
16909
|
const noradStr = String(omm.NORAD_CAT_ID);
|
|
16712
16910
|
this.sccNum = noradStr.padStart(5, "0");
|
|
16713
|
-
this.
|
|
16714
|
-
this.sccNum6 = Tle.convertA5to6Digit(this.sccNum5);
|
|
16911
|
+
this.assignAlpha5Forms_();
|
|
16715
16912
|
this.intlDes = omm.OBJECT_ID;
|
|
16716
16913
|
const YYYY = omm.EPOCH.slice(0, 4);
|
|
16717
16914
|
const MM = omm.EPOCH.slice(5, 7);
|
|
@@ -16771,8 +16968,10 @@ var Satellite = class _Satellite extends SpaceObject {
|
|
|
16771
16968
|
ecen: this.eccentricity,
|
|
16772
16969
|
epochyr: this.epochYear,
|
|
16773
16970
|
epochday: this.epochDay,
|
|
16774
|
-
intl: omm.OBJECT_ID,
|
|
16775
|
-
|
|
16971
|
+
intl: _Satellite.ommObjectIdToTleIntlDes_(omm.OBJECT_ID),
|
|
16972
|
+
// Extended (7+ digit) IDs don't fit TLE cols 3-7. Truncate to the last 5
|
|
16973
|
+
// digits for the TLE string; the canonical ID stays on this.sccNum.
|
|
16974
|
+
scc: Tle.classifySatNum(this.sccNum) === "extended" ? this.sccNum.slice(-5) : this.sccNum,
|
|
16776
16975
|
bstar: this.bstar,
|
|
16777
16976
|
meanMotionDot: this.meanMoDev1,
|
|
16778
16977
|
meanMotionDdot: this.meanMoDev2,
|
|
@@ -21564,6 +21763,82 @@ var ModifiedGoodingIOD = class {
|
|
|
21564
21763
|
}
|
|
21565
21764
|
};
|
|
21566
21765
|
|
|
21766
|
+
// src/orbit-determination/Rv2Tle.ts
|
|
21767
|
+
var angleDiff_ = (a, b) => {
|
|
21768
|
+
let diff = (a - b) % TAU;
|
|
21769
|
+
if (diff > Math.PI) {
|
|
21770
|
+
diff -= TAU;
|
|
21771
|
+
} else if (diff < -Math.PI) {
|
|
21772
|
+
diff += TAU;
|
|
21773
|
+
}
|
|
21774
|
+
return diff;
|
|
21775
|
+
};
|
|
21776
|
+
var wrapTau_ = (angle) => {
|
|
21777
|
+
let wrapped = angle % TAU;
|
|
21778
|
+
if (wrapped < 0) {
|
|
21779
|
+
wrapped += TAU;
|
|
21780
|
+
}
|
|
21781
|
+
return wrapped;
|
|
21782
|
+
};
|
|
21783
|
+
var toElements_ = (epoch, position, velocity) => new J20002(
|
|
21784
|
+
epoch,
|
|
21785
|
+
new Vector3D(position.x, position.y, position.z),
|
|
21786
|
+
new Vector3D(velocity.x, velocity.y, velocity.z)
|
|
21787
|
+
).toClassicalElements();
|
|
21788
|
+
var rv2tle = (epoch, position, velocity, options = {}) => {
|
|
21789
|
+
const maxIterations = options.maxIterations ?? 15;
|
|
21790
|
+
const toleranceKm = options.toleranceKm ?? 1e-4;
|
|
21791
|
+
const epochUtc = EpochUTC.fromDateTime(epoch);
|
|
21792
|
+
const target = toElements_(epochUtc, position, velocity);
|
|
21793
|
+
let mean2 = new ClassicalElements({
|
|
21794
|
+
epoch: epochUtc,
|
|
21795
|
+
semimajorAxis: target.semimajorAxis,
|
|
21796
|
+
eccentricity: target.eccentricity,
|
|
21797
|
+
inclination: target.inclination,
|
|
21798
|
+
rightAscension: target.rightAscension,
|
|
21799
|
+
argPerigee: target.argPerigee,
|
|
21800
|
+
trueAnomaly: target.trueAnomaly
|
|
21801
|
+
});
|
|
21802
|
+
let best = null;
|
|
21803
|
+
for (let iteration = 1; iteration <= maxIterations; iteration++) {
|
|
21804
|
+
let tle;
|
|
21805
|
+
let sv;
|
|
21806
|
+
try {
|
|
21807
|
+
tle = Tle.fromClassicalElements(mean2);
|
|
21808
|
+
const tsinceMin = (epoch.getTime() - tle.epoch.toDateTime().getTime()) / 6e4;
|
|
21809
|
+
sv = Sgp4.propagate(Sgp4.createSatrec(tle.line1, tle.line2), tsinceMin);
|
|
21810
|
+
} catch {
|
|
21811
|
+
return best;
|
|
21812
|
+
}
|
|
21813
|
+
if (!sv.position || typeof sv.position === "boolean" || !sv.velocity || typeof sv.velocity === "boolean") {
|
|
21814
|
+
return best;
|
|
21815
|
+
}
|
|
21816
|
+
const achievedPos = sv.position;
|
|
21817
|
+
const achievedVel = sv.velocity;
|
|
21818
|
+
const dx = achievedPos.x - position.x;
|
|
21819
|
+
const dy = achievedPos.y - position.y;
|
|
21820
|
+
const dz = achievedPos.z - position.z;
|
|
21821
|
+
const positionErrorKm = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
21822
|
+
if (!best || positionErrorKm < best.positionErrorKm) {
|
|
21823
|
+
best = { tle1: tle.line1, tle2: tle.line2, positionErrorKm, iterations: iteration };
|
|
21824
|
+
}
|
|
21825
|
+
if (positionErrorKm < toleranceKm) {
|
|
21826
|
+
break;
|
|
21827
|
+
}
|
|
21828
|
+
const achieved = toElements_(epochUtc, achievedPos, achievedVel);
|
|
21829
|
+
mean2 = new ClassicalElements({
|
|
21830
|
+
epoch: epochUtc,
|
|
21831
|
+
semimajorAxis: mean2.semimajorAxis + (target.semimajorAxis - achieved.semimajorAxis),
|
|
21832
|
+
eccentricity: Math.min(Math.max(mean2.eccentricity + (target.eccentricity - achieved.eccentricity), 0), 0.999),
|
|
21833
|
+
inclination: Math.min(Math.max(mean2.inclination + angleDiff_(target.inclination, achieved.inclination), 0), Math.PI),
|
|
21834
|
+
rightAscension: wrapTau_(mean2.rightAscension + angleDiff_(target.rightAscension, achieved.rightAscension)),
|
|
21835
|
+
argPerigee: wrapTau_(mean2.argPerigee + angleDiff_(target.argPerigee, achieved.argPerigee)),
|
|
21836
|
+
trueAnomaly: wrapTau_(mean2.trueAnomaly + angleDiff_(target.trueAnomaly, achieved.trueAnomaly))
|
|
21837
|
+
});
|
|
21838
|
+
}
|
|
21839
|
+
return best;
|
|
21840
|
+
};
|
|
21841
|
+
|
|
21567
21842
|
// src/covariance/CovarianceSample.ts
|
|
21568
21843
|
var CovarianceSample = class {
|
|
21569
21844
|
origin_;
|
|
@@ -21756,6 +22031,19 @@ var CovarianceSample = class {
|
|
|
21756
22031
|
}
|
|
21757
22032
|
};
|
|
21758
22033
|
|
|
22034
|
+
// src/covariance/displayRadii.ts
|
|
22035
|
+
function scaleAndClampRicSigmas(sigmas, confidence, caps) {
|
|
22036
|
+
const { radial, inTrack, crossTrack } = sigmas;
|
|
22037
|
+
if (![radial, inTrack, crossTrack].every((v) => Number.isFinite(v) && v >= 0)) {
|
|
22038
|
+
return null;
|
|
22039
|
+
}
|
|
22040
|
+
return {
|
|
22041
|
+
radial: Math.min(radial * confidence, caps.radial),
|
|
22042
|
+
inTrack: Math.min(inTrack * confidence, caps.inTrack),
|
|
22043
|
+
crossTrack: Math.min(crossTrack * confidence, caps.crossTrack)
|
|
22044
|
+
};
|
|
22045
|
+
}
|
|
22046
|
+
|
|
21759
22047
|
// src/conjunction/ConjunctionAssessment.ts
|
|
21760
22048
|
var ConjunctionAssessment = class {
|
|
21761
22049
|
constructor(primary, secondary) {
|
|
@@ -24180,12 +24468,12 @@ var HorizonsParser = class {
|
|
|
24180
24468
|
for (let i = 0; i < lines.length; i++) {
|
|
24181
24469
|
const line = lines[i].trim();
|
|
24182
24470
|
if (line.startsWith("Target body name:")) {
|
|
24183
|
-
targetName = this.
|
|
24471
|
+
targetName = this.extractTargetName_(line, "Target body name:");
|
|
24184
24472
|
metadata.targetName = targetName;
|
|
24185
24473
|
} else if (line.startsWith("Center body name:")) {
|
|
24186
24474
|
centerBody = this.extractValue_(line, "Center body name:");
|
|
24187
24475
|
metadata.centerBody = centerBody;
|
|
24188
|
-
} else if (line.startsWith("Center-site name:")) {
|
|
24476
|
+
} else if (line.startsWith("Center-site name:") && !centerBody) {
|
|
24189
24477
|
centerBody = this.extractValue_(line, "Center-site name:");
|
|
24190
24478
|
metadata.centerBody = centerBody;
|
|
24191
24479
|
} else if (line.startsWith("Output type:")) {
|
|
@@ -24240,7 +24528,7 @@ var HorizonsParser = class {
|
|
|
24240
24528
|
for (let i = 0; i < lines.length; i++) {
|
|
24241
24529
|
const line = lines[i].trim();
|
|
24242
24530
|
if (line.startsWith("Target body name:")) {
|
|
24243
|
-
targetName = this.
|
|
24531
|
+
targetName = this.extractTargetName_(line, "Target body name:");
|
|
24244
24532
|
} else if (line.startsWith("Center-site name:")) {
|
|
24245
24533
|
observerLocation = this.extractValue_(line, "Center-site name:");
|
|
24246
24534
|
}
|
|
@@ -24271,7 +24559,23 @@ var HorizonsParser = class {
|
|
|
24271
24559
|
*/
|
|
24272
24560
|
static extractValue_(line, key) {
|
|
24273
24561
|
const value = line.substring(key.length).trim();
|
|
24274
|
-
return value.split(/\s{2,}/)[0].trim();
|
|
24562
|
+
return value.split(/\s{2,}/u)[0].trim();
|
|
24563
|
+
}
|
|
24564
|
+
/**
|
|
24565
|
+
* Extracts the target body name, stripping parenthetical descriptors and IDs.
|
|
24566
|
+
*
|
|
24567
|
+
* Horizons format examples:
|
|
24568
|
+
* - "1 Ceres {source: astDys}" → "1 Ceres"
|
|
24569
|
+
* - "Artemis II (spacecraft) (-1024) {source: ...}" → "Artemis II"
|
|
24570
|
+
* - "Mars (499)" → "Mars"
|
|
24571
|
+
*/
|
|
24572
|
+
static extractTargetName_(line, key) {
|
|
24573
|
+
const raw = this.extractValue_(line, key);
|
|
24574
|
+
const parenIdx = raw.search(/\s\(/u);
|
|
24575
|
+
if (parenIdx > 0) {
|
|
24576
|
+
return raw.substring(0, parenIdx).trim();
|
|
24577
|
+
}
|
|
24578
|
+
return raw;
|
|
24275
24579
|
}
|
|
24276
24580
|
/**
|
|
24277
24581
|
* Parses a vector data line from Horizons output.
|
|
@@ -24341,7 +24645,9 @@ var HorizonsParser = class {
|
|
|
24341
24645
|
return null;
|
|
24342
24646
|
}
|
|
24343
24647
|
const posLine = allLines[nextIndex].trim();
|
|
24344
|
-
const
|
|
24648
|
+
const posMatchLabeled = posLine.match(/X\s*=\s*([-\d.E+]+)\s+Y\s*=\s*([-\d.E+]+)\s+Z\s*=\s*([-\d.E+]+)/iu);
|
|
24649
|
+
const posMatchBare = !posMatchLabeled ? posLine.match(/^\s*([-\d.E+]+)\s+([-\d.E+]+)\s+([-\d.E+]+)\s*$/iu) : null;
|
|
24650
|
+
const posMatch = posMatchLabeled ?? posMatchBare;
|
|
24345
24651
|
if (!posMatch) {
|
|
24346
24652
|
return null;
|
|
24347
24653
|
}
|
|
@@ -24353,7 +24659,9 @@ var HorizonsParser = class {
|
|
|
24353
24659
|
let velocity;
|
|
24354
24660
|
if (nextIndex < allLines.length) {
|
|
24355
24661
|
const velLine = allLines[nextIndex].trim();
|
|
24356
|
-
const
|
|
24662
|
+
const velMatchLabeled = velLine.match(/VX\s*=\s*([-\d.E+]+)\s+VY\s*=\s*([-\d.E+]+)\s+VZ\s*=\s*([-\d.E+]+)/iu);
|
|
24663
|
+
const velMatchBare = !velMatchLabeled ? velLine.match(/^\s*([-\d.E+]+)\s+([-\d.E+]+)\s+([-\d.E+]+)\s*$/iu) : null;
|
|
24664
|
+
const velMatch = velMatchLabeled ?? velMatchBare;
|
|
24357
24665
|
if (velMatch) {
|
|
24358
24666
|
velocity = new Vector3D(
|
|
24359
24667
|
parseFloat(velMatch[1]),
|
|
@@ -24363,6 +24671,17 @@ var HorizonsParser = class {
|
|
|
24363
24671
|
nextIndex++;
|
|
24364
24672
|
}
|
|
24365
24673
|
}
|
|
24674
|
+
while (nextIndex < allLines.length) {
|
|
24675
|
+
const extraLine = allLines[nextIndex].trim();
|
|
24676
|
+
if (extraLine === "$$EOE" || extraLine.length === 0 || /^\d+\.\d+\s*=\s*A\.D\./u.test(extraLine)) {
|
|
24677
|
+
break;
|
|
24678
|
+
}
|
|
24679
|
+
if (/^\s*[-\d.E+]+(\s+[-\d.E+]+)*\s*$/u.test(extraLine)) {
|
|
24680
|
+
nextIndex++;
|
|
24681
|
+
} else {
|
|
24682
|
+
break;
|
|
24683
|
+
}
|
|
24684
|
+
}
|
|
24366
24685
|
return {
|
|
24367
24686
|
data: { epoch, position, velocity },
|
|
24368
24687
|
nextIndex
|
|
@@ -25109,6 +25428,58 @@ var OdmExporter = class _OdmExporter {
|
|
|
25109
25428
|
return lines.join("\n");
|
|
25110
25429
|
}
|
|
25111
25430
|
// ==========================================================================
|
|
25431
|
+
// OEM from State Vectors — direct J2000 array export
|
|
25432
|
+
// ==========================================================================
|
|
25433
|
+
/**
|
|
25434
|
+
* Export an array of J2000 state vectors as OEM KVN.
|
|
25435
|
+
*
|
|
25436
|
+
* Unlike {@link formatOem}, which propagates a TLE-based satellite, this
|
|
25437
|
+
* method serializes pre-computed state vectors directly. Useful for
|
|
25438
|
+
* converting parsed ephemeris data (e.g., NASA Horizons) to CCSDS OEM.
|
|
25439
|
+
*
|
|
25440
|
+
* @param stateVectors - Array of J2000 state vectors to export
|
|
25441
|
+
* @param metadata - Object identification metadata
|
|
25442
|
+
* @param options - Export configuration
|
|
25443
|
+
* @returns OEM KVN format string
|
|
25444
|
+
*/
|
|
25445
|
+
static formatOemFromStateVectors(stateVectors, metadata, options = {}) {
|
|
25446
|
+
if (stateVectors.length === 0) {
|
|
25447
|
+
throw new Error("Cannot export OEM with no state vectors");
|
|
25448
|
+
}
|
|
25449
|
+
const refFrame = options.refFrame ?? "EME2000";
|
|
25450
|
+
const centerName = options.centerName ?? "EARTH";
|
|
25451
|
+
const startTime = stateVectors[0].epoch.toDateTime();
|
|
25452
|
+
const stopTime = stateVectors[stateVectors.length - 1].epoch.toDateTime();
|
|
25453
|
+
const lines = [];
|
|
25454
|
+
lines.push("CCSDS_OEM_VERS = 2.0");
|
|
25455
|
+
_OdmExporter.appendComments_(lines, options.comments);
|
|
25456
|
+
lines.push(`CREATION_DATE = ${_OdmExporter.formatDateTime_(/* @__PURE__ */ new Date())}`);
|
|
25457
|
+
lines.push(`ORIGINATOR = ${options.originator ?? "KeepTrack"}`);
|
|
25458
|
+
if (options.messageId) {
|
|
25459
|
+
lines.push(`MESSAGE_ID = ${options.messageId}`);
|
|
25460
|
+
}
|
|
25461
|
+
lines.push("");
|
|
25462
|
+
lines.push("META_START");
|
|
25463
|
+
lines.push(`OBJECT_NAME = ${metadata.objectName}`);
|
|
25464
|
+
lines.push(`OBJECT_ID = ${metadata.objectId}`);
|
|
25465
|
+
lines.push(`CENTER_NAME = ${centerName}`);
|
|
25466
|
+
lines.push(`REF_FRAME = ${refFrame}`);
|
|
25467
|
+
lines.push("TIME_SYSTEM = UTC");
|
|
25468
|
+
lines.push(`START_TIME = ${_OdmExporter.formatDateTime_(startTime)}`);
|
|
25469
|
+
lines.push(`STOP_TIME = ${_OdmExporter.formatDateTime_(stopTime)}`);
|
|
25470
|
+
lines.push(`INTERPOLATION = ${options.interpolation ?? "LAGRANGE"}`);
|
|
25471
|
+
lines.push(`INTERPOLATION_DEGREE = ${options.interpolationDegree ?? 7}`);
|
|
25472
|
+
lines.push("META_STOP");
|
|
25473
|
+
lines.push("");
|
|
25474
|
+
for (const sv of stateVectors) {
|
|
25475
|
+
const epoch = _OdmExporter.formatDateTime_(sv.epoch.toDateTime());
|
|
25476
|
+
lines.push(
|
|
25477
|
+
`${epoch} ${_OdmExporter.formatNumber_(sv.position.x)} ${_OdmExporter.formatNumber_(sv.position.y)} ${_OdmExporter.formatNumber_(sv.position.z)} ${_OdmExporter.formatNumber_(sv.velocity.x)} ${_OdmExporter.formatNumber_(sv.velocity.y)} ${_OdmExporter.formatNumber_(sv.velocity.z)}`
|
|
25478
|
+
);
|
|
25479
|
+
}
|
|
25480
|
+
return lines.join("\n");
|
|
25481
|
+
}
|
|
25482
|
+
// ==========================================================================
|
|
25112
25483
|
// OMM — Orbit Mean-Elements Message
|
|
25113
25484
|
// ==========================================================================
|
|
25114
25485
|
/**
|
|
@@ -29475,8 +29846,20 @@ var OrbitFinder = class _OrbitFinder {
|
|
|
29475
29846
|
this.debug_(`Found perigee at altitude ${perigeeParams.altitude} km`);
|
|
29476
29847
|
return perigeeParams;
|
|
29477
29848
|
}
|
|
29849
|
+
/**
|
|
29850
|
+
* Returns the 5-char satnum to embed in the synthesized TLEs.
|
|
29851
|
+
*
|
|
29852
|
+
* Why not {@link Satellite.sccNum}? For extended (7+ digit) IDs sccNum is
|
|
29853
|
+
* the full 9-digit canonical value, which would push the TLE past 69 chars
|
|
29854
|
+
* and break Sgp4.createSatrec. The input tle1 always has a valid 5-char
|
|
29855
|
+
* satnum at columns 2-6, and SGP4 itself doesn't care what value lives
|
|
29856
|
+
* there as long as line 1 and line 2 agree — so use that.
|
|
29857
|
+
*/
|
|
29858
|
+
tleSatNum_() {
|
|
29859
|
+
return this.sat_.tle1.substring(2, 7);
|
|
29860
|
+
}
|
|
29478
29861
|
generateTle1_() {
|
|
29479
|
-
return `1 ${this.
|
|
29862
|
+
return `1 ${this.tleSatNum_()}U ${this.sat_.tle1.substring(9, 17)} ${this.sat_.tle1.substring(18, 32)}${this.sat_.tle1.substring(32, 71)}`;
|
|
29480
29863
|
}
|
|
29481
29864
|
generateTle2_(newParams) {
|
|
29482
29865
|
const mergedParams = {
|
|
@@ -29491,7 +29874,7 @@ var OrbitFinder = class _OrbitFinder {
|
|
|
29491
29874
|
const argPer = mergedParams.argOfPerigee.toFixed(4).padStart(8, "0");
|
|
29492
29875
|
const meanA = mergedParams.meanAnomaly.toFixed(4).padStart(8, "0");
|
|
29493
29876
|
const meanMo = this.sat_.tle2.substring(52, 63);
|
|
29494
|
-
return `2 ${this.
|
|
29877
|
+
return `2 ${this.tleSatNum_()} ${inc} ${raan} ${ecc} ${argPer} ${meanA} ${meanMo} 10`;
|
|
29495
29878
|
}
|
|
29496
29879
|
};
|
|
29497
29880
|
|
|
@@ -29851,6 +30234,33 @@ var RepeatGroundTrack = class _RepeatGroundTrack {
|
|
|
29851
30234
|
* You should have received a copy of the GNU Affero General Public License along with
|
|
29852
30235
|
* Orbital Object ToolKit. If not, see <http://www.gnu.org/licenses/>.
|
|
29853
30236
|
*/
|
|
30237
|
+
/**
|
|
30238
|
+
* @author Theodore Kruczek.
|
|
30239
|
+
* @description Orbital Object ToolKit (ootk) is a collection of tools for working
|
|
30240
|
+
* with satellites and other orbital objects.
|
|
30241
|
+
*
|
|
30242
|
+
* @license MIT License
|
|
30243
|
+
*
|
|
30244
|
+
* @Copyright (c) 2025 Theodore Kruczek
|
|
30245
|
+
*
|
|
30246
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
30247
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
30248
|
+
* in the Software without restriction, including without limitation the rights
|
|
30249
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
30250
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
30251
|
+
* furnished to do so, subject to the following conditions:
|
|
30252
|
+
*
|
|
30253
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
30254
|
+
* copies or substantial portions of the Software.
|
|
30255
|
+
*
|
|
30256
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
30257
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
30258
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30259
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
30260
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30261
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30262
|
+
* SOFTWARE.
|
|
30263
|
+
*/
|
|
29854
30264
|
/**
|
|
29855
30265
|
* @author Theodore Kruczek
|
|
29856
30266
|
* @description Orbital Object ToolKit (ootk) is a collection of tools for working
|
|
@@ -29952,4 +30362,4 @@ astronomy-engine/esm/astronomy.js:
|
|
|
29952
30362
|
*)
|
|
29953
30363
|
*/
|
|
29954
30364
|
|
|
29955
|
-
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, J20002 as 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, OdmExporter, OemParser, OmmParser, OotkError, OpticalSensor, OrbitDeterminationError, OrbitFinder, OrbitRegime, PI, ParseError, PassType, PassiveRFSensor, PayloadStatus, PhasedArrayRadar, PlaneChangeBurn, PlanetBody, Pluto, PolynomialRegression, ProbabilityOfCollision, PropagationError, Propagator, PropagatorType, Quaternion, RAD2DEG, RADIUS_OF_EARTH, RAE, RIC, RadarSensor, RadecGeocentric, RadecTopocentric, Random, RandomGaussianSource, Receiver, RelativeState, RepeatGroundTrack, RkCheckpoint, RkResult, RungeKutta4Propagator, RungeKutta89Propagator, RungeKuttaAdaptive, SLR_WAVELENGTHS, SPEED_OF_LIGHT_KM_S, Satellite, Saturn, ScheduledContact, ScreeningFilter, SegmentedLagrangeInterpolator, Sensor, SensorType, Sgp4, Sgp4Error, Sgp4ErrorCode, Sgp4OpsMode, Sgp4Propagator, SimpleLinearRegression, SolarRadiationPressure, SolarSystem, SpaceObject, SpaceObjectType, Star, StateCovariance, StateInterpolator, StateVector2 as StateVector, Sun, SunBody, SunStatus, TAU, TEME, ThirdBodyGravity, Thrust, TimeStamped, Tle, Transmitter, Transponder, 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, clamp, concat, copySign, covariance, createCovarianceFromTle, createSampleCovarianceFromTle, createVec, csch, dbmToDbw, dbwToDbm, dbwToWatts, deg2rad, derivative, dopplerFactor, earthGravityParam, ecef2eci, ecef2enu, ecef2rae, ecefRad2rae, eci2ecef, eci2lla, eci2rae, enu2rf, estimateRcs, evalPoly, factorial, gamma, getDayOfYear, getDegLat, getDegLon, getRadLat, getRadLon, halfPi, isLeapYear, jacobian, jday, linearDistance, linearInterpolate, lla2ecef, lla2eci, lla2sez, llaRad2ecef, log10, mag2db, masec2rad, matchHalfPlane, mean, msec2sec, newtonM, newtonNu, normalizeAngle, observationDerivative, observationNoiseFromSigmas, parseCenterBody, rad2deg, radecToPosition, radecToVelocity, rae2ecef, rae2eci, rae2enu, rae2raeOffBoresight, rae2ruv, rae2sez, relativeVelocity, sec2day, sec2deg, sec2min, sech, secondsPerDay, secondsPerSiderealDay, secondsPerWeek, sez2rae, sign, spaceObjType2Str, std, temp4, toPrecision, ttasec2rad, uv2azel, wattsToDbw, wrapAngle, x2o3 };
|
|
30365
|
+
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, J20002 as 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, OdmExporter, OemParser, OmmParser, OotkError, OpticalSensor, OrbitDeterminationError, OrbitFinder, OrbitRegime, PI, ParseError, PassType, PassiveRFSensor, PayloadStatus, PhasedArrayRadar, PlaneChangeBurn, PlanetBody, Pluto, PolynomialRegression, ProbabilityOfCollision, PropagationError, Propagator, PropagatorType, 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, SLR_WAVELENGTHS, SPEED_OF_LIGHT_KM_S, Satellite, Saturn, ScheduledContact, ScreeningFilter, SegmentedLagrangeInterpolator, Sensor, SensorType, Sgp4, Sgp4Error, Sgp4ErrorCode, Sgp4OpsMode, Sgp4Propagator, SimpleLinearRegression, SolarRadiationPressure, SolarSystem, SpaceObject, SpaceObjectType, Star, StateCovariance, StateInterpolator, StateVector2 as StateVector, Sun, SunBody, SunStatus, TAU, TEME, ThirdBodyGravity, Thrust, TimeStamped, Tle, Transmitter, Transponder, 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, halfPi, isLeapYear, jacobian, jday, linearDistance, linearInterpolate, lla2ecef, lla2eci, lla2sez, llaRad2ecef, log10, mag2db, masec2rad, matchHalfPlane, mean, msec2sec, newtonM, newtonNu, normalizeAngle, observationDerivative, observationNoiseFromSigmas, parseCenterBody, rad2deg, radecToPosition, radecToVelocity, rae2ecef, rae2eci, rae2enu, rae2raeOffBoresight, rae2ruv, rae2sez, relativeVelocity, rv2tle, scaleAndClampRicSigmas, sec2day, sec2deg, sec2min, sech, secondsPerDay, secondsPerSiderealDay, secondsPerWeek, sez2rae, sign, spaceObjType2Str, std, temp4, toPrecision, ttasec2rad, uv2azel, wattsToDbw, wrapAngle, x2o3 };
|