gnss-js 1.14.0 → 1.15.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.
@@ -1,10 +1,270 @@
1
- // src/novatel/index.ts
1
+ import {
2
+ getUtcDate
3
+ } from "./chunk-HVXYFUCB.js";
4
+
5
+ // src/novatel/frame.ts
6
+ var OEM4_HLEN = 28;
7
+ function crc32(data, start, len) {
8
+ let crc = 0;
9
+ for (let i = start; i < start + len; i++) {
10
+ crc ^= data[i];
11
+ for (let j = 0; j < 8; j++) {
12
+ crc = crc & 1 ? crc >>> 1 ^ 3988292384 : crc >>> 1;
13
+ }
14
+ }
15
+ return crc >>> 0;
16
+ }
17
+ function* oem4Frames(data, view, stats) {
18
+ let i = 0;
19
+ while (i + OEM4_HLEN + 4 <= data.length) {
20
+ if (data[i] !== 170 || data[i + 1] !== 68 || data[i + 2] !== 18) {
21
+ i++;
22
+ continue;
23
+ }
24
+ const hlen = data[i + 3];
25
+ const msgLen = view.getUint16(i + 8, true);
26
+ const total = hlen + msgLen + 4;
27
+ if (hlen < OEM4_HLEN || i + total > data.length) {
28
+ i++;
29
+ continue;
30
+ }
31
+ if (crc32(data, i, hlen + msgLen) !== view.getUint32(i + hlen + msgLen, true)) {
32
+ stats.badCrc++;
33
+ i++;
34
+ continue;
35
+ }
36
+ yield {
37
+ id: view.getUint16(i + 4, true),
38
+ start: i,
39
+ payload: i + hlen,
40
+ msgLen,
41
+ binary: (data[i + 6] >> 4 & 3) === 0,
42
+ week: view.getUint16(i + 14, true),
43
+ towMs: view.getUint32(i + 16, true)
44
+ };
45
+ i += total;
46
+ }
47
+ }
48
+
49
+ // src/navbits/index.ts
50
+ var GPS_PI = 3.1415926535898;
2
51
  var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
