gnss-js 1.14.1 → 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.
package/dist/sbf.cjs CHANGED
@@ -20,12 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/sbf/index.ts
21
21
  var sbf_exports = {};
22
22
  __export(sbf_exports, {
23
- parseSbfMeas: () => parseSbfMeas
23
+ parseSbfAlmanac: () => parseSbfAlmanac,
24
+ parseSbfMeas: () => parseSbfMeas,
25
+ parseSbfNav: () => parseSbfNav
24
26
  });
25
27
  module.exports = __toCommonJS(sbf_exports);
26
- var CLIGHT = 299792458;
27
- var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
28
- var MS_PER_WEEK = 7 * 864e5;
28
+
29
+ // src/sbf/frame.ts
29
30
  var CRC_TABLE = (() => {
30
31
  const t = new Uint16Array(256);
31
32
  for (let n = 0; n < 256; n++) {
@@ -42,6 +43,449 @@ function crc16(data, start, end) {
42
43
  crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
43
44
  return crc;
44
45
  }
46
+ function scanSbfFrames(data, view, onBlock) {
47
+ let badCrc = 0;
48
+ let i = 0;
49
+ while (i + 8 <= data.length) {
50
+ if (data[i] !== 36 || data[i + 1] !== 64) {
51
+ i++;
52
+ continue;
53
+ }
54
+ const len = view.getUint16(i + 6, true);
55
+ if (len < 8 || len % 4 !== 0) {
56
+ i++;
57
+ continue;
58
+ }
59
+ if (i + len > data.length) break;
60
+ if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
61
+ badCrc++;
62
+ i++;
63
+ continue;
64
+ }
65
+ onBlock(view.getUint16(i + 4, true) & 8191, i, len);
66
+ i += len;
67
+ }
68
+ return badCrc;
69
+ }
70
+ var two = (n) => String(n).padStart(2, "0");
71
+ function svidToPrn(svid) {
72
+ if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
73
+ if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
74
+ if (svid <= 62) return null;
75
+ if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
76
+ if (svid <= 70) return null;
77
+ if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
78
+ if (svid <= 119) return null;
79
+ if (svid <= 140) return `S${two(svid - 100)}`;
80
+ if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
81
+ if (svid <= 190) return `J${two(svid - 180)}`;
82
+ if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
83
+ if (svid <= 215) return `S${two(svid - 157)}`;
84
+ if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
85
+ if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
86
+ return null;
87
+ }
88
+
89
+ // src/constants/time.ts
90
+ var MILLISECONDS_IN_SECOND = 1e3;
91
+ var MILLISECONDS_GPS_TAI = 19e3;
92
+ var START_NTP_TIME = /* @__PURE__ */ new Date("1900-01-01T00:00:00Z");
93
+
94
+ // src/time/gnss.ts
95
+ function getTaiDate(date) {
96
+ return new Date(date.getTime() + MILLISECONDS_GPS_TAI);
97
+ }
98
+ function getNtpTime(date) {
99
+ return getTaiDate(date).getTime() - START_NTP_TIME.getTime();
100
+ }
101
+
102
+ // src/time/utc.ts
103
+ var LEAP_SECONDS_TABLE = [
104
+ [3692217600, 37],
105
+ // 1 Jan 2017
106
+ [3644697600, 36],
107
+ // 1 Jul 2015
108
+ [3550089600, 35],
109
+ // 1 Jul 2012
110
+ [3439756800, 34],
111
+ // 1 Jan 2009
112
+ [3345062400, 33],
113
+ // 1 Jan 2006
114
+ [3124137600, 32],
115
+ // 1 Jan 1999
116
+ [3076704e3, 31],
117
+ // 1 Jul 1997
118
+ [3029443200, 30],
119
+ // 1 Jan 1996
120
+ [2982009600, 29],
121
+ // 1 Jul 1994
122
+ [2950473600, 28],
123
+ // 1 Jul 1993
124
+ [2918937600, 27],
125
+ // 1 Jul 1992
126
+ [2871676800, 26],
127
+ // 1 Jan 1991
128
+ [2840140800, 25],
129
+ // 1 Jan 1990
130
+ [2776982400, 24],
131
+ // 1 Jan 1988
132
+ [2698012800, 23],
133
+ // 1 Jul 1985
134
+ [2634854400, 22],
135
+ // 1 Jul 1983
136
+ [2603318400, 21],
137
+ // 1 Jul 1982
138
+ [2571782400, 20],
139
+ // 1 Jul 1981
140
+ [2524521600, 19],
141
+ // 1 Jan 1980
142
+ [2492985600, 18],
143
+ // 1 Jan 1979
144
+ [2461449600, 17],
145
+ // 1 Jan 1978
146
+ [2429913600, 16],
147
+ // 1 Jan 1977
148
+ [2398291200, 15],
149
+ // 1 Jan 1976
150
+ [2366755200, 14],
151
+ // 1 Jan 1975
152
+ [2335219200, 13],
153
+ // 1 Jan 1974
154
+ [2303683200, 12],
155
+ // 1 Jan 1973
156
+ [2287785600, 11],
157
+ // 1 Jul 1972
158
+ [2272060800, 10]
159
+ // 1 Jan 1972
160
+ ];
161
+ function getLeap(date) {
162
+ const ntp_time = getNtpTime(date);
163
+ for (const [timestamp, leapSeconds] of LEAP_SECONDS_TABLE) {
164
+ if (ntp_time / MILLISECONDS_IN_SECOND - leapSeconds >= timestamp) {
165
+ return leapSeconds;
166
+ }
167
+ }
168
+ return 8;
169
+ }
170
+ function getGpsLeap(date) {
171
+ const leap_seconds = getLeap(date);
172
+ if (leap_seconds < 0) return 0;
173
+ return leap_seconds - 19;
174
+ }
175
+
176
+ // src/sbf/nav.ts
177
+ var PI = Math.PI;
178
+ var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
179
+ var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
180
+ var SEC_PER_WEEK = 7 * 86400;
181
+ var MS_PER_WEEK = SEC_PER_WEEK * 1e3;
182
+ var F4_DNU = -2e10;
183
+ var U4_DNU = 4294967295;
184
+ function adjustWeek(refWeek, wn, mod) {
185
+ let offset = refWeek % mod - wn;
186
+ if (offset > mod / 2) offset -= mod;
187
+ if (offset < -(mod / 2 - 1)) offset += mod;
188
+ return refWeek - offset;
189
+ }
190
+ var gpsMs = (week, sec) => GPS_EPOCH_MS + week * MS_PER_WEEK + sec * 1e3;
191
+ var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
192
+ function decodeGpsQzsNav(view, b, sys) {
193
+ const svid = view.getUint8(b + 14);
194
+ const prn = sys === "G" ? svid : svid - 180;
195
+ const max = sys === "G" ? 32 : 10;
196
+ if (prn < 1 || prn > max) return null;
197
+ const wnc = view.getUint16(b + 12, true);
198
+ const tgdRaw = view.getFloat32(b + 28, true);
199
+ const tocs = view.getUint32(b + 32, true);
200
+ const toes = view.getUint32(b + 88, true);
201
+ const wnToc = adjustWeek(wnc, view.getUint16(b + 136, true), 1024);
202
+ const tocDate = new Date(gpsMs(wnToc, tocs));
203
+ return {
204
+ system: sys,
205
+ prn: `${sys}${String(prn).padStart(2, "0")}`,
206
+ toc: sowOf(tocDate.getTime()),
207
+ tocDate,
208
+ af0: view.getFloat32(b + 44, true),
209
+ af1: view.getFloat32(b + 40, true),
210
+ af2: view.getFloat32(b + 36, true),
211
+ iode: view.getUint8(b + 24),
212
+ crs: view.getFloat32(b + 48, true),
213
+ deltaN: view.getFloat32(b + 52, true) * PI,
214
+ m0: view.getFloat64(b + 56, true) * PI,
215
+ cuc: view.getFloat32(b + 64, true),
216
+ e: view.getFloat64(b + 68, true),
217
+ cus: view.getFloat32(b + 76, true),
218
+ sqrtA: view.getFloat64(b + 80, true),
219
+ toe: toes,
220
+ cic: view.getFloat32(b + 92, true),
221
+ omega0: view.getFloat64(b + 96, true) * PI,
222
+ cis: view.getFloat32(b + 104, true),
223
+ i0: view.getFloat64(b + 108, true) * PI,
224
+ crc: view.getFloat32(b + 116, true),
225
+ omega: view.getFloat64(b + 120, true) * PI,
226
+ omegaDot: view.getFloat32(b + 128, true) * PI,
227
+ idot: view.getFloat32(b + 132, true) * PI,
228
+ // RINEX "GPS week to go with toe": RTKLIB writes WNc for GPS and
229
+ // the week of toc for QZSS — mirrored here so records compare
230
+ // 1:1 against convbin output. wnToe is still used for `toe` above.
231
+ week: sys === "G" ? wnc : wnToc,
232
+ svHealth: view.getUint8(b + 20),
233
+ tgd: tgdRaw !== F4_DNU ? tgdRaw : 0
234
+ };
235
+ }
236
+ function decodeGalNav(view, b) {
237
+ const prn = view.getUint8(b + 14) - 70;
238
+ if (prn < 1 || prn > 36) return null;
239
+ const source = view.getUint8(b + 15);
240
+ if (source !== 2 && source !== 16) return null;
241
+ const wnc = view.getUint16(b + 12, true);
242
+ const toes = view.getUint32(b + 100, true);
243
+ const tocs = view.getUint32(b + 104, true);
244
+ const wnToc = adjustWeek(wnc, view.getUint16(b + 126, true), 4096);
245
+ const tocDate = new Date(gpsMs(wnToc, tocs));
246
+ const health = view.getUint16(b + 130, true);
247
+ let svh = 0;
248
+ if (health & 1) svh |= health >> 1 & 7;
249
+ if (health & 16) svh |= (health >> 5 & 7) << 6;
250
+ if (health & 256) svh |= (health >> 9 & 7) << 3;
251
+ const bgdE5a = view.getFloat32(b + 136, true);
252
+ return {
253
+ system: "E",
254
+ prn: `E${String(prn).padStart(2, "0")}`,
255
+ toc: sowOf(tocDate.getTime()),
256
+ tocDate,
257
+ af0: view.getFloat64(b + 116, true),
258
+ af1: view.getFloat32(b + 112, true),
259
+ af2: view.getFloat32(b + 108, true),
260
+ iode: view.getUint16(b + 128, true),
261
+ // IODnav
262
+ crs: view.getFloat32(b + 88, true),
263
+ deltaN: view.getFloat32(b + 72, true) * PI,
264
+ m0: view.getFloat64(b + 24, true) * PI,
265
+ cuc: view.getFloat32(b + 76, true),
266
+ e: view.getFloat64(b + 32, true),
267
+ cus: view.getFloat32(b + 80, true),
268
+ sqrtA: view.getFloat64(b + 16, true),
269
+ toe: toes,
270
+ cic: view.getFloat32(b + 92, true),
271
+ omega0: view.getFloat64(b + 56, true) * PI,
272
+ cis: view.getFloat32(b + 96, true),
273
+ i0: view.getFloat64(b + 40, true) * PI,
274
+ crc: view.getFloat32(b + 84, true),
275
+ omega: view.getFloat64(b + 48, true) * PI,
276
+ omegaDot: view.getFloat32(b + 64, true) * PI,
277
+ idot: view.getFloat32(b + 68, true) * PI,
278
+ week: wnc,
279
+ // RINEX GAL week is GPS-aligned and continuous
280
+ svHealth: svh,
281
+ tgd: bgdE5a !== F4_DNU ? bgdE5a : 0
282
+ // BGD E5a/E1 (RINEX slot)
283
+ };
284
+ }
285
+ function decodeBdsNav(view, b) {
286
+ const prn = svidToPrn(view.getUint8(b + 14));
287
+ if (!prn || prn[0] !== "C") return null;
288
+ const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
289
+ const tgd1 = view.getFloat32(b + 24, true);
290
+ const tocs = view.getUint32(b + 32, true);
291
+ const toes = view.getUint32(b + 88, true);
292
+ const wnToc = adjustWeek(bdsWeekRef, view.getUint16(b + 136, true), 8192);
293
+ const wnToe = adjustWeek(bdsWeekRef, view.getUint16(b + 138, true), 8192);
294
+ const tocDate = new Date(BDT_EPOCH_MS + wnToc * MS_PER_WEEK + tocs * 1e3);
295
+ return {
296
+ system: "C",
297
+ prn,
298
+ toc: sowOf(tocDate.getTime()),
299
+ tocDate,
300
+ af0: view.getFloat32(b + 44, true),
301
+ af1: view.getFloat32(b + 40, true),
302
+ af2: view.getFloat32(b + 36, true),
303
+ iode: view.getUint8(b + 21),
304
+ // AODE
305
+ crs: view.getFloat32(b + 48, true),
306
+ deltaN: view.getFloat32(b + 52, true) * PI,
307
+ m0: view.getFloat64(b + 56, true) * PI,
308
+ cuc: view.getFloat32(b + 64, true),
309
+ e: view.getFloat64(b + 68, true),
310
+ cus: view.getFloat32(b + 76, true),
311
+ sqrtA: view.getFloat64(b + 80, true),
312
+ toe: toes,
313
+ cic: view.getFloat32(b + 92, true),
314
+ omega0: view.getFloat64(b + 96, true) * PI,
315
+ cis: view.getFloat32(b + 104, true),
316
+ i0: view.getFloat64(b + 108, true) * PI,
317
+ crc: view.getFloat32(b + 116, true),
318
+ omega: view.getFloat64(b + 120, true) * PI,
319
+ omegaDot: view.getFloat32(b + 128, true) * PI,
320
+ idot: view.getFloat32(b + 132, true) * PI,
321
+ week: wnToe,
322
+ // RINEX BDS week field is the BDT week
323
+ svHealth: view.getUint8(b + 19),
324
+ // SatH1
325
+ tgd: tgd1 !== F4_DNU ? tgd1 : 0
326
+ // TGD1 B1/B3 (RINEX slot)
327
+ };
328
+ }
329
+ function decodeGloNav(view, b) {
330
+ const prn = svidToPrn(view.getUint8(b + 14));
331
+ if (!prn || prn[0] !== "R") return null;
332
+ const wnc = view.getUint16(b + 12, true);
333
+ const toes = view.getUint32(b + 76, true);
334
+ const wnToe = adjustWeek(wnc, view.getUint16(b + 80, true), 1024);
335
+ const toeGpsMs = gpsMs(wnToe, toes);
336
+ const leapMs = getGpsLeap(new Date(toeGpsMs)) * 1e3;
337
+ const tocDate = new Date(toeGpsMs - leapMs);
338
+ const tofGpsMs = gpsMs(wnc, 0) + view.getUint32(b + 8, true);
339
+ const tofLeapMs = getGpsLeap(new Date(tofGpsMs)) * 1e3;
340
+ const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS) / 1e3 % SEC_PER_WEEK;
341
+ return {
342
+ system: "R",
343
+ prn,
344
+ tocDate,
345
+ // RINEX stores −τn as the clock bias; SBF carries τn (ICD sign)
346
+ tauN: -view.getFloat32(b + 68, true),
347
+ gammaN: view.getFloat32(b + 64, true),
348
+ messageFrameTime,
349
+ x: view.getFloat64(b + 16, true),
350
+ // SBF unit is km, like RINEX
351
+ xDot: view.getFloat32(b + 40, true),
352
+ xAcc: view.getFloat32(b + 52, true),
353
+ y: view.getFloat64(b + 24, true),
354
+ yDot: view.getFloat32(b + 44, true),
355
+ yAcc: view.getFloat32(b + 56, true),
356
+ z: view.getFloat64(b + 32, true),
357
+ zDot: view.getFloat32(b + 48, true),
358
+ zAcc: view.getFloat32(b + 60, true),
359
+ // MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
360
+ health: view.getUint8(b + 85) >> 2,
361
+ freqNum: view.getUint8(b + 15) - 8
362
+ };
363
+ }
364
+ function parseSbfNav(data) {
365
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
366
+ const ephemerides = [];
367
+ const badCrc = scanSbfFrames(data, view, (id, b, len) => {
368
+ let eph = null;
369
+ if (id === 5891 && len >= 140) eph = decodeGpsQzsNav(view, b, "G");
370
+ else if (id === 4095 && len >= 140) eph = decodeGpsQzsNav(view, b, "J");
371
+ else if (id === 4002 && len >= 149) eph = decodeGalNav(view, b);
372
+ else if (id === 4081 && len >= 140) eph = decodeBdsNav(view, b);
373
+ else if (id === 4004 && len >= 96) eph = decodeGloNav(view, b);
374
+ if (eph) ephemerides.push(eph);
375
+ });
376
+ return { ephemerides, badCrc };
377
+ }
378
+ var GAL_SQRT_A_NOMINAL = Math.sqrt(296e5);
379
+ var GAL_I_NOMINAL = 56 / 180 * PI;
380
+ var I_REF_03 = 0.3 * PI;
381
+ function decodeGpsGalAlm(view, b, sys) {
382
+ const wnc = view.getUint16(b + 12, true);
383
+ const toa = view.getUint32(b + 20, true);
384
+ if (toa === U4_DNU) return null;
385
+ let prn;
386
+ let weekAlm;
387
+ let i0;
388
+ let sqrtA = view.getFloat32(b + 32, true);
389
+ let health;
390
+ if (sys === "G") {
391
+ prn = view.getUint8(b + 14);
392
+ if (prn < 1 || prn > 32) return null;
393
+ weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 256);
394
+ i0 = I_REF_03 + view.getFloat32(b + 24, true) * PI;
395
+ health = view.getUint8(b + 58);
396
+ } else {
397
+ prn = view.getUint8(b + 57) - 70;
398
+ if (prn < 1 || prn > 36) return null;
399
+ weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 4);
400
+ i0 = GAL_I_NOMINAL + view.getFloat32(b + 24, true) * PI;
401
+ sqrtA += GAL_SQRT_A_NOMINAL;
402
+ health = view.getUint16(b + 58, true);
403
+ }
404
+ return {
405
+ system: sys,
406
+ prn: `${sys}${String(prn).padStart(2, "0")}`,
407
+ weekAlm,
408
+ toaSec: toa,
409
+ sqrtA,
410
+ e: view.getFloat32(b + 16, true),
411
+ i0OrDeltaI: i0,
412
+ omega0: view.getFloat32(b + 36, true) * PI,
413
+ omega: view.getFloat32(b + 40, true) * PI,
414
+ m0: view.getFloat32(b + 44, true) * PI,
415
+ omegaDot: view.getFloat32(b + 28, true) * PI,
416
+ af0: view.getFloat32(b + 52, true),
417
+ af1: view.getFloat32(b + 48, true),
418
+ health
419
+ };
420
+ }
421
+ function decodeBdsAlm(view, b) {
422
+ const prnStr = svidToPrn(view.getUint8(b + 14));
423
+ if (!prnStr || prnStr[0] !== "C") return null;
424
+ const prn = parseInt(prnStr.slice(1), 10);
425
+ const toa = view.getUint32(b + 16, true);
426
+ if (toa === U4_DNU) return null;
427
+ const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
428
+ const geo = prn <= 5 || prn >= 59;
429
+ return {
430
+ system: "C",
431
+ prn: prnStr,
432
+ weekAlm: adjustWeek(bdsWeekRef, view.getUint8(b + 15), 256),
433
+ toaSec: toa,
434
+ sqrtA: view.getFloat32(b + 20, true),
435
+ e: view.getFloat32(b + 24, true),
436
+ i0OrDeltaI: (geo ? 0 : I_REF_03) + view.getFloat32(b + 44, true) * PI,
437
+ omega0: view.getFloat32(b + 36, true) * PI,
438
+ omega: view.getFloat32(b + 28, true) * PI,
439
+ m0: view.getFloat32(b + 32, true) * PI,
440
+ omegaDot: view.getFloat32(b + 40, true) * PI,
441
+ af0: view.getFloat32(b + 48, true),
442
+ af1: view.getFloat32(b + 52, true),
443
+ health: view.getUint16(b + 56, true)
444
+ };
445
+ }
446
+ function decodeGloAlm(view, b) {
447
+ const prn = svidToPrn(view.getUint8(b + 14));
448
+ if (!prn || prn[0] !== "R") return null;
449
+ const wnc = view.getUint16(b + 12, true);
450
+ const toa = view.getUint32(b + 20, true);
451
+ if (toa === U4_DNU) return null;
452
+ return {
453
+ system: "R",
454
+ prn,
455
+ freqNr: view.getUint8(b + 15) - 8,
456
+ weekAlm: adjustWeek(wnc, view.getUint8(b + 52), 256),
457
+ toaSec: toa,
458
+ epsilon: view.getFloat32(b + 16, true),
459
+ deltaI: view.getFloat32(b + 24, true) * PI,
460
+ lambda: view.getFloat32(b + 28, true) * PI,
461
+ tLambda: view.getFloat32(b + 32, true),
462
+ omega: view.getFloat32(b + 36, true) * PI,
463
+ deltaT: view.getFloat32(b + 40, true),
464
+ deltaTDot: view.getFloat32(b + 44, true),
465
+ tau: view.getFloat32(b + 48, true),
466
+ health: view.getUint8(b + 53),
467
+ nDay: view.getUint16(b + 54, true),
468
+ n4: view.getUint8(b + 57)
469
+ };
470
+ }
471
+ function parseSbfAlmanac(data) {
472
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
473
+ const almanacs = [];
474
+ const badCrc = scanSbfFrames(data, view, (id, b, len) => {
475
+ let alm = null;
476
+ if (id === 5892 && len >= 60) alm = decodeGpsGalAlm(view, b, "G");
477
+ else if (id === 4003 && len >= 61) alm = decodeGpsGalAlm(view, b, "E");
478
+ else if (id === 4119 && len >= 60) alm = decodeBdsAlm(view, b);
479
+ else if (id === 4005 && len >= 60) alm = decodeGloAlm(view, b);
480
+ if (alm) almanacs.push(alm);
481
+ });
482
+ return { almanacs, badCrc };
483
+ }
484
+
485
+ // src/sbf/index.ts
486
+ var CLIGHT = 299792458;
487
+ var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
488
+ var MS_PER_WEEK2 = 7 * 864e5;
45
489
  var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
