gnss-js 2.8.2 → 2.10.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.
@@ -0,0 +1,108 @@
1
+ import { b as Rtcm3Frame } from './ephemeris-BAnBLLB9.js';
2
+
3
+ /**
4
+ * Full RTCM3 MSM decoder — extracts pseudorange, carrier phase,
5
+ * Doppler, and C/N0 for every satellite × signal cell.
6
+ *
7
+ * Reference: BNC 2.13.4 RTCM3Decoder.cpp (BKG), RTKLIB rtcm3.c and
8
+ * RTCM 10403.3 §3.5.
9
+ *
10
+ * MSM4–7 carry full observations. MSM1–3 (compact: MSM1 code, MSM2 phase,
11
+ * MSM3 both; no C/N0, no Doppler) carry each satellite's rough range only
12
+ * modulo 1 ms (DF398, no DF397 integer milliseconds): their epochs come back
13
+ * with `rangeModulo1ms` set, ranges complete except for a whole number of
14
+ * light-milliseconds per satellite — see `resolveMsmRanges` (positioning).
15
+ */
16
+
17
+ /** Single signal observation for one satellite */
18
+ interface ObsSignal {
19
+ /** RINEX 2-char obs code, e.g. "1C", "5X" */
20
+ rinexCode: string;
21
+ /** Pseudorange in meters (undefined if invalid) */
22
+ pseudorange?: number;
23
+ /** Carrier phase in cycles (undefined if invalid) */
24
+ phase?: number;
25
+ /** Doppler in Hz (undefined if invalid) */
26
+ doppler?: number;
27
+ /** Signal-to-noise ratio in dB-Hz */
28
+ cn0?: number;
29
+ /** Lock time in seconds */
30
+ lockTime?: number;
31
+ /** Half-cycle ambiguity indicator */
32
+ halfCycle?: boolean;
33
+ /** Wavelength used (meters) — needed for RINEX phase */
34
+ wavelength: number;
35
+ }
36
+ /** All observations for one satellite in one epoch */
37
+ interface ObsSatObs {
38
+ /** PRN string, e.g. "G01", "R15", "E04" */
39
+ prn: string;
40
+ /** System letter: G, R, E, C, J, I, S */
41
+ system: string;
42
+ /** Signal observations */
43
+ signals: ObsSignal[];
44
+ }
45
+ /**
46
+ * One decoded observation epoch. Originally the MSM4–7 output shape, now the
47
+ * neutral shape every RTCM3 obs source fills: MSM (`decodeMsmFull`), legacy
48
+ * 1001–1012 (`decodeLegacyObs`), via the `decodeObs` dispatcher.
49
+ */
50
+ interface ObsEpoch {
51
+ /** Message type (1074, 1077, etc.) */
52
+ messageType: number;
53
+ /** Epoch time as GPS milliseconds of week (or GLONASS day-of-week ms, BDS SOW) */
54
+ epochMs: number;
55
+ /** System letter */
56
+ system: string;
57
+ /** Observations per satellite */
58
+ observations: ObsSatObs[];
59
+ /**
60
+ * Set for MSM1–3: every pseudorange and phase is short by an unknown whole
61
+ * number of light-milliseconds (~299.79 km) per satellite. Resolve with
62
+ * `resolveMsmRanges` before using the ranges.
63
+ */
64
+ rangeModulo1ms?: true;
65
+ }
66
+ /** Options for the RTCM3 observation decoders. */
67
+ interface ObsDecodeOptions {
68
+ /**
69
+ * Also decode compact MSM1–3. Their epochs carry `rangeModulo1ms`: every
70
+ * range is short by whole light-milliseconds until `resolveMsmRanges`
71
+ * completes it, so a consumer must opt in (default false → null, as
72
+ * before).
73
+ */
74
+ compactMsm?: boolean;
75
+ }
76
+ /**
77
+ * Decode a single RTCM3 MSM frame into observations: MSM4–7, and MSM1–3
78
+ * with `opts.compactMsm` (flagged `rangeModulo1ms`). Null otherwise.
79
+ */
80
+ declare function decodeMsmFull(frame: Rtcm3Frame, opts?: ObsDecodeOptions): ObsEpoch | null;
81
+ /**
82
+ * Convert an observation epoch timestamp to a Date.
83
+ * For GPS/Galileo/BDS/QZSS: epochMs is ms of GPS week.
84
+ * For GLONASS: epochMs is day-of-week × 86400000 + ms of day (UTC+3).
85
+ *
86
+ * Requires an approximate reference time to resolve the current week.
87
+ */
88
+ /**
89
+ * NOTE: returns a GPS-scale Date — the clock-face convention used by
90
+ * the RINEX parser and every consumer of epoch milliseconds in this
91
+ * library (satellite propagation, SPP, RINEX epoch tags). It is NOT
92
+ * UTC: converting to UTC here put every downstream satellite position
93
+ * 18 s (the current leap-second count) off — a ±14 km per-satellite
94
+ * range error that scattered live SPP solutions by kilometres.
95
+ */
96
+ declare function obsEpochToDate(sys: string, epochMs: number, refTime?: Date): Date;
97
+ /**
98
+ * Set a GLONASS FDMA frequency number (k = -7..+13) for a slot (1-24),
99
+ * e.g. from a decoded 1020 ephemeris. MSM4/6 carry no DF419, so without
100
+ * this the wavelength — and hence carrier phase — stays unknown.
101
+ */
102
+ declare function setGloFreqNumber(slot: number, k: number): void;
103
+ /**
104
+ * Reset the GLONASS frequency number cache (e.g., when switching mountpoints).
105
+ */
106
+ declare function resetGloFreqCache(): void;
107
+
108
+ export { type ObsDecodeOptions as O, type ObsEpoch as a, type ObsSatObs as b, type ObsSignal as c, decodeMsmFull as d, obsEpochToDate as o, resetGloFreqCache as r, setGloFreqNumber as s };
@@ -0,0 +1,108 @@
1
+ import { b as Rtcm3Frame } from './ephemeris-BAnBLLB9.cjs';
2
+
3
+ /**
4
+ * Full RTCM3 MSM decoder — extracts pseudorange, carrier phase,
5
+ * Doppler, and C/N0 for every satellite × signal cell.
6
+ *
7
+ * Reference: BNC 2.13.4 RTCM3Decoder.cpp (BKG), RTKLIB rtcm3.c and
8
+ * RTCM 10403.3 §3.5.
9
+ *
10
+ * MSM4–7 carry full observations. MSM1–3 (compact: MSM1 code, MSM2 phase,
11
+ * MSM3 both; no C/N0, no Doppler) carry each satellite's rough range only
12
+ * modulo 1 ms (DF398, no DF397 integer milliseconds): their epochs come back
13
+ * with `rangeModulo1ms` set, ranges complete except for a whole number of
14
+ * light-milliseconds per satellite — see `resolveMsmRanges` (positioning).
15
+ */
16
+
17
+ /** Single signal observation for one satellite */
18
+ interface ObsSignal {
19
+ /** RINEX 2-char obs code, e.g. "1C", "5X" */
20
+ rinexCode: string;
21
+ /** Pseudorange in meters (undefined if invalid) */
22
+ pseudorange?: number;
23
+ /** Carrier phase in cycles (undefined if invalid) */
24
+ phase?: number;
25
+ /** Doppler in Hz (undefined if invalid) */
26
+ doppler?: number;
27
+ /** Signal-to-noise ratio in dB-Hz */
28
+ cn0?: number;
29
+ /** Lock time in seconds */
30
+ lockTime?: number;
31
+ /** Half-cycle ambiguity indicator */
32
+ halfCycle?: boolean;
33
+ /** Wavelength used (meters) — needed for RINEX phase */
34
+ wavelength: number;
35
+ }
36
+ /** All observations for one satellite in one epoch */
37
+ interface ObsSatObs {
38
+ /** PRN string, e.g. "G01", "R15", "E04" */
39
+ prn: string;
40
+ /** System letter: G, R, E, C, J, I, S */
41
+ system: string;
42
+ /** Signal observations */
43
+ signals: ObsSignal[];
44
+ }
45
+ /**
46
+ * One decoded observation epoch. Originally the MSM4–7 output shape, now the
47
+ * neutral shape every RTCM3 obs source fills: MSM (`decodeMsmFull`), legacy
48
+ * 1001–1012 (`decodeLegacyObs`), via the `decodeObs` dispatcher.
49
+ */
50
+ interface ObsEpoch {
51
+ /** Message type (1074, 1077, etc.) */
52
+ messageType: number;
53
+ /** Epoch time as GPS milliseconds of week (or GLONASS day-of-week ms, BDS SOW) */
54
+ epochMs: number;
55
+ /** System letter */
56
+ system: string;
57
+ /** Observations per satellite */
58
+ observations: ObsSatObs[];
59
+ /**
60
+ * Set for MSM1–3: every pseudorange and phase is short by an unknown whole
61
+ * number of light-milliseconds (~299.79 km) per satellite. Resolve with
62
+ * `resolveMsmRanges` before using the ranges.
63
+ */
64
+ rangeModulo1ms?: true;
65
+ }
66
+ /** Options for the RTCM3 observation decoders. */
67
+ interface ObsDecodeOptions {
68
+ /**
69
+ * Also decode compact MSM1–3. Their epochs carry `rangeModulo1ms`: every
70
+ * range is short by whole light-milliseconds until `resolveMsmRanges`
71
+ * completes it, so a consumer must opt in (default false → null, as
72
+ * before).
73
+ */
74
+ compactMsm?: boolean;
75
+ }
76
+ /**
77
+ * Decode a single RTCM3 MSM frame into observations: MSM4–7, and MSM1–3
78
+ * with `opts.compactMsm` (flagged `rangeModulo1ms`). Null otherwise.
79
+ */
80
+ declare function decodeMsmFull(frame: Rtcm3Frame, opts?: ObsDecodeOptions): ObsEpoch | null;
81
+ /**
82
+ * Convert an observation epoch timestamp to a Date.
83
+ * For GPS/Galileo/BDS/QZSS: epochMs is ms of GPS week.
84
+ * For GLONASS: epochMs is day-of-week × 86400000 + ms of day (UTC+3).
85
+ *
86
+ * Requires an approximate reference time to resolve the current week.
87
+ */
88
+ /**
89
+ * NOTE: returns a GPS-scale Date — the clock-face convention used by
90
+ * the RINEX parser and every consumer of epoch milliseconds in this
91
+ * library (satellite propagation, SPP, RINEX epoch tags). It is NOT
92
+ * UTC: converting to UTC here put every downstream satellite position
93
+ * 18 s (the current leap-second count) off — a ±14 km per-satellite
94
+ * range error that scattered live SPP solutions by kilometres.
95
+ */
96
+ declare function obsEpochToDate(sys: string, epochMs: number, refTime?: Date): Date;
97
+ /**
98
+ * Set a GLONASS FDMA frequency number (k = -7..+13) for a slot (1-24),
99
+ * e.g. from a decoded 1020 ephemeris. MSM4/6 carry no DF419, so without
100
+ * this the wavelength — and hence carrier phase — stays unknown.
101
+ */
102
+ declare function setGloFreqNumber(slot: number, k: number): void;
103
+ /**
104
+ * Reset the GLONASS frequency number cache (e.g., when switching mountpoints).
105
+ */
106
+ declare function resetGloFreqCache(): void;
107
+
108
+ export { type ObsDecodeOptions as O, type ObsEpoch as a, type ObsSatObs as b, type ObsSignal as c, decodeMsmFull as d, obsEpochToDate as o, resetGloFreqCache as r, setGloFreqNumber as s };
package/dist/orbit.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/orbit/index.ts
21
21
  var orbit_exports = {};
