gnss-js 1.28.0 → 1.30.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.
@@ -10,8 +10,9 @@ import {
10
10
  getEnuDifference
11
11
  } from "./chunk-37QNKGTC.js";
12
12
  import {
13
+ clkBias,
13
14
  sp3Position
14
- } from "./chunk-4KMWWCUR.js";
15
+ } from "./chunk-GPEP67TG.js";
15
16
  import {
16
17
  C_LIGHT,
17
18
  OMEGA_E
@@ -1582,7 +1583,7 @@ function sagnacRotate(x, y, z, travelS) {
1582
1583
  const s = Math.sin(theta);
1583
1584
  return [x * c + y * s, -x * s + y * c, z];
1584
1585
  }
1585
- function satStateAtEmission(sp3, prn, recvTimeMs, rcv) {
1586
+ function satStateAtEmission(sp3, prn, recvTimeMs, rcv, clk) {
1586
1587
  let travelS = 0.075;
1587
1588
  let sx = 0;
1588
1589
  let sy = 0;
@@ -1601,7 +1602,7 @@ function satStateAtEmission(sp3, prn, recvTimeMs, rcv) {
1601
1602
  const vz = (pPlus.z - pMinus.z) / 1;
1602
1603
  velDotPos = p.x * vx + p.y * vy + p.z * vz;
1603
1604
  }
1604
- clkS = p.clk;
1605
+ clkS = (clk && clkBias(clk, prn, tEmit)) ?? p.clk;
1605
1606
  const [rx, ry, rz] = sagnacRotate(p.x, p.y, p.z, travelS);
1606
1607
  sx = rx;
1607
1608
  sy = ry;
@@ -1620,9 +1621,9 @@ function satStateAtEmission(sp3, prn, recvTimeMs, rcv) {
1620
1621
  var IDX_X = 0;
1621
1622
  var IDX_Y = 1;
1622
1623
  var IDX_Z = 2;
1623
- var IDX_CLK = 3;
1624
- var IDX_ZWD = 4;
1625
- var NBASE = 5;
1624
+ var IDX_ZWD = 3;
1625
+ var NBASE = 4;
1626
+ var CLK_VAR = 1e10;
1626
1627
  function solvePpp(epochs, sp3, opts) {
1627
1628
  const elevMask = (opts.elevationMaskDeg ?? 10) * Math.PI / 180;
1628
1629
  const codeSigma = opts.codeSigma ?? 3;
@@ -1633,8 +1634,6 @@ function solvePpp(epochs, sp3, opts) {
1633
1634
  opts.aprioriPos[0],
1634
1635
  opts.aprioriPos[1],
1635
1636
  opts.aprioriPos[2],
1636
- 0,
1637
- // receiver clock (m)
1638
1637
  0.1
1639
1638
  // zenith wet delay (m) a priori
1640
1639
  ];
@@ -1645,8 +1644,9 @@ function solvePpp(epochs, sp3, opts) {
1645
1644
  P[IDX_X][IDX_X] = 100 * 100;
1646
1645
  P[IDX_Y][IDX_Y] = 100 * 100;
1647
1646
  P[IDX_Z][IDX_Z] = 100 * 100;
1648
- P[IDX_CLK][IDX_CLK] = 1e10;
1649
1647
  P[IDX_ZWD][IDX_ZWD] = 0.5 * 0.5;
1648
+ const clkIdx = /* @__PURE__ */ new Map();
1649
+ const clockSeeded = /* @__PURE__ */ new Set();
1650
1650
  const ambIdx = /* @__PURE__ */ new Map();
1651
1651
  const lastSeen = /* @__PURE__ */ new Map();
1652
1652
  const mwState = /* @__PURE__ */ new Map();
@@ -1687,8 +1687,18 @@ function solvePpp(epochs, sp3, opts) {
1687
1687
  if (i === idx) ambIdx.delete(prn);
1688
1688
  else if (i > idx) ambIdx.set(prn, i - 1);
1689
1689
  }
1690
+ for (const [sys, i] of clkIdx) {
1691
+ if (i > idx) clkIdx.set(sys, i - 1);
1692
+ }
1693
+ };
1694
+ const clockOf = (sys) => {
1695
+ let i = clkIdx.get(sys);
1696
+ if (i === void 0) {
1697
+ i = growState(0, CLK_VAR);
1698
+ clkIdx.set(sys, i);
1699
+ }
1700
+ return i;
1690
1701
  };
1691
- let clockSeeded = false;
1692
1702
  const series = [];
1693
1703
  const truth = opts.groundTruth;
1694
1704
  const t0 = epochs[0]?.timeMs ?? 0;
@@ -1696,7 +1706,7 @@ function solvePpp(epochs, sp3, opts) {
1696
1706
  let convergedStreak = 0;
1697
1707
  for (let ei = 0; ei < epochs.length; ei++) {
1698
1708
  const epoch = epochs[ei];
1699
- P[IDX_CLK][IDX_CLK] = 1e10;
1709
+ for (const ci of clkIdx.values()) P[ci][ci] = CLK_VAR;
1700
1710
  P[IDX_ZWD][IDX_ZWD] += ztdQ * (ei > 0 ? 1 : 0);
1701
1711
  const rcv = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
1702
1712
  const [latDeg, , heightM] = ecefToGeodetic(rcv[0], rcv[1], rcv[2]);
@@ -1706,7 +1716,7 @@ function solvePpp(epochs, sp3, opts) {
1706
1716
  for (const o of epoch.obs) {
1707
1717
  if (o.c1 === 0 || o.c2 === 0 || o.l1 === 0 || o.l2 === 0) continue;
1708
1718
  const slip = detectSlip(o, ei);
1709
- const sat = satStateAtEmission(sp3, o.prn, epoch.timeMs, rcv);
1719
+ const sat = satStateAtEmission(sp3, o.prn, epoch.timeMs, rcv, opts.clk);
1710
1720
  if (!sat) continue;
1711
1721
  const [elRad, azRad] = getAer(
1712
1722
  sat.state.x,
@@ -1760,21 +1770,29 @@ function solvePpp(epochs, sp3, opts) {
1760
1770
  });
1761
1771
  }
1762
1772
  const zwd0 = x[IDX_ZWD];
1763
- if (vis.length > 0) {
1764
- if (!clockSeeded) {
1765
- const s = vis.map(
1766
- (v) => v.pIf - (v.range - v.satClkM + v.tropoFixed + zwd0 * v.mw)
1767
- ).sort((a, b) => a - b);
1768
- x[IDX_CLK] = s[Math.floor(s.length / 2)];
1769
- clockSeeded = true;
1773
+ const bySys = /* @__PURE__ */ new Map();
1774
+ for (const v of vis) {
1775
+ const sys = v.prn[0];
1776
+ const r = v.pIf - (v.range - v.satClkM + v.tropoFixed + zwd0 * v.mw);
1777
+ const arr = bySys.get(sys);
1778
+ if (arr) arr.push(r);
1779
+ else bySys.set(sys, [r]);
1780
+ }
1781
+ for (const [sys, res] of bySys) {
1782
+ const ci = clockOf(sys);
1783
+ if (!clockSeeded.has(sys)) {
1784
+ res.sort((a, b) => a - b);
1785
+ x[ci] = res[Math.floor(res.length / 2)];
1786
+ clockSeeded.add(sys);
1770
1787
  }
1771
- P[IDX_CLK][IDX_CLK] = 1e10;
1788
+ P[ci][ci] = CLK_VAR;
1772
1789
  }
1773
1790
  let nSats = 0;
1774
1791
  let sumPhaseResSq = 0;
1775
1792
  let nPhaseRes = 0;
1776
1793
  for (const v of vis) {
1777
- const common = v.range + x[IDX_CLK] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
1794
+ const ci = clockOf(v.prn[0]);
1795
+ const common = v.range + x[ci] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
1778
1796
  let ai = ambIdx.get(v.prn);
1779
1797
  if (ai === void 0 || v.slip) {
1780
1798
  const bInit = v.lIf - (common + v.windupM);
@@ -1799,13 +1817,13 @@ function solvePpp(epochs, sp3, opts) {
1799
1817
  h[IDX_X] = -v.e[0];
1800
1818
  h[IDX_Y] = -v.e[1];
1801
1819
  h[IDX_Z] = -v.e[2];
1802
- h[IDX_CLK] = 1;
1820
+ h[ci] = 1;
1803
1821
  h[IDX_ZWD] = v.mw;
1804
1822
  const innov = v.pIf - common;
1805
1823
  if (Math.abs(innov) < 1e6) ekfUpdateScalar(x, P, h, innov, rCode, 4);
1806
1824
  }
1807
1825
  {
1808
- const commonPh = v.range + x[IDX_CLK] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
1826
+ const commonPh = v.range + x[ci] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
1809
1827
  const innov = v.lIf - (commonPh + v.windupM + x[ai]);
1810
1828
  if (Math.abs(innov) > 0.15) {
1811
1829
  x[ai] = v.lIf - (commonPh + v.windupM);
@@ -1819,7 +1837,7 @@ function solvePpp(epochs, sp3, opts) {
1819
1837
  h[IDX_X] = -v.e[0];
1820
1838
  h[IDX_Y] = -v.e[1];
1821
1839
  h[IDX_Z] = -v.e[2];
1822
- h[IDX_CLK] = 1;
1840
+ h[ci] = 1;
1823
1841
  h[IDX_ZWD] = v.mw;
1824
1842
  h[ai] = 1;
1825
1843
  if (ekfUpdateScalar(x, P, h, innov, rPhase, 4)) {
@@ -114,7 +114,69 @@ function sp3Position(sp3, prn, tMs, order = 9) {
114
114
  return { x, y, z, clk };
115
115
  }
116
116
 
117
+ // src/rinex/clk.ts
118
+ var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
119
+ function parseClk(text) {
120
+ const sats = {};
121
+ let inData = false;
122
+ for (const line of text.split("\n")) {
123
+ if (!inData) {
124
+ if (line.includes("END OF HEADER")) inData = true;
125
+ continue;
126
+ }
127
+ if (line.length < 3 || line[0] !== "A" || line[1] !== "S") continue;
128
+ const p = line.slice(2).trim().split(/\s+/);
129
+ if (p.length < 9) continue;
130
+ const prn = p[0];
131
+ const y = +p[1];
132
+ const mo = +p[2];
133
+ const d = +p[3];
134
+ const h = +p[4];
135
+ const mi = +p[5];
136
+ const s = +p[6];
137
+ const bias = +p[8];
138
+ if (!Number.isFinite(bias)) continue;
139
+ const t = Date.UTC(y, mo - 1, d, h, mi, Math.floor(s)) + s % 1 * 1e3;
140
+ let e = sats[prn];
141
+ if (!e) {
142
+ e = { t: [], c: [] };
143
+ sats[prn] = e;
144
+ }
145
+ e.t.push(t);
146
+ e.c.push(bias);
147
+ }
148
+ let intervalSec = 30;
149
+ for (const e of Object.values(sats)) {
150
+ if (e.t.length >= 2) {
151
+ intervalSec = Math.round((e.t[1] - e.t[0]) / 1e3);
152
+ break;
153
+ }
154
+ }
155
+ return { sats, intervalSec };
156
+ }
157
+ function clkBias(clk, prn, tMs) {
158
+ const e = clk.sats[prn];
159
+ if (!e || e.t.length === 0) return null;
160
+ const { t, c } = e;
161
+ let lo = 0;
162
+ let hi = t.length - 1;
163
+ if (tMs < t[0] || tMs > t[hi]) return null;
164
+ while (lo < hi) {
165
+ const mid = lo + hi + 1 >> 1;
166
+ if (t[mid] <= tMs) lo = mid;
167
+ else hi = mid - 1;
168
+ }
169
+ if (t[lo] === tMs || lo === t.length - 1) return c[lo];
170
+ const t0 = t[lo];
171
+ const t1 = t[lo + 1];
172
+ if (t1 - t0 > clk.intervalSec * 1e3 * 4) return null;
173
+ const f = (tMs - t0) / (t1 - t0);
174
+ return c[lo] + f * (c[lo + 1] - c[lo]);
175
+ }
176
+
117
177
  export {
118
178
  parseSp3,
119
- sp3Position
179
+ sp3Position,
180
+ parseClk,
181
+ clkBias
120
182
  };
@@ -67,4 +67,30 @@ declare function sp3Position(sp3: Sp3File, prn: string, tMs: number, order?: num
67
67
  clk: number | null;
68
68
  } | null;
69
69
 
70
- export { type IonexGrid as I, type Sp3File as S, type Sp3Sample as a, parseSp3 as b, parseIonex as p, sp3Position as s };
70
+ /**
71
+ * RINEX CLOCK (.CLK) parser — high-rate (typically 30 s) precise satellite
72
+ * clock corrections, the accuracy-limiting term for PPP that SP3's 5-minute
73
+ * (linearly interpolated) clocks cannot match.
74
+ *
75
+ * Only `AS` (satellite) records are kept; `AR` (station) records — the bulk
76
+ * of a combined file — are skipped. Clock bias is in seconds.
77
+ */
78
+ interface ClkFile {
79
+ /** prn → time-sorted samples { t: GPS-scale ms, c: clock bias (s) }. */
80
+ sats: Record<string, {
81
+ t: number[];
82
+ c: number[];
83
+ }>;
84
+ /** Nominal sampling interval (s), inferred from the first satellite. */
85
+ intervalSec: number;
86
+ }
87
+ /** Parse a RINEX CLOCK file. */
88
+ declare function parseClk(text: string): ClkFile;
89
+ /**
90
+ * Satellite clock bias (seconds) at an arbitrary time by linear
91
+ * interpolation between the bracketing samples. Returns null outside the
92
+ * span or across a gap larger than 4 sampling intervals.
93
+ */
94
+ declare function clkBias(clk: ClkFile, prn: string, tMs: number): number | null;
95
+
96
+ export { type ClkFile as C, type IonexGrid as I, type Sp3File as S, type Sp3Sample as a, parseIonex as b, clkBias as c, parseSp3 as d, parseClk as p, sp3Position as s };
@@ -67,4 +67,30 @@ declare function sp3Position(sp3: Sp3File, prn: string, tMs: number, order?: num
67
67
  clk: number | null;
68
68
  } | null;
69
69
 
70
- export { type IonexGrid as I, type Sp3File as S, type Sp3Sample as a, parseSp3 as b, parseIonex as p, sp3Position as s };
70
+ /**
71
+ * RINEX CLOCK (.CLK) parser — high-rate (typically 30 s) precise satellite
72
+ * clock corrections, the accuracy-limiting term for PPP that SP3's 5-minute
73
+ * (linearly interpolated) clocks cannot match.
74
+ *
75
+ * Only `AS` (satellite) records are kept; `AR` (station) records — the bulk
76
+ * of a combined file — are skipped. Clock bias is in seconds.
77
+ */
78
+ interface ClkFile {
79
+ /** prn → time-sorted samples { t: GPS-scale ms, c: clock bias (s) }. */
80
+ sats: Record<string, {
81
+ t: number[];
82
+ c: number[];
83
+ }>;
84
+ /** Nominal sampling interval (s), inferred from the first satellite. */
85
+ intervalSec: number;
86
+ }
87
+ /** Parse a RINEX CLOCK file. */
88
+ declare function parseClk(text: string): ClkFile;
89
+ /**
90
+ * Satellite clock bias (seconds) at an arbitrary time by linear
91
+ * interpolation between the bracketing samples. Returns null outside the
92
+ * span or across a gap larger than 4 sampling intervals.
93
+ */
94
+ declare function clkBias(clk: ClkFile, prn: string, tMs: number): number | null;
95
+
96
+ export { type ClkFile as C, type IonexGrid as I, type Sp3File as S, type Sp3Sample as a, parseIonex as b, clkBias as c, parseSp3 as d, parseClk as p, sp3Position as s };
package/dist/index.cjs CHANGED
@@ -119,6 +119,7 @@ __export(src_exports, {
119
119
  buildObsIndices: () => buildObsIndices,
120
120
  buildPppAntenna: () => buildPppAntenna,
121
121
  clampUnit: () => clampUnit,
122
+ clkBias: () => clkBias,
122
123
  computeAllPositions: () => computeAllPositions,
123
124
  computeDop: () => computeDop,
124
125
  computeIonoRate: () => computeIonoRate,
@@ -237,6 +238,7 @@ __export(src_exports, {
237
238
  padR: () => padR,
238
239
  parseAntex: () => parseAntex,
239
240
  parseBinex: () => parseBinex,
241
+ parseClk: () => parseClk,
240
242
  parseCrxDataLine: () => parseCrxDataLine,
241
243
  parseIonex: () => parseIonex,
242
244
  parseNavFile: () => parseNavFile,
@@ -3896,6 +3898,66 @@ function sp3Position(sp3, prn, tMs, order = 9) {
3896
3898
  return { x, y, z, clk };
3897
3899
  }
3898
3900
 
3901
+ // src/rinex/clk.ts
3902
+ var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
3903
+ function parseClk(text) {
3904
+ const sats = {};
3905
+ let inData = false;
3906
+ for (const line of text.split("\n")) {
3907
+ if (!inData) {
3908
+ if (line.includes("END OF HEADER")) inData = true;
3909
+ continue;
3910
+ }
3911
+ if (line.length < 3 || line[0] !== "A" || line[1] !== "S") continue;
3912
+ const p = line.slice(2).trim().split(/\s+/);
3913
+ if (p.length < 9) continue;
3914
+ const prn = p[0];
3915
+ const y = +p[1];
3916
+ const mo = +p[2];
3917
+ const d = +p[3];
3918
+ const h = +p[4];
3919
+ const mi = +p[5];
3920
+ const s = +p[6];
3921
+ const bias = +p[8];
3922
+ if (!Number.isFinite(bias)) continue;
3923
+ const t = Date.UTC(y, mo - 1, d, h, mi, Math.floor(s)) + s % 1 * 1e3;
3924
+ let e = sats[prn];
3925
+ if (!e) {
3926
+ e = { t: [], c: [] };
3927
+ sats[prn] = e;
3928
+ }
3929
+ e.t.push(t);
3930
+ e.c.push(bias);
3931
+ }
3932
+ let intervalSec = 30;
3933
+ for (const e of Object.values(sats)) {
3934
+ if (e.t.length >= 2) {
3935
+ intervalSec = Math.round((e.t[1] - e.t[0]) / 1e3);
3936
+ break;
3937
+ }
3938
+ }
3939
+ return { sats, intervalSec };
3940
+ }
3941
+ function clkBias(clk, prn, tMs) {
3942
+ const e = clk.sats[prn];
3943
+ if (!e || e.t.length === 0) return null;
3944
+ const { t, c } = e;
3945
+ let lo = 0;
3946
+ let hi = t.length - 1;
3947
+ if (tMs < t[0] || tMs > t[hi]) return null;
3948
+ while (lo < hi) {
3949
+ const mid = lo + hi + 1 >> 1;
3950
+ if (t[mid] <= tMs) lo = mid;
3951
+ else hi = mid - 1;
3952
+ }
3953
+ if (t[lo] === tMs || lo === t.length - 1) return c[lo];
3954
+ const t0 = t[lo];
3955
+ const t1 = t[lo + 1];
3956
+ if (t1 - t0 > clk.intervalSec * 1e3 * 4) return null;
3957
+ const f = (tMs - t0) / (t1 - t0);
3958
+ return c[lo] + f * (c[lo + 1] - c[lo]);
3959
+ }
3960
+
3899
3961
  // src/rinex/obs-writer-common.ts
3900
3962
  function createGzipLineSink() {
3901
3963
  const encoder = new TextEncoder();
@@ -6147,7 +6209,7 @@ var BdsAssembler = class {
6147
6209
  };
6148
6210
 
6149
6211
  // src/navbits/gal.ts
6150
- var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
6212
+ var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
6151
6213
  var SEC_PER_WEEK5 = 7 * 86400;
6152
6214
  var HALF_WEEK3 = 302400;
6153
6215
  var GST_GPS_WEEK_OFFSET = 1024;
@@ -6160,7 +6222,7 @@ function galFnavPageCrcOk(page) {
6160
6222
  return crc24q(page, 214) === getBitU(page, 214, 24);
6161
6223
  }
6162
6224
  function gpsDate2(week, sec) {
6163
- return new Date(GPS_EPOCH_MS4 + (week * SEC_PER_WEEK5 + sec) * 1e3);
6225
+ return new Date(GPS_EPOCH_MS5 + (week * SEC_PER_WEEK5 + sec) * 1e3);
6164
6226
  }
6165
6227
  function weekOfToe(week, tow, toe) {
6166
6228
  if (toe - tow > HALF_WEEK3) return week - 1;
@@ -6455,7 +6517,7 @@ var GalFnavAssembler = class {
6455
6517
  };
6456
6518
 
6457
6519
  // src/navbits/glo.ts
6458
- var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
6520
+ var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
6459
6521
  var SEC_PER_WEEK6 = 7 * 86400;
6460
6522
  var SEC_PER_DAY = 86400;
6461
6523
  var GLO_STRING_BYTES = 10;
@@ -6549,7 +6611,7 @@ function decodeGloStrings(strings, refDate, opts = {}) {
6549
6611
  const slot = getBitU(b, i, 5);
6550
6612
  if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3 || frn4 !== 4) return null;
6551
6613
  if (slot < 1 || slot > 27) return null;
6552
- const utcSec = (getUtcDate(refDate).getTime() - GPS_EPOCH_MS5) / 1e3;
6614
+ const utcSec = (getUtcDate(refDate).getTime() - GPS_EPOCH_MS6) / 1e3;
6553
6615
  const week = Math.floor(utcSec / SEC_PER_WEEK6);
6554
6616
  let tow = utcSec - week * SEC_PER_WEEK6;
6555
6617
  const tod = tow % SEC_PER_DAY;
@@ -6564,7 +6626,7 @@ function decodeGloStrings(strings, refDate, opts = {}) {
6564
6626
  system: "R",
6565
6627
  prn: `R${String(slot).padStart(2, "0")}`,
6566
6628
  // RINEX GLONASS epochs are UTC: build the UTC toe Date directly.
6567
- tocDate: new Date(GPS_EPOCH_MS5 + (week * SEC_PER_WEEK6 + tow + toe) * 1e3),
6629
+ tocDate: new Date(GPS_EPOCH_MS6 + (week * SEC_PER_WEEK6 + tow + toe) * 1e3),
6568
6630
  tauN: -tauN,
6569
6631
  // RINEX stores −τn; the SIS carries τn (ICD sign)
6570
6632
  gammaN,
@@ -6618,7 +6680,7 @@ var GloStringAssembler = class {
6618
6680
  };
6619
6681
 
6620
6682
  // src/ubx/rawnav.ts
6621
- var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
6683
+ var GPS_EPOCH_MS7 = Date.UTC(1980, 0, 6);
6622
6684
  var SEC_PER_WEEK7 = 7 * 86400;
6623
6685
  var two2 = (n) => String(n).padStart(2, "0");
6624
6686
  function parseUbxRawNav(data, opts = {}) {
@@ -6638,7 +6700,7 @@ function parseUbxRawNav(data, opts = {}) {
6638
6700
  if (week === 0 && opts.refWeek !== void 0) week = opts.refWeek;
6639
6701
  if (week > 0) {
6640
6702
  refDate = new Date(
6641
- GPS_EPOCH_MS6 + (week * SEC_PER_WEEK7 + rcvTow) * 1e3
6703
+ GPS_EPOCH_MS7 + (week * SEC_PER_WEEK7 + rcvTow) * 1e3
6642
6704
  );
6643
6705
  }
6644
6706
  continue;
@@ -6718,7 +6780,7 @@ function parseUbxRawNav(data, opts = {}) {
6718
6780
  }
6719
6781
 
6720
6782
  // src/ubx/index.ts
6721
- var GPS_EPOCH_MS7 = Date.UTC(1980, 0, 6);
6783
+ var GPS_EPOCH_MS8 = Date.UTC(1980, 0, 6);
6722
6784
  var MS_PER_WEEK3 = 7 * 864e5;
6723
6785
  var SIGNALS = {
6724
6786
  0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
@@ -6801,7 +6863,7 @@ function parseUbxRawx(data) {
6801
6863
  if (!codes.includes(sig[1])) codes.push(sig[1]);
6802
6864
  }
6803
6865
  epochs.push({
6804
- timeMs: GPS_EPOCH_MS7 + week * MS_PER_WEEK3 + Math.round(rcvTow * 1e3),
6866
+ timeMs: GPS_EPOCH_MS8 + week * MS_PER_WEEK3 + Math.round(rcvTow * 1e3),
6805
6867
  leapS: leapValid ? leapS : null,
6806
6868
  meas
6807
6869
  });
@@ -6872,7 +6934,7 @@ function svidToPrn(svid) {
6872
6934
 
6873
6935
  // src/sbf/nav.ts
6874
6936
  var PI2 = Math.PI;
6875
- var GPS_EPOCH_MS8 = Date.UTC(1980, 0, 6);
6937
+ var GPS_EPOCH_MS9 = Date.UTC(1980, 0, 6);
6876
6938
  var BDT_EPOCH_MS2 = Date.UTC(2006, 0, 1);
6877
6939
  var SEC_PER_WEEK8 = 7 * 86400;
6878
6940
  var MS_PER_WEEK4 = SEC_PER_WEEK8 * 1e3;
@@ -6884,7 +6946,7 @@ function adjustWeek(refWeek, wn, mod) {
6884
6946
  if (offset < -(mod / 2 - 1)) offset += mod;
6885
6947
  return refWeek - offset;
6886
6948
  }
6887
- var gpsMs = (week, sec) => GPS_EPOCH_MS8 + week * MS_PER_WEEK4 + sec * 1e3;
6949
+ var gpsMs = (week, sec) => GPS_EPOCH_MS9 + week * MS_PER_WEEK4 + sec * 1e3;
6888
6950
  var sowOf2 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK8;
6889
6951
  function decodeGpsQzsNav(view, b, sys) {
6890
6952
  const svid = view.getUint8(b + 14);
@@ -7034,7 +7096,7 @@ function decodeGloNav(view, b) {
7034
7096
  const tocDate = new Date(toeGpsMs - leapMs);
7035
7097
  const tofGpsMs = gpsMs(wnc, 0) + view.getUint32(b + 8, true);
7036
7098
  const tofLeapMs = getGpsLeap(new Date(tofGpsMs)) * 1e3;
7037
- const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS8) / 1e3 % SEC_PER_WEEK8;
7099
+ const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS9) / 1e3 % SEC_PER_WEEK8;
7038
7100
  return {
7039
7101
  system: "R",
7040
7102
  prn,
@@ -7306,7 +7368,7 @@ function isFnavDummy(page) {
7306
7368
  }
7307
7369
 
7308
7370
  // src/sbf/rawnav-bds.ts
7309
- var GPS_EPOCH_MS9 = Date.UTC(1980, 0, 6);
7371
+ var GPS_EPOCH_MS10 = Date.UTC(1980, 0, 6);
7310
7372
  var MS_PER_WEEK5 = 7 * 86400 * 1e3;
7311
7373
  var TOW_DNU = 4294967295;
7312
7374
  var WNC_DNU = 65535;
@@ -7376,7 +7438,7 @@ function parseSbfGloNav(data) {
7376
7438
  const eph = assembler.push(
7377
7439
  prn,
7378
7440
  str,
7379
- new Date(GPS_EPOCH_MS9 + wnc * MS_PER_WEEK5 + towMs),
7441
+ new Date(GPS_EPOCH_MS10 + wnc * MS_PER_WEEK5 + towMs),
7380
7442
  view.getUint8(b + 18) - 8
7381
7443
  // FreqNr, offset by 8
7382
7444
  );
@@ -7910,7 +7972,7 @@ function parseSbfHas(data) {
7910
7972
 
7911
7973
  // src/sbf/index.ts
7912
7974
  var CLIGHT = 299792458;
7913
- var GPS_EPOCH_MS10 = Date.UTC(1980, 0, 6);
7975
+ var GPS_EPOCH_MS11 = Date.UTC(1980, 0, 6);
7914
7976
  var MS_PER_WEEK6 = 7 * 864e5;
7915
7977
  var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
7916
7978
  var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
@@ -8564,7 +8626,7 @@ function parseSbfMeas(data) {
8564
8626
  const tow = view.getUint32(i + 8, true);
8565
8627
  const wnc = view.getUint16(i + 12, true);
8566
8628
  if (tow !== 4294967295 && wnc !== 65535) {
8567
- ensureEpoch(GPS_EPOCH_MS10 + wnc * MS_PER_WEEK6 + tow);
8629
+ ensureEpoch(GPS_EPOCH_MS11 + wnc * MS_PER_WEEK6 + tow);
8568
8630
  if (id === 4109) decodeMeas3Ranges(i, tow);
8569
8631
  else if (id === 4110) decodeMeas3CN0(i, len);
8570
8632
  else if (id === 4111) decodeMeas3Doppler(i, len);
@@ -8629,15 +8691,15 @@ var ID_GALEPHEMERIS = 1122;
8629
8691
  var ID_QZSSEPHEMERIS = 1336;
8630
8692
  var ID_GPSEPHEM = 7;
8631
8693
  var ID_BDSEPHEMERIS = 1696;
8632
- var GPS_EPOCH_MS11 = Date.UTC(1980, 0, 6);
8694
+ var GPS_EPOCH_MS12 = Date.UTC(1980, 0, 6);
8633
8695
  var BDT_EPOCH_MS3 = Date.UTC(2006, 0, 1);
8634
8696
  var SEC_PER_WEEK9 = 7 * 86400;
8635
8697
  var SEC_PER_DAY2 = 86400;
8636
8698
  var MS_PER_WEEK7 = SEC_PER_WEEK9 * 1e3;
8637
- var gpsMs2 = (week, sec) => GPS_EPOCH_MS11 + week * MS_PER_WEEK7 + sec * 1e3;
8699
+ var gpsMs2 = (week, sec) => GPS_EPOCH_MS12 + week * MS_PER_WEEK7 + sec * 1e3;
8638
8700
  var sowOf3 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK9;
8639
8701
  function nearWeekMs(refMs, sow) {
8640
- const refWeek = Math.floor((refMs - GPS_EPOCH_MS11) / MS_PER_WEEK7);
8702
+ const refWeek = Math.floor((refMs - GPS_EPOCH_MS12) / MS_PER_WEEK7);
8641
8703
  let ms = gpsMs2(refWeek, sow);
8642
8704
  if (ms < refMs - MS_PER_WEEK7 / 2) ms += MS_PER_WEEK7;
8643
8705
  else if (ms > refMs + MS_PER_WEEK7 / 2) ms -= MS_PER_WEEK7;
@@ -8664,9 +8726,9 @@ function decodeGloEphemeris(view, p) {
8664
8726
  tof += Math.floor(tow / SEC_PER_DAY2) * SEC_PER_DAY2;
8665
8727
  if (tof < tow - 43200) tof += SEC_PER_DAY2;
8666
8728
  else if (tof > tow + 43200) tof -= SEC_PER_DAY2;
8667
- const toeGpsMs = GPS_EPOCH_MS11 + (week * SEC_PER_WEEK9 + tow) * 1e3;
8668
- const tofGpsMs = GPS_EPOCH_MS11 + (week * SEC_PER_WEEK9 + tof) * 1e3;
8669
- const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() - GPS_EPOCH_MS11) / 1e3;
8729
+ const toeGpsMs = GPS_EPOCH_MS12 + (week * SEC_PER_WEEK9 + tow) * 1e3;
8730
+ const tofGpsMs = GPS_EPOCH_MS12 + (week * SEC_PER_WEEK9 + tof) * 1e3;
8731
+ const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() - GPS_EPOCH_MS12) / 1e3;
8670
8732
  return {
8671
8733
  system: "R",
8672
8734
  prn: `R${String(slot).padStart(2, "0")}`,
@@ -8731,7 +8793,7 @@ function decodeGalEphemeris(view, p, headerMs) {
8731
8793
  omega: view.getFloat64(p + 60, true),
8732
8794
  omegaDot: view.getFloat64(p + 140, true),
8733
8795
  idot: view.getFloat64(p + 124, true),
8734
- week: Math.floor((toeMs - GPS_EPOCH_MS11) / MS_PER_WEEK7),
8796
+ week: Math.floor((toeMs - GPS_EPOCH_MS12) / MS_PER_WEEK7),
8735
8797
  // RINEX Galileo SVH bit layout (E1B DVS/HS in bits 0-2, E5a in
8736
8798
  // 3-5, E5b in 6-8) — same packing as RTKLIB.
8737
8799
  svHealth: svhE5b << 7 | dvsE5b << 6 | svhE5a << 4 | dvsE5a << 3 | svhE1b << 1 | dvsE1b,
@@ -8903,7 +8965,7 @@ function parseNovatelNav(data) {
8903
8965
  }
8904
8966
 
8905
8967
  // src/novatel/index.ts
8906
- var GPS_EPOCH_MS12 = Date.UTC(1980, 0, 6);
8968
+ var GPS_EPOCH_MS13 = Date.UTC(1980, 0, 6);
8907
8969
  var MS_PER_WEEK8 = 7 * 864e5;
8908
8970
  var HDR = 28;
8909
8971
  var ID_RANGE = 43;
@@ -9073,7 +9135,7 @@ function parseNovatelRange(data) {
9073
9135
  if (!codes.includes(code)) codes.push(code);
9074
9136
  }
9075
9137
  epochs.push({
9076
- timeMs: GPS_EPOCH_MS12 + week * MS_PER_WEEK8 + towMs,
9138
+ timeMs: GPS_EPOCH_MS13 + week * MS_PER_WEEK8 + towMs,
9077
9139
  meas
9078
9140
  });
9079
9141
  }
@@ -9125,7 +9187,7 @@ function parseNovatelRange(data) {
9125
9187
  if (!codes.includes(code)) codes.push(code);
9126
9188
  }
9127
9189
  epochs.push({
9128
- timeMs: GPS_EPOCH_MS12 + week * MS_PER_WEEK8 + towMs,
9190
+ timeMs: GPS_EPOCH_MS13 + week * MS_PER_WEEK8 + towMs,
9129
9191
  meas
9130
9192
  });
9131
9193
  }
@@ -9176,11 +9238,11 @@ function* trimbleFrames(data, stats) {
9176
9238
  }
9177
9239
 
9178
9240
  // src/trimble/nav.ts
9179
- var GPS_EPOCH_MS13 = Date.UTC(1980, 0, 6);
9241
+ var GPS_EPOCH_MS14 = Date.UTC(1980, 0, 6);
9180
9242
  var SEC_PER_WEEK10 = 7 * 86400;
9181
9243
  var MS_PER_WEEK9 = SEC_PER_WEEK10 * 1e3;
9182
9244
  var GPS_PI2 = 3.1415926535898;
9183
- var gpsMs3 = (week, sec) => GPS_EPOCH_MS13 + week * MS_PER_WEEK9 + sec * 1e3;
9245
+ var gpsMs3 = (week, sec) => GPS_EPOCH_MS14 + week * MS_PER_WEEK9 + sec * 1e3;
9184
9246
  var sowOf4 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK10;
9185
9247
  var SUB_GPS_EPHEMERIS = 1;
9186
9248
  var SUB_ION_UTC = 3;
@@ -9282,7 +9344,7 @@ function parseTrimbleNav(data) {
9282
9344
  }
9283
9345
 
9284
9346
  // src/trimble/index.ts
9285
- var GPS_EPOCH_MS14 = Date.UTC(1980, 0, 6);
9347
+ var GPS_EPOCH_MS15 = Date.UTC(1980, 0, 6);
9286
9348
  var MS_PER_WEEK10 = 7 * 864e5;
9287
9349
  var REC_TYPE_27 = 6;
9288
9350
  var SYS = {
@@ -9480,7 +9542,7 @@ function decodeEpoch(epoch, epochs, obsCodes) {
9480
9542
  o += c.length;
9481
9543
  }
9482
9544
  const dv = new DataView(M.buffer, M.byteOffset, M.byteLength);
9483
- const timeMs = GPS_EPOCH_MS14 + epoch.week * MS_PER_WEEK10 + epoch.towMs;
9545
+ const timeMs = GPS_EPOCH_MS15 + epoch.week * MS_PER_WEEK10 + epoch.towMs;
9484
9546
  const meas = [];
9485
9547
  let q = 0;
9486
9548
  for (let sv = 0; sv < epoch.nsv && q + 8 <= M.length; sv++) {
@@ -9661,13 +9723,13 @@ function* binexRecords(data, view, stats) {
9661
9723
  }
9662
9724
 
9663
9725
  // src/binex/nav.ts
9664
- var GPS_EPOCH_MS15 = Date.UTC(1980, 0, 6);
9726
+ var GPS_EPOCH_MS16 = Date.UTC(1980, 0, 6);
9665
9727
  var GLO_DAY0_MS = Date.UTC(1980, 0, 1);
9666
9728
  var BDT_EPOCH_MS4 = Date.UTC(2006, 0, 1);
9667
9729
  var SEC_PER_WEEK11 = 7 * 86400;
9668
9730
  var MS_PER_WEEK11 = SEC_PER_WEEK11 * 1e3;
9669
9731
  var SC2RAD = GPS_PI;
9670
- var gpsMs4 = (week, sec) => GPS_EPOCH_MS15 + week * MS_PER_WEEK11 + sec * 1e3;
9732
+ var gpsMs4 = (week, sec) => GPS_EPOCH_MS16 + week * MS_PER_WEEK11 + sec * 1e3;
9671
9733
  var sowOf5 = (dateMs) => (dateMs / 1e3 % SEC_PER_WEEK11 + SEC_PER_WEEK11) % SEC_PER_WEEK11;
9672
9734
  var two5 = (n) => String(n).padStart(2, "0");
9673
9735
  var Reader = class {
@@ -9955,7 +10017,7 @@ function decodeBinexEph(view, body, len, le) {
9955
10017
  }
9956
10018
 
9957
10019
  // src/binex/index.ts
9958
- var GPS_EPOCH_MS16 = Date.UTC(1980, 0, 6);
10020
+ var GPS_EPOCH_MS17 = Date.UTC(1980, 0, 6);
9959
10021
  var CLIGHT2 = 299792458;
9960
10022
  var OBS_CODES = {
9961
10023
  0: [
@@ -10213,7 +10275,7 @@ function decodeObsRecord(data, view, body, len, obsCodes) {
10213
10275
  p += 4;
10214
10276
  const msec = view.getUint16(p);
10215
10277
  p += 2;
10216
- const timeMs = GPS_EPOCH_MS16 + min * 6e4 + msec;
10278
+ const timeMs = GPS_EPOCH_MS17 + min * 6e4 + msec;
10217
10279
  const flag = data[p++];
10218
10280
  const nsat = (flag & 63) + 1;
10219
10281
  if (flag & 128) p += 3;
@@ -10299,7 +10361,7 @@ function parseBinex(data) {
10299
10361
  var GM_GPS = 3986005e8;
10300
10362
  var GM_GAL = 3986004418e5;
10301
10363
  var GM_BDS = 3986004418e5;
10302
- var GPS_EPOCH_MS17 = Date.UTC(1980, 0, 6);
10364
+ var GPS_EPOCH_MS18 = Date.UTC(1980, 0, 6);
10303
10365
  var BDT_EPOCH_MS5 = Date.UTC(2006, 0, 1);
10304
10366
  var WEEK_MS = 6048e5;
10305
10367
  var BDT_MINUS_GPST_MS = 14e3;
@@ -10308,7 +10370,7 @@ function almanacEpochMs(alm) {
10308
10370
  if (alm.system === "C") {
10309
10371
  return BDT_EPOCH_MS5 + alm.weekAlm * WEEK_MS + alm.toaSec * 1e3 + BDT_MINUS_GPST_MS;
10310
10372
  }
10311
- return GPS_EPOCH_MS17 + alm.weekAlm * WEEK_MS + alm.toaSec * 1e3;
10373
+ return GPS_EPOCH_MS18 + alm.weekAlm * WEEK_MS + alm.toaSec * 1e3;
10312
10374
  }
10313
10375
  function almanacSatPosition(alm, timeMs) {
10314
10376
  const GM = alm.system === "E" ? GM_GAL : alm.system === "C" ? GM_BDS : GM_GPS;
@@ -10817,10 +10879,10 @@ function selectBest(ephs, timeMs) {
10817
10879
  }
10818
10880
  return best;
10819
10881
  }
10820
- var GPS_EPOCH_MS18 = START_GPS_TIME.getTime();
10882
+ var GPS_EPOCH_MS19 = START_GPS_TIME.getTime();
10821
10883
  var MS_PER_WEEK12 = 7 * 864e5;
10822
10884
  var BDS_EPOCH_MS = START_BDS_TIME.getTime();
10823
- var GAL_EPOCH_MS = GPS_EPOCH_MS18 + 1024 * MS_PER_WEEK12;
10885
+ var GAL_EPOCH_MS = GPS_EPOCH_MS19 + 1024 * MS_PER_WEEK12;
10824
10886
  function ephInfoToEphemeris(info) {
10825
10887
  const sys = info.prn.charAt(0);
10826
10888
  if (sys === "R") {
@@ -10871,8 +10933,8 @@ function ephInfoToEphemeris(info) {
10871
10933
  } else if (sys === "E") {
10872
10934
  epochMs = GAL_EPOCH_MS;
10873
10935
  } else {
10874
- epochMs = GPS_EPOCH_MS18;
10875
- const weeksNow = (info.lastReceived - GPS_EPOCH_MS18) / MS_PER_WEEK12;
10936
+ epochMs = GPS_EPOCH_MS19;
10937
+ const weeksNow = (info.lastReceived - GPS_EPOCH_MS19) / MS_PER_WEEK12;
10876
10938
  week += 1024 * Math.round((weeksNow - week) / 1024);
10877
10939
  }
10878
10940
  const tocDate = new Date(epochMs + week * MS_PER_WEEK12 + tocSec * 1e3);
@@ -12834,7 +12896,7 @@ function sagnacRotate(x, y, z, travelS) {
12834
12896
  const s = Math.sin(theta);
12835
12897
  return [x * c + y * s, -x * s + y * c, z];
12836
12898
  }
12837
- function satStateAtEmission(sp3, prn, recvTimeMs, rcv) {
12899
+ function satStateAtEmission(sp3, prn, recvTimeMs, rcv, clk) {
12838
12900
  let travelS = 0.075;
12839
12901
  let sx = 0;
12840
12902
  let sy = 0;
@@ -12853,7 +12915,7 @@ function satStateAtEmission(sp3, prn, recvTimeMs, rcv) {
12853
12915
  const vz = (pPlus.z - pMinus.z) / 1;
12854
12916
  velDotPos = p.x * vx + p.y * vy + p.z * vz;
12855
12917
  }
12856
- clkS = p.clk;
12918
+ clkS = (clk && clkBias(clk, prn, tEmit)) ?? p.clk;
12857
12919
  const [rx, ry, rz] = sagnacRotate(p.x, p.y, p.z, travelS);
12858
12920
  sx = rx;
12859
12921
  sy = ry;
@@ -12872,9 +12934,9 @@ function satStateAtEmission(sp3, prn, recvTimeMs, rcv) {
12872
12934
  var IDX_X = 0;
12873
12935
  var IDX_Y = 1;
12874
12936
  var IDX_Z = 2;
12875
- var IDX_CLK = 3;
12876
- var IDX_ZWD = 4;
12877
- var NBASE = 5;
12937
+ var IDX_ZWD = 3;
12938
+ var NBASE = 4;
12939
+ var CLK_VAR = 1e10;
12878
12940
  function solvePpp(epochs, sp3, opts) {
12879
12941
  const elevMask = (opts.elevationMaskDeg ?? 10) * Math.PI / 180;
12880
12942
  const codeSigma = opts.codeSigma ?? 3;
@@ -12885,8 +12947,6 @@ function solvePpp(epochs, sp3, opts) {
12885
12947
  opts.aprioriPos[0],
12886
12948
  opts.aprioriPos[1],
12887
12949
  opts.aprioriPos[2],
12888
- 0,
12889
- // receiver clock (m)
12890
12950
  0.1
12891
12951
  // zenith wet delay (m) a priori
12892
12952
  ];
@@ -12897,8 +12957,9 @@ function solvePpp(epochs, sp3, opts) {
12897
12957
  P[IDX_X][IDX_X] = 100 * 100;
12898
12958
  P[IDX_Y][IDX_Y] = 100 * 100;
12899
12959
  P[IDX_Z][IDX_Z] = 100 * 100;
12900
- P[IDX_CLK][IDX_CLK] = 1e10;
12901
12960
  P[IDX_ZWD][IDX_ZWD] = 0.5 * 0.5;
12961
+ const clkIdx = /* @__PURE__ */ new Map();
12962
+ const clockSeeded = /* @__PURE__ */ new Set();
12902
12963
  const ambIdx = /* @__PURE__ */ new Map();
12903
12964
  const lastSeen = /* @__PURE__ */ new Map();
12904
12965
  const mwState = /* @__PURE__ */ new Map();
@@ -12939,8 +13000,18 @@ function solvePpp(epochs, sp3, opts) {
12939
13000
  if (i === idx) ambIdx.delete(prn);
12940
13001
  else if (i > idx) ambIdx.set(prn, i - 1);
12941
13002
  }
13003
+ for (const [sys, i] of clkIdx) {
13004
+ if (i > idx) clkIdx.set(sys, i - 1);
13005
+ }
13006
+ };
13007
+ const clockOf = (sys) => {
13008
+ let i = clkIdx.get(sys);
13009
+ if (i === void 0) {
13010
+ i = growState(0, CLK_VAR);
13011
+ clkIdx.set(sys, i);
13012
+ }
13013
+ return i;
12942
13014
  };
12943
- let clockSeeded = false;
12944
13015
  const series = [];
12945
13016
  const truth = opts.groundTruth;
12946
13017
  const t0 = epochs[0]?.timeMs ?? 0;
@@ -12948,7 +13019,7 @@ function solvePpp(epochs, sp3, opts) {
12948
13019
  let convergedStreak = 0;
12949
13020
  for (let ei = 0; ei < epochs.length; ei++) {
12950
13021
  const epoch = epochs[ei];
12951
- P[IDX_CLK][IDX_CLK] = 1e10;
13022
+ for (const ci of clkIdx.values()) P[ci][ci] = CLK_VAR;
12952
13023
  P[IDX_ZWD][IDX_ZWD] += ztdQ * (ei > 0 ? 1 : 0);
12953
13024
  const rcv = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
12954
13025
  const [latDeg, , heightM] = ecefToGeodetic(rcv[0], rcv[1], rcv[2]);
@@ -12958,7 +13029,7 @@ function solvePpp(epochs, sp3, opts) {
12958
13029
  for (const o of epoch.obs) {
12959
13030
  if (o.c1 === 0 || o.c2 === 0 || o.l1 === 0 || o.l2 === 0) continue;
12960
13031
  const slip = detectSlip(o, ei);
12961
- const sat = satStateAtEmission(sp3, o.prn, epoch.timeMs, rcv);
13032
+ const sat = satStateAtEmission(sp3, o.prn, epoch.timeMs, rcv, opts.clk);
12962
13033
  if (!sat) continue;
12963
13034
  const [elRad, azRad] = getAer(
12964
13035
  sat.state.x,
@@ -13012,21 +13083,29 @@ function solvePpp(epochs, sp3, opts) {
13012
13083
  });
13013
13084
  }
13014
13085
  const zwd0 = x[IDX_ZWD];
13015
- if (vis.length > 0) {
13016
- if (!clockSeeded) {
13017
- const s = vis.map(
13018
- (v) => v.pIf - (v.range - v.satClkM + v.tropoFixed + zwd0 * v.mw)
13019
- ).sort((a, b) => a - b);
13020
- x[IDX_CLK] = s[Math.floor(s.length / 2)];
13021
- clockSeeded = true;
13086
+ const bySys = /* @__PURE__ */ new Map();
13087
+ for (const v of vis) {
13088
+ const sys = v.prn[0];
13089
+ const r = v.pIf - (v.range - v.satClkM + v.tropoFixed + zwd0 * v.mw);
13090
+ const arr = bySys.get(sys);
13091
+ if (arr) arr.push(r);
13092
+ else bySys.set(sys, [r]);
13093
+ }
13094
+ for (const [sys, res] of bySys) {
13095
+ const ci = clockOf(sys);
13096
+ if (!clockSeeded.has(sys)) {
13097
+ res.sort((a, b) => a - b);
13098
+ x[ci] = res[Math.floor(res.length / 2)];
13099
+ clockSeeded.add(sys);
13022
13100
  }
13023
- P[IDX_CLK][IDX_CLK] = 1e10;
13101
+ P[ci][ci] = CLK_VAR;
13024
13102
  }
13025
13103
  let nSats = 0;
13026
13104
  let sumPhaseResSq = 0;
13027
13105
  let nPhaseRes = 0;
13028
13106
  for (const v of vis) {
13029
- const common = v.range + x[IDX_CLK] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
13107
+ const ci = clockOf(v.prn[0]);
13108
+ const common = v.range + x[ci] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
13030
13109
  let ai = ambIdx.get(v.prn);
13031
13110
  if (ai === void 0 || v.slip) {
13032
13111
  const bInit = v.lIf - (common + v.windupM);
@@ -13051,13 +13130,13 @@ function solvePpp(epochs, sp3, opts) {
13051
13130
  h[IDX_X] = -v.e[0];
13052
13131
  h[IDX_Y] = -v.e[1];
13053
13132
  h[IDX_Z] = -v.e[2];
13054
- h[IDX_CLK] = 1;
13133
+ h[ci] = 1;
13055
13134
  h[IDX_ZWD] = v.mw;
13056
13135
  const innov = v.pIf - common;
13057
13136
  if (Math.abs(innov) < 1e6) ekfUpdateScalar(x, P, h, innov, rCode, 4);
13058
13137
  }
13059
13138
  {
13060
- const commonPh = v.range + x[IDX_CLK] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
13139
+ const commonPh = v.range + x[ci] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
13061
13140
  const innov = v.lIf - (commonPh + v.windupM + x[ai]);
13062
13141
  if (Math.abs(innov) > 0.15) {
13063
13142
  x[ai] = v.lIf - (commonPh + v.windupM);
@@ -13071,7 +13150,7 @@ function solvePpp(epochs, sp3, opts) {
13071
13150
  h[IDX_X] = -v.e[0];
13072
13151
  h[IDX_Y] = -v.e[1];
13073
13152
  h[IDX_Z] = -v.e[2];
13074
- h[IDX_CLK] = 1;
13153
+ h[ci] = 1;
13075
13154
  h[IDX_ZWD] = v.mw;
13076
13155
  h[ai] = 1;
13077
13156
  if (ekfUpdateScalar(x, P, h, innov, rPhase, 4)) {
@@ -15732,6 +15811,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
15732
15811
  buildObsIndices,
15733
15812
  buildPppAntenna,
15734
15813
  clampUnit,
15814
+ clkBias,
15735
15815
  computeAllPositions,
15736
15816
  computeDop,
15737
15817
  computeIonoRate,
@@ -15850,6 +15930,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
15850
15930
  padR,
15851
15931
  parseAntex,
15852
15932
  parseBinex,
15933
+ parseClk,
15853
15934
  parseCrxDataLine,
15854
15935
  parseIonex,
15855
15936
  parseNavFile,
package/dist/index.d.cts CHANGED
@@ -7,7 +7,7 @@ export { c as clampUnit, e as ecefToGeodetic, g as geodeticToEcef, a as getAer,
7
7
  export { CompactEpoch, CrxField, DiffState, EMPTY_WARNINGS, Nav4Input, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, WriteCrxOptions, createGzipLineSink, crxDecompress, crxRepair, fmtD, fmtF, hdrLine, padL, padR, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob } from './rinex.cjs';
8
8
  export { E as EpochSummary, P as ParseOptions, R as RinexHeader, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.cjs';
9
9
  export { C as CnavEphemeris, E as Ephemeris, G as GlonassEphemeris, K as KeplerEphemeris, N as NavHeader, a as NavResult, R as RinexCnavEphemeris, p as parseNavFile } from './nav-DImXUdbp.cjs';
10
- export { I as IonexGrid, S as Sp3File, a as Sp3Sample, p as parseIonex, b as parseSp3, s as sp3Position } from './sp3-BAq8l39b.cjs';
10
+ export { C as ClkFile, I as IonexGrid, S as Sp3File, a as Sp3Sample, c as clkBias, p as parseClk, b as parseIonex, d as parseSp3, s as sp3Position } from './clk-DGVBGRzA.cjs';
11
11
  export { B as BitReader, E as EphemerisInfo, R as Rtcm3DebugHandler, a as Rtcm3Decoder, b as Rtcm3Frame, d as decodeEphemeris, r as readString, c as reportDecodeError, s as setRtcm3DebugHandler } from './ephemeris-Ohl6NAfw.cjs';
12
12
  export { MessageTypeStats, MsmEpoch, MsmSatObs, MsmSignal, RTCM3_MESSAGE_NAMES, SatCn0, SignalCn0, StationMeta, StreamStats, createStationMeta, createStreamStats, decodeMsmFull, msmEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats } from './rtcm3.cjs';
13
13
  export { UbxCnavResult, UbxFrame, UbxIonoUtcResult, UbxMeasurement, UbxNavOptions, UbxNavResult, UbxParseResult, UbxRawNavOptions, UbxRawNavResult, UbxRawxEpoch, parseUbxCnav, parseUbxIonoUtc, parseUbxNav, parseUbxRawNav, parseUbxRawx, ubxFrames } from './ubx.cjs';
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export { c as clampUnit, e as ecefToGeodetic, g as geodeticToEcef, a as getAer,
7
7
  export { CompactEpoch, CrxField, DiffState, EMPTY_WARNINGS, Nav4Input, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, WriteCrxOptions, createGzipLineSink, crxDecompress, crxRepair, fmtD, fmtF, hdrLine, padL, padR, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob } from './rinex.js';
8
8
  export { E as EpochSummary, P as ParseOptions, R as RinexHeader, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.js';
9
9
  export { C as CnavEphemeris, E as Ephemeris, G as GlonassEphemeris, K as KeplerEphemeris, N as NavHeader, a as NavResult, R as RinexCnavEphemeris, p as parseNavFile } from './nav-DImXUdbp.js';
10
- export { I as IonexGrid, S as Sp3File, a as Sp3Sample, p as parseIonex, b as parseSp3, s as sp3Position } from './sp3-BAq8l39b.js';
10
+ export { C as ClkFile, I as IonexGrid, S as Sp3File, a as Sp3Sample, c as clkBias, p as parseClk, b as parseIonex, d as parseSp3, s as sp3Position } from './clk-DGVBGRzA.js';
11
11
  export { B as BitReader, E as EphemerisInfo, R as Rtcm3DebugHandler, a as Rtcm3Decoder, b as Rtcm3Frame, d as decodeEphemeris, r as readString, c as reportDecodeError, s as setRtcm3DebugHandler } from './ephemeris-Ohl6NAfw.js';
12
12
  export { MessageTypeStats, MsmEpoch, MsmSatObs, MsmSignal, RTCM3_MESSAGE_NAMES, SatCn0, SignalCn0, StationMeta, StreamStats, createStationMeta, createStreamStats, decodeMsmFull, msmEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats } from './rtcm3.js';
13
13
  export { UbxCnavResult, UbxFrame, UbxIonoUtcResult, UbxMeasurement, UbxNavOptions, UbxNavResult, UbxParseResult, UbxRawNavOptions, UbxRawNavResult, UbxRawxEpoch, parseUbxCnav, parseUbxIonoUtc, parseUbxNav, parseUbxRawNav, parseUbxRawx, ubxFrames } from './ubx.js';
package/dist/index.js CHANGED
@@ -162,7 +162,7 @@ import {
162
162
  solveSpp,
163
163
  sunEcef,
164
164
  toRtkEpoch
165
- } from "./chunk-O65WA6CM.js";
165
+ } from "./chunk-CRFFUU5Y.js";
166
166
  import {
167
167
  almanacEpochMs,
168
168
  almanacSatPosition,
@@ -217,7 +217,7 @@ import {
217
217
  writeRinexNav,
218
218
  writeRinexNav4,
219
219
  writeRinexObsBlob
220
- } from "./chunk-6GTS5MVI.js";
220
+ } from "./chunk-KDM2NVBO.js";
221
221
  import "./chunk-726LQBGM.js";
222
222
  import "./chunk-IQ5OIMQD.js";
223
223
  import {
@@ -230,9 +230,11 @@ import {
230
230
  systemName
231
231
  } from "./chunk-3U5AX7PY.js";
232
232
  import {
233
+ clkBias,
234
+ parseClk,
233
235
  parseSp3,
234
236
  sp3Position
235
- } from "./chunk-4KMWWCUR.js";
237
+ } from "./chunk-GPEP67TG.js";
236
238
  import {
237
239
  BitReader,
238
240
  RTCM3_MESSAGE_NAMES,
@@ -434,6 +436,7 @@ export {
434
436
  buildObsIndices,
435
437
  buildPppAntenna,
436
438
  clampUnit,
439
+ clkBias,
437
440
  computeAllPositions,
438
441
  computeDop,
439
442
  computeIonoRate,
@@ -552,6 +555,7 @@ export {
552
555
  padR,
553
556
  parseAntex,
554
557
  parseBinex,
558
+ parseClk,
555
559
  parseCrxDataLine,
556
560
  parseIonex,
557
561
  parseNavFile,
@@ -1597,6 +1597,28 @@ function sp3Position(sp3, prn, tMs, order = 9) {
1597
1597
  return { x, y, z, clk };
1598
1598
  }
1599
1599
 
1600
+ // src/rinex/clk.ts
1601
+ var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
1602
+ function clkBias(clk, prn, tMs) {
1603
+ const e = clk.sats[prn];
1604
+ if (!e || e.t.length === 0) return null;
1605
+ const { t, c } = e;
1606
+ let lo = 0;
1607
+ let hi = t.length - 1;
1608
+ if (tMs < t[0] || tMs > t[hi]) return null;
1609
+ while (lo < hi) {
1610
+ const mid = lo + hi + 1 >> 1;
1611
+ if (t[mid] <= tMs) lo = mid;
1612
+ else hi = mid - 1;
1613
+ }
1614
+ if (t[lo] === tMs || lo === t.length - 1) return c[lo];
1615
+ const t0 = t[lo];
1616
+ const t1 = t[lo + 1];
1617
+ if (t1 - t0 > clk.intervalSec * 1e3 * 4) return null;
1618
+ const f = (tMs - t0) / (t1 - t0);
1619
+ return c[lo] + f * (c[lo + 1] - c[lo]);
1620
+ }
1621
+
1600
1622
  // src/positioning/ppp-tropo.ts
1601
1623
  var HYD_AVG_A = [
1602
1624
  0.0012769934,
@@ -2080,7 +2102,7 @@ function sagnacRotate(x, y, z, travelS) {
2080
2102
  const s = Math.sin(theta);
2081
2103
  return [x * c + y * s, -x * s + y * c, z];
2082
2104
  }
2083
- function satStateAtEmission(sp3, prn, recvTimeMs, rcv) {
2105
+ function satStateAtEmission(sp3, prn, recvTimeMs, rcv, clk) {
2084
2106
  let travelS = 0.075;
2085
2107
  let sx = 0;
2086
2108
  let sy = 0;
@@ -2099,7 +2121,7 @@ function satStateAtEmission(sp3, prn, recvTimeMs, rcv) {
2099
2121
  const vz = (pPlus.z - pMinus.z) / 1;
2100
2122
  velDotPos = p.x * vx + p.y * vy + p.z * vz;
2101
2123
  }
2102
- clkS = p.clk;
2124
+ clkS = (clk && clkBias(clk, prn, tEmit)) ?? p.clk;
2103
2125
  const [rx, ry, rz] = sagnacRotate(p.x, p.y, p.z, travelS);
2104
2126
  sx = rx;
2105
2127
  sy = ry;
@@ -2118,9 +2140,9 @@ function satStateAtEmission(sp3, prn, recvTimeMs, rcv) {
2118
2140
  var IDX_X = 0;
2119
2141
  var IDX_Y = 1;
2120
2142
  var IDX_Z = 2;
2121
- var IDX_CLK = 3;
2122
- var IDX_ZWD = 4;
2123
- var NBASE = 5;
2143
+ var IDX_ZWD = 3;
2144
+ var NBASE = 4;
2145
+ var CLK_VAR = 1e10;
2124
2146
  function solvePpp(epochs, sp3, opts) {
2125
2147
  const elevMask = (opts.elevationMaskDeg ?? 10) * Math.PI / 180;
2126
2148
  const codeSigma = opts.codeSigma ?? 3;
@@ -2131,8 +2153,6 @@ function solvePpp(epochs, sp3, opts) {
2131
2153
  opts.aprioriPos[0],
2132
2154
  opts.aprioriPos[1],
2133
2155
  opts.aprioriPos[2],
2134
- 0,
2135
- // receiver clock (m)
2136
2156
  0.1
2137
2157
  // zenith wet delay (m) a priori
2138
2158
  ];
@@ -2143,8 +2163,9 @@ function solvePpp(epochs, sp3, opts) {
2143
2163
  P[IDX_X][IDX_X] = 100 * 100;
2144
2164
  P[IDX_Y][IDX_Y] = 100 * 100;
2145
2165
  P[IDX_Z][IDX_Z] = 100 * 100;
2146
- P[IDX_CLK][IDX_CLK] = 1e10;
2147
2166
  P[IDX_ZWD][IDX_ZWD] = 0.5 * 0.5;
2167
+ const clkIdx = /* @__PURE__ */ new Map();
2168
+ const clockSeeded = /* @__PURE__ */ new Set();
2148
2169
  const ambIdx = /* @__PURE__ */ new Map();
2149
2170
  const lastSeen = /* @__PURE__ */ new Map();
2150
2171
  const mwState = /* @__PURE__ */ new Map();
@@ -2185,8 +2206,18 @@ function solvePpp(epochs, sp3, opts) {
2185
2206
  if (i === idx) ambIdx.delete(prn);
2186
2207
  else if (i > idx) ambIdx.set(prn, i - 1);
2187
2208
  }
2209
+ for (const [sys, i] of clkIdx) {
2210
+ if (i > idx) clkIdx.set(sys, i - 1);
2211
+ }
2212
+ };
2213
+ const clockOf = (sys) => {
2214
+ let i = clkIdx.get(sys);
2215
+ if (i === void 0) {
2216
+ i = growState(0, CLK_VAR);
2217
+ clkIdx.set(sys, i);
2218
+ }
2219
+ return i;
2188
2220
  };
2189
- let clockSeeded = false;
2190
2221
  const series = [];
2191
2222
  const truth = opts.groundTruth;
2192
2223
  const t0 = epochs[0]?.timeMs ?? 0;
@@ -2194,7 +2225,7 @@ function solvePpp(epochs, sp3, opts) {
2194
2225
  let convergedStreak = 0;
2195
2226
  for (let ei = 0; ei < epochs.length; ei++) {
2196
2227
  const epoch = epochs[ei];
2197
- P[IDX_CLK][IDX_CLK] = 1e10;
2228
+ for (const ci of clkIdx.values()) P[ci][ci] = CLK_VAR;
2198
2229
  P[IDX_ZWD][IDX_ZWD] += ztdQ * (ei > 0 ? 1 : 0);
2199
2230
  const rcv = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
2200
2231
  const [latDeg, , heightM] = ecefToGeodetic(rcv[0], rcv[1], rcv[2]);
@@ -2204,7 +2235,7 @@ function solvePpp(epochs, sp3, opts) {
2204
2235
  for (const o of epoch.obs) {
2205
2236
  if (o.c1 === 0 || o.c2 === 0 || o.l1 === 0 || o.l2 === 0) continue;
2206
2237
  const slip = detectSlip(o, ei);
2207
- const sat = satStateAtEmission(sp3, o.prn, epoch.timeMs, rcv);
2238
+ const sat = satStateAtEmission(sp3, o.prn, epoch.timeMs, rcv, opts.clk);
2208
2239
  if (!sat) continue;
2209
2240
  const [elRad, azRad] = getAer(
2210
2241
  sat.state.x,
@@ -2258,21 +2289,29 @@ function solvePpp(epochs, sp3, opts) {
2258
2289
  });
2259
2290
  }
2260
2291
  const zwd0 = x[IDX_ZWD];
2261
- if (vis.length > 0) {
2262
- if (!clockSeeded) {
2263
- const s = vis.map(
2264
- (v) => v.pIf - (v.range - v.satClkM + v.tropoFixed + zwd0 * v.mw)
2265
- ).sort((a, b) => a - b);
2266
- x[IDX_CLK] = s[Math.floor(s.length / 2)];
2267
- clockSeeded = true;
2292
+ const bySys = /* @__PURE__ */ new Map();
2293
+ for (const v of vis) {
2294
+ const sys = v.prn[0];
2295
+ const r = v.pIf - (v.range - v.satClkM + v.tropoFixed + zwd0 * v.mw);
2296
+ const arr = bySys.get(sys);
2297
+ if (arr) arr.push(r);
2298
+ else bySys.set(sys, [r]);
2299
+ }
2300
+ for (const [sys, res] of bySys) {
2301
+ const ci = clockOf(sys);
2302
+ if (!clockSeeded.has(sys)) {
2303
+ res.sort((a, b) => a - b);
2304
+ x[ci] = res[Math.floor(res.length / 2)];
2305
+ clockSeeded.add(sys);
2268
2306
  }
2269
- P[IDX_CLK][IDX_CLK] = 1e10;
2307
+ P[ci][ci] = CLK_VAR;
2270
2308
  }
2271
2309
  let nSats = 0;
2272
2310
  let sumPhaseResSq = 0;
2273
2311
  let nPhaseRes = 0;
2274
2312
  for (const v of vis) {
2275
- const common = v.range + x[IDX_CLK] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
2313
+ const ci = clockOf(v.prn[0]);
2314
+ const common = v.range + x[ci] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
2276
2315
  let ai = ambIdx.get(v.prn);
2277
2316
  if (ai === void 0 || v.slip) {
2278
2317
  const bInit = v.lIf - (common + v.windupM);
@@ -2297,13 +2336,13 @@ function solvePpp(epochs, sp3, opts) {
2297
2336
  h[IDX_X] = -v.e[0];
2298
2337
  h[IDX_Y] = -v.e[1];
2299
2338
  h[IDX_Z] = -v.e[2];
2300
- h[IDX_CLK] = 1;
2339
+ h[ci] = 1;
2301
2340
  h[IDX_ZWD] = v.mw;
2302
2341
  const innov = v.pIf - common;
2303
2342
  if (Math.abs(innov) < 1e6) ekfUpdateScalar(x, P, h, innov, rCode, 4);
2304
2343
  }
2305
2344
  {
2306
- const commonPh = v.range + x[IDX_CLK] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
2345
+ const commonPh = v.range + x[ci] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
2307
2346
  const innov = v.lIf - (commonPh + v.windupM + x[ai]);
2308
2347
  if (Math.abs(innov) > 0.15) {
2309
2348
  x[ai] = v.lIf - (commonPh + v.windupM);
@@ -2317,7 +2356,7 @@ function solvePpp(epochs, sp3, opts) {
2317
2356
  h[IDX_X] = -v.e[0];
2318
2357
  h[IDX_Y] = -v.e[1];
2319
2358
  h[IDX_Z] = -v.e[2];
2320
- h[IDX_CLK] = 1;
2359
+ h[ci] = 1;
2321
2360
  h[IDX_ZWD] = v.mw;
2322
2361
  h[ai] = 1;
2323
2362
  if (ekfUpdateScalar(x, P, h, innov, rPhase, 4)) {
@@ -1,6 +1,6 @@
1
1
  import { E as Ephemeris } from './nav-DImXUdbp.cjs';
2
2
  import { DopValues } from './orbit.cjs';
3
- import { I as IonexGrid, S as Sp3File } from './sp3-BAq8l39b.cjs';
3
+ import { I as IonexGrid, C as ClkFile, S as Sp3File } from './clk-DGVBGRzA.cjs';
4
4
  import { AntexFile } from './antex.cjs';
5
5
  import './ephemeris-Ohl6NAfw.cjs';
6
6
  import './ecef-CF0uAysr.cjs';
@@ -631,6 +631,11 @@ interface PppOptions {
631
631
  ztdProcessNoise?: number;
632
632
  /** Model + estimate the troposphere. Default true. */
633
633
  troposphere?: boolean;
634
+ /** High-rate precise satellite clocks (RINEX CLOCK, typically 30 s). When
635
+ * given, satellite clock offsets come from this by linear interpolation
636
+ * instead of the SP3 5-minute clocks — the dominant PPP accuracy term.
637
+ * Satellites/epochs absent from the CLK fall back to the SP3 clock. */
638
+ clk?: ClkFile;
634
639
  }
635
640
  interface PppEpochResult {
636
641
  timeMs: number;
@@ -1,6 +1,6 @@
1
1
  import { E as Ephemeris } from './nav-DImXUdbp.js';
2
2
  import { DopValues } from './orbit.js';
3
- import { I as IonexGrid, S as Sp3File } from './sp3-BAq8l39b.js';
3
+ import { I as IonexGrid, C as ClkFile, S as Sp3File } from './clk-DGVBGRzA.js';
4
4
  import { AntexFile } from './antex.js';
5
5
  import './ephemeris-Ohl6NAfw.js';
6
6
  import './ecef-CF0uAysr.js';
@@ -631,6 +631,11 @@ interface PppOptions {
631
631
  ztdProcessNoise?: number;
632
632
  /** Model + estimate the troposphere. Default true. */
633
633
  troposphere?: boolean;
634
+ /** High-rate precise satellite clocks (RINEX CLOCK, typically 30 s). When
635
+ * given, satellite clock offsets come from this by linear interpolation
636
+ * instead of the SP3 5-minute clocks — the dominant PPP accuracy term.
637
+ * Satellites/epochs absent from the CLK fall back to the SP3 clock. */
638
+ clk?: ClkFile;
634
639
  }
635
640
  interface PppEpochResult {
636
641
  timeMs: number;
@@ -19,11 +19,11 @@ import {
19
19
  solveSpp,
20
20
  sunEcef,
21
21
  toRtkEpoch
22
- } from "./chunk-O65WA6CM.js";
22
+ } from "./chunk-CRFFUU5Y.js";
23
23
  import "./chunk-MHGNKSSH.js";
24
24
  import "./chunk-37QNKGTC.js";
25
25
  import "./chunk-6FAL6P4G.js";
26
- import "./chunk-4KMWWCUR.js";
26
+ import "./chunk-GPEP67TG.js";
27
27
  import "./chunk-HVXYFUCB.js";
28
28
  import "./chunk-LEEU5OIO.js";
29
29
  import "./chunk-FIEWO4J4.js";
package/dist/rinex.cjs CHANGED
@@ -23,6 +23,7 @@ __export(rinex_exports, {
23
23
  EMPTY_WARNINGS: () => EMPTY_WARNINGS,
24
24
  SYSTEM_ORDER: () => SYSTEM_ORDER,
25
25
  WarningAccumulator: () => WarningAccumulator,
26
+ clkBias: () => clkBias,
26
27
  createGzipLineSink: () => createGzipLineSink,
27
28
  crxDecompress: () => crxDecompress,
28
29
  crxRepair: () => crxRepair,
@@ -31,6 +32,7 @@ __export(rinex_exports, {
31
32
  hdrLine: () => hdrLine,
32
33
  padL: () => padL,
33
34
  padR: () => padR,
35
+ parseClk: () => parseClk,
34
36
  parseCrxDataLine: () => parseCrxDataLine,
35
37
  parseIonex: () => parseIonex,
36
38
  parseNavFile: () => parseNavFile,
@@ -2388,6 +2390,66 @@ function sp3Position(sp3, prn, tMs, order = 9) {
2388
2390
  return { x, y, z, clk };
2389
2391
  }
2390
2392
 
2393
+ // src/rinex/clk.ts
2394
+ var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
2395
+ function parseClk(text) {
2396
+ const sats = {};
2397
+ let inData = false;
2398
+ for (const line of text.split("\n")) {
2399
+ if (!inData) {
2400
+ if (line.includes("END OF HEADER")) inData = true;
2401
+ continue;
2402
+ }
2403
+ if (line.length < 3 || line[0] !== "A" || line[1] !== "S") continue;
2404
+ const p = line.slice(2).trim().split(/\s+/);
2405
+ if (p.length < 9) continue;
2406
+ const prn = p[0];
2407
+ const y = +p[1];
2408
+ const mo = +p[2];
2409
+ const d = +p[3];
2410
+ const h = +p[4];
2411
+ const mi = +p[5];
2412
+ const s = +p[6];
2413
+ const bias = +p[8];
2414
+ if (!Number.isFinite(bias)) continue;
2415
+ const t = Date.UTC(y, mo - 1, d, h, mi, Math.floor(s)) + s % 1 * 1e3;
2416
+ let e = sats[prn];
2417
+ if (!e) {
2418
+ e = { t: [], c: [] };
2419
+ sats[prn] = e;
2420
+ }
2421
+ e.t.push(t);
2422
+ e.c.push(bias);
2423
+ }
2424
+ let intervalSec = 30;
2425
+ for (const e of Object.values(sats)) {
2426
+ if (e.t.length >= 2) {
2427
+ intervalSec = Math.round((e.t[1] - e.t[0]) / 1e3);
2428
+ break;
2429
+ }
2430
+ }
2431
+ return { sats, intervalSec };
2432
+ }
2433
+ function clkBias(clk, prn, tMs) {
2434
+ const e = clk.sats[prn];
2435
+ if (!e || e.t.length === 0) return null;
2436
+ const { t, c } = e;
2437
+ let lo = 0;
2438
+ let hi = t.length - 1;
2439
+ if (tMs < t[0] || tMs > t[hi]) return null;
2440
+ while (lo < hi) {
2441
+ const mid = lo + hi + 1 >> 1;
2442
+ if (t[mid] <= tMs) lo = mid;
2443
+ else hi = mid - 1;
2444
+ }
2445
+ if (t[lo] === tMs || lo === t.length - 1) return c[lo];
2446
+ const t0 = t[lo];
2447
+ const t1 = t[lo + 1];
2448
+ if (t1 - t0 > clk.intervalSec * 1e3 * 4) return null;
2449
+ const f = (tMs - t0) / (t1 - t0);
2450
+ return c[lo] + f * (c[lo + 1] - c[lo]);
2451
+ }
2452
+
2391
2453
  // src/rinex/obs-writer-common.ts
2392
2454
  function createGzipLineSink() {
2393
2455
  const encoder = new TextEncoder();
@@ -2766,6 +2828,7 @@ function writeRinex4ObsBlob(header, epochs, obsTypes) {
2766
2828
  EMPTY_WARNINGS,
2767
2829
  SYSTEM_ORDER,
2768
2830
  WarningAccumulator,
2831
+ clkBias,
2769
2832
  createGzipLineSink,
2770
2833
  crxDecompress,
2771
2834
  crxRepair,
@@ -2774,6 +2837,7 @@ function writeRinex4ObsBlob(header, epochs, obsTypes) {
2774
2837
  hdrLine,
2775
2838
  padL,
2776
2839
  padR,
2840
+ parseClk,
2777
2841
  parseCrxDataLine,
2778
2842
  parseIonex,
2779
2843
  parseNavFile,
package/dist/rinex.d.cts CHANGED
@@ -2,7 +2,7 @@ import { R as RinexHeader } from './parser-JPjjFgeP.cjs';
2
2
  export { E as EpochSummary, P as ParseOptions, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.cjs';
3
3
  import { a as NavResult, N as NavHeader, E as Ephemeris, R as RinexCnavEphemeris } from './nav-DImXUdbp.cjs';
4
4
  export { G as GlonassEphemeris, K as KeplerEphemeris, p as parseNavFile } from './nav-DImXUdbp.cjs';
5
- export { I as IonexGrid, S as Sp3File, a as Sp3Sample, p as parseIonex, b as parseSp3, s as sp3Position } from './sp3-BAq8l39b.cjs';
5
+ export { C as ClkFile, I as IonexGrid, S as Sp3File, a as Sp3Sample, c as clkBias, p as parseClk, b as parseIonex, d as parseSp3, s as sp3Position } from './clk-DGVBGRzA.cjs';
6
6
 
7
7
  /**
8
8
  * Compact RINEX (Hatanaka / CRX) decompression utilities.
package/dist/rinex.d.ts CHANGED
@@ -2,7 +2,7 @@ import { R as RinexHeader } from './parser-JPjjFgeP.js';
2
2
  export { E as EpochSummary, P as ParseOptions, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.js';
3
3
  import { a as NavResult, N as NavHeader, E as Ephemeris, R as RinexCnavEphemeris } from './nav-DImXUdbp.js';
4
4
  export { G as GlonassEphemeris, K as KeplerEphemeris, p as parseNavFile } from './nav-DImXUdbp.js';
5
- export { I as IonexGrid, S as Sp3File, a as Sp3Sample, p as parseIonex, b as parseSp3, s as sp3Position } from './sp3-BAq8l39b.js';
5
+ export { C as ClkFile, I as IonexGrid, S as Sp3File, a as Sp3Sample, c as clkBias, p as parseClk, b as parseIonex, d as parseSp3, s as sp3Position } from './clk-DGVBGRzA.js';
6
6
 
7
7
  /**
8
8
  * Compact RINEX (Hatanaka / CRX) decompression utilities.
package/dist/rinex.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  writeRinexNav,
18
18
  writeRinexNav4,
19
19
  writeRinexObsBlob
20
- } from "./chunk-6GTS5MVI.js";
20
+ } from "./chunk-KDM2NVBO.js";
21
21
  import "./chunk-726LQBGM.js";
22
22
  import "./chunk-IQ5OIMQD.js";
23
23
  import {
@@ -30,14 +30,17 @@ import {
30
30
  systemName
31
31
  } from "./chunk-3U5AX7PY.js";
32
32
  import {
33
+ clkBias,
34
+ parseClk,
33
35
  parseSp3,
34
36
  sp3Position
35
- } from "./chunk-4KMWWCUR.js";
37
+ } from "./chunk-GPEP67TG.js";
36
38
  import "./chunk-FIEWO4J4.js";
37
39
  export {
38
40
  EMPTY_WARNINGS,
39
41
  SYSTEM_ORDER,
40
42
  WarningAccumulator,
43
+ clkBias,
41
44
  createGzipLineSink,
42
45
  crxDecompress,
43
46
  crxRepair,
@@ -46,6 +49,7 @@ export {
46
49
  hdrLine,
47
50
  padL,
48
51
  padR,
52
+ parseClk,
49
53
  parseCrxDataLine,
50
54
  parseIonex,
51
55
  parseNavFile,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "1.28.0",
3
+ "version": "1.30.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,
File without changes