gnss-js 1.13.0 → 1.14.1

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,116 @@
1
+ // src/ubx/index.ts
2
+ var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
3
+ var MS_PER_WEEK = 7 * 864e5;
4
+ var SIGNALS = {
5
+ 0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
6
+ // GPS L1C/A, L2CL, L2CM
7
+ 1: { 0: ["S", "1C"] },
8
+ // SBAS L1C/A
9
+ 2: { 0: ["E", "1X"], 1: ["E", "1X"], 5: ["E", "7X"], 6: ["E", "7X"] },
10
+ // E1C/B, E5bI/Q
11
+ 3: { 0: ["C", "2I"], 1: ["C", "2I"], 2: ["C", "7I"], 3: ["C", "7I"] },
12
+ // B1I D1/D2, B2I D1/D2
13
+ 5: { 0: ["J", "1C"], 4: ["J", "2X"], 5: ["J", "2X"] },
14
+ // QZSS L1C/A, L2CM/CL
15
+ 6: { 0: ["R", "1C"], 2: ["R", "2C"] }
16
+ // GLONASS L1OF, L2OF
17
+ };
18
+ function prnFor(gnssId, svId) {
19
+ const two = (n) => String(n).padStart(2, "0");
20
+ switch (gnssId) {
21
+ case 0:
22
+ return svId >= 1 && svId <= 32 ? `G${two(svId)}` : null;
23
+ case 1:
24
+ return svId >= 120 && svId <= 158 ? `S${two(svId - 100)}` : null;
25
+ case 2:
26
+ return svId >= 1 && svId <= 36 ? `E${two(svId)}` : null;
27
+ case 3:
28
+ return svId >= 1 && svId <= 63 ? `C${two(svId)}` : null;
29
+ case 5:
30
+ return svId >= 1 && svId <= 10 ? `J${two(svId)}` : null;
31
+ case 6:
32
+ return svId >= 1 && svId <= 32 ? `R${two(svId)}` : null;
33
+ default:
34
+ return null;
35
+ }
36
+ }
37
+ function parseUbxRawx(data) {
38
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
39
+ const epochs = [];
40
+ const messageCounts = {};
41
+ const obsCodes = {};
42
+ let badChecksums = 0;
43
+ let i = 0;
44
+ while (i + 8 <= data.length) {
45
+ if (data[i] !== 181 || data[i + 1] !== 98) {
46
+ i++;
47
+ continue;
48
+ }
49
+ const cls = data[i + 2];
50
+ const id = data[i + 3];
51
+ const len = data[i + 4] | data[i + 5] << 8;
52
+ const end = i + 6 + len + 2;
53
+ if (end > data.length) break;
54
+ let ckA = 0;
55
+ let ckB = 0;
56
+ for (let j = i + 2; j < i + 6 + len; j++) {
57
+ ckA = ckA + data[j] & 255;
58
+ ckB = ckB + ckA & 255;
59
+ }
60
+ if (ckA !== data[i + 6 + len] || ckB !== data[i + 7 + len]) {
61
+ badChecksums++;
62
+ i++;
63
+ continue;
64
+ }
65
+ const key = `${cls.toString(16).padStart(2, "0")}-${id.toString(16).padStart(2, "0")}`;
66
+ messageCounts[key] = (messageCounts[key] ?? 0) + 1;
67
+ if (cls === 2 && id === 21 && len >= 16) {
68
+ const p = i + 6;
69
+ const rcvTow = view.getFloat64(p, true);
70
+ const week = view.getUint16(p + 8, true);
71
+ const leapS = view.getInt8(p + 10);
72
+ const numMeas = data[p + 11];
73
+ const recStat = data[p + 12];
74
+ const leapValid = (recStat & 1) !== 0;
75
+ const meas = [];
76
+ for (let k = 0; k < numMeas; k++) {
77
+ const off = p + 16 + 32 * k;
78
+ if (off + 32 > p + len) break;
79
+ const gnssId = data[off + 20];
80
+ const svId = data[off + 21];
81
+ const sigId = data[off + 22];
82
+ const trkStat = data[off + 30];
83
+ const prn = prnFor(gnssId, svId);
84
+ const sig = SIGNALS[gnssId]?.[sigId];
85
+ if (!prn || !sig) continue;
86
+ const prValid = (trkStat & 1) !== 0;
87
+ const cpValid = (trkStat & 2) !== 0;
88
+ const cp = view.getFloat64(off + 8, true);
89
+ meas.push({
90
+ prn,
91
+ code: sig[1],
92
+ pr: prValid ? view.getFloat64(off, true) : null,
93
+ cp: cpValid && cp !== 0 ? cp : null,
94
+ doppler: view.getFloat32(off + 16, true),
95
+ cn0: data[off + 26],
96
+ halfCycleAmbiguous: (trkStat & 4) !== 0,
97
+ lockTimeMs: view.getUint16(off + 24, true)
98
+ });
99
+ const sys = sig[0];
100
+ const codes = obsCodes[sys] ??= [];
101
+ if (!codes.includes(sig[1])) codes.push(sig[1]);
102
+ }
103
+ epochs.push({
104
+ timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + Math.round(rcvTow * 1e3),
105
+ leapS: leapValid ? leapS : null,
106
+ meas
107
+ });
108
+ }
109
+ i = end;
110
+ }
111
+ return { epochs, messageCounts, obsCodes, badChecksums };
112
+ }
113
+
114
+ export {
115
+ parseUbxRawx
116
+ };
@@ -1,3 +1,6 @@
1
+ import {
2
+ getGpsLeap
3
+ } from "./chunk-HVXYFUCB.js";
1
4
  import {
2
5
  MILLISECONDS_IN_WEEK,
3
6
  START_BDS_TIME,
@@ -1069,7 +1072,7 @@ function msmEpochToDate(sys, epochMs, refTime = /* @__PURE__ */ new Date()) {
1069
1072
  let t2 = refMs - refMs % dayMs + diff * dayMs + msOfDay - 3 * 36e5;
1070
1073
  while (t2 - refMs > 3 * dayMs) t2 -= 7 * dayMs;
1071
1074
  while (refMs - t2 > 3 * dayMs) t2 += 7 * dayMs;
1072
- return new Date(t2);
1075
+ return new Date(t2 + getGpsLeap(new Date(t2)) * 1e3);
1073
1076
  }
1074
1077
  if (sys === "C") {
1075
1078
  const BDS_EPOCH = START_BDS_TIME.getTime();
@@ -1077,14 +1080,12 @@ function msmEpochToDate(sys, epochMs, refTime = /* @__PURE__ */ new Date()) {
1077
1080
  let t2 = BDS_EPOCH + weeksSinceEpoch2 * MS_PER_WEEK + epochMs;
1078
1081
  if (t2 - refMs > MS_PER_WEEK / 2) t2 -= MS_PER_WEEK;
1079
1082
  else if (refMs - t2 > MS_PER_WEEK / 2) t2 += MS_PER_WEEK;
1080
- t2 -= 18e3;
1081
1083
  return new Date(t2);
1082
1084
  }
1083
1085
  const weeksSinceEpoch = Math.floor((refMs - GPS_EPOCH) / MS_PER_WEEK);
1084
1086
  let t = GPS_EPOCH + weeksSinceEpoch * MS_PER_WEEK + epochMs;
1085
1087
  if (t - refMs > MS_PER_WEEK / 2) t -= MS_PER_WEEK;
1086
1088
  else if (refMs - t > MS_PER_WEEK / 2) t += MS_PER_WEEK;
1087
- t -= 18e3;
1088
1089
  return new Date(t);
1089
1090
  }
1090
1091
  function setGloFreqNumber(slot, k) {
@@ -2,7 +2,7 @@ import {
2
2
  computeDop,
3
3
  computeSatPosition,
4
4
  ecefToAzEl
5
- } from "./chunk-VAHMHYID.js";
5
+ } from "./chunk-OMVL3X7V.js";
6
6
  import {
7
7
  ecefToGeodetic
8
8
  } from "./chunk-37QNKGTC.js";
@@ -0,0 +1,246 @@
1
+ // src/novatel/index.ts
2
+ var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
3
+ var MS_PER_WEEK = 7 * 864e5;
4
+ var HDR = 28;
5
+ var ID_RANGE = 43;
6
+ var ID_RANGECMP = 140;
7
+ var ID_GLOEPHEMERIS = 723;
8
+ var C_LIGHT = 299792458;
9
+ var ADR_ROLL = 8388608;
10
+ function carrierFreq(sys, code, gloK) {
11
+ const band = code[0];
12
+ switch (sys) {
13
+ case "G":
14
+ case "J":
15
+ return band === "1" ? 157542e4 : band === "2" ? 12276e5 : 117645e4;
16
+ case "S":
17
+ return band === "1" ? 157542e4 : 117645e4;
18
+ case "E":
19
+ return band === "1" ? 157542e4 : band === "5" ? 117645e4 : band === "7" ? 120714e4 : band === "8" ? 1191795e3 : 127875e4;
20
+ case "C":
21
+ return band === "1" ? 157542e4 : band === "2" ? 1561098e3 : band === "5" ? 117645e4 : band === "7" ? 120714e4 : 126852e4;
22
+ case "R": {
23
+ if (gloK === null) return 0;
24
+ if (band === "1") return 1602e6 + gloK * 562500;
25
+ if (band === "2") return 1246e6 + gloK * 437500;
26
+ return 1202025e3;
27
+ }
28
+ default:
29
+ return 0;
30
+ }
31
+ }
32
+ function exsign(v, bits) {
33
+ return v & 1 << bits - 1 ? v - 2 ** bits : v;
34
+ }
35
+ function crc32(data, start, len) {
36
+ let crc = 0;
37
+ for (let i = start; i < start + len; i++) {
38
+ crc ^= data[i];
39
+ for (let j = 0; j < 8; j++) {
40
+ crc = crc & 1 ? crc >>> 1 ^ 3988292384 : crc >>> 1;
41
+ }
42
+ }
43
+ return crc >>> 0;
44
+ }
45
+ var SIGNALS = {
46
+ 0: {
47
+ 0: ["G", "1C"],
48
+ 5: ["G", "2P"],
49
+ 9: ["G", "2W"],
50
+ 14: ["G", "5Q"],
51
+ 16: ["G", "1L"],
52
+ 17: ["G", "2S"]
53
+ },
54
+ 1: { 0: ["R", "1C"], 1: ["R", "2C"], 5: ["R", "2P"], 6: ["R", "3Q"] },
55
+ 2: { 0: ["S", "1C"], 6: ["S", "5I"] },
56
+ 3: {
57
+ 1: ["E", "1C"],
58
+ 2: ["E", "1C"],
59
+ 6: ["E", "6B"],
60
+ 7: ["E", "6C"],
61
+ 12: ["E", "5Q"],
62
+ 17: ["E", "7Q"],
63
+ 20: ["E", "8Q"]
64
+ },
65
+ 4: {
66
+ 0: ["C", "2I"],
67
+ 1: ["C", "7I"],
68
+ 2: ["C", "6I"],
69
+ 4: ["C", "2I"],
70
+ 5: ["C", "7I"],
71
+ 6: ["C", "6I"],
72
+ 7: ["C", "1P"],
73
+ 9: ["C", "5P"],
74
+ 11: ["C", "7D"]
75
+ },
76
+ 5: {
77
+ 0: ["J", "1C"],
78
+ 14: ["J", "5Q"],
79
+ 16: ["J", "1L"],
80
+ 17: ["J", "2S"],
81
+ 27: ["J", "6L"]
82
+ },
83
+ 6: { 0: ["I", "5A"] }
84
+ };
85
+ function parseNovatelRange(data) {
86
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
87
+ const epochs = [];
88
+ const messageCounts = {};
89
+ const obsCodes = {};
90
+ let badCrc = 0;
91
+ const gloFcn = /* @__PURE__ */ new Map();
92
+ for (let j = 0; j + HDR + 4 <= data.length; j++) {
93
+ if (data[j] !== 170 || data[j + 1] !== 68 || data[j + 2] !== 18)
94
+ continue;
95
+ const hl = data[j + 3];
96
+ const id = view.getUint16(j + 4, true);
97
+ const ln = view.getUint16(j + 8, true);
98
+ if (id !== ID_GLOEPHEMERIS || hl < HDR || j + hl + ln + 4 > data.length)
99
+ continue;
100
+ if (crc32(data, j, hl + ln) !== view.getUint32(j + hl + ln, true)) continue;
101
+ const slot = view.getUint16(j + hl, true) - 37;
102
+ const k = view.getUint16(j + hl + 2, true) - 7;
103
+ if (slot >= 1 && slot <= 32) gloFcn.set(slot, k);
104
+ j += hl + ln + 3;
105
+ }
106
+ let i = 0;
107
+ while (i + HDR + 4 <= data.length) {
108
+ if (data[i] !== 170 || data[i + 1] !== 68 || data[i + 2] !== 18) {
109
+ i++;
110
+ continue;
111
+ }
112
+ const hlen = data[i + 3];
113
+ const msgId = view.getUint16(i + 4, true);
114
+ const msgLen = view.getUint16(i + 8, true);
115
+ const total = hlen + msgLen + 4;
116
+ if (hlen < HDR || i + total > data.length) {
117
+ i++;
118
+ continue;
119
+ }
120
+ if (crc32(data, i, hlen + msgLen) !== view.getUint32(i + hlen + msgLen, true)) {
121
+ badCrc++;
122
+ i++;
123
+ continue;
124
+ }
125
+ messageCounts[msgId] = (messageCounts[msgId] ?? 0) + 1;
126
+ const binary = (data[i + 6] >> 4 & 3) === 0;
127
+ const week = view.getUint16(i + 14, true);
128
+ const towMs = view.getUint32(i + 16, true);
129
+ if (msgId === ID_RANGECMP && binary && week > 0) {
130
+ const p = i + hlen;
131
+ const nobs = view.getUint32(p, true);
132
+ if (p + 4 + nobs * 24 <= i + hlen + msgLen) {
133
+ const meas = [];
134
+ for (let k = 0; k < nobs; k++) {
135
+ const o = p + 4 + 24 * k;
136
+ const stat = view.getUint32(o, true);
137
+ const satsys = stat >>> 16 & 7;
138
+ const sigtype = stat >>> 21 & 31;
139
+ const plock = stat >>> 10 & 1;
140
+ const parity = stat >>> 11 & 1;
141
+ const clock = stat >>> 12 & 1;
142
+ const sig = SIGNALS[satsys]?.[sigtype];
143
+ if (!sig) continue;
144
+ let [sysL, code] = sig;
145
+ let prnNum = data[o + 17];
146
+ if (sysL === "R") prnNum -= 37;
147
+ if (sysL === "S" && prnNum >= 183 && prnNum <= 191 && code === "1C") {
148
+ sysL = "J";
149
+ code = "1Z";
150
+ prnNum -= 182;
151
+ } else if (sysL === "S") {
152
+ prnNum -= 100;
153
+ }
154
+ if (prnNum < 1 || prnNum > 99) continue;
155
+ if (sysL === "R" && !parity) continue;
156
+ const gloK = sysL === "R" ? gloFcn.get(prnNum) ?? null : null;
157
+ const dop = exsign(view.getUint32(o + 4, true) & 268435455, 28) / 256;
158
+ const psr = (view.getUint32(o + 7, true) >>> 4) / 128 + data[o + 11] * 2097152;
159
+ let cp = null;
160
+ const freq = carrierFreq(sysL, code, gloK);
161
+ if (freq > 0) {
162
+ const adr = view.getInt32(o + 12, true) / 256;
163
+ const rolls = psr * freq / C_LIGHT / ADR_ROLL + adr / ADR_ROLL;
164
+ cp = -adr + ADR_ROLL * Math.floor(rolls + (rolls <= 0 ? -0.5 : 0.5));
165
+ }
166
+ const lockt = (view.getUint32(o + 18, true) & 2097151) / 32;
167
+ const cn0 = ((view.getUint16(o + 20, true) & 1023) >>> 5) + 20;
168
+ meas.push({
169
+ prn: `${sysL}${String(prnNum).padStart(2, "0")}`,
170
+ code,
171
+ pr: clock ? psr : null,
172
+ cp: plock ? cp : null,
173
+ doppler: plock ? dop : null,
174
+ cn0,
175
+ lockTimeS: lockt,
176
+ gloChannel: gloK
177
+ });
178
+ const codes = obsCodes[sysL] ??= [];
179
+ if (!codes.includes(code)) codes.push(code);
180
+ }
181
+ epochs.push({
182
+ timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + towMs,
183
+ meas
184
+ });
185
+ }
186
+ } else if (msgId === ID_RANGE && binary && week > 0) {
187
+ const p = i + hlen;
188
+ const nobs = view.getUint32(p, true);
189
+ if (p + 4 + nobs * 44 <= i + hlen + msgLen) {
190
+ const meas = [];
191
+ for (let k = 0; k < nobs; k++) {
192
+ const o = p + 4 + 44 * k;
193
+ const stat = view.getUint32(o + 40, true);
194
+ const satsys = stat >>> 16 & 7;
195
+ const sigtype = stat >>> 21 & 31;
196
+ const plock = stat >>> 10 & 1;
197
+ const parity = stat >>> 11 & 1;
198
+ const clock = stat >>> 12 & 1;
199
+ const sig = SIGNALS[satsys]?.[sigtype];
200
+ if (!sig) continue;
201
+ let [sysL, code] = sig;
202
+ let prnNum = view.getUint16(o, true);
203
+ if (sysL === "R") prnNum -= 37;
204
+ if (sysL === "S" && prnNum >= 183 && prnNum <= 191 && code === "1C") {
205
+ sysL = "J";
206
+ code = "1Z";
207
+ prnNum -= 182;
208
+ } else if (sysL === "S") {
209
+ prnNum -= 100;
210
+ }
211
+ if (prnNum < 1 || prnNum > 99) continue;
212
+ if (sysL === "R" && !parity) continue;
213
+ const prn = `${sysL}${String(prnNum).padStart(2, "0")}`;
214
+ const gfrq = view.getUint16(o + 2, true);
215
+ const psr = view.getFloat64(o + 4, true);
216
+ const adr = view.getFloat64(o + 16, true);
217
+ const dop = view.getFloat32(o + 28, true);
218
+ const snr = view.getFloat32(o + 32, true);
219
+ const lockt = view.getFloat32(o + 36, true);
220
+ meas.push({
221
+ prn,
222
+ code,
223
+ pr: clock ? psr : null,
224
+ cp: plock ? -adr : null,
225
+ doppler: plock ? dop : null,
226
+ cn0: snr,
227
+ lockTimeS: lockt,
228
+ gloChannel: sysL === "R" ? gfrq - 8 : null
229
+ });
230
+ const codes = obsCodes[sysL] ??= [];
231
+ if (!codes.includes(code)) codes.push(code);
232
+ }
233
+ epochs.push({
234
+ timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + towMs,
235
+ meas
236
+ });
237
+ }
238
+ }
239
+ i += total;
240
+ }
241
+ return { epochs, messageCounts, obsCodes, badCrc };
242
+ }
243
+
244
+ export {
245
+ parseNovatelRange
246
+ };