22
22
  __export(orbit_exports, {
23
+ EPHEMERIS_VALIDITY_MS: () => EPHEMERIS_VALIDITY_MS,
23
24
  almanacEpochMs: () => almanacEpochMs,
24
25
  almanacSatPosition: () => almanacSatPosition,
25
26
  computeAllPositions: () => computeAllPositions,
@@ -36,9 +37,11 @@ __export(orbit_exports, {
36
37
  glonassAlmanacEpochMs: () => glonassAlmanacEpochMs,
37
38
  glonassAlmanacPosition: () => glonassAlmanacPosition,
38
39
  glonassPosition: () => glonassPosition,
40
+ isEphemerisValid: () => isEphemerisValid,
39
41
  keplerPosition: () => keplerPosition,
40
42
  maskRadForAzimuth: () => maskRadForAzimuth,
41
43
  navTimesFromEph: () => navTimesFromEph,
44
+ nearestValidEphemeris: () => nearestValidEphemeris,
42
45
  rangeRate: () => rangeRate,
43
46
  selectEphemeris: () => selectEphemeris
44
47
  });
@@ -198,6 +201,42 @@ function invertMatrix(A) {
198
201
  return M.map((row) => row.slice(n));
199
202
  }
200
203
 
204
+ // src/orbit/validity.ts
205
+ var H = 36e5;
206
+ var MIN = 6e4;
207
+ var EPHEMERIS_VALIDITY_MS = {
208
+ G: 2 * H,
209
+ J: 2 * H,
210
+ E: 2.5 * H,
211
+ C: 90 * MIN,
212
+ R: 15 * MIN,
213
+ I: 2 * H,
214
+ S: 6 * MIN
215
+ };
216
+ function tocDistanceMs(eph, tGpsMs) {
217
+ const t = eph.system === "R" || eph.system === "S" ? tGpsMs - getGpsLeap(new Date(tGpsMs)) * 1e3 : tGpsMs;
218
+ return Math.abs(eph.tocDate.getTime() - t);
219
+ }
220
+ function isEphemerisValid(eph, tGpsMs) {
221
+ const w = EPHEMERIS_VALIDITY_MS[eph.system];
222
+ return w !== void 0 && tocDistanceMs(eph, tGpsMs) <= w;
223
+ }
224
+ function nearestValidEphemeris(ephs, tGpsMs) {
225
+ if (!ephs) return null;
226
+ let best = null;
227
+ let bestDt = Infinity;
228
+ for (const eph of ephs) {
229
+ const dt = tocDistanceMs(eph, tGpsMs);
230
+ const w = EPHEMERIS_VALIDITY_MS[eph.system];
231
+ if (w === void 0 || dt > w) continue;
232
+ if (dt < bestDt) {
233
+ bestDt = dt;
234
+ best = eph;
235
+ }
236
+ }
237
+ return best;
238
+ }
239
+
201
240
  // src/orbit/almanac.ts
202
241
  var GM_GPS = 3986005e8;
203
242
  var GM_GAL = 3986004418e5;
@@ -580,20 +619,20 @@ function computeDop(satAzEls) {
580
619
  const clocks = [...new Set(sats.map((s) => dopClock(s.prn)))];
581
620
  const m = 3 + clocks.length;
582
621
  if (n < Math.max(4, m)) return null;
583
- const H = [];
622
+ const H2 = [];
584
623
  for (const { az, el, prn } of sats) {
585
624
  const cosEl = Math.cos(el);
586
625
  const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
587
626
  const clock = dopClock(prn);
588
627
  for (const c of clocks) row.push(c === clock ? 1 : 0);
589
- H.push(row);
628
+ H2.push(row);
590
629
  }
591
630
  const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
592
631
  for (let i = 0; i < m; i++) {
593
632
  for (let j = 0; j < m; j++) {
594
633
  let sum = 0;
595
634
  for (let k = 0; k < n; k++) {
596
- sum += H[k][i] * H[k][j];
635
+ sum += H2[k][i] * H2[k][j];
597
636
  }
598
637
  HtH[i][j] = sum;
599
638
  }
@@ -694,18 +733,7 @@ function computeAllPositions(ephemerides, times, rxPos) {
694
733
  return { prns, times, positions };
695
734
  }
696
735
  function selectBest(ephs, timeMs) {
697
- let best = null;
698
- let bestDt = Infinity;
699
- for (const eph of ephs) {
700
- const dt = Math.abs(timeMs - eph.tocDate.getTime());
701
- const maxAge = eph.system === "R" || eph.system === "S" ? 18e5 : 4 * 36e5;
702
- if (dt > maxAge) continue;
703
- if (dt < bestDt) {
704
- bestDt = dt;
705
- best = eph;
706
- }
707
- }
708
- return best;
736
+ return nearestValidEphemeris(ephs, timeMs);
709
737
  }
710
738
  var GPS_EPOCH_MS2 = START_GPS_TIME.getTime();
711
739
  var MS_PER_WEEK = 7 * 864e5;
@@ -946,6 +974,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
946
974
  }
947
975
  // Annotate the CommonJS export names for ESM import in node:
948
976
  0 && (module.exports = {
977
+ EPHEMERIS_VALIDITY_MS,
949
978
  almanacEpochMs,
950
979
  almanacSatPosition,
951
980
  computeAllPositions,
@@ -962,9 +991,11 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
962
991
  glonassAlmanacEpochMs,
963
992
  glonassAlmanacPosition,
964
993
  glonassPosition,
994
+ isEphemerisValid,
965
995
  keplerPosition,
966
996
  maskRadForAzimuth,
967
997
  navTimesFromEph,
998
+ nearestValidEphemeris,
968
999
  rangeRate,
969
1000
  selectEphemeris
970
1001
  });
package/dist/orbit.d.cts CHANGED
@@ -91,6 +91,40 @@ declare function glonassAlmanacEpochMs(alm: SbfGlonassAlmanac): number;
91
91
  */
92
92
  declare function glonassAlmanacPosition(alm: SbfGlonassAlmanac, timeMs: number): AlmanacPosition;
93
93
 
94
+ /**
95
+ * Broadcast-ephemeris validity — whether an ephemeris may position its
96
+ * satellite at a given epoch, and the nearest one that may.
97
+ *
98
+ * Each system's half-window |t − toc| follows its broadcast design, never
99
+ * wider than the documented validity:
100
+ * - GPS / QZSS: the 4-h curve fit centred on toe → ±2 h (IS-GPS-200,
101
+ * IS-QZSS; RTKLIB MAXDTOE).
102
+ * - Galileo: 2.5 h, inside the 4-h nominal validity (RTKLIB MAXDTOE_GAL).
103
+ * - BeiDou: 90 min — ephemerides are refreshed hourly.
104
+ * - GLONASS: the immediate data cover a 30-min interval centred on tb →
105
+ * ±15 min.
106
+ * - NavIC: ±2 h (2-h update cycle).
107
+ * - SBAS GEO (MT9 state vector): 6 min (RTKLIB MAXDTOE_SBS) — a GEO
108
+ * extrapolated from an hours-old state vector is not an orbit.
109
+ * Any other system is never valid.
110
+ *
111
+ * Time: `tGpsMs` is GPS-scale epoch ms, like everywhere in this module.
112
+ * GLONASS and SBAS tocs are on the UTC axis (see {@link computeSatPosition}),
113
+ * so for those t is moved to UTC first — on a ±15-min window the 18-s
114
+ * leap-second offset matters.
115
+ */
116
+
117
+ /** Validity half-window |t − toc| per system letter, in ms. */
118
+ declare const EPHEMERIS_VALIDITY_MS: Readonly<Record<string, number>>;
119
+ /** May `eph` position its satellite at GPS-scale epoch `tGpsMs`? */
120
+ declare function isEphemerisValid(eph: Ephemeris, tGpsMs: number): boolean;
121
+ /**
122
+ * The ephemeris in `ephs` whose toc is nearest to `tGpsMs` among those valid
123
+ * at `tGpsMs`, or null when none is. `ephs` may mix satellites; filter by PRN
124
+ * first if it does.
125
+ */
126
+ declare function nearestValidEphemeris(ephs: readonly Ephemeris[] | undefined, tGpsMs: number): Ephemeris | null;
127
+
94
128
  /**
95
129
  * Satellite orbit computation from broadcast ephemerides.
96
130
  * Supports GPS, Galileo, BeiDou (Keplerian) and GLONASS (Runge-Kutta).
@@ -181,11 +215,12 @@ declare function computeDop(satAzEls: {
181
215
  prn?: string;
182
216
  }[]): DopValues | null;
183
217
  /**
184
- * Find the best ephemeris for a given PRN at a given time.
185
- * Selects the one with the closest epoch (toe/toc) to the target time.
218
+ * Find the best ephemeris for a given PRN at a given time: the one with the
219
+ * closest epoch (toe/toc) among those within their broadcast validity at
220
+ * that time ({@link isEphemerisValid}); null when none is valid.
186
221
  * @param ephemerides Array of candidate ephemerides
187
222
  * @param prn Satellite PRN identifier (e.g. "G01")
188
- * @param timeMs Target time in Unix milliseconds
223
+ * @param timeMs Target time, GPS-scale epoch milliseconds
189
224
  */
190
225
  declare function selectEphemeris(ephemerides: Ephemeris[], prn: string, timeMs: number): Ephemeris | null;
191
226
  /**
@@ -303,4 +338,4 @@ declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, num
303
338
  */
304
339
  declare function computeVisibilityFromPositions(all: AllPositionsData, elevationMaskDeg?: number | number[]): VisibilityResult;
305
340
 
306
- export { type AllPositionsData, type AlmanacPosition, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris };
341
+ export { type AllPositionsData, type AlmanacPosition, type DopValues, EPHEMERIS_VALIDITY_MS, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, isEphemerisValid, keplerPosition, maskRadForAzimuth, navTimesFromEph, nearestValidEphemeris, rangeRate, selectEphemeris };
package/dist/orbit.d.ts CHANGED
@@ -91,6 +91,40 @@ declare function glonassAlmanacEpochMs(alm: SbfGlonassAlmanac): number;
91
91
  */
92
92
  declare function glonassAlmanacPosition(alm: SbfGlonassAlmanac, timeMs: number): AlmanacPosition;
93
93
 
94
+ /**
95
+ * Broadcast-ephemeris validity — whether an ephemeris may position its
96
+ * satellite at a given epoch, and the nearest one that may.
97
+ *
98
+ * Each system's half-window |t − toc| follows its broadcast design, never
99
+ * wider than the documented validity:
100
+ * - GPS / QZSS: the 4-h curve fit centred on toe → ±2 h (IS-GPS-200,
101
+ * IS-QZSS; RTKLIB MAXDTOE).
102
+ * - Galileo: 2.5 h, inside the 4-h nominal validity (RTKLIB MAXDTOE_GAL).
103
+ * - BeiDou: 90 min — ephemerides are refreshed hourly.
104
+ * - GLONASS: the immediate data cover a 30-min interval centred on tb →
105
+ * ±15 min.
106
+ * - NavIC: ±2 h (2-h update cycle).
107
+ * - SBAS GEO (MT9 state vector): 6 min (RTKLIB MAXDTOE_SBS) — a GEO
108
+ * extrapolated from an hours-old state vector is not an orbit.
109
+ * Any other system is never valid.
110
+ *
111
+ * Time: `tGpsMs` is GPS-scale epoch ms, like everywhere in this module.
112
+ * GLONASS and SBAS tocs are on the UTC axis (see {@link computeSatPosition}),
113
+ * so for those t is moved to UTC first — on a ±15-min window the 18-s
114
+ * leap-second offset matters.
115
+ */
116
+
117
+ /** Validity half-window |t − toc| per system letter, in ms. */
118
+ declare const EPHEMERIS_VALIDITY_MS: Readonly<Record<string, number>>;
119
+ /** May `eph` position its satellite at GPS-scale epoch `tGpsMs`? */
120
+ declare function isEphemerisValid(eph: Ephemeris, tGpsMs: number): boolean;
121
+ /**
122
+ * The ephemeris in `ephs` whose toc is nearest to `tGpsMs` among those valid
123
+ * at `tGpsMs`, or null when none is. `ephs` may mix satellites; filter by PRN
124
+ * first if it does.
125
+ */
126
+ declare function nearestValidEphemeris(ephs: readonly Ephemeris[] | undefined, tGpsMs: number): Ephemeris | null;
127
+
94
128
  /**
95
129
  * Satellite orbit computation from broadcast ephemerides.
96
130
  * Supports GPS, Galileo, BeiDou (Keplerian) and GLONASS (Runge-Kutta).
@@ -181,11 +215,12 @@ declare function computeDop(satAzEls: {
181
215
  prn?: string;
182
216
  }[]): DopValues | null;
183
217
  /**
184
- * Find the best ephemeris for a given PRN at a given time.
185
- * Selects the one with the closest epoch (toe/toc) to the target time.
218
+ * Find the best ephemeris for a given PRN at a given time: the one with the
219
+ * closest epoch (toe/toc) among those within their broadcast validity at
220
+ * that time ({@link isEphemerisValid}); null when none is valid.
186
221
  * @param ephemerides Array of candidate ephemerides
187
222
  * @param prn Satellite PRN identifier (e.g. "G01")
188
- * @param timeMs Target time in Unix milliseconds
223
+ * @param timeMs Target time, GPS-scale epoch milliseconds
189
224
  */
190
225
  declare function selectEphemeris(ephemerides: Ephemeris[], prn: string, timeMs: number): Ephemeris | null;
191
226
  /**
@@ -303,4 +338,4 @@ declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, num
303
338
  */
304
339
  declare function computeVisibilityFromPositions(all: AllPositionsData, elevationMaskDeg?: number | number[]): VisibilityResult;
305
340
 
306
- export { type AllPositionsData, type AlmanacPosition, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris };
341
+ export { type AllPositionsData, type AlmanacPosition, type DopValues, EPHEMERIS_VALIDITY_MS, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, isEphemerisValid, keplerPosition, maskRadForAzimuth, navTimesFromEph, nearestValidEphemeris, rangeRate, selectEphemeris };
package/dist/orbit.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ EPHEMERIS_VALIDITY_MS,
2
3
  almanacEpochMs,
3
4
  almanacSatPosition,
4
5
  computeAllPositions,
@@ -13,12 +14,14 @@ import {
13
14
  glonassAlmanacEpochMs,
14
15
  glonassAlmanacPosition,
15
16
  glonassPosition,
17
+ isEphemerisValid,
16
18
  keplerPosition,
17
19
  maskRadForAzimuth,
18
20
  navTimesFromEph,
21
+ nearestValidEphemeris,
19
22
  rangeRate,
20
23
  selectEphemeris
21
- } from "./chunk-5OWRCF56.js";
24
+ } from "./chunk-7T5DLVZL.js";
22
25
  import "./chunk-HVXYFUCB.js";
23
26
  import {
24
27
  ecefToGeodetic,
@@ -28,6 +31,7 @@ import "./chunk-LEEU5OIO.js";
28
31
  import "./chunk-6FAL6P4G.js";
29
32
  import "./chunk-FIEWO4J4.js";
30
33
  export {
34
+ EPHEMERIS_VALIDITY_MS,
31
35
  almanacEpochMs,
32
36
  almanacSatPosition,
33
37
  computeAllPositions,
@@ -44,9 +48,11 @@ export {
44
48
  glonassAlmanacEpochMs,
45
49
  glonassAlmanacPosition,
46
50
  glonassPosition,
51
+ isEphemerisValid,
47
52
  keplerPosition,
48
53
  maskRadForAzimuth,
49
54
  navTimesFromEph,
55
+ nearestValidEphemeris,
50
56
  rangeRate,
51
57
  selectEphemeris
52
58
  };