ootk 6.0.6 → 7.0.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/dist/main.js CHANGED
@@ -21,8 +21,6 @@ var ValidationError = class extends OotkError {
21
21
  this.value = value;
22
22
  this.name = "ValidationError";
23
23
  }
24
- field;
25
- value;
26
24
  };
27
25
  var ParseError = class extends OotkError {
28
26
  /**
@@ -37,8 +35,6 @@ var ParseError = class extends OotkError {
37
35
  this.line = line;
38
36
  this.name = "ParseError";
39
37
  }
40
- format;
41
- line;
42
38
  };
43
39
  var PropagationError = class extends OotkError {
44
40
  /**
@@ -51,7 +47,6 @@ var PropagationError = class extends OotkError {
51
47
  this.epoch = epoch;
52
48
  this.name = "PropagationError";
53
49
  }
54
- epoch;
55
50
  };
56
51
  var OrbitDeterminationError = class extends OotkError {
57
52
  /**
@@ -64,7 +59,6 @@ var OrbitDeterminationError = class extends OotkError {
64
59
  this.algorithm = algorithm;
65
60
  this.name = "OrbitDeterminationError";
66
61
  }
67
- algorithm;
68
62
  };
69
63
 
70
64
  // src/enums/AngularDiameterMethod.ts
@@ -282,7 +276,6 @@ var Epoch = class {
282
276
  throw new ValidationError("Epoch posix time must be a valid number", "posix", posix);
283
277
  }
284
278
  }
285
- posix;
286
279
  toString() {
287
280
  return this.toDateTime().toISOString();
288
281
  }
@@ -306,7 +299,7 @@ var Epoch = class {
306
299
  const currentDateObj = this.toDateTime();
307
300
  const epochYear = currentDateObj.getUTCFullYear().toString().slice(2, 4);
308
301
  const epochDay = this.getDayOfYear_(currentDateObj);
309
- const timeOfDay = (currentDateObj.getUTCHours() * 60 + currentDateObj.getUTCMinutes()) / 1440;
302
+ const timeOfDay = (currentDateObj.getUTCHours() * 3600 + currentDateObj.getUTCMinutes() * 60 + currentDateObj.getUTCSeconds() + currentDateObj.getUTCMilliseconds() / 1e3) / 86400;
310
303
  const epochDayStr = (epochDay + timeOfDay).toFixed(8).padStart(12, "0");
311
304
  return {
312
305
  epochYr: epochYear,
@@ -1826,8 +1819,6 @@ var EpochGPS = class _EpochGPS {
1826
1819
  throw new ValidationError("GPS seconds must be between 0 and 604799", "seconds", seconds);
1827
1820
  }
1828
1821
  }
1829
- week;
1830
- seconds;
1831
1822
  /** Cached GPS reference epoch (1980-01-06T00:00:00.000Z) */
1832
1823
  static reference_ = null;
1833
1824
  /**
@@ -2253,6 +2244,32 @@ function createSampleCovarianceFromTle(tleLine1, tleLine2, frame = "ric" /* RIC
2253
2244
  const sample = new CovarianceSample(j2000State, covariance2, tle);
2254
2245
  return frame === "ric" /* RIC */ ? sample.desampleRIC() : sample.desampleJ2000();
2255
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
+ }
2256
2273
 
2257
2274
  // src/utils/jacobian.ts