52
+ var SEC_PER_WEEK = 7 * 86400;
53
+ function getBitU(buff, pos, len) {
54
+ let bits = 0;
55
+ for (let i = pos; i < pos + len; i++) {
56
+ bits = bits * 2 + (buff[i >> 3] >> 7 - (i & 7) & 1);
57
+ }
58
+ return bits;
59
+ }
60
+ function getBitS(buff, pos, len) {
61
+ const bits = getBitU(buff, pos, len);
62
+ if (len <= 0 || bits < 2 ** (len - 1)) return bits;
63
+ return bits - 2 ** len;
64
+ }
65
+ function decodeGpsLnavFrame(subframes, opts = {}) {
66
+ if (subframes.length < 90) return null;
67
+ const b = subframes;
68
+ let i = 24;
69
+ const tow1 = getBitU(b, i, 17) * 6;
70
+ i += 17 + 2;
71
+ const id1 = getBitU(b, i, 3);
72
+ i += 3 + 2;
73
+ const week10 = getBitU(b, i, 10);
74
+ i += 10;
75
+ i += 2;
76
+ i += 4;
77
+ const svHealth = getBitU(b, i, 6);
78
+ i += 6;
79
+ const iodc0 = getBitU(b, i, 2);
80
+ i += 2;
81
+ i += 1 + 87;
82
+ const tgdRaw = getBitS(b, i, 8);
83
+ i += 8;
84
+ const iodc1 = getBitU(b, i, 8);
85
+ i += 8;
86
+ const tocSec = getBitU(b, i, 16) * 16;
87
+ i += 16;
88
+ const af2 = getBitS(b, i, 8) * 2 ** -55;
89
+ i += 8;
90
+ const af1 = getBitS(b, i, 16) * 2 ** -43;
91
+ i += 16;
92
+ const af0 = getBitS(b, i, 22) * 2 ** -31;
93
+ i = 240 + 24;
94
+ i += 17 + 2;
95
+ const id2 = getBitU(b, i, 3);
96
+ i += 3 + 2;
97
+ const iode = getBitU(b, i, 8);
98
+ i += 8;
99
+ const crs = getBitS(b, i, 16) * 2 ** -5;
100
+ i += 16;
101
+ const deltaN = getBitS(b, i, 16) * 2 ** -43 * GPS_PI;
102
+ i += 16;
103
+ const m0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
104
+ i += 32;
105
+ const cuc = getBitS(b, i, 16) * 2 ** -29;
106
+ i += 16;
107
+ const e = getBitU(b, i, 32) * 2 ** -33;
108
+ i += 32;
109
+ const cus = getBitS(b, i, 16) * 2 ** -29;
110
+ i += 16;
111
+ const sqrtA = getBitU(b, i, 32) * 2 ** -19;
112
+ i += 32;
113
+ const toes = getBitU(b, i, 16) * 16;
114
+ i = 480 + 24;
115
+ i += 17 + 2;
116
+ const id3 = getBitU(b, i, 3);
117
+ i += 3 + 2;
118
+ const cic = getBitS(b, i, 16) * 2 ** -29;
119
+ i += 16;
120
+ const omega0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
121
+ i += 32;
122
+ const cis = getBitS(b, i, 16) * 2 ** -29;
123
+ i += 16;
124
+ const i0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
125
+ i += 32;
126
+ const crc = getBitS(b, i, 16) * 2 ** -5;
127
+ i += 16;
128
+ const omega = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
129
+ i += 32;
130
+ const omegaDot = getBitS(b, i, 24) * 2 ** -43 * GPS_PI;
131
+ i += 24;
132
+ const iode3 = getBitU(b, i, 8);
133
+ i += 8;
134
+ const idot = getBitS(b, i, 14) * 2 ** -43 * GPS_PI;
135
+ const iodc = iodc0 * 256 + iodc1;
136
+ if (id1 !== 1 || id2 !== 2 || id3 !== 3) return null;
137
+ if (iode3 !== iode || iode !== (iodc & 255)) return null;
138
+ const ref = opts.refWeek ?? Math.floor((Date.now() - GPS_EPOCH_MS) / 1e3 / SEC_PER_WEEK);
139
+ let week = week10 + 1024 * Math.round((ref - week10) / 1024);
140
+ if (toes < tow1 - 302400) week++;
141
+ else if (toes > tow1 + 302400) week--;
142
+ const prn = opts.prn ?? "G00";
143
+ const tocDate = new Date(
144
+ GPS_EPOCH_MS + (week * SEC_PER_WEEK + tocSec) * 1e3
145
+ );
146
+ return {
147
+ system: prn[0] === "J" ? "J" : "G",
148
+ prn,
149
+ tocDate,
150
+ // Same seconds-of-week convention as parseNavFile (rinex/nav.ts).
151
+ toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK,
152
+ af0,
153
+ af1,
154
+ af2,
155
+ iode,
156
+ crs,
157
+ deltaN,
158
+ m0,
159
+ cuc,
160
+ e,
161
+ cus,
162
+ sqrtA,
163
+ toe: toes,
164
+ cic,
165
+ omega0,
166
+ cis,
167
+ i0,
168
+ crc,
169
+ omega,
170
+ omegaDot,
171
+ idot,
172
+ week,
173
+ svHealth,
174
+ tgd: tgdRaw === -128 ? 0 : tgdRaw * 2 ** -31
175
+ // IS-GPS-200: -128 reserved
176
+ };
177
+ }
178
+
179
+ // src/novatel/nav.ts
180
+ var ID_RAWEPHEM = 41;
181
+ var ID_GLOEPHEMERIS = 723;
182
+ var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
183
+ var SEC_PER_WEEK2 = 7 * 86400;
184
+ var SEC_PER_DAY = 86400;
185
+ function decodeRawEphem(data, view, p) {
186
+ const prn = view.getUint32(p, true);
187
+ if (prn < 1 || prn > 32) return null;
188
+ const refWeek = view.getUint32(p + 4, true);
189
+ return decodeGpsLnavFrame(data.subarray(p + 12, p + 102), {
190
+ prn: `G${String(prn).padStart(2, "0")}`,
191
+ refWeek
192
+ });
193
+ }
194
+ function decodeGloEphemeris(view, p) {
195
+ const slot = view.getUint16(p, true) - 37;
196
+ if (slot < 1 || slot > 27) return null;
197
+ const freqNum = view.getUint16(p + 2, true) - 7;
198
+ const week = view.getUint16(p + 6, true);
199
+ const tow = Math.floor(view.getUint32(p + 8, true) / 1e3 + 0.5);
200
+ const toff = view.getUint32(p + 12, true);
201
+ const health = view.getUint32(p + 24, true) < 4 ? 0 : 1;
202
+ let tof = view.getUint32(p + 124, true) - toff;
203
+ tof += Math.floor(tow / SEC_PER_DAY) * SEC_PER_DAY;
204
+ if (tof < tow - 43200) tof += SEC_PER_DAY;
205
+ else if (tof > tow + 43200) tof -= SEC_PER_DAY;
206
+ const toeGpsMs = GPS_EPOCH_MS2 + (week * SEC_PER_WEEK2 + tow) * 1e3;
207
+ const tofGpsMs = GPS_EPOCH_MS2 + (week * SEC_PER_WEEK2 + tof) * 1e3;
208
+ const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() - GPS_EPOCH_MS2) / 1e3;
209
+ return {
210
+ system: "R",
211
+ prn: `R${String(slot).padStart(2, "0")}`,
212
+ tocDate: getUtcDate(new Date(toeGpsMs)),
213
+ tauN: -view.getFloat64(p + 100, true),
214
+ // RINEX stores −τ_n
215
+ gammaN: view.getFloat64(p + 116, true),
216
+ messageFrameTime: (tofUtcSec % SEC_PER_WEEK2 + SEC_PER_WEEK2) % SEC_PER_WEEK2,
217
+ x: view.getFloat64(p + 28, true) / 1e3,
218
+ y: view.getFloat64(p + 36, true) / 1e3,
219
+ z: view.getFloat64(p + 44, true) / 1e3,
220
+ xDot: view.getFloat64(p + 52, true) / 1e3,
221
+ yDot: view.getFloat64(p + 60, true) / 1e3,
222
+ zDot: view.getFloat64(p + 68, true) / 1e3,
223
+ xAcc: view.getFloat64(p + 76, true) / 1e3,
224
+ yAcc: view.getFloat64(p + 84, true) / 1e3,
225
+ zAcc: view.getFloat64(p + 92, true) / 1e3,
226
+ health,
227
+ freqNum
228
+ };
229
+ }
230
+ function parseNovatelNav(data) {
231
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
232
+ const stats = { badCrc: 0 };
233
+ const ephemerides = [];
234
+ const lastGps = /* @__PURE__ */ new Map();
235
+ const lastGlo = /* @__PURE__ */ new Map();
236
+ for (const frame of oem4Frames(data, view, stats)) {
237
+ if (!frame.binary) continue;
238
+ if (frame.id === ID_RAWEPHEM && frame.msgLen >= 102) {
239
+ const eph = decodeRawEphem(data, view, frame.payload);
240
+ if (!eph) continue;
241
+ const prev = lastGps.get(eph.prn);
242
+ if (prev && prev.iode === eph.iode && prev.tocDate.getTime() === eph.tocDate.getTime()) {
243
+ continue;
244
+ }
245
+ lastGps.set(eph.prn, eph);
246
+ ephemerides.push(eph);
247
+ } else if (frame.id === ID_GLOEPHEMERIS && frame.msgLen >= 144) {
248
+ const eph = decodeGloEphemeris(view, frame.payload);
249
+ if (!eph) continue;
250
+ const prev = lastGlo.get(eph.prn);
251
+ if (prev && Math.abs(prev.tocDate.getTime() - eph.tocDate.getTime()) < 1e3 && prev.health === eph.health) {
252
+ continue;
253
+ }
254
+ lastGlo.set(eph.prn, eph);
255
+ ephemerides.push(eph);
256
+ }
257
+ }
258
+ return { ephemerides, badCrc: stats.badCrc };
259
+ }
260
+
261
+ // src/novatel/index.ts
262
+ var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
3
263
  var MS_PER_WEEK = 7 * 864e5;
4
264
  var HDR = 28;
5
265
  var ID_RANGE = 43;
6
266
  var ID_RANGECMP = 140;
7
- var ID_GLOEPHEMERIS = 723;
267
+ var ID_GLOEPHEMERIS2 = 723;
8
268
  var C_LIGHT = 299792458;
9
269
  var ADR_ROLL = 8388608;
10
270
  function carrierFreq(sys, code, gloK) {
@@ -32,16 +292,6 @@ function carrierFreq(sys, code, gloK) {
32
292
  function exsign(v, bits) {
33
293
  return v & 1 << bits - 1 ? v - 2 ** bits : v;
34
294
  }
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
295
  var SIGNALS = {
46
296
  0: {
47
297
  0: ["G", "1C"],
@@ -95,7 +345,7 @@ function parseNovatelRange(data) {
95
345
  const hl = data[j + 3];
96
346
  const id = view.getUint16(j + 4, true);
97
347
  const ln = view.getUint16(j + 8, true);
98
- if (id !== ID_GLOEPHEMERIS || hl < HDR || j + hl + ln + 4 > data.length)
348
+ if (id !== ID_GLOEPHEMERIS2 || hl < HDR || j + hl + ln + 4 > data.length)
99
349
  continue;
100
350
  if (crc32(data, j, hl + ln) !== view.getUint32(j + hl + ln, true)) continue;
101
351
  const slot = view.getUint16(j + hl, true) - 37;
@@ -179,7 +429,7 @@ function parseNovatelRange(data) {
179
429
  if (!codes.includes(code)) codes.push(code);
180
430
  }
181
431
  epochs.push({
182
- timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + towMs,
432
+ timeMs: GPS_EPOCH_MS3 + week * MS_PER_WEEK + towMs,
183
433
  meas
184
434
  });
185
435
  }
@@ -231,7 +481,7 @@ function parseNovatelRange(data) {
231
481
  if (!codes.includes(code)) codes.push(code);
232
482
  }
233
483
  epochs.push({
234
- timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + towMs,
484
+ timeMs: GPS_EPOCH_MS3 + week * MS_PER_WEEK + towMs,
235
485
  meas
236
486
  });
237
487
  }
@@ -242,5 +492,9 @@ function parseNovatelRange(data) {
242
492
  }
243
493
 
244
494
  export {
495
+ OEM4_HLEN,
496
+ crc32,
497
+ oem4Frames,
498
+ parseNovatelNav,
245
499
  parseNovatelRange
246
500
  };
@@ -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";