gnss-js 2.9.0 → 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.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 };
@@ -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,
@@ -2134,6 +2135,66 @@ var ZeroBaselineEngine = class {
2134
2135
  }
2135
2136
  };
2136
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
+
2137
2198
  // src/navbits/index.ts
2138
2199
  var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
2139
2200
  var SEC_PER_WEEK = 7 * 86400;
@@ -4633,6 +4694,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
4633
4694
  nlWavelength,
4634
4695
  postProcessRtk,
4635
4696
  rcvAntennaRangeM,
4697
+ resolveMsmRanges,
4636
4698
  resolvePppAmbiguities,
4637
4699
  satClockCorrection,
4638
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,7 +41,7 @@ import {
40
41
  sunEcef,
41
42
  toRtkEpoch,
42
43
  wlWavelength
43
- } from "./chunk-NOGA5KD7.js";
44
+ } from "./chunk-K3GLUWZI.js";
44
45
  import "./chunk-WQX354KD.js";
45
46
  import "./chunk-7T5DLVZL.js";
46
47
  import "./chunk-HVXYFUCB.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
package/dist/rtcm3.d.cts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { b as Rtcm3Frame, E as EphemerisInfo } from './ephemeris-BAnBLLB9.cjs';
2
2
  export { B as BitReader, R as Rtcm3DebugHandler, a as Rtcm3Decoder, d as decodeEphemeris, r as readString, c as reportDecodeError, s as setRtcm3DebugHandler } from './ephemeris-BAnBLLB9.cjs';
3
+ import { a as ObsEpoch, O as ObsDecodeOptions } from './msm-DWtoTvx2.cjs';
4
+ export { b as ObsSatObs, c as ObsSignal, d as decodeMsmFull, o as obsEpochToDate, r as resetGloFreqCache, s as setGloFreqNumber } from './msm-DWtoTvx2.cjs';
3
5
 
4
6
  /** One code bias for a specific signal (DF380/DF381 + DF383). */