2258
2275
  var jacobian = (f, m, x0, step = 1e-5) => {
@@ -2279,6 +2296,17 @@ function linearDistance(pos1, pos2) {
2279
2296
  return Math.sqrt((pos1.x - pos2.x) ** 2 + (pos1.y - pos2.y) ** 2 + (pos1.z - pos2.z) ** 2);
2280
2297
  }
2281
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
+
2282
2310
  // src/operations/Vector3D.ts
2283
2311
  var Vector3D = class _Vector3D {
2284
2312
  constructor(x, y, z) {
@@ -2286,9 +2314,6 @@ var Vector3D = class _Vector3D {
2286
2314
  this.y = y;
2287
2315
  this.z = z;
2288
2316
  }
2289
- x;
2290
- y;
2291
- z;
2292
2317
  /**
2293
2318
  * Create a new Vector3D object from the first three elements of a Vector
2294
2319
  * object.
@@ -2486,7 +2511,6 @@ var Vector = class _Vector {
2486
2511
  this.elements = elements;
2487
2512
  this.length = elements.length;
2488
2513
  }
2489
- elements;
2490
2514
  /**
2491
2515
  * The length of the vector.
2492
2516
  */
@@ -3267,7 +3291,7 @@ var Random = class {
3267
3291
  }
3268
3292
  };
3269
3293
 
3270
- // ../../../node_modules/astronomy-engine/esm/astronomy.js
3294
+ // node_modules/astronomy-engine/esm/astronomy.js
3271
3295
  var C_AUDAY = 173.1446326846693;
3272
3296
  var KM_PER_AU2 = 14959787069098932e-8;
3273
3297
  var DEG2RAD2 = 0.017453292519943295;
@@ -8607,12 +8631,6 @@ var CubicSpline = class {
8607
8631
  this.p1 = p1;
8608
8632
  this.m1 = m1;
8609
8633
  }
8610
- t0;
8611
- p0;
8612
- m0;
8613
- t1;
8614
- p1;
8615
- m1;
8616
8634
  // / Interpolate position at the provided time [t] _(POSIX seconds)_.
8617
8635
  position_(t) {
8618
8636
  const t2 = t * t;
@@ -8688,7 +8706,6 @@ var CubicSplineInterpolator = class _CubicSplineInterpolator extends StateInterp
8688
8706
  super();
8689
8707
  this.splines_ = splines_;
8690
8708
  }
8691
- splines_;
8692
8709
  static fromEphemeris(ephemeris) {
8693
8710
  const splines = [];
8694
8711
  for (let i = 0; i < ephemeris.length - 1; i++) {
@@ -8888,7 +8905,6 @@ var VerletBlendInterpolator = class _VerletBlendInterpolator extends StateInterp
8888
8905
  super();
8889
8906
  this.ephemeris = ephemeris;
8890
8907
  }
8891
- ephemeris;
8892
8908
  get sizeBytes() {
8893
8909
  return 64 * 7 * this.ephemeris.length / 8;
8894
8910
  }
@@ -9876,7 +9892,10 @@ var FormatTle = class _FormatTle {
9876
9892
  */
9877
9893
  static createTle(tleParams) {
9878
9894
  const { inc, meanmo, rasc, argPe, meana, ecen, epochyr, epochday, intl } = tleParams;
9879
- const scc = Tle.convert6DigitToA5(tleParams.scc);
9895
+ let scc = Tle.convert6DigitToA5(tleParams.scc);
9896
+ if (/^\d{1,4}$/u.test(scc)) {
9897
+ scc = scc.padStart(5, "0");
9898
+ }
9880
9899
  const epochYrStr = String(epochyr).padStart(2, "0");
9881
9900
  const epochdayStr = Number(epochday).toFixed(8).padStart(12, "0");
9882
9901
  const incStr = _FormatTle.inclination(inc);
@@ -10389,7 +10408,7 @@ var Sgp4 = class _Sgp4 {
10389
10408
  error: 0 /* NO_ERROR */
10390
10409
  };
10391
10410
  const xpdotp = 1440 / (2 * PI);
10392
- satrec.satnum = tleLine1.substring(2, 7);
10411
+ satrec.satnum = tleLine1.charAt(7) === "V" ? "0" : tleLine1.substring(2, 7);
10393
10412
  satrec.epochyr = parseInt(tleLine1.substring(18, 20));
10394
10413
  satrec.epochdays = parseFloat(tleLine1.substring(20, 32));
10395
10414
  satrec.ndot = parseFloat(tleLine1.substring(33, 43));
@@ -13556,11 +13575,6 @@ var Thrust = class {
13556
13575
  crosstrack * 1e-3
13557
13576
  );
13558
13577
  }
13559
- center;
13560
- radial;
13561
- intrack;
13562
- crosstrack;
13563
- durationRate;
13564
13578
  deltaV;
13565
13579
  get magnitude() {
13566
13580
  return this.deltaV.magnitude() * 1e3;
@@ -13599,9 +13613,6 @@ var Hill = class _Hill {
13599
13613
  this.semimajorAxis_ = semimajorAxis;
13600
13614
  this.meanMotion_ = Earth.smaToMeanMotion(this.semimajorAxis_);
13601
13615
  }
13602
- epoch;
13603
- position;
13604
- velocity;
13605
13616
  semimajorAxis_;
13606
13617
  meanMotion_;
13607
13618
  static fromState(origin, radialPosition, intrackPosition, nodeVelocity, nodeOffsetTime) {
@@ -13915,7 +13926,7 @@ var Tle = class _Tle {
13915
13926
  this.line1 = line1;
13916
13927
  this.line2 = line2;
13917
13928
  this.epoch = _Tle.parseEpoch_(line1.substring(18, 32));
13918
- this.satnum = parseInt(_Tle.convertA5to6Digit(line1.substring(2, 7)));
13929
+ this.satnum = _Tle.satNum(this.line1);
13919
13930
  this.satrec_ = Sgp4.createSatrec(line1, line2, gravConst, opsMode);
13920
13931
  }
13921
13932
  toString() {
@@ -14004,7 +14015,7 @@ ${this.line2}`;
14004
14015
  epochYearFull = 1900 + epochYearShort;
14005
14016
  }
14006
14017
  const epochJday = epochDayOfYear + epochYearFull * 365;
14007
- const currentJday = getDayOfYear() + currentYearFull * 365;
14018
+ const currentJday = getDayOfYear(nowInput) + currentYearFull * 365;
14008
14019
  const currentTime = (nowInput.getUTCHours() * 3600 + nowInput.getUTCMinutes() * 60 + nowInput.getUTCSeconds()) / 86400;
14009
14020
  const daysOld = currentJday + currentTime - epochJday;
14010
14021
  switch (outputUnits) {
@@ -14152,7 +14163,7 @@ ${this.line2}`;
14152
14163
  const BSTAR_PART_3 = _Tle.bstar_.start + 6;
14153
14164
  const BSTAR_PART_4 = _Tle.bstar_.stop - 1;
14154
14165
  const bstarSymbol = tleLine1.substring(_Tle.bstar_.start, BSTAR_PART_2);
14155
- let bstar1 = parseFloat(`0.${tleLine1.substring(BSTAR_PART_2, BSTAR_PART_3)}`);
14166
+ const mantissaFraction = tleLine1.substring(BSTAR_PART_2, BSTAR_PART_3);
14156
14167
  const exponentSymbol = tleLine1.substring(BSTAR_PART_3, BSTAR_PART_4);
14157
14168
  let exponent = parseInt(tleLine1.substring(BSTAR_PART_4, _Tle.bstar_.stop));
14158
14169
  if (exponentSymbol === "-") {
@@ -14160,12 +14171,16 @@ ${this.line2}`;
14160
14171
  } else if (exponentSymbol !== "+" && exponentSymbol !== " " && exponentSymbol !== "0") {
14161
14172
  throw new ParseError(`Invalid BSTAR exponent symbol: ${exponentSymbol}`, "TLE");
14162
14173
  }
14163
- bstar1 *= 10 ** exponent;
14174
+ let sign2 = 1;
14175
+ let integerPart = "0";
14164
14176
  if (bstarSymbol === "-") {
14165
- bstar1 *= -1;
14166
- } 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 {
14167
14181
  throw new ParseError(`Invalid BSTAR symbol: ${bstarSymbol}`, "TLE");
14168
14182
  }
14183
+ const bstar1 = sign2 * parseFloat(`${integerPart}.${mantissaFraction}`) * 10 ** exponent;
14169
14184
  return toPrecision(bstar1, 14);
14170
14185
  }
14171
14186
  /**
@@ -14220,22 +14235,25 @@ ${this.line2}`;
14220
14235
  }
14221
14236
  /**
14222
14237
  * Private value - used by United States Space Force to reference the orbit model used to generate the Tle. Will
14223
- * 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!).
14224
14239
  *
14225
- * Starting in 2024, this may contain a 4 if the Tle was generated using the new SGP4-XP model. Until the source code
14226
- * is released, there is no way to support that format in JavaScript or TypeScript.
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.
14227
14245
  * @example 0
14228
14246
  * @param tleLine1 The first line of the Tle to parse.
14229
- * @returns The ephemeris type.
14247
+ * @returns The ephemeris type (0 or 1).
14230
14248
  */
14231
14249
  static ephemerisType(tleLine1) {
14232
14250
  const ephemerisType = parseInt(tleLine1.substring(_Tle.ephemerisType_.start, _Tle.ephemerisType_.stop));
14233
- if (ephemerisType !== 0 && ephemerisType !== 4) {
14234
- throw new ParseError(`Invalid ephemeris type: ${ephemerisType}`, "TLE");
14235
- }
14236
14251
  if (ephemerisType === 4) {
14237
14252
  throw new ParseError("SGP4-XP ephemeris type is not supported", "TLE");
14238
14253
  }
14254
+ if (ephemerisType !== 0 && ephemerisType !== 1) {
14255
+ throw new ParseError(`Invalid ephemeris type: ${ephemerisType}`, "TLE");
14256
+ }
14239
14257
  return ephemerisType;
14240
14258
  }
14241
14259
  /**
@@ -14466,6 +14484,9 @@ ${this.line2}`;
14466
14484
  */
14467
14485
  static satNum(tleLine) {
14468
14486
  const satNumStr = tleLine.substring(_Tle.satNum_.start, _Tle.satNum_.stop);
14487
+ if (satNumStr.trim().length === 0) {
14488
+ return NaN;
14489
+ }
14469
14490
  const sixDigitSatNum = _Tle.convertA5to6Digit(satNumStr);
14470
14491
  return parseInt(sixDigitSatNum);
14471
14492
  }
@@ -14556,12 +14577,13 @@ ${this.line2}`;
14556
14577
  static parse(tleLine1, tleLine2) {
14557
14578
  const line1 = _Tle.parseLine1(tleLine1);
14558
14579
  const line2 = _Tle.parseLine2(tleLine2);
14559
- if (line1.satNum !== line2.satNum) {
14580
+ const isVimpel = line1.classification === "V";
14581
+ if (!isVimpel && line1.satNum !== line2.satNum) {
14560
14582
  console.info("Line 1 satNum:", line1.satNum);
14561
14583
  console.info("Line 2 satNum:", line2.satNum);
14562
14584
  throw new ParseError("Satellite numbers do not match between TLE lines", "TLE");
14563
14585
  }
14564
- if (line1.satNumRaw !== line2.satNumRaw) {
14586
+ if (!isVimpel && line1.satNumRaw !== line2.satNumRaw) {
14565
14587
  console.info("Line 1 satNumRaw:", line1.satNumRaw);
14566
14588
  console.info("Line 2 satNumRaw:", line2.satNumRaw);
14567
14589
  throw new ParseError("Raw satellite numbers do not match between TLE lines", "TLE");
@@ -14602,10 +14624,11 @@ ${this.line2}`;
14602
14624
  static parseAll(tleLine1, tleLine2) {
14603
14625
  const line1 = _Tle.parseLine1(tleLine1);
14604
14626
  const line2 = _Tle.parseLine2(tleLine2);
14605
- if (line1.satNum !== line2.satNum) {
14627
+ const isVimpel = line1.classification === "V";
14628
+ if (!isVimpel && line1.satNum !== line2.satNum) {
14606
14629
  throw new ParseError("Satellite numbers do not match between TLE lines", "TLE");
14607
14630
  }
14608
- if (line1.satNumRaw !== line2.satNumRaw) {
14631
+ if (!isVimpel && line1.satNumRaw !== line2.satNumRaw) {
14609
14632
  throw new ParseError("Raw satellite numbers do not match between TLE lines", "TLE");
14610
14633
  }
14611
14634
  if (line1.lineNumber1 !== 1) {
@@ -14961,9 +14984,6 @@ var SolarRadiationPressure = class _SolarRadiationPressure extends Force {
14961
14984
  this.area = area;
14962
14985
  this.reflectCoeff = reflectCoeff;
14963
14986
  }
14964
- mass;
14965
- area;
14966
- reflectCoeff;
14967
14987
  // / Solar pressure _(N/m²)_;
14968
14988
  static _kRef = 456e-8 * KM_PER_AU ** 2;
14969
14989
  acceleration(state) {
@@ -15346,8 +15366,6 @@ var ThirdBodyGravity = class _ThirdBodyGravity {
15346
15366
  this.moon = moon;
15347
15367
  this.sun = sun;
15348
15368
  }
15349
- moon;
15350
- sun;
15351
15369
  static moonGravity_(state) {
15352
15370
  const rMoon = Moon.eci(state.epoch.toDateTime());
15353
15371
  const aNum = rMoon.subtract(state.position);
@@ -15450,13 +15468,6 @@ var RAE = class _RAE {
15450
15468
  this.azRateRad = azRateRad;
15451
15469
  this.elRateRad = elRateRad;
15452
15470
  }
15453
- epoch;
15454
- rng;
15455
- azRad;
15456
- elRad;
15457
- rngRate;
15458
- azRateRad;
15459
- elRateRad;
15460
15471
  // / Create a new [Razel] object, using degrees for the angular values.
15461
15472
  static fromDegrees(epoch, range, azimuth, elevation, rangeRate, azimuthRate, elevationRate) {
15462
15473
  const azimuthRateRad = azimuthRate ? azimuthRate * DEG2RAD : void 0;
@@ -15925,9 +15936,6 @@ var RungeKuttaAdaptive = class _RungeKuttaAdaptive extends Propagator {
15925
15936
  this._cacheState = this.initState_;
15926
15937
  this.tolerance_ = Math.max(_RungeKuttaAdaptive._minTolerance, Math.abs(tolerance_));
15927
15938
  }
15928
- initState_;
15929
- forceModel_;
15930
- tolerance_;
15931
15939
  // / Initial state vector.
15932
15940
  _cacheState;
15933
15941
  _checkpoints = [];
@@ -16223,11 +16231,6 @@ var RungeKutta4Propagator = class extends Propagator {
16223
16231
  this.checkpoints_ = checkpoints_;
16224
16232
  this.stepSize_ = Math.abs(stepSize_);
16225
16233
  }
16226
- initState_;
16227
- forceModel_;
16228
- stepSize_;
16229
- cacheState_;
16230
- checkpoints_;
16231
16234
  // / Set the integrator step size to the provided number of [seconds].
16232
16235
  setStepSize(seconds) {
16233
16236
  this.stepSize_ = Math.abs(seconds);
@@ -16556,7 +16559,6 @@ var Sgp4Propagator = class extends Propagator {
16556
16559
  this.tle_ = tle_;
16557
16560
  this.cacheState_ = tle_.state.toJ2000();
16558
16561
  }
16559
- tle_;
16560
16562
  cacheState_;
16561
16563
  checkpoints_ = [];
16562
16564
  /**
@@ -16849,15 +16851,30 @@ var Satellite = class _Satellite extends SpaceObject {
16849
16851
  return objectId;
16850
16852
  }
16851
16853
  /**
16852
- * Derives {@link sccNum5} and {@link sccNum6} from {@link sccNum}. When the
16853
- * canonical ID is an extended (7+ digit) value that exceeds the TLE alpha-5
16854
- * capacity (max 339 999), both forms are set to `null`.
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.
16855
16865
  */
16856
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
+ }
16857
16874
  const kind = Tle.classifySatNum(this.sccNum);
16858
- if (kind === "numeric5" || kind === "alpha5" || kind === "numeric6") {
16875
+ if (kind === "numeric5" || kind === "numeric6") {
16859
16876
  this.sccNum5 = Tle.convert6DigitToA5(this.sccNum);
16860
- this.sccNum6 = Tle.convertA5to6Digit(this.sccNum5);
16877
+ this.sccNum6 = this.sccNum;
16861
16878
  } else {
16862
16879
  this.sccNum5 = null;
16863
16880
  this.sccNum6 = null;
@@ -19288,6 +19305,206 @@ var observationNoiseFromSigmas = (sigmas) => {
19288
19305
  return new Matrix(result);
19289
19306
  };
19290
19307
 
19308
+ // src/observation/Observation.ts
19309
+ var Observation = class {
19310
+ /**
19311
+ * Convert this observation's noise matrix into a covariance matrix.
19312
+ * @returns A matrix representing the noise covariance.
19313
+ */
19314
+ noiseCovariance() {
19315
+ return this.noise.reciprocal();
19316
+ }
19317
+ /**
19318
+ * Generates a noise sample from the noise covariance matrix.
19319
+ * @param sigma - The scaling factor for the noise covariance matrix.
19320
+ * @returns A matrix representing the noise sample.
19321
+ */
19322
+ noiseSample_(sigma) {
19323
+ return this.noiseCovariance().scale(sigma).cholesky();
19324
+ }
19325
+ /**
19326
+ * Randomly sample this observation in vector form within the
19327
+ * observation noise.
19328
+ * @param random Random number generator.
19329
+ * @param sigma Sigma value to scale the noise by.
19330
+ * @returns Sampled observation.
19331
+ */
19332
+ sampleVector(random, sigma) {
19333
+ const chol = this.noiseSample_(sigma);
19334
+ const gauss = random.gaussVector(this.noise.columns);
19335
+ const meas = this.toVector();
19336
+ return meas.add(chol.multiplyVector(gauss));
19337
+ }
19338
+ };
19339
+
19340
+ // src/observation/ObservationOptical.ts
19341
+ var ObservationOptical = class _ObservationOptical extends Observation {
19342
+ // / Create a new [ObservationOptical] object.
19343
+ constructor(site_, observation, noise_ = _ObservationOptical.defaultNoise) {
19344
+ super();
19345
+ this.site_ = site_;
19346
+ this.observation = observation;
19347
+ this.noise_ = noise_;
19348
+ }
19349
+ // / Inertial site location.
19350
+ get site() {
19351
+ return this.site_;
19352
+ }
19353
+ // / Noise matrix.
19354
+ get noise() {
19355
+ return this.noise_;
19356
+ }
19357
+ /**
19358
+ * Default noise matrix _(right-ascension, declination)_.
19359
+ * Based on the Maui Optical Site noise model.
19360
+ */
19361
+ static defaultNoise = _ObservationOptical.noiseFromSigmas(
19362
+ 37e-4 * DEG2RAD,
19363
+ 3e-3 * DEG2RAD
19364
+ );
19365
+ get epoch() {
19366
+ return this.observation.epoch;
19367
+ }
19368
+ toVector() {
19369
+ return Vector.fromList([this.observation.rightAscension, this.observation.declination]);
19370
+ }
19371
+ clos(propagator) {
19372
+ const position = propagator.propagate(this.epoch).position;
19373
+ const offset = position.subtract(this.site.position);
19374
+ const actual = this.observation.lineOfSight().normalize();
19375
+ const expected = offset.normalize();
19376
+ const slantRange = offset.magnitude();
19377
+ const theta = actual.angle(expected);
19378
+ if (isNaN(theta)) {
19379
+ return 0;
19380
+ }
19381
+ return 2 * slantRange * Math.sin(theta * 0.5);
19382
+ }
19383
+ ricDiff(propagator) {
19384
+ const r0 = this.site;
19385
+ const r1 = propagator.propagate(this.epoch);
19386
+ const r2 = this.observation.position(this.site, r1.position.distance(r0.position));
19387
+ return RIC.fromJ2000(new J20002(this.epoch, r2, Vector3D.origin), r1).position;
19388
+ }
19389
+ sample(random, sigma = 1) {
19390
+ const resultEl = this.sampleVector(random, sigma).elements;
19391
+ return new _ObservationOptical(
19392
+ this.site,
19393
+ new RadecTopocentric(this.epoch, resultEl[0], resultEl[1]),
19394
+ this.noise
19395
+ );
19396
+ }
19397
+ jacobian(propPairs) {
19398
+ const result = array2d(2, 6, 0);
19399
+ for (let i = 0; i < 6; i++) {
19400
+ const step = propPairs.step(i);
19401
+ const [high, low] = propPairs.get(i);
19402
+ const sl = low.propagate(this.epoch);
19403
+ const sh = high.propagate(this.epoch);
19404
+ const ol = RadecTopocentric.fromStateVector(sl, this.site);
19405
+ const oh = RadecTopocentric.fromStateVector(sh, this.site);
19406
+ result[0][i] = observationDerivative(oh.rightAscension, ol.rightAscension, step, true);
19407
+ result[1][i] = observationDerivative(oh.declination, ol.declination, step, true);
19408
+ }
19409
+ return new Matrix(result);
19410
+ }
19411
+ residual(propagator) {
19412
+ const result = array2d(2, 1, 0);
19413
+ const state = propagator.propagate(this.epoch);
19414
+ const radec = RadecTopocentric.fromStateVector(state, this.site);
19415
+ result[0][0] = normalizeAngle(this.observation.rightAscension, radec.rightAscension);
19416
+ result[1][0] = normalizeAngle(this.observation.declination, radec.declination);
19417
+ return new Matrix(result);
19418
+ }
19419
+ /**
19420
+ * Create a noise matrix from right ascension and declination standard deviantions.
19421
+ * @param raSigma Right ascension standard deviation
19422
+ * @param decSigma Declination standard deviation
19423
+ * @returns Noise matrix
19424
+ */
19425
+ static noiseFromSigmas(raSigma, decSigma) {
19426
+ return observationNoiseFromSigmas([raSigma, decSigma]);
19427
+ }
19428
+ };
19429
+
19430
+ // src/observation/ObservationRadar.ts
19431
+ var ObservationRadar = class _ObservationRadar extends Observation {
19432
+ // / Create a new [ObservationRadar] object.
19433
+ constructor(site_, observation, noise_ = _ObservationRadar.defaultNoise) {
19434
+ super();
19435
+ this.site_ = site_;
19436
+ this.observation = observation;
19437
+ this.noise_ = noise_;
19438
+ }
19439
+ // / Default noise matrix.
19440
+ static defaultNoise = _ObservationRadar.noiseFromSigmas(0.32, 0.015 * DEG2RAD, 0.015 * DEG2RAD);
19441
+ get epoch() {
19442
+ return this.observation.epoch;
19443
+ }
19444
+ get site() {
19445
+ return this.site_;
19446
+ }
19447
+ get noise() {
19448
+ return this.noise_;
19449
+ }
19450
+ toVector() {
19451
+ return Vector.fromList([this.observation.rng, this.observation.azRad, this.observation.elRad]);
19452
+ }
19453
+ clos(propagator) {
19454
+ const ri = propagator.propagate(this.epoch).position.subtract(this.site.position);
19455
+ return Math.abs(this.observation.rng - ri.magnitude());
19456
+ }
19457
+ ricDiff(propagator) {
19458
+ const r0 = propagator.propagate(this.epoch);
19459
+ const r1 = RAE.fromStateVector(r0, this.site);
19460
+ const r2 = this.observation.position(this.site, r1.azRad, r1.elRad);
19461
+ return RIC.fromJ2000(new J20002(this.epoch, r2, Vector3D.origin), r0).position;
19462
+ }
19463
+ sample(random, sigma = 1) {
19464
+ const result = this.sampleVector(random, sigma).elements;
19465
+ return new _ObservationRadar(
19466
+ this.site,
19467
+ new RAE(this.observation.epoch, result[0], result[1], result[2]),
19468
+ this.noise
19469
+ );
19470
+ }
19471
+ jacobian(propPairs) {
19472
+ const result = array2d(3, 6, 0);
19473
+ for (let i = 0; i < 6; i++) {
19474
+ const step = propPairs.step(i);
19475
+ const [high, low] = propPairs.get(i);
19476
+ const sl = low.propagate(this.epoch);
19477
+ const sh = high.propagate(this.epoch);
19478
+ const ol = RAE.fromStateVector(sl, this.site);
19479
+ const oh = RAE.fromStateVector(sh, this.site);
19480
+ result[0][i] = observationDerivative(oh.rng, ol.rng, step);
19481
+ result[1][i] = observationDerivative(oh.azRad, ol.azRad, step, true);
19482
+ result[2][i] = observationDerivative(oh.elRad, ol.elRad, step, true);
19483
+ }
19484
+ return new Matrix(result);
19485
+ }
19486
+ residual(propagator) {
19487
+ const result = array2d(3, 1, 0);
19488
+ const state = propagator.propagate(this.epoch);
19489
+ const razel = RAE.fromStateVector(state, this.site);
19490
+ result[0][0] = this.observation.rng - razel.rng;
19491
+ result[1][0] = normalizeAngle(this.observation.azRad, razel.azRad);
19492
+ result[2][0] = normalizeAngle(this.observation.elRad, razel.elRad);
19493
+ return new Matrix(result);
19494
+ }
19495
+ /**
19496
+ * Create a noise matrix from the range, azimuth, and elevation standard
19497
+ * deviations _(kilometers/radians)_.
19498
+ * @param rngSigma - The range standard deviation _(kilometers)_.
19499
+ * @param azSigma - The azimuth standard deviation _(radians)_.
19500
+ * @param elSigma - The elevation standard deviation _(radians)_.
19501
+ * @returns The noise matrix.
19502
+ */
19503
+ static noiseFromSigmas(rngSigma, azSigma, elSigma) {
19504
+ return observationNoiseFromSigmas([rngSigma, azSigma, elSigma]);
19505
+ }
19506
+ };
19507
+
19291
19508
  // src/observation/RadecGeocentric.ts
19292
19509
  var RadecGeocentric = class _RadecGeocentric {
19293
19510
  constructor(epoch, rightAscension, declination, range, rightAscensionRate, declinationRate, rangeRate) {
@@ -19299,13 +19516,6 @@ var RadecGeocentric = class _RadecGeocentric {
19299
19516
  this.declinationRate = declinationRate;
19300
19517
  this.rangeRate = rangeRate;
19301
19518
  }
19302
- epoch;
19303
- rightAscension;
19304
- declination;
19305
- range;
19306
- rightAscensionRate;
19307
- declinationRate;
19308
- rangeRate;
19309
19519
  /**
19310
19520
  * Creates a RadecGeocentric object from the given parameters in degrees.
19311
19521
  * @param epoch - The epoch in UTC.
@@ -19448,13 +19658,6 @@ var RadecTopocentric = class _RadecTopocentric {
19448
19658
  this.declinationRate = declinationRate;
19449
19659
  this.rangeRate = rangeRate;
19450
19660
  }
19451
- epoch;
19452
- rightAscension;
19453
- declination;
19454
- range;
19455
- rightAscensionRate;
19456
- declinationRate;
19457
- rangeRate;
19458
19661
  /**
19459
19662
  * Create a new RadecTopocentric object, using degrees for the angular values.
19460
19663
  * @param epoch UTC epoch.
@@ -20012,8 +20215,6 @@ var RkCheckpoint = class {
20012
20215
  this.cacheState = cacheState;
20013
20216
  this.stepSize = stepSize;
20014
20217
  }
20015
- cacheState;
20016
- stepSize;
20017
20218
  };
20018
20219
 
20019
20220
  // src/propagator/RkResult.ts
@@ -20024,9 +20225,6 @@ var RkResult = class {
20024
20225
  this.error = error;
20025
20226
  this.newStep = newStep;
20026
20227
  }
20027
- state;
20028
- error;
20029
- newStep;
20030
20228
  };
20031
20229
 
20032
20230
  // src/covariance/StateCovariance.ts
@@ -20047,8 +20245,6 @@ var StateCovariance = class _StateCovariance {
20047
20245
  this.matrix = matrix;
20048
20246
  this.frame = frame;
20049
20247
  }
20050
- matrix;
20051
- frame;
20052
20248
  // / Create a new [StateCovariance] object from 1-sigma values.
20053
20249
  static fromSigmas(sigmas, frame) {
20054
20250
  const n = sigmas.length;
@@ -20079,8 +20275,6 @@ var PropagatorPairs = class {
20079
20275
  this.posStep_ = posStep_;
20080
20276
  this.velStep_ = velStep_;
20081
20277
  }
20082
- posStep_;
20083
- velStep_;
20084
20278
  _high = Array(6).fill(null);
20085
20279
  _low = Array(6).fill(null);
20086
20280
  set(index, high, low) {
@@ -20114,9 +20308,6 @@ var BatchLeastSquaresResult = class {
20114
20308
  this.covariance = covariance2;
20115
20309
  this.rms = rms;
20116
20310
  }
20117
- state;
20118
- covariance;
20119
- rms;
20120
20311
  };
20121
20312
 
20122
20313
  // src/orbit-determination/BatchLeastSquaresOD.ts
@@ -20147,12 +20338,6 @@ var BatchLeastSquaresOD = class _BatchLeastSquaresOD {
20147
20338
  this.propagator_ = new RungeKutta89Propagator(this.apriori_, this.forceModel_);
20148
20339
  this.nominal_ = this.propagator_.propagate(this.start_);
20149
20340
  }
20150
- observations_;
20151
- apriori_;
20152
- forceModel_;
20153
- posStep_;
20154
- velStep_;
20155
- fastDerivatives_;
20156
20341
  /** Propagator pair cache, for generating observation Jacobians. */
20157
20342
  propPairs_;
20158
20343
  /** Nominal state propagator. */
@@ -20253,7 +20438,6 @@ var GibbsIOD = class _GibbsIOD {
20253
20438
  constructor(mu = Earth.mu) {
20254
20439
  this.mu = mu;
20255
20440
  }
20256
- mu;
20257
20441
  /** Abort solve if position plane exceeds this value. */
20258
20442
  static coplanarThreshold_ = 5 * DEG2RAD;
20259
20443
  /**
@@ -20312,7 +20496,6 @@ var HerrickGibbsIOD = class {
20312
20496
  constructor(mu = Earth.mu) {
20313
20497
  this.mu = mu;
20314
20498
  }
20315
- mu;
20316
20499
  // / Attempt to create a state estimate from three inertial position vectors.
20317
20500
  solve(r1, t1, r2, t2, r3, t3) {
20318
20501
  const dt31 = t3.difference(t1);
@@ -20573,7 +20756,6 @@ var LambertIOD = class _LambertIOD {
20573
20756
  constructor(mu = Earth.mu) {
20574
20757
  this.mu = mu;
20575
20758
  }
20576
- mu;
20577
20759
  /**
20578
20760
  * Try to guess the short path argument given an [interceptor] and
20579
20761
  * [target] state.
@@ -21261,11 +21443,6 @@ var LevenbergMarquardtResult = class {
21261
21443
  this.iterations = iterations;
21262
21444
  this.converged = converged;
21263
21445
  }
21264
- state;
21265
- covariance;
21266
- rms;
21267
- iterations;
21268
- converged;
21269
21446
  };
21270
21447
 
21271
21448
  // src/orbit-determination/LevenbergMarquardtOD.ts
@@ -21296,12 +21473,6 @@ var LevenbergMarquardtOD = class _LevenbergMarquardtOD {
21296
21473
  const propagator = new RungeKutta89Propagator(this.apriori_, this.forceModel_);
21297
21474
  this.nominal_ = propagator.propagate(this.start_);
21298
21475
  }
21299
- observations_;
21300
- apriori_;
21301
- forceModel_;
21302
- posStep_;
21303
- velStep_;
21304
- fastDerivatives_;
21305
21476
  /** Propagator pair cache, for generating observation Jacobians. */
21306
21477
  propPairs_;
21307
21478
  /** State estimate during solve. */
@@ -21460,8 +21631,6 @@ var SimplexEntry = class _SimplexEntry {
21460
21631
  this.x_ = new Vector(points);
21461
21632
  this.score = this.f_(points);
21462
21633
  }
21463
- f_;
21464
- points;
21465
21634
  /**
21466
21635
  * The cost function score at this vertex.
21467
21636
  *
@@ -21794,6 +21963,82 @@ var ModifiedGoodingIOD = class {
21794
21963
  }
21795
21964
  };
21796
21965
 
21966
+ // src/orbit-determination/Rv2Tle.ts
21967
+ var angleDiff_ = (a, b) => {
21968
+ let diff = (a - b) % TAU;
21969
+ if (diff > Math.PI) {
21970
+ diff -= TAU;
21971
+ } else if (diff < -Math.PI) {
21972
+ diff += TAU;
21973
+ }
21974
+ return diff;
21975
+ };
21976
+ var wrapTau_ = (angle) => {
21977
+ let wrapped = angle % TAU;
21978
+ if (wrapped < 0) {
21979
+ wrapped += TAU;
21980
+ }
21981
+ return wrapped;
21982
+ };
21983
+ var toElements_ = (epoch, position, velocity) => new J20002(
21984
+ epoch,
21985
+ new Vector3D(position.x, position.y, position.z),
21986
+ new Vector3D(velocity.x, velocity.y, velocity.z)
21987
+ ).toClassicalElements();
21988
+ var rv2tle = (epoch, position, velocity, options = {}) => {
21989
+ const maxIterations = options.maxIterations ?? 15;
21990
+ const toleranceKm = options.toleranceKm ?? 1e-4;
21991
+ const epochUtc = EpochUTC.fromDateTime(epoch);
21992
+ const target = toElements_(epochUtc, position, velocity);
21993
+ let mean2 = new ClassicalElements({
21994
+ epoch: epochUtc,
21995
+ semimajorAxis: target.semimajorAxis,
21996
+ eccentricity: target.eccentricity,
21997
+ inclination: target.inclination,
21998
+ rightAscension: target.rightAscension,
21999
+ argPerigee: target.argPerigee,
22000
+ trueAnomaly: target.trueAnomaly
22001
+ });
22002
+ let best = null;
22003
+ for (let iteration = 1; iteration <= maxIterations; iteration++) {
22004
+ let tle;
22005
+ let sv;
22006
+ try {
22007
+ tle = Tle.fromClassicalElements(mean2);
22008
+ const tsinceMin = (epoch.getTime() - tle.epoch.toDateTime().getTime()) / 6e4;
22009
+ sv = Sgp4.propagate(Sgp4.createSatrec(tle.line1, tle.line2), tsinceMin);
22010
+ } catch {
22011
+ return best;
22012
+ }
22013
+ if (!sv.position || typeof sv.position === "boolean" || !sv.velocity || typeof sv.velocity === "boolean") {
22014
+ return best;
22015
+ }
22016
+ const achievedPos = sv.position;
22017
+ const achievedVel = sv.velocity;
22018
+ const dx = achievedPos.x - position.x;
22019
+ const dy = achievedPos.y - position.y;
22020
+ const dz = achievedPos.z - position.z;
22021
+ const positionErrorKm = Math.sqrt(dx * dx + dy * dy + dz * dz);
22022
+ if (!best || positionErrorKm < best.positionErrorKm) {
22023
+ best = { tle1: tle.line1, tle2: tle.line2, positionErrorKm, iterations: iteration };
22024
+ }
22025
+ if (positionErrorKm < toleranceKm) {
22026
+ break;
22027
+ }
22028
+ const achieved = toElements_(epochUtc, achievedPos, achievedVel);
22029
+ mean2 = new ClassicalElements({
22030
+ epoch: epochUtc,
22031
+ semimajorAxis: mean2.semimajorAxis + (target.semimajorAxis - achieved.semimajorAxis),
22032
+ eccentricity: Math.min(Math.max(mean2.eccentricity + (target.eccentricity - achieved.eccentricity), 0), 0.999),
22033
+ inclination: Math.min(Math.max(mean2.inclination + angleDiff_(target.inclination, achieved.inclination), 0), Math.PI),
22034
+ rightAscension: wrapTau_(mean2.rightAscension + angleDiff_(target.rightAscension, achieved.rightAscension)),
22035
+ argPerigee: wrapTau_(mean2.argPerigee + angleDiff_(target.argPerigee, achieved.argPerigee)),
22036
+ trueAnomaly: wrapTau_(mean2.trueAnomaly + angleDiff_(target.trueAnomaly, achieved.trueAnomaly))
22037
+ });
22038
+ }
22039
+ return best;
22040
+ };
22041
+
21797
22042
  // src/covariance/CovarianceSample.ts
21798
22043
  var CovarianceSample = class {
21799
22044
  origin_;
@@ -21986,6 +22231,19 @@ var CovarianceSample = class {
21986
22231
  }
21987
22232
  };
21988
22233
 
22234
+ // src/covariance/displayRadii.ts
22235
+ function scaleAndClampRicSigmas(sigmas, confidence, caps) {
22236
+ const { radial, inTrack, crossTrack } = sigmas;
22237
+ if (![radial, inTrack, crossTrack].every((v) => Number.isFinite(v) && v >= 0)) {
22238
+ return null;
22239
+ }
22240
+ return {
22241
+ radial: Math.min(radial * confidence, caps.radial),
22242
+ inTrack: Math.min(inTrack * confidence, caps.inTrack),
22243
+ crossTrack: Math.min(crossTrack * confidence, caps.crossTrack)
22244
+ };
22245
+ }
22246
+
21989
22247
  // src/conjunction/ConjunctionAssessment.ts
21990
22248
  var ConjunctionAssessment = class {
21991
22249
  constructor(primary, secondary) {
@@ -21994,8 +22252,6 @@ var ConjunctionAssessment = class {
21994
22252
  this.primaryProp = this.createPropagator(primary, false);
21995
22253
  this.secondaryProp = this.createPropagator(secondary, false);
21996
22254
  }
21997
- primary;
21998
- secondary;
21999
22255
  primaryProp;
22000
22256
  secondaryProp;
22001
22257
  primaryCovSample;
@@ -23400,119 +23656,6 @@ var Sensor = class _Sensor {
23400
23656
  }
23401
23657
  };
23402
23658
 
23403
- // src/observation/Observation.ts
23404
- var Observation = class {
23405
- /**
23406
- * Convert this observation's noise matrix into a covariance matrix.
23407
- * @returns A matrix representing the noise covariance.
23408
- */
23409
- noiseCovariance() {
23410
- return this.noise.reciprocal();
23411
- }
23412
- /**
23413
- * Generates a noise sample from the noise covariance matrix.
23414
- * @param sigma - The scaling factor for the noise covariance matrix.
23415
- * @returns A matrix representing the noise sample.
23416
- */
23417
- noiseSample_(sigma) {
23418
- return this.noiseCovariance().scale(sigma).cholesky();
23419
- }
23420
- /**
23421
- * Randomly sample this observation in vector form within the
23422
- * observation noise.
23423
- * @param random Random number generator.
23424
- * @param sigma Sigma value to scale the noise by.
23425
- * @returns Sampled observation.
23426
- */
23427
- sampleVector(random, sigma) {
23428
- const chol = this.noiseSample_(sigma);
23429
- const gauss = random.gaussVector(this.noise.columns);
23430
- const meas = this.toVector();
23431
- return meas.add(chol.multiplyVector(gauss));
23432
- }
23433
- };
23434
-
23435
- // src/observation/ObservationRadar.ts
23436
- var ObservationRadar = class _ObservationRadar extends Observation {
23437
- // / Create a new [ObservationRadar] object.
23438
- constructor(site_, observation, noise_ = _ObservationRadar.defaultNoise) {
23439
- super();
23440
- this.site_ = site_;
23441
- this.observation = observation;
23442
- this.noise_ = noise_;
23443
- }
23444
- site_;
23445
- observation;
23446
- noise_;
23447
- // / Default noise matrix.
23448
- static defaultNoise = _ObservationRadar.noiseFromSigmas(0.32, 0.015 * DEG2RAD, 0.015 * DEG2RAD);
23449
- get epoch() {
23450
- return this.observation.epoch;
23451
- }
23452
- get site() {
23453
- return this.site_;
23454
- }
23455
- get noise() {
23456
- return this.noise_;
23457
- }
23458
- toVector() {
23459
- return Vector.fromList([this.observation.rng, this.observation.azRad, this.observation.elRad]);
23460
- }
23461
- clos(propagator) {
23462
- const ri = propagator.propagate(this.epoch).position.subtract(this.site.position);
23463
- return Math.abs(this.observation.rng - ri.magnitude());
23464
- }
23465
- ricDiff(propagator) {
23466
- const r0 = propagator.propagate(this.epoch);
23467
- const r1 = RAE.fromStateVector(r0, this.site);
23468
- const r2 = this.observation.position(this.site, r1.azRad, r1.elRad);
23469
- return RIC.fromJ2000(new J20002(this.epoch, r2, Vector3D.origin), r0).position;
23470
- }
23471
- sample(random, sigma = 1) {
23472
- const result = this.sampleVector(random, sigma).elements;
23473
- return new _ObservationRadar(
23474
- this.site,
23475
- new RAE(this.observation.epoch, result[0], result[1], result[2]),
23476
- this.noise
23477
- );
23478
- }
23479
- jacobian(propPairs) {
23480
- const result = array2d(3, 6, 0);
23481
- for (let i = 0; i < 6; i++) {
23482
- const step = propPairs.step(i);
23483
- const [high, low] = propPairs.get(i);
23484
- const sl = low.propagate(this.epoch);
23485
- const sh = high.propagate(this.epoch);
23486
- const ol = RAE.fromStateVector(sl, this.site);
23487
- const oh = RAE.fromStateVector(sh, this.site);
23488
- result[0][i] = observationDerivative(oh.rng, ol.rng, step);
23489
- result[1][i] = observationDerivative(oh.azRad, ol.azRad, step, true);
23490
- result[2][i] = observationDerivative(oh.elRad, ol.elRad, step, true);
23491
- }
23492
- return new Matrix(result);
23493
- }
23494
- residual(propagator) {
23495
- const result = array2d(3, 1, 0);
23496
- const state = propagator.propagate(this.epoch);
23497
- const razel = RAE.fromStateVector(state, this.site);
23498
- result[0][0] = this.observation.rng - razel.rng;
23499
- result[1][0] = normalizeAngle(this.observation.azRad, razel.azRad);
23500
- result[2][0] = normalizeAngle(this.observation.elRad, razel.elRad);
23501
- return new Matrix(result);
23502
- }
23503
- /**
23504
- * Create a noise matrix from the range, azimuth, and elevation standard
23505
- * deviations _(kilometers/radians)_.
23506
- * @param rngSigma - The range standard deviation _(kilometers)_.
23507
- * @param azSigma - The azimuth standard deviation _(radians)_.
23508
- * @param elSigma - The elevation standard deviation _(radians)_.
23509
- * @returns The noise matrix.
23510
- */
23511
- static noiseFromSigmas(rngSigma, azSigma, elSigma) {
23512
- return observationNoiseFromSigmas([rngSigma, azSigma, elSigma]);
23513
- }
23514
- };
23515
-
23516
23659
  // src/sensor/RadarSensor.ts
23517
23660
  var RadarSensor = class extends Sensor {
23518
23661
  /** Radar beamwidth in degrees */
@@ -23924,99 +24067,6 @@ var MechanicalRadar = class _MechanicalRadar extends RadarSensor {
23924
24067
  }
23925
24068
  };
23926
24069
 
23927
- // src/observation/ObservationOptical.ts
23928
- var ObservationOptical = class _ObservationOptical extends Observation {
23929
- // / Create a new [ObservationOptical] object.
23930
- constructor(site_, observation, noise_ = _ObservationOptical.defaultNoise) {
23931
- super();
23932
- this.site_ = site_;
23933
- this.observation = observation;
23934
- this.noise_ = noise_;
23935
- }
23936
- site_;
23937
- observation;
23938
- noise_;
23939
- // / Inertial site location.
23940
- get site() {
23941
- return this.site_;
23942
- }
23943
- // / Noise matrix.
23944
- get noise() {
23945
- return this.noise_;
23946
- }
23947
- /**
23948
- * Default noise matrix _(right-ascension, declination)_.
23949
- * Based on the Maui Optical Site noise model.
23950
- */
23951
- static defaultNoise = _ObservationOptical.noiseFromSigmas(
23952
- 37e-4 * DEG2RAD,
23953
- 3e-3 * DEG2RAD
23954
- );
23955
- get epoch() {
23956
- return this.observation.epoch;
23957
- }
23958
- toVector() {
23959
- return Vector.fromList([this.observation.rightAscension, this.observation.declination]);
23960
- }
23961
- clos(propagator) {
23962
- const position = propagator.propagate(this.epoch).position;
23963
- const offset = position.subtract(this.site.position);
23964
- const actual = this.observation.lineOfSight().normalize();
23965
- const expected = offset.normalize();
23966
- const slantRange = offset.magnitude();
23967
- const theta = actual.angle(expected);
23968
- if (isNaN(theta)) {
23969
- return 0;
23970
- }
23971
- return 2 * slantRange * Math.sin(theta * 0.5);
23972
- }
23973
- ricDiff(propagator) {
23974
- const r0 = this.site;
23975
- const r1 = propagator.propagate(this.epoch);
23976
- const r2 = this.observation.position(this.site, r1.position.distance(r0.position));
23977
- return RIC.fromJ2000(new J20002(this.epoch, r2, Vector3D.origin), r1).position;
23978
- }
23979
- sample(random, sigma = 1) {
23980
- const resultEl = this.sampleVector(random, sigma).elements;
23981
- return new _ObservationOptical(
23982
- this.site,
23983
- new RadecTopocentric(this.epoch, resultEl[0], resultEl[1]),
23984
- this.noise
23985
- );
23986
- }
23987
- jacobian(propPairs) {
23988
- const result = array2d(2, 6, 0);
23989
- for (let i = 0; i < 6; i++) {
23990
- const step = propPairs.step(i);
23991
- const [high, low] = propPairs.get(i);
23992
- const sl = low.propagate(this.epoch);
23993
- const sh = high.propagate(this.epoch);
23994
- const ol = RadecTopocentric.fromStateVector(sl, this.site);
23995
- const oh = RadecTopocentric.fromStateVector(sh, this.site);
23996
- result[0][i] = observationDerivative(oh.rightAscension, ol.rightAscension, step, true);
23997
- result[1][i] = observationDerivative(oh.declination, ol.declination, step, true);
23998
- }
23999
- return new Matrix(result);
24000
- }
24001
- residual(propagator) {
24002
- const result = array2d(2, 1, 0);
24003
- const state = propagator.propagate(this.epoch);
24004
- const radec = RadecTopocentric.fromStateVector(state, this.site);
24005
- result[0][0] = normalizeAngle(this.observation.rightAscension, radec.rightAscension);
24006
- result[1][0] = normalizeAngle(this.observation.declination, radec.declination);
24007
- return new Matrix(result);
24008
- }
24009
- /**
24010
- * Create a noise matrix from right ascension and declination standard deviantions.
24011
- * @param raSigma Right ascension standard deviation
24012
- * @param decSigma Declination standard deviation
24013
- * @returns Noise matrix
24014
- */
24015
- static noiseFromSigmas(raSigma, decSigma) {
24016
- return observationNoiseFromSigmas([raSigma, decSigma]);
24017
- }
24018
- };
24019
-
24020
24070
  // src/sensor/OpticalSensor.ts
24021
24071
  var OpticalSensor = class _OpticalSensor extends Sensor {
24022
24072
  /** Telescope aperture in meters */
@@ -27229,11 +27279,6 @@ var ChebyshevCoefficients = class _ChebyshevCoefficients {
27229
27279
  this.cyd_ = _ChebyshevCoefficients._derivative(a, b, cy_);
27230
27280
  this.czd_ = _ChebyshevCoefficients._derivative(a, b, cz_);
27231
27281
  }
27232
- a;
27233
- b;
27234
- cx_;
27235
- cy_;
27236
- cz_;
27237
27282
  cxd_;
27238
27283
  cyd_;
27239
27284
  czd_;
@@ -27755,11 +27800,6 @@ var TwoBurnOrbitTransfer = class _TwoBurnOrbitTransfer {
27755
27800
  this.vTransB = vTransB;
27756
27801
  this.tTrans = tTrans;
27757
27802
  }
27758
- vInit;
27759
- vFinal;
27760
- vTransA;
27761
- vTransB;
27762
- tTrans;
27763
27803
  /**
27764
27804
  * Calculates the parameters for a Hohmann transfer orbit between two circular orbits.
27765
27805
  * @param rInit The initial radius of the orbit. (km)
@@ -27812,7 +27852,6 @@ var ChebyshevCompressor = class _ChebyshevCompressor {
27812
27852
  constructor(interpolator_) {
27813
27853
  this.interpolator_ = interpolator_;
27814
27854
  }
27815
- interpolator_;
27816
27855
  /** Returns the cosine of π times x. */
27817
27856
  static cosPi_(x) {
27818
27857
  return Math.cos(Math.PI * x);
@@ -28053,8 +28092,6 @@ var SimpleLinearRegression = class _SimpleLinearRegression {
28053
28092
  this.ys = ys;
28054
28093
  this.update();
28055
28094
  }
28056
- xs;
28057
- ys;
28058
28095
  /** Line slope (m in y = mx + b). */
28059
28096
  slope_ = 0;
28060
28097
  /** Y-axis intercept (b in y = mx + b). */
@@ -29184,6 +29221,24 @@ var LaunchTrajectoryGenerator = class _LaunchTrajectoryGenerator {
29184
29221
  }
29185
29222
  return azimuth;
29186
29223
  }
29224
+ /**
29225
+ * Compute orbital inclination from a launch azimuth and launch site latitude.
29226
+ *
29227
+ * Inverse of {@link computeLaunchAzimuth}: from the spherical-triangle relation
29228
+ * `cos(i) = sin(azimuth) * cos(latitude)`. The result is the magnitude of the
29229
+ * inclination in degrees (always in [0, 180]); the launch direction (N/S) is
29230
+ * implied by the azimuth quadrant, not by this value.
29231
+ *
29232
+ * @param azimuthDeg Launch azimuth in degrees (from north, clockwise).
29233
+ * @param latDeg Launch site latitude in degrees.
29234
+ * @returns Orbital inclination in degrees, in [0, 180].
29235
+ */
29236
+ static computeInclinationFromAzimuth(azimuthDeg, latDeg) {
29237
+ const az = azimuthDeg * DEG2RAD;
29238
+ const lat = latDeg * DEG2RAD;
29239
+ const cosInc = Math.max(-1, Math.min(1, Math.sin(az) * Math.cos(lat)));
29240
+ return Math.acos(cosInc) * RAD2DEG;
29241
+ }
29187
29242
  /**
29188
29243
  * Estimate ascent duration based on target insertion altitude.
29189
29244
  * Loosely calibrated: ~480s for 200km, ~540s for 400km, ~600s for 800km.
@@ -29809,8 +29864,20 @@ var OrbitFinder = class _OrbitFinder {
29809
29864
  this.debug_(`Found perigee at altitude ${perigeeParams.altitude} km`);
29810
29865
  return perigeeParams;
29811
29866
  }
29867
+ /**
29868
+ * Returns the 5-char satnum to embed in the synthesized TLEs.
29869
+ *
29870
+ * Why not {@link Satellite.sccNum}? For extended (7+ digit) IDs sccNum is
29871
+ * the full 9-digit canonical value, which would push the TLE past 69 chars
29872
+ * and break Sgp4.createSatrec. The input tle1 always has a valid 5-char
29873
+ * satnum at columns 2-6, and SGP4 itself doesn't care what value lives
29874
+ * there as long as line 1 and line 2 agree — so use that.
29875
+ */
29876
+ tleSatNum_() {
29877
+ return this.sat_.tle1.substring(2, 7);
29878
+ }
29812
29879
  generateTle1_() {
29813
- return `1 ${this.sat_.sccNum}U ${this.sat_.tle1.substring(9, 17)} ${this.sat_.tle1.substring(18, 32)}${this.sat_.tle1.substring(32, 71)}`;
29880
+ return `1 ${this.tleSatNum_()}U ${this.sat_.tle1.substring(9, 17)} ${this.sat_.tle1.substring(18, 32)}${this.sat_.tle1.substring(32, 71)}`;
29814
29881
  }
29815
29882
  generateTle2_(newParams) {
29816
29883
  const mergedParams = {
@@ -29825,7 +29892,7 @@ var OrbitFinder = class _OrbitFinder {
29825
29892
  const argPer = mergedParams.argOfPerigee.toFixed(4).padStart(8, "0");
29826
29893
  const meanA = mergedParams.meanAnomaly.toFixed(4).padStart(8, "0");
29827
29894
  const meanMo = this.sat_.tle2.substring(52, 63);
29828
- return `2 ${this.sat_.sccNum} ${inc} ${raan} ${ecc} ${argPer} ${meanA} ${meanMo} 10`;
29895
+ return `2 ${this.tleSatNum_()} ${inc} ${raan} ${ecc} ${argPer} ${meanA} ${meanMo} 10`;
29829
29896
  }
29830
29897
  };
29831
29898
 
@@ -30185,6 +30252,33 @@ var RepeatGroundTrack = class _RepeatGroundTrack {
30185
30252
  * You should have received a copy of the GNU Affero General Public License along with
30186
30253
  * Orbital Object ToolKit. If not, see <http://www.gnu.org/licenses/>.
30187
30254
  */
30255
+ /**
30256
+ * @author Theodore Kruczek.
30257
+ * @description Orbital Object ToolKit (ootk) is a collection of tools for working
30258
+ * with satellites and other orbital objects.
30259
+ *
30260
+ * @license MIT License
30261
+ *
30262
+ * @Copyright (c) 2025 Theodore Kruczek
30263
+ *
30264
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
30265
+ * of this software and associated documentation files (the "Software"), to deal
30266
+ * in the Software without restriction, including without limitation the rights
30267
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30268
+ * copies of the Software, and to permit persons to whom the Software is
30269
+ * furnished to do so, subject to the following conditions:
30270
+ *
30271
+ * The above copyright notice and this permission notice shall be included in all
30272
+ * copies or substantial portions of the Software.
30273
+ *
30274
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30275
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30276
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30277
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30278
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30279
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30280
+ * SOFTWARE.
30281
+ */
30188
30282
  /**
30189
30283
  * @author Theodore Kruczek
30190
30284
  * @description Orbital Object ToolKit (ootk) is a collection of tools for working
@@ -30286,4 +30380,4 @@ astronomy-engine/esm/astronomy.js:
30286
30380
  *)
30287
30381
  */
30288
30382
 
30289
- 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 };
30383
+ 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, Observation, ObservationOptical, ObservationRadar, 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 };