gnss-js 1.0.1 → 1.1.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.
package/README.md CHANGED
@@ -167,6 +167,32 @@ Every exported function has JSDoc with `@param` and `@returns` annotations inclu
167
167
  - **Class-based API** — Ergonomic wrappers like `GnssTime.fromUtc(date).gps` and `Position.fromGeodetic(lat, lon, h).utm` on top of the existing functional core
168
168
  - **RINEX observation writers** (RINEX 2/3/4 with gzip compression)
169
169
 
170
+ ## Development
171
+
172
+ ```bash
173
+ pnpm install
174
+ bash scripts/fetch-test-data.sh # RINEX test fixtures (~5 MB, checksummed; gitignored)
175
+ pnpm test
176
+ pnpm run lint
177
+ pnpm run build
178
+ ```
179
+
180
+ Without the fixtures, the nav-file test suites are skipped silently (`describe.skipIf`), so run the fetch script before trusting a green test run.
181
+
182
+ **Releasing**: bump the version and push the tag — CI publishes to npm via Trusted Publishing (one-time setup: npm package settings → Trusted Publisher → this repo + publish.yml) and creates the GitHub Release:
183
+
184
+ ```bash
185
+ npm version patch # or minor/major; updates package.json + creates the tag
186
+ git push --follow-tags
187
+ ```
188
+
189
+ **Developing against [gnsscalc](https://github.com/MiguelPuntoEs/gnsscalc)** without publishing:
190
+
191
+ ```bash
192
+ cd ../gnsscalc && pnpm link ../gnss-js # then `pnpm run build --watch` here
193
+ # undo with: pnpm unlink gnss-js && pnpm install
194
+ ```
195
+
170
196
  ## License
171
197
 
172
198
  This project is dual-licensed:
package/dist/analysis.cjs CHANGED
@@ -40,7 +40,8 @@ var SYSTEM_NAMES = {
40
40
  var C_LIGHT = 299792458;
41
41
  var FREQ = {
42
42
  G: { "1": 157542e4, "2": 12276e5, "5": 117645e4 },
43
- R: { "3": 1202025e3 },
43
+ // R bands 4/6 are the CDMA L1OC/L2OC center frequencies
44
+ R: { "3": 1202025e3, "4": 1600995e3, "6": 124806e4 },
44
45
  E: {
45
46
  "1": 157542e4,
46
47
  "5": 117645e4,
@@ -98,7 +99,6 @@ var GLO_F1_BASE = 1602e6;
98
99
  var GLO_F1_STEP = 562500;
99
100
  var GLO_F2_BASE = 1246e6;
100
101
  var GLO_F2_STEP = 437500;
101
- var GLO_F3 = 1202025e3;
102
102
  var GLO_CHANNEL_FALLBACK = {
103
103
  R01: 1,
104
104
  R02: -4,
@@ -134,11 +134,11 @@ function buildGloChannelMap(slots) {
134
134
  return map;
135
135
  }
136
136
  function gloFreq(gloChannels, prn, band) {
137
+ if (band === "3" || band === "4" || band === "6") return FREQ.R[band];
137
138
  const k = gloChannels[prn];
138
139
  if (k === void 0) return void 0;
139
140
  if (band === "1") return GLO_F1_BASE + k * GLO_F1_STEP;
140
141
  if (band === "2") return GLO_F2_BASE + k * GLO_F2_STEP;
141
- if (band === "3") return GLO_F3;
142
142
  return void 0;
143
143
  }
144
144
  function getFreq(gloChannels, prn, band) {
package/dist/analysis.js CHANGED
@@ -3,9 +3,9 @@ import {
3
3
  CycleSlipAccumulator,
4
4
  MultipathAccumulator,
5
5
  analyzeQuality
6
- } from "./chunk-SO3POWWR.js";
7
- import "./chunk-HBLU2EJ4.js";
8
- import "./chunk-LWNTWBHB.js";
6
+ } from "./chunk-G3N4S3DM.js";
7
+ import "./chunk-OZCYOM5D.js";
8
+ import "./chunk-W5WKEV7U.js";
9
9
  export {
10
10
  CompletenessAccumulator,
11
11
  CycleSlipAccumulator,
@@ -1,30 +1,39 @@
1
+ import {
2
+ FREQ,
3
+ GLO_F1_BASE,
4
+ GLO_F1_STEP,
5
+ GLO_F2_BASE,
6
+ GLO_F2_STEP
7
+ } from "./chunk-W5WKEV7U.js";
8
+
1
9
  // src/signals/definitions.ts
2
- var FREQ_GPS_L1 = 1575.42;
3
- var FREQ_GPS_L2 = 1227.6;
4
- var FREQ_GPS_L5 = 1176.45;
5
- var FREQ_GLO_L1 = 1602;
6
- var FREQ_GLO_L2 = 1246;
7
- var DELTA_GLO_L1 = 0.5625;
8
- var DELTA_GLO_L2 = 0.4375;
9
- var FREQ_GLO_L1OC = 1600.995;
10
- var FREQ_GLO_L2OC = 1248.06;
11
- var FREQ_GLO_L3OC = 1202.025;
12
- var FREQ_GAL_E1 = 1575.42;
13
- var FREQ_GAL_E5 = 1191.795;
14
- var FREQ_GAL_E5a = 1176.45;
15
- var FREQ_GAL_E5b = 1207.14;
16
- var FREQ_GAL_E6 = 1278.75;
17
- var FREQ_BDS_B1I = 1561.098;
18
- var FREQ_BDS_B2I = 1207.14;
19
- var FREQ_BDS_B3I = 1268.52;
20
- var FREQ_BDS_B1A = 1575.42;
21
- var FREQ_BDS_B1C = 1575.42;
22
- var FREQ_BDS_B3A = 1268.52;
23
- var FREQ_QZS_L1 = 1575.42;
24
- var FREQ_QZS_L2 = 1227.6;
25
- var FREQ_QZS_L5 = 1176.45;
26
- var FREQ_QZS_L6 = 1278.75;
27
- var FREQ_NAVIC_L5 = 1176.45;
10
+ var MHz = 1e6;
11
+ var FREQ_GPS_L1 = FREQ.G["1"] / MHz;
12
+ var FREQ_GPS_L2 = FREQ.G["2"] / MHz;
13
+ var FREQ_GPS_L5 = FREQ.G["5"] / MHz;
14
+ var FREQ_GLO_L1 = GLO_F1_BASE / MHz;
15
+ var FREQ_GLO_L2 = GLO_F2_BASE / MHz;
16
+ var DELTA_GLO_L1 = GLO_F1_STEP / MHz;
17
+ var DELTA_GLO_L2 = GLO_F2_STEP / MHz;
18
+ var FREQ_GLO_L1OC = FREQ.R["4"] / MHz;
19
+ var FREQ_GLO_L2OC = FREQ.R["6"] / MHz;
20
+ var FREQ_GLO_L3OC = FREQ.R["3"] / MHz;
21
+ var FREQ_GAL_E1 = FREQ.E["1"] / MHz;
22
+ var FREQ_GAL_E5 = FREQ.E["8"] / MHz;
23
+ var FREQ_GAL_E5a = FREQ.E["5"] / MHz;
24
+ var FREQ_GAL_E5b = FREQ.E["7"] / MHz;
25
+ var FREQ_GAL_E6 = FREQ.E["6"] / MHz;
26
+ var FREQ_BDS_B1I = FREQ.C["2"] / MHz;
27
+ var FREQ_BDS_B2I = FREQ.C["7"] / MHz;
28
+ var FREQ_BDS_B3I = FREQ.C["6"] / MHz;
29
+ var FREQ_BDS_B1A = FREQ.C["1"] / MHz;
30
+ var FREQ_BDS_B1C = FREQ.C["1"] / MHz;
31
+ var FREQ_BDS_B3A = FREQ.C["6"] / MHz;
32
+ var FREQ_QZS_L1 = FREQ.J["1"] / MHz;
33
+ var FREQ_QZS_L2 = FREQ.J["2"] / MHz;
34
+ var FREQ_QZS_L5 = FREQ.J["5"] / MHz;
35
+ var FREQ_QZS_L6 = FREQ.J["6"] / MHz;
36
+ var FREQ_NAVIC_L5 = FREQ.I["5"] / MHz;
28
37
  var GPS_COLOR = "#4ade80";
29
38
  var GPS_M_COLOR = "#ef4444";
30
39
  var GPS_P_COLOR = "#dbc51f";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseRinexStream
3
- } from "./chunk-HBLU2EJ4.js";
3
+ } from "./chunk-OZCYOM5D.js";
4
4
  import {
5
5
  ARC_GAP_FACTOR,
6
6
  BAND_LABELS,
@@ -11,7 +11,7 @@ import {
11
11
  buildGloChannelMap,
12
12
  buildObsIndices,
13
13
  getFreq
14
- } from "./chunk-LWNTWBHB.js";
14
+ } from "./chunk-W5WKEV7U.js";
15
15
 
16
16
  // src/analysis/multipath.ts
17
17
  var MIN_ARC_LENGTH = 10;
@@ -1,12 +1,25 @@
1
1
  import {
2
2
  C_LIGHT,
3
+ FREQ,
3
4
  GLO_F1_BASE,
4
5
  GLO_F1_STEP,
5
6
  GLO_F2_BASE,
6
7
  GLO_F2_STEP
7
- } from "./chunk-LWNTWBHB.js";
8
+ } from "./chunk-W5WKEV7U.js";
9
+ import {
10
+ MILLISECONDS_IN_WEEK,
11
+ START_BDS_TIME,
12
+ START_GPS_TIME
13
+ } from "./chunk-LEEU5OIO.js";
8
14
 
9
15
  // src/rtcm3/decoder.ts
16
+ var debugHandler = null;
17
+ function setRtcm3DebugHandler(fn) {
18
+ debugHandler = fn;
19
+ }
20
+ function reportDecodeError(context, error) {
21
+ if (debugHandler) debugHandler(context, error);
22
+ }
10
23
  var BitReader = class {
11
24
  data;
12
25
  bitPos = 0;
@@ -541,31 +554,32 @@ function decodeEphemeris(frame) {
541
554
  default:
542
555
  return null;
543
556
  }
544
- } catch {
557
+ } catch (err) {
558
+ reportDecodeError("ephemeris", err);
545
559
  return null;
546
560
  }
547
561
  }
548
562
 
549
563
  // src/rtcm3/msm.ts
550
- var GPS_L1 = 157542e4;
551
- var GPS_L2 = 12276e5;
552
- var GPS_L5 = 117645e4;
553
- var GLO_L1a = 1600995e3;
554
- var GLO_L2a = 124806e4;
555
- var GLO_L3 = 1202025e3;
556
- var GAL_E1 = 157542e4;
557
- var GAL_E5a = 117645e4;
558
- var GAL_E5b = 120714e4;
559
- var GAL_E5 = 1191795e3;
560
- var GAL_E6 = 127875e4;
561
- var BDS_B1 = 1561098e3;
562
- var BDS_B3 = 126852e4;
563
- var BDS_B2 = 120714e4;
564
- var BDS_B1C = 157542e4;
565
- var BDS_B2a = 117645e4;
566
- var BDS_B2b = 120714e4;
567
- var QZSS_L6 = 127875e4;
568
- var NAVIC_S = 2492028e3;
564
+ var GPS_L1 = FREQ.G["1"];
565
+ var GPS_L2 = FREQ.G["2"];
566
+ var GPS_L5 = FREQ.G["5"];
567
+ var GLO_L1a = FREQ.R["4"];
568
+ var GLO_L2a = FREQ.R["6"];
569
+ var GLO_L3 = FREQ.R["3"];
570
+ var GAL_E1 = FREQ.E["1"];
571
+ var GAL_E5a = FREQ.E["5"];
572
+ var GAL_E5b = FREQ.E["7"];
573
+ var GAL_E5 = FREQ.E["8"];
574
+ var GAL_E6 = FREQ.E["6"];
575
+ var BDS_B1 = FREQ.C["2"];
576
+ var BDS_B3 = FREQ.C["6"];
577
+ var BDS_B2 = FREQ.C["7"];
578
+ var BDS_B1C = FREQ.C["1"];
579
+ var BDS_B2a = FREQ.C["5"];
580
+ var BDS_B2b = FREQ.C["7"];
581
+ var QZSS_L6 = FREQ.J["6"];
582
+ var NAVIC_S = FREQ.I["9"];
569
583
  var EMPTY = { code: "", freq: 0 };
570
584
  function sd(code, freq) {
571
585
  return { code, freq };
@@ -817,40 +831,6 @@ function signalTable(sys) {
817
831
  return GPS_SIGNALS;
818
832
  }
819
833
  }
820
- var BitReader2 = class {
821
- data;
822
- pos;
823
- constructor(data, startBit = 0) {
824
- this.data = data;
825
- this.pos = startBit;
826
- }
827
- /** Read unsigned value of numBits bits (max 53) */
828
- u(numBits) {
829
- let val = 0;
830
- for (let i = 0; i < numBits; i++) {
831
- const byteIdx = this.pos + i >>> 3;
832
- const bitIdx = 7 - (this.pos + i & 7);
833
- const bit = byteIdx < this.data.length ? this.data[byteIdx] >>> bitIdx & 1 : 0;
834
- val = val * 2 + bit;
835
- }
836
- this.pos += numBits;
837
- return val;
838
- }
839
- /** Read signed value (two's complement) */
840
- s(numBits) {
841
- const raw = this.u(numBits);
842
- const half = 2 ** (numBits - 1);
843
- return raw >= half ? raw - 2 ** numBits : raw;
844
- }
845
- /** Skip bits */
846
- skip(n) {
847
- this.pos += n;
848
- }
849
- /** Current bit position */
850
- get position() {
851
- return this.pos;
852
- }
853
- };
854
834
  var gloFreqNum = new Int8Array(64).fill(-128);
855
835
  function gloWavelength(satIdx, freqMarker) {
856
836
  const k = gloFreqNum[satIdx];
@@ -885,28 +865,9 @@ function lockTimeSec(msmType, lti) {
885
865
  if (variant <= 5) {
886
866
  return LTI_TABLE_4BIT[lti] ?? 0;
887
867
  }
888
- if (lti < 64) return lti;
889
- if (lti < 96) return (lti - 64) * 2 + 64;
890
- if (lti < 128) return (lti - 96) * 4 + 128;
891
- if (lti < 160) return (lti - 128) * 8 + 256;
892
- if (lti < 192) return (lti - 160) * 16 + 512;
893
- if (lti < 224) return (lti - 192) * 32 + 1024;
894
- if (lti < 256) return (lti - 224) * 64 + 2048;
895
- if (lti < 288) return (lti - 256) * 128 + 4096;
896
- if (lti < 320) return (lti - 288) * 256 + 8192;
897
- if (lti < 352) return (lti - 320) * 512 + 16384;
898
- if (lti < 384) return (lti - 352) * 1024 + 32768;
899
- if (lti < 416) return (lti - 384) * 2048 + 65536;
900
- if (lti < 448) return (lti - 416) * 4096 + 131072;
901
- if (lti < 480) return (lti - 448) * 8192 + 262144;
902
- if (lti < 512) return (lti - 480) * 16384 + 524288;
903
- if (lti < 544) return (lti - 512) * 32768 + 1048576;
904
- if (lti < 576) return (lti - 544) * 65536 + 2097152;
905
- if (lti < 608) return (lti - 576) * 131072 + 4194304;
906
- if (lti < 640) return (lti - 608) * 262144 + 8388608;
907
- if (lti < 672) return (lti - 640) * 524288 + 16777216;
908
- if (lti < 704) return (lti - 672) * 1048576 + 33554432;
909
- return (lti - 704) * 2097152 + 67108864;
868
+ if (lti < 64) return lti / 1e3;
869
+ const n = Math.min(20, Math.floor((lti - 64) / 32));
870
+ return ((lti - 64 - 32 * n) * 2 ** (n + 1) + 2 ** (n + 6)) / 1e3;
910
871
  }
911
872
  function msmSystem(type) {
912
873
  if (type >= 1071 && type <= 1077) return "G";
@@ -932,10 +893,10 @@ function decodeMsmFull(frame) {
932
893
  const payload = frame.payload;
933
894
  if (!payload || payload.length < 10) return null;
934
895
  try {
935
- const bits = new BitReader2(payload);
896
+ const bits = new BitReader(payload);
936
897
  bits.skip(12);
937
898
  bits.skip(12);
938
- const epochMs = bits.u(30);
899
+ const epochMs = bits.readU(30);
939
900
  bits.skip(1);
940
901
  bits.skip(3);
941
902
  bits.skip(7);
@@ -943,8 +904,8 @@ function decodeMsmFull(frame) {
943
904
  bits.skip(2);
944
905
  bits.skip(1);
945
906
  bits.skip(3);
946
- const satMaskHi = bits.u(32) >>> 0;
947
- const satMaskLo = bits.u(32) >>> 0;
907
+ const satMaskHi = bits.readU(32) >>> 0;
908
+ const satMaskLo = bits.readU(32) >>> 0;
948
909
  const satIndices = [];
949
910
  for (let i = 0; i < 32; i++) {
950
911
  if (satMaskHi & 1 << 31 - i) satIndices.push(i + 1);
@@ -954,7 +915,7 @@ function decodeMsmFull(frame) {
954
915
  }
955
916
  const numSat = satIndices.length;
956
917
  if (numSat === 0) return null;
957
- const sigMask = bits.u(32) >>> 0;
918
+ const sigMask = bits.readU(32) >>> 0;
958
919
  const sigIndices = [];
959
920
  for (let i = 0; i < 32; i++) {
960
921
  if (sigMask & 1 << 31 - i) sigIndices.push(i);
@@ -964,7 +925,7 @@ function decodeMsmFull(frame) {
964
925
  const numCells = numSat * numSig;
965
926
  const cellMask = [];
966
927
  for (let i = 0; i < numCells; i++) {
967
- cellMask.push(bits.u(1) === 1);
928
+ cellMask.push(bits.readU(1) === 1);
968
929
  }
969
930
  let activeCells = 0;
970
931
  for (const c of cellMask) if (c) activeCells++;
@@ -974,13 +935,13 @@ function decodeMsmFull(frame) {
974
935
  const extsat = new Int8Array(numSat);
975
936
  const rdop = new Float64Array(numSat);
976
937
  if (variant === 4 || variant === 6) {
977
- for (let j = 0; j < numSat; j++) rrint[j] = bits.u(8);
978
- for (let j = 0; j < numSat; j++) rrmod[j] = bits.u(10) / 1024;
938
+ for (let j = 0; j < numSat; j++) rrint[j] = bits.readU(8);
939
+ for (let j = 0; j < numSat; j++) rrmod[j] = bits.readU(10) / 1024;
979
940
  } else {
980
- for (let j = 0; j < numSat; j++) rrint[j] = bits.u(8);
981
- for (let j = 0; j < numSat; j++) extsat[j] = bits.u(4);
982
- for (let j = 0; j < numSat; j++) rrmod[j] = bits.u(10) / 1024;
983
- for (let j = 0; j < numSat; j++) rdop[j] = bits.s(14) * 1;
941
+ for (let j = 0; j < numSat; j++) rrint[j] = bits.readU(8);
942
+ for (let j = 0; j < numSat; j++) extsat[j] = bits.readU(4);
943
+ for (let j = 0; j < numSat; j++) rrmod[j] = bits.readU(10) / 1024;
944
+ for (let j = 0; j < numSat; j++) rdop[j] = bits.readS(14) * 1;
984
945
  }
985
946
  if (sys === "R") {
986
947
  for (let j = 0; j < numSat; j++) {
@@ -1000,31 +961,31 @@ function decodeMsmFull(frame) {
1000
961
  cp.fill(-1e30);
1001
962
  dop.fill(-1e30);
1002
963
  if (variant === 4) {
1003
- for (let i = 0; i < activeCells; i++) psr[i] = bits.s(15) / 2 ** 24;
1004
- for (let i = 0; i < activeCells; i++) cp[i] = bits.s(22) / 2 ** 29;
1005
- for (let i = 0; i < activeCells; i++) ll[i] = bits.u(4);
1006
- for (let i = 0; i < activeCells; i++) hc[i] = bits.u(1);
1007
- for (let i = 0; i < activeCells; i++) cnr[i] = bits.u(6);
964
+ for (let i = 0; i < activeCells; i++) psr[i] = bits.readS(15) / 2 ** 24;
965
+ for (let i = 0; i < activeCells; i++) cp[i] = bits.readS(22) / 2 ** 29;
966
+ for (let i = 0; i < activeCells; i++) ll[i] = bits.readU(4);
967
+ for (let i = 0; i < activeCells; i++) hc[i] = bits.readU(1);
968
+ for (let i = 0; i < activeCells; i++) cnr[i] = bits.readU(6);
1008
969
  } else if (variant === 5) {
1009
- for (let i = 0; i < activeCells; i++) psr[i] = bits.s(15) / 2 ** 24;
1010
- for (let i = 0; i < activeCells; i++) cp[i] = bits.s(22) / 2 ** 29;
1011
- for (let i = 0; i < activeCells; i++) ll[i] = bits.u(4);
1012
- for (let i = 0; i < activeCells; i++) hc[i] = bits.u(1);
1013
- for (let i = 0; i < activeCells; i++) cnr[i] = bits.u(6);
1014
- for (let i = 0; i < activeCells; i++) dop[i] = bits.s(15) * 1e-4;
970
+ for (let i = 0; i < activeCells; i++) psr[i] = bits.readS(15) / 2 ** 24;
971
+ for (let i = 0; i < activeCells; i++) cp[i] = bits.readS(22) / 2 ** 29;
972
+ for (let i = 0; i < activeCells; i++) ll[i] = bits.readU(4);
973
+ for (let i = 0; i < activeCells; i++) hc[i] = bits.readU(1);
974
+ for (let i = 0; i < activeCells; i++) cnr[i] = bits.readU(6);
975
+ for (let i = 0; i < activeCells; i++) dop[i] = bits.readS(15) * 1e-4;
1015
976
  } else if (variant === 6) {
1016
- for (let i = 0; i < activeCells; i++) psr[i] = bits.s(20) / 2 ** 29;
1017
- for (let i = 0; i < activeCells; i++) cp[i] = bits.s(24) / 2 ** 31;
1018
- for (let i = 0; i < activeCells; i++) ll[i] = bits.u(10);
1019
- for (let i = 0; i < activeCells; i++) hc[i] = bits.u(1);
1020
- for (let i = 0; i < activeCells; i++) cnr[i] = bits.u(10) / 16;
977
+ for (let i = 0; i < activeCells; i++) psr[i] = bits.readS(20) / 2 ** 29;
978
+ for (let i = 0; i < activeCells; i++) cp[i] = bits.readS(24) / 2 ** 31;
979
+ for (let i = 0; i < activeCells; i++) ll[i] = bits.readU(10);
980
+ for (let i = 0; i < activeCells; i++) hc[i] = bits.readU(1);
981
+ for (let i = 0; i < activeCells; i++) cnr[i] = bits.readU(10) / 16;
1021
982
  } else {
1022
- for (let i = 0; i < activeCells; i++) psr[i] = bits.s(20) / 2 ** 29;
1023
- for (let i = 0; i < activeCells; i++) cp[i] = bits.s(24) / 2 ** 31;
1024
- for (let i = 0; i < activeCells; i++) ll[i] = bits.u(10);
1025
- for (let i = 0; i < activeCells; i++) hc[i] = bits.u(1);
1026
- for (let i = 0; i < activeCells; i++) cnr[i] = bits.u(10) / 16;
1027
- for (let i = 0; i < activeCells; i++) dop[i] = bits.s(15) * 1e-4;
983
+ for (let i = 0; i < activeCells; i++) psr[i] = bits.readS(20) / 2 ** 29;
984
+ for (let i = 0; i < activeCells; i++) cp[i] = bits.readS(24) / 2 ** 31;
985
+ for (let i = 0; i < activeCells; i++) ll[i] = bits.readU(10);
986
+ for (let i = 0; i < activeCells; i++) hc[i] = bits.readU(1);
987
+ for (let i = 0; i < activeCells; i++) cnr[i] = bits.readU(10) / 16;
988
+ for (let i = 0; i < activeCells; i++) dop[i] = bits.readS(15) * 1e-4;
1028
989
  }
1029
990
  const sigTable = signalTable(sys);
1030
991
  const observations = [];
@@ -1058,11 +1019,11 @@ function decodeMsmFull(frame) {
1058
1019
  wavelength
1059
1020
  };
1060
1021
  const psrVal = psr[cellIdx];
1061
- if (psrVal > -1 / (1 << 10)) {
1022
+ if (psrVal > -(2 ** -10)) {
1062
1023
  signal.pseudorange = psrVal * C_LIGHT / 1e3 + roughRange_m;
1063
1024
  }
1064
1025
  const cpVal = cp[cellIdx];
1065
- if (cpVal > -1 / (1 << 8) && wavelength > 0) {
1026
+ if (cpVal > -(2 ** -8) && wavelength > 0) {
1066
1027
  signal.phase = cpVal * C_LIGHT / 1e3 / wavelength + roughRange_m / wavelength;
1067
1028
  }
1068
1029
  if (variant === 5 || variant === 7) {
@@ -1090,12 +1051,13 @@ function decodeMsmFull(frame) {
1090
1051
  system: sys,
1091
1052
  observations
1092
1053
  };
1093
- } catch {
1054
+ } catch (err) {
1055
+ reportDecodeError("msm", err);
1094
1056
  return null;
1095
1057
  }
1096
1058
  }
1097
- var GPS_EPOCH = Date.UTC(1980, 0, 6);
1098
- var MS_PER_WEEK = 6048e5;
1059
+ var GPS_EPOCH = START_GPS_TIME.getTime();
1060
+ var MS_PER_WEEK = MILLISECONDS_IN_WEEK;
1099
1061
  function msmEpochToDate(sys, epochMs, refTime = /* @__PURE__ */ new Date()) {
1100
1062
  const refMs = refTime.getTime();
1101
1063
  if (sys === "R") {
@@ -1110,11 +1072,12 @@ function msmEpochToDate(sys, epochMs, refTime = /* @__PURE__ */ new Date()) {
1110
1072
  return new Date(t2);
1111
1073
  }
1112
1074
  if (sys === "C") {
1113
- const BDS_EPOCH = Date.UTC(2006, 0, 1) - 14e3;
1075
+ const BDS_EPOCH = START_BDS_TIME.getTime();
1114
1076
  const weeksSinceEpoch2 = Math.floor((refMs - BDS_EPOCH) / MS_PER_WEEK);
1115
1077
  let t2 = BDS_EPOCH + weeksSinceEpoch2 * MS_PER_WEEK + epochMs;
1116
1078
  if (t2 - refMs > MS_PER_WEEK / 2) t2 -= MS_PER_WEEK;
1117
1079
  else if (refMs - t2 > MS_PER_WEEK / 2) t2 += MS_PER_WEEK;
1080
+ t2 -= 18e3;
1118
1081
  return new Date(t2);
1119
1082
  }
1120
1083
  const weeksSinceEpoch = Math.floor((refMs - GPS_EPOCH) / MS_PER_WEEK);
@@ -1124,6 +1087,11 @@ function msmEpochToDate(sys, epochMs, refTime = /* @__PURE__ */ new Date()) {
1124
1087
  t -= 18e3;
1125
1088
  return new Date(t);
1126
1089
  }
1090
+ function setGloFreqNumber(slot, k) {
1091
+ if (slot >= 1 && slot <= 64 && k >= -7 && k <= 13) {
1092
+ gloFreqNum[slot - 1] = k;
1093
+ }
1094
+ }
1127
1095
  function resetGloFreqCache() {
1128
1096
  gloFreqNum.fill(-128);
1129
1097
  }
@@ -1257,7 +1225,8 @@ function updateStationMeta(meta, frame) {
1257
1225
  default:
1258
1226
  return false;
1259
1227
  }
1260
- } catch {
1228
+ } catch (err) {
1229
+ reportDecodeError("station", err);
1261
1230
  return false;
1262
1231
  }
1263
1232
  }
@@ -1445,6 +1414,9 @@ function updateStreamStats(stats, frames, rawBytes) {
1445
1414
  const eph = decodeEphemeris(frame);
1446
1415
  if (eph) {
1447
1416
  stats.ephemerides.set(eph.prn, eph);
1417
+ if (eph.freqChannel !== void 0 && eph.prn.startsWith("R")) {
1418
+ setGloFreqNumber(parseInt(eph.prn.slice(1), 10), eph.freqChannel);
1419
+ }
1448
1420
  }
1449
1421
  updateStationMeta(stats.stationMeta, frame);
1450
1422
  }
@@ -1459,12 +1431,15 @@ function updateStreamStats(stats, frames, rawBytes) {
1459
1431
  }
1460
1432
 
1461
1433
  export {
1434
+ setRtcm3DebugHandler,
1435
+ reportDecodeError,
1462
1436
  BitReader,
1463
1437
  Rtcm3Decoder,
1464
1438
  readString,
1465
1439
  decodeEphemeris,
1466
1440
  decodeMsmFull,
1467
1441
  msmEpochToDate,
1442
+ setGloFreqNumber,
1468
1443
  resetGloFreqCache,
1469
1444
  createStationMeta,
1470
1445
  updateStationMeta,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SYSTEM_NAMES
3
- } from "./chunk-LWNTWBHB.js";
3
+ } from "./chunk-W5WKEV7U.js";
4
4
 
5
5
  // src/rinex/crx.ts
6
6
  function crxRepair(old, diff) {
@@ -1,6 +1,10 @@
1
1
  import {
2
2
  ecefToGeodetic
3
3
  } from "./chunk-37QNKGTC.js";
4
+ import {
5
+ START_BDS_TIME,
6
+ START_GPS_TIME
7
+ } from "./chunk-LEEU5OIO.js";
4
8
 
5
9
  // src/orbit/index.ts
6
10
  var GM_GPS = 3986005e8;
@@ -8,6 +12,8 @@ var GM_GAL = 3986004418e5;
8
12
  var GM_BDS = 3986004418e5;
9
13
  var GM_GLO = 39860044e7;
10
14
  var OMEGA_E = 72921151467e-15;
15
+ var BDS_GEO_MAX_INCLINATION_RAD = 0.1;
16
+ var BDS_GEO_MIN_SEMIMAJOR_AXIS_M = 4e7;
11
17
  var AE_GLO = 6378136;
12
18
  var J2_GLO = 108263e-8;
13
19
  var TWO_PI = 2 * Math.PI;
@@ -45,7 +51,7 @@ function keplerPosition(eph, t) {
45
51
  const ik = eph.i0 + dik + eph.idot * tk;
46
52
  const xp = rk * Math.cos(uk);
47
53
  const yp = rk * Math.sin(uk);
48
- const isBdsGeo = eph.system === "C" && Math.abs(eph.i0) < 0.1 && a > 4e7;
54
+ const isBdsGeo = eph.system === "C" && Math.abs(eph.i0) < BDS_GEO_MAX_INCLINATION_RAD && a > BDS_GEO_MIN_SEMIMAJOR_AXIS_M;
49
55
  if (isBdsGeo) {
50
56
  const omegak2 = eph.omega0 + eph.omegaDot * tk - OMEGA_E * eph.toe;
51
57
  const cosO2 = Math.cos(omegak2);
@@ -226,9 +232,10 @@ function computeSatPosition(eph, timeMs) {
226
232
  if (eph.system === "R" || eph.system === "S") {
227
233
  return glonassPosition(eph, timeMs / 1e3);
228
234
  }
229
- const GPS_EPOCH = Date.UTC(1980, 0, 6);
235
+ const GPS_EPOCH = START_GPS_TIME.getTime();
230
236
  const gpsSeconds = (timeMs - GPS_EPOCH) / 1e3;
231
- const tow = gpsSeconds % (7 * 86400);
237
+ let tow = gpsSeconds % (7 * 86400);
238
+ if (eph.system === "C") tow = (tow - 14 + 604800) % 604800;
232
239
  return keplerPosition(eph, tow);
233
240
  }
234
241
  function navTimesFromEph(ephs) {
@@ -310,8 +317,8 @@ function selectBest(ephs, timeMs) {
310
317
  }
311
318
  return best;
312
319
  }
313
- var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
314
- var BDS_EPOCH_MS = Date.UTC(2006, 0, 1);
320
+ var GPS_EPOCH_MS = START_GPS_TIME.getTime();
321
+ var BDS_EPOCH_MS = START_BDS_TIME.getTime();
315
322
  var GAL_EPOCH_MS = GPS_EPOCH_MS;
316
323
  function ephInfoToEphemeris(info) {
317
324
  const sys = info.prn.charAt(0);
@@ -352,6 +359,9 @@ function ephInfoToEphemeris(info) {
352
359
  }
353
360
  if (info.sqrtA === void 0 || info.eccentricity === void 0 || info.inclination === void 0 || info.omega0 === void 0 || info.argPerigee === void 0 || info.meanAnomaly === void 0 || info.toe === void 0 || info.week === void 0)
354
361
  return null;
362
+ if (info.eccentricity < 0 || info.eccentricity >= 1 || info.sqrtA < 3e3 || info.sqrtA > 9e3 || info.toe < 0 || info.toe > 604800) {
363
+ return null;
364
+ }
355
365
  let epochMs;
356
366
  const tocSec = info.toc ?? info.toe;
357
367
  if (sys === "C") {
@@ -20,7 +20,8 @@ var SYS_SHORT = {
20
20
  var C_LIGHT = 299792458;
21
21
  var FREQ = {
22
22
  G: { "1": 157542e4, "2": 12276e5, "5": 117645e4 },
23
- R: { "3": 1202025e3 },
23
+ // R bands 4/6 are the CDMA L1OC/L2OC center frequencies
24
+ R: { "3": 1202025e3, "4": 1600995e3, "6": 124806e4 },
24
25
  E: {
25
26
  "1": 157542e4,
26
27
  "5": 117645e4,
@@ -114,11 +115,11 @@ function buildGloChannelMap(slots) {
114
115
  return map;
115
116
  }
116
117
  function gloFreq(gloChannels, prn, band) {
118
+ if (band === "3" || band === "4" || band === "6") return FREQ.R[band];
117
119
  const k = gloChannels[prn];
118
120
  if (k === void 0) return void 0;
119
121
  if (band === "1") return GLO_F1_BASE + k * GLO_F1_STEP;
120
122
  if (band === "2") return GLO_F2_BASE + k * GLO_F2_STEP;
121
- if (band === "3") return GLO_F3;
122
123
  return void 0;
123
124
  }
124
125
  function getFreq(gloChannels, prn, band) {
@@ -148,7 +148,8 @@ var SYS_SHORT = {
148
148
  var C_LIGHT = 299792458;
149
149
  var FREQ = {
150
150
  G: { "1": 157542e4, "2": 12276e5, "5": 117645e4 },
151
- R: { "3": 1202025e3 },
151
+ // R bands 4/6 are the CDMA L1OC/L2OC center frequencies
152
+ R: { "3": 1202025e3, "4": 1600995e3, "6": 124806e4 },
152
153
  E: {
153
154
  "1": 157542e4,
154
155
  "5": 117645e4,
@@ -242,11 +243,11 @@ function buildGloChannelMap(slots) {
242
243
  return map;
243
244
  }
244
245
  function gloFreq(gloChannels, prn, band) {
246
+ if (band === "3" || band === "4" || band === "6") return FREQ.R[band];
245
247
  const k = gloChannels[prn];
246
248
  if (k === void 0) return void 0;
247
249
  if (band === "1") return GLO_F1_BASE + k * GLO_F1_STEP;
248
250
  if (band === "2") return GLO_F2_BASE + k * GLO_F2_STEP;
249
- if (band === "3") return GLO_F3;
250
251
  return void 0;
251
252
  }
252
253
  function getFreq(gloChannels, prn, band) {