gnss-js 1.14.1 → 1.16.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,7 +1,8 @@
1
- // src/sbf/index.ts
2
- var CLIGHT = 299792458;
3
- var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
4
- var MS_PER_WEEK = 7 * 864e5;
1
+ import {
2
+ getGpsLeap
3
+ } from "./chunk-HVXYFUCB.js";
4
+
5
+ // src/sbf/frame.ts
5
6
  var CRC_TABLE = (() => {
6
7
  const t = new Uint16Array(256);
7
8
  for (let n = 0; n < 256; n++) {
@@ -18,6 +19,362 @@ function crc16(data, start, end) {
18
19
  crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
19
20
  return crc;
20
21
  }
22
+ function scanSbfFrames(data, view, onBlock) {
23
+ let badCrc = 0;
24
+ let i = 0;
25
+ while (i + 8 <= data.length) {
26
+ if (data[i] !== 36 || data[i + 1] !== 64) {
27
+ i++;
28
+ continue;
29
+ }
30
+ const len = view.getUint16(i + 6, true);
31
+ if (len < 8 || len % 4 !== 0) {
32
+ i++;
33
+ continue;
34
+ }
35
+ if (i + len > data.length) break;
36
+ if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
37
+ badCrc++;
38
+ i++;
39
+ continue;
40
+ }
41
+ onBlock(view.getUint16(i + 4, true) & 8191, i, len);
42
+ i += len;
43
+ }
44
+ return badCrc;
45
+ }
46
+ var two = (n) => String(n).padStart(2, "0");
47
+ function svidToPrn(svid) {
48
+ if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
49
+ if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
50
+ if (svid <= 62) return null;
51
+ if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
52
+ if (svid <= 70) return null;
53
+ if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
54
+ if (svid <= 119) return null;
55
+ if (svid <= 140) return `S${two(svid - 100)}`;
56
+ if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
57
+ if (svid <= 190) return `J${two(svid - 180)}`;
58
+ if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
59
+ if (svid <= 215) return `S${two(svid - 157)}`;
60
+ if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
61
+ if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
62
+ return null;
63
+ }
64
+
65
+ // src/sbf/nav.ts
66
+ var PI = Math.PI;
67
+ var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
68
+ var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
69
+ var SEC_PER_WEEK = 7 * 86400;
70
+ var MS_PER_WEEK = SEC_PER_WEEK * 1e3;
71
+ var F4_DNU = -2e10;
72
+ var U4_DNU = 4294967295;
73
+ function adjustWeek(refWeek, wn, mod) {
74
+ let offset = refWeek % mod - wn;
75
+ if (offset > mod / 2) offset -= mod;
76
+ if (offset < -(mod / 2 - 1)) offset += mod;
77
+ return refWeek - offset;
78
+ }
79
+ var gpsMs = (week, sec) => GPS_EPOCH_MS + week * MS_PER_WEEK + sec * 1e3;
80
+ var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
81
+ function decodeGpsQzsNav(view, b, sys) {
82
+ const svid = view.getUint8(b + 14);
83
+ const prn = sys === "G" ? svid : svid - 180;
84
+ const max = sys === "G" ? 32 : 10;
85
+ if (prn < 1 || prn > max) return null;
86
+ const wnc = view.getUint16(b + 12, true);
87
+ const tgdRaw = view.getFloat32(b + 28, true);
88
+ const tocs = view.getUint32(b + 32, true);
89
+ const toes = view.getUint32(b + 88, true);
90
+ const wnToc = adjustWeek(wnc, view.getUint16(b + 136, true), 1024);
91
+ const tocDate = new Date(gpsMs(wnToc, tocs));
92
+ return {
93
+ system: sys,
94
+ prn: `${sys}${String(prn).padStart(2, "0")}`,
95
+ toc: sowOf(tocDate.getTime()),
96
+ tocDate,
97
+ af0: view.getFloat32(b + 44, true),
98
+ af1: view.getFloat32(b + 40, true),
99
+ af2: view.getFloat32(b + 36, true),
100
+ iode: view.getUint8(b + 24),
101
+ crs: view.getFloat32(b + 48, true),
102
+ deltaN: view.getFloat32(b + 52, true) * PI,
103
+ m0: view.getFloat64(b + 56, true) * PI,
104
+ cuc: view.getFloat32(b + 64, true),
105
+ e: view.getFloat64(b + 68, true),
106
+ cus: view.getFloat32(b + 76, true),
107
+ sqrtA: view.getFloat64(b + 80, true),
108
+ toe: toes,
109
+ cic: view.getFloat32(b + 92, true),
110
+ omega0: view.getFloat64(b + 96, true) * PI,
111
+ cis: view.getFloat32(b + 104, true),
112
+ i0: view.getFloat64(b + 108, true) * PI,
113
+ crc: view.getFloat32(b + 116, true),
114
+ omega: view.getFloat64(b + 120, true) * PI,
115
+ omegaDot: view.getFloat32(b + 128, true) * PI,
116
+ idot: view.getFloat32(b + 132, true) * PI,
117
+ // RINEX "GPS week to go with toe": RTKLIB writes WNc for GPS and
118
+ // the week of toc for QZSS — mirrored here so records compare
119
+ // 1:1 against convbin output. wnToe is still used for `toe` above.
120
+ week: sys === "G" ? wnc : wnToc,
121
+ svHealth: view.getUint8(b + 20),
122
+ tgd: tgdRaw !== F4_DNU ? tgdRaw : 0
123
+ };
124
+ }
125
+ function decodeGalNav(view, b) {
126
+ const prn = view.getUint8(b + 14) - 70;
127
+ if (prn < 1 || prn > 36) return null;
128
+ const source = view.getUint8(b + 15);
129
+ if (source !== 2 && source !== 16) return null;
130
+ const wnc = view.getUint16(b + 12, true);
131
+ const toes = view.getUint32(b + 100, true);
132
+ const tocs = view.getUint32(b + 104, true);
133
+ const wnToc = adjustWeek(wnc, view.getUint16(b + 126, true), 4096);
134
+ const tocDate = new Date(gpsMs(wnToc, tocs));
135
+ const health = view.getUint16(b + 130, true);
136
+ let svh = 0;
137
+ if (health & 1) svh |= health >> 1 & 7;
138
+ if (health & 16) svh |= (health >> 5 & 7) << 6;
139
+ if (health & 256) svh |= (health >> 9 & 7) << 3;
140
+ const bgdE5a = view.getFloat32(b + 136, true);
141
+ return {
142
+ system: "E",
143
+ prn: `E${String(prn).padStart(2, "0")}`,
144
+ toc: sowOf(tocDate.getTime()),
145
+ tocDate,
146
+ af0: view.getFloat64(b + 116, true),
147
+ af1: view.getFloat32(b + 112, true),
148
+ af2: view.getFloat32(b + 108, true),
149
+ iode: view.getUint16(b + 128, true),
150
+ // IODnav
151
+ crs: view.getFloat32(b + 88, true),
152
+ deltaN: view.getFloat32(b + 72, true) * PI,
153
+ m0: view.getFloat64(b + 24, true) * PI,
154
+ cuc: view.getFloat32(b + 76, true),
155
+ e: view.getFloat64(b + 32, true),
156
+ cus: view.getFloat32(b + 80, true),
157
+ sqrtA: view.getFloat64(b + 16, true),
158
+ toe: toes,
159
+ cic: view.getFloat32(b + 92, true),
160
+ omega0: view.getFloat64(b + 56, true) * PI,
161
+ cis: view.getFloat32(b + 96, true),
162
+ i0: view.getFloat64(b + 40, true) * PI,
163
+ crc: view.getFloat32(b + 84, true),
164
+ omega: view.getFloat64(b + 48, true) * PI,
165
+ omegaDot: view.getFloat32(b + 64, true) * PI,
166
+ idot: view.getFloat32(b + 68, true) * PI,
167
+ week: wnc,
168
+ // RINEX GAL week is GPS-aligned and continuous
169
+ svHealth: svh,
170
+ tgd: bgdE5a !== F4_DNU ? bgdE5a : 0
171
+ // BGD E5a/E1 (RINEX slot)
172
+ };
173
+ }
174
+ function decodeBdsNav(view, b) {
175
+ const prn = svidToPrn(view.getUint8(b + 14));
176
+ if (!prn || prn[0] !== "C") return null;
177
+ const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
178
+ const tgd1 = view.getFloat32(b + 24, true);
179
+ const tocs = view.getUint32(b + 32, true);
180
+ const toes = view.getUint32(b + 88, true);
181
+ const wnToc = adjustWeek(bdsWeekRef, view.getUint16(b + 136, true), 8192);
182
+ const wnToe = adjustWeek(bdsWeekRef, view.getUint16(b + 138, true), 8192);
183
+ const tocDate = new Date(BDT_EPOCH_MS + wnToc * MS_PER_WEEK + tocs * 1e3);
184
+ return {
185
+ system: "C",
186
+ prn,
187
+ toc: sowOf(tocDate.getTime()),
188
+ tocDate,
189
+ af0: view.getFloat32(b + 44, true),
190
+ af1: view.getFloat32(b + 40, true),
191
+ af2: view.getFloat32(b + 36, true),
192
+ iode: view.getUint8(b + 21),
193
+ // AODE
194
+ crs: view.getFloat32(b + 48, true),
195
+ deltaN: view.getFloat32(b + 52, true) * PI,
196
+ m0: view.getFloat64(b + 56, true) * PI,
197
+ cuc: view.getFloat32(b + 64, true),
198
+ e: view.getFloat64(b + 68, true),
199
+ cus: view.getFloat32(b + 76, true),
200
+ sqrtA: view.getFloat64(b + 80, true),
201
+ toe: toes,
202
+ cic: view.getFloat32(b + 92, true),
203
+ omega0: view.getFloat64(b + 96, true) * PI,
204
+ cis: view.getFloat32(b + 104, true),
205
+ i0: view.getFloat64(b + 108, true) * PI,
206
+ crc: view.getFloat32(b + 116, true),
207
+ omega: view.getFloat64(b + 120, true) * PI,
208
+ omegaDot: view.getFloat32(b + 128, true) * PI,
209
+ idot: view.getFloat32(b + 132, true) * PI,
210
+ week: wnToe,
211
+ // RINEX BDS week field is the BDT week
212
+ svHealth: view.getUint8(b + 19),
213
+ // SatH1
214
+ tgd: tgd1 !== F4_DNU ? tgd1 : 0
215
+ // TGD1 B1/B3 (RINEX slot)
216
+ };
217
+ }
218
+ function decodeGloNav(view, b) {
219
+ const prn = svidToPrn(view.getUint8(b + 14));
220
+ if (!prn || prn[0] !== "R") return null;
221
+ const wnc = view.getUint16(b + 12, true);
222
+ const toes = view.getUint32(b + 76, true);
223
+ const wnToe = adjustWeek(wnc, view.getUint16(b + 80, true), 1024);
224
+ const toeGpsMs = gpsMs(wnToe, toes);
225
+ const leapMs = getGpsLeap(new Date(toeGpsMs)) * 1e3;
226
+ const tocDate = new Date(toeGpsMs - leapMs);
227
+ const tofGpsMs = gpsMs(wnc, 0) + view.getUint32(b + 8, true);
228
+ const tofLeapMs = getGpsLeap(new Date(tofGpsMs)) * 1e3;
229
+ const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS) / 1e3 % SEC_PER_WEEK;
230
+ return {
231
+ system: "R",
232
+ prn,
233
+ tocDate,
234
+ // RINEX stores −τn as the clock bias; SBF carries τn (ICD sign)
235
+ tauN: -view.getFloat32(b + 68, true),
236
+ gammaN: view.getFloat32(b + 64, true),
237
+ messageFrameTime,
238
+ x: view.getFloat64(b + 16, true),
239
+ // SBF unit is km, like RINEX
240
+ xDot: view.getFloat32(b + 40, true),
241
+ xAcc: view.getFloat32(b + 52, true),
242
+ y: view.getFloat64(b + 24, true),
243
+ yDot: view.getFloat32(b + 44, true),
244
+ yAcc: view.getFloat32(b + 56, true),
245
+ z: view.getFloat64(b + 32, true),
246
+ zDot: view.getFloat32(b + 48, true),
247
+ zAcc: view.getFloat32(b + 60, true),
248
+ // MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
249
+ health: view.getUint8(b + 85) >> 2,
250
+ freqNum: view.getUint8(b + 15) - 8
251
+ };
252
+ }
253
+ function parseSbfNav(data) {
254
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
255
+ const ephemerides = [];
256
+ const badCrc = scanSbfFrames(data, view, (id, b, len) => {
257
+ let eph = null;
258
+ if (id === 5891 && len >= 140) eph = decodeGpsQzsNav(view, b, "G");
259
+ else if (id === 4095 && len >= 140) eph = decodeGpsQzsNav(view, b, "J");
260
+ else if (id === 4002 && len >= 149) eph = decodeGalNav(view, b);
261
+ else if (id === 4081 && len >= 140) eph = decodeBdsNav(view, b);
262
+ else if (id === 4004 && len >= 96) eph = decodeGloNav(view, b);
263
+ if (eph) ephemerides.push(eph);
264
+ });
265
+ return { ephemerides, badCrc };
266
+ }
267
+ var GAL_SQRT_A_NOMINAL = Math.sqrt(296e5);
268
+ var GAL_I_NOMINAL = 56 / 180 * PI;
269
+ var I_REF_03 = 0.3 * PI;
270
+ function decodeGpsGalAlm(view, b, sys) {
271
+ const wnc = view.getUint16(b + 12, true);
272
+ const toa = view.getUint32(b + 20, true);
273
+ if (toa === U4_DNU) return null;
274
+ let prn;
275
+ let weekAlm;
276
+ let i0;
277
+ let sqrtA = view.getFloat32(b + 32, true);
278
+ let health;
279
+ if (sys === "G") {
280
+ prn = view.getUint8(b + 14);
281
+ if (prn < 1 || prn > 32) return null;
282
+ weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 256);
283
+ i0 = I_REF_03 + view.getFloat32(b + 24, true) * PI;
284
+ health = view.getUint8(b + 58);
285
+ } else {
286
+ prn = view.getUint8(b + 57) - 70;
287
+ if (prn < 1 || prn > 36) return null;
288
+ weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 4);
289
+ i0 = GAL_I_NOMINAL + view.getFloat32(b + 24, true) * PI;
290
+ sqrtA += GAL_SQRT_A_NOMINAL;
291
+ health = view.getUint16(b + 58, true);
292
+ }
293
+ return {
294
+ system: sys,
295
+ prn: `${sys}${String(prn).padStart(2, "0")}`,
296
+ weekAlm,
297
+ toaSec: toa,
298
+ sqrtA,
299
+ e: view.getFloat32(b + 16, true),
300
+ i0OrDeltaI: i0,
301
+ omega0: view.getFloat32(b + 36, true) * PI,
302
+ omega: view.getFloat32(b + 40, true) * PI,
303
+ m0: view.getFloat32(b + 44, true) * PI,
304
+ omegaDot: view.getFloat32(b + 28, true) * PI,
305
+ af0: view.getFloat32(b + 52, true),
306
+ af1: view.getFloat32(b + 48, true),
307
+ health
308
+ };
309
+ }
310
+ function decodeBdsAlm(view, b) {
311
+ const prnStr = svidToPrn(view.getUint8(b + 14));
312
+ if (!prnStr || prnStr[0] !== "C") return null;
313
+ const prn = parseInt(prnStr.slice(1), 10);
314
+ const toa = view.getUint32(b + 16, true);
315
+ if (toa === U4_DNU) return null;
316
+ const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
317
+ const geo = prn <= 5 || prn >= 59;
318
+ return {
319
+ system: "C",
320
+ prn: prnStr,
321
+ weekAlm: adjustWeek(bdsWeekRef, view.getUint8(b + 15), 256),
322
+ toaSec: toa,
323
+ sqrtA: view.getFloat32(b + 20, true),
324
+ e: view.getFloat32(b + 24, true),
325
+ i0OrDeltaI: (geo ? 0 : I_REF_03) + view.getFloat32(b + 44, true) * PI,
326
+ omega0: view.getFloat32(b + 36, true) * PI,
327
+ omega: view.getFloat32(b + 28, true) * PI,
328
+ m0: view.getFloat32(b + 32, true) * PI,
329
+ omegaDot: view.getFloat32(b + 40, true) * PI,
330
+ af0: view.getFloat32(b + 48, true),
331
+ af1: view.getFloat32(b + 52, true),
332
+ health: view.getUint16(b + 56, true)
333
+ };
334
+ }
335
+ function decodeGloAlm(view, b) {
336
+ const prn = svidToPrn(view.getUint8(b + 14));
337
+ if (!prn || prn[0] !== "R") return null;
338
+ const wnc = view.getUint16(b + 12, true);
339
+ const toa = view.getUint32(b + 20, true);
340
+ if (toa === U4_DNU) return null;
341
+ return {
342
+ system: "R",
343
+ prn,
344
+ freqNr: view.getUint8(b + 15) - 8,
345
+ weekAlm: adjustWeek(wnc, view.getUint8(b + 52), 256),
346
+ toaSec: toa,
347
+ epsilon: view.getFloat32(b + 16, true),
348
+ deltaI: view.getFloat32(b + 24, true) * PI,
349
+ lambda: view.getFloat32(b + 28, true) * PI,
350
+ tLambda: view.getFloat32(b + 32, true),
351
+ omega: view.getFloat32(b + 36, true) * PI,
352
+ deltaT: view.getFloat32(b + 40, true),
353
+ deltaTDot: view.getFloat32(b + 44, true),
354
+ tau: view.getFloat32(b + 48, true),
355
+ health: view.getUint8(b + 53),
356
+ nDay: view.getUint16(b + 54, true),
357
+ n4: view.getUint8(b + 57)
358
+ };
359
+ }
360
+ function parseSbfAlmanac(data) {
361
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
362
+ const almanacs = [];
363
+ const badCrc = scanSbfFrames(data, view, (id, b, len) => {
364
+ let alm = null;
365
+ if (id === 5892 && len >= 60) alm = decodeGpsGalAlm(view, b, "G");
366
+ else if (id === 4003 && len >= 61) alm = decodeGpsGalAlm(view, b, "E");
367
+ else if (id === 4119 && len >= 60) alm = decodeBdsAlm(view, b);
368
+ else if (id === 4005 && len >= 60) alm = decodeGloAlm(view, b);
369
+ if (alm) almanacs.push(alm);
370
+ });
371
+ return { almanacs, badCrc };
372
+ }
373
+
374
+ // src/sbf/index.ts
375
+ var CLIGHT = 299792458;
376
+ var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
377
+ var MS_PER_WEEK2 = 7 * 864e5;
21
378
  var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
