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/sbf.cjs
CHANGED
|
@@ -20,15 +20,18 @@ 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
|
+
decodeSbfNavigation: () => decodeSbfNavigation,
|
|
23
24
|
parseSbfAlmanac: () => parseSbfAlmanac,
|
|
24
25
|
parseSbfBdsNav: () => parseSbfBdsNav,
|
|
25
26
|
parseSbfCnav: () => parseSbfCnav,
|
|
26
27
|
parseSbfGalNav: () => parseSbfGalNav,
|
|
27
28
|
parseSbfGloNav: () => parseSbfGloNav,
|
|
29
|
+
parseSbfGpsNav: () => parseSbfGpsNav,
|
|
28
30
|
parseSbfHas: () => parseSbfHas,
|
|
29
31
|
parseSbfIonoUtc: () => parseSbfIonoUtc,
|
|
30
32
|
parseSbfMeas: () => parseSbfMeas,
|
|
31
33
|
parseSbfNav: () => parseSbfNav,
|
|
34
|
+
parseSbfReceiverSetup: () => parseSbfReceiverSetup,
|
|
32
35
|
sbfCrc16: () => crc16,
|
|
33
36
|
scanSbfFrames: () => scanSbfFrames
|
|
34
37
|
});
|
|
@@ -528,6 +531,74 @@ function parseSbfIonoUtc(data) {
|
|
|
528
531
|
return { ionoCorrections, leapSeconds };
|
|
529
532
|
}
|
|
530
533
|
|
|
534
|
+
// src/constants/wgs84.ts
|
|
535
|
+
var WGS84_SEMI_MAJOR_AXIS = 6378137;
|
|
536
|
+
var WGS84_FLATTENING = 1 / 298.257223563;
|
|
537
|
+
var WGS84_ECCENTRICITY_SQUARED = 0.006694379990197;
|
|
538
|
+
|
|
539
|
+
// src/coordinates/ecef.ts
|
|
540
|
+
function geodeticToEcef(lat, lon, h) {
|
|
541
|
+
const N = WGS84_SEMI_MAJOR_AXIS / Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED * Math.sin(lat) ** 2);
|
|
542
|
+
const x = (N + h) * Math.cos(lat) * Math.cos(lon);
|
|
543
|
+
const y = (N + h) * Math.cos(lat) * Math.sin(lon);
|
|
544
|
+
const z = ((1 - WGS84_ECCENTRICITY_SQUARED) * N + h) * Math.sin(lat);
|
|
545
|
+
return [x, y, z];
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/sbf/setup.ts
|
|
549
|
+
var F8_DNU = -2e10;
|
|
550
|
+
var F4_DNU3 = -2e10;
|
|
551
|
+
function str(data, off, n) {
|
|
552
|
+
let end = off;
|
|
553
|
+
const stop = off + n;
|
|
554
|
+
while (end < stop && data[end] !== 0) end++;
|
|
555
|
+
let s = "";
|
|
556
|
+
for (let i = off; i < end; i++) s += String.fromCharCode(data[i]);
|
|
557
|
+
return s.trim();
|
|
558
|
+
}
|
|
559
|
+
function parseSbfReceiverSetup(data) {
|
|
560
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
561
|
+
let out = null;
|
|
562
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
563
|
+
if (id !== 5902 || len < 256) return;
|
|
564
|
+
const setup = {
|
|
565
|
+
markerName: str(data, b + 16, 60),
|
|
566
|
+
markerNumber: str(data, b + 76, 20),
|
|
567
|
+
observer: str(data, b + 96, 20),
|
|
568
|
+
agency: str(data, b + 116, 40),
|
|
569
|
+
rxSerialNumber: str(data, b + 156, 20),
|
|
570
|
+
rxName: str(data, b + 176, 20),
|
|
571
|
+
rxVersion: str(data, b + 196, 20),
|
|
572
|
+
antSerialNumber: str(data, b + 216, 20),
|
|
573
|
+
antType: str(data, b + 236, 20),
|
|
574
|
+
deltaH: len >= 268 ? view.getFloat32(b + 256, true) : 0,
|
|
575
|
+
deltaE: len >= 268 ? view.getFloat32(b + 260, true) : 0,
|
|
576
|
+
deltaN: len >= 268 ? view.getFloat32(b + 264, true) : 0,
|
|
577
|
+
markerType: len >= 288 ? str(data, b + 268, 20) : null,
|
|
578
|
+
gnssFwVersion: len >= 328 ? str(data, b + 288, 40) : null,
|
|
579
|
+
productName: len >= 368 ? str(data, b + 328, 40) : null,
|
|
580
|
+
latitude: null,
|
|
581
|
+
longitude: null,
|
|
582
|
+
height: null,
|
|
583
|
+
position: null,
|
|
584
|
+
stationCode: len >= 398 ? str(data, b + 388, 10) : null
|
|
585
|
+
};
|
|
586
|
+
if (len >= 388) {
|
|
587
|
+
const lat = view.getFloat64(b + 368, true);
|
|
588
|
+
const lon = view.getFloat64(b + 376, true);
|
|
589
|
+
const h = view.getFloat32(b + 384, true);
|
|
590
|
+
if (lat !== F8_DNU && lon !== F8_DNU && h !== F4_DNU3) {
|
|
591
|
+
setup.latitude = lat;
|
|
592
|
+
setup.longitude = lon;
|
|
593
|
+
setup.height = h;
|
|
594
|
+
setup.position = geodeticToEcef(lat, lon, h);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
out = setup;
|
|
598
|
+
});
|
|
599
|
+
return out;
|
|
600
|
+
}
|
|
601
|
+
|
|
531
602
|
// src/navbits/index.ts
|
|
532
603
|
var GPS_PI = 3.1415926535898;
|
|
533
604
|
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
@@ -544,6 +615,199 @@ function getBitS(buff, pos, len) {
|
|
|
544
615
|
if (len <= 0 || bits < 2 ** (len - 1)) return bits;
|
|
545
616
|
return bits - 2 ** len;
|
|
546
617
|
}
|
|
618
|
+
function setBitU(buff, pos, len, data) {
|
|
619
|
+
let mask = 2 ** (len - 1);
|
|
620
|
+
for (let i = pos; i < pos + len; i++, mask /= 2) {
|
|
621
|
+
if (data >= mask) {
|
|
622
|
+
buff[i >> 3] |= 1 << 7 - (i & 7);
|
|
623
|
+
data -= mask;
|
|
624
|
+
} else {
|
|
625
|
+
buff[i >> 3] &= ~(1 << 7 - (i & 7));
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
var GpsLnavAssembler = class _GpsLnavAssembler {
|
|
630
|
+
buffers = /* @__PURE__ */ new Map();
|
|
631
|
+
last = /* @__PURE__ */ new Map();
|
|
632
|
+
/** Subframe ID (1–5) from the HOW word of a parity-stripped subframe. */
|
|
633
|
+
static subframeId(subframe) {
|
|
634
|
+
return getBitU(subframe, 43, 3);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Push one parity-stripped 30-byte LNAV subframe for `prn`, resolving
|
|
638
|
+
* the 10-bit week against `refWeek`. See {@link LnavPush} for outcomes.
|
|
639
|
+
*/
|
|
640
|
+
push(prn, subframe, refWeek) {
|
|
641
|
+
const id = _GpsLnavAssembler.subframeId(subframe);
|
|
642
|
+
if (id < 1 || id > 3) return { kind: "skipped" };
|
|
643
|
+
let sf = this.buffers.get(prn);
|
|
644
|
+
if (!sf) {
|
|
645
|
+
sf = { buf: new Uint8Array(90), have: 0 };
|
|
646
|
+
this.buffers.set(prn, sf);
|
|
647
|
+
}
|
|
648
|
+
sf.buf.set(subframe, (id - 1) * 30);
|
|
649
|
+
sf.have |= 1 << id - 1;
|
|
650
|
+
if (id !== 3 || sf.have !== 7) return { kind: "incomplete" };
|
|
651
|
+
const eph = decodeGpsLnavFrame(sf.buf, { prn, refWeek });
|
|
652
|
+
if (!eph) return { kind: "decodeFailed" };
|
|
653
|
+
const prev = this.last.get(prn);
|
|
654
|
+
if (prev && prev.iode === eph.iode && prev.tocDate.getTime() === eph.tocDate.getTime()) {
|
|
655
|
+
return { kind: "duplicate" };
|
|
656
|
+
}
|
|
657
|
+
this.last.set(prn, eph);
|
|
658
|
+
return { kind: "eph", eph };
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
function decodeGpsLnavFrame(subframes, opts = {}) {
|
|
662
|
+
if (subframes.length < 90) return null;
|
|
663
|
+
const b = subframes;
|
|
664
|
+
let i = 24;
|
|
665
|
+
const tow1 = getBitU(b, i, 17) * 6;
|
|
666
|
+
i += 17 + 2;
|
|
667
|
+
const id1 = getBitU(b, i, 3);
|
|
668
|
+
i += 3 + 2;
|
|
669
|
+
const week10 = getBitU(b, i, 10);
|
|
670
|
+
i += 10;
|
|
671
|
+
i += 2;
|
|
672
|
+
i += 4;
|
|
673
|
+
const svHealth = getBitU(b, i, 6);
|
|
674
|
+
i += 6;
|
|
675
|
+
const iodc0 = getBitU(b, i, 2);
|
|
676
|
+
i += 2;
|
|
677
|
+
i += 1 + 87;
|
|
678
|
+
const tgdRaw = getBitS(b, i, 8);
|
|
679
|
+
i += 8;
|
|
680
|
+
const iodc1 = getBitU(b, i, 8);
|
|
681
|
+
i += 8;
|
|
682
|
+
const tocSec = getBitU(b, i, 16) * 16;
|
|
683
|
+
i += 16;
|
|
684
|
+
const af2 = getBitS(b, i, 8) * 2 ** -55;
|
|
685
|
+
i += 8;
|
|
686
|
+
const af1 = getBitS(b, i, 16) * 2 ** -43;
|
|
687
|
+
i += 16;
|
|
688
|
+
const af0 = getBitS(b, i, 22) * 2 ** -31;
|
|
689
|
+
i = 240 + 24;
|
|
690
|
+
i += 17 + 2;
|
|
691
|
+
const id2 = getBitU(b, i, 3);
|
|
692
|
+
i += 3 + 2;
|
|
693
|
+
const iode = getBitU(b, i, 8);
|
|
694
|
+
i += 8;
|
|
695
|
+
const crs = getBitS(b, i, 16) * 2 ** -5;
|
|
696
|
+
i += 16;
|
|
697
|
+
const deltaN = getBitS(b, i, 16) * 2 ** -43 * GPS_PI;
|
|
698
|
+
i += 16;
|
|
699
|
+
const m0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
700
|
+
i += 32;
|
|
701
|
+
const cuc = getBitS(b, i, 16) * 2 ** -29;
|
|
702
|
+
i += 16;
|
|
703
|
+
const e = getBitU(b, i, 32) * 2 ** -33;
|
|
704
|
+
i += 32;
|
|
705
|
+
const cus = getBitS(b, i, 16) * 2 ** -29;
|
|
706
|
+
i += 16;
|
|
707
|
+
const sqrtA = getBitU(b, i, 32) * 2 ** -19;
|
|
708
|
+
i += 32;
|
|
709
|
+
const toes = getBitU(b, i, 16) * 16;
|
|
710
|
+
i = 480 + 24;
|
|
711
|
+
i += 17 + 2;
|
|
712
|
+
const id3 = getBitU(b, i, 3);
|
|
713
|
+
i += 3 + 2;
|
|
714
|
+
const cic = getBitS(b, i, 16) * 2 ** -29;
|
|
715
|
+
i += 16;
|
|
716
|
+
const omega0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
717
|
+
i += 32;
|
|
718
|
+
const cis = getBitS(b, i, 16) * 2 ** -29;
|
|
719
|
+
i += 16;
|
|
720
|
+
const i0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
721
|
+
i += 32;
|
|
722
|
+
const crc = getBitS(b, i, 16) * 2 ** -5;
|
|
723
|
+
i += 16;
|
|
724
|
+
const omega = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
725
|
+
i += 32;
|
|
726
|
+
const omegaDot = getBitS(b, i, 24) * 2 ** -43 * GPS_PI;
|
|
727
|
+
i += 24;
|
|
728
|
+
const iode3 = getBitU(b, i, 8);
|
|
729
|
+
i += 8;
|
|
730
|
+
const idot = getBitS(b, i, 14) * 2 ** -43 * GPS_PI;
|
|
731
|
+
const iodc = iodc0 * 256 + iodc1;
|
|
732
|
+
if (id1 !== 1 || id2 !== 2 || id3 !== 3) return null;
|
|
733
|
+
if (iode3 !== iode || iode !== (iodc & 255)) return null;
|
|
734
|
+
const ref = opts.refWeek ?? Math.floor((Date.now() - GPS_EPOCH_MS2) / 1e3 / SEC_PER_WEEK2);
|
|
735
|
+
let week = week10 + 1024 * Math.round((ref - week10) / 1024);
|
|
736
|
+
if (toes < tow1 - 302400) week++;
|
|
737
|
+
else if (toes > tow1 + 302400) week--;
|
|
738
|
+
const prn = opts.prn ?? "G00";
|
|
739
|
+
const tocDate = new Date(
|
|
740
|
+
GPS_EPOCH_MS2 + (week * SEC_PER_WEEK2 + tocSec) * 1e3
|
|
741
|
+
);
|
|
742
|
+
return {
|
|
743
|
+
system: prn[0] === "J" ? "J" : "G",
|
|
744
|
+
prn,
|
|
745
|
+
tocDate,
|
|
746
|
+
// Same seconds-of-week convention as parseNavFile (rinex/nav.ts).
|
|
747
|
+
toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK2,
|
|
748
|
+
af0,
|
|
749
|
+
af1,
|
|
750
|
+
af2,
|
|
751
|
+
iode,
|
|
752
|
+
crs,
|
|
753
|
+
deltaN,
|
|
754
|
+
m0,
|
|
755
|
+
cuc,
|
|
756
|
+
e,
|
|
757
|
+
cus,
|
|
758
|
+
sqrtA,
|
|
759
|
+
toe: toes,
|
|
760
|
+
cic,
|
|
761
|
+
omega0,
|
|
762
|
+
cis,
|
|
763
|
+
i0,
|
|
764
|
+
crc,
|
|
765
|
+
omega,
|
|
766
|
+
omegaDot,
|
|
767
|
+
idot,
|
|
768
|
+
week,
|
|
769
|
+
svHealth,
|
|
770
|
+
tgd: tgdRaw === -128 ? 0 : tgdRaw * 2 ** -31
|
|
771
|
+
// IS-GPS-200: -128 reserved
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// src/sbf/rawnav-gps.ts
|
|
776
|
+
var WNC_DNU = 65535;
|
|
777
|
+
function extractSbfLnavSubframe(view, b) {
|
|
778
|
+
const sf = new Uint8Array(30);
|
|
779
|
+
for (let k = 0; k < 10; k++) {
|
|
780
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
781
|
+
setBitU(sf, 24 * k, 24, w >>> 6 & 16777215);
|
|
782
|
+
}
|
|
783
|
+
return sf;
|
|
784
|
+
}
|
|
785
|
+
function feedGpsLnavBlock(view, b, assembler) {
|
|
786
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
787
|
+
if (!prn || prn[0] !== "G" && prn[0] !== "J") return {};
|
|
788
|
+
if (view.getUint8(b + 15) !== 1) return {};
|
|
789
|
+
const wnc = view.getUint16(b + 12, true);
|
|
790
|
+
if (wnc === WNC_DNU) return {};
|
|
791
|
+
const r = assembler.push(prn, extractSbfLnavSubframe(view, b), wnc);
|
|
792
|
+
if (r.kind === "eph") return { eph: r.eph };
|
|
793
|
+
if (r.kind === "decodeFailed") return { badParity: true };
|
|
794
|
+
return {};
|
|
795
|
+
}
|
|
796
|
+
function parseSbfGpsNav(data) {
|
|
797
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
798
|
+
const ephemerides = [];
|
|
799
|
+
const assembler = new GpsLnavAssembler();
|
|
800
|
+
let badParity = 0;
|
|
801
|
+
let messages = 0;
|
|
802
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
803
|
+
if (id !== 4017 && id !== 4066 || len < 60) return;
|
|
804
|
+
messages++;
|
|
805
|
+
const r = feedGpsLnavBlock(view, b, assembler);
|
|
806
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
807
|
+
else if (r.badParity) badParity++;
|
|
808
|
+
});
|
|
809
|
+
return { ephemerides, badParity, messages };
|
|
810
|
+
}
|
|
547
811
|
|
|
548
812
|
// src/navbits/cnav.ts
|
|
549
813
|
var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
|
|
@@ -742,37 +1006,40 @@ var CnavAssembler = class {
|
|
|
742
1006
|
};
|
|
743
1007
|
|
|
744
1008
|
// src/sbf/rawnav.ts
|
|
745
|
-
var
|
|
1009
|
+
var CNAV_SIGNAL_OF_BLOCK = {
|
|
746
1010
|
4018: "L2C",
|
|
747
1011
|
4019: "L5"
|
|
748
1012
|
};
|
|
1013
|
+
function newCnavAssemblers() {
|
|
1014
|
+
return { L2C: new CnavAssembler(), L5: new CnavAssembler() };
|
|
1015
|
+
}
|
|
1016
|
+
function feedCnavBlock(view, b, id, asm) {
|
|
1017
|
+
const signal = CNAV_SIGNAL_OF_BLOCK[id];
|
|
1018
|
+
if (!signal) return {};
|
|
1019
|
+
const msg = new Uint8Array(40);
|
|
1020
|
+
for (let k = 0; k < 10; k++) {
|
|
1021
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
1022
|
+
msg[4 * k] = w >>> 24;
|
|
1023
|
+
msg[4 * k + 1] = w >>> 16 & 255;
|
|
1024
|
+
msg[4 * k + 2] = w >>> 8 & 255;
|
|
1025
|
+
msg[4 * k + 3] = w & 255;
|
|
1026
|
+
}
|
|
1027
|
+
if (!cnavCrcOk(msg)) return { badCrc: true };
|
|
1028
|
+
const eph = asm[signal].push(msg);
|
|
1029
|
+
return eph ? { eph: { ...eph, signal } } : {};
|
|
1030
|
+
}
|
|
749
1031
|
function parseSbfCnav(data) {
|
|
750
1032
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
751
1033
|
const ephemerides = [];
|
|
752
|
-
const
|
|
753
|
-
L2C: new CnavAssembler(),
|
|
754
|
-
L5: new CnavAssembler()
|
|
755
|
-
};
|
|
1034
|
+
const asm = newCnavAssemblers();
|
|
756
1035
|
let badCrc = 0;
|
|
757
1036
|
let messages = 0;
|
|
758
1037
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
759
|
-
|
|
760
|
-
if (!signal || len < 60) return;
|
|
1038
|
+
if (!CNAV_SIGNAL_OF_BLOCK[id] || len < 60) return;
|
|
761
1039
|
messages++;
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
msg[4 * k] = w >>> 24;
|
|
766
|
-
msg[4 * k + 1] = w >>> 16 & 255;
|
|
767
|
-
msg[4 * k + 2] = w >>> 8 & 255;
|
|
768
|
-
msg[4 * k + 3] = w & 255;
|
|
769
|
-
}
|
|
770
|
-
if (!cnavCrcOk(msg)) {
|
|
771
|
-
badCrc++;
|
|
772
|
-
return;
|
|
773
|
-
}
|
|
774
|
-
const eph = assemblers[signal].push(msg);
|
|
775
|
-
if (eph) ephemerides.push({ ...eph, signal });
|
|
1040
|
+
const r = feedCnavBlock(view, b, id, asm);
|
|
1041
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
1042
|
+
else if (r.badCrc) badCrc++;
|
|
776
1043
|
});
|
|
777
1044
|
return { ephemerides, badCrc, messages };
|
|
778
1045
|
}
|
|
@@ -1088,50 +1355,51 @@ var GalFnavAssembler = class {
|
|
|
1088
1355
|
// src/sbf/rawnav-gal.ts
|
|
1089
1356
|
var INAV_SOURCES = [17, 21, 22];
|
|
1090
1357
|
var FNAV_SOURCES = [20, 22];
|
|
1358
|
+
function newGalAssemblers() {
|
|
1359
|
+
return { inav: new GalInavAssembler(), fnav: new GalFnavAssembler() };
|
|
1360
|
+
}
|
|
1361
|
+
function feedGalBlock(data, view, b, id, asm) {
|
|
1362
|
+
const prnStr = svidToPrn(data[b + 14]);
|
|
1363
|
+
if (!prnStr || prnStr[0] !== "E") return {};
|
|
1364
|
+
const prn = parseInt(prnStr.slice(1), 10);
|
|
1365
|
+
const source = data[b + 17] & 31;
|
|
1366
|
+
const page = new Uint8Array(32);
|
|
1367
|
+
for (let k = 0; k < 8; k++) {
|
|
1368
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
1369
|
+
page[4 * k] = w >>> 24;
|
|
1370
|
+
page[4 * k + 1] = w >>> 16 & 255;
|
|
1371
|
+
page[4 * k + 2] = w >>> 8 & 255;
|
|
1372
|
+
page[4 * k + 3] = w & 255;
|
|
1373
|
+
}
|
|
1374
|
+
let eph;
|
|
1375
|
+
let src;
|
|
1376
|
+
if (id === 4023) {
|
|
1377
|
+
src = "inav";
|
|
1378
|
+
if (!INAV_SOURCES.includes(source)) return {};
|
|
1379
|
+
if (getBitU2(page, 0) !== 0 || getBitU2(page, 114) !== 1) return {};
|
|
1380
|
+
if (!galInavPageCrcOk(page)) return { badCrc: true };
|
|
1381
|
+
eph = asm.inav.push(prn, page);
|
|
1382
|
+
} else {
|
|
1383
|
+
src = "fnav";
|
|
1384
|
+
if (!FNAV_SOURCES.includes(source)) return {};
|
|
1385
|
+
if (isFnavDummy(page)) return {};
|
|
1386
|
+
if (!galFnavPageCrcOk(page)) return { badCrc: true };
|
|
1387
|
+
eph = asm.fnav.push(prn, page);
|
|
1388
|
+
}
|
|
1389
|
+
return eph ? { eph: { ...eph, source: src } } : {};
|
|
1390
|
+
}
|
|
1091
1391
|
function parseSbfGalNav(data) {
|
|
1092
1392
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
1093
1393
|
const ephemerides = [];
|
|
1094
|
-
const
|
|
1095
|
-
const fnav = new GalFnavAssembler();
|
|
1394
|
+
const asm = newGalAssemblers();
|
|
1096
1395
|
let badCrc = 0;
|
|
1097
1396
|
let messages = 0;
|
|
1098
1397
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
1099
1398
|
if (id !== 4022 && id !== 4023 || len < 52) return;
|
|
1100
1399
|
messages++;
|
|
1101
|
-
const
|
|
1102
|
-
if (
|
|
1103
|
-
|
|
1104
|
-
const source = data[b + 17] & 31;
|
|
1105
|
-
const page = new Uint8Array(32);
|
|
1106
|
-
for (let k = 0; k < 8; k++) {
|
|
1107
|
-
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
1108
|
-
page[4 * k] = w >>> 24;
|
|
1109
|
-
page[4 * k + 1] = w >>> 16 & 255;
|
|
1110
|
-
page[4 * k + 2] = w >>> 8 & 255;
|
|
1111
|
-
page[4 * k + 3] = w & 255;
|
|
1112
|
-
}
|
|
1113
|
-
let eph;
|
|
1114
|
-
let src;
|
|
1115
|
-
if (id === 4023) {
|
|
1116
|
-
src = "inav";
|
|
1117
|
-
if (!INAV_SOURCES.includes(source)) return;
|
|
1118
|
-
if (getBitU2(page, 0) !== 0 || getBitU2(page, 114) !== 1) return;
|
|
1119
|
-
if (!galInavPageCrcOk(page)) {
|
|
1120
|
-
badCrc++;
|
|
1121
|
-
return;
|
|
1122
|
-
}
|
|
1123
|
-
eph = inav.push(prn, page);
|
|
1124
|
-
} else {
|
|
1125
|
-
src = "fnav";
|
|
1126
|
-
if (!FNAV_SOURCES.includes(source)) return;
|
|
1127
|
-
if (isFnavDummy(page)) return;
|
|
1128
|
-
if (!galFnavPageCrcOk(page)) {
|
|
1129
|
-
badCrc++;
|
|
1130
|
-
return;
|
|
1131
|
-
}
|
|
1132
|
-
eph = fnav.push(prn, page);
|
|
1133
|
-
}
|
|
1134
|
-
if (eph) ephemerides.push({ ...eph, source: src });
|
|
1400
|
+
const r = feedGalBlock(data, view, b, id, asm);
|
|
1401
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
1402
|
+
else if (r.badCrc) badCrc++;
|
|
1135
1403
|
});
|
|
1136
1404
|
return { ephemerides, badCrc, messages };
|
|
1137
1405
|
}
|
|
@@ -1558,9 +1826,9 @@ var GloStringAssembler = class {
|
|
|
1558
1826
|
* Returns the newly completed ephemeris, or null. The decoded
|
|
1559
1827
|
* string-4 slot number must match `prn`, or nothing is emitted.
|
|
1560
1828
|
*/
|
|
1561
|
-
push(prn,
|
|
1562
|
-
if (
|
|
1563
|
-
const m = getBitU(
|
|
1829
|
+
push(prn, str2, time, freqNum = 0) {
|
|
1830
|
+
if (str2.length < GLO_STRING_BYTES) return null;
|
|
1831
|
+
const m = getBitU(str2, 1, 4);
|
|
1564
1832
|
if (m < 1) return null;
|
|
1565
1833
|
const sec = Math.floor(time.getTime() / 1e3);
|
|
1566
1834
|
let sat = this.sats.get(prn);
|
|
@@ -1572,7 +1840,7 @@ var GloStringAssembler = class {
|
|
|
1572
1840
|
sat.batchSec = sec;
|
|
1573
1841
|
}
|
|
1574
1842
|
if (m > 4) return null;
|
|
1575
|
-
sat.buf.set(
|
|
1843
|
+
sat.buf.set(str2.subarray(0, GLO_STRING_BYTES), (m - 1) * 10);
|
|
1576
1844
|
if (m !== 4) return null;
|
|
1577
1845
|
const eph = decodeGloStrings(sat.buf, time, { freqNum });
|
|
1578
1846
|
if (!eph || eph.prn !== prn) return null;
|
|
@@ -1587,7 +1855,26 @@ var GloStringAssembler = class {
|
|
|
1587
1855
|
var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
|
|
1588
1856
|
var MS_PER_WEEK3 = 7 * 86400 * 1e3;
|
|
1589
1857
|
var TOW_DNU = 4294967295;
|
|
1590
|
-
var
|
|
1858
|
+
var WNC_DNU2 = 65535;
|
|
1859
|
+
function feedBdsBlock(view, b, assembler) {
|
|
1860
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
1861
|
+
if (!prn || prn[0] !== "C") return {};
|
|
1862
|
+
const sf = new Uint8Array(38);
|
|
1863
|
+
for (let k = 0; k < 10; k++) {
|
|
1864
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
1865
|
+
sf[4 * k] = w >>> 24;
|
|
1866
|
+
if (k < 9) {
|
|
1867
|
+
sf[4 * k + 1] = w >>> 16 & 255;
|
|
1868
|
+
sf[4 * k + 2] = w >>> 8 & 255;
|
|
1869
|
+
sf[4 * k + 3] = w & 255;
|
|
1870
|
+
} else {
|
|
1871
|
+
sf[37] = w >>> 16 & 240;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
if (!bdsSubframeParityOk(sf)) return { badCrc: true };
|
|
1875
|
+
const eph = assembler.push(prn, sf);
|
|
1876
|
+
return eph ? { eph } : {};
|
|
1877
|
+
}
|
|
1591
1878
|
function parseSbfBdsNav(data) {
|
|
1592
1879
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
1593
1880
|
const ephemerides = [];
|
|
@@ -1597,29 +1884,41 @@ function parseSbfBdsNav(data) {
|
|
|
1597
1884
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
1598
1885
|
if (id !== 4047 || len < 60) return;
|
|
1599
1886
|
messages++;
|
|
1600
|
-
const
|
|
1601
|
-
if (
|
|
1602
|
-
|
|
1603
|
-
for (let k = 0; k < 10; k++) {
|
|
1604
|
-
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
1605
|
-
sf[4 * k] = w >>> 24;
|
|
1606
|
-
if (k < 9) {
|
|
1607
|
-
sf[4 * k + 1] = w >>> 16 & 255;
|
|
1608
|
-
sf[4 * k + 2] = w >>> 8 & 255;
|
|
1609
|
-
sf[4 * k + 3] = w & 255;
|
|
1610
|
-
} else {
|
|
1611
|
-
sf[37] = w >>> 16 & 240;
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
if (!bdsSubframeParityOk(sf)) {
|
|
1615
|
-
badCrc++;
|
|
1616
|
-
return;
|
|
1617
|
-
}
|
|
1618
|
-
const eph = assembler.push(prn, sf);
|
|
1619
|
-
if (eph) ephemerides.push(eph);
|
|
1887
|
+
const r = feedBdsBlock(view, b, assembler);
|
|
1888
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
1889
|
+
else if (r.badCrc) badCrc++;
|
|
1620
1890
|
});
|
|
1621
1891
|
return { ephemerides, badCrc, messages };
|
|
1622
1892
|
}
|
|
1893
|
+
function feedGloBlock(view, b, assembler) {
|
|
1894
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
1895
|
+
if (!prn || prn[0] !== "R") return {};
|
|
1896
|
+
const towMs = view.getUint32(b + 8, true);
|
|
1897
|
+
const wnc = view.getUint16(b + 12, true);
|
|
1898
|
+
if (towMs === TOW_DNU || wnc === WNC_DNU2) return {};
|
|
1899
|
+
const str2 = new Uint8Array(11);
|
|
1900
|
+
for (let k = 0; k < 3; k++) {
|
|
1901
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
1902
|
+
str2[4 * k] = w >>> 24;
|
|
1903
|
+
if (k < 2) {
|
|
1904
|
+
str2[4 * k + 1] = w >>> 16 & 255;
|
|
1905
|
+
str2[4 * k + 2] = w >>> 8 & 255;
|
|
1906
|
+
str2[4 * k + 3] = w & 255;
|
|
1907
|
+
} else {
|
|
1908
|
+
str2[9] = w >>> 16 & 255;
|
|
1909
|
+
str2[10] = w >>> 8 & 248;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
if (!testGloString(str2)) return { badCrc: true };
|
|
1913
|
+
const eph = assembler.push(
|
|
1914
|
+
prn,
|
|
1915
|
+
str2,
|
|
1916
|
+
new Date(GPS_EPOCH_MS6 + wnc * MS_PER_WEEK3 + towMs),
|
|
1917
|
+
view.getUint8(b + 18) - 8
|
|
1918
|
+
// FreqNr, offset by 8
|
|
1919
|
+
);
|
|
1920
|
+
return eph ? { eph } : {};
|
|
1921
|
+
}
|
|
1623
1922
|
function parseSbfGloNav(data) {
|
|
1624
1923
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
1625
1924
|
const ephemerides = [];
|
|
@@ -1629,38 +1928,161 @@ function parseSbfGloNav(data) {
|
|
|
1629
1928
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
1630
1929
|
if (id !== 4026 || len < 32) return;
|
|
1631
1930
|
messages++;
|
|
1632
|
-
const
|
|
1633
|
-
if (
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1931
|
+
const r = feedGloBlock(view, b, assembler);
|
|
1932
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
1933
|
+
else if (r.badCrc) badCrc++;
|
|
1934
|
+
});
|
|
1935
|
+
return { ephemerides, badCrc, messages };
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
// src/sbf/navigation.ts
|
|
1939
|
+
var F4_DNU4 = -2e10;
|
|
1940
|
+
function ephKey(e) {
|
|
1941
|
+
const source = "source" in e ? e.source : "";
|
|
1942
|
+
return `${e.prn}|${e.tocDate.getTime()}|${source}`;
|
|
1943
|
+
}
|
|
1944
|
+
function decodeSbfNavigation(data) {
|
|
1945
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
1946
|
+
const ephemerides = [];
|
|
1947
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1948
|
+
const cnav = [];
|
|
1949
|
+
const ionoCorrections = {};
|
|
1950
|
+
let leapSeconds = null;
|
|
1951
|
+
const counts = {
|
|
1952
|
+
decodedNav: 0,
|
|
1953
|
+
gpsLnavRaw: 0,
|
|
1954
|
+
cnavRaw: 0,
|
|
1955
|
+
galRaw: 0,
|
|
1956
|
+
gloRaw: 0,
|
|
1957
|
+
bdsRaw: 0,
|
|
1958
|
+
badFrames: 0
|
|
1959
|
+
};
|
|
1960
|
+
const cnavAsm = newCnavAssemblers();
|
|
1961
|
+
const galAsm = newGalAssemblers();
|
|
1962
|
+
const gloAsm = new GloStringAssembler();
|
|
1963
|
+
const bdsAsm = new BdsAssembler();
|
|
1964
|
+
const lnavAsm = new GpsLnavAssembler();
|
|
1965
|
+
const addEph = (e) => {
|
|
1966
|
+
if (!e) return;
|
|
1967
|
+
const key = ephKey(e);
|
|
1968
|
+
if (seen.has(key)) return;
|
|
1969
|
+
seen.add(key);
|
|
1970
|
+
ephemerides.push(e);
|
|
1971
|
+
};
|
|
1972
|
+
const f4s = (b, n) => {
|
|
1973
|
+
const out = [];
|
|
1974
|
+
for (let k = 0; k < n; k++) {
|
|
1975
|
+
const v = view.getFloat32(b + 4 * k, true);
|
|
1976
|
+
if (v === F4_DNU4) return null;
|
|
1977
|
+
out.push(v);
|
|
1649
1978
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1979
|
+
return out;
|
|
1980
|
+
};
|
|
1981
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
1982
|
+
switch (id) {
|
|
1983
|
+
/* ---- decoded ephemeris blocks ---- */
|
|
1984
|
+
case 5891:
|
|
1985
|
+
if (len >= 140) {
|
|
1986
|
+
counts.decodedNav++;
|
|
1987
|
+
addEph(decodeGpsQzsNav(view, b, "G"));
|
|
1988
|
+
}
|
|
1989
|
+
return;
|
|
1990
|
+
case 4095:
|
|
1991
|
+
if (len >= 140) {
|
|
1992
|
+
counts.decodedNav++;
|
|
1993
|
+
addEph(decodeGpsQzsNav(view, b, "J"));
|
|
1994
|
+
}
|
|
1995
|
+
return;
|
|
1996
|
+
case 4002:
|
|
1997
|
+
if (len >= 149) {
|
|
1998
|
+
counts.decodedNav++;
|
|
1999
|
+
addEph(decodeGalNav(view, b));
|
|
2000
|
+
}
|
|
2001
|
+
return;
|
|
2002
|
+
case 4081:
|
|
2003
|
+
if (len >= 140) {
|
|
2004
|
+
counts.decodedNav++;
|
|
2005
|
+
addEph(decodeBdsNav(view, b));
|
|
2006
|
+
}
|
|
2007
|
+
return;
|
|
2008
|
+
case 4004:
|
|
2009
|
+
if (len >= 96) {
|
|
2010
|
+
counts.decodedNav++;
|
|
2011
|
+
addEph(decodeGloNav(view, b));
|
|
2012
|
+
}
|
|
2013
|
+
return;
|
|
2014
|
+
/* ---- raw navigation-frame blocks ---- */
|
|
2015
|
+
case 4017:
|
|
2016
|
+
// GPSRawCA
|
|
2017
|
+
case 4066: {
|
|
2018
|
+
if (len < 60) return;
|
|
2019
|
+
counts.gpsLnavRaw++;
|
|
2020
|
+
const r = feedGpsLnavBlock(view, b, lnavAsm);
|
|
2021
|
+
if (r.eph) addEph(r.eph);
|
|
2022
|
+
else if (r.badParity) counts.badFrames++;
|
|
2023
|
+
return;
|
|
2024
|
+
}
|
|
2025
|
+
case 4018:
|
|
2026
|
+
// GPSRawL2C
|
|
2027
|
+
case 4019: {
|
|
2028
|
+
if (len < 60) return;
|
|
2029
|
+
counts.cnavRaw++;
|
|
2030
|
+
const r = feedCnavBlock(view, b, id, cnavAsm);
|
|
2031
|
+
if (r.eph) cnav.push(r.eph);
|
|
2032
|
+
else if (r.badCrc) counts.badFrames++;
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
case 4022:
|
|
2036
|
+
// GALRawFNAV
|
|
2037
|
+
case 4023: {
|
|
2038
|
+
if (len < 52) return;
|
|
2039
|
+
counts.galRaw++;
|
|
2040
|
+
const r = feedGalBlock(data, view, b, id, galAsm);
|
|
2041
|
+
if (r.eph) addEph(r.eph);
|
|
2042
|
+
else if (r.badCrc) counts.badFrames++;
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
case 4026: {
|
|
2046
|
+
if (len < 32) return;
|
|
2047
|
+
counts.gloRaw++;
|
|
2048
|
+
const r = feedGloBlock(view, b, gloAsm);
|
|
2049
|
+
if (r.eph) addEph(r.eph);
|
|
2050
|
+
else if (r.badCrc) counts.badFrames++;
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
case 4047: {
|
|
2054
|
+
if (len < 60) return;
|
|
2055
|
+
counts.bdsRaw++;
|
|
2056
|
+
const r = feedBdsBlock(view, b, bdsAsm);
|
|
2057
|
+
if (r.eph) addEph(r.eph);
|
|
2058
|
+
else if (r.badCrc) counts.badFrames++;
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
/* ---- iono / UTC ---- */
|
|
2062
|
+
case 5893:
|
|
2063
|
+
// GPSIon
|
|
2064
|
+
case 4120: {
|
|
2065
|
+
if (len < 48) return;
|
|
2066
|
+
const alpha = f4s(b + 16, 4);
|
|
2067
|
+
const beta = f4s(b + 32, 4);
|
|
2068
|
+
if (!alpha || !beta) return;
|
|
2069
|
+
const sys = id === 5893 ? "GPS" : "BDS";
|
|
2070
|
+
ionoCorrections[`${sys}A`] = alpha;
|
|
2071
|
+
ionoCorrections[`${sys}B`] = beta;
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
case 4030: {
|
|
2075
|
+
if (len < 28) return;
|
|
2076
|
+
const ai = f4s(b + 16, 3);
|
|
2077
|
+
if (ai) ionoCorrections["GAL"] = ai;
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
case 5894:
|
|
2081
|
+
if (len >= 37) leapSeconds = view.getInt8(b + 33);
|
|
2082
|
+
return;
|
|
1653
2083
|
}
|
|
1654
|
-
const eph = assembler.push(
|
|
1655
|
-
prn,
|
|
1656
|
-
str,
|
|
1657
|
-
new Date(GPS_EPOCH_MS6 + wnc * MS_PER_WEEK3 + towMs),
|
|
1658
|
-
view.getUint8(b + 18) - 8
|
|
1659
|
-
// FreqNr, offset by 8
|
|
1660
|
-
);
|
|
1661
|
-
if (eph) ephemerides.push(eph);
|
|
1662
2084
|
});
|
|
1663
|
-
return { ephemerides,
|
|
2085
|
+
return { ephemerides, cnav, ionoCorrections, leapSeconds, counts };
|
|
1664
2086
|
}
|
|
1665
2087
|
|
|
1666
2088
|
// src/navbits/rs255.ts
|
|
@@ -2855,15 +3277,18 @@ function parseSbfMeas(data) {
|
|
|
2855
3277
|
}
|
|
2856
3278
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2857
3279
|
0 && (module.exports = {
|
|
3280
|
+
decodeSbfNavigation,
|
|
2858
3281
|
parseSbfAlmanac,
|
|
2859
3282
|
parseSbfBdsNav,
|
|
2860
3283
|
parseSbfCnav,
|
|
2861
3284
|
parseSbfGalNav,
|
|
2862
3285
|
parseSbfGloNav,
|
|
3286
|
+
parseSbfGpsNav,
|
|
2863
3287
|
parseSbfHas,
|
|
2864
3288
|
parseSbfIonoUtc,
|
|
2865
3289
|
parseSbfMeas,
|
|
2866
3290
|
parseSbfNav,
|
|
3291
|
+
parseSbfReceiverSetup,
|
|
2867
3292
|
sbfCrc16,
|
|
2868
3293
|
scanSbfFrames
|
|
2869
3294
|
});
|