gnss-js 1.16.0 → 1.17.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/{chunk-R33A2VA7.js → chunk-7QR2KPDU.js} +188 -12
- package/dist/{chunk-6CX6EXRK.js → chunk-IG5CDNDS.js} +58 -23
- package/dist/{chunk-4LVD4DYL.js → chunk-REYOYF7O.js} +1 -0
- package/dist/{chunk-YPOVPXNK.js → chunk-ZNCQNHNI.js} +34 -0
- package/dist/index.cjs +283 -37
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -4
- package/dist/novatel.cjs +187 -11
- package/dist/novatel.d.cts +36 -13
- package/dist/novatel.d.ts +36 -13
- package/dist/novatel.js +2 -2
- package/dist/sbf.cjs +35 -0
- package/dist/sbf.d.cts +41 -1
- package/dist/sbf.d.ts +41 -1
- package/dist/sbf.js +3 -1
- package/dist/ubx.cjs +57 -22
- package/dist/ubx.d.cts +42 -2
- package/dist/ubx.d.ts +42 -2
- package/dist/ubx.js +4 -2
- package/package.json +1 -1
package/dist/novatel.cjs
CHANGED
|
@@ -290,11 +290,26 @@ function getUtcDate(date) {
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
// src/novatel/nav.ts
|
|
293
|
+
var ID_IONUTC = 8;
|
|
293
294
|
var ID_RAWEPHEM = 41;
|
|
294
295
|
var ID_GLOEPHEMERIS = 723;
|
|
296
|
+
var ID_GALEPHEMERIS = 1122;
|
|
297
|
+
var ID_QZSSEPHEMERIS = 1336;
|
|
298
|
+
var ID_BDSEPHEMERIS = 1696;
|
|
295
299
|
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
300
|
+
var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
|
|
296
301
|
var SEC_PER_WEEK2 = 7 * 86400;
|
|
297
302
|
var SEC_PER_DAY = 86400;
|
|
303
|
+
var MS_PER_WEEK = SEC_PER_WEEK2 * 1e3;
|
|
304
|
+
var gpsMs = (week, sec) => GPS_EPOCH_MS2 + week * MS_PER_WEEK + sec * 1e3;
|
|
305
|
+
var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK2;
|
|
306
|
+
function nearWeekMs(refMs, sow) {
|
|
307
|
+
const refWeek = Math.floor((refMs - GPS_EPOCH_MS2) / MS_PER_WEEK);
|
|
308
|
+
let ms = gpsMs(refWeek, sow);
|
|
309
|
+
if (ms < refMs - MS_PER_WEEK / 2) ms += MS_PER_WEEK;
|
|
310
|
+
else if (ms > refMs + MS_PER_WEEK / 2) ms -= MS_PER_WEEK;
|
|
311
|
+
return ms;
|
|
312
|
+
}
|
|
298
313
|
function decodeRawEphem(data, view, p) {
|
|
299
314
|
const prn = view.getUint32(p, true);
|
|
300
315
|
if (prn < 1 || prn > 32) return null;
|
|
@@ -340,23 +355,184 @@ function decodeGloEphemeris(view, p) {
|
|
|
340
355
|
freqNum
|
|
341
356
|
};
|
|
342
357
|
}
|
|
358
|
+
function decodeGalEphemeris(view, p, headerMs) {
|
|
359
|
+
const prn = view.getUint32(p, true);
|
|
360
|
+
if (prn < 1 || prn > 36) return null;
|
|
361
|
+
const fnav = (view.getUint32(p + 4, true) & 1) !== 0;
|
|
362
|
+
const svhE1b = view.getUint8(p + 12) & 3;
|
|
363
|
+
const svhE5a = view.getUint8(p + 13) & 3;
|
|
364
|
+
const svhE5b = view.getUint8(p + 14) & 3;
|
|
365
|
+
const dvsE1b = view.getUint8(p + 15) & 1;
|
|
366
|
+
const dvsE5a = view.getUint8(p + 16) & 1;
|
|
367
|
+
const dvsE5b = view.getUint8(p + 17) & 1;
|
|
368
|
+
const iodNav = view.getUint32(p + 20, true);
|
|
369
|
+
const toes = view.getUint32(p + 24, true);
|
|
370
|
+
const c = fnav ? p + 148 : p + 176;
|
|
371
|
+
const tocs = view.getUint32(c, true);
|
|
372
|
+
const toeMs = nearWeekMs(headerMs, toes);
|
|
373
|
+
const tocDate = new Date(nearWeekMs(headerMs, tocs));
|
|
374
|
+
return {
|
|
375
|
+
fnav,
|
|
376
|
+
eph: {
|
|
377
|
+
system: "E",
|
|
378
|
+
prn: `E${String(prn).padStart(2, "0")}`,
|
|
379
|
+
toc: sowOf(tocDate.getTime()),
|
|
380
|
+
tocDate,
|
|
381
|
+
af0: view.getFloat64(c + 4, true),
|
|
382
|
+
af1: view.getFloat64(c + 12, true),
|
|
383
|
+
af2: view.getFloat64(c + 20, true),
|
|
384
|
+
iode: iodNav,
|
|
385
|
+
crs: view.getFloat64(p + 92, true),
|
|
386
|
+
deltaN: view.getFloat64(p + 36, true),
|
|
387
|
+
m0: view.getFloat64(p + 44, true),
|
|
388
|
+
cuc: view.getFloat64(p + 68, true),
|
|
389
|
+
e: view.getFloat64(p + 52, true),
|
|
390
|
+
cus: view.getFloat64(p + 76, true),
|
|
391
|
+
sqrtA: view.getFloat64(p + 28, true),
|
|
392
|
+
toe: toes,
|
|
393
|
+
cic: view.getFloat64(p + 100, true),
|
|
394
|
+
omega0: view.getFloat64(p + 132, true),
|
|
395
|
+
cis: view.getFloat64(p + 108, true),
|
|
396
|
+
i0: view.getFloat64(p + 116, true),
|
|
397
|
+
crc: view.getFloat64(p + 84, true),
|
|
398
|
+
omega: view.getFloat64(p + 60, true),
|
|
399
|
+
omegaDot: view.getFloat64(p + 140, true),
|
|
400
|
+
idot: view.getFloat64(p + 124, true),
|
|
401
|
+
week: Math.floor((toeMs - GPS_EPOCH_MS2) / MS_PER_WEEK),
|
|
402
|
+
// RINEX Galileo SVH bit layout (E1B DVS/HS in bits 0-2, E5a in
|
|
403
|
+
// 3-5, E5b in 6-8) — same packing as RTKLIB.
|
|
404
|
+
svHealth: svhE5b << 7 | dvsE5b << 6 | svhE5a << 4 | dvsE5a << 3 | svhE1b << 1 | dvsE1b,
|
|
405
|
+
tgd: view.getFloat64(p + 204, true)
|
|
406
|
+
// BGD E5a/E1 (RINEX slot)
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function decodeBdsEphemeris(view, p) {
|
|
411
|
+
const prn = view.getUint32(p, true);
|
|
412
|
+
if (prn < 1 || prn > 63) return null;
|
|
413
|
+
const week = view.getUint32(p + 4, true);
|
|
414
|
+
const health = view.getUint32(p + 16, true) & 1;
|
|
415
|
+
const tgd1 = view.getFloat64(p + 20, true);
|
|
416
|
+
const tocs = view.getUint32(p + 40, true);
|
|
417
|
+
const toes = view.getUint32(p + 72, true);
|
|
418
|
+
const tocDate = new Date(BDT_EPOCH_MS + week * MS_PER_WEEK + tocs * 1e3);
|
|
419
|
+
return {
|
|
420
|
+
system: "C",
|
|
421
|
+
prn: `C${String(prn).padStart(2, "0")}`,
|
|
422
|
+
toc: sowOf(tocDate.getTime()),
|
|
423
|
+
tocDate,
|
|
424
|
+
af0: view.getFloat64(p + 44, true),
|
|
425
|
+
af1: view.getFloat64(p + 52, true),
|
|
426
|
+
af2: view.getFloat64(p + 60, true),
|
|
427
|
+
iode: view.getUint32(p + 68, true),
|
|
428
|
+
// AODE
|
|
429
|
+
crs: view.getFloat64(p + 172, true),
|
|
430
|
+
deltaN: view.getFloat64(p + 100, true),
|
|
431
|
+
m0: view.getFloat64(p + 108, true),
|
|
432
|
+
cuc: view.getFloat64(p + 148, true),
|
|
433
|
+
e: view.getFloat64(p + 84, true),
|
|
434
|
+
cus: view.getFloat64(p + 156, true),
|
|
435
|
+
sqrtA: view.getFloat64(p + 76, true),
|
|
436
|
+
toe: toes,
|
|
437
|
+
cic: view.getFloat64(p + 180, true),
|
|
438
|
+
omega0: view.getFloat64(p + 116, true),
|
|
439
|
+
cis: view.getFloat64(p + 188, true),
|
|
440
|
+
i0: view.getFloat64(p + 132, true),
|
|
441
|
+
crc: view.getFloat64(p + 164, true),
|
|
442
|
+
omega: view.getFloat64(p + 92, true),
|
|
443
|
+
omegaDot: view.getFloat64(p + 124, true),
|
|
444
|
+
idot: view.getFloat64(p + 140, true),
|
|
445
|
+
week,
|
|
446
|
+
// RINEX BDS week field is the BDT week
|
|
447
|
+
svHealth: health,
|
|
448
|
+
tgd: tgd1
|
|
449
|
+
// TGD1 (B1) — RINEX slot
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function decodeQzssEphemeris(view, p) {
|
|
453
|
+
const prn = view.getUint32(p, true);
|
|
454
|
+
if (prn < 193 || prn > 202) return null;
|
|
455
|
+
const health = view.getUint32(p + 12, true) & 63;
|
|
456
|
+
const iode1 = view.getUint32(p + 16, true);
|
|
457
|
+
const week = view.getUint32(p + 24, true);
|
|
458
|
+
const toes = view.getFloat64(p + 32, true);
|
|
459
|
+
const toeMs = gpsMs(week, toes);
|
|
460
|
+
const tocs = view.getFloat64(p + 164, true);
|
|
461
|
+
const tocDate = new Date(nearWeekMs(toeMs, tocs));
|
|
462
|
+
return {
|
|
463
|
+
system: "J",
|
|
464
|
+
prn: `J${String(prn - 192).padStart(2, "0")}`,
|
|
465
|
+
toc: sowOf(tocDate.getTime()),
|
|
466
|
+
tocDate,
|
|
467
|
+
af0: view.getFloat64(p + 180, true),
|
|
468
|
+
af1: view.getFloat64(p + 188, true),
|
|
469
|
+
af2: view.getFloat64(p + 196, true),
|
|
470
|
+
iode: iode1,
|
|
471
|
+
crs: view.getFloat64(p + 104, true),
|
|
472
|
+
deltaN: view.getFloat64(p + 48, true),
|
|
473
|
+
m0: view.getFloat64(p + 56, true),
|
|
474
|
+
cuc: view.getFloat64(p + 80, true),
|
|
475
|
+
e: view.getFloat64(p + 64, true),
|
|
476
|
+
cus: view.getFloat64(p + 88, true),
|
|
477
|
+
sqrtA: Math.sqrt(view.getFloat64(p + 40, true)),
|
|
478
|
+
toe: toes,
|
|
479
|
+
cic: view.getFloat64(p + 112, true),
|
|
480
|
+
omega0: view.getFloat64(p + 144, true),
|
|
481
|
+
cis: view.getFloat64(p + 120, true),
|
|
482
|
+
i0: view.getFloat64(p + 128, true),
|
|
483
|
+
crc: view.getFloat64(p + 96, true),
|
|
484
|
+
omega: view.getFloat64(p + 72, true),
|
|
485
|
+
omegaDot: view.getFloat64(p + 152, true),
|
|
486
|
+
idot: view.getFloat64(p + 136, true),
|
|
487
|
+
week,
|
|
488
|
+
svHealth: health,
|
|
489
|
+
tgd: view.getFloat64(p + 172, true)
|
|
490
|
+
};
|
|
491
|
+
}
|
|
343
492
|
function parseNovatelNav(data) {
|
|
344
493
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
345
494
|
const stats = { badCrc: 0 };
|
|
346
495
|
const ephemerides = [];
|
|
347
|
-
const
|
|
496
|
+
const ionoCorrections = {};
|
|
497
|
+
let leapSeconds = null;
|
|
498
|
+
const lastKep = /* @__PURE__ */ new Map();
|
|
348
499
|
const lastGlo = /* @__PURE__ */ new Map();
|
|
500
|
+
const pushKepler = (key, eph) => {
|
|
501
|
+
const prev = lastKep.get(key);
|
|
502
|
+
if (prev && prev.iode === eph.iode && prev.toe === eph.toe && prev.tocDate.getTime() === eph.tocDate.getTime()) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
lastKep.set(key, eph);
|
|
506
|
+
ephemerides.push(eph);
|
|
507
|
+
};
|
|
349
508
|
for (const frame of oem4Frames(data, view, stats)) {
|
|
350
509
|
if (!frame.binary) continue;
|
|
351
510
|
if (frame.id === ID_RAWEPHEM && frame.msgLen >= 102) {
|
|
352
511
|
const eph = decodeRawEphem(data, view, frame.payload);
|
|
353
|
-
if (
|
|
354
|
-
|
|
355
|
-
if (
|
|
356
|
-
|
|
512
|
+
if (eph) pushKepler(eph.prn, eph);
|
|
513
|
+
} else if (frame.id === ID_GALEPHEMERIS && frame.msgLen >= 220) {
|
|
514
|
+
if (frame.week === 0) continue;
|
|
515
|
+
const headerMs = gpsMs(frame.week, frame.towMs / 1e3);
|
|
516
|
+
const dec = decodeGalEphemeris(view, frame.payload, headerMs);
|
|
517
|
+
if (dec)
|
|
518
|
+
pushKepler(`${dec.eph.prn}:${dec.fnav ? "fnav" : "inav"}`, dec.eph);
|
|
519
|
+
} else if (frame.id === ID_BDSEPHEMERIS && frame.msgLen >= 196) {
|
|
520
|
+
const eph = decodeBdsEphemeris(view, frame.payload);
|
|
521
|
+
if (eph) pushKepler(eph.prn, eph);
|
|
522
|
+
} else if (frame.id === ID_QZSSEPHEMERIS && frame.msgLen >= 204) {
|
|
523
|
+
const eph = decodeQzssEphemeris(view, frame.payload);
|
|
524
|
+
if (eph) pushKepler(eph.prn, eph);
|
|
525
|
+
} else if (frame.id === ID_IONUTC && frame.msgLen >= 108) {
|
|
526
|
+
const p = frame.payload;
|
|
527
|
+
const alpha = [];
|
|
528
|
+
const beta = [];
|
|
529
|
+
for (let i = 0; i < 4; i++) {
|
|
530
|
+
alpha.push(view.getFloat64(p + i * 8, true));
|
|
531
|
+
beta.push(view.getFloat64(p + 32 + i * 8, true));
|
|
357
532
|
}
|
|
358
|
-
|
|
359
|
-
|
|
533
|
+
ionoCorrections["GPSA"] = alpha;
|
|
534
|
+
ionoCorrections["GPSB"] = beta;
|
|
535
|
+
leapSeconds = view.getInt32(p + 96, true);
|
|
360
536
|
} else if (frame.id === ID_GLOEPHEMERIS && frame.msgLen >= 144) {
|
|
361
537
|
const eph = decodeGloEphemeris(view, frame.payload);
|
|
362
538
|
if (!eph) continue;
|
|
@@ -368,12 +544,12 @@ function parseNovatelNav(data) {
|
|
|
368
544
|
ephemerides.push(eph);
|
|
369
545
|
}
|
|
370
546
|
}
|
|
371
|
-
return { ephemerides, badCrc: stats.badCrc };
|
|
547
|
+
return { ephemerides, ionoCorrections, leapSeconds, badCrc: stats.badCrc };
|
|
372
548
|
}
|
|
373
549
|
|
|
374
550
|
// src/novatel/index.ts
|
|
375
551
|
var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
|
|
376
|
-
var
|
|
552
|
+
var MS_PER_WEEK2 = 7 * 864e5;
|
|
377
553
|
var HDR = 28;
|
|
378
554
|
var ID_RANGE = 43;
|
|
379
555
|
var ID_RANGECMP = 140;
|
|
@@ -542,7 +718,7 @@ function parseNovatelRange(data) {
|
|
|
542
718
|
if (!codes.includes(code)) codes.push(code);
|
|
543
719
|
}
|
|
544
720
|
epochs.push({
|
|
545
|
-
timeMs: GPS_EPOCH_MS3 + week *
|
|
721
|
+
timeMs: GPS_EPOCH_MS3 + week * MS_PER_WEEK2 + towMs,
|
|
546
722
|
meas
|
|
547
723
|
});
|
|
548
724
|
}
|
|
@@ -594,7 +770,7 @@ function parseNovatelRange(data) {
|
|
|
594
770
|
if (!codes.includes(code)) codes.push(code);
|
|
595
771
|
}
|
|
596
772
|
epochs.push({
|
|
597
|
-
timeMs: GPS_EPOCH_MS3 + week *
|
|
773
|
+
timeMs: GPS_EPOCH_MS3 + week * MS_PER_WEEK2 + towMs,
|
|
598
774
|
meas
|
|
599
775
|
});
|
|
600
776
|
}
|
package/dist/novatel.d.cts
CHANGED
|
@@ -37,31 +37,54 @@ declare function oem4Frames(data: Uint8Array, view: DataView, stats: {
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* NovAtel navigation-message decoding: RAWEPHEM (message 41, raw GPS
|
|
40
|
-
* LNAV subframes 1–3)
|
|
41
|
-
* L1 C/A ephemeris)
|
|
40
|
+
* LNAV subframes 1–3), GLOEPHEMERIS (message 723, decoded GLONASS
|
|
41
|
+
* L1 C/A ephemeris), GALEPHEMERIS (1122), BDSEPHEMERIS (1696),
|
|
42
|
+
* QZSSEPHEMERIS (1336) and IONUTC (8, GPS Klobuchar + UTC parameters).
|
|
42
43
|
*
|
|
43
44
|
* Ported from RTKLIB demo5 (rtklibexplorer fork, src/rcv/novatel.c:
|
|
44
|
-
* decode_rawephemb / decode_gloephemerisb
|
|
45
|
+
* decode_rawephemb / decode_gloephemerisb / decode_galephemerisb /
|
|
46
|
+
* decode_bdsephemerisb / decode_ionutcb, Copyright (c) 2007-2020
|
|
45
47
|
* T. Takasu, BSD-2-Clause) and cross-checked against the OEM7
|
|
46
|
-
* Commands and Logs Reference Manual.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
48
|
+
* Commands and Logs Reference Manual. QZSSEPHEMERIS has no RTKLIB
|
|
49
|
+
* decoder (RTKLIB only handles the raw-subframe QZSSRAWEPHEM 1331);
|
|
50
|
+
* its layout is taken from the OEM7 manual §3.150 and is therefore
|
|
51
|
+
* synthetic-tested only, like SBF's QZSNav.
|
|
52
|
+
*
|
|
53
|
+
* Output records mirror what `parseNavFile` produces for the
|
|
54
|
+
* equivalent RINEX 3 navigation file: Keplerian `tocDate` is a
|
|
55
|
+
* GPS-scale Date except BeiDou, whose epochs and week stay on the BDT
|
|
56
|
+
* scale like a RINEX file; angles arrive from the receiver already in
|
|
57
|
+
* radians (NovAtel decodes the semicircle SIS fields itself), so no
|
|
58
|
+
* GPS_PI scaling is applied here; the Galileo clock set follows the
|
|
59
|
+
* received data source (F/NAV when the rcv_fnav flag is set, I/NAV
|
|
60
|
+
* otherwise — RTKLIB's convention, which convbin tags with the RINEX
|
|
61
|
+
* I/NAV / F/NAV data-source flags) and `tgd` is BGD E5a/E1, the first
|
|
62
|
+
* RINEX BGD slot; GLONASS `tocDate` is the UTC epoch, state vectors
|
|
63
|
+
* are in km (PZ-90), the RINEX clock bias is −τ_n, and
|
|
64
|
+
* `messageFrameTime` is the frame time as seconds of the UTC week.
|
|
52
65
|
*/
|
|
53
66
|
|
|
54
67
|
interface NovatelNavResult {
|
|
55
68
|
/** Broadcast ephemerides in stream order, duplicates suppressed. */
|
|
56
69
|
ephemerides: Ephemeris[];
|
|
70
|
+
/**
|
|
71
|
+
* Klobuchar coefficients from IONUTC in the RINEX-header shape
|
|
72
|
+
* (`GPSA` = alpha0..3, `GPSB` = beta0..3), last message wins.
|
|
73
|
+
* Empty when no IONUTC message was seen.
|
|
74
|
+
*/
|
|
75
|
+
ionoCorrections: Record<string, number[]>;
|
|
76
|
+
/** GPS−UTC leap seconds (Δt_LS) from IONUTC, null when unseen. */
|
|
77
|
+
leapSeconds: number | null;
|
|
57
78
|
/** Frames whose CRC failed (corruption indicator). */
|
|
58
79
|
badCrc: number;
|
|
59
80
|
}
|
|
60
81
|
/**
|
|
61
|
-
* Decode every navigation message (RAWEPHEM, GLOEPHEMERIS
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
82
|
+
* Decode every navigation message (RAWEPHEM, GLOEPHEMERIS,
|
|
83
|
+
* GALEPHEMERIS, BDSEPHEMERIS, QZSSEPHEMERIS, IONUTC) in a NovAtel
|
|
84
|
+
* binary byte stream. Repeated broadcasts of an unchanged ephemeris
|
|
85
|
+
* are suppressed the same way RTKLIB does: GPS/QZSS by issue of data,
|
|
86
|
+
* Galileo by IODNav + toe + toc per data source, BeiDou by toe + toc,
|
|
87
|
+
* GLONASS by reference time and health.
|
|
65
88
|
*/
|
|
66
89
|
declare function parseNovatelNav(data: Uint8Array): NovatelNavResult;
|
|
67
90
|
|
package/dist/novatel.d.ts
CHANGED
|
@@ -37,31 +37,54 @@ declare function oem4Frames(data: Uint8Array, view: DataView, stats: {
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* NovAtel navigation-message decoding: RAWEPHEM (message 41, raw GPS
|
|
40
|
-
* LNAV subframes 1–3)
|
|
41
|
-
* L1 C/A ephemeris)
|
|
40
|
+
* LNAV subframes 1–3), GLOEPHEMERIS (message 723, decoded GLONASS
|
|
41
|
+
* L1 C/A ephemeris), GALEPHEMERIS (1122), BDSEPHEMERIS (1696),
|
|
42
|
+
* QZSSEPHEMERIS (1336) and IONUTC (8, GPS Klobuchar + UTC parameters).
|
|
42
43
|
*
|
|
43
44
|
* Ported from RTKLIB demo5 (rtklibexplorer fork, src/rcv/novatel.c:
|
|
44
|
-
* decode_rawephemb / decode_gloephemerisb
|
|
45
|
+
* decode_rawephemb / decode_gloephemerisb / decode_galephemerisb /
|
|
46
|
+
* decode_bdsephemerisb / decode_ionutcb, Copyright (c) 2007-2020
|
|
45
47
|
* T. Takasu, BSD-2-Clause) and cross-checked against the OEM7
|
|
46
|
-
* Commands and Logs Reference Manual.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
48
|
+
* Commands and Logs Reference Manual. QZSSEPHEMERIS has no RTKLIB
|
|
49
|
+
* decoder (RTKLIB only handles the raw-subframe QZSSRAWEPHEM 1331);
|
|
50
|
+
* its layout is taken from the OEM7 manual §3.150 and is therefore
|
|
51
|
+
* synthetic-tested only, like SBF's QZSNav.
|
|
52
|
+
*
|
|
53
|
+
* Output records mirror what `parseNavFile` produces for the
|
|
54
|
+
* equivalent RINEX 3 navigation file: Keplerian `tocDate` is a
|
|
55
|
+
* GPS-scale Date except BeiDou, whose epochs and week stay on the BDT
|
|
56
|
+
* scale like a RINEX file; angles arrive from the receiver already in
|
|
57
|
+
* radians (NovAtel decodes the semicircle SIS fields itself), so no
|
|
58
|
+
* GPS_PI scaling is applied here; the Galileo clock set follows the
|
|
59
|
+
* received data source (F/NAV when the rcv_fnav flag is set, I/NAV
|
|
60
|
+
* otherwise — RTKLIB's convention, which convbin tags with the RINEX
|
|
61
|
+
* I/NAV / F/NAV data-source flags) and `tgd` is BGD E5a/E1, the first
|
|
62
|
+
* RINEX BGD slot; GLONASS `tocDate` is the UTC epoch, state vectors
|
|
63
|
+
* are in km (PZ-90), the RINEX clock bias is −τ_n, and
|
|
64
|
+
* `messageFrameTime` is the frame time as seconds of the UTC week.
|
|
52
65
|
*/
|
|
53
66
|
|
|
54
67
|
interface NovatelNavResult {
|
|
55
68
|
/** Broadcast ephemerides in stream order, duplicates suppressed. */
|
|
56
69
|
ephemerides: Ephemeris[];
|
|
70
|
+
/**
|
|
71
|
+
* Klobuchar coefficients from IONUTC in the RINEX-header shape
|
|
72
|
+
* (`GPSA` = alpha0..3, `GPSB` = beta0..3), last message wins.
|
|
73
|
+
* Empty when no IONUTC message was seen.
|
|
74
|
+
*/
|
|
75
|
+
ionoCorrections: Record<string, number[]>;
|
|
76
|
+
/** GPS−UTC leap seconds (Δt_LS) from IONUTC, null when unseen. */
|
|
77
|
+
leapSeconds: number | null;
|
|
57
78
|
/** Frames whose CRC failed (corruption indicator). */
|
|
58
79
|
badCrc: number;
|
|
59
80
|
}
|
|
60
81
|
/**
|
|
61
|
-
* Decode every navigation message (RAWEPHEM, GLOEPHEMERIS
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
82
|
+
* Decode every navigation message (RAWEPHEM, GLOEPHEMERIS,
|
|
83
|
+
* GALEPHEMERIS, BDSEPHEMERIS, QZSSEPHEMERIS, IONUTC) in a NovAtel
|
|
84
|
+
* binary byte stream. Repeated broadcasts of an unchanged ephemeris
|
|
85
|
+
* are suppressed the same way RTKLIB does: GPS/QZSS by issue of data,
|
|
86
|
+
* Galileo by IODNav + toe + toc per data source, BeiDou by toe + toc,
|
|
87
|
+
* GLONASS by reference time and health.
|
|
65
88
|
*/
|
|
66
89
|
declare function parseNovatelNav(data: Uint8Array): NovatelNavResult;
|
|
67
90
|
|
package/dist/novatel.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
oem4Frames,
|
|
5
5
|
parseNovatelNav,
|
|
6
6
|
parseNovatelRange
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-7QR2KPDU.js";
|
|
8
|
+
import "./chunk-REYOYF7O.js";
|
|
9
9
|
import "./chunk-HVXYFUCB.js";
|
|
10
10
|
import "./chunk-LEEU5OIO.js";
|
|
11
11
|
export {
|
package/dist/sbf.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var sbf_exports = {};
|
|
22
22
|
__export(sbf_exports, {
|
|
23
23
|
parseSbfAlmanac: () => parseSbfAlmanac,
|
|
24
|
+
parseSbfIonoUtc: () => parseSbfIonoUtc,
|
|
24
25
|
parseSbfMeas: () => parseSbfMeas,
|
|
25
26
|
parseSbfNav: () => parseSbfNav,
|
|
26
27
|
sbfCrc16: () => crc16,
|
|
@@ -484,6 +485,39 @@ function parseSbfAlmanac(data) {
|
|
|
484
485
|
return { almanacs, badCrc };
|
|
485
486
|
}
|
|
486
487
|
|
|
488
|
+
// src/sbf/iono.ts
|
|
489
|
+
var F4_DNU2 = -2e10;
|
|
490
|
+
function parseSbfIonoUtc(data) {
|
|
491
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
492
|
+
const ionoCorrections = {};
|
|
493
|
+
let leapSeconds = null;
|
|
494
|
+
const f4s = (b, n) => {
|
|
495
|
+
const out = [];
|
|
496
|
+
for (let k = 0; k < n; k++) {
|
|
497
|
+
const v = view.getFloat32(b + 4 * k, true);
|
|
498
|
+
if (v === F4_DNU2) return null;
|
|
499
|
+
out.push(v);
|
|
500
|
+
}
|
|
501
|
+
return out;
|
|
502
|
+
};
|
|
503
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
504
|
+
if ((id === 5893 || id === 4120) && len >= 48) {
|
|
505
|
+
const alpha = f4s(b + 16, 4);
|
|
506
|
+
const beta = f4s(b + 32, 4);
|
|
507
|
+
if (!alpha || !beta) return;
|
|
508
|
+
const sys = id === 5893 ? "GPS" : "BDS";
|
|
509
|
+
ionoCorrections[`${sys}A`] = alpha;
|
|
510
|
+
ionoCorrections[`${sys}B`] = beta;
|
|
511
|
+
} else if (id === 4030 && len >= 28) {
|
|
512
|
+
const ai = f4s(b + 16, 3);
|
|
513
|
+
if (ai) ionoCorrections["GAL"] = ai;
|
|
514
|
+
} else if (id === 5894 && len >= 37) {
|
|
515
|
+
leapSeconds = view.getInt8(b + 33);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
return { ionoCorrections, leapSeconds };
|
|
519
|
+
}
|
|
520
|
+
|
|
487
521
|
// src/sbf/index.ts
|
|
488
522
|
var CLIGHT = 299792458;
|
|
489
523
|
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
@@ -1154,6 +1188,7 @@ function parseSbfMeas(data) {
|
|
|
1154
1188
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1155
1189
|
0 && (module.exports = {
|
|
1156
1190
|
parseSbfAlmanac,
|
|
1191
|
+
parseSbfIonoUtc,
|
|
1157
1192
|
parseSbfMeas,
|
|
1158
1193
|
parseSbfNav,
|
|
1159
1194
|
sbfCrc16,
|
package/dist/sbf.d.cts
CHANGED
|
@@ -151,6 +151,46 @@ interface SbfAlmanacResult {
|
|
|
151
151
|
*/
|
|
152
152
|
declare function parseSbfAlmanac(data: Uint8Array): SbfAlmanacResult;
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Septentrio SBF decoded ionosphere / UTC blocks.
|
|
156
|
+
*
|
|
157
|
+
* GPSIon (5893) and BDSIon (4120) carry the eight Klobuchar vertical
|
|
158
|
+
* delay coefficients, GALIon (4030) the three NeQuick-G effective
|
|
159
|
+
* ionisation level coefficients, and GPSUtc (5894) the GPS-UTC
|
|
160
|
+
* parameters from LNAV subframe 4 page 18 (only ΔtLS, the current
|
|
161
|
+
* leap-second count, is extracted here).
|
|
162
|
+
*
|
|
163
|
+
* Units: SBF stores the coefficients as f4 floats already in the
|
|
164
|
+
* semicircle-based SI units of the broadcast messages — alpha_n in
|
|
165
|
+
* s/semicircle^n, beta_n in s/semicircle^n, ai_n in sfu/deg^n — which
|
|
166
|
+
* are exactly the units a RINEX nav header prints. Values are passed
|
|
167
|
+
* through unscaled, so the output matches what `parseNavFile` reads
|
|
168
|
+
* from a converted nav header to float32 precision.
|
|
169
|
+
*
|
|
170
|
+
* Ported from RTKLIB demo5 (rtklibexplorer), src/rcv/septentrio.c
|
|
171
|
+
* (decode_gpsion / decode_galion / decode_cmpion / decode_gpsutc),
|
|
172
|
+
* BSD-2-Clause, and cross-checked against the Septentrio mosaic-X5
|
|
173
|
+
* reference guide (which documents all four block layouts, including
|
|
174
|
+
* the do-not-use markers RTKLIB does not check).
|
|
175
|
+
*/
|
|
176
|
+
interface SbfIonoUtcResult {
|
|
177
|
+
/**
|
|
178
|
+
* Iono coefficient sets keyed like `NavHeader.ionoCorrections`:
|
|
179
|
+
* `GPSA`/`GPSB` (Klobuchar alpha/beta), `GAL` ([ai0, ai1, ai2],
|
|
180
|
+
* RINEX GAL header convention), `BDSA`/`BDSB`. The blocks repeat;
|
|
181
|
+
* the last valid block of each type in the stream wins.
|
|
182
|
+
*/
|
|
183
|
+
ionoCorrections: Record<string, number[]>;
|
|
184
|
+
/** GPS-UTC ΔtLS from the last GPSUtc block, if any. */
|
|
185
|
+
leapSeconds: number | null;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Decode every GPSIon/GALIon/BDSIon/GPSUtc block in an SBF byte
|
|
189
|
+
* stream. Blocks with do-not-use coefficients are skipped; other block
|
|
190
|
+
* types are skipped silently.
|
|
191
|
+
*/
|
|
192
|
+
declare function parseSbfIonoUtc(data: Uint8Array): SbfIonoUtcResult;
|
|
193
|
+
|
|
154
194
|
/**
|
|
155
195
|
* Septentrio SBF raw-measurement decoding (MeasEpoch + Meas3) — the
|
|
156
196
|
* path from a receiver log to RINEX-grade observables.
|
|
@@ -214,4 +254,4 @@ interface SbfParseResult {
|
|
|
214
254
|
*/
|
|
215
255
|
declare function parseSbfMeas(data: Uint8Array): SbfParseResult;
|
|
216
256
|
|
|
217
|
-
export { type SbfAlmanac, type SbfAlmanacResult, type SbfGlonassAlmanac, type SbfKeplerAlmanac, type SbfMeasEpoch, type SbfMeasurement, type SbfNavResult, type SbfParseResult, parseSbfAlmanac, parseSbfMeas, parseSbfNav, crc16 as sbfCrc16, scanSbfFrames };
|
|
257
|
+
export { type SbfAlmanac, type SbfAlmanacResult, type SbfGlonassAlmanac, type SbfIonoUtcResult, type SbfKeplerAlmanac, type SbfMeasEpoch, type SbfMeasurement, type SbfNavResult, type SbfParseResult, parseSbfAlmanac, parseSbfIonoUtc, parseSbfMeas, parseSbfNav, crc16 as sbfCrc16, scanSbfFrames };
|
package/dist/sbf.d.ts
CHANGED
|
@@ -151,6 +151,46 @@ interface SbfAlmanacResult {
|
|
|
151
151
|
*/
|
|
152
152
|
declare function parseSbfAlmanac(data: Uint8Array): SbfAlmanacResult;
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Septentrio SBF decoded ionosphere / UTC blocks.
|
|
156
|
+
*
|
|
157
|
+
* GPSIon (5893) and BDSIon (4120) carry the eight Klobuchar vertical
|
|
158
|
+
* delay coefficients, GALIon (4030) the three NeQuick-G effective
|
|
159
|
+
* ionisation level coefficients, and GPSUtc (5894) the GPS-UTC
|
|
160
|
+
* parameters from LNAV subframe 4 page 18 (only ΔtLS, the current
|
|
161
|
+
* leap-second count, is extracted here).
|
|
162
|
+
*
|
|
163
|
+
* Units: SBF stores the coefficients as f4 floats already in the
|
|
164
|
+
* semicircle-based SI units of the broadcast messages — alpha_n in
|
|
165
|
+
* s/semicircle^n, beta_n in s/semicircle^n, ai_n in sfu/deg^n — which
|
|
166
|
+
* are exactly the units a RINEX nav header prints. Values are passed
|
|
167
|
+
* through unscaled, so the output matches what `parseNavFile` reads
|
|
168
|
+
* from a converted nav header to float32 precision.
|
|
169
|
+
*
|
|
170
|
+
* Ported from RTKLIB demo5 (rtklibexplorer), src/rcv/septentrio.c
|
|
171
|
+
* (decode_gpsion / decode_galion / decode_cmpion / decode_gpsutc),
|
|
172
|
+
* BSD-2-Clause, and cross-checked against the Septentrio mosaic-X5
|
|
173
|
+
* reference guide (which documents all four block layouts, including
|
|
174
|
+
* the do-not-use markers RTKLIB does not check).
|
|
175
|
+
*/
|
|
176
|
+
interface SbfIonoUtcResult {
|
|
177
|
+
/**
|
|
178
|
+
* Iono coefficient sets keyed like `NavHeader.ionoCorrections`:
|
|
179
|
+
* `GPSA`/`GPSB` (Klobuchar alpha/beta), `GAL` ([ai0, ai1, ai2],
|
|
180
|
+
* RINEX GAL header convention), `BDSA`/`BDSB`. The blocks repeat;
|
|
181
|
+
* the last valid block of each type in the stream wins.
|
|
182
|
+
*/
|
|
183
|
+
ionoCorrections: Record<string, number[]>;
|
|
184
|
+
/** GPS-UTC ΔtLS from the last GPSUtc block, if any. */
|
|
185
|
+
leapSeconds: number | null;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Decode every GPSIon/GALIon/BDSIon/GPSUtc block in an SBF byte
|
|
189
|
+
* stream. Blocks with do-not-use coefficients are skipped; other block
|
|
190
|
+
* types are skipped silently.
|
|
191
|
+
*/
|
|
192
|
+
declare function parseSbfIonoUtc(data: Uint8Array): SbfIonoUtcResult;
|
|
193
|
+
|
|
154
194
|
/**
|
|
155
195
|
* Septentrio SBF raw-measurement decoding (MeasEpoch + Meas3) — the
|
|
156
196
|
* path from a receiver log to RINEX-grade observables.
|
|
@@ -214,4 +254,4 @@ interface SbfParseResult {
|
|
|
214
254
|
*/
|
|
215
255
|
declare function parseSbfMeas(data: Uint8Array): SbfParseResult;
|
|
216
256
|
|
|
217
|
-
export { type SbfAlmanac, type SbfAlmanacResult, type SbfGlonassAlmanac, type SbfKeplerAlmanac, type SbfMeasEpoch, type SbfMeasurement, type SbfNavResult, type SbfParseResult, parseSbfAlmanac, parseSbfMeas, parseSbfNav, crc16 as sbfCrc16, scanSbfFrames };
|
|
257
|
+
export { type SbfAlmanac, type SbfAlmanacResult, type SbfGlonassAlmanac, type SbfIonoUtcResult, type SbfKeplerAlmanac, type SbfMeasEpoch, type SbfMeasurement, type SbfNavResult, type SbfParseResult, parseSbfAlmanac, parseSbfIonoUtc, parseSbfMeas, parseSbfNav, crc16 as sbfCrc16, scanSbfFrames };
|
package/dist/sbf.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
crc16,
|
|
3
3
|
parseSbfAlmanac,
|
|
4
|
+
parseSbfIonoUtc,
|
|
4
5
|
parseSbfMeas,
|
|
5
6
|
parseSbfNav,
|
|
6
7
|
scanSbfFrames
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ZNCQNHNI.js";
|
|
8
9
|
import "./chunk-HVXYFUCB.js";
|
|
9
10
|
import "./chunk-LEEU5OIO.js";
|
|
10
11
|
export {
|
|
11
12
|
parseSbfAlmanac,
|
|
13
|
+
parseSbfIonoUtc,
|
|
12
14
|
parseSbfMeas,
|
|
13
15
|
parseSbfNav,
|
|
14
16
|
crc16 as sbfCrc16,
|