5
7
  interface SsrCodeBias {
@@ -116,91 +118,6 @@ interface IgsSsrIonoLayer {
116
118
  */
117
119
  declare function decodeIgsSsr(frame: Rtcm3Frame): IgsSsrMessage | null;
118
120
 
119
- /**
120
- * Full RTCM3 MSM4-7 decoder — extracts pseudorange, carrier phase,
121
- * Doppler, and C/N0 for every satellite × signal cell.
122
- *
123
- * Reference: BNC 2.13.4 RTCM3Decoder.cpp (BKG) and RTCM 10403.3 §3.5.
124
- *
125
- * Only MSM types 4-7 are decoded (full data). Types 1-3 carry partial
126
- * data and are skipped.
127
- */
128
-
129
- /** Single signal observation for one satellite */
130
- interface ObsSignal {
131
- /** RINEX 2-char obs code, e.g. "1C", "5X" */
132
- rinexCode: string;
133
- /** Pseudorange in meters (undefined if invalid) */
134
- pseudorange?: number;
135
- /** Carrier phase in cycles (undefined if invalid) */
136
- phase?: number;
137
- /** Doppler in Hz (undefined if invalid) */
138
- doppler?: number;
139
- /** Signal-to-noise ratio in dB-Hz */
140
- cn0?: number;
141
- /** Lock time in seconds */
142
- lockTime?: number;
143
- /** Half-cycle ambiguity indicator */
144
- halfCycle?: boolean;
145
- /** Wavelength used (meters) — needed for RINEX phase */
146
- wavelength: number;
147
- }
148
- /** All observations for one satellite in one epoch */
149
- interface ObsSatObs {
150
- /** PRN string, e.g. "G01", "R15", "E04" */
151
- prn: string;
152
- /** System letter: G, R, E, C, J, I, S */
153
- system: string;
154
- /** Signal observations */
155
- signals: ObsSignal[];
156
- }
157
- /**
158
- * One decoded observation epoch. Originally the MSM4–7 output shape, now the
159
- * neutral shape every RTCM3 obs source fills: MSM (`decodeMsmFull`), legacy
160
- * 1001–1012 (`decodeLegacyObs`), via the `decodeObs` dispatcher.
161
- */
162
- interface ObsEpoch {
163
- /** Message type (1074, 1077, etc.) */
164
- messageType: number;
165
- /** Epoch time as GPS milliseconds of week (or GLONASS day-of-week ms, BDS SOW) */
166
- epochMs: number;
167
- /** System letter */
168
- system: string;
169
- /** Observations per satellite */
170
- observations: ObsSatObs[];
171
- }
172
- /**
173
- * Decode a single RTCM3 MSM4-7 frame into full observations.
174
- * Returns null for non-MSM or MSM1-3 frames.
175
- */
176
- declare function decodeMsmFull(frame: Rtcm3Frame): ObsEpoch | null;
177
- /**
178
- * Convert an observation epoch timestamp to a Date.
179
- * For GPS/Galileo/BDS/QZSS: epochMs is ms of GPS week.
180
- * For GLONASS: epochMs is day-of-week × 86400000 + ms of day (UTC+3).
181
- *
182
- * Requires an approximate reference time to resolve the current week.
183
- */
184
- /**
185
- * NOTE: returns a GPS-scale Date — the clock-face convention used by
186
- * the RINEX parser and every consumer of epoch milliseconds in this
187
- * library (satellite propagation, SPP, RINEX epoch tags). It is NOT
188
- * UTC: converting to UTC here put every downstream satellite position
189
- * 18 s (the current leap-second count) off — a ±14 km per-satellite
190
- * range error that scattered live SPP solutions by kilometres.
191
- */
192
- declare function obsEpochToDate(sys: string, epochMs: number, refTime?: Date): Date;
193
- /**
194
- * Set a GLONASS FDMA frequency number (k = -7..+13) for a slot (1-24),
195
- * e.g. from a decoded 1020 ephemeris. MSM4/6 carry no DF419, so without
196
- * this the wavelength — and hence carrier phase — stays unknown.
197
- */
198
- declare function setGloFreqNumber(slot: number, k: number): void;
199
- /**
200
- * Reset the GLONASS frequency number cache (e.g., when switching mountpoints).
201
- */
202
- declare function resetGloFreqCache(): void;
203
-
204
121
  /**
205
122
  * Decode a legacy RTCM3 observation frame (1001–1004 GPS, 1009–1012 GLONASS)
206
123
  * into an {@link ObsEpoch}, or null for any other message type.
@@ -208,13 +125,14 @@ declare function resetGloFreqCache(): void;
208
125
  declare function decodeLegacyObs(frame: Rtcm3Frame): ObsEpoch | null;
209
126
  /**
210
127
  * Decode any RTCM3 observation frame into an {@link ObsEpoch}: MSM4–7 first
211
- * ({@link decodeMsmFull}), then the legacy fixed-layout obs (1001–1004 GPS,
128
+ * ({@link decodeMsmFull}; MSM1–3 too with `opts.compactMsm` — resolve those
129
+ * with `resolveMsmRanges` before use), then the legacy fixed-layout obs (1001–1004 GPS,
212
130
  * 1009–1012 GLONASS) via {@link decodeLegacyObs}. Returns null for any
213
131
  * non-observation message. This is the single obs entry point every consumer
214
132
  * (stream stats, live SPP/QC/RTK, station monitor) should call, so a new obs
215
133
  * source is added here once rather than at every call site.
216
134
  */
217
- declare function decodeObs(frame: Rtcm3Frame): ObsEpoch | null;
135
+ declare function decodeObs(frame: Rtcm3Frame, opts?: ObsDecodeOptions): ObsEpoch | null;
218
136
 
219
137
  /** One announced message in the station's schedule (DF055/DF056/DF057). */
220
138
  interface SystemParamsMessage {
@@ -319,4 +237,4 @@ declare function rtcm3Constellation(msgType: number): string | null;
319
237
  declare function createStreamStats(): StreamStats;
320
238
  declare function updateStreamStats(stats: StreamStats, frames: Rtcm3Frame[], rawBytes: number): void;
321
239
 
322
- export { EphemerisInfo, type IgsSsrIonoLayer, type IgsSsrMessage, type MessageTypeStats, type ObsEpoch, type ObsSatObs, type ObsSignal, RTCM3_MESSAGE_NAMES, Rtcm3Frame, type SatCn0, type SignalCn0, type SsrCodeBias, type SsrKind, type SsrMessage, type SsrPhaseBias, type SsrSatCorrection, type StationMeta, type StreamStats, type SystemParams, type SystemParamsMessage, createStationMeta, createStreamStats, decodeIgsSsr, decodeLegacyObs, decodeMsmFull, decodeObs, decodeSsr, decodeSystemParams, isSsrMessage, obsEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats };
240
+ export { EphemerisInfo, type IgsSsrIonoLayer, type IgsSsrMessage, type MessageTypeStats, ObsDecodeOptions, ObsEpoch, RTCM3_MESSAGE_NAMES, Rtcm3Frame, type SatCn0, type SignalCn0, type SsrCodeBias, type SsrKind, type SsrMessage, type SsrPhaseBias, type SsrSatCorrection, type StationMeta, type StreamStats, type SystemParams, type SystemParamsMessage, createStationMeta, createStreamStats, decodeIgsSsr, decodeLegacyObs, decodeObs, decodeSsr, decodeSystemParams, isSsrMessage, rtcm3Constellation, updateStationMeta, updateStreamStats };