gnss-js 2.8.2 → 2.9.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.
@@ -37,6 +37,42 @@ function invertMatrix(A) {
37
37
  return M.map((row) => row.slice(n));
38
38
  }
39
39
 
40
+ // src/orbit/validity.ts
41
+ var H = 36e5;
42
+ var MIN = 6e4;
43
+ var EPHEMERIS_VALIDITY_MS = {
44
+ G: 2 * H,
45
+ J: 2 * H,
46
+ E: 2.5 * H,
47
+ C: 90 * MIN,
48
+ R: 15 * MIN,
49
+ I: 2 * H,
50
+ S: 6 * MIN
51
+ };
52
+ function tocDistanceMs(eph, tGpsMs) {
53
+ const t = eph.system === "R" || eph.system === "S" ? tGpsMs - getGpsLeap(new Date(tGpsMs)) * 1e3 : tGpsMs;
54
+ return Math.abs(eph.tocDate.getTime() - t);
55
+ }
56
+ function isEphemerisValid(eph, tGpsMs) {
57
+ const w = EPHEMERIS_VALIDITY_MS[eph.system];
58
+ return w !== void 0 && tocDistanceMs(eph, tGpsMs) <= w;
59
+ }
60
+ function nearestValidEphemeris(ephs, tGpsMs) {
61
+ if (!ephs) return null;
62
+ let best = null;
63
+ let bestDt = Infinity;
64
+ for (const eph of ephs) {
65
+ const dt = tocDistanceMs(eph, tGpsMs);
66
+ const w = EPHEMERIS_VALIDITY_MS[eph.system];
67
+ if (w === void 0 || dt > w) continue;
68
+ if (dt < bestDt) {
69
+ bestDt = dt;
70
+ best = eph;
71
+ }
72
+ }
73
+ return best;
74
+ }
75
+
40
76
  // src/orbit/almanac.ts
41
77
  var GM_GPS = 3986005e8;
42
78
  var GM_GAL = 3986004418e5;
@@ -419,20 +455,20 @@ function computeDop(satAzEls) {
419
455
  const clocks = [...new Set(sats.map((s) => dopClock(s.prn)))];
420
456
  const m = 3 + clocks.length;
421
457
  if (n < Math.max(4, m)) return null;
422
- const H = [];
458
+ const H2 = [];
423
459
  for (const { az, el, prn } of sats) {
424
460
  const cosEl = Math.cos(el);
425
461
  const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
426
462
  const clock = dopClock(prn);
427
463
  for (const c of clocks) row.push(c === clock ? 1 : 0);
428
- H.push(row);
464
+ H2.push(row);
429
465
  }
430
466
  const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
431
467
  for (let i = 0; i < m; i++) {
432
468
  for (let j = 0; j < m; j++) {
433
469
  let sum = 0;
434
470
  for (let k = 0; k < n; k++) {
435
- sum += H[k][i] * H[k][j];
471
+ sum += H2[k][i] * H2[k][j];
436
472
  }
437
473
  HtH[i][j] = sum;
438
474
  }
@@ -533,18 +569,7 @@ function computeAllPositions(ephemerides, times, rxPos) {
533
569
  return { prns, times, positions };
534
570
  }
535
571
  function selectBest(ephs, timeMs) {
536
- let best = null;
537
- let bestDt = Infinity;
538
- for (const eph of ephs) {
539
- const dt = Math.abs(timeMs - eph.tocDate.getTime());
540
- const maxAge = eph.system === "R" || eph.system === "S" ? 18e5 : 4 * 36e5;
541
- if (dt > maxAge) continue;
542
- if (dt < bestDt) {
543
- bestDt = dt;
544
- best = eph;
545
- }
546
- }
547
- return best;
572
+ return nearestValidEphemeris(ephs, timeMs);
548
573
  }
549
574
  var GPS_EPOCH_MS2 = START_GPS_TIME.getTime();
550
575
  var MS_PER_WEEK = 7 * 864e5;
@@ -786,6 +811,9 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
786
811
 
787
812
  export {
788
813
  invertMatrix,
814
+ EPHEMERIS_VALIDITY_MS,
815
+ isEphemerisValid,
816
+ nearestValidEphemeris,
789
817
  almanacEpochMs,
790
818
  almanacSatPosition,
791
819
  glonassAlmanacEpochMs,
@@ -7,7 +7,7 @@ import {
7
7
  ecefToAzEl,
8
8
  invertMatrix,
9
9
  selectEphemeris
10
- } from "./chunk-5OWRCF56.js";
10
+ } from "./chunk-7T5DLVZL.js";
11
11
  import {
12
12
  ecefToGeodetic,
13
13
  getAer,
package/dist/index.cjs CHANGED
@@ -36,6 +36,7 @@ __export(src_exports, {
36
36
  DELTA_GLO_L2: () => DELTA_GLO_L2,
37
37
  DUAL_FREQ_PAIRS: () => DUAL_FREQ_PAIRS,
38
38
  EMPTY_WARNINGS: () => EMPTY_WARNINGS,
39
+ EPHEMERIS_VALIDITY_MS: () => EPHEMERIS_VALIDITY_MS,
39
40
  FREQ: () => FREQ,
40
41
  FREQ_BDS_B1A: () => FREQ_BDS_B1A,
41
42
  FREQ_BDS_B1C: () => FREQ_BDS_B1C,
@@ -252,6 +253,7 @@ __export(src_exports, {
252
253
  hdrLine: () => hdrLine,
253
254
  horizonDistance: () => horizonDistance,
254
255
  ionoFree: () => ionoFree,
256
+ isEphemerisValid: () => isEphemerisValid,
255
257
  isSbasL1Preamble: () => isSbasL1Preamble,
256
258
  isSbasL5Correction: () => isSbasL5Correction,
257
259
  isSsrMessage: () => isSsrMessage,
@@ -264,6 +266,7 @@ __export(src_exports, {
264
266
  meridianArc: () => meridianArc,
265
267
  moonEcef: () => moonEcef,
266
268
  navTimesFromEph: () => navTimesFromEph,
269
+ nearestValidEphemeris: () => nearestValidEphemeris,
267
270
  niellMapping: () => niellMapping,
268
271
  nlWavelength: () => nlWavelength,
269
272
  nmeaCoordToDecimal: () => nmeaCoordToDecimal,
@@ -12312,6 +12315,42 @@ function invertMatrix(A) {
12312
12315
  return M.map((row) => row.slice(n));
12313
12316
  }
12314
12317
 
12318
+ // src/orbit/validity.ts
12319
+ var H = 36e5;
12320
+ var MIN = 6e4;
12321
+ var EPHEMERIS_VALIDITY_MS = {
12322
+ G: 2 * H,
12323
+ J: 2 * H,
12324
+ E: 2.5 * H,
12325
+ C: 90 * MIN,
12326
+ R: 15 * MIN,
12327
+ I: 2 * H,
12328
+ S: 6 * MIN
12329
+ };
12330
+ function tocDistanceMs(eph, tGpsMs) {
12331
+ const t = eph.system === "R" || eph.system === "S" ? tGpsMs - getGpsLeap(new Date(tGpsMs)) * 1e3 : tGpsMs;
12332
+ return Math.abs(eph.tocDate.getTime() - t);
12333
+ }
12334
+ function isEphemerisValid(eph, tGpsMs) {
12335
+ const w = EPHEMERIS_VALIDITY_MS[eph.system];
12336
+ return w !== void 0 && tocDistanceMs(eph, tGpsMs) <= w;
12337
+ }
12338
+ function nearestValidEphemeris(ephs, tGpsMs) {
12339
+ if (!ephs) return null;
12340
+ let best = null;
12341
+ let bestDt = Infinity;
12342
+ for (const eph of ephs) {
12343
+ const dt = tocDistanceMs(eph, tGpsMs);
12344
+ const w = EPHEMERIS_VALIDITY_MS[eph.system];
12345
+ if (w === void 0 || dt > w) continue;
12346
+ if (dt < bestDt) {
12347
+ bestDt = dt;
12348
+ best = eph;
12349
+ }
12350
+ }
12351
+ return best;
12352
+ }
12353
+
12315
12354
  // src/orbit/almanac.ts
12316
12355
  var GM_GPS = 3986005e8;
12317
12356
  var GM_GAL = 3986004418e5;
@@ -12808,18 +12847,7 @@ function computeAllPositions(ephemerides, times, rxPos) {
12808
12847
  return { prns, times, positions };
12809
12848
  }
12810
12849
  function selectBest(ephs, timeMs) {
12811
- let best = null;
12812
- let bestDt = Infinity;
12813
- for (const eph of ephs) {
12814
- const dt = Math.abs(timeMs - eph.tocDate.getTime());
12815
- const maxAge = eph.system === "R" || eph.system === "S" ? 18e5 : 4 * 36e5;
12816
- if (dt > maxAge) continue;
12817
- if (dt < bestDt) {
12818
- bestDt = dt;
12819
- best = eph;
12820
- }
12821
- }
12822
- return best;
12850
+ return nearestValidEphemeris(ephs, timeMs);
12823
12851
  }
12824
12852
  var GPS_EPOCH_MS24 = START_GPS_TIME.getTime();
12825
12853
  var MS_PER_WEEK13 = 7 * 864e5;
@@ -13060,14 +13088,14 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
13060
13088
  }
13061
13089
 
13062
13090
  // src/frames/index.ts
13063
- var H = (t, d, r, tDot, dDot, rDot, epoch) => ({ t, d, r, tDot, dDot, rDot, epoch });
13091
+ var H2 = (t, d, r, tDot, dDot, rDot, epoch) => ({ t, d, r, tDot, dDot, rDot, epoch });
13064
13092
  var Z = [0, 0, 0];
13065
13093
  var FROM_ITRF2020 = {
13066
- ITRF2014: H([-1.4, -0.9, 1.4], -0.42, Z, [0, -0.1, 0.2], 0, Z, 2015),
13067
- ITRF2008: H([0.2, 1, 3.3], -0.29, Z, [0, -0.1, 0.1], 0.03, Z, 2015),
13068
- ITRF2005: H([2.7, 0.1, -1.4], 0.65, Z, [0.3, -0.1, 0.1], 0.03, Z, 2015),
13069
- ITRF2000: H([-0.2, 0.8, -34.2], 2.25, Z, [0.1, 0, -1.7], 0.11, Z, 2015),
13070
- ITRF97: H(
13094
+ ITRF2014: H2([-1.4, -0.9, 1.4], -0.42, Z, [0, -0.1, 0.2], 0, Z, 2015),
13095
+ ITRF2008: H2([0.2, 1, 3.3], -0.29, Z, [0, -0.1, 0.1], 0.03, Z, 2015),
13096
+ ITRF2005: H2([2.7, 0.1, -1.4], 0.65, Z, [0.3, -0.1, 0.1], 0.03, Z, 2015),
13097
+ ITRF2000: H2([-0.2, 0.8, -34.2], 2.25, Z, [0.1, 0, -1.7], 0.11, Z, 2015),
13098
+ ITRF97: H2(
13071
13099
  [6.5, -3.9, -77.9],
13072
13100
  3.98,
13073
13101
  [0, 0, 0.36],
@@ -13076,7 +13104,7 @@ var FROM_ITRF2020 = {
13076
13104
  [0, 0, 0.02],
13077
13105
  2015
13078
13106
  ),
13079
- ITRF96: H(
13107
+ ITRF96: H2(
13080
13108
  [6.5, -3.9, -77.9],
13081
13109
  3.98,
13082
13110
  [0, 0, 0.36],
@@ -13085,7 +13113,7 @@ var FROM_ITRF2020 = {
13085
13113
  [0, 0, 0.02],
13086
13114
  2015
13087
13115
  ),
13088
- ITRF94: H(
13116
+ ITRF94: H2(
13089
13117
  [6.5, -3.9, -77.9],
13090
13118
  3.98,
13091
13119
  [0, 0, 0.36],
@@ -13094,7 +13122,7 @@ var FROM_ITRF2020 = {
13094
13122
  [0, 0, 0.02],
13095
13123
  2015
13096
13124
  ),
13097
- ITRF93: H(
13125
+ ITRF93: H2(
13098
13126
  [-65.8, 1.9, -71.3],
13099
13127
  4.47,
13100
13128
  [-3.36, -4.33, 0.75],
@@ -13103,7 +13131,7 @@ var FROM_ITRF2020 = {
13103
13131
  [-0.11, -0.19, 0.07],
13104
13132
  2015
13105
13133
  ),
13106
- ITRF92: H(
13134
+ ITRF92: H2(
13107
13135
  [14.5, -1.9, -85.9],
13108
13136
  3.27,
13109
13137
  [0, 0, 0.36],
@@ -13112,7 +13140,7 @@ var FROM_ITRF2020 = {
13112
13140
  [0, 0, 0.02],
13113
13141
  2015
13114
13142
  ),
13115
- ITRF91: H(
13143
+ ITRF91: H2(
13116
13144
  [26.5, 12.1, -91.9],
13117
13145
  4.67,
13118
13146
  [0, 0, 0.36],
@@ -13121,7 +13149,7 @@ var FROM_ITRF2020 = {
13121
13149
  [0, 0, 0.02],
13122
13150
  2015
13123
13151
  ),
13124
- ITRF90: H(
13152
+ ITRF90: H2(
13125
13153
  [24.5, 8.1, -107.9],
13126
13154
  4.97,
13127
13155
  [0, 0, 0.36],
@@ -13130,7 +13158,7 @@ var FROM_ITRF2020 = {
13130
13158
  [0, 0, 0.02],
13131
13159
  2015
13132
13160
  ),
13133
- ITRF89: H(
13161
+ ITRF89: H2(
13134
13162
  [29.5, 32.1, -145.9],
13135
13163
  8.37,
13136
13164
  [0, 0, 0.36],
@@ -13139,7 +13167,7 @@ var FROM_ITRF2020 = {
13139
13167
  [0, 0, 0.02],
13140
13168
  2015
13141
13169
  ),
13142
- ITRF88: H(
13170
+ ITRF88: H2(
13143
13171
  [24.5, -3.9, -169.9],
13144
13172
  11.47,
13145
13173
  [0.1, 0, 0.36],
@@ -13149,10 +13177,10 @@ var FROM_ITRF2020 = {
13149
13177
  2015
13150
13178
  ),
13151
13179
  /** ITRF2020 → ETRF2020, EPSG:10572 (epoch-1989 variant) */
13152
- ETRF2020: H(Z, 0, Z, Z, 0, [0.086, 0.519, -0.753], 1989)
13180
+ ETRF2020: H2(Z, 0, Z, Z, 0, [0.086, 0.519, -0.753], 1989)
13153
13181
  };
13154
- var ITRF2014_TO_ETRF2014 = H(Z, 0, Z, Z, 0, [0.085, 0.531, -0.77], 1989);
13155
- var ITRF2014_TO_ETRF2000 = H(
13182
+ var ITRF2014_TO_ETRF2014 = H2(Z, 0, Z, Z, 0, [0.085, 0.531, -0.77], 1989);
13183
+ var ITRF2014_TO_ETRF2000 = H2(
13156
13184
  [54.7, 52.2, -74.1],
13157
13185
  2.12,
13158
13186
  [1.701, 10.29, -16.632],
@@ -13161,7 +13189,7 @@ var ITRF2014_TO_ETRF2000 = H(
13161
13189
  [0.081, 0.49, -0.792],
13162
13190
  2010
13163
13191
  );
13164
- var ITRF2014_TO_NAD83_2011 = H(
13192
+ var ITRF2014_TO_NAD83_2011 = H2(
13165
13193
  [1005.3, -1909.21, -541.57],
13166
13194
  0.36891,
13167
13195
  [-26.78138, 0.42027, -10.93206],
@@ -15837,7 +15865,7 @@ function moonEcef(timeMs) {
15837
15865
  ];
15838
15866
  return eciToEcef(eci, gmst(timeMs));
15839
15867
  }
15840
- var H2 = 0.6078;
15868
+ var H22 = 0.6078;
15841
15869
  var L2 = 0.0847;
15842
15870
  var RE = 6378137;
15843
15871
  var GM_SUN_RATIO = 332946.0482;
@@ -15862,7 +15890,7 @@ function solidEarthTide(rcv, sun, moon) {
15862
15890
  ];
15863
15891
  const dot2 = rHat[0] * jHat[0] + rHat[1] * jHat[1] + rHat[2] * jHat[2];
15864
15892
  const factor = ratio * RE * RE * RE * RE / (rj * rj * rj);
15865
- const radialScale = H2 * (1.5 * dot2 * dot2 - 0.5);
15893
+ const radialScale = H22 * (1.5 * dot2 * dot2 - 0.5);
15866
15894
  const tanScale = 3 * L2 * dot2;
15867
15895
  for (let k = 0; k < 3; k++) {
15868
15896
  disp[k] += factor * (radialScale * rHat[k] + tanScale * (jHat[k] - dot2 * rHat[k]));
@@ -19522,6 +19550,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
19522
19550
  DELTA_GLO_L2,
19523
19551
  DUAL_FREQ_PAIRS,
19524
19552
  EMPTY_WARNINGS,
19553
+ EPHEMERIS_VALIDITY_MS,
19525
19554
  FREQ,
19526
19555
  FREQ_BDS_B1A,
19527
19556
  FREQ_BDS_B1C,
@@ -19738,6 +19767,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
19738
19767
  hdrLine,
19739
19768
  horizonDistance,
19740
19769
  ionoFree,
19770
+ isEphemerisValid,
19741
19771
  isSbasL1Preamble,
19742
19772
  isSbasL5Correction,
19743
19773
  isSsrMessage,
@@ -19750,6 +19780,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
19750
19780
  meridianArc,
19751
19781
  moonEcef,
19752
19782
  navTimesFromEph,
19783
+ nearestValidEphemeris,
19753
19784
  niellMapping,
19754
19785
  nlWavelength,
19755
19786
  nmeaCoordToDecimal,
package/dist/index.d.cts CHANGED
@@ -17,7 +17,7 @@ export { DfmcCensus, DfmcMessageCb, GeoL5Result, SBAS_L5_MT_NAMES, SBAS_L5_PREAM
17
17
  export { NovatelEpoch, NovatelMeasurement, NovatelNavResult, NovatelParseResult, NovatelPvtResult, OEM4_HLEN, Oem4Frame, crc32, oem4Frames, parseNovatelNav, parseNovatelPvt, parseNovatelRange } from './novatel.cjs';
18
18
  export { TrimbleEpoch, TrimbleFrame, TrimbleMeasurement, TrimbleNavResult, TrimbleParseResult, parseTrimble, parseTrimbleNav, trimbleFrames } from './trimble.cjs';
19
19
  export { BinexEpoch, BinexMeasurement, BinexParseResult, BinexRecord, binexCrc16, binexCrc32, binexCsum8, binexRecords, decodeBinexEph, getBnxi, parseBinex } from './binex.cjs';
20
- export { AllPositionsData, AlmanacPosition, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris } from './orbit.cjs';
20
+ export { AllPositionsData, AlmanacPosition, DopValues, EPHEMERIS_VALIDITY_MS, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, isEphemerisValid, keplerPosition, maskRadForAzimuth, navTimesFromEph, nearestValidEphemeris, rangeRate, selectEphemeris } from './orbit.cjs';
21
21
  export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.cjs';
22
22
  export { AntennaOffset, AppliedOrbitClock, ArFixedSat, ArOptions, ArResult, ArSat, CorrectionResult, Degradation, DgnssOptions, DgnssSolution, EphemerisSource, FixedPositionResult, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, NetworkFcbResult, NetworkFcbSummary, NlArc, NlFcbResult, OrbitClockCorrection, PppAntennaModel, PppArc, PppCorrectionConfig, PppCorrections, PppEngine, PppEphemerisSource, PppEpoch, PppEpochContext, PppEpochResult, PppFixState, PppOptions, PppSatObs, PppSolution, RawObservation, ReceiverBias, ReceiverClockSample, RtkAntennaConfig, RtkAntennaModel, RtkAntennaOffset, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, RtkObsEpoch, RtkObsInput, RtkPostProcessOptions, RtkPostProcessResult, RtkReceiverAntenna, RtkTrackPoint, SatGeom, SatSample, SbasCorrectionSource, SbasIgp, SbasIonoDelay, SbasProcessor, SbasSatCorrection, Sp3EphemerisSource, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, ZeroBaselineEngine, ZeroBaselineOptions, antexFreqOfGroup, applyOrbitClock, buildPppAntenna, buildRtkAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, hasToOrbitClock, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, rcvAntennaRangeM, resolvePppAmbiguities, satClockCorrection, sbasIonoDeg, sbasLongTermDeg, sbasProtectionLevels, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength } from './positioning.cjs';
23
23
  export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.cjs';
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@ export { DfmcCensus, DfmcMessageCb, GeoL5Result, SBAS_L5_MT_NAMES, SBAS_L5_PREAM
17
17
  export { NovatelEpoch, NovatelMeasurement, NovatelNavResult, NovatelParseResult, NovatelPvtResult, OEM4_HLEN, Oem4Frame, crc32, oem4Frames, parseNovatelNav, parseNovatelPvt, parseNovatelRange } from './novatel.js';
18
18
  export { TrimbleEpoch, TrimbleFrame, TrimbleMeasurement, TrimbleNavResult, TrimbleParseResult, parseTrimble, parseTrimbleNav, trimbleFrames } from './trimble.js';
19
19
  export { BinexEpoch, BinexMeasurement, BinexParseResult, BinexRecord, binexCrc16, binexCrc32, binexCsum8, binexRecords, decodeBinexEph, getBnxi, parseBinex } from './binex.js';
20
- export { AllPositionsData, AlmanacPosition, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris } from './orbit.js';
20
+ export { AllPositionsData, AlmanacPosition, DopValues, EPHEMERIS_VALIDITY_MS, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, isEphemerisValid, keplerPosition, maskRadForAzimuth, navTimesFromEph, nearestValidEphemeris, rangeRate, selectEphemeris } from './orbit.js';
21
21
  export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.js';
22
22
  export { AntennaOffset, AppliedOrbitClock, ArFixedSat, ArOptions, ArResult, ArSat, CorrectionResult, Degradation, DgnssOptions, DgnssSolution, EphemerisSource, FixedPositionResult, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, NetworkFcbResult, NetworkFcbSummary, NlArc, NlFcbResult, OrbitClockCorrection, PppAntennaModel, PppArc, PppCorrectionConfig, PppCorrections, PppEngine, PppEphemerisSource, PppEpoch, PppEpochContext, PppEpochResult, PppFixState, PppOptions, PppSatObs, PppSolution, RawObservation, ReceiverBias, ReceiverClockSample, RtkAntennaConfig, RtkAntennaModel, RtkAntennaOffset, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, RtkObsEpoch, RtkObsInput, RtkPostProcessOptions, RtkPostProcessResult, RtkReceiverAntenna, RtkTrackPoint, SatGeom, SatSample, SbasCorrectionSource, SbasIgp, SbasIonoDelay, SbasProcessor, SbasSatCorrection, Sp3EphemerisSource, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, ZeroBaselineEngine, ZeroBaselineOptions, antexFreqOfGroup, applyOrbitClock, buildPppAntenna, buildRtkAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, hasToOrbitClock, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, rcvAntennaRangeM, resolvePppAmbiguities, satClockCorrection, sbasIonoDeg, sbasLongTermDeg, sbasProtectionLevels, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength } from './positioning.js';
23
23
  export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.js';
package/dist/index.js CHANGED
@@ -213,9 +213,10 @@ import {
213
213
  sunEcef,
214
214
  toRtkEpoch,
215
215
  wlWavelength
216
- } from "./chunk-CLNLOYBX.js";
216
+ } from "./chunk-NOGA5KD7.js";
217
217
  import "./chunk-WQX354KD.js";
218
218
  import {
219
+ EPHEMERIS_VALIDITY_MS,
219
220
  almanacEpochMs,
220
221
  almanacSatPosition,
221
222
  computeAllPositions,
@@ -230,12 +231,14 @@ import {
230
231
  glonassAlmanacEpochMs,
231
232
  glonassAlmanacPosition,
232
233
  glonassPosition,
234
+ isEphemerisValid,
233
235
  keplerPosition,
234
236
  maskRadForAzimuth,
235
237
  navTimesFromEph,
238
+ nearestValidEphemeris,
236
239
  rangeRate,
237
240
  selectEphemeris
238
- } from "./chunk-5OWRCF56.js";
241
+ } from "./chunk-7T5DLVZL.js";
239
242
  import {
240
243
  getBdsTime,
241
244
  getDateFromBdsTime,
@@ -415,6 +418,7 @@ export {
415
418
  DELTA_GLO_L2,
416
419
  DUAL_FREQ_PAIRS,
417
420
  EMPTY_WARNINGS,
421
+ EPHEMERIS_VALIDITY_MS,
418
422
  FREQ,
419
423
  FREQ_BDS_B1A,
420
424
  FREQ_BDS_B1C,
@@ -631,6 +635,7 @@ export {
631
635
  hdrLine,
632
636
  horizonDistance,
633
637
  ionoFree,
638
+ isEphemerisValid,
634
639
  isSbasL1Preamble,
635
640
  isSbasL5Correction,
636
641
  isSsrMessage,
@@ -643,6 +648,7 @@ export {
643
648
  meridianArc,
644
649
  moonEcef,
645
650
  navTimesFromEph,
651
+ nearestValidEphemeris,
646
652
  niellMapping,
647
653
  nlWavelength,
648
654
  nmeaCoordToDecimal,
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
  };
@@ -254,6 +254,38 @@ function invertMatrix(A) {
254
254
  return M.map((row) => row.slice(n));
255
255
  }
256
256
 
257
+ // src/orbit/validity.ts
258
+ var H = 36e5;
259
+ var MIN = 6e4;
260
+ var EPHEMERIS_VALIDITY_MS = {
261
+ G: 2 * H,
262
+ J: 2 * H,
263
+ E: 2.5 * H,
264
+ C: 90 * MIN,
265
+ R: 15 * MIN,
266
+ I: 2 * H,
267
+ S: 6 * MIN
268
+ };
269
+ function tocDistanceMs(eph, tGpsMs) {
270
+ const t = eph.system === "R" || eph.system === "S" ? tGpsMs - getGpsLeap(new Date(tGpsMs)) * 1e3 : tGpsMs;
271
+ return Math.abs(eph.tocDate.getTime() - t);
272
+ }
273
+ function nearestValidEphemeris(ephs, tGpsMs) {
274
+ if (!ephs) return null;
275
+ let best = null;
276
+ let bestDt = Infinity;
277
+ for (const eph of ephs) {
278
+ const dt = tocDistanceMs(eph, tGpsMs);
279
+ const w = EPHEMERIS_VALIDITY_MS[eph.system];
280
+ if (w === void 0 || dt > w) continue;
281
+ if (dt < bestDt) {
282
+ bestDt = dt;
283
+ best = eph;
284
+ }
285
+ }
286
+ return best;
287
+ }
288
+
257
289
  // src/orbit/index.ts
258
290
  var GM_GPS = 3986005e8;
259
291
  var GM_GAL = 3986004418e5;
@@ -452,20 +484,20 @@ function computeDop(satAzEls) {
452
484
  const clocks = [...new Set(sats.map((s) => dopClock(s.prn)))];
453
485
  const m = 3 + clocks.length;
454
486
  if (n < Math.max(4, m)) return null;
455
- const H = [];
487
+ const H3 = [];
456
488
  for (const { az, el, prn } of sats) {
457
489
  const cosEl = Math.cos(el);
458
490
  const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
459
491
  const clock = dopClock(prn);
460
492
  for (const c of clocks) row.push(c === clock ? 1 : 0);
461
- H.push(row);
493
+ H3.push(row);
462
494
  }
463
495
  const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
464
496
  for (let i = 0; i < m; i++) {
465
497
  for (let j = 0; j < m; j++) {
466
498
  let sum = 0;
467
499
  for (let k = 0; k < n; k++) {
468
- sum += H[k][i] * H[k][j];
500
+ sum += H3[k][i] * H3[k][j];
469
501
  }
470
502
  HtH[i][j] = sum;
471
503
  }
@@ -501,18 +533,7 @@ function computeSatPosition(eph, timeMs) {
501
533
  return keplerPosition(eph, tow);
502
534
  }
503
535
  function selectBest(ephs, timeMs) {
504
- let best = null;
505
- let bestDt = Infinity;
506
- for (const eph of ephs) {
507
- const dt = Math.abs(timeMs - eph.tocDate.getTime());
508
- const maxAge = eph.system === "R" || eph.system === "S" ? 18e5 : 4 * 36e5;
509
- if (dt > maxAge) continue;
510
- if (dt < bestDt) {
511
- bestDt = dt;
512
- best = eph;
513
- }
514
- }
515
- return best;
536
+ return nearestValidEphemeris(ephs, timeMs);
516
537
  }
517
538
  var GPS_EPOCH_MS = START_GPS_TIME.getTime();
518
539
  var MS_PER_WEEK = 7 * 864e5;
@@ -1308,7 +1329,7 @@ function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
1308
1329
  let cov = null;
1309
1330
  for (let iter = 0; iter < maxIterations; iter++) {
1310
1331
  iterations = iter + 1;
1311
- const H = [];
1332
+ const H3 = [];
1312
1333
  const v = [];
1313
1334
  const terms = /* @__PURE__ */ new Map();
1314
1335
  const termOf = (g) => {
@@ -1324,11 +1345,11 @@ function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
1324
1345
  const tr = termOf(row.ref);
1325
1346
  const pred = ts.rho - row.g.rhoB + ts.dTropo - (tr.rho - row.ref.rhoB + tr.dTropo);
1326
1347
  v.push(row.z - pred);
1327
- H.push([ts.u[0] - tr.u[0], ts.u[1] - tr.u[1], ts.u[2] - tr.u[2]]);
1348
+ H3.push([ts.u[0] - tr.u[0], ts.u[1] - tr.u[1], ts.u[2] - tr.u[2]]);
1328
1349
  }
1329
- const Ht = transpose(H);
1350
+ const Ht = transpose(H3);
1330
1351
  const HtW = matMul(Ht, W);
1331
- const N = matMul(HtW, H);
1352
+ const N = matMul(HtW, H3);
1332
1353
  const b = matMul(
1333
1354
  HtW,
1334
1355
  v.map((s) => [s])
@@ -1344,7 +1365,7 @@ function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
1344
1365
  z += dx[2][0];
1345
1366
  cov = Ninv;
1346
1367
  rows.forEach((row, i) => {
1347
- residuals[row.g.prn] = v[i] - (H[i][0] * dx[0][0] + H[i][1] * dx[1][0] + H[i][2] * dx[2][0]);
1368
+ residuals[row.g.prn] = v[i] - (H3[i][0] * dx[0][0] + H3[i][1] * dx[1][0] + H3[i][2] * dx[2][0]);
1348
1369
  });
1349
1370
  if (Math.hypot(dx[0][0], dx[1][0], dx[2][0]) < convergenceM) {
1350
1371
  converged = true;
@@ -1738,19 +1759,19 @@ var RtkFloatEngine = class {
1738
1759
  const PPrior = this.P;
1739
1760
  let xi = [...this.x];
1740
1761
  let K = [];
1741
- let H = [];
1762
+ let H3 = [];
1742
1763
  for (let it = 0; it < Math.max(o.updateIterations, 1); it++) {
1743
- H = [];
1764
+ H3 = [];
1744
1765
  const v = [];
1745
1766
  for (const row of rows) {
1746
1767
  const { pred, h } = predict(row, xi);
1747
1768
  let corr = 0;
1748
1769
  for (let j = 0; j < n; j++) corr += h[j] * (xPrior[j] - xi[j]);
1749
1770
  v.push(row.z - pred - corr);
1750
- H.push(h);
1771
+ H3.push(h);
1751
1772
  }
1752
- const Ht = transpose(H);
1753
- const S = matMul(matMul(H, PPrior), Ht);
1773
+ const Ht = transpose(H3);
1774
+ const S = matMul(matMul(H3, PPrior), Ht);
1754
1775
  for (let i = 0; i < rows.length; i++)
1755
1776
  for (let j = 0; j < rows.length; j++) S[i][j] += R[i][j];
1756
1777
  const Sinv = matInv(S);
@@ -1766,7 +1787,7 @@ var RtkFloatEngine = class {
1766
1787
  xi = xPrior.map((s, i) => s + dx[i][0]);
1767
1788
  }
1768
1789
  this.x = xi;
1769
- const KH = matMul(K, H);
1790
+ const KH = matMul(K, H3);
1770
1791
  const IKH = zeros(n, n);
1771
1792
  for (let i = 0; i < n; i++)
1772
1793
  for (let j = 0; j < n; j++) IKH[i][j] = (i === j ? 1 : 0) - KH[i][j];
@@ -40,9 +40,9 @@ import {
40
40
  sunEcef,
41
41
  toRtkEpoch,
42
42
  wlWavelength
43
- } from "./chunk-CLNLOYBX.js";
43
+ } from "./chunk-NOGA5KD7.js";
44
44
  import "./chunk-WQX354KD.js";
45
- import "./chunk-5OWRCF56.js";
45
+ import "./chunk-7T5DLVZL.js";
46
46
  import "./chunk-HVXYFUCB.js";
47
47
  import "./chunk-CVCTRIWU.js";
48
48
  import "./chunk-37QNKGTC.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "2.8.2",
3
+ "version": "2.9.0",
4
4
  "description": "Comprehensive GNSS library for JavaScript — time scales, coordinates, RINEX parsing, RTCM3 decoding, orbit computation, and signal analysis",
5
5
  "type": "module",
6
6
  "sideEffects": false,