gnss-js 1.36.0 → 1.38.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/binex.js +2 -2
- package/dist/{chunk-AFEDNWXI.js → chunk-4TAN27LO.js} +368 -111
- package/dist/{chunk-F4CM6Q4F.js → chunk-5X4AG4MK.js} +2 -2
- package/dist/{chunk-5RNDQQ6J.js → chunk-6SWJTN6H.js} +2 -2
- package/dist/{chunk-OK4GGNOO.js → chunk-D25WNE43.js} +1 -1
- package/dist/{chunk-5AUK42AL.js → chunk-FA2UTLEQ.js} +2 -2
- package/dist/{chunk-IQ5OIMQD.js → chunk-V2D5KUKH.js} +33 -0
- package/dist/{chunk-726LQBGM.js → chunk-YUW33VXK.js} +1 -1
- package/dist/{chunk-FEOMYZEU.js → chunk-ZQDLYLQA.js} +9 -28
- package/dist/index.cjs +410 -142
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +14 -8
- package/dist/novatel.js +3 -3
- package/dist/rinex.js +3 -3
- package/dist/sbf.cjs +536 -111
- package/dist/sbf.d.cts +169 -14
- package/dist/sbf.d.ts +169 -14
- package/dist/sbf.js +12 -4
- package/dist/ubx.cjs +37 -24
- package/dist/ubx.js +4 -4
- package/package.json +1 -1
package/dist/binex.js
CHANGED
|
@@ -7,16 +7,21 @@ import {
|
|
|
7
7
|
galFnavPageCrcOk,
|
|
8
8
|
galInavPageCrcOk,
|
|
9
9
|
testGloString
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-FA2UTLEQ.js";
|
|
11
|
+
import {
|
|
12
|
+
geodeticToEcef
|
|
13
|
+
} from "./chunk-37QNKGTC.js";
|
|
11
14
|
import {
|
|
12
15
|
CnavAssembler,
|
|
13
16
|
cnavCrcOk,
|
|
14
17
|
crc24q
|
|
15
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-YUW33VXK.js";
|
|
16
19
|
import {
|
|
20
|
+
GpsLnavAssembler,
|
|
17
21
|
getBitS,
|
|
18
|
-
getBitU
|
|
19
|
-
|
|
22
|
+
getBitU,
|
|
23
|
+
setBitU
|
|
24
|
+
} from "./chunk-V2D5KUKH.js";
|
|
20
25
|
import {
|
|
21
26
|
getGpsLeap
|
|
22
27
|
} from "./chunk-HVXYFUCB.js";
|
|
@@ -423,38 +428,132 @@ function parseSbfIonoUtc(data) {
|
|
|
423
428
|
return { ionoCorrections, leapSeconds };
|
|
424
429
|
}
|
|
425
430
|
|
|
431
|
+
// src/sbf/setup.ts
|
|
432
|
+
var F8_DNU = -2e10;
|
|
433
|
+
var F4_DNU3 = -2e10;
|
|
434
|
+
function str(data, off, n) {
|
|
435
|
+
let end = off;
|
|
436
|
+
const stop = off + n;
|
|
437
|
+
while (end < stop && data[end] !== 0) end++;
|
|
438
|
+
let s = "";
|
|
439
|
+
for (let i = off; i < end; i++) s += String.fromCharCode(data[i]);
|
|
440
|
+
return s.trim();
|
|
441
|
+
}
|
|
442
|
+
function parseSbfReceiverSetup(data) {
|
|
443
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
444
|
+
let out = null;
|
|
445
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
446
|
+
if (id !== 5902 || len < 256) return;
|
|
447
|
+
const setup = {
|
|
448
|
+
markerName: str(data, b + 16, 60),
|
|
449
|
+
markerNumber: str(data, b + 76, 20),
|
|
450
|
+
observer: str(data, b + 96, 20),
|
|
451
|
+
agency: str(data, b + 116, 40),
|
|
452
|
+
rxSerialNumber: str(data, b + 156, 20),
|
|
453
|
+
rxName: str(data, b + 176, 20),
|
|
454
|
+
rxVersion: str(data, b + 196, 20),
|
|
455
|
+
antSerialNumber: str(data, b + 216, 20),
|
|
456
|
+
antType: str(data, b + 236, 20),
|
|
457
|
+
deltaH: len >= 268 ? view.getFloat32(b + 256, true) : 0,
|
|
458
|
+
deltaE: len >= 268 ? view.getFloat32(b + 260, true) : 0,
|
|
459
|
+
deltaN: len >= 268 ? view.getFloat32(b + 264, true) : 0,
|
|
460
|
+
markerType: len >= 288 ? str(data, b + 268, 20) : null,
|
|
461
|
+
gnssFwVersion: len >= 328 ? str(data, b + 288, 40) : null,
|
|
462
|
+
productName: len >= 368 ? str(data, b + 328, 40) : null,
|
|
463
|
+
latitude: null,
|
|
464
|
+
longitude: null,
|
|
465
|
+
height: null,
|
|
466
|
+
position: null,
|
|
467
|
+
stationCode: len >= 398 ? str(data, b + 388, 10) : null
|
|
468
|
+
};
|
|
469
|
+
if (len >= 388) {
|
|
470
|
+
const lat = view.getFloat64(b + 368, true);
|
|
471
|
+
const lon = view.getFloat64(b + 376, true);
|
|
472
|
+
const h = view.getFloat32(b + 384, true);
|
|
473
|
+
if (lat !== F8_DNU && lon !== F8_DNU && h !== F4_DNU3) {
|
|
474
|
+
setup.latitude = lat;
|
|
475
|
+
setup.longitude = lon;
|
|
476
|
+
setup.height = h;
|
|
477
|
+
setup.position = geodeticToEcef(lat, lon, h);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
out = setup;
|
|
481
|
+
});
|
|
482
|
+
return out;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// src/sbf/rawnav-gps.ts
|
|
486
|
+
var WNC_DNU = 65535;
|
|
487
|
+
function extractSbfLnavSubframe(view, b) {
|
|
488
|
+
const sf = new Uint8Array(30);
|
|
489
|
+
for (let k = 0; k < 10; k++) {
|
|
490
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
491
|
+
setBitU(sf, 24 * k, 24, w >>> 6 & 16777215);
|
|
492
|
+
}
|
|
493
|
+
return sf;
|
|
494
|
+
}
|
|
495
|
+
function feedGpsLnavBlock(view, b, assembler) {
|
|
496
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
497
|
+
if (!prn || prn[0] !== "G" && prn[0] !== "J") return {};
|
|
498
|
+
if (view.getUint8(b + 15) !== 1) return {};
|
|
499
|
+
const wnc = view.getUint16(b + 12, true);
|
|
500
|
+
if (wnc === WNC_DNU) return {};
|
|
501
|
+
const r = assembler.push(prn, extractSbfLnavSubframe(view, b), wnc);
|
|
502
|
+
if (r.kind === "eph") return { eph: r.eph };
|
|
503
|
+
if (r.kind === "decodeFailed") return { badParity: true };
|
|
504
|
+
return {};
|
|
505
|
+
}
|
|
506
|
+
function parseSbfGpsNav(data) {
|
|
507
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
508
|
+
const ephemerides = [];
|
|
509
|
+
const assembler = new GpsLnavAssembler();
|
|
510
|
+
let badParity = 0;
|
|
511
|
+
let messages = 0;
|
|
512
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
513
|
+
if (id !== 4017 && id !== 4066 || len < 60) return;
|
|
514
|
+
messages++;
|
|
515
|
+
const r = feedGpsLnavBlock(view, b, assembler);
|
|
516
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
517
|
+
else if (r.badParity) badParity++;
|
|
518
|
+
});
|
|
519
|
+
return { ephemerides, badParity, messages };
|
|
520
|
+
}
|
|
521
|
+
|
|
426
522
|
// src/sbf/rawnav.ts
|
|
427
|
-
var
|
|
523
|
+
var CNAV_SIGNAL_OF_BLOCK = {
|
|
428
524
|
4018: "L2C",
|
|
429
525
|
4019: "L5"
|
|
430
526
|
};
|
|
527
|
+
function newCnavAssemblers() {
|
|
528
|
+
return { L2C: new CnavAssembler(), L5: new CnavAssembler() };
|
|
529
|
+
}
|
|
530
|
+
function feedCnavBlock(view, b, id, asm) {
|
|
531
|
+
const signal = CNAV_SIGNAL_OF_BLOCK[id];
|
|
532
|
+
if (!signal) return {};
|
|
533
|
+
const msg = new Uint8Array(40);
|
|
534
|
+
for (let k = 0; k < 10; k++) {
|
|
535
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
536
|
+
msg[4 * k] = w >>> 24;
|
|
537
|
+
msg[4 * k + 1] = w >>> 16 & 255;
|
|
538
|
+
msg[4 * k + 2] = w >>> 8 & 255;
|
|
539
|
+
msg[4 * k + 3] = w & 255;
|
|
540
|
+
}
|
|
541
|
+
if (!cnavCrcOk(msg)) return { badCrc: true };
|
|
542
|
+
const eph = asm[signal].push(msg);
|
|
543
|
+
return eph ? { eph: { ...eph, signal } } : {};
|
|
544
|
+
}
|
|
431
545
|
function parseSbfCnav(data) {
|
|
432
546
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
433
547
|
const ephemerides = [];
|
|
434
|
-
const
|
|
435
|
-
L2C: new CnavAssembler(),
|
|
436
|
-
L5: new CnavAssembler()
|
|
437
|
-
};
|
|
548
|
+
const asm = newCnavAssemblers();
|
|
438
549
|
let badCrc = 0;
|
|
439
550
|
let messages = 0;
|
|
440
551
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
441
|
-
|
|
442
|
-
if (!signal || len < 60) return;
|
|
552
|
+
if (!CNAV_SIGNAL_OF_BLOCK[id] || len < 60) return;
|
|
443
553
|
messages++;
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
msg[4 * k] = w >>> 24;
|
|
448
|
-
msg[4 * k + 1] = w >>> 16 & 255;
|
|
449
|
-
msg[4 * k + 2] = w >>> 8 & 255;
|
|
450
|
-
msg[4 * k + 3] = w & 255;
|
|
451
|
-
}
|
|
452
|
-
if (!cnavCrcOk(msg)) {
|
|
453
|
-
badCrc++;
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
const eph = assemblers[signal].push(msg);
|
|
457
|
-
if (eph) ephemerides.push({ ...eph, signal });
|
|
554
|
+
const r = feedCnavBlock(view, b, id, asm);
|
|
555
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
556
|
+
else if (r.badCrc) badCrc++;
|
|
458
557
|
});
|
|
459
558
|
return { ephemerides, badCrc, messages };
|
|
460
559
|
}
|
|
@@ -462,50 +561,51 @@ function parseSbfCnav(data) {
|
|
|
462
561
|
// src/sbf/rawnav-gal.ts
|
|
463
562
|
var INAV_SOURCES = [17, 21, 22];
|
|
464
563
|
var FNAV_SOURCES = [20, 22];
|
|
564
|
+
function newGalAssemblers() {
|
|
565
|
+
return { inav: new GalInavAssembler(), fnav: new GalFnavAssembler() };
|
|
566
|
+
}
|
|
567
|
+
function feedGalBlock(data, view, b, id, asm) {
|
|
568
|
+
const prnStr = svidToPrn(data[b + 14]);
|
|
569
|
+
if (!prnStr || prnStr[0] !== "E") return {};
|
|
570
|
+
const prn = parseInt(prnStr.slice(1), 10);
|
|
571
|
+
const source = data[b + 17] & 31;
|
|
572
|
+
const page = new Uint8Array(32);
|
|
573
|
+
for (let k = 0; k < 8; k++) {
|
|
574
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
575
|
+
page[4 * k] = w >>> 24;
|
|
576
|
+
page[4 * k + 1] = w >>> 16 & 255;
|
|
577
|
+
page[4 * k + 2] = w >>> 8 & 255;
|
|
578
|
+
page[4 * k + 3] = w & 255;
|
|
579
|
+
}
|
|
580
|
+
let eph;
|
|
581
|
+
let src;
|
|
582
|
+
if (id === 4023) {
|
|
583
|
+
src = "inav";
|
|
584
|
+
if (!INAV_SOURCES.includes(source)) return {};
|
|
585
|
+
if (getBitU2(page, 0) !== 0 || getBitU2(page, 114) !== 1) return {};
|
|
586
|
+
if (!galInavPageCrcOk(page)) return { badCrc: true };
|
|
587
|
+
eph = asm.inav.push(prn, page);
|
|
588
|
+
} else {
|
|
589
|
+
src = "fnav";
|
|
590
|
+
if (!FNAV_SOURCES.includes(source)) return {};
|
|
591
|
+
if (isFnavDummy(page)) return {};
|
|
592
|
+
if (!galFnavPageCrcOk(page)) return { badCrc: true };
|
|
593
|
+
eph = asm.fnav.push(prn, page);
|
|
594
|
+
}
|
|
595
|
+
return eph ? { eph: { ...eph, source: src } } : {};
|
|
596
|
+
}
|
|
465
597
|
function parseSbfGalNav(data) {
|
|
466
598
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
467
599
|
const ephemerides = [];
|
|
468
|
-
const
|
|
469
|
-
const fnav = new GalFnavAssembler();
|
|
600
|
+
const asm = newGalAssemblers();
|
|
470
601
|
let badCrc = 0;
|
|
471
602
|
let messages = 0;
|
|
472
603
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
473
604
|
if (id !== 4022 && id !== 4023 || len < 52) return;
|
|
474
605
|
messages++;
|
|
475
|
-
const
|
|
476
|
-
if (
|
|
477
|
-
|
|
478
|
-
const source = data[b + 17] & 31;
|
|
479
|
-
const page = new Uint8Array(32);
|
|
480
|
-
for (let k = 0; k < 8; k++) {
|
|
481
|
-
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
482
|
-
page[4 * k] = w >>> 24;
|
|
483
|
-
page[4 * k + 1] = w >>> 16 & 255;
|
|
484
|
-
page[4 * k + 2] = w >>> 8 & 255;
|
|
485
|
-
page[4 * k + 3] = w & 255;
|
|
486
|
-
}
|
|
487
|
-
let eph;
|
|
488
|
-
let src;
|
|
489
|
-
if (id === 4023) {
|
|
490
|
-
src = "inav";
|
|
491
|
-
if (!INAV_SOURCES.includes(source)) return;
|
|
492
|
-
if (getBitU2(page, 0) !== 0 || getBitU2(page, 114) !== 1) return;
|
|
493
|
-
if (!galInavPageCrcOk(page)) {
|
|
494
|
-
badCrc++;
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
eph = inav.push(prn, page);
|
|
498
|
-
} else {
|
|
499
|
-
src = "fnav";
|
|
500
|
-
if (!FNAV_SOURCES.includes(source)) return;
|
|
501
|
-
if (isFnavDummy(page)) return;
|
|
502
|
-
if (!galFnavPageCrcOk(page)) {
|
|
503
|
-
badCrc++;
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
eph = fnav.push(prn, page);
|
|
507
|
-
}
|
|
508
|
-
if (eph) ephemerides.push({ ...eph, source: src });
|
|
606
|
+
const r = feedGalBlock(data, view, b, id, asm);
|
|
607
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
608
|
+
else if (r.badCrc) badCrc++;
|
|
509
609
|
});
|
|
510
610
|
return { ephemerides, badCrc, messages };
|
|
511
611
|
}
|
|
@@ -520,7 +620,26 @@ function isFnavDummy(page) {
|
|
|
520
620
|
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
521
621
|
var MS_PER_WEEK2 = 7 * 86400 * 1e3;
|
|
522
622
|
var TOW_DNU = 4294967295;
|
|
523
|
-
var
|
|
623
|
+
var WNC_DNU2 = 65535;
|
|
624
|
+
function feedBdsBlock(view, b, assembler) {
|
|
625
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
626
|
+
if (!prn || prn[0] !== "C") return {};
|
|
627
|
+
const sf = new Uint8Array(38);
|
|
628
|
+
for (let k = 0; k < 10; k++) {
|
|
629
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
630
|
+
sf[4 * k] = w >>> 24;
|
|
631
|
+
if (k < 9) {
|
|
632
|
+
sf[4 * k + 1] = w >>> 16 & 255;
|
|
633
|
+
sf[4 * k + 2] = w >>> 8 & 255;
|
|
634
|
+
sf[4 * k + 3] = w & 255;
|
|
635
|
+
} else {
|
|
636
|
+
sf[37] = w >>> 16 & 240;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
if (!bdsSubframeParityOk(sf)) return { badCrc: true };
|
|
640
|
+
const eph = assembler.push(prn, sf);
|
|
641
|
+
return eph ? { eph } : {};
|
|
642
|
+
}
|
|
524
643
|
function parseSbfBdsNav(data) {
|
|
525
644
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
526
645
|
const ephemerides = [];
|
|
@@ -530,29 +649,41 @@ function parseSbfBdsNav(data) {
|
|
|
530
649
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
531
650
|
if (id !== 4047 || len < 60) return;
|
|
532
651
|
messages++;
|
|
533
|
-
const
|
|
534
|
-
if (
|
|
535
|
-
|
|
536
|
-
for (let k = 0; k < 10; k++) {
|
|
537
|
-
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
538
|
-
sf[4 * k] = w >>> 24;
|
|
539
|
-
if (k < 9) {
|
|
540
|
-
sf[4 * k + 1] = w >>> 16 & 255;
|
|
541
|
-
sf[4 * k + 2] = w >>> 8 & 255;
|
|
542
|
-
sf[4 * k + 3] = w & 255;
|
|
543
|
-
} else {
|
|
544
|
-
sf[37] = w >>> 16 & 240;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
if (!bdsSubframeParityOk(sf)) {
|
|
548
|
-
badCrc++;
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
const eph = assembler.push(prn, sf);
|
|
552
|
-
if (eph) ephemerides.push(eph);
|
|
652
|
+
const r = feedBdsBlock(view, b, assembler);
|
|
653
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
654
|
+
else if (r.badCrc) badCrc++;
|
|
553
655
|
});
|
|
554
656
|
return { ephemerides, badCrc, messages };
|
|
555
657
|
}
|
|
658
|
+
function feedGloBlock(view, b, assembler) {
|
|
659
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
660
|
+
if (!prn || prn[0] !== "R") return {};
|
|
661
|
+
const towMs = view.getUint32(b + 8, true);
|
|
662
|
+
const wnc = view.getUint16(b + 12, true);
|
|
663
|
+
if (towMs === TOW_DNU || wnc === WNC_DNU2) return {};
|
|
664
|
+
const str2 = new Uint8Array(11);
|
|
665
|
+
for (let k = 0; k < 3; k++) {
|
|
666
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
667
|
+
str2[4 * k] = w >>> 24;
|
|
668
|
+
if (k < 2) {
|
|
669
|
+
str2[4 * k + 1] = w >>> 16 & 255;
|
|
670
|
+
str2[4 * k + 2] = w >>> 8 & 255;
|
|
671
|
+
str2[4 * k + 3] = w & 255;
|
|
672
|
+
} else {
|
|
673
|
+
str2[9] = w >>> 16 & 255;
|
|
674
|
+
str2[10] = w >>> 8 & 248;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (!testGloString(str2)) return { badCrc: true };
|
|
678
|
+
const eph = assembler.push(
|
|
679
|
+
prn,
|
|
680
|
+
str2,
|
|
681
|
+
new Date(GPS_EPOCH_MS2 + wnc * MS_PER_WEEK2 + towMs),
|
|
682
|
+
view.getUint8(b + 18) - 8
|
|
683
|
+
// FreqNr, offset by 8
|
|
684
|
+
);
|
|
685
|
+
return eph ? { eph } : {};
|
|
686
|
+
}
|
|
556
687
|
function parseSbfGloNav(data) {
|
|
557
688
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
558
689
|
const ephemerides = [];
|
|
@@ -562,38 +693,161 @@ function parseSbfGloNav(data) {
|
|
|
562
693
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
563
694
|
if (id !== 4026 || len < 32) return;
|
|
564
695
|
messages++;
|
|
565
|
-
const
|
|
566
|
-
if (
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
696
|
+
const r = feedGloBlock(view, b, assembler);
|
|
697
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
698
|
+
else if (r.badCrc) badCrc++;
|
|
699
|
+
});
|
|
700
|
+
return { ephemerides, badCrc, messages };
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// src/sbf/navigation.ts
|
|
704
|
+
var F4_DNU4 = -2e10;
|
|
705
|
+
function ephKey(e) {
|
|
706
|
+
const source = "source" in e ? e.source : "";
|
|
707
|
+
return `${e.prn}|${e.tocDate.getTime()}|${source}`;
|
|
708
|
+
}
|
|
709
|
+
function decodeSbfNavigation(data) {
|
|
710
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
711
|
+
const ephemerides = [];
|
|
712
|
+
const seen = /* @__PURE__ */ new Set();
|
|
713
|
+
const cnav = [];
|
|
714
|
+
const ionoCorrections = {};
|
|
715
|
+
let leapSeconds = null;
|
|
716
|
+
const counts = {
|
|
717
|
+
decodedNav: 0,
|
|
718
|
+
gpsLnavRaw: 0,
|
|
719
|
+
cnavRaw: 0,
|
|
720
|
+
galRaw: 0,
|
|
721
|
+
gloRaw: 0,
|
|
722
|
+
bdsRaw: 0,
|
|
723
|
+
badFrames: 0
|
|
724
|
+
};
|
|
725
|
+
const cnavAsm = newCnavAssemblers();
|
|
726
|
+
const galAsm = newGalAssemblers();
|
|
727
|
+
const gloAsm = new GloStringAssembler();
|
|
728
|
+
const bdsAsm = new BdsAssembler();
|
|
729
|
+
const lnavAsm = new GpsLnavAssembler();
|
|
730
|
+
const addEph = (e) => {
|
|
731
|
+
if (!e) return;
|
|
732
|
+
const key = ephKey(e);
|
|
733
|
+
if (seen.has(key)) return;
|
|
734
|
+
seen.add(key);
|
|
735
|
+
ephemerides.push(e);
|
|
736
|
+
};
|
|
737
|
+
const f4s = (b, n) => {
|
|
738
|
+
const out = [];
|
|
739
|
+
for (let k = 0; k < n; k++) {
|
|
740
|
+
const v = view.getFloat32(b + 4 * k, true);
|
|
741
|
+
if (v === F4_DNU4) return null;
|
|
742
|
+
out.push(v);
|
|
582
743
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
744
|
+
return out;
|
|
745
|
+
};
|
|
746
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
747
|
+
switch (id) {
|
|
748
|
+
/* ---- decoded ephemeris blocks ---- */
|
|
749
|
+
case 5891:
|
|
750
|
+
if (len >= 140) {
|
|
751
|
+
counts.decodedNav++;
|
|
752
|
+
addEph(decodeGpsQzsNav(view, b, "G"));
|
|
753
|
+
}
|
|
754
|
+
return;
|
|
755
|
+
case 4095:
|
|
756
|
+
if (len >= 140) {
|
|
757
|
+
counts.decodedNav++;
|
|
758
|
+
addEph(decodeGpsQzsNav(view, b, "J"));
|
|
759
|
+
}
|
|
760
|
+
return;
|
|
761
|
+
case 4002:
|
|
762
|
+
if (len >= 149) {
|
|
763
|
+
counts.decodedNav++;
|
|
764
|
+
addEph(decodeGalNav(view, b));
|
|
765
|
+
}
|
|
766
|
+
return;
|
|
767
|
+
case 4081:
|
|
768
|
+
if (len >= 140) {
|
|
769
|
+
counts.decodedNav++;
|
|
770
|
+
addEph(decodeBdsNav(view, b));
|
|
771
|
+
}
|
|
772
|
+
return;
|
|
773
|
+
case 4004:
|
|
774
|
+
if (len >= 96) {
|
|
775
|
+
counts.decodedNav++;
|
|
776
|
+
addEph(decodeGloNav(view, b));
|
|
777
|
+
}
|
|
778
|
+
return;
|
|
779
|
+
/* ---- raw navigation-frame blocks ---- */
|
|
780
|
+
case 4017:
|
|
781
|
+
// GPSRawCA
|
|
782
|
+
case 4066: {
|
|
783
|
+
if (len < 60) return;
|
|
784
|
+
counts.gpsLnavRaw++;
|
|
785
|
+
const r = feedGpsLnavBlock(view, b, lnavAsm);
|
|
786
|
+
if (r.eph) addEph(r.eph);
|
|
787
|
+
else if (r.badParity) counts.badFrames++;
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
case 4018:
|
|
791
|
+
// GPSRawL2C
|
|
792
|
+
case 4019: {
|
|
793
|
+
if (len < 60) return;
|
|
794
|
+
counts.cnavRaw++;
|
|
795
|
+
const r = feedCnavBlock(view, b, id, cnavAsm);
|
|
796
|
+
if (r.eph) cnav.push(r.eph);
|
|
797
|
+
else if (r.badCrc) counts.badFrames++;
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
case 4022:
|
|
801
|
+
// GALRawFNAV
|
|
802
|
+
case 4023: {
|
|
803
|
+
if (len < 52) return;
|
|
804
|
+
counts.galRaw++;
|
|
805
|
+
const r = feedGalBlock(data, view, b, id, galAsm);
|
|
806
|
+
if (r.eph) addEph(r.eph);
|
|
807
|
+
else if (r.badCrc) counts.badFrames++;
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
case 4026: {
|
|
811
|
+
if (len < 32) return;
|
|
812
|
+
counts.gloRaw++;
|
|
813
|
+
const r = feedGloBlock(view, b, gloAsm);
|
|
814
|
+
if (r.eph) addEph(r.eph);
|
|
815
|
+
else if (r.badCrc) counts.badFrames++;
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
case 4047: {
|
|
819
|
+
if (len < 60) return;
|
|
820
|
+
counts.bdsRaw++;
|
|
821
|
+
const r = feedBdsBlock(view, b, bdsAsm);
|
|
822
|
+
if (r.eph) addEph(r.eph);
|
|
823
|
+
else if (r.badCrc) counts.badFrames++;
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
/* ---- iono / UTC ---- */
|
|
827
|
+
case 5893:
|
|
828
|
+
// GPSIon
|
|
829
|
+
case 4120: {
|
|
830
|
+
if (len < 48) return;
|
|
831
|
+
const alpha = f4s(b + 16, 4);
|
|
832
|
+
const beta = f4s(b + 32, 4);
|
|
833
|
+
if (!alpha || !beta) return;
|
|
834
|
+
const sys = id === 5893 ? "GPS" : "BDS";
|
|
835
|
+
ionoCorrections[`${sys}A`] = alpha;
|
|
836
|
+
ionoCorrections[`${sys}B`] = beta;
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
case 4030: {
|
|
840
|
+
if (len < 28) return;
|
|
841
|
+
const ai = f4s(b + 16, 3);
|
|
842
|
+
if (ai) ionoCorrections["GAL"] = ai;
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
case 5894:
|
|
846
|
+
if (len >= 37) leapSeconds = view.getInt8(b + 33);
|
|
847
|
+
return;
|
|
586
848
|
}
|
|
587
|
-
const eph = assembler.push(
|
|
588
|
-
prn,
|
|
589
|
-
str,
|
|
590
|
-
new Date(GPS_EPOCH_MS2 + wnc * MS_PER_WEEK2 + towMs),
|
|
591
|
-
view.getUint8(b + 18) - 8
|
|
592
|
-
// FreqNr, offset by 8
|
|
593
|
-
);
|
|
594
|
-
if (eph) ephemerides.push(eph);
|
|
595
849
|
});
|
|
596
|
-
return { ephemerides,
|
|
850
|
+
return { ephemerides, cnav, ionoCorrections, leapSeconds, counts };
|
|
597
851
|
}
|
|
598
852
|
|
|
599
853
|
// src/navbits/rs255.ts
|
|
@@ -1793,10 +2047,13 @@ export {
|
|
|
1793
2047
|
parseSbfNav,
|
|
1794
2048
|
parseSbfAlmanac,
|
|
1795
2049
|
parseSbfIonoUtc,
|
|
2050
|
+
parseSbfReceiverSetup,
|
|
2051
|
+
parseSbfGpsNav,
|
|
1796
2052
|
parseSbfCnav,
|
|
1797
2053
|
parseSbfGalNav,
|
|
1798
2054
|
parseSbfBdsNav,
|
|
1799
2055
|
parseSbfGloNav,
|
|
2056
|
+
decodeSbfNavigation,
|
|
1800
2057
|
parseSbfHas,
|
|
1801
2058
|
parseSbfMeas
|
|
1802
2059
|
};
|
|
@@ -25,6 +25,38 @@ function setBitU(buff, pos, len, data) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
var GpsLnavAssembler = class _GpsLnavAssembler {
|
|
29
|
+
buffers = /* @__PURE__ */ new Map();
|
|
30
|
+
last = /* @__PURE__ */ new Map();
|
|
31
|
+
/** Subframe ID (1–5) from the HOW word of a parity-stripped subframe. */
|
|
32
|
+
static subframeId(subframe) {
|
|
33
|
+
return getBitU(subframe, 43, 3);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Push one parity-stripped 30-byte LNAV subframe for `prn`, resolving
|
|
37
|
+
* the 10-bit week against `refWeek`. See {@link LnavPush} for outcomes.
|
|
38
|
+
*/
|
|
39
|
+
push(prn, subframe, refWeek) {
|
|
40
|
+
const id = _GpsLnavAssembler.subframeId(subframe);
|
|
41
|
+
if (id < 1 || id > 3) return { kind: "skipped" };
|
|
42
|
+
let sf = this.buffers.get(prn);
|
|
43
|
+
if (!sf) {
|
|
44
|
+
sf = { buf: new Uint8Array(90), have: 0 };
|
|
45
|
+
this.buffers.set(prn, sf);
|
|
46
|
+
}
|
|
47
|
+
sf.buf.set(subframe, (id - 1) * 30);
|
|
48
|
+
sf.have |= 1 << id - 1;
|
|
49
|
+
if (id !== 3 || sf.have !== 7) return { kind: "incomplete" };
|
|
50
|
+
const eph = decodeGpsLnavFrame(sf.buf, { prn, refWeek });
|
|
51
|
+
if (!eph) return { kind: "decodeFailed" };
|
|
52
|
+
const prev = this.last.get(prn);
|
|
53
|
+
if (prev && prev.iode === eph.iode && prev.tocDate.getTime() === eph.tocDate.getTime()) {
|
|
54
|
+
return { kind: "duplicate" };
|
|
55
|
+
}
|
|
56
|
+
this.last.set(prn, eph);
|
|
57
|
+
return { kind: "eph", eph };
|
|
58
|
+
}
|
|
59
|
+
};
|
|
28
60
|
function decodeGpsLnavFrame(subframes, opts = {}) {
|
|
29
61
|
if (subframes.length < 90) return null;
|
|
30
62
|
const b = subframes;
|
|
@@ -144,5 +176,6 @@ export {
|
|
|
144
176
|
getBitU,
|
|
145
177
|
getBitS,
|
|
146
178
|
setBitU,
|
|
179
|
+
GpsLnavAssembler,
|
|
147
180
|
decodeGpsLnavFrame
|
|
148
181
|
};
|