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.cjs CHANGED
@@ -23,8 +23,6 @@ var ValidationError = class extends OotkError {
23
23
  this.value = value;
24
24
  this.name = "ValidationError";
25
25
  }
26
- field;
27
- value;
28
26
  };
29
27
  var ParseError = class extends OotkError {
30
28
  /**
@@ -39,8 +37,6 @@ var ParseError = class extends OotkError {
39
37
  this.line = line;
40
38
  this.name = "ParseError";
41
39
  }
42
- format;
43
- line;
44
40
  };
45
41
  var PropagationError = class extends OotkError {
46
42
  /**
@@ -53,7 +49,6 @@ var PropagationError = class extends OotkError {
53
49
  this.epoch = epoch;
54
50
  this.name = "PropagationError";
55
51
  }
56
- epoch;
57
52
  };
58
53
  var OrbitDeterminationError = class extends OotkError {
59
54
  /**
@@ -66,7 +61,6 @@ var OrbitDeterminationError = class extends OotkError {
66
61
  this.algorithm = algorithm;
67
62
  this.name = "OrbitDeterminationError";
68
63
  }
69
- algorithm;
70
64
  };
71
65
 
72
66
  // src/enums/AngularDiameterMethod.ts
@@ -284,7 +278,6 @@ var Epoch = class {
284
278
  throw new ValidationError("Epoch posix time must be a valid number", "posix", posix);
285
279
  }
286
280
  }
287
- posix;
288
281
  toString() {
289
282
  return this.toDateTime().toISOString();
290
283
  }
@@ -308,7 +301,7 @@ var Epoch = class {
308
301
  const currentDateObj = this.toDateTime();
309
302
  const epochYear = currentDateObj.getUTCFullYear().toString().slice(2, 4);
310
303
  const epochDay = this.getDayOfYear_(currentDateObj);
311
- const timeOfDay = (currentDateObj.getUTCHours() * 60 + currentDateObj.getUTCMinutes()) / 1440;
304
+ const timeOfDay = (currentDateObj.getUTCHours() * 3600 + currentDateObj.getUTCMinutes() * 60 + currentDateObj.getUTCSeconds() + currentDateObj.getUTCMilliseconds() / 1e3) / 86400;
312
305
  const epochDayStr = (epochDay + timeOfDay).toFixed(8).padStart(12, "0");
313
306
  return {
314
307
  epochYr: epochYear,
@@ -1828,8 +1821,6 @@ var EpochGPS = class _EpochGPS {
1828
1821
  throw new ValidationError("GPS seconds must be between 0 and 604799", "seconds", seconds);
1829
1822
  }
1830
1823
  }
1831
- week;
1832
- seconds;
1833
1824
  /** Cached GPS reference epoch (1980-01-06T00:00:00.000Z) */
1834
1825
  static reference_ = null;
1835
1826
  /**
@@ -2255,6 +2246,32 @@ function createSampleCovarianceFromTle(tleLine1, tleLine2, frame = "ric" /* RIC
2255
2246
  const sample = new CovarianceSample(j2000State, covariance2, tle);
2256
2247
  return frame === "ric" /* RIC */ ? sample.desampleRIC() : sample.desampleJ2000();
2257
2248
  }
2249
+ var DEFAULT_SCREENING_SIGMA_CAPS = [1200, 1e3, 5e3];
2250
+ function cappedScreeningCovarianceFromTle(tleLine1, tleLine2, confidenceLevel, caps = DEFAULT_SCREENING_SIGMA_CAPS) {
2251
+ let elements;
2252
+ try {
2253
+ elements = createSampleCovarianceFromTle(tleLine1, tleLine2).matrix.elements;
2254
+ } catch {
2255
+ return buildFallbackScreeningCovariance_(caps);
2256
+ }
2257
+ const radialVar = elements[0][0];
2258
+ const intrackVar = elements[1][1];
2259
+ const crosstrackVar = elements[2][2];
2260
+ elements[0][0] = Math.min(Math.sqrt(radialVar) * confidenceLevel, caps[0]);
2261
+ elements[1][1] = Math.min(Math.sqrt(crosstrackVar) * confidenceLevel, caps[1]);
2262
+ elements[2][2] = Math.min(Math.sqrt(intrackVar) * confidenceLevel, caps[2]);
2263
+ if (!elements[0][0] || !elements[1][1] || !elements[2][2]) {
2264
+ return buildFallbackScreeningCovariance_(caps);
2265
+ }
2266
+ return new StateCovariance(new Matrix(elements), "eci" /* ECI */);
2267
+ }
2268
+ function buildFallbackScreeningCovariance_(caps) {
2269
+ const fallback = Array.from({ length: 6 }, () => new Array(6).fill(0));
2270
+ fallback[0][0] = caps[0];
2271
+ fallback[1][1] = caps[1];
2272
+ fallback[2][2] = caps[2];
2273
+ return new StateCovariance(new Matrix(fallback), "eci" /* ECI */);
2274
+ }
2258
2275
 
2259
2276
  // src/utils/jacobian.ts
2260
2277
  var jacobian = (f, m, x0, step = 1e-5) => {
@@ -2281,6 +2298,17 @@ function linearDistance(pos1, pos2) {
2281
2298
  return Math.sqrt((pos1.x - pos2.x) ** 2 + (pos1.y - pos2.y) ** 2 + (pos1.z - pos2.z) ** 2);
2282
2299
  }
2283
2300
 
2301
+ // src/utils/photometry.ts
2302
+ var RCS_VMAG_ESTIMATE_MIN = -5;
2303
+ var RCS_VMAG_ESTIMATE_MAX = 15;
2304
+ function estimateVmagFromRcs(rcs) {
2305
+ if (typeof rcs !== "number" || !Number.isFinite(rcs) || rcs <= 0) {
2306
+ return null;
2307
+ }
2308
+ const vmag = -1.3 - 2.5 * Math.log10(rcs);
2309
+ return Math.min(Math.max(vmag, RCS_VMAG_ESTIMATE_MIN), RCS_VMAG_ESTIMATE_MAX);
2310
+ }
2311
+
2284
2312
  // src/operations/Vector3D.ts
2285
2313
  var Vector3D = class _Vector3D {
2286
2314
  constructor(x, y, z) {
@@ -2288,9 +2316,6 @@ var Vector3D = class _Vector3D {
2288
2316
  this.y = y;
2289
2317
  this.z = z;
2290
2318
  }
2291
- x;
2292
- y;
2293
- z;
2294
2319
  /**
2295
2320
  * Create a new Vector3D object from the first three elements of a Vector
2296
2321
  * object.
@@ -2488,7 +2513,6 @@ var Vector = class _Vector {
2488
2513
  this.elements = elements;
2489
2514
  this.length = elements.length;
2490
2515
  }
2491
- elements;
2492
2516
  /**
2493
2517
  * The length of the vector.
2494
2518
  */
@@ -3269,7 +3293,7 @@ var Random = class {
3269
3293
  }
3270
3294
  };
3271
3295
 
3272
- // ../../../node_modules/astronomy-engine/esm/astronomy.js
3296
+ // node_modules/astronomy-engine/esm/astronomy.js
3273
3297
  var C_AUDAY = 173.1446326846693;
3274
3298
  var KM_PER_AU2 = 14959787069098932e-8;
3275
3299
  var DEG2RAD2 = 0.017453292519943295;
@@ -8609,12 +8633,6 @@ var CubicSpline = class {
8609
8633
  this.p1 = p1;
8610
8634
  this.m1 = m1;
8611
8635
  }
8612
- t0;
8613
- p0;
8614
- m0;
8615
- t1;
8616
- p1;
8617
- m1;
8618
8636
  // / Interpolate position at the provided time [t] _(POSIX seconds)_.
8619
8637
  position_(t) {
8620
8638
  const t2 = t * t;
@@ -8690,7 +8708,6 @@ var CubicSplineInterpolator = class _CubicSplineInterpolator extends StateInterp
8690
8708
  super();
8691
8709
  this.splines_ = splines_;
8692
8710
  }
8693
- splines_;
8694
8711
  static fromEphemeris(ephemeris) {
8695
8712
  const splines = [];
8696
8713
  for (let i = 0; i < ephemeris.length - 1; i++) {
@@ -8890,7 +8907,6 @@ var VerletBlendInterpolator = class _VerletBlendInterpolator extends StateInterp
8890
8907
  super();
8891
8908
  this.ephemeris = ephemeris;
8892
8909
  }
8893
- ephemeris;
8894
8910
  get sizeBytes() {
8895
8911
  return 64 * 7 * this.ephemeris.length / 8;
8896
8912
  }
@@ -9878,7 +9894,10 @@ var FormatTle = class _FormatTle {
9878
9894
  */
9879
9895
  static createTle(tleParams) {
9880
9896
  const { inc, meanmo, rasc, argPe, meana, ecen, epochyr, epochday, intl } = tleParams;
9881
- const scc = Tle.convert6DigitToA5(tleParams.scc);
9897
+ let scc = Tle.convert6DigitToA5(tleParams.scc);
9898
+ if (/^\d{1,4}$/u.test(scc)) {
9899
+ scc = scc.padStart(5, "0");
9900
+ }
9882
9901
  const epochYrStr = String(epochyr).padStart(2, "0");
9883
9902
  const epochdayStr = Number(epochday).toFixed(8).padStart(12, "0");
9884
9903
  const incStr = _FormatTle.inclination(inc);
@@ -10391,7 +10410,7 @@ var Sgp4 = class _Sgp4 {
10391
10410
  error: 0 /* NO_ERROR */
10392
10411
  };
10393
10412
  const xpdotp = 1440 / (2 * PI);
10394
- satrec.satnum = tleLine1.substring(2, 7);
10413
+ satrec.satnum = tleLine1.charAt(7) === "V" ? "0" : tleLine1.substring(2, 7);
10395
10414
  satrec.epochyr = parseInt(tleLine1.substring(18, 20));
10396
10415
  satrec.epochdays = parseFloat(tleLine1.substring(20, 32));
10397
10416
  satrec.ndot = parseFloat(tleLine1.substring(33, 43));
@@ -13558,11 +13577,6 @@ var Thrust = class {
13558
13577
  crosstrack * 1e-3
13559
13578
  );
13560
13579
  }
13561
- center;
13562
- radial;
13563
- intrack;
13564
- crosstrack;
13565
- durationRate;
13566
13580
  deltaV;
13567
13581
  get magnitude() {
13568
13582
  return this.deltaV.magnitude() * 1e3;
@@ -13601,9 +13615,6 @@ var Hill = class _Hill {
13601
13615
  this.semimajorAxis_ = semimajorAxis;
13602
13616
  this.meanMotion_ = Earth.smaToMeanMotion(this.semimajorAxis_);
13603
13617
  }
13604
- epoch;
13605
- position;
13606
- velocity;
13607
13618
  semimajorAxis_;
13608
13619
  meanMotion_;
13609
13620
  static fromState(origin, radialPosition, intrackPosition, nodeVelocity, nodeOffsetTime) {
@@ -13917,7 +13928,7 @@ var Tle = class _Tle {
13917
13928
  this.line1 = line1;
13918
13929
  this.line2 = line2;
13919
13930
  this.epoch = _Tle.parseEpoch_(line1.substring(18, 32));
13920
- this.satnum = parseInt(_Tle.convertA5to6Digit(line1.substring(2, 7)));
13931
+ this.satnum = _Tle.satNum(this.line1);
13921
13932
  this.satrec_ = Sgp4.createSatrec(line1, line2, gravConst, opsMode);
13922
13933
  }
13923
13934
  toString() {
@@ -14006,7 +14017,7 @@ ${this.line2}`;
14006
14017
  epochYearFull = 1900 + epochYearShort;
14007
14018
  }
14008
14019
  const epochJday = epochDayOfYear + epochYearFull * 365;
14009
- const currentJday = getDayOfYear() + currentYearFull * 365;
14020
+ const currentJday = getDayOfYear(nowInput) + currentYearFull * 365;
14010
14021
  const currentTime = (nowInput.getUTCHours() * 3600 + nowInput.getUTCMinutes() * 60 + nowInput.getUTCSeconds()) / 86400;
14011
14022
  const daysOld = currentJday + currentTime - epochJday;
14012
14023
  switch (outputUnits) {
@@ -14154,7 +14165,7 @@ ${this.line2}`;
14154
14165
  const BSTAR_PART_3 = _Tle.bstar_.start + 6;
14155
14166
  const BSTAR_PART_4 = _Tle.bstar_.stop - 1;
14156
14167
  const bstarSymbol = tleLine1.substring(_Tle.bstar_.start, BSTAR_PART_2);
14157
- let bstar1 = parseFloat(`0.${tleLine1.substring(BSTAR_PART_2, BSTAR_PART_3)}`);
14168
+ const mantissaFraction = tleLine1.substring(BSTAR_PART_2, BSTAR_PART_3);
14158
14169
  const exponentSymbol = tleLine1.substring(BSTAR_PART_3, BSTAR_PART_4);
14159
14170
  let exponent = parseInt(tleLine1.substring(BSTAR_PART_4, _Tle.bstar_.stop));
14160
14171
  if (exponentSymbol === "-") {
@@ -14162,12 +14173,16 @@ ${this.line2}`;
14162
14173
  } else if (exponentSymbol !== "+" && exponentSymbol !== " " && exponentSymbol !== "0") {
14163
14174
  throw new ParseError(`Invalid BSTAR exponent symbol: ${exponentSymbol}`, "TLE");
14164
14175
  }
14165
- bstar1 *= 10 ** exponent;
14176
+ let sign2 = 1;
14177
+ let integerPart = "0";
14166
14178
  if (bstarSymbol === "-") {
14167
- bstar1 *= -1;
14168
- } else if (bstarSymbol === "+" || bstarSymbol === " " || bstarSymbol === "0") ; else {
14179
+ sign2 = -1;
14180
+ } else if (bstarSymbol === "+" || bstarSymbol === " " || bstarSymbol === "0") ; else if (/^\d$/u.test(bstarSymbol)) {
14181
+ integerPart = bstarSymbol;
14182
+ } else {
14169
14183
  throw new ParseError(`Invalid BSTAR symbol: ${bstarSymbol}`, "TLE");
14170
14184
  }
14185
+ const bstar1 = sign2 * parseFloat(`${integerPart}.${mantissaFraction}`) * 10 ** exponent;
14171
14186
  return toPrecision(bstar1, 14);
14172
14187
  }
14173
14188
  /**
@@ -14222,22 +14237,25 @@ ${this.line2}`;
14222
14237
  }
14223
14238
  /**
14224
14239
  * Private value - used by United States Space Force to reference the orbit model used to generate the Tle. Will
14225
- * always be seen as zero externally (e.g. by "us", unless you are "them" - in which case, hello!).
14240
+ * almost always be seen as zero externally (e.g. by "us", unless you are "them" - in which case, hello!).
14226
14241
  *
14227
- * Starting in 2024, this may contain a 4 if the Tle was generated using the new SGP4-XP model. Until the source code
14228
- * is released, there is no way to support that format in JavaScript or TypeScript.
14242
+ * A value of 1 is tolerated in addition to 0: the field is informational and does not change how SGP4/SDP4
14243
+ * propagate the element set, and a handful of archival TLEs carry a 1 (originally an SGP marker).
14244
+ *
14245
+ * A value of 4 indicates the SGP4-XP model. Until that source code is released there is no way to support that
14246
+ * format in JavaScript or TypeScript, so it is rejected explicitly. Any other value is treated as malformed.
14229
14247
  * @example 0
14230
14248
  * @param tleLine1 The first line of the Tle to parse.
14231
- * @returns The ephemeris type.
14249
+ * @returns The ephemeris type (0 or 1).
14232
14250
  */
14233
14251
  static ephemerisType(tleLine1) {
14234
14252
  const ephemerisType = parseInt(tleLine1.substring(_Tle.ephemerisType_.start, _Tle.ephemerisType_.stop));
14235
- if (ephemerisType !== 0 && ephemerisType !== 4) {
14236
- throw new ParseError(`Invalid ephemeris type: ${ephemerisType}`, "TLE");
14237
- }
14238
14253
  if (ephemerisType === 4) {
14239
14254
  throw new ParseError("SGP4-XP ephemeris type is not supported", "TLE");
14240
14255
  }
14256
+ if (ephemerisType !== 0 && ephemerisType !== 1) {
14257
+ throw new ParseError(`Invalid ephemeris type: ${ephemerisType}`, "TLE");
14258
+ }
14241
14259
  return ephemerisType;
14242
14260
  }
14243
14261
  /**
@@ -14468,6 +14486,9 @@ ${this.line2}`;
14468
14486
  */
14469
14487
  static satNum(tleLine) {
14470
14488
  const satNumStr = tleLine.substring(_Tle.satNum_.start, _Tle.satNum_.stop);
14489
+ if (satNumStr.trim().length === 0) {
14490
+ return NaN;
14491
+ }
14471
14492
  const sixDigitSatNum = _Tle.convertA5to6Digit(satNumStr);
14472
14493
  return parseInt(sixDigitSatNum);
14473
14494
  }
@@ -14558,12 +14579,13 @@ ${this.line2}`;
14558
14579
  static parse(tleLine1, tleLine2) {
14559
14580
  const line1 = _Tle.parseLine1(tleLine1);
14560
14581
  const line2 = _Tle.parseLine2(tleLine2);
14561
- if (line1.satNum !== line2.satNum) {
14582
+ const isVimpel = line1.classification === "V";
14583
+ if (!isVimpel && line1.satNum !== line2.satNum) {
14562
14584
  console.info("Line 1 satNum:", line1.satNum);
14563
14585
  console.info("Line 2 satNum:", line2.satNum);
14564
14586
  throw new ParseError("Satellite numbers do not match between TLE lines", "TLE");
14565
14587
  }
14566
- if (line1.satNumRaw !== line2.satNumRaw) {
14588
+ if (!isVimpel && line1.satNumRaw !== line2.satNumRaw) {
14567
14589
  console.info("Line 1 satNumRaw:", line1.satNumRaw);
14568
14590
  console.info("Line 2 satNumRaw:", line2.satNumRaw);
14569
14591
  throw new ParseError("Raw satellite numbers do not match between TLE lines", "TLE");
@@ -14604,10 +14626,11 @@ ${this.line2}`;
14604
14626
  static parseAll(tleLine1, tleLine2) {
14605
14627
  const line1 = _Tle.parseLine1(tleLine1);
14606
14628
  const line2 = _Tle.parseLine2(tleLine2);
14607
- if (line1.satNum !== line2.satNum) {
14629
+ const isVimpel = line1.classification === "V";
14630
+ if (!isVimpel && line1.satNum !== line2.satNum) {
14608
14631
  throw new ParseError("Satellite numbers do not match between TLE lines", "TLE");
14609
14632
  }
14610
- if (line1.satNumRaw !== line2.satNumRaw) {
14633
+ if (!isVimpel && line1.satNumRaw !== line2.satNumRaw) {
14611
14634
  throw new ParseError("Raw satellite numbers do not match between TLE lines", "TLE");
14612
14635
  }
14613
14636
  if (line1.lineNumber1 !== 1) {
@@ -14963,9 +14986,6 @@ var SolarRadiationPressure = class _SolarRadiationPressure extends Force {
14963
14986
  this.area = area;
14964
14987
  this.reflectCoeff = reflectCoeff;
14965
14988
  }
14966
- mass;
14967
- area;
14968
- reflectCoeff;
14969
14989
  // / Solar pressure _(N/m²)_;
14970
14990
  static _kRef = 456e-8 * KM_PER_AU ** 2;
14971
14991
  acceleration(state) {
@@ -15348,8 +15368,6 @@ var ThirdBodyGravity = class _ThirdBodyGravity {
15348
15368
  this.moon = moon;
15349
15369
  this.sun = sun;
15350
15370
  }
15351
- moon;
15352
- sun;
15353
15371
  static moonGravity_(state) {
15354
15372
  const rMoon = Moon.eci(state.epoch.toDateTime());
15355
15373
  const aNum = rMoon.subtract(state.position);
@@ -15452,13 +15470,6 @@ var RAE = class _RAE {
15452
15470
  this.azRateRad = azRateRad;
15453
15471
  this.elRateRad = elRateRad;
15454
15472
  }
15455
- epoch;
15456
- rng;
15457
- azRad;
15458
- elRad;
15459
- rngRate;
15460
- azRateRad;
15461
- elRateRad;
15462
15473
  // / Create a new [Razel] object, using degrees for the angular values.
15463
15474
  static fromDegrees(epoch, range, azimuth, elevation, rangeRate, azimuthRate, elevationRate) {
15464
15475
  const azimuthRateRad = azimuthRate ? azimuthRate * DEG2RAD : void 0;
@@ -15927,9 +15938,6 @@ var RungeKuttaAdaptive = class _RungeKuttaAdaptive extends Propagator {
15927
15938
  this._cacheState = this.initState_;
15928
15939
  this.tolerance_ = Math.max(_RungeKuttaAdaptive._minTolerance, Math.abs(tolerance_));
15929
15940
  }
15930
- initState_;
15931
- forceModel_;
15932
- tolerance_;
15933
15941
  // / Initial state vector.
15934
15942
  _cacheState;
15935
15943
  _checkpoints = [];
@@ -16225,11 +16233,6 @@ var RungeKutta4Propagator = class extends Propagator {
16225
16233
  this.checkpoints_ = checkpoints_;
16226
16234
  this.stepSize_ = Math.abs(stepSize_);
16227
16235
  }
16228
- initState_;
16229
- forceModel_;
16230
- stepSize_;
16231
- cacheState_;
16232
- checkpoints_;
16233
16236
  // / Set the integrator step size to the provided number of [seconds].
16234
16237
  setStepSize(seconds) {
16235
16238
  this.stepSize_ = Math.abs(seconds);
@@ -16558,7 +16561,6 @@ var Sgp4Propagator = class extends Propagator {
16558
16561
  this.tle_ = tle_;
16559
16562
  this.cacheState_ = tle_.state.toJ2000();
16560
16563
  }
16561
- tle_;
16562
16564
  cacheState_;
16563
16565
  checkpoints_ = [];
16564
16566
  /**
@@ -16851,15 +16853,30 @@ var Satellite = class _Satellite extends SpaceObject {
16851
16853
  return objectId;
16852
16854
  }
16853
16855
  /**
16854
- * Derives {@link sccNum5} and {@link sccNum6} from {@link sccNum}. When the
16855
- * canonical ID is an extended (7+ digit) value that exceeds the TLE alpha-5
16856
- * capacity (max 339 999), both forms are set to `null`.
16856
+ * Normalizes {@link sccNum} to the display-canonical numeric form and derives
16857
+ * {@link sccNum5} and {@link sccNum6}. The class invariant is that
16858
+ * `Satellite.sccNum` is always numeric — never an alpha-5 string. Alpha-5
16859
+ * inputs ("T0001") are converted to their 6-digit numeric equivalent
16860
+ * ("270001"); the alpha-5 form is preserved on {@link sccNum5}.
16861
+ *
16862
+ * Extended (7+ digit) IDs that exceed the alpha-5 capacity (max 339 999)
16863
+ * leave sccNum5/sccNum6 set to `null`.
16864
+ *
16865
+ * Invalid input (empty string, malformed token) is passed through unchanged
16866
+ * so callers can store placeholder sccNums on notional / debris stubs.
16857
16867
  */
16858
16868
  assignAlpha5Forms_() {
16869
+ try {
16870
+ this.sccNum = Tle.convertA5to6Digit(this.sccNum);
16871
+ } catch {
16872
+ }
16873
+ if (/^0+\d/u.test(this.sccNum)) {
16874
+ this.sccNum = this.sccNum.replace(/^0+/u, "");
16875
+ }
16859
16876
  const kind = Tle.classifySatNum(this.sccNum);
16860
- if (kind === "numeric5" || kind === "alpha5" || kind === "numeric6") {
16877
+ if (kind === "numeric5" || kind === "numeric6") {
16861
16878
  this.sccNum5 = Tle.convert6DigitToA5(this.sccNum);
16862
- this.sccNum6 = Tle.convertA5to6Digit(this.sccNum5);
16879
+ this.sccNum6 = this.sccNum;
16863
16880
  } else {
16864
16881
  this.sccNum5 = null;
16865
16882
  this.sccNum6 = null;
@@ -19290,6 +19307,206 @@ var observationNoiseFromSigmas = (sigmas) => {
19290
19307
  return new Matrix(result);
19291
19308
  };
19292
19309
 
19310
+ // src/observation/Observation.ts
19311
+ var Observation = class {
19312
+ /**
19313
+ * Convert this observation's noise matrix into a covariance matrix.
19314
+ * @returns A matrix representing the noise covariance.
19315
+ */
19316
+ noiseCovariance() {
19317
+ return this.noise.reciprocal();
19318
+ }
19319
+ /**
19320
+ * Generates a noise sample from the noise covariance matrix.
19321
+ * @param sigma - The scaling factor for the noise covariance matrix.
19322
+ * @returns A matrix representing the noise sample.
19323
+ */
19324
+ noiseSample_(sigma) {
19325
+ return this.noiseCovariance().scale(sigma).cholesky();
19326
+ }
19327
+ /**
19328
+ * Randomly sample this observation in vector form within the
19329
+ * observation noise.
19330
+ * @param random Random number generator.
19331
+ * @param sigma Sigma value to scale the noise by.
19332
+ * @returns Sampled observation.
19333
+ */
19334
+ sampleVector(random, sigma) {
19335
+ const chol = this.noiseSample_(sigma);
19336
+ const gauss = random.gaussVector(this.noise.columns);
19337
+ const meas = this.toVector();
19338
+ return meas.add(chol.multiplyVector(gauss));
19339
+ }
19340
+ };
19341
+
19342
+ // src/observation/ObservationOptical.ts
19343
+ var ObservationOptical = class _ObservationOptical extends Observation {
19344
+ // / Create a new [ObservationOptical] object.
19345
+ constructor(site_, observation, noise_ = _ObservationOptical.defaultNoise) {
19346
+ super();
19347
+ this.site_ = site_;
19348
+ this.observation = observation;
19349
+ this.noise_ = noise_;
19350
+ }
19351
+ // / Inertial site location.
19352
+ get site() {
19353
+ return this.site_;
19354
+ }
19355
+ // / Noise matrix.
19356
+ get noise() {
19357
+ return this.noise_;
19358
+ }
19359
+ /**
19360
+ * Default noise matrix _(right-ascension, declination)_.
19361
+ * Based on the Maui Optical Site noise model.
19362
+ */
19363
+ static defaultNoise = _ObservationOptical.noiseFromSigmas(
19364
+ 37e-4 * DEG2RAD,
19365
+ 3e-3 * DEG2RAD
19366
+ );
19367
+ get epoch() {
19368
+ return this.observation.epoch;
19369
+ }
19370
+ toVector() {
19371
+ return Vector.fromList([this.observation.rightAscension, this.observation.declination]);
19372
+ }
19373
+ clos(propagator) {
19374
+ const position = propagator.propagate(this.epoch).position;
19375
+ const offset = position.subtract(this.site.position);
19376
+ const actual = this.observation.lineOfSight().normalize();
19377
+ const expected = offset.normalize();
19378
+ const slantRange = offset.magnitude();
19379
+ const theta = actual.angle(expected);
19380
+ if (isNaN(theta)) {
19381
+ return 0;
19382
+ }
19383
+ return 2 * slantRange * Math.sin(theta * 0.5);
19384
+ }
19385
+ ricDiff(propagator) {
19386
+ const r0 = this.site;
19387
+ const r1 = propagator.propagate(this.epoch);
19388
+ const r2 = this.observation.position(this.site, r1.position.distance(r0.position));
19389
+ return RIC.fromJ2000(new J20002(this.epoch, r2, Vector3D.origin), r1).position;
19390
+ }
19391
+ sample(random, sigma = 1) {
19392
+ const resultEl = this.sampleVector(random, sigma).elements;
19393
+ return new _ObservationOptical(
19394
+ this.site,
19395
+ new RadecTopocentric(this.epoch, resultEl[0], resultEl[1]),
19396
+ this.noise
19397
+ );
19398
+ }
19399
+ jacobian(propPairs) {
19400
+ const result = array2d(2, 6, 0);
19401
+ for (let i = 0; i < 6; i++) {
19402
+ const step = propPairs.step(i);
19403
+ const [high, low] = propPairs.get(i);
19404
+ const sl = low.propagate(this.epoch);
19405
+ const sh = high.propagate(this.epoch);
19406
+ const ol = RadecTopocentric.fromStateVector(sl, this.site);
19407
+ const oh = RadecTopocentric.fromStateVector(sh, this.site);
19408
+ result[0][i] = observationDerivative(oh.rightAscension, ol.rightAscension, step, true);
19409
+ result[1][i] = observationDerivative(oh.declination, ol.declination, step, true);
19410
+ }
19411
+ return new Matrix(result);
19412
+ }
19413
+ residual(propagator) {
19414
+ const result = array2d(2, 1, 0);
19415
+ const state = propagator.propagate(this.epoch);
19416
+ const radec = RadecTopocentric.fromStateVector(state, this.site);
19417
+ result[0][0] = normalizeAngle(this.observation.rightAscension, radec.rightAscension);
19418
+ result[1][0] = normalizeAngle(this.observation.declination, radec.declination);
19419
+ return new Matrix(result);
19420
+ }
19421
+ /**
19422
+ * Create a noise matrix from right ascension and declination standard deviantions.
19423
+ * @param raSigma Right ascension standard deviation
19424
+ * @param decSigma Declination standard deviation
19425
+ * @returns Noise matrix
19426
+ */
19427
+ static noiseFromSigmas(raSigma, decSigma) {
19428
+ return observationNoiseFromSigmas([raSigma, decSigma]);
19429
+ }
19430
+ };
19431
+
19432
+ // src/observation/ObservationRadar.ts
19433
+ var ObservationRadar = class _ObservationRadar extends Observation {
19434
+ // / Create a new [ObservationRadar] object.
19435
+ constructor(site_, observation, noise_ = _ObservationRadar.defaultNoise) {
19436
+ super();
19437
+ this.site_ = site_;
19438
+ this.observation = observation;
19439
+ this.noise_ = noise_;
19440
+ }
19441
+ // / Default noise matrix.
19442
+ static defaultNoise = _ObservationRadar.noiseFromSigmas(0.32, 0.015 * DEG2RAD, 0.015 * DEG2RAD);
19443
+ get epoch() {
19444
+ return this.observation.epoch;
19445
+ }
19446
+ get site() {
19447
+ return this.site_;
19448
+ }
19449
+ get noise() {
19450
+ return this.noise_;
19451
+ }
19452
+ toVector() {
19453
+ return Vector.fromList([this.observation.rng, this.observation.azRad, this.observation.elRad]);
19454
+ }
19455
+ clos(propagator) {
19456
+ const ri = propagator.propagate(this.epoch).position.subtract(this.site.position);
19457
+ return Math.abs(this.observation.rng - ri.magnitude());
19458
+ }
19459
+ ricDiff(propagator) {
19460
+ const r0 = propagator.propagate(this.epoch);
19461
+ const r1 = RAE.fromStateVector(r0, this.site);
19462
+ const r2 = this.observation.position(this.site, r1.azRad, r1.elRad);
19463
+ return RIC.fromJ2000(new J20002(this.epoch, r2, Vector3D.origin), r0).position;
19464
+ }
19465
+ sample(random, sigma = 1) {
19466
+ const result = this.sampleVector(random, sigma).elements;
19467
+ return new _ObservationRadar(
19468
+ this.site,
19469
+ new RAE(this.observation.epoch, result[0], result[1], result[2]),
19470
+ this.noise
19471
+ );
19472
+ }
19473
+ jacobian(propPairs) {
19474
+ const result = array2d(3, 6, 0);
19475
+ for (let i = 0; i < 6; i++) {
19476
+ const step = propPairs.step(i);
19477
+ const [high, low] = propPairs.get(i);
19478
+ const sl = low.propagate(this.epoch);
19479
+ const sh = high.propagate(this.epoch);
19480
+ const ol = RAE.fromStateVector(sl, this.site);
19481
+ const oh = RAE.fromStateVector(sh, this.site);
19482
+ result[0][i] = observationDerivative(oh.rng, ol.rng, step);
19483
+ result[1][i] = observationDerivative(oh.azRad, ol.azRad, step, true);
19484
+ result[2][i] = observationDerivative(oh.elRad, ol.elRad, step, true);
19485
+ }
19486
+ return new Matrix(result);
19487
+ }
19488
+ residual(propagator) {
19489
+ const result = array2d(3, 1, 0);
19490
+ const state = propagator.propagate(this.epoch);
19491
+ const razel = RAE.fromStateVector(state, this.site);
19492
+ result[0][0] = this.observation.rng - razel.rng;
19493
+ result[1][0] = normalizeAngle(this.observation.azRad, razel.azRad);
19494
+ result[2][0] = normalizeAngle(this.observation.elRad, razel.elRad);
19495
+ return new Matrix(result);
19496
+ }
19497
+ /**
19498
+ * Create a noise matrix from the range, azimuth, and elevation standard
19499
+ * deviations _(kilometers/radians)_.
19500
+ * @param rngSigma - The range standard deviation _(kilometers)_.
19501
+ * @param azSigma - The azimuth standard deviation _(radians)_.
19502
+ * @param elSigma - The elevation standard deviation _(radians)_.
19503
+ * @returns The noise matrix.
19504
+ */
19505
+ static noiseFromSigmas(rngSigma, azSigma, elSigma) {
19506
+ return observationNoiseFromSigmas([rngSigma, azSigma, elSigma]);
19507
+ }
19508
+ };
19509
+
19293
19510
  // src/observation/RadecGeocentric.ts
19294
19511
  var RadecGeocentric = class _RadecGeocentric {
19295
19512
  constructor(epoch, rightAscension, declination, range, rightAscensionRate, declinationRate, rangeRate) {
@@ -19301,13 +19518,6 @@ var RadecGeocentric = class _RadecGeocentric {
19301
19518
  this.declinationRate = declinationRate;
19302
19519
  this.rangeRate = rangeRate;
19303
19520
  }
19304
- epoch;
19305
- rightAscension;
19306
- declination;
19307
- range;
19308
- rightAscensionRate;
19309
- declinationRate;
19310
- rangeRate;
19311
19521
  /**
19312
19522
  * Creates a RadecGeocentric object from the given parameters in degrees.
19313
19523
  * @param epoch - The epoch in UTC.
@@ -19450,13 +19660,6 @@ var RadecTopocentric = class _RadecTopocentric {
19450
19660
  this.declinationRate = declinationRate;
19451
19661
  this.rangeRate = rangeRate;
19452
19662
  }
19453
- epoch;
19454
- rightAscension;
19455
- declination;
19456
- range;
19457
- rightAscensionRate;
19458
- declinationRate;
19459
- rangeRate;
19460
19663
  /**
19461
19664
  * Create a new RadecTopocentric object, using degrees for the angular values.
19462
19665
  * @param epoch UTC epoch.
@@ -20014,8 +20217,6 @@ var RkCheckpoint = class {
20014
20217
  this.cacheState = cacheState;
20015
20218
  this.stepSize = stepSize;
20016
20219
  }
20017
- cacheState;
20018
- stepSize;
20019
20220
  };
20020
20221
 
20021
20222
  // src/propagator/RkResult.ts
@@ -20026,9 +20227,6 @@ var RkResult = class {
20026
20227
  this.error = error;
20027
20228
  this.newStep = newStep;
20028
20229
  }
20029
- state;
20030
- error;
20031
- newStep;
20032
20230
  };
20033
20231
 
20034
20232
  // src/covariance/StateCovariance.ts
@@ -20049,8 +20247,6 @@ var StateCovariance = class _StateCovariance {
20049
20247
  this.matrix = matrix;
20050
20248
  this.frame = frame;
20051
20249
  }
20052
- matrix;
20053
- frame;
20054
20250
  // / Create a new [StateCovariance] object from 1-sigma values.
20055
20251
  static fromSigmas(sigmas, frame) {
20056
20252
  const n = sigmas.length;
@@ -20081,8 +20277,6 @@ var PropagatorPairs = class {
20081
20277
  this.posStep_ = posStep_;
20082
20278
  this.velStep_ = velStep_;
20083
20279
  }
20084
- posStep_;
20085
- velStep_;
20086
20280
  _high = Array(6).fill(null);
20087
20281
  _low = Array(6).fill(null);
20088
20282
  set(index, high, low) {
@@ -20116,9 +20310,6 @@ var BatchLeastSquaresResult = class {
20116
20310
  this.covariance = covariance2;
20117
20311
  this.rms = rms;
20118
20312
  }
20119
- state;
20120
- covariance;
20121
- rms;
20122
20313
  };
20123
20314
 
20124
20315
  // src/orbit-determination/BatchLeastSquaresOD.ts
@@ -20149,12 +20340,6 @@ var BatchLeastSquaresOD = class _BatchLeastSquaresOD {
20149
20340
  this.propagator_ = new RungeKutta89Propagator(this.apriori_, this.forceModel_);
20150
20341
  this.nominal_ = this.propagator_.propagate(this.start_);
20151
20342
  }
20152
- observations_;
20153
- apriori_;
20154
- forceModel_;
20155
- posStep_;
20156
- velStep_;
20157
- fastDerivatives_;
20158
20343
  /** Propagator pair cache, for generating observation Jacobians. */
20159
20344
  propPairs_;
20160
20345
  /** Nominal state propagator. */
@@ -20255,7 +20440,6 @@ var GibbsIOD = class _GibbsIOD {
20255
20440
  constructor(mu = Earth.mu) {
20256
20441
  this.mu = mu;
20257
20442
  }
20258
- mu;
20259
20443
  /** Abort solve if position plane exceeds this value. */
20260
20444
  static coplanarThreshold_ = 5 * DEG2RAD;
20261
20445
  /**
@@ -20314,7 +20498,6 @@ var HerrickGibbsIOD = class {
20314
20498
  constructor(mu = Earth.mu) {
20315
20499
  this.mu = mu;
20316
20500
  }
20317
- mu;
20318
20501
  // / Attempt to create a state estimate from three inertial position vectors.
20319
20502
  solve(r1, t1, r2, t2, r3, t3) {
20320
20503
  const dt31 = t3.difference(t1);
@@ -20575,7 +20758,6 @@ var LambertIOD = class _LambertIOD {
20575
20758
  constructor(mu = Earth.mu) {
20576
20759
  this.mu = mu;
20577
20760
  }
20578
- mu;
20579
20761
  /**
20580
20762
  * Try to guess the short path argument given an [interceptor] and
20581
20763
  * [target] state.
@@ -21263,11 +21445,6 @@ var LevenbergMarquardtResult = class {
21263
21445
  this.iterations = iterations;
21264
21446
  this.converged = converged;
21265
21447
  }
21266
- state;
21267
- covariance;
21268
- rms;
21269
- iterations;
21270
- converged;
21271
21448
  };
21272
21449
 
21273
21450
  // src/orbit-determination/LevenbergMarquardtOD.ts
@@ -21298,12 +21475,6 @@ var LevenbergMarquardtOD = class _LevenbergMarquardtOD {
21298
21475
  const propagator = new RungeKutta89Propagator(this.apriori_, this.forceModel_);
21299
21476
  this.nominal_ = propagator.propagate(this.start_);
21300
21477
  }
21301
- observations_;
21302
- apriori_;
21303
- forceModel_;
21304
- posStep_;
21305
- velStep_;
21306
- fastDerivatives_;
21307
21478
  /** Propagator pair cache, for generating observation Jacobians. */
21308
21479
  propPairs_;
21309
21480
  /** State estimate during solve. */
@@ -21462,8 +21633,6 @@ var SimplexEntry = class _SimplexEntry {
21462
21633
  this.x_ = new Vector(points);
21463
21634
  this.score = this.f_(points);
21464
21635
  }
21465
- f_;
21466
- points;
21467
21636
  /**
21468
21637
  * The cost function score at this vertex.
21469
21638
  *
@@ -21796,6 +21965,82 @@ var ModifiedGoodingIOD = class {
21796
21965
  }
21797
21966
  };
21798
21967
 
21968
+ // src/orbit-determination/Rv2Tle.ts
21969
+ var angleDiff_ = (a, b) => {
21970
+ let diff = (a - b) % TAU;
21971
+ if (diff > Math.PI) {
21972
+ diff -= TAU;
21973
+ } else if (diff < -Math.PI) {
21974
+ diff += TAU;
21975
+ }
21976
+ return diff;
21977
+ };
21978
+ var wrapTau_ = (angle) => {
21979
+ let wrapped = angle % TAU;
21980
+ if (wrapped < 0) {
21981
+ wrapped += TAU;
21982
+ }
21983
+ return wrapped;
21984
+ };
21985
+ var toElements_ = (epoch, position, velocity) => new J20002(
21986
+ epoch,
21987
+ new Vector3D(position.x, position.y, position.z),
21988
+ new Vector3D(velocity.x, velocity.y, velocity.z)
21989
+ ).toClassicalElements();
21990
+ var rv2tle = (epoch, position, velocity, options = {}) => {
21991
+ const maxIterations = options.maxIterations ?? 15;
21992
+ const toleranceKm = options.toleranceKm ?? 1e-4;
21993
+ const epochUtc = EpochUTC.fromDateTime(epoch);
21994
+ const target = toElements_(epochUtc, position, velocity);
21995
+ let mean2 = new ClassicalElements({
21996
+ epoch: epochUtc,
21997
+ semimajorAxis: target.semimajorAxis,
21998
+ eccentricity: target.eccentricity,
21999
+ inclination: target.inclination,
22000
+ rightAscension: target.rightAscension,
22001
+ argPerigee: target.argPerigee,
22002
+ trueAnomaly: target.trueAnomaly
22003
+ });
22004
+ let best = null;
22005
+ for (let iteration = 1; iteration <= maxIterations; iteration++) {
22006
+ let tle;
22007
+ let sv;
22008
+ try {
22009
+ tle = Tle.fromClassicalElements(mean2);
22010
+ const tsinceMin = (epoch.getTime() - tle.epoch.toDateTime().getTime()) / 6e4;
22011
+ sv = Sgp4.propagate(Sgp4.createSatrec(tle.line1, tle.line2), tsinceMin);
22012
+ } catch {
22013
+ return best;
22014
+ }
22015
+ if (!sv.position || typeof sv.position === "boolean" || !sv.velocity || typeof sv.velocity === "boolean") {
22016
+ return best;
22017
+ }
22018
+ const achievedPos = sv.position;
22019
+ const achievedVel = sv.velocity;
22020
+ const dx = achievedPos.x - position.x;
22021
+ const dy = achievedPos.y - position.y;
22022
+ const dz = achievedPos.z - position.z;
22023
+ const positionErrorKm = Math.sqrt(dx * dx + dy * dy + dz * dz);
22024
+ if (!best || positionErrorKm < best.positionErrorKm) {
22025
+ best = { tle1: tle.line1, tle2: tle.line2, positionErrorKm, iterations: iteration };
22026
+ }
22027
+ if (positionErrorKm < toleranceKm) {
22028
+ break;
22029
+ }
22030
+ const achieved = toElements_(epochUtc, achievedPos, achievedVel);
22031
+ mean2 = new ClassicalElements({
22032
+ epoch: epochUtc,
22033
+ semimajorAxis: mean2.semimajorAxis + (target.semimajorAxis - achieved.semimajorAxis),
22034
+ eccentricity: Math.min(Math.max(mean2.eccentricity + (target.eccentricity - achieved.eccentricity), 0), 0.999),
22035
+ inclination: Math.min(Math.max(mean2.inclination + angleDiff_(target.inclination, achieved.inclination), 0), Math.PI),
22036
+ rightAscension: wrapTau_(mean2.rightAscension + angleDiff_(target.rightAscension, achieved.rightAscension)),
22037
+ argPerigee: wrapTau_(mean2.argPerigee + angleDiff_(target.argPerigee, achieved.argPerigee)),
22038
+ trueAnomaly: wrapTau_(mean2.trueAnomaly + angleDiff_(target.trueAnomaly, achieved.trueAnomaly))
22039
+ });
22040
+ }
22041
+ return best;
22042
+ };
22043
+
21799
22044
  // src/covariance/CovarianceSample.ts
21800
22045
  var CovarianceSample = class {
21801
22046
  origin_;
@@ -21988,6 +22233,19 @@ var CovarianceSample = class {
21988
22233
  }
21989
22234
  };
21990
22235
 
22236
+ // src/covariance/displayRadii.ts
22237
+ function scaleAndClampRicSigmas(sigmas, confidence, caps) {
22238
+ const { radial, inTrack, crossTrack } = sigmas;
22239
+ if (![radial, inTrack, crossTrack].every((v) => Number.isFinite(v) && v >= 0)) {
22240
+ return null;
22241
+ }
22242
+ return {
22243
+ radial: Math.min(radial * confidence, caps.radial),
22244
+ inTrack: Math.min(inTrack * confidence, caps.inTrack),
22245
+ crossTrack: Math.min(crossTrack * confidence, caps.crossTrack)
22246
+ };
22247
+ }
22248
+
21991
22249
  // src/conjunction/ConjunctionAssessment.ts
21992
22250
  var ConjunctionAssessment = class {
21993
22251
  constructor(primary, secondary) {
@@ -21996,8 +22254,6 @@ var ConjunctionAssessment = class {
21996
22254
  this.primaryProp = this.createPropagator(primary, false);
21997
22255
  this.secondaryProp = this.createPropagator(secondary, false);
21998
22256
  }
21999
- primary;
22000
- secondary;
22001
22257
  primaryProp;
22002
22258
  secondaryProp;
22003
22259
  primaryCovSample;
@@ -23402,119 +23658,6 @@ var Sensor = class _Sensor {
23402
23658
  }
23403
23659
  };
23404
23660
 
23405
- // src/observation/Observation.ts
23406
- var Observation = class {
23407
- /**
23408
- * Convert this observation's noise matrix into a covariance matrix.
23409
- * @returns A matrix representing the noise covariance.
23410
- */
23411
- noiseCovariance() {
23412
- return this.noise.reciprocal();
23413
- }
23414
- /**
23415
- * Generates a noise sample from the noise covariance matrix.
23416
- * @param sigma - The scaling factor for the noise covariance matrix.
23417
- * @returns A matrix representing the noise sample.
23418
- */
23419
- noiseSample_(sigma) {
23420
- return this.noiseCovariance().scale(sigma).cholesky();
23421
- }
23422
- /**
23423
- * Randomly sample this observation in vector form within the
23424
- * observation noise.
23425
- * @param random Random number generator.
23426
- * @param sigma Sigma value to scale the noise by.
23427
- * @returns Sampled observation.
23428
- */
23429
- sampleVector(random, sigma) {
23430
- const chol = this.noiseSample_(sigma);
23431
- const gauss = random.gaussVector(this.noise.columns);
23432
- const meas = this.toVector();
23433
- return meas.add(chol.multiplyVector(gauss));
23434
- }
23435
- };
23436
-
23437
- // src/observation/ObservationRadar.ts
23438
- var ObservationRadar = class _ObservationRadar extends Observation {
23439
- // / Create a new [ObservationRadar] object.
23440
- constructor(site_, observation, noise_ = _ObservationRadar.defaultNoise) {
23441
- super();
23442
- this.site_ = site_;
23443
- this.observation = observation;
23444
- this.noise_ = noise_;
23445
- }
23446
- site_;
23447
- observation;
23448
- noise_;
23449
- // / Default noise matrix.
23450
- static defaultNoise = _ObservationRadar.noiseFromSigmas(0.32, 0.015 * DEG2RAD, 0.015 * DEG2RAD);
23451
- get epoch() {
23452
- return this.observation.epoch;
23453
- }
23454
- get site() {
23455
- return this.site_;
23456
- }
23457
- get noise() {
23458
- return this.noise_;
23459
- }
23460
- toVector() {
23461
- return Vector.fromList([this.observation.rng, this.observation.azRad, this.observation.elRad]);
23462
- }
23463
- clos(propagator) {
23464
- const ri = propagator.propagate(this.epoch).position.subtract(this.site.position);
23465
- return Math.abs(this.observation.rng - ri.magnitude());
23466
- }
23467
- ricDiff(propagator) {
23468
- const r0 = propagator.propagate(this.epoch);
23469
- const r1 = RAE.fromStateVector(r0, this.site);
23470
- const r2 = this.observation.position(this.site, r1.azRad, r1.elRad);
23471
- return RIC.fromJ2000(new J20002(this.epoch, r2, Vector3D.origin), r0).position;
23472
- }
23473
- sample(random, sigma = 1) {
23474
- const result = this.sampleVector(random, sigma).elements;
23475
- return new _ObservationRadar(
23476
- this.site,
23477
- new RAE(this.observation.epoch, result[0], result[1], result[2]),
23478
- this.noise
23479
- );
23480
- }
23481
- jacobian(propPairs) {
23482
- const result = array2d(3, 6, 0);
23483
- for (let i = 0; i < 6; i++) {
23484
- const step = propPairs.step(i);
23485
- const [high, low] = propPairs.get(i);
23486
- const sl = low.propagate(this.epoch);
23487
- const sh = high.propagate(this.epoch);
23488
- const ol = RAE.fromStateVector(sl, this.site);
23489
- const oh = RAE.fromStateVector(sh, this.site);
23490
- result[0][i] = observationDerivative(oh.rng, ol.rng, step);
23491
- result[1][i] = observationDerivative(oh.azRad, ol.azRad, step, true);
23492
- result[2][i] = observationDerivative(oh.elRad, ol.elRad, step, true);
23493
- }
23494
- return new Matrix(result);
23495
- }
23496
- residual(propagator) {
23497
- const result = array2d(3, 1, 0);
23498
- const state = propagator.propagate(this.epoch);
23499
- const razel = RAE.fromStateVector(state, this.site);
23500
- result[0][0] = this.observation.rng - razel.rng;
23501
- result[1][0] = normalizeAngle(this.observation.azRad, razel.azRad);
23502
- result[2][0] = normalizeAngle(this.observation.elRad, razel.elRad);
23503
- return new Matrix(result);
23504
- }
23505
- /**
23506
- * Create a noise matrix from the range, azimuth, and elevation standard
23507
- * deviations _(kilometers/radians)_.
23508
- * @param rngSigma - The range standard deviation _(kilometers)_.
23509
- * @param azSigma - The azimuth standard deviation _(radians)_.
23510
- * @param elSigma - The elevation standard deviation _(radians)_.
23511
- * @returns The noise matrix.
23512
- */
23513
- static noiseFromSigmas(rngSigma, azSigma, elSigma) {
23514
- return observationNoiseFromSigmas([rngSigma, azSigma, elSigma]);
23515
- }
23516
- };
23517
-
23518
23661
  // src/sensor/RadarSensor.ts
23519
23662
  var RadarSensor = class extends Sensor {
23520
23663
  /** Radar beamwidth in degrees */
@@ -23926,99 +24069,6 @@ var MechanicalRadar = class _MechanicalRadar extends RadarSensor {
23926
24069
  }
23927
24070
  };
23928
24071
 
23929
- // src/observation/ObservationOptical.ts
23930
- var ObservationOptical = class _ObservationOptical extends Observation {
23931
- // / Create a new [ObservationOptical] object.
23932
- constructor(site_, observation, noise_ = _ObservationOptical.defaultNoise) {
23933
- super();
23934
- this.site_ = site_;
23935
- this.observation = observation;
23936
- this.noise_ = noise_;
23937
- }
23938
- site_;
23939
- observation;
23940
- noise_;
23941
- // / Inertial site location.
23942
- get site() {
23943
- return this.site_;
23944
- }
23945
- // / Noise matrix.
23946
- get noise() {
23947
- return this.noise_;
23948
- }
23949
- /**
23950
- * Default noise matrix _(right-ascension, declination)_.
23951
- * Based on the Maui Optical Site noise model.
23952
- */
23953
- static defaultNoise = _ObservationOptical.noiseFromSigmas(
23954
- 37e-4 * DEG2RAD,
23955
- 3e-3 * DEG2RAD
23956
- );
23957
- get epoch() {
23958
- return this.observation.epoch;
23959
- }
23960
- toVector() {
23961
- return Vector.fromList([this.observation.rightAscension, this.observation.declination]);
23962
- }
23963
- clos(propagator) {
23964
- const position = propagator.propagate(this.epoch).position;
23965
- const offset = position.subtract(this.site.position);
23966
- const actual = this.observation.lineOfSight().normalize();
23967
- const expected = offset.normalize();
23968
- const slantRange = offset.magnitude();
23969
- const theta = actual.angle(expected);
23970
- if (isNaN(theta)) {
23971
- return 0;
23972
- }
23973
- return 2 * slantRange * Math.sin(theta * 0.5);
23974
- }
23975
- ricDiff(propagator) {
23976
- const r0 = this.site;
23977
- const r1 = propagator.propagate(this.epoch);
23978
- const r2 = this.observation.position(this.site, r1.position.distance(r0.position));
23979
- return RIC.fromJ2000(new J20002(this.epoch, r2, Vector3D.origin), r1).position;
23980
- }
23981
- sample(random, sigma = 1) {
23982
- const resultEl = this.sampleVector(random, sigma).elements;
23983
- return new _ObservationOptical(
23984
- this.site,
23985
- new RadecTopocentric(this.epoch, resultEl[0], resultEl[1]),
23986
- this.noise
23987
- );
23988
- }
23989
- jacobian(propPairs) {
23990
- const result = array2d(2, 6, 0);
23991
- for (let i = 0; i < 6; i++) {
23992
- const step = propPairs.step(i);
23993
- const [high, low] = propPairs.get(i);
23994
- const sl = low.propagate(this.epoch);
23995
- const sh = high.propagate(this.epoch);
23996
- const ol = RadecTopocentric.fromStateVector(sl, this.site);
23997
- const oh = RadecTopocentric.fromStateVector(sh, this.site);
23998
- result[0][i] = observationDerivative(oh.rightAscension, ol.rightAscension, step, true);
23999
- result[1][i] = observationDerivative(oh.declination, ol.declination, step, true);
24000
- }
24001
- return new Matrix(result);
24002
- }
24003
- residual(propagator) {
24004
- const result = array2d(2, 1, 0);
24005
- const state = propagator.propagate(this.epoch);
24006
- const radec = RadecTopocentric.fromStateVector(state, this.site);
24007
- result[0][0] = normalizeAngle(this.observation.rightAscension, radec.rightAscension);
24008
- result[1][0] = normalizeAngle(this.observation.declination, radec.declination);
24009
- return new Matrix(result);
24010
- }
24011
- /**
24012
- * Create a noise matrix from right ascension and declination standard deviantions.
24013
- * @param raSigma Right ascension standard deviation
24014
- * @param decSigma Declination standard deviation
24015
- * @returns Noise matrix
24016
- */
24017
- static noiseFromSigmas(raSigma, decSigma) {
24018
- return observationNoiseFromSigmas([raSigma, decSigma]);
24019
- }
24020
- };
24021
-
24022
24072
  // src/sensor/OpticalSensor.ts
24023
24073
  var OpticalSensor = class _OpticalSensor extends Sensor {
24024
24074
  /** Telescope aperture in meters */
@@ -27231,11 +27281,6 @@ var ChebyshevCoefficients = class _ChebyshevCoefficients {
27231
27281
  this.cyd_ = _ChebyshevCoefficients._derivative(a, b, cy_);
27232
27282
  this.czd_ = _ChebyshevCoefficients._derivative(a, b, cz_);
27233
27283
  }
27234
- a;
27235
- b;
27236
- cx_;
27237
- cy_;
27238
- cz_;
27239
27284
  cxd_;
27240
27285
  cyd_;
27241
27286
  czd_;
@@ -27757,11 +27802,6 @@ var TwoBurnOrbitTransfer = class _TwoBurnOrbitTransfer {
27757
27802
  this.vTransB = vTransB;
27758
27803
  this.tTrans = tTrans;
27759
27804
  }
27760
- vInit;
27761
- vFinal;
27762
- vTransA;
27763
- vTransB;
27764
- tTrans;
27765
27805
  /**
27766
27806
  * Calculates the parameters for a Hohmann transfer orbit between two circular orbits.
27767
27807
  * @param rInit The initial radius of the orbit. (km)
@@ -27814,7 +27854,6 @@ var ChebyshevCompressor = class _ChebyshevCompressor {
27814
27854
  constructor(interpolator_) {
27815
27855
  this.interpolator_ = interpolator_;
27816
27856
  }
27817
- interpolator_;
27818
27857
  /** Returns the cosine of π times x. */
27819
27858
  static cosPi_(x) {
27820
27859
  return Math.cos(Math.PI * x);
@@ -28055,8 +28094,6 @@ var SimpleLinearRegression = class _SimpleLinearRegression {
28055
28094
  this.ys = ys;
28056
28095
  this.update();
28057
28096
  }
28058
- xs;
28059
- ys;
28060
28097
  /** Line slope (m in y = mx + b). */
28061
28098
  slope_ = 0;
28062
28099
  /** Y-axis intercept (b in y = mx + b). */
@@ -29186,6 +29223,24 @@ var LaunchTrajectoryGenerator = class _LaunchTrajectoryGenerator {
29186
29223
  }
29187
29224
  return azimuth;
29188
29225
  }
29226
+ /**
29227
+ * Compute orbital inclination from a launch azimuth and launch site latitude.
29228
+ *
29229
+ * Inverse of {@link computeLaunchAzimuth}: from the spherical-triangle relation
29230
+ * `cos(i) = sin(azimuth) * cos(latitude)`. The result is the magnitude of the
29231
+ * inclination in degrees (always in [0, 180]); the launch direction (N/S) is
29232
+ * implied by the azimuth quadrant, not by this value.
29233
+ *
29234
+ * @param azimuthDeg Launch azimuth in degrees (from north, clockwise).
29235
+ * @param latDeg Launch site latitude in degrees.
29236
+ * @returns Orbital inclination in degrees, in [0, 180].
29237
+ */
29238
+ static computeInclinationFromAzimuth(azimuthDeg, latDeg) {
29239
+ const az = azimuthDeg * DEG2RAD;
29240
+ const lat = latDeg * DEG2RAD;
29241
+ const cosInc = Math.max(-1, Math.min(1, Math.sin(az) * Math.cos(lat)));
29242
+ return Math.acos(cosInc) * RAD2DEG;
29243
+ }
29189
29244
  /**
29190
29245
  * Estimate ascent duration based on target insertion altitude.
29191
29246
  * Loosely calibrated: ~480s for 200km, ~540s for 400km, ~600s for 800km.
@@ -29811,8 +29866,20 @@ var OrbitFinder = class _OrbitFinder {
29811
29866
  this.debug_(`Found perigee at altitude ${perigeeParams.altitude} km`);
29812
29867
  return perigeeParams;
29813
29868
  }
29869
+ /**
29870
+ * Returns the 5-char satnum to embed in the synthesized TLEs.
29871
+ *
29872
+ * Why not {@link Satellite.sccNum}? For extended (7+ digit) IDs sccNum is
29873
+ * the full 9-digit canonical value, which would push the TLE past 69 chars
29874
+ * and break Sgp4.createSatrec. The input tle1 always has a valid 5-char
29875
+ * satnum at columns 2-6, and SGP4 itself doesn't care what value lives
29876
+ * there as long as line 1 and line 2 agree — so use that.
29877
+ */
29878
+ tleSatNum_() {
29879
+ return this.sat_.tle1.substring(2, 7);
29880
+ }
29814
29881
  generateTle1_() {
29815
- return `1 ${this.sat_.sccNum}U ${this.sat_.tle1.substring(9, 17)} ${this.sat_.tle1.substring(18, 32)}${this.sat_.tle1.substring(32, 71)}`;
29882
+ return `1 ${this.tleSatNum_()}U ${this.sat_.tle1.substring(9, 17)} ${this.sat_.tle1.substring(18, 32)}${this.sat_.tle1.substring(32, 71)}`;
29816
29883
  }
29817
29884
  generateTle2_(newParams) {
29818
29885
  const mergedParams = {
@@ -29827,7 +29894,7 @@ var OrbitFinder = class _OrbitFinder {
29827
29894
  const argPer = mergedParams.argOfPerigee.toFixed(4).padStart(8, "0");
29828
29895
  const meanA = mergedParams.meanAnomaly.toFixed(4).padStart(8, "0");
29829
29896
  const meanMo = this.sat_.tle2.substring(52, 63);
29830
- return `2 ${this.sat_.sccNum} ${inc} ${raan} ${ecc} ${argPer} ${meanA} ${meanMo} 10`;
29897
+ return `2 ${this.tleSatNum_()} ${inc} ${raan} ${ecc} ${argPer} ${meanA} ${meanMo} 10`;
29831
29898
  }
29832
29899
  };
29833
29900
 
@@ -30187,6 +30254,33 @@ var RepeatGroundTrack = class _RepeatGroundTrack {
30187
30254
  * You should have received a copy of the GNU Affero General Public License along with
30188
30255
  * Orbital Object ToolKit. If not, see <http://www.gnu.org/licenses/>.
30189
30256
  */
30257
+ /**
30258
+ * @author Theodore Kruczek.
30259
+ * @description Orbital Object ToolKit (ootk) is a collection of tools for working
30260
+ * with satellites and other orbital objects.
30261
+ *
30262
+ * @license MIT License
30263
+ *
30264
+ * @Copyright (c) 2025 Theodore Kruczek
30265
+ *
30266
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
30267
+ * of this software and associated documentation files (the "Software"), to deal
30268
+ * in the Software without restriction, including without limitation the rights
30269
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30270
+ * copies of the Software, and to permit persons to whom the Software is
30271
+ * furnished to do so, subject to the following conditions:
30272
+ *
30273
+ * The above copyright notice and this permission notice shall be included in all
30274
+ * copies or substantial portions of the Software.
30275
+ *
30276
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30277
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30278
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30279
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30280
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30281
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30282
+ * SOFTWARE.
30283
+ */
30190
30284
  /**
30191
30285
  * @author Theodore Kruczek
30192
30286
  * @description Orbital Object ToolKit (ootk) is a collection of tools for working
@@ -30390,6 +30484,9 @@ exports.ModulationType = ModulationType;
30390
30484
  exports.Moon = Moon;
30391
30485
  exports.MoonBody = MoonBody;
30392
30486
  exports.Neptune = Neptune;
30487
+ exports.Observation = Observation;
30488
+ exports.ObservationOptical = ObservationOptical;
30489
+ exports.ObservationRadar = ObservationRadar;
30393
30490
  exports.OdmExporter = OdmExporter;
30394
30491
  exports.OemParser = OemParser;
30395
30492
  exports.OmmParser = OmmParser;
@@ -30416,6 +30513,8 @@ exports.Quaternion = Quaternion;
30416
30513
  exports.RAD2DEG = RAD2DEG;
30417
30514
  exports.RADIUS_OF_EARTH = RADIUS_OF_EARTH;
30418
30515
  exports.RAE = RAE;
30516
+ exports.RCS_VMAG_ESTIMATE_MAX = RCS_VMAG_ESTIMATE_MAX;
30517
+ exports.RCS_VMAG_ESTIMATE_MIN = RCS_VMAG_ESTIMATE_MIN;
30419
30518
  exports.RIC = RIC;
30420
30519
  exports.RadarSensor = RadarSensor;
30421
30520
  exports.RadecGeocentric = RadecGeocentric;
@@ -30493,6 +30592,7 @@ exports.calcIncFromAz = calcIncFromAz;
30493
30592
  exports.calcInertAz = calcInertAz;
30494
30593
  exports.calculateFspl = calculateFspl;
30495
30594
  exports.calculatePropagationDelay = calculatePropagationDelay;
30595
+ exports.cappedScreeningCovarianceFromTle = cappedScreeningCovarianceFromTle;
30496
30596
  exports.clamp = clamp;
30497
30597
  exports.concat = concat;
30498
30598
  exports.copySign = copySign;
@@ -30517,6 +30617,7 @@ exports.eci2lla = eci2lla;
30517
30617
  exports.eci2rae = eci2rae;
30518
30618
  exports.enu2rf = enu2rf;
30519
30619
  exports.estimateRcs = estimateRcs;
30620
+ exports.estimateVmagFromRcs = estimateVmagFromRcs;
30520
30621
  exports.evalPoly = evalPoly;
30521
30622
  exports.factorial = factorial;
30522
30623
  exports.gamma = gamma;
@@ -30557,6 +30658,8 @@ exports.rae2raeOffBoresight = rae2raeOffBoresight;
30557
30658
  exports.rae2ruv = rae2ruv;
30558
30659
  exports.rae2sez = rae2sez;
30559
30660
  exports.relativeVelocity = relativeVelocity;
30661
+ exports.rv2tle = rv2tle;
30662
+ exports.scaleAndClampRicSigmas = scaleAndClampRicSigmas;
30560
30663
  exports.sec2day = sec2day;
30561
30664
  exports.sec2deg = sec2deg;
30562
30665
  exports.sec2min = sec2min;