46
490
  var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
47
491
  var M3_INTERVALS = [
@@ -140,44 +584,27 @@ var MEAS2_SIG = [
140
584
  ["J", "1E"],
141
585
  ["J", "5P"]
142
586
  ];
143
- var two = (n) => String(n).padStart(2, "0");
587
+ var two2 = (n) => String(n).padStart(2, "0");
144
588
  function m3Prn(navsys, svid) {
145
589
  switch (navsys) {
146
590
  case 0:
147
- return svid < 32 ? `G${two(svid + 1)}` : null;
591
+ return svid < 32 ? `G${two2(svid + 1)}` : null;
148
592
  case 1:
149
- return svid < 27 ? `R${two(svid + 1)}` : null;
593
+ return svid < 27 ? `R${two2(svid + 1)}` : null;
150
594
  case 2:
151
- return svid < 36 ? `E${two(svid + 1)}` : null;
595
+ return svid < 36 ? `E${two2(svid + 1)}` : null;
152
596
  case 3:
153
- return svid < 50 ? `C${two(svid + 1)}` : null;
597
+ return svid < 50 ? `C${two2(svid + 1)}` : null;
154
598
  case 4:
155
- return svid <= 38 ? `S${two(svid + 20)}` : null;
599
+ return svid <= 38 ? `S${two2(svid + 20)}` : null;
156
600
  case 5:
157
- return svid < 10 ? `J${two(svid + 1)}` : null;
601
+ return svid < 10 ? `J${two2(svid + 1)}` : null;
158
602
  case 6:
159
- return svid < 14 ? `I${two(svid + 1)}` : null;
603
+ return svid < 14 ? `I${two2(svid + 1)}` : null;
160
604
  default:
161
605
  return null;
162
606
  }
163
607
  }
164
- function svidToPrn(svid) {
165
- if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
166
- if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
167
- if (svid <= 62) return null;
168
- if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
169
- if (svid <= 70) return null;
170
- if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
171
- if (svid <= 119) return null;
172
- if (svid <= 140) return `S${two(svid - 100)}`;
173
- if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
174
- if (svid <= 190) return `J${two(svid - 180)}`;
175
- if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
176
- if (svid <= 215) return `S${two(svid - 157)}`;
177
- if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
178
- if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
179
- return null;
180
- }
181
608
  function carrierFreq(sys, code, fcn) {
182
609
  const band = code.charCodeAt(0) - 48;
183
610
  switch (sys) {
@@ -240,7 +667,6 @@ function parseSbfMeas(data) {
240
667
  const epochs = [];
241
668
  const messageCounts = {};
242
669
  const obsCodes = {};
243
- let badCrc = 0;
244
670
  let ref = freshRefEpoch(-1);
245
671
  let curTime = NaN;
246
672
  let curMeas = [];
@@ -271,8 +697,8 @@ function parseSbfMeas(data) {
271
697
  curTime = timeMs;
272
698
  }
273
699
  };
274
- const decodeMeas3Ranges = (i2, tow) => {
275
- const o = i2 + 8;
700
+ const decodeMeas3Ranges = (i, tow) => {
701
+ const o = i + 8;
276
702
  const constellations = view.getUint16(o + 8, true) & 127;
277
703
  const misc = data[o + 10];
278
704
  const version = data[o + 11];
@@ -550,8 +976,8 @@ function parseSbfMeas(data) {
550
976
  }
551
977
  }
552
978
  };
553
- const decodeMeas3CN0 = (i2, len) => {
554
- const flags = view.getUint16(i2 + 14, true);
979
+ const decodeMeas3CN0 = (i, len) => {
980
+ const flags = view.getUint16(i + 14, true);
555
981
  if ((flags & 7) !== 0) return;
556
982
  const maxNibbles = (len - 16) * 2;
557
983
  let off = 0;
@@ -561,7 +987,7 @@ function parseSbfMeas(data) {
561
987
  for (let k = 0; k < e.slots.length; k++) {
562
988
  const slot = e.slots[k];
563
989
  if (k > 0 && !slot) break;
564
- const nib = (data[i2 + 16 + (off >> 1)] ?? 0) >> (off & 1) * 4 & 15;
990
+ const nib = (data[i + 16 + (off >> 1)] ?? 0) >> (off & 1) * 4 & 15;
565
991
  if (slot && slot.cn0 !== null) slot.cn0 += nib * 0.0625 - 0.5;
566
992
  off++;
567
993
  }
@@ -588,14 +1014,14 @@ function parseSbfMeas(data) {
588
1014
  if ((v & 1) === 1) value = -value;
589
1015
  return { value, size };
590
1016
  };
591
- const decodeMeas3Doppler = (i2, len) => {
592
- const flags = view.getUint16(i2 + 14, true);
1017
+ const decodeMeas3Doppler = (i, len) => {
1018
+ const flags = view.getUint16(i + 14, true);
593
1019
  if ((flags & 7) !== 0) return;
594
1020
  let off = 0;
595
1021
  for (const e of curSats) {
596
1022
  if (off + 16 >= len) break;
597
1023
  if (!e.prn) continue;
598
- const master = readPrRate(i2 + 16, off);
1024
+ const master = readPrRate(i + 16, off);
599
1025
  off += master.size;
600
1026
  if (master.value === -268435455) continue;
601
1027
  const refPrRate = ref.sats[e.navsys][e.svid]?.prRate ?? 0;
@@ -605,20 +1031,20 @@ function parseSbfMeas(data) {
605
1031
  for (let k = 1; k < e.slots.length; k++) {
606
1032
  const slot = e.slots[k];
607
1033
  if (!slot) break;
608
- const slave = readPrRate(i2 + 16, off);
1034
+ const slave = readPrRate(i + 16, off);
609
1035
  off += slave.size;
610
1036
  if (m)
611
1037
  slot.doppler = (mD * (CLIGHT / m.freq) * 1e3 - slave.value) * 1e-3 / (CLIGHT / slot.freq);
612
1038
  }
613
1039
  }
614
1040
  };
615
- const decodeMeasEpoch = (i2, len) => {
616
- const n1 = data[i2 + 14];
617
- const len1 = data[i2 + 15];
618
- const len2 = data[i2 + 16];
619
- if ((data[i2 + 17] & 128) !== 0) return;
620
- const end = i2 + len;
621
- let p = i2 + 20;
1041
+ const decodeMeasEpoch = (i, len) => {
1042
+ const n1 = data[i + 14];
1043
+ const len1 = data[i + 15];
1044
+ const len2 = data[i + 16];
1045
+ if ((data[i + 17] & 128) !== 0) return;
1046
+ const end = i + len;
1047
+ let p = i + 20;
622
1048
  for (let b1 = 0; b1 < n1 && p + 20 <= end; b1++) {
623
1049
  const ant = data[p + 1] >> 5;
624
1050
  let sig = data[p + 1] & 31;
@@ -705,43 +1131,27 @@ function parseSbfMeas(data) {
705
1131
  }
706
1132
  }
707
1133
  };
708
- let i = 0;
709
- while (i + 8 <= data.length) {
710
- if (data[i] !== 36 || data[i + 1] !== 64) {
711
- i++;
712
- continue;
713
- }
714
- const len = view.getUint16(i + 6, true);
715
- if (len < 8 || len % 4 !== 0) {
716
- i++;
717
- continue;
718
- }
719
- if (i + len > data.length) break;
720
- if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
721
- badCrc++;
722
- i++;
723
- continue;
724
- }
725
- const id = view.getUint16(i + 4, true) & 8191;
1134
+ const badCrc = scanSbfFrames(data, view, (id, i, len) => {
726
1135
  const key = String(id);
727
1136
  messageCounts[key] = (messageCounts[key] ?? 0) + 1;
728
1137
  if ((id === 4027 || id === 4109 || id === 4110 || id === 4111) && len >= 16) {
729
1138
  const tow = view.getUint32(i + 8, true);
730
1139
  const wnc = view.getUint16(i + 12, true);
731
1140
  if (tow !== 4294967295 && wnc !== 65535) {
732
- ensureEpoch(GPS_EPOCH_MS + wnc * MS_PER_WEEK + tow);
1141
+ ensureEpoch(GPS_EPOCH_MS2 + wnc * MS_PER_WEEK2 + tow);
733
1142
  if (id === 4109) decodeMeas3Ranges(i, tow);
734
1143
  else if (id === 4110) decodeMeas3CN0(i, len);
735
1144
  else if (id === 4111) decodeMeas3Doppler(i, len);
736
1145
  else decodeMeasEpoch(i, len);
737
1146
  }
738
1147
  }
739
- i += len;
740
- }
1148
+ });
741
1149
  flush();
742
1150
  return { epochs, messageCounts, obsCodes, badCrc };
743
1151
  }
744
1152
  // Annotate the CommonJS export names for ESM import in node:
745
1153
  0 && (module.exports = {
746
- parseSbfMeas
1154
+ parseSbfAlmanac,
1155
+ parseSbfMeas,
1156
+ parseSbfNav
747
1157
  });