22
379
  var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
23
380
  var M3_INTERVALS = [
@@ -116,44 +473,27 @@ var MEAS2_SIG = [
116
473
  ["J", "1E"],
117
474
  ["J", "5P"]
118
475
  ];
119
- var two = (n) => String(n).padStart(2, "0");
476
+ var two2 = (n) => String(n).padStart(2, "0");
120
477
  function m3Prn(navsys, svid) {
121
478
  switch (navsys) {
122
479
  case 0:
123
- return svid < 32 ? `G${two(svid + 1)}` : null;
480
+ return svid < 32 ? `G${two2(svid + 1)}` : null;
124
481
  case 1:
125
- return svid < 27 ? `R${two(svid + 1)}` : null;
482
+ return svid < 27 ? `R${two2(svid + 1)}` : null;
126
483
  case 2:
127
- return svid < 36 ? `E${two(svid + 1)}` : null;
484
+ return svid < 36 ? `E${two2(svid + 1)}` : null;
128
485
  case 3:
129
- return svid < 50 ? `C${two(svid + 1)}` : null;
486
+ return svid < 50 ? `C${two2(svid + 1)}` : null;
130
487
  case 4:
131
- return svid <= 38 ? `S${two(svid + 20)}` : null;
488
+ return svid <= 38 ? `S${two2(svid + 20)}` : null;
132
489
  case 5:
133
- return svid < 10 ? `J${two(svid + 1)}` : null;
490
+ return svid < 10 ? `J${two2(svid + 1)}` : null;
134
491
  case 6:
135
- return svid < 14 ? `I${two(svid + 1)}` : null;
492
+ return svid < 14 ? `I${two2(svid + 1)}` : null;
136
493
  default:
137
494
  return null;
138
495
  }
139
496
  }
140
- function svidToPrn(svid) {
141
- if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
142
- if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
143
- if (svid <= 62) return null;
144
- if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
145
- if (svid <= 70) return null;
146
- if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
147
- if (svid <= 119) return null;
148
- if (svid <= 140) return `S${two(svid - 100)}`;
149
- if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
150
- if (svid <= 190) return `J${two(svid - 180)}`;
151
- if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
152
- if (svid <= 215) return `S${two(svid - 157)}`;
153
- if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
154
- if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
155
- return null;
156
- }
157
497
  function carrierFreq(sys, code, fcn) {
158
498
  const band = code.charCodeAt(0) - 48;
159
499
  switch (sys) {
@@ -216,7 +556,6 @@ function parseSbfMeas(data) {
216
556
  const epochs = [];
217
557
  const messageCounts = {};
218
558
  const obsCodes = {};
219
- let badCrc = 0;
220
559
  let ref = freshRefEpoch(-1);
221
560
  let curTime = NaN;
222
561
  let curMeas = [];
@@ -247,8 +586,8 @@ function parseSbfMeas(data) {
247
586
  curTime = timeMs;
248
587
  }
249
588
  };
250
- const decodeMeas3Ranges = (i2, tow) => {
251
- const o = i2 + 8;
589
+ const decodeMeas3Ranges = (i, tow) => {
590
+ const o = i + 8;
252
591
  const constellations = view.getUint16(o + 8, true) & 127;
253
592
  const misc = data[o + 10];
254
593
  const version = data[o + 11];
@@ -526,8 +865,8 @@ function parseSbfMeas(data) {
526
865
  }
527
866
  }
528
867
  };
529
- const decodeMeas3CN0 = (i2, len) => {
530
- const flags = view.getUint16(i2 + 14, true);
868
+ const decodeMeas3CN0 = (i, len) => {
869
+ const flags = view.getUint16(i + 14, true);
531
870
  if ((flags & 7) !== 0) return;
532
871
  const maxNibbles = (len - 16) * 2;
533
872
  let off = 0;
@@ -537,7 +876,7 @@ function parseSbfMeas(data) {
537
876
  for (let k = 0; k < e.slots.length; k++) {
538
877
  const slot = e.slots[k];
539
878
  if (k > 0 && !slot) break;
540
- const nib = (data[i2 + 16 + (off >> 1)] ?? 0) >> (off & 1) * 4 & 15;
879
+ const nib = (data[i + 16 + (off >> 1)] ?? 0) >> (off & 1) * 4 & 15;
541
880
  if (slot && slot.cn0 !== null) slot.cn0 += nib * 0.0625 - 0.5;
542
881
  off++;
543
882
  }
@@ -564,14 +903,14 @@ function parseSbfMeas(data) {
564
903
  if ((v & 1) === 1) value = -value;
565
904
  return { value, size };
566
905
  };
567
- const decodeMeas3Doppler = (i2, len) => {
568
- const flags = view.getUint16(i2 + 14, true);
906
+ const decodeMeas3Doppler = (i, len) => {
907
+ const flags = view.getUint16(i + 14, true);
569
908
  if ((flags & 7) !== 0) return;
570
909
  let off = 0;
571
910
  for (const e of curSats) {
572
911
  if (off + 16 >= len) break;
573
912
  if (!e.prn) continue;
574
- const master = readPrRate(i2 + 16, off);
913
+ const master = readPrRate(i + 16, off);
575
914
  off += master.size;
576
915
  if (master.value === -268435455) continue;
577
916
  const refPrRate = ref.sats[e.navsys][e.svid]?.prRate ?? 0;
@@ -581,20 +920,20 @@ function parseSbfMeas(data) {
581
920
  for (let k = 1; k < e.slots.length; k++) {
582
921
  const slot = e.slots[k];
583
922
  if (!slot) break;
584
- const slave = readPrRate(i2 + 16, off);
923
+ const slave = readPrRate(i + 16, off);
585
924
  off += slave.size;
586
925
  if (m)
587
926
  slot.doppler = (mD * (CLIGHT / m.freq) * 1e3 - slave.value) * 1e-3 / (CLIGHT / slot.freq);
588
927
  }
589
928
  }
590
929
  };
591
- const decodeMeasEpoch = (i2, len) => {
592
- const n1 = data[i2 + 14];
593
- const len1 = data[i2 + 15];
594
- const len2 = data[i2 + 16];
595
- if ((data[i2 + 17] & 128) !== 0) return;
596
- const end = i2 + len;
597
- let p = i2 + 20;
930
+ const decodeMeasEpoch = (i, len) => {
931
+ const n1 = data[i + 14];
932
+ const len1 = data[i + 15];
933
+ const len2 = data[i + 16];
934
+ if ((data[i + 17] & 128) !== 0) return;
935
+ const end = i + len;
936
+ let p = i + 20;
598
937
  for (let b1 = 0; b1 < n1 && p + 20 <= end; b1++) {
599
938
  const ant = data[p + 1] >> 5;
600
939
  let sig = data[p + 1] & 31;
@@ -681,43 +1020,29 @@ function parseSbfMeas(data) {
681
1020
  }
682
1021
  }
683
1022
  };
684
- let i = 0;
685
- while (i + 8 <= data.length) {
686
- if (data[i] !== 36 || data[i + 1] !== 64) {
687
- i++;
688
- continue;
689
- }
690
- const len = view.getUint16(i + 6, true);
691
- if (len < 8 || len % 4 !== 0) {
692
- i++;
693
- continue;
694
- }
695
- if (i + len > data.length) break;
696
- if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
697
- badCrc++;
698
- i++;
699
- continue;
700
- }
701
- const id = view.getUint16(i + 4, true) & 8191;
1023
+ const badCrc = scanSbfFrames(data, view, (id, i, len) => {
702
1024
  const key = String(id);
703
1025
  messageCounts[key] = (messageCounts[key] ?? 0) + 1;
704
1026
  if ((id === 4027 || id === 4109 || id === 4110 || id === 4111) && len >= 16) {
705
1027
  const tow = view.getUint32(i + 8, true);
706
1028
  const wnc = view.getUint16(i + 12, true);
707
1029
  if (tow !== 4294967295 && wnc !== 65535) {
708
- ensureEpoch(GPS_EPOCH_MS + wnc * MS_PER_WEEK + tow);
1030
+ ensureEpoch(GPS_EPOCH_MS2 + wnc * MS_PER_WEEK2 + tow);
709
1031
  if (id === 4109) decodeMeas3Ranges(i, tow);
710
1032
  else if (id === 4110) decodeMeas3CN0(i, len);
711
1033
  else if (id === 4111) decodeMeas3Doppler(i, len);
712
1034
  else decodeMeasEpoch(i, len);
713
1035
  }
714
1036
  }
715
- i += len;
716
- }
1037
+ });
717
1038
  flush();
718
1039
  return { epochs, messageCounts, obsCodes, badCrc };
719
1040
  }
720
1041
 
721
1042
  export {
1043
+ crc16,
1044
+ scanSbfFrames,
1045
+ parseSbfNav,
1046
+ parseSbfAlmanac,
722
1047
  parseSbfMeas
723
1048
  };