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.
@@ -49,6 +49,7 @@ __export(positioning_exports, {
49
49
  nlWavelength: () => nlWavelength,
50
50
  postProcessRtk: () => postProcessRtk,
51
51
  rcvAntennaRangeM: () => rcvAntennaRangeM,
52
+ resolveMsmRanges: () => resolveMsmRanges,
52
53
  resolvePppAmbiguities: () => resolvePppAmbiguities,
53
54
  satClockCorrection: () => satClockCorrection,
54
55
  sbasIonoDeg: () => sbasIonoDeg,
@@ -254,6 +255,38 @@ function invertMatrix(A) {
254
255
  return M.map((row) => row.slice(n));
255
256
  }
256
257
 
258
+ // src/orbit/validity.ts
259
+ var H = 36e5;
260
+ var MIN = 6e4;
261
+ var EPHEMERIS_VALIDITY_MS = {
262
+ G: 2 * H,
263
+ J: 2 * H,
264
+ E: 2.5 * H,
265
+ C: 90 * MIN,
266
+ R: 15 * MIN,
267
+ I: 2 * H,
268
+ S: 6 * MIN
269
+ };
270
+ function tocDistanceMs(eph, tGpsMs) {
271
+ const t = eph.system === "R" || eph.system === "S" ? tGpsMs - getGpsLeap(new Date(tGpsMs)) * 1e3 : tGpsMs;
272
+ return Math.abs(eph.tocDate.getTime() - t);
273
+ }
274
+ function nearestValidEphemeris(ephs, tGpsMs) {
275
+ if (!ephs) return null;
276
+ let best = null;
277
+ let bestDt = Infinity;
278
+ for (const eph of ephs) {
279
+ const dt = tocDistanceMs(eph, tGpsMs);
280
+ const w = EPHEMERIS_VALIDITY_MS[eph.system];
281
+ if (w === void 0 || dt > w) continue;
282
+ if (dt < bestDt) {
283
+ bestDt = dt;
284
+ best = eph;
285
+ }
286
+ }
287
+ return best;
288
+ }
289
+
257
290
  // src/orbit/index.ts
258
291
  var GM_GPS = 3986005e8;
259
292
  var GM_GAL = 3986004418e5;
@@ -452,20 +485,20 @@ function computeDop(satAzEls) {
452
485
  const clocks = [...new Set(sats.map((s) => dopClock(s.prn)))];
453
486
  const m = 3 + clocks.length;
454
487
  if (n < Math.max(4, m)) return null;
455
- const H = [];
488
+ const H3 = [];
456
489
  for (const { az, el, prn } of sats) {
457
490
  const cosEl = Math.cos(el);
458
491
  const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
459
492
  const clock = dopClock(prn);
460
493
  for (const c of clocks) row.push(c === clock ? 1 : 0);
461
- H.push(row);
494
+ H3.push(row);
462
495
  }
463
496
  const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
464
497
  for (let i = 0; i < m; i++) {
465
498
  for (let j = 0; j < m; j++) {
466
499
  let sum = 0;
467
500
  for (let k = 0; k < n; k++) {
468
- sum += H[k][i] * H[k][j];
501
+ sum += H3[k][i] * H3[k][j];
469
502
  }
470
503
  HtH[i][j] = sum;
471
504
  }
@@ -501,18 +534,7 @@ function computeSatPosition(eph, timeMs) {
501
534
  return keplerPosition(eph, tow);
502
535
  }
503
536
  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;
537
+ return nearestValidEphemeris(ephs, timeMs);
516
538
  }
517
539
  var GPS_EPOCH_MS = START_GPS_TIME.getTime();
518
540
  var MS_PER_WEEK = 7 * 864e5;
@@ -1308,7 +1330,7 @@ function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
1308
1330
  let cov = null;
1309
1331
  for (let iter = 0; iter < maxIterations; iter++) {
1310
1332
  iterations = iter + 1;
1311
- const H = [];
1333
+ const H3 = [];
1312
1334
  const v = [];
1313
1335
  const terms = /* @__PURE__ */ new Map();
1314
1336
  const termOf = (g) => {
@@ -1324,11 +1346,11 @@ function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
1324
1346
  const tr = termOf(row.ref);
1325
1347
  const pred = ts.rho - row.g.rhoB + ts.dTropo - (tr.rho - row.ref.rhoB + tr.dTropo);
1326
1348
  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]]);
1349
+ H3.push([ts.u[0] - tr.u[0], ts.u[1] - tr.u[1], ts.u[2] - tr.u[2]]);
1328
1350
  }
1329
- const Ht = transpose(H);
1351
+ const Ht = transpose(H3);
1330
1352
  const HtW = matMul(Ht, W);
1331
- const N = matMul(HtW, H);
1353
+ const N = matMul(HtW, H3);
1332
1354
  const b = matMul(
1333
1355
  HtW,
1334
1356
  v.map((s) => [s])
@@ -1344,7 +1366,7 @@ function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
1344
1366
  z += dx[2][0];
1345
1367
  cov = Ninv;
1346
1368
  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]);
1369
+ 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
1370
  });
1349
1371
  if (Math.hypot(dx[0][0], dx[1][0], dx[2][0]) < convergenceM) {
1350
1372
  converged = true;
@@ -1738,19 +1760,19 @@ var RtkFloatEngine = class {
1738
1760
  const PPrior = this.P;
1739
1761
  let xi = [...this.x];
1740
1762
  let K = [];
1741
- let H = [];
1763
+ let H3 = [];
1742
1764
  for (let it = 0; it < Math.max(o.updateIterations, 1); it++) {
1743
- H = [];
1765
+ H3 = [];
1744
1766
  const v = [];
1745
1767
  for (const row of rows) {
1746
1768
  const { pred, h } = predict(row, xi);
1747
1769
  let corr = 0;
1748
1770
  for (let j = 0; j < n; j++) corr += h[j] * (xPrior[j] - xi[j]);
1749
1771
  v.push(row.z - pred - corr);
1750
- H.push(h);
1772
+ H3.push(h);
1751
1773
  }
1752
- const Ht = transpose(H);
1753
- const S = matMul(matMul(H, PPrior), Ht);
1774
+ const Ht = transpose(H3);
1775
+ const S = matMul(matMul(H3, PPrior), Ht);
1754
1776
  for (let i = 0; i < rows.length; i++)
1755
1777
  for (let j = 0; j < rows.length; j++) S[i][j] += R[i][j];
1756
1778
  const Sinv = matInv(S);
@@ -1766,7 +1788,7 @@ var RtkFloatEngine = class {
1766
1788
  xi = xPrior.map((s, i) => s + dx[i][0]);
1767
1789
  }
1768
1790
  this.x = xi;
1769
- const KH = matMul(K, H);
1791
+ const KH = matMul(K, H3);
1770
1792
  const IKH = zeros(n, n);
1771
1793
  for (let i = 0; i < n; i++)
1772
1794
  for (let j = 0; j < n; j++) IKH[i][j] = (i === j ? 1 : 0) - KH[i][j];
@@ -2113,6 +2135,66 @@ var ZeroBaselineEngine = class {
2113
2135
  }
2114
2136
  };
2115
2137
 
2138
+ // src/positioning/msm-resolve.ts
2139
+ var LIGHT_MS = C_LIGHT / 1e3;
2140
+ var MAX_FRACTION_MISFIT = 0.25;
2141
+ function predictedRange(eph, timeMs, rx) {
2142
+ let tau = 0.075;
2143
+ for (let i = 0; i < 2; i++) {
2144
+ const tEmit = timeMs - tau * 1e3;
2145
+ const p = computeSatPosition(eph, tEmit);
2146
+ if (!Number.isFinite(p.x)) return null;
2147
+ const rho = Math.hypot(p.x - rx[0], p.y - rx[1], p.z - rx[2]);
2148
+ tau = rho / C_LIGHT;
2149
+ if (i === 1) return rho - C_LIGHT * satClockCorrection(eph, tEmit);
2150
+ }
2151
+ return null;
2152
+ }
2153
+ function moduloRange(sat) {
2154
+ for (const s of sat.signals)
2155
+ if (s.pseudorange !== void 0) return s.pseudorange;
2156
+ for (const s of sat.signals)
2157
+ if (s.phase !== void 0 && s.wavelength > 0)
2158
+ return s.phase * s.wavelength;
2159
+ return null;
2160
+ }
2161
+ function resolveMsmRanges(epoch, opts) {
2162
+ if (!epoch.rangeModulo1ms) return epoch;
2163
+ const cand = [];
2164
+ for (const sat of epoch.observations) {
2165
+ const eph = opts.ephFor(sat.prn);
2166
+ const mod = moduloRange(sat);
2167
+ if (!eph || mod === null) continue;
2168
+ const pred = predictedRange(eph, opts.timeMs, opts.rxEcef);
2169
+ if (pred === null) continue;
2170
+ cand.push({ sat, k: (pred - mod) / LIGHT_MS });
2171
+ }
2172
+ const { rangeModulo1ms: _drop, ...rest } = epoch;
2173
+ if (cand.length === 0) return { ...rest, observations: [] };
2174
+ let sx = 0;
2175
+ let sy = 0;
2176
+ for (const c of cand) {
2177
+ sx += Math.cos(2 * Math.PI * c.k);
2178
+ sy += Math.sin(2 * Math.PI * c.k);
2179
+ }
2180
+ const b = Math.atan2(sy, sx) / (2 * Math.PI);
2181
+ const observations = [];
2182
+ for (const { sat, k } of cand) {
2183
+ const n = Math.round(k - b);
2184
+ if (Math.abs(k - b - n) > MAX_FRACTION_MISFIT) continue;
2185
+ const add = n * LIGHT_MS;
2186
+ observations.push({
2187
+ ...sat,
2188
+ signals: sat.signals.map((s) => ({
2189
+ ...s,
2190
+ ...s.pseudorange !== void 0 ? { pseudorange: s.pseudorange + add } : {},
2191
+ ...s.phase !== void 0 && s.wavelength > 0 ? { phase: s.phase + add / s.wavelength } : {}
2192
+ }))
2193
+ });
2194
+ }
2195
+ return { ...rest, observations };
2196
+ }
2197
+
2116
2198
  // src/navbits/index.ts
2117
2199
  var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
2118
2200
  var SEC_PER_WEEK = 7 * 86400;
@@ -4612,6 +4694,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
4612
4694
  nlWavelength,
4613
4695
  postProcessRtk,
4614
4696
  rcvAntennaRangeM,
4697
+ resolveMsmRanges,
4615
4698
  resolvePppAmbiguities,
4616
4699
  satClockCorrection,
4617
4700
  sbasIonoDeg,
@@ -2,6 +2,7 @@ import { E as Ephemeris } from './nav-DImXUdbp.cjs';
2
2
  import { DopValues } from './orbit.cjs';
3
3
  import { I as IonexGrid, C as ClkFile, S as Sp3File } from './clk-DGVBGRzA.cjs';
4
4
  import { AntexFile } from './antex.cjs';
5
+ import { a as ObsEpoch } from './msm-DWtoTvx2.cjs';
5
6
  import { a as HasOrbitCorrection, b as HasClockCorrection } from './has-BaXnQarR.cjs';
6
7
  import './ephemeris-BAnBLLB9.cjs';
7
8
  import './ecef-CF0uAysr.cjs';
@@ -613,6 +614,23 @@ declare class ZeroBaselineEngine {
613
614
  private processReceiver;
614
615
  }
615
616
 
617
+ interface MsmResolveOptions {
618
+ /** Epoch time, GPS-scale ms (as `obsEpochToDate` returns). */
619
+ timeMs: number;
620
+ /** Approximate receiver ECEF (m) — the station's 1005/1006, a sourcetable
621
+ * position, or a previous fix. Tens of km of error are tolerated. */
622
+ rxEcef: readonly [number, number, number];
623
+ /** Broadcast ephemeris for a PRN at this time, or null. */
624
+ ephFor: (prn: string) => Ephemeris | null;
625
+ }
626
+ /**
627
+ * Add the missing whole light-milliseconds to an MSM1–3 epoch's ranges.
628
+ * Returns the epoch with `rangeModulo1ms` cleared, keeping only satellites
629
+ * whose range could be resolved (an ephemeris, and a prediction consistent
630
+ * with the rest of the epoch). Epochs that aren't modulo-1-ms pass through.
631
+ */
632
+ declare function resolveMsmRanges(epoch: ObsEpoch, opts: MsmResolveOptions): ObsEpoch;
633
+
616
634
  /**
617
635
  * Integer ambiguity resolution: LAMBDA decorrelation + MLAMBDA
618
636
  * integer least-squares search.
@@ -1714,4 +1732,4 @@ declare function sbasProtectionLevels(sats: readonly {
1714
1732
  */
1715
1733
  declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
1716
1734
 
1717
- export { type AntennaOffset, type AppliedOrbitClock, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type Degradation, type DgnssOptions, type DgnssSolution, type EphemerisSource, type FixedPositionResult, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type NetworkFcbResult, type NetworkFcbSummary, type NlArc, type NlFcbResult, type OrbitClockCorrection, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, PppEngine, type PppEphemerisSource, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppFixState, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type ReceiverBias, type ReceiverClockSample, type RtkAntennaConfig, type RtkAntennaModel, type RtkAntennaOffset, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type RtkObsEpoch, type RtkObsInput, type RtkPostProcessOptions, type RtkPostProcessResult, type RtkReceiverAntenna, type RtkTrackPoint, type SatGeom, type SatSample, type SbasCorrectionSource, type SbasIgp, type SbasIonoDelay, SbasProcessor, type SbasSatCorrection, Sp3EphemerisSource, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, ZeroBaselineEngine, type 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 };
1735
+ export { type AntennaOffset, type AppliedOrbitClock, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type Degradation, type DgnssOptions, type DgnssSolution, type EphemerisSource, type FixedPositionResult, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type MsmResolveOptions, type NetworkFcbResult, type NetworkFcbSummary, type NlArc, type NlFcbResult, type OrbitClockCorrection, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, PppEngine, type PppEphemerisSource, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppFixState, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type ReceiverBias, type ReceiverClockSample, type RtkAntennaConfig, type RtkAntennaModel, type RtkAntennaOffset, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type RtkObsEpoch, type RtkObsInput, type RtkPostProcessOptions, type RtkPostProcessResult, type RtkReceiverAntenna, type RtkTrackPoint, type SatGeom, type SatSample, type SbasCorrectionSource, type SbasIgp, type SbasIonoDelay, SbasProcessor, type SbasSatCorrection, Sp3EphemerisSource, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, ZeroBaselineEngine, type ZeroBaselineOptions, antexFreqOfGroup, applyOrbitClock, buildPppAntenna, buildRtkAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, hasToOrbitClock, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, rcvAntennaRangeM, resolveMsmRanges, resolvePppAmbiguities, satClockCorrection, sbasIonoDeg, sbasLongTermDeg, sbasProtectionLevels, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
@@ -2,6 +2,7 @@ import { E as Ephemeris } from './nav-DImXUdbp.js';
2
2
  import { DopValues } from './orbit.js';
3
3
  import { I as IonexGrid, C as ClkFile, S as Sp3File } from './clk-DGVBGRzA.js';
4
4
  import { AntexFile } from './antex.js';
5
+ import { a as ObsEpoch } from './msm-CcQJV3PC.js';
5
6
  import { a as HasOrbitCorrection, b as HasClockCorrection } from './has-BaXnQarR.js';
6
7
  import './ephemeris-BAnBLLB9.js';
7
8
  import './ecef-CF0uAysr.js';
@@ -613,6 +614,23 @@ declare class ZeroBaselineEngine {
613
614
  private processReceiver;
614
615
  }
615
616
 
617
+ interface MsmResolveOptions {
618
+ /** Epoch time, GPS-scale ms (as `obsEpochToDate` returns). */
619
+ timeMs: number;
620
+ /** Approximate receiver ECEF (m) — the station's 1005/1006, a sourcetable
621
+ * position, or a previous fix. Tens of km of error are tolerated. */
622
+ rxEcef: readonly [number, number, number];
623
+ /** Broadcast ephemeris for a PRN at this time, or null. */
624
+ ephFor: (prn: string) => Ephemeris | null;
625
+ }
626
+ /**
627
+ * Add the missing whole light-milliseconds to an MSM1–3 epoch's ranges.
628
+ * Returns the epoch with `rangeModulo1ms` cleared, keeping only satellites
629
+ * whose range could be resolved (an ephemeris, and a prediction consistent
630
+ * with the rest of the epoch). Epochs that aren't modulo-1-ms pass through.
631
+ */
632
+ declare function resolveMsmRanges(epoch: ObsEpoch, opts: MsmResolveOptions): ObsEpoch;
633
+
616
634
  /**
617
635
  * Integer ambiguity resolution: LAMBDA decorrelation + MLAMBDA
618
636
  * integer least-squares search.
@@ -1714,4 +1732,4 @@ declare function sbasProtectionLevels(sats: readonly {
1714
1732
  */
1715
1733
  declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
1716
1734
 
1717
- export { type AntennaOffset, type AppliedOrbitClock, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type Degradation, type DgnssOptions, type DgnssSolution, type EphemerisSource, type FixedPositionResult, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type NetworkFcbResult, type NetworkFcbSummary, type NlArc, type NlFcbResult, type OrbitClockCorrection, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, PppEngine, type PppEphemerisSource, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppFixState, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type ReceiverBias, type ReceiverClockSample, type RtkAntennaConfig, type RtkAntennaModel, type RtkAntennaOffset, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type RtkObsEpoch, type RtkObsInput, type RtkPostProcessOptions, type RtkPostProcessResult, type RtkReceiverAntenna, type RtkTrackPoint, type SatGeom, type SatSample, type SbasCorrectionSource, type SbasIgp, type SbasIonoDelay, SbasProcessor, type SbasSatCorrection, Sp3EphemerisSource, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, ZeroBaselineEngine, type 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 };
1735
+ export { type AntennaOffset, type AppliedOrbitClock, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type Degradation, type DgnssOptions, type DgnssSolution, type EphemerisSource, type FixedPositionResult, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type MsmResolveOptions, type NetworkFcbResult, type NetworkFcbSummary, type NlArc, type NlFcbResult, type OrbitClockCorrection, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, PppEngine, type PppEphemerisSource, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppFixState, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type ReceiverBias, type ReceiverClockSample, type RtkAntennaConfig, type RtkAntennaModel, type RtkAntennaOffset, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type RtkObsEpoch, type RtkObsInput, type RtkPostProcessOptions, type RtkPostProcessResult, type RtkReceiverAntenna, type RtkTrackPoint, type SatGeom, type SatSample, type SbasCorrectionSource, type SbasIgp, type SbasIonoDelay, SbasProcessor, type SbasSatCorrection, Sp3EphemerisSource, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, ZeroBaselineEngine, type ZeroBaselineOptions, antexFreqOfGroup, applyOrbitClock, buildPppAntenna, buildRtkAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, hasToOrbitClock, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, rcvAntennaRangeM, resolveMsmRanges, resolvePppAmbiguities, satClockCorrection, sbasIonoDeg, sbasLongTermDeg, sbasProtectionLevels, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
@@ -28,6 +28,7 @@ import {
28
28
  nlWavelength,
29
29
  postProcessRtk,
30
30
  rcvAntennaRangeM,
31
+ resolveMsmRanges,
31
32
  resolvePppAmbiguities,
32
33
  satClockCorrection,
33
34
  sbasIonoDeg,
@@ -40,9 +41,9 @@ import {
40
41
  sunEcef,
41
42
  toRtkEpoch,
42
43
  wlWavelength
43
- } from "./chunk-CLNLOYBX.js";
44
+ } from "./chunk-K3GLUWZI.js";
44
45
  import "./chunk-WQX354KD.js";
45
- import "./chunk-5OWRCF56.js";
46
+ import "./chunk-7T5DLVZL.js";
46
47
  import "./chunk-HVXYFUCB.js";
47
48
  import "./chunk-CVCTRIWU.js";
48
49
  import "./chunk-37QNKGTC.js";
@@ -82,6 +83,7 @@ export {
82
83
  nlWavelength,
83
84
  postProcessRtk,
84
85
  rcvAntennaRangeM,
86
+ resolveMsmRanges,
85
87
  resolvePppAmbiguities,
86
88
  satClockCorrection,
87
89
  sbasIonoDeg,
package/dist/rinex.cjs CHANGED
@@ -32,6 +32,7 @@ __export(rinex_exports, {
32
32
  fmtD: () => fmtD,
33
33
  fmtF: () => fmtF,
34
34
  hdrLine: () => hdrLine,
35
+ modalIntervalS: () => modalIntervalS,
35
36
  padL: () => padL,
36
37
  padR: () => padR,
37
38
  parseBiasSinex: () => parseBiasSinex,
@@ -2600,6 +2601,21 @@ function writeRinexObsBlob(header, epochs, obsTypes) {
2600
2601
  comment: "Merged from multiple RINEX files"
2601
2602
  });
2602
2603
  }
2604
+ function modalIntervalS(epochs) {
2605
+ const counts = /* @__PURE__ */ new Map();
2606
+ for (let i = 1; i < epochs.length; i++) {
2607
+ const d = Math.round(epochs[i].time - epochs[i - 1].time);
2608
+ if (d > 0) counts.set(d, (counts.get(d) ?? 0) + 1);
2609
+ }
2610
+ let best = null;
2611
+ let bestN = 0;
2612
+ for (const [d, n] of counts)
2613
+ if (n > bestN || n === bestN && best !== null && d < best) {
2614
+ best = d;
2615
+ bestN = n;
2616
+ }
2617
+ return best === null ? null : best / 1e3;
2618
+ }
2603
2619
  async function writeModernObsBlob(header, epochs, obsTypes, opts) {
2604
2620
  if (epochs.length === 0) return new Blob([], { type: "application/gzip" });
2605
2621
  const sink = createGzipLineSink();
@@ -2646,11 +2662,9 @@ async function writeModernObsBlob(header, epochs, obsTypes, opts) {
2646
2662
  "TIME OF LAST OBS"
2647
2663
  )
2648
2664
  );
2649
- if (epochs.length >= 2) {
2650
- const interval = (epochs[1].time - epochs[0].time) / 1e3;
2651
- if (interval > 0 && interval < 3600) {
2652
- sink.push(hdrLine(fmtF(interval, 10, 3), "INTERVAL"));
2653
- }
2665
+ const interval = modalIntervalS(epochs);
2666
+ if (interval !== null && interval < 3600) {
2667
+ sink.push(hdrLine(fmtF(interval, 10, 3), "INTERVAL"));
2654
2668
  }
2655
2669
  if (header.glonassSlots && Object.keys(header.glonassSlots).length > 0) {
2656
2670
  const entries = Object.entries(header.glonassSlots).sort(
@@ -2916,6 +2930,7 @@ function writeRinex4ObsBlob(header, epochs, obsTypes) {
2916
2930
  fmtD,
2917
2931
  fmtF,
2918
2932
  hdrLine,
2933
+ modalIntervalS,
2919
2934
  padL,
2920
2935
  padR,
2921
2936
  parseBiasSinex,
package/dist/rinex.d.cts CHANGED
@@ -258,6 +258,15 @@ interface CompactEpoch {
258
258
  * the Float64Array index order for each system.
259
259
  */
260
260
  declare function writeRinexObsBlob(header: RinexHeader, epochs: CompactEpoch[], obsTypes: Map<string, string[]>): Promise<Blob>;
261
+ /**
262
+ * The nominal observation interval (s): the most common spacing between
263
+ * consecutive epochs. The first difference alone mis-states it whenever the
264
+ * file opens with an irregular gap (a late second epoch, a burst). Null with
265
+ * fewer than two epochs.
266
+ */
267
+ declare function modalIntervalS(epochs: readonly {
268
+ time: number;
269
+ }[]): number | null;
261
270
  /** Shared RINEX 3/4 implementation (also used by obs-writer-v4.ts). */
262
271
  declare function writeModernObsBlob(header: RinexHeader, epochs: CompactEpoch[], obsTypes: Map<string, string[]>, opts: {
263
272
  version: string;
@@ -311,4 +320,4 @@ declare function createGzipLineSink(): {
311
320
  */
312
321
  declare function stationHeaderLines(header: RinexHeader): string[];
313
322
 
314
- export { type BiasRecord, type BiasSinex, type CompactEpoch, type CrxField, type DiffState, EMPTY_WARNINGS, Ephemeris, type Nav4Input, NavHeader, NavResult, RinexCnavEphemeris, RinexHeader, type RinexWarning, type RinexWarnings, WarningAccumulator, type WarningSeverity, type WriteCrxOptions, biasMetres, createGzipLineSink, crxDecompress, crxRepair, findSatBias, fmtD, fmtF, hdrLine, padL, padR, parseBiasSinex, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob };
323
+ export { type BiasRecord, type BiasSinex, type CompactEpoch, type CrxField, type DiffState, EMPTY_WARNINGS, Ephemeris, type Nav4Input, NavHeader, NavResult, RinexCnavEphemeris, RinexHeader, type RinexWarning, type RinexWarnings, WarningAccumulator, type WarningSeverity, type WriteCrxOptions, biasMetres, createGzipLineSink, crxDecompress, crxRepair, findSatBias, fmtD, fmtF, hdrLine, modalIntervalS, padL, padR, parseBiasSinex, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob };
package/dist/rinex.d.ts CHANGED
@@ -258,6 +258,15 @@ interface CompactEpoch {
258
258
  * the Float64Array index order for each system.
259
259
  */
260
260
  declare function writeRinexObsBlob(header: RinexHeader, epochs: CompactEpoch[], obsTypes: Map<string, string[]>): Promise<Blob>;
261
+ /**
262
+ * The nominal observation interval (s): the most common spacing between
263
+ * consecutive epochs. The first difference alone mis-states it whenever the
264
+ * file opens with an irregular gap (a late second epoch, a burst). Null with
265
+ * fewer than two epochs.
266
+ */
267
+ declare function modalIntervalS(epochs: readonly {
268
+ time: number;
269
+ }[]): number | null;
261
270
  /** Shared RINEX 3/4 implementation (also used by obs-writer-v4.ts). */
262
271
  declare function writeModernObsBlob(header: RinexHeader, epochs: CompactEpoch[], obsTypes: Map<string, string[]>, opts: {
263
272
  version: string;
@@ -311,4 +320,4 @@ declare function createGzipLineSink(): {
311
320
  */
312
321
  declare function stationHeaderLines(header: RinexHeader): string[];
313
322
 
314
- export { type BiasRecord, type BiasSinex, type CompactEpoch, type CrxField, type DiffState, EMPTY_WARNINGS, Ephemeris, type Nav4Input, NavHeader, NavResult, RinexCnavEphemeris, RinexHeader, type RinexWarning, type RinexWarnings, WarningAccumulator, type WarningSeverity, type WriteCrxOptions, biasMetres, createGzipLineSink, crxDecompress, crxRepair, findSatBias, fmtD, fmtF, hdrLine, padL, padR, parseBiasSinex, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob };
323
+ export { type BiasRecord, type BiasSinex, type CompactEpoch, type CrxField, type DiffState, EMPTY_WARNINGS, Ephemeris, type Nav4Input, NavHeader, NavResult, RinexCnavEphemeris, RinexHeader, type RinexWarning, type RinexWarnings, WarningAccumulator, type WarningSeverity, type WriteCrxOptions, biasMetres, createGzipLineSink, crxDecompress, crxRepair, findSatBias, fmtD, fmtF, hdrLine, modalIntervalS, padL, padR, parseBiasSinex, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob };
package/dist/rinex.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  fmtD,
8
8
  fmtF,
9
9
  hdrLine,
10
+ modalIntervalS,
10
11
  padL,
11
12
  padR,
12
13
  parseBiasSinex,
@@ -20,7 +21,7 @@ import {
20
21
  writeRinexNav,
21
22
  writeRinexNav4,
22
23
  writeRinexObsBlob
23
- } from "./chunk-6SWJTN6H.js";
24
+ } from "./chunk-56HM5B6T.js";
24
25
  import {
25
26
  SYSTEM_ORDER,
26
27
  crxDecompress,
@@ -52,6 +53,7 @@ export {
52
53
  fmtD,
53
54
  fmtF,
54
55
  hdrLine,
56
+ modalIntervalS,
55
57
  padL,
56
58
  padR,
57
59
  parseBiasSinex,
package/dist/rtcm3.cjs CHANGED
@@ -1287,12 +1287,14 @@ function satPrn(sys, satIdx1) {
1287
1287
  const num = sys === "S" ? satIdx1 + 119 : satIdx1;
1288
1288
  return `${prefix}${String(num).padStart(2, "0")}`;
1289
1289
  }
1290
- function decodeMsmFull(frame) {
1290
+ function decodeMsmFull(frame, opts = {}) {
1291
1291
  const type = frame.messageType;
1292
1292
  const sys = msmSystem(type);
1293
1293
  if (!sys) return null;
1294
1294
  const variant = type % 10;
1295
- if (variant < 4 || variant > 7) return null;
1295
+ if (variant < 1 || variant > 7) return null;
1296
+ const compact = variant <= 3;
1297
+ if (compact && !opts.compactMsm) return null;
1296
1298
  const payload = frame.payload;
1297
1299
  if (!payload || payload.length < 10) return null;
1298
1300
  try {
@@ -1337,7 +1339,9 @@ function decodeMsmFull(frame) {
1337
1339
  const rrmod = new Float64Array(numSat);
1338
1340
  const extsat = new Int8Array(numSat);
1339
1341
  const rdop = new Float64Array(numSat);
1340
- if (variant === 4 || variant === 6) {
1342
+ if (compact) {
1343
+ for (let j = 0; j < numSat; j++) rrmod[j] = bits.readU(10) / 1024;
1344
+ } else if (variant === 4 || variant === 6) {
1341
1345
  for (let j = 0; j < numSat; j++) rrint[j] = bits.readU(8);
1342
1346
  for (let j = 0; j < numSat; j++) rrmod[j] = bits.readU(10) / 1024;
1343
1347
  } else {
@@ -1363,7 +1367,18 @@ function decodeMsmFull(frame) {
1363
1367
  psr.fill(-1e30);
1364
1368
  cp.fill(-1e30);
1365
1369
  dop.fill(-1e30);
1366
- if (variant === 4) {
1370
+ if (variant === 1) {
1371
+ for (let i = 0; i < activeCells; i++) psr[i] = bits.readS(15) / 2 ** 24;
1372
+ } else if (variant === 2) {
1373
+ for (let i = 0; i < activeCells; i++) cp[i] = bits.readS(22) / 2 ** 29;
1374
+ for (let i = 0; i < activeCells; i++) ll[i] = bits.readU(4);
1375
+ for (let i = 0; i < activeCells; i++) hc[i] = bits.readU(1);
1376
+ } else if (variant === 3) {
1377
+ for (let i = 0; i < activeCells; i++) psr[i] = bits.readS(15) / 2 ** 24;
1378
+ for (let i = 0; i < activeCells; i++) cp[i] = bits.readS(22) / 2 ** 29;
1379
+ for (let i = 0; i < activeCells; i++) ll[i] = bits.readU(4);
1380
+ for (let i = 0; i < activeCells; i++) hc[i] = bits.readU(1);
1381
+ } else if (variant === 4) {
1367
1382
  for (let i = 0; i < activeCells; i++) psr[i] = bits.readS(15) / 2 ** 24;
1368
1383
  for (let i = 0; i < activeCells; i++) cp[i] = bits.readS(22) / 2 ** 29;
1369
1384
  for (let i = 0; i < activeCells; i++) ll[i] = bits.readU(4);
@@ -1439,8 +1454,10 @@ function decodeMsmFull(frame) {
1439
1454
  if (cnrVal > 0) {
1440
1455
  signal.cn0 = cnrVal;
1441
1456
  }
1442
- signal.lockTime = lockTimeSec(type, ll[cellIdx]);
1443
- signal.halfCycle = hc[cellIdx] === 1;
1457
+ if (variant !== 1) {
1458
+ signal.lockTime = lockTimeSec(type, ll[cellIdx]);
1459
+ signal.halfCycle = hc[cellIdx] === 1;
1460
+ }
1444
1461
  signals.push(signal);
1445
1462
  cellIdx++;
1446
1463
  }
@@ -1452,7 +1469,8 @@ function decodeMsmFull(frame) {
1452
1469
  messageType: type,
1453
1470
  epochMs,
1454
1471
  system: sys,
1455
- observations
1472
+ observations,
1473
+ ...compact ? { rangeModulo1ms: true } : {}
1456
1474
  };
1457
1475
  } catch (err) {
1458
1476
  reportDecodeError("msm", err);
@@ -1606,8 +1624,8 @@ function decodeLegacyObs(frame) {
1606
1624
  }
1607
1625
  return { messageType: type, epochMs, system, observations };
1608
1626
  }
1609
- function decodeObs(frame) {
1610
- return decodeMsmFull(frame) ?? decodeLegacyObs(frame);
1627
+ function decodeObs(frame, opts = {}) {
1628
+ return decodeMsmFull(frame, opts) ?? decodeLegacyObs(frame);
1611
1629
  }
1612
1630
 
1613
1631
  // src/rtcm3/ssr.ts