ootk 6.0.1 → 6.0.3

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
@@ -9849,7 +9849,7 @@ var FormatTle = class _FormatTle {
9849
9849
  const { inc, meanmo, rasc, argPe, meana, ecen, epochyr, epochday, intl } = tleParams;
9850
9850
  const scc = Tle.convert6DigitToA5(tleParams.scc);
9851
9851
  const epochYrStr = String(epochyr).padStart(2, "0");
9852
- const epochdayStr = parseFloat(String(epochday)).toFixed(8).padStart(12, "0");
9852
+ const epochdayStr = Number(epochday).toFixed(8).padStart(12, "0");
9853
9853
  const incStr = _FormatTle.inclination(inc);
9854
9854
  const meanmoStr = _FormatTle.meanMotion(meanmo);
9855
9855
  const rascStr = _FormatTle.rightAscension(rasc);
@@ -9857,13 +9857,23 @@ var FormatTle = class _FormatTle {
9857
9857
  const meanaStr = _FormatTle.meanAnomaly(meana);
9858
9858
  const ecenStr = _FormatTle.eccentricity(ecen);
9859
9859
  const intlStr = intl.padEnd(8, " ");
9860
- let TLE1Ending = tleParams.sat ? tleParams.sat.tle1.substring(32, 71) : " +.00000000 +00000+0 +00000-0 0 9990";
9861
- TLE1Ending = TLE1Ending[1] === " " ? _FormatTle.setCharAt(TLE1Ending, 1, "+") : TLE1Ending;
9862
- TLE1Ending = TLE1Ending[12] === " " ? _FormatTle.setCharAt(TLE1Ending, 12, "+") : TLE1Ending;
9863
- TLE1Ending = TLE1Ending[21] === " " ? _FormatTle.setCharAt(TLE1Ending, 21, "+") : TLE1Ending;
9864
- TLE1Ending = TLE1Ending[32] === " " ? _FormatTle.setCharAt(TLE1Ending, 32, "0") : TLE1Ending;
9865
- const tle1 = `1 ${scc}U ${intlStr} ${epochYrStr}${epochdayStr}${TLE1Ending}`;
9866
- const tle2 = `2 ${scc} ${incStr} ${rascStr} ${ecenStr} ${argPeStr} ${meanaStr} ${meanmoStr} 00010`;
9860
+ const classification = tleParams.classification ?? "U";
9861
+ const elementSetNo = (tleParams.elementSetNo ?? 999).toString().padStart(4, " ");
9862
+ const ephemerisType = (tleParams.ephemerisType ?? 0).toString();
9863
+ const revAtEpoch = (tleParams.revAtEpoch ?? 0).toString().padStart(5, " ");
9864
+ let TLE1Ending;
9865
+ if (tleParams.sat) {
9866
+ TLE1Ending = tleParams.sat.tle1.substring(32, 71);
9867
+ } else {
9868
+ const mmDot = _FormatTle.formatMeanMotionDot(tleParams.meanMotionDot ?? 0);
9869
+ const mmDdot = _FormatTle.formatTleExponential(tleParams.meanMotionDdot ?? 0);
9870
+ const bstarStr = _FormatTle.formatTleExponential(tleParams.bstar ?? 0);
9871
+ TLE1Ending = ` ${mmDot} ${mmDdot} ${bstarStr} ${ephemerisType} ${elementSetNo}0`;
9872
+ }
9873
+ const tle1Pre = `1 ${scc}${classification} ${intlStr} ${epochYrStr}${epochdayStr}${TLE1Ending}`;
9874
+ const tle1 = _FormatTle.setCharAt(tle1Pre, 68, _FormatTle.tleChecksum(tle1Pre).toString());
9875
+ const tle2Pre = `2 ${scc} ${incStr} ${rascStr} ${ecenStr} ${argPeStr} ${meanaStr} ${meanmoStr}${revAtEpoch}0`;
9876
+ const tle2 = _FormatTle.setCharAt(tle2Pre, 68, _FormatTle.tleChecksum(tle2Pre).toString());
9867
9877
  return { tle1, tle2 };
9868
9878
  }
9869
9879
  /**
@@ -9877,7 +9887,7 @@ var FormatTle = class _FormatTle {
9877
9887
  argPe = argPe.toString();
9878
9888
  }
9879
9889
  const argPeNum = parseFloat(argPe).toFixed(4);
9880
- const argPe0 = argPeNum.padStart(8, "0");
9890
+ const argPe0 = argPeNum.padStart(8, " ");
9881
9891
  if (argPe0.length !== 8) {
9882
9892
  throw new ValidationError("Argument of perigee must be 8 characters", "argPe", argPe0);
9883
9893
  }
@@ -9891,7 +9901,10 @@ var FormatTle = class _FormatTle {
9891
9901
  */
9892
9902
  static eccentricity(ecen) {
9893
9903
  if (typeof ecen === "number") {
9894
- ecen = ecen.toFixed(7);
9904
+ const ecenStr = ecen.toString();
9905
+ const dotIdx = ecenStr.indexOf(".");
9906
+ const afterDot = dotIdx >= 0 ? ecenStr.substring(dotIdx + 1) : "";
9907
+ ecen = `0.${afterDot.substring(0, 7).padEnd(7, "0")}`;
9895
9908
  }
9896
9909
  let ecen0 = ecen.padEnd(9, "0");
9897
9910
  if (ecen0[1] === ".") {
@@ -9915,7 +9928,7 @@ var FormatTle = class _FormatTle {
9915
9928
  inc = inc.toString();
9916
9929
  }
9917
9930
  const incNum = parseFloat(inc).toFixed(4);
9918
- const inc0 = incNum.padStart(8, "0");
9931
+ const inc0 = incNum.padStart(8, " ");
9919
9932
  if (inc0.length !== 8) {
9920
9933
  throw new ValidationError("Inclination must be 8 characters", "inclination", inc0);
9921
9934
  }
@@ -9932,7 +9945,7 @@ var FormatTle = class _FormatTle {
9932
9945
  meana = meana.toString();
9933
9946
  }
9934
9947
  const meanaNum = parseFloat(meana).toFixed(4);
9935
- const meana0 = meanaNum.padStart(8, "0");
9948
+ const meana0 = meanaNum.padStart(8, " ");
9936
9949
  if (meana0.length !== 8) {
9937
9950
  throw new ValidationError("Mean anomaly must be 8 characters", "meanAnomaly", meana0);
9938
9951
  }
@@ -9973,7 +9986,7 @@ var FormatTle = class _FormatTle {
9973
9986
  rasc = rasc.toString();
9974
9987
  }
9975
9988
  const rascNum = parseFloat(rasc).toFixed(4);
9976
- const rasc0 = rascNum.padStart(8, "0");
9989
+ const rasc0 = rascNum.padStart(8, " ");
9977
9990
  if (rasc0.length !== 8) {
9978
9991
  throw new ValidationError("Right ascension must be 8 characters", "rightAscension", rasc0);
9979
9992
  }
@@ -9992,6 +10005,61 @@ var FormatTle = class _FormatTle {
9992
10005
  }
9993
10006
  return `${str.substring(0, index)}${chr}${str.substring(index + 1)}`;
9994
10007
  }
10008
+ /**
10009
+ * Format mean motion dot (first derivative / 2) for TLE line 1.
10010
+ * Format: sign + ".NNNNNNNN" = 10 chars total.
10011
+ * @param value - Mean motion dot value (rev/day^2)
10012
+ * @returns Formatted 10-character string
10013
+ */
10014
+ static formatMeanMotionDot(value) {
10015
+ const sign2 = value >= 0 ? " " : "-";
10016
+ return sign2 + Math.abs(value).toFixed(8).substring(1);
10017
+ }
10018
+ /**
10019
+ * Format a value in TLE exponential notation for BSTAR or mean motion ddot.
10020
+ * Format: "sNNNNN±N" (8 chars) where mantissa has implied leading decimal point.
10021
+ * Example: 0.00017507 → " 17507-3" (i.e., .17507 × 10^-3)
10022
+ * @param value - The value to format
10023
+ * @returns Formatted 8-character string
10024
+ */
10025
+ static formatTleExponential(value) {
10026
+ if (value === 0) {
10027
+ return " 00000+0";
10028
+ }
10029
+ const sign2 = value >= 0 ? " " : "-";
10030
+ const absVal = Math.abs(value);
10031
+ let exponent = Math.floor(Math.log10(absVal)) + 1;
10032
+ let mantissa = absVal / Math.pow(10, exponent);
10033
+ if (mantissa >= 1) {
10034
+ mantissa /= 10;
10035
+ exponent++;
10036
+ }
10037
+ if (mantissa < 0.1 && mantissa > 0) {
10038
+ mantissa *= 10;
10039
+ exponent--;
10040
+ }
10041
+ const mantissaStr = Math.round(mantissa * 1e5).toString().padStart(5, "0");
10042
+ const expSign = exponent >= 0 ? "+" : "-";
10043
+ const expStr = Math.abs(exponent).toString();
10044
+ return sign2 + mantissaStr + expSign + expStr;
10045
+ }
10046
+ /**
10047
+ * Compute TLE line checksum (modulo 10 sum of digits, '-' counts as 1).
10048
+ * @param line - TLE line (first 68 characters are summed)
10049
+ * @returns Checksum digit (0-9)
10050
+ */
10051
+ static tleChecksum(line) {
10052
+ let sum = 0;
10053
+ for (let i = 0; i < 68 && i < line.length; i++) {
10054
+ const c = line[i];
10055
+ if (c >= "0" && c <= "9") {
10056
+ sum += parseInt(c);
10057
+ } else if (c === "-") {
10058
+ sum += 1;
10059
+ }
10060
+ }
10061
+ return sum % 10;
10062
+ }
9995
10063
  };
9996
10064
 
9997
10065
  // src/coordinate/RelativeState.ts
@@ -13187,13 +13255,28 @@ var ClassicalElements = class _ClassicalElements {
13187
13255
  const h = pos.cross(vel);
13188
13256
  const i = Math.acos(clamp(h.z / h.magnitude(), -1, 1));
13189
13257
  const n = Vector3D.zAxis.cross(h);
13190
- let o = Math.acos(clamp(n.x / n.magnitude(), -1, 1));
13191
- if (n.y < 0) {
13192
- o = TAU - o;
13193
- }
13194
- let w = n.angle(eVec);
13195
- if (eVec.z < 0) {
13196
- w = TAU - w;
13258
+ const nMag = n.magnitude();
13259
+ let o;
13260
+ let w;
13261
+ if (nMag < 1e-12) {
13262
+ o = 0;
13263
+ if (e > 1e-12) {
13264
+ w = Math.atan2(eVec.y, eVec.x);
13265
+ if (w < 0) {
13266
+ w = w + TAU;
13267
+ }
13268
+ } else {
13269
+ w = 0;
13270
+ }
13271
+ } else {
13272
+ o = Math.acos(clamp(n.x / nMag, -1, 1));
13273
+ if (n.y < 0) {
13274
+ o = TAU - o;
13275
+ }
13276
+ w = n.angle(eVec);
13277
+ if (eVec.z < 0) {
13278
+ w = TAU - w;
13279
+ }
13197
13280
  }
13198
13281
  let v = eVec.angle(pos);
13199
13282
  if (pos.dot(vel) < 0) {
@@ -16625,8 +16708,9 @@ var Satellite = class _Satellite extends SpaceObject {
16625
16708
  this.satrec = Sgp4.createSatrec(tle1, tle2);
16626
16709
  }
16627
16710
  parseOmmAndUpdateOrbit_(omm) {
16628
- this.sccNum = omm.NORAD_CAT_ID.padStart(5, "0");
16629
- this.sccNum5 = Tle.convert6DigitToA5(omm.NORAD_CAT_ID);
16711
+ const noradStr = String(omm.NORAD_CAT_ID);
16712
+ this.sccNum = noradStr.padStart(5, "0");
16713
+ this.sccNum5 = Tle.convert6DigitToA5(noradStr);
16630
16714
  this.sccNum6 = Tle.convertA5to6Digit(this.sccNum5);
16631
16715
  this.intlDes = omm.OBJECT_ID;
16632
16716
  const YYYY = omm.EPOCH.slice(0, 4);
@@ -16639,6 +16723,19 @@ var Satellite = class _Satellite extends SpaceObject {
16639
16723
  const dayOfYear = (epochDateObj - Date.UTC(Number(YYYY), 0, 0)) / 864e5;
16640
16724
  const ommParsed = {
16641
16725
  ...omm,
16726
+ NORAD_CAT_ID: noradStr,
16727
+ MEAN_MOTION: String(omm.MEAN_MOTION),
16728
+ ECCENTRICITY: String(omm.ECCENTRICITY),
16729
+ INCLINATION: String(omm.INCLINATION),
16730
+ RA_OF_ASC_NODE: String(omm.RA_OF_ASC_NODE),
16731
+ ARG_OF_PERICENTER: String(omm.ARG_OF_PERICENTER),
16732
+ MEAN_ANOMALY: String(omm.MEAN_ANOMALY),
16733
+ EPHEMERIS_TYPE: String(omm.EPHEMERIS_TYPE),
16734
+ ELEMENT_SET_NO: String(omm.ELEMENT_SET_NO),
16735
+ REV_AT_EPOCH: String(omm.REV_AT_EPOCH),
16736
+ BSTAR: String(omm.BSTAR),
16737
+ MEAN_MOTION_DOT: String(omm.MEAN_MOTION_DOT),
16738
+ MEAN_MOTION_DDOT: String(omm.MEAN_MOTION_DDOT),
16642
16739
  epoch: {
16643
16740
  year: Number(YYYY),
16644
16741
  month: Number(MM),
@@ -16651,20 +16748,41 @@ var Satellite = class _Satellite extends SpaceObject {
16651
16748
  };
16652
16749
  this.epochYear = parseInt(YYYY.slice(2, 4));
16653
16750
  this.epochDay = dayOfYear;
16654
- this.meanMoDev1 = parseFloat(omm.MEAN_MOTION_DOT);
16655
- this.meanMoDev2 = parseFloat(omm.MEAN_MOTION_DDOT);
16656
- this.bstar = parseFloat(omm.BSTAR);
16657
- this.inclination = parseFloat(omm.INCLINATION);
16658
- this.rightAscension = parseFloat(omm.RA_OF_ASC_NODE);
16659
- this.eccentricity = parseFloat(omm.ECCENTRICITY);
16660
- this.argOfPerigee = parseFloat(omm.ARG_OF_PERICENTER);
16661
- this.meanAnomaly = parseFloat(omm.MEAN_ANOMALY);
16662
- this.meanMotion = parseFloat(omm.MEAN_MOTION);
16751
+ this.meanMoDev1 = Number(omm.MEAN_MOTION_DOT);
16752
+ this.meanMoDev2 = Number(omm.MEAN_MOTION_DDOT);
16753
+ this.bstar = Number(omm.BSTAR);
16754
+ this.inclination = Number(omm.INCLINATION);
16755
+ this.rightAscension = Number(omm.RA_OF_ASC_NODE);
16756
+ this.eccentricity = Number(omm.ECCENTRICITY);
16757
+ this.argOfPerigee = Number(omm.ARG_OF_PERICENTER);
16758
+ this.meanAnomaly = Number(omm.MEAN_ANOMALY);
16759
+ this.meanMotion = Number(omm.MEAN_MOTION);
16663
16760
  this.period = 1440 / this.meanMotion;
16664
16761
  this.semiMajorAxis = (8681663653e-3 / this.meanMotion) ** (2 / 3);
16665
16762
  this.semiMinorAxis = this.semiMajorAxis * Math.sqrt(1 - this.eccentricity ** 2);
16666
16763
  this.apogee = this.semiMajorAxis * (1 + this.eccentricity) - 6371;
16667
16764
  this.perigee = this.semiMajorAxis * (1 - this.eccentricity) - 6371;
16765
+ const { tle1, tle2 } = FormatTle.createTle({
16766
+ inc: this.inclination,
16767
+ meanmo: this.meanMotion,
16768
+ rasc: this.rightAscension,
16769
+ argPe: this.argOfPerigee,
16770
+ meana: this.meanAnomaly,
16771
+ ecen: this.eccentricity,
16772
+ epochyr: this.epochYear,
16773
+ epochday: this.epochDay,
16774
+ intl: omm.OBJECT_ID,
16775
+ scc: this.sccNum,
16776
+ bstar: this.bstar,
16777
+ meanMotionDot: this.meanMoDev1,
16778
+ meanMotionDdot: this.meanMoDev2,
16779
+ classification: String(omm.CLASSIFICATION_TYPE) || "U",
16780
+ revAtEpoch: Number(omm.REV_AT_EPOCH) || 0,
16781
+ elementSetNo: Number(omm.ELEMENT_SET_NO) || 999,
16782
+ ephemerisType: Number(omm.EPHEMERIS_TYPE) || 0
16783
+ });
16784
+ this.tle1 = tle1;
16785
+ this.tle2 = tle2;
16668
16786
  this.satrec = Sgp4.createSatrecFromOmm(ommParsed);
16669
16787
  }
16670
16788
  // ==================== Type Checking ====================
@@ -25180,6 +25298,61 @@ var OmmParser = class _OmmParser {
25180
25298
  const theory = omm.metadata.MEAN_ELEMENT_THEORY.toUpperCase();
25181
25299
  return theory === "SGP" || theory === "SGP4" || theory === "SGP4-XP";
25182
25300
  }
25301
+ /**
25302
+ * Parse a CelesTrak-style flat JSON OMM object into the structured ParsedOmm format.
25303
+ *
25304
+ * CelesTrak's JSON OMM format is a flat object with all fields at the top level,
25305
+ * omitting metadata fields like CENTER_NAME, REF_FRAME, TIME_SYSTEM, and
25306
+ * MEAN_ELEMENT_THEORY (which are always EARTH/TEME/UTC/SGP4 for GP data).
25307
+ *
25308
+ * @param omm - A flat OMM JSON object from CelesTrak
25309
+ * @returns Parsed OMM structure with inferred metadata
25310
+ */
25311
+ static parseJson(omm) {
25312
+ const result = {
25313
+ header: {
25314
+ CCSDS_OMM_VERS: "3.0",
25315
+ CREATION_DATE: (/* @__PURE__ */ new Date()).toISOString(),
25316
+ ORIGINATOR: "CelesTrak"
25317
+ },
25318
+ metadata: {
25319
+ OBJECT_NAME: omm.OBJECT_NAME,
25320
+ OBJECT_ID: omm.OBJECT_ID,
25321
+ CENTER_NAME: "EARTH",
25322
+ REF_FRAME: "TEME",
25323
+ TIME_SYSTEM: "UTC",
25324
+ MEAN_ELEMENT_THEORY: "SGP4"
25325
+ },
25326
+ meanElements: {
25327
+ EPOCH: omm.EPOCH,
25328
+ MEAN_MOTION: Number(omm.MEAN_MOTION),
25329
+ ECCENTRICITY: Number(omm.ECCENTRICITY),
25330
+ INCLINATION: Number(omm.INCLINATION),
25331
+ RA_OF_ASC_NODE: Number(omm.RA_OF_ASC_NODE),
25332
+ ARG_OF_PERICENTER: Number(omm.ARG_OF_PERICENTER),
25333
+ MEAN_ANOMALY: Number(omm.MEAN_ANOMALY)
25334
+ },
25335
+ tleParameters: {
25336
+ EPHEMERIS_TYPE: Number(omm.EPHEMERIS_TYPE),
25337
+ CLASSIFICATION_TYPE: String(omm.CLASSIFICATION_TYPE),
25338
+ NORAD_CAT_ID: Number(omm.NORAD_CAT_ID),
25339
+ ELEMENT_SET_NO: Number(omm.ELEMENT_SET_NO),
25340
+ REV_AT_EPOCH: Number(omm.REV_AT_EPOCH),
25341
+ BSTAR: Number(omm.BSTAR),
25342
+ MEAN_MOTION_DOT: Number(omm.MEAN_MOTION_DOT),
25343
+ MEAN_MOTION_DDOT: Number(omm.MEAN_MOTION_DDOT)
25344
+ }
25345
+ };
25346
+ return result;
25347
+ }
25348
+ /**
25349
+ * Parse an array of CelesTrak-style flat JSON OMM objects.
25350
+ * @param ommArray - Array of flat OMM JSON objects from CelesTrak
25351
+ * @returns Array of parsed OMM structures
25352
+ */
25353
+ static parseJsonArray(ommArray) {
25354
+ return ommArray.map((omm) => _OmmParser.parseJson(omm));
25355
+ }
25183
25356
  /**
25184
25357
  * Parse all key-value pairs from OMM lines (excluding COMMENT lines).
25185
25358
  */
package/dist/main.d.cts CHANGED
@@ -1816,6 +1816,27 @@ declare abstract class FormatTle {
1816
1816
  * @returns The modified string with the character set at the specified index.
1817
1817
  */
1818
1818
  static setCharAt(str: string, index: number, chr: string): string;
1819
+ /**
1820
+ * Format mean motion dot (first derivative / 2) for TLE line 1.
1821
+ * Format: sign + ".NNNNNNNN" = 10 chars total.
1822
+ * @param value - Mean motion dot value (rev/day^2)
1823
+ * @returns Formatted 10-character string
1824
+ */
1825
+ static formatMeanMotionDot(value: number): string;
1826
+ /**
1827
+ * Format a value in TLE exponential notation for BSTAR or mean motion ddot.
1828
+ * Format: "sNNNNN±N" (8 chars) where mantissa has implied leading decimal point.
1829
+ * Example: 0.00017507 → " 17507-3" (i.e., .17507 × 10^-3)
1830
+ * @param value - The value to format
1831
+ * @returns Formatted 8-character string
1832
+ */
1833
+ static formatTleExponential(value: number): string;
1834
+ /**
1835
+ * Compute TLE line checksum (modulo 10 sum of digits, '-' counts as 1).
1836
+ * @param line - TLE line (first 68 characters are summed)
1837
+ * @returns Checksum digit (0-9)
1838
+ */
1839
+ static tleChecksum(line: string): number;
1819
1840
  }
1820
1841
 
1821
1842
  /**
@@ -2780,37 +2801,34 @@ declare class RIC extends RelativeState {
2780
2801
  }
2781
2802
 
2782
2803
  /**
2783
- * Represents the data format for orbital elements as provided by the Omm system.
2784
- * Each property is expressed as a string to accommodate various formatting or precision
2785
- * requirements from the data source.
2804
+ * Represents the data format for orbital elements as provided by the OMM system.
2805
+ * Numeric fields accept both string and number to support CelesTrak's JSON format
2806
+ * (which sends numbers as numbers) and other sources that send them as strings.
2786
2807
  */
2787
2808
  interface OmmDataFormat {
2788
2809
  OBJECT_NAME: string;
2789
2810
  OBJECT_ID: string;
2790
2811
  /** Date in YYYY-MM-DDTHH:MM:SS.SSSSSS UTC format */
2791
2812
  EPOCH: string;
2792
- MEAN_MOTION: string;
2793
- ECCENTRICITY: string;
2794
- INCLINATION: string;
2795
- RA_OF_ASC_NODE: string;
2796
- ARG_OF_PERICENTER: string;
2797
- MEAN_ANOMALY: string;
2798
- EPHEMERIS_TYPE: string;
2813
+ MEAN_MOTION: string | number;
2814
+ ECCENTRICITY: string | number;
2815
+ INCLINATION: string | number;
2816
+ RA_OF_ASC_NODE: string | number;
2817
+ ARG_OF_PERICENTER: string | number;
2818
+ MEAN_ANOMALY: string | number;
2819
+ EPHEMERIS_TYPE: string | number;
2799
2820
  CLASSIFICATION_TYPE: string;
2800
- NORAD_CAT_ID: string;
2801
- ELEMENT_SET_NO: string;
2802
- REV_AT_EPOCH: string;
2803
- BSTAR: string;
2804
- MEAN_MOTION_DOT: string;
2805
- MEAN_MOTION_DDOT: string;
2821
+ NORAD_CAT_ID: string | number;
2822
+ ELEMENT_SET_NO: string | number;
2823
+ REV_AT_EPOCH: string | number;
2824
+ BSTAR: string | number;
2825
+ MEAN_MOTION_DOT: string | number;
2826
+ MEAN_MOTION_DDOT: string | number;
2806
2827
  }
2807
2828
  /**
2808
- * Represents the parsed data format for orbital elements as provided by the Omm system.
2809
- * Each property is expressed as a string to accommodate various formatting or precision
2810
- * requirements from the data source.
2811
- * The `epoch` property is an object that contains the parsed date and time values.
2812
- * The `doy` property is the day of the year.
2813
- * The `epoch` property is an object that contains the parsed date and time values.
2829
+ * Represents the parsed data format for orbital elements as provided by the OMM system.
2830
+ * String fields are preserved from the original data; the `epoch` property contains
2831
+ * the parsed date/time values used for SGP4 initialization.
2814
2832
  */
2815
2833
  interface OmmParsedDataFormat {
2816
2834
  OBJECT_NAME: string;
@@ -5919,6 +5937,20 @@ type TleParams = {
5919
5937
  intl: string;
5920
5938
  /** alpha 5 satellite number */
5921
5939
  scc: string;
5940
+ /** B* drag term (1/Earth radii). Used when `sat` is not provided. */
5941
+ bstar?: number;
5942
+ /** First derivative of mean motion / 2 (rev/day^2). Used when `sat` is not provided. */
5943
+ meanMotionDot?: number;
5944
+ /** Second derivative of mean motion / 6 (rev/day^3). Used when `sat` is not provided. */
5945
+ meanMotionDdot?: number;
5946
+ /** Classification type (default 'U'). Used when `sat` is not provided. */
5947
+ classification?: string;
5948
+ /** Revolution number at epoch. Used when `sat` is not provided. */
5949
+ revAtEpoch?: number;
5950
+ /** Element set number (default 999). Used when `sat` is not provided. */
5951
+ elementSetNo?: number;
5952
+ /** Ephemeris type (default 0). Used when `sat` is not provided. */
5953
+ ephemerisType?: number;
5922
5954
  };
5923
5955
  type PositionVelocity = {
5924
5956
  position: Vector3D<Kilometers>;
@@ -14078,6 +14110,23 @@ declare class OmmParser {
14078
14110
  * @returns true if the OMM uses SGP, SGP4, or SGP4-XP mean element theory
14079
14111
  */
14080
14112
  static isTleCompatible(omm: ParsedOmm): boolean;
14113
+ /**
14114
+ * Parse a CelesTrak-style flat JSON OMM object into the structured ParsedOmm format.
14115
+ *
14116
+ * CelesTrak's JSON OMM format is a flat object with all fields at the top level,
14117
+ * omitting metadata fields like CENTER_NAME, REF_FRAME, TIME_SYSTEM, and
14118
+ * MEAN_ELEMENT_THEORY (which are always EARTH/TEME/UTC/SGP4 for GP data).
14119
+ *
14120
+ * @param omm - A flat OMM JSON object from CelesTrak
14121
+ * @returns Parsed OMM structure with inferred metadata
14122
+ */
14123
+ static parseJson(omm: OmmDataFormat): ParsedOmm;
14124
+ /**
14125
+ * Parse an array of CelesTrak-style flat JSON OMM objects.
14126
+ * @param ommArray - Array of flat OMM JSON objects from CelesTrak
14127
+ * @returns Array of parsed OMM structures
14128
+ */
14129
+ static parseJsonArray(ommArray: OmmDataFormat[]): ParsedOmm[];
14081
14130
  /**
14082
14131
  * Parse all key-value pairs from OMM lines (excluding COMMENT lines).
14083
14132
  */
package/dist/main.d.ts CHANGED
@@ -1816,6 +1816,27 @@ declare abstract class FormatTle {
1816
1816
  * @returns The modified string with the character set at the specified index.
1817
1817
  */
1818
1818
  static setCharAt(str: string, index: number, chr: string): string;
1819
+ /**
1820
+ * Format mean motion dot (first derivative / 2) for TLE line 1.
1821
+ * Format: sign + ".NNNNNNNN" = 10 chars total.
1822
+ * @param value - Mean motion dot value (rev/day^2)
1823
+ * @returns Formatted 10-character string
1824
+ */
1825
+ static formatMeanMotionDot(value: number): string;
1826
+ /**
1827
+ * Format a value in TLE exponential notation for BSTAR or mean motion ddot.
1828
+ * Format: "sNNNNN±N" (8 chars) where mantissa has implied leading decimal point.
1829
+ * Example: 0.00017507 → " 17507-3" (i.e., .17507 × 10^-3)
1830
+ * @param value - The value to format
1831
+ * @returns Formatted 8-character string
1832
+ */
1833
+ static formatTleExponential(value: number): string;
1834
+ /**
1835
+ * Compute TLE line checksum (modulo 10 sum of digits, '-' counts as 1).
1836
+ * @param line - TLE line (first 68 characters are summed)
1837
+ * @returns Checksum digit (0-9)
1838
+ */
1839
+ static tleChecksum(line: string): number;
1819
1840
  }
1820
1841
 
1821
1842
  /**
@@ -2780,37 +2801,34 @@ declare class RIC extends RelativeState {
2780
2801
  }
2781
2802
 
2782
2803
  /**
2783
- * Represents the data format for orbital elements as provided by the Omm system.
2784
- * Each property is expressed as a string to accommodate various formatting or precision
2785
- * requirements from the data source.
2804
+ * Represents the data format for orbital elements as provided by the OMM system.
2805
+ * Numeric fields accept both string and number to support CelesTrak's JSON format
2806
+ * (which sends numbers as numbers) and other sources that send them as strings.
2786
2807
  */
2787
2808
  interface OmmDataFormat {
2788
2809
  OBJECT_NAME: string;
2789
2810
  OBJECT_ID: string;
2790
2811
  /** Date in YYYY-MM-DDTHH:MM:SS.SSSSSS UTC format */
2791
2812
  EPOCH: string;
2792
- MEAN_MOTION: string;
2793
- ECCENTRICITY: string;
2794
- INCLINATION: string;
2795
- RA_OF_ASC_NODE: string;
2796
- ARG_OF_PERICENTER: string;
2797
- MEAN_ANOMALY: string;
2798
- EPHEMERIS_TYPE: string;
2813
+ MEAN_MOTION: string | number;
2814
+ ECCENTRICITY: string | number;
2815
+ INCLINATION: string | number;
2816
+ RA_OF_ASC_NODE: string | number;
2817
+ ARG_OF_PERICENTER: string | number;
2818
+ MEAN_ANOMALY: string | number;
2819
+ EPHEMERIS_TYPE: string | number;
2799
2820
  CLASSIFICATION_TYPE: string;
2800
- NORAD_CAT_ID: string;
2801
- ELEMENT_SET_NO: string;
2802
- REV_AT_EPOCH: string;
2803
- BSTAR: string;
2804
- MEAN_MOTION_DOT: string;
2805
- MEAN_MOTION_DDOT: string;
2821
+ NORAD_CAT_ID: string | number;
2822
+ ELEMENT_SET_NO: string | number;
2823
+ REV_AT_EPOCH: string | number;
2824
+ BSTAR: string | number;
2825
+ MEAN_MOTION_DOT: string | number;
2826
+ MEAN_MOTION_DDOT: string | number;
2806
2827
  }
2807
2828
  /**
2808
- * Represents the parsed data format for orbital elements as provided by the Omm system.
2809
- * Each property is expressed as a string to accommodate various formatting or precision
2810
- * requirements from the data source.
2811
- * The `epoch` property is an object that contains the parsed date and time values.
2812
- * The `doy` property is the day of the year.
2813
- * The `epoch` property is an object that contains the parsed date and time values.
2829
+ * Represents the parsed data format for orbital elements as provided by the OMM system.
2830
+ * String fields are preserved from the original data; the `epoch` property contains
2831
+ * the parsed date/time values used for SGP4 initialization.
2814
2832
  */
2815
2833
  interface OmmParsedDataFormat {
2816
2834
  OBJECT_NAME: string;
@@ -5919,6 +5937,20 @@ type TleParams = {
5919
5937
  intl: string;
5920
5938
  /** alpha 5 satellite number */
5921
5939
  scc: string;
5940
+ /** B* drag term (1/Earth radii). Used when `sat` is not provided. */
5941
+ bstar?: number;
5942
+ /** First derivative of mean motion / 2 (rev/day^2). Used when `sat` is not provided. */
5943
+ meanMotionDot?: number;
5944
+ /** Second derivative of mean motion / 6 (rev/day^3). Used when `sat` is not provided. */
5945
+ meanMotionDdot?: number;
5946
+ /** Classification type (default 'U'). Used when `sat` is not provided. */
5947
+ classification?: string;
5948
+ /** Revolution number at epoch. Used when `sat` is not provided. */
5949
+ revAtEpoch?: number;
5950
+ /** Element set number (default 999). Used when `sat` is not provided. */
5951
+ elementSetNo?: number;
5952
+ /** Ephemeris type (default 0). Used when `sat` is not provided. */
5953
+ ephemerisType?: number;
5922
5954
  };
5923
5955
  type PositionVelocity = {
5924
5956
  position: Vector3D<Kilometers>;
@@ -14078,6 +14110,23 @@ declare class OmmParser {
14078
14110
  * @returns true if the OMM uses SGP, SGP4, or SGP4-XP mean element theory
14079
14111
  */
14080
14112
  static isTleCompatible(omm: ParsedOmm): boolean;
14113
+ /**
14114
+ * Parse a CelesTrak-style flat JSON OMM object into the structured ParsedOmm format.
14115
+ *
14116
+ * CelesTrak's JSON OMM format is a flat object with all fields at the top level,
14117
+ * omitting metadata fields like CENTER_NAME, REF_FRAME, TIME_SYSTEM, and
14118
+ * MEAN_ELEMENT_THEORY (which are always EARTH/TEME/UTC/SGP4 for GP data).
14119
+ *
14120
+ * @param omm - A flat OMM JSON object from CelesTrak
14121
+ * @returns Parsed OMM structure with inferred metadata
14122
+ */
14123
+ static parseJson(omm: OmmDataFormat): ParsedOmm;
14124
+ /**
14125
+ * Parse an array of CelesTrak-style flat JSON OMM objects.
14126
+ * @param ommArray - Array of flat OMM JSON objects from CelesTrak
14127
+ * @returns Array of parsed OMM structures
14128
+ */
14129
+ static parseJsonArray(ommArray: OmmDataFormat[]): ParsedOmm[];
14081
14130
  /**
14082
14131
  * Parse all key-value pairs from OMM lines (excluding COMMENT lines).
14083
14132
  */
package/dist/main.js CHANGED
@@ -9847,7 +9847,7 @@ var FormatTle = class _FormatTle {
9847
9847
  const { inc, meanmo, rasc, argPe, meana, ecen, epochyr, epochday, intl } = tleParams;
9848
9848
  const scc = Tle.convert6DigitToA5(tleParams.scc);
9849
9849
  const epochYrStr = String(epochyr).padStart(2, "0");
9850
- const epochdayStr = parseFloat(String(epochday)).toFixed(8).padStart(12, "0");
9850
+ const epochdayStr = Number(epochday).toFixed(8).padStart(12, "0");
9851
9851
  const incStr = _FormatTle.inclination(inc);
9852
9852
  const meanmoStr = _FormatTle.meanMotion(meanmo);
9853
9853
  const rascStr = _FormatTle.rightAscension(rasc);
@@ -9855,13 +9855,23 @@ var FormatTle = class _FormatTle {
9855
9855
  const meanaStr = _FormatTle.meanAnomaly(meana);
9856
9856
  const ecenStr = _FormatTle.eccentricity(ecen);
9857
9857
  const intlStr = intl.padEnd(8, " ");
9858
- let TLE1Ending = tleParams.sat ? tleParams.sat.tle1.substring(32, 71) : " +.00000000 +00000+0 +00000-0 0 9990";
9859
- TLE1Ending = TLE1Ending[1] === " " ? _FormatTle.setCharAt(TLE1Ending, 1, "+") : TLE1Ending;
9860
- TLE1Ending = TLE1Ending[12] === " " ? _FormatTle.setCharAt(TLE1Ending, 12, "+") : TLE1Ending;
9861
- TLE1Ending = TLE1Ending[21] === " " ? _FormatTle.setCharAt(TLE1Ending, 21, "+") : TLE1Ending;
9862
- TLE1Ending = TLE1Ending[32] === " " ? _FormatTle.setCharAt(TLE1Ending, 32, "0") : TLE1Ending;
9863
- const tle1 = `1 ${scc}U ${intlStr} ${epochYrStr}${epochdayStr}${TLE1Ending}`;
9864
- const tle2 = `2 ${scc} ${incStr} ${rascStr} ${ecenStr} ${argPeStr} ${meanaStr} ${meanmoStr} 00010`;
9858
+ const classification = tleParams.classification ?? "U";
9859
+ const elementSetNo = (tleParams.elementSetNo ?? 999).toString().padStart(4, " ");
9860
+ const ephemerisType = (tleParams.ephemerisType ?? 0).toString();
9861
+ const revAtEpoch = (tleParams.revAtEpoch ?? 0).toString().padStart(5, " ");
9862
+ let TLE1Ending;
9863
+ if (tleParams.sat) {
9864
+ TLE1Ending = tleParams.sat.tle1.substring(32, 71);
9865
+ } else {
9866
+ const mmDot = _FormatTle.formatMeanMotionDot(tleParams.meanMotionDot ?? 0);
9867
+ const mmDdot = _FormatTle.formatTleExponential(tleParams.meanMotionDdot ?? 0);
9868
+ const bstarStr = _FormatTle.formatTleExponential(tleParams.bstar ?? 0);
9869
+ TLE1Ending = ` ${mmDot} ${mmDdot} ${bstarStr} ${ephemerisType} ${elementSetNo}0`;
9870
+ }
9871
+ const tle1Pre = `1 ${scc}${classification} ${intlStr} ${epochYrStr}${epochdayStr}${TLE1Ending}`;
9872
+ const tle1 = _FormatTle.setCharAt(tle1Pre, 68, _FormatTle.tleChecksum(tle1Pre).toString());
9873
+ const tle2Pre = `2 ${scc} ${incStr} ${rascStr} ${ecenStr} ${argPeStr} ${meanaStr} ${meanmoStr}${revAtEpoch}0`;
9874
+ const tle2 = _FormatTle.setCharAt(tle2Pre, 68, _FormatTle.tleChecksum(tle2Pre).toString());
9865
9875
  return { tle1, tle2 };
9866
9876
  }
9867
9877
  /**
@@ -9875,7 +9885,7 @@ var FormatTle = class _FormatTle {
9875
9885
  argPe = argPe.toString();
9876
9886
  }
9877
9887
  const argPeNum = parseFloat(argPe).toFixed(4);
9878
- const argPe0 = argPeNum.padStart(8, "0");
9888
+ const argPe0 = argPeNum.padStart(8, " ");
9879
9889
  if (argPe0.length !== 8) {
9880
9890
  throw new ValidationError("Argument of perigee must be 8 characters", "argPe", argPe0);
9881
9891
  }
@@ -9889,7 +9899,10 @@ var FormatTle = class _FormatTle {
9889
9899
  */
9890
9900
  static eccentricity(ecen) {
9891
9901
  if (typeof ecen === "number") {
9892
- ecen = ecen.toFixed(7);
9902
+ const ecenStr = ecen.toString();
9903
+ const dotIdx = ecenStr.indexOf(".");
9904
+ const afterDot = dotIdx >= 0 ? ecenStr.substring(dotIdx + 1) : "";
9905
+ ecen = `0.${afterDot.substring(0, 7).padEnd(7, "0")}`;
9893
9906
  }
9894
9907
  let ecen0 = ecen.padEnd(9, "0");
9895
9908
  if (ecen0[1] === ".") {
@@ -9913,7 +9926,7 @@ var FormatTle = class _FormatTle {
9913
9926
  inc = inc.toString();
9914
9927
  }
9915
9928
  const incNum = parseFloat(inc).toFixed(4);
9916
- const inc0 = incNum.padStart(8, "0");
9929
+ const inc0 = incNum.padStart(8, " ");
9917
9930
  if (inc0.length !== 8) {
9918
9931
  throw new ValidationError("Inclination must be 8 characters", "inclination", inc0);
9919
9932
  }
@@ -9930,7 +9943,7 @@ var FormatTle = class _FormatTle {
9930
9943
  meana = meana.toString();
9931
9944
  }
9932
9945
  const meanaNum = parseFloat(meana).toFixed(4);
9933
- const meana0 = meanaNum.padStart(8, "0");
9946
+ const meana0 = meanaNum.padStart(8, " ");
9934
9947
  if (meana0.length !== 8) {
9935
9948
  throw new ValidationError("Mean anomaly must be 8 characters", "meanAnomaly", meana0);
9936
9949
  }
@@ -9971,7 +9984,7 @@ var FormatTle = class _FormatTle {
9971
9984
  rasc = rasc.toString();
9972
9985
  }
9973
9986
  const rascNum = parseFloat(rasc).toFixed(4);
9974
- const rasc0 = rascNum.padStart(8, "0");
9987
+ const rasc0 = rascNum.padStart(8, " ");
9975
9988
  if (rasc0.length !== 8) {
9976
9989
  throw new ValidationError("Right ascension must be 8 characters", "rightAscension", rasc0);
9977
9990
  }
@@ -9990,6 +10003,61 @@ var FormatTle = class _FormatTle {
9990
10003
  }
9991
10004
  return `${str.substring(0, index)}${chr}${str.substring(index + 1)}`;
9992
10005
  }
10006
+ /**
10007
+ * Format mean motion dot (first derivative / 2) for TLE line 1.
10008
+ * Format: sign + ".NNNNNNNN" = 10 chars total.
10009
+ * @param value - Mean motion dot value (rev/day^2)
10010
+ * @returns Formatted 10-character string
10011
+ */
10012
+ static formatMeanMotionDot(value) {
10013
+ const sign2 = value >= 0 ? " " : "-";
10014
+ return sign2 + Math.abs(value).toFixed(8).substring(1);
10015
+ }
10016
+ /**
10017
+ * Format a value in TLE exponential notation for BSTAR or mean motion ddot.
10018
+ * Format: "sNNNNN±N" (8 chars) where mantissa has implied leading decimal point.
10019
+ * Example: 0.00017507 → " 17507-3" (i.e., .17507 × 10^-3)
10020
+ * @param value - The value to format
10021
+ * @returns Formatted 8-character string
10022
+ */
10023
+ static formatTleExponential(value) {
10024
+ if (value === 0) {
10025
+ return " 00000+0";
10026
+ }
10027
+ const sign2 = value >= 0 ? " " : "-";
10028
+ const absVal = Math.abs(value);
10029
+ let exponent = Math.floor(Math.log10(absVal)) + 1;
10030
+ let mantissa = absVal / Math.pow(10, exponent);
10031
+ if (mantissa >= 1) {
10032
+ mantissa /= 10;
10033
+ exponent++;
10034
+ }
10035
+ if (mantissa < 0.1 && mantissa > 0) {
10036
+ mantissa *= 10;
10037
+ exponent--;
10038
+ }
10039
+ const mantissaStr = Math.round(mantissa * 1e5).toString().padStart(5, "0");
10040
+ const expSign = exponent >= 0 ? "+" : "-";
10041
+ const expStr = Math.abs(exponent).toString();
10042
+ return sign2 + mantissaStr + expSign + expStr;
10043
+ }
10044
+ /**
10045
+ * Compute TLE line checksum (modulo 10 sum of digits, '-' counts as 1).
10046
+ * @param line - TLE line (first 68 characters are summed)
10047
+ * @returns Checksum digit (0-9)
10048
+ */
10049
+ static tleChecksum(line) {
10050
+ let sum = 0;
10051
+ for (let i = 0; i < 68 && i < line.length; i++) {
10052
+ const c = line[i];
10053
+ if (c >= "0" && c <= "9") {
10054
+ sum += parseInt(c);
10055
+ } else if (c === "-") {
10056
+ sum += 1;
10057
+ }
10058
+ }
10059
+ return sum % 10;
10060
+ }
9993
10061
  };
9994
10062
 
9995
10063
  // src/coordinate/RelativeState.ts
@@ -13185,13 +13253,28 @@ var ClassicalElements = class _ClassicalElements {
13185
13253
  const h = pos.cross(vel);
13186
13254
  const i = Math.acos(clamp(h.z / h.magnitude(), -1, 1));
13187
13255
  const n = Vector3D.zAxis.cross(h);
13188
- let o = Math.acos(clamp(n.x / n.magnitude(), -1, 1));
13189
- if (n.y < 0) {
13190
- o = TAU - o;
13191
- }
13192
- let w = n.angle(eVec);
13193
- if (eVec.z < 0) {
13194
- w = TAU - w;
13256
+ const nMag = n.magnitude();
13257
+ let o;
13258
+ let w;
13259
+ if (nMag < 1e-12) {
13260
+ o = 0;
13261
+ if (e > 1e-12) {
13262
+ w = Math.atan2(eVec.y, eVec.x);
13263
+ if (w < 0) {
13264
+ w = w + TAU;
13265
+ }
13266
+ } else {
13267
+ w = 0;
13268
+ }
13269
+ } else {
13270
+ o = Math.acos(clamp(n.x / nMag, -1, 1));
13271
+ if (n.y < 0) {
13272
+ o = TAU - o;
13273
+ }
13274
+ w = n.angle(eVec);
13275
+ if (eVec.z < 0) {
13276
+ w = TAU - w;
13277
+ }
13195
13278
  }
13196
13279
  let v = eVec.angle(pos);
13197
13280
  if (pos.dot(vel) < 0) {
@@ -16623,8 +16706,9 @@ var Satellite = class _Satellite extends SpaceObject {
16623
16706
  this.satrec = Sgp4.createSatrec(tle1, tle2);
16624
16707
  }
16625
16708
  parseOmmAndUpdateOrbit_(omm) {
16626
- this.sccNum = omm.NORAD_CAT_ID.padStart(5, "0");
16627
- this.sccNum5 = Tle.convert6DigitToA5(omm.NORAD_CAT_ID);
16709
+ const noradStr = String(omm.NORAD_CAT_ID);
16710
+ this.sccNum = noradStr.padStart(5, "0");
16711
+ this.sccNum5 = Tle.convert6DigitToA5(noradStr);
16628
16712
  this.sccNum6 = Tle.convertA5to6Digit(this.sccNum5);
16629
16713
  this.intlDes = omm.OBJECT_ID;
16630
16714
  const YYYY = omm.EPOCH.slice(0, 4);
@@ -16637,6 +16721,19 @@ var Satellite = class _Satellite extends SpaceObject {
16637
16721
  const dayOfYear = (epochDateObj - Date.UTC(Number(YYYY), 0, 0)) / 864e5;
16638
16722
  const ommParsed = {
16639
16723
  ...omm,
16724
+ NORAD_CAT_ID: noradStr,
16725
+ MEAN_MOTION: String(omm.MEAN_MOTION),
16726
+ ECCENTRICITY: String(omm.ECCENTRICITY),
16727
+ INCLINATION: String(omm.INCLINATION),
16728
+ RA_OF_ASC_NODE: String(omm.RA_OF_ASC_NODE),
16729
+ ARG_OF_PERICENTER: String(omm.ARG_OF_PERICENTER),
16730
+ MEAN_ANOMALY: String(omm.MEAN_ANOMALY),
16731
+ EPHEMERIS_TYPE: String(omm.EPHEMERIS_TYPE),
16732
+ ELEMENT_SET_NO: String(omm.ELEMENT_SET_NO),
16733
+ REV_AT_EPOCH: String(omm.REV_AT_EPOCH),
16734
+ BSTAR: String(omm.BSTAR),
16735
+ MEAN_MOTION_DOT: String(omm.MEAN_MOTION_DOT),
16736
+ MEAN_MOTION_DDOT: String(omm.MEAN_MOTION_DDOT),
16640
16737
  epoch: {
16641
16738
  year: Number(YYYY),
16642
16739
  month: Number(MM),
@@ -16649,20 +16746,41 @@ var Satellite = class _Satellite extends SpaceObject {
16649
16746
  };
16650
16747
  this.epochYear = parseInt(YYYY.slice(2, 4));
16651
16748
  this.epochDay = dayOfYear;
16652
- this.meanMoDev1 = parseFloat(omm.MEAN_MOTION_DOT);
16653
- this.meanMoDev2 = parseFloat(omm.MEAN_MOTION_DDOT);
16654
- this.bstar = parseFloat(omm.BSTAR);
16655
- this.inclination = parseFloat(omm.INCLINATION);
16656
- this.rightAscension = parseFloat(omm.RA_OF_ASC_NODE);
16657
- this.eccentricity = parseFloat(omm.ECCENTRICITY);
16658
- this.argOfPerigee = parseFloat(omm.ARG_OF_PERICENTER);
16659
- this.meanAnomaly = parseFloat(omm.MEAN_ANOMALY);
16660
- this.meanMotion = parseFloat(omm.MEAN_MOTION);
16749
+ this.meanMoDev1 = Number(omm.MEAN_MOTION_DOT);
16750
+ this.meanMoDev2 = Number(omm.MEAN_MOTION_DDOT);
16751
+ this.bstar = Number(omm.BSTAR);
16752
+ this.inclination = Number(omm.INCLINATION);
16753
+ this.rightAscension = Number(omm.RA_OF_ASC_NODE);
16754
+ this.eccentricity = Number(omm.ECCENTRICITY);
16755
+ this.argOfPerigee = Number(omm.ARG_OF_PERICENTER);
16756
+ this.meanAnomaly = Number(omm.MEAN_ANOMALY);
16757
+ this.meanMotion = Number(omm.MEAN_MOTION);
16661
16758
  this.period = 1440 / this.meanMotion;
16662
16759
  this.semiMajorAxis = (8681663653e-3 / this.meanMotion) ** (2 / 3);
16663
16760
  this.semiMinorAxis = this.semiMajorAxis * Math.sqrt(1 - this.eccentricity ** 2);
16664
16761
  this.apogee = this.semiMajorAxis * (1 + this.eccentricity) - 6371;
16665
16762
  this.perigee = this.semiMajorAxis * (1 - this.eccentricity) - 6371;
16763
+ const { tle1, tle2 } = FormatTle.createTle({
16764
+ inc: this.inclination,
16765
+ meanmo: this.meanMotion,
16766
+ rasc: this.rightAscension,
16767
+ argPe: this.argOfPerigee,
16768
+ meana: this.meanAnomaly,
16769
+ ecen: this.eccentricity,
16770
+ epochyr: this.epochYear,
16771
+ epochday: this.epochDay,
16772
+ intl: omm.OBJECT_ID,
16773
+ scc: this.sccNum,
16774
+ bstar: this.bstar,
16775
+ meanMotionDot: this.meanMoDev1,
16776
+ meanMotionDdot: this.meanMoDev2,
16777
+ classification: String(omm.CLASSIFICATION_TYPE) || "U",
16778
+ revAtEpoch: Number(omm.REV_AT_EPOCH) || 0,
16779
+ elementSetNo: Number(omm.ELEMENT_SET_NO) || 999,
16780
+ ephemerisType: Number(omm.EPHEMERIS_TYPE) || 0
16781
+ });
16782
+ this.tle1 = tle1;
16783
+ this.tle2 = tle2;
16666
16784
  this.satrec = Sgp4.createSatrecFromOmm(ommParsed);
16667
16785
  }
16668
16786
  // ==================== Type Checking ====================
@@ -25178,6 +25296,61 @@ var OmmParser = class _OmmParser {
25178
25296
  const theory = omm.metadata.MEAN_ELEMENT_THEORY.toUpperCase();
25179
25297
  return theory === "SGP" || theory === "SGP4" || theory === "SGP4-XP";
25180
25298
  }
25299
+ /**
25300
+ * Parse a CelesTrak-style flat JSON OMM object into the structured ParsedOmm format.
25301
+ *
25302
+ * CelesTrak's JSON OMM format is a flat object with all fields at the top level,
25303
+ * omitting metadata fields like CENTER_NAME, REF_FRAME, TIME_SYSTEM, and
25304
+ * MEAN_ELEMENT_THEORY (which are always EARTH/TEME/UTC/SGP4 for GP data).
25305
+ *
25306
+ * @param omm - A flat OMM JSON object from CelesTrak
25307
+ * @returns Parsed OMM structure with inferred metadata
25308
+ */
25309
+ static parseJson(omm) {
25310
+ const result = {
25311
+ header: {
25312
+ CCSDS_OMM_VERS: "3.0",
25313
+ CREATION_DATE: (/* @__PURE__ */ new Date()).toISOString(),
25314
+ ORIGINATOR: "CelesTrak"
25315
+ },
25316
+ metadata: {
25317
+ OBJECT_NAME: omm.OBJECT_NAME,
25318
+ OBJECT_ID: omm.OBJECT_ID,
25319
+ CENTER_NAME: "EARTH",
25320
+ REF_FRAME: "TEME",
25321
+ TIME_SYSTEM: "UTC",
25322
+ MEAN_ELEMENT_THEORY: "SGP4"
25323
+ },
25324
+ meanElements: {
25325
+ EPOCH: omm.EPOCH,
25326
+ MEAN_MOTION: Number(omm.MEAN_MOTION),
25327
+ ECCENTRICITY: Number(omm.ECCENTRICITY),
25328
+ INCLINATION: Number(omm.INCLINATION),
25329
+ RA_OF_ASC_NODE: Number(omm.RA_OF_ASC_NODE),
25330
+ ARG_OF_PERICENTER: Number(omm.ARG_OF_PERICENTER),
25331
+ MEAN_ANOMALY: Number(omm.MEAN_ANOMALY)
25332
+ },
25333
+ tleParameters: {
25334
+ EPHEMERIS_TYPE: Number(omm.EPHEMERIS_TYPE),
25335
+ CLASSIFICATION_TYPE: String(omm.CLASSIFICATION_TYPE),
25336
+ NORAD_CAT_ID: Number(omm.NORAD_CAT_ID),
25337
+ ELEMENT_SET_NO: Number(omm.ELEMENT_SET_NO),
25338
+ REV_AT_EPOCH: Number(omm.REV_AT_EPOCH),
25339
+ BSTAR: Number(omm.BSTAR),
25340
+ MEAN_MOTION_DOT: Number(omm.MEAN_MOTION_DOT),
25341
+ MEAN_MOTION_DDOT: Number(omm.MEAN_MOTION_DDOT)
25342
+ }
25343
+ };
25344
+ return result;
25345
+ }
25346
+ /**
25347
+ * Parse an array of CelesTrak-style flat JSON OMM objects.
25348
+ * @param ommArray - Array of flat OMM JSON objects from CelesTrak
25349
+ * @returns Array of parsed OMM structures
25350
+ */
25351
+ static parseJsonArray(ommArray) {
25352
+ return ommArray.map((omm) => _OmmParser.parseJson(omm));
25353
+ }
25181
25354
  /**
25182
25355
  * Parse all key-value pairs from OMM lines (excluding COMMENT lines).
25183
25356
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ootk",
3
- "version": "6.0.1",
3
+ "version": "6.0.3",
4
4
  "type": "module",
5
5
  "description": "Orbital Object Toolkit including Multiple Propagators, Initial Orbit Determination, and Maneuver Calculations.",
6
6
  "exports": {