gnss-js 1.20.0 → 1.22.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/ubx.cjs CHANGED
@@ -23,6 +23,7 @@ __export(ubx_exports, {
23
23
  parseUbxCnav: () => parseUbxCnav,
24
24
  parseUbxIonoUtc: () => parseUbxIonoUtc,
25
25
  parseUbxNav: () => parseUbxNav,
26
+ parseUbxRawNav: () => parseUbxRawNav,
26
27
  parseUbxRawx: () => parseUbxRawx,
27
28
  ubxFrames: () => ubxFrames
28
29
  });
@@ -538,9 +539,806 @@ function parseUbxCnav(data) {
538
539
  return { ephemerides, badCrc, messages };
539
540
  }
540
541
 
541
- // src/ubx/index.ts
542
+ // src/navbits/bds.ts
543
+ var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
544
+ var SEC_PER_WEEK3 = 7 * 86400;
545
+ var MS_PER_WEEK = SEC_PER_WEEK3 * 1e3;
546
+ var HALF_WEEK2 = 302400;
547
+ var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK3;
548
+ var BDS_SUBFRAME_BYTES = 38;
549
+ function getBitU2(b, p1, l1, p2, l2) {
550
+ return getBitU(b, p1, l1) * 2 ** l2 + getBitU(b, p2, l2);
551
+ }
552
+ function getBitS2(b, p1, l1, p2, l2) {
553
+ return getBitS(b, p1, l1) * 2 ** l2 + getBitU(b, p2, l2);
554
+ }
555
+ function getBitU3(b, p1, l1, p2, l2, p3, l3) {
556
+ return getBitU(b, p1, l1) * 2 ** (l2 + l3) + getBitU(b, p2, l2) * 2 ** l3 + getBitU(b, p3, l3);
557
+ }
558
+ function getBitS3(b, p1, l1, p2, l2, p3, l3) {
559
+ return getBitS(b, p1, l1) * 2 ** (l2 + l3) + getBitU(b, p2, l2) * 2 ** l3 + getBitU(b, p3, l3);
560
+ }
561
+ var mergeS = (hi, lo, n) => hi * 2 ** n + lo;
562
+ function bchOk(cw) {
563
+ for (let i = 14; i >= 4; i--) {
564
+ if (cw & 1 << i) cw ^= 19 << i - 4;
565
+ }
566
+ return cw === 0;
567
+ }
568
+ function bdsSubframeParityOk(subframe) {
569
+ if (subframe.length < BDS_SUBFRAME_BYTES) return false;
570
+ if (!bchOk(getBitU(subframe, 15, 15))) return false;
571
+ for (let w = 1; w < 10; w++) {
572
+ const base = 30 * w;
573
+ const cw1 = getBitU(subframe, base, 11) * 16 + getBitU(subframe, base + 22, 4);
574
+ const cw2 = getBitU(subframe, base + 11, 11) * 16 + getBitU(subframe, base + 26, 4);
575
+ if (!bchOk(cw1) || !bchOk(cw2)) return false;
576
+ }
577
+ return true;
578
+ }
579
+ function buildBdsEphemeris(prn, f) {
580
+ let week = f.week;
581
+ if (f.toes < f.sow - HALF_WEEK2) week++;
582
+ else if (f.toes > f.sow + HALF_WEEK2) week--;
583
+ const tocDate = new Date(BDT_EPOCH_MS + week * MS_PER_WEEK + f.tocSec * 1e3);
584
+ return {
585
+ system: "C",
586
+ prn,
587
+ toc: sowOf(tocDate.getTime()),
588
+ tocDate,
589
+ af0: f.af0,
590
+ af1: f.af1,
591
+ af2: f.af2,
592
+ // AODE is not in the ephemeris subframes; RTKLIB derives the RINEX
593
+ // IODE/AODE slot from toc per the BDS ICD update schedule.
594
+ iode: Math.floor(f.tocSec / 720) % 240,
595
+ crs: f.crs,
596
+ deltaN: f.deltaN,
597
+ m0: f.m0,
598
+ cuc: f.cuc,
599
+ e: f.e,
600
+ cus: f.cus,
601
+ sqrtA: f.sqrtA,
602
+ toe: f.toes,
603
+ cic: f.cic,
604
+ omega0: f.omega0,
605
+ cis: f.cis,
606
+ i0: f.i0,
607
+ crc: f.crc,
608
+ omega: f.omega,
609
+ omegaDot: f.omegaDot,
610
+ idot: f.idot,
611
+ week,
612
+ // RINEX BDS week field is the BDT week of toe
613
+ svHealth: f.svh,
614
+ // SatH1
615
+ tgd: f.tgd1
616
+ // TGD1 (B1) — RINEX slot
617
+ };
618
+ }
619
+ function decodeBdsD1Frame(subframes, opts = {}) {
620
+ if (subframes.length < 3 * BDS_SUBFRAME_BYTES) return null;
621
+ const b = subframes;
622
+ let i = 8 * 38 * 0;
623
+ const frn1 = getBitU(b, i + 15, 3);
624
+ const sow1 = getBitU2(b, i + 18, 8, i + 30, 12);
625
+ const svh = getBitU(b, i + 42, 1);
626
+ const week = getBitU(b, i + 60, 13);
627
+ const tocSec = getBitU2(b, i + 73, 9, i + 90, 8) * 8;
628
+ const tgd1 = getBitS(b, i + 98, 10) * 0.1 * 1e-9;
629
+ const af2 = getBitS(b, i + 214, 11) * 2 ** -66;
630
+ const af0 = getBitS2(b, i + 225, 7, i + 240, 17) * 2 ** -33;
631
+ const af1 = getBitS2(b, i + 257, 5, i + 270, 17) * 2 ** -50;
632
+ i = 8 * 38 * 1;
633
+ const frn2 = getBitU(b, i + 15, 3);
634
+ const sow2 = getBitU2(b, i + 18, 8, i + 30, 12);
635
+ const deltaN = getBitS2(b, i + 42, 10, i + 60, 6) * 2 ** -43 * GPS_PI;
636
+ const cuc = getBitS2(b, i + 66, 16, i + 90, 2) * 2 ** -31;
637
+ const m0 = getBitS2(b, i + 92, 20, i + 120, 12) * 2 ** -31 * GPS_PI;
638
+ const e = getBitU2(b, i + 132, 10, i + 150, 22) * 2 ** -33;
639
+ const cus = getBitS(b, i + 180, 18) * 2 ** -31;
640
+ const crc = getBitS2(b, i + 198, 4, i + 210, 14) * 2 ** -6;
641
+ const crs = getBitS2(b, i + 224, 8, i + 240, 10) * 2 ** -6;
642
+ const sqrtA = getBitU2(b, i + 250, 12, i + 270, 20) * 2 ** -19;
643
+ const toe1 = getBitU(b, i + 290, 2);
644
+ i = 8 * 38 * 2;
645
+ const frn3 = getBitU(b, i + 15, 3);
646
+ const sow3 = getBitU2(b, i + 18, 8, i + 30, 12);
647
+ const toe2 = getBitU2(b, i + 42, 10, i + 60, 5);
648
+ const i0 = getBitS2(b, i + 65, 17, i + 90, 15) * 2 ** -31 * GPS_PI;
649
+ const cic = getBitS2(b, i + 105, 7, i + 120, 11) * 2 ** -31;
650
+ const omegaDot = getBitS2(b, i + 131, 11, i + 150, 13) * 2 ** -43 * GPS_PI;
651
+ const cis = getBitS2(b, i + 163, 9, i + 180, 9) * 2 ** -31;
652
+ const idot = getBitS2(b, i + 189, 13, i + 210, 1) * 2 ** -43 * GPS_PI;
653
+ const omega0 = getBitS2(b, i + 211, 21, i + 240, 11) * 2 ** -31 * GPS_PI;
654
+ const omega = getBitS2(b, i + 251, 11, i + 270, 21) * 2 ** -31 * GPS_PI;
655
+ const toes = (toe1 * 2 ** 15 + toe2) * 8;
656
+ if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3) return null;
657
+ if (sow2 !== sow1 + 6 || sow3 !== sow2 + 6) return null;
658
+ if (tocSec !== toes) return null;
659
+ return buildBdsEphemeris(opts.prn ?? "C00", {
660
+ week,
661
+ sow: sow1,
662
+ toes,
663
+ tocSec,
664
+ svh,
665
+ tgd1,
666
+ af0,
667
+ af1,
668
+ af2,
669
+ crs,
670
+ deltaN,
671
+ m0,
672
+ cuc,
673
+ e,
674
+ cus,
675
+ sqrtA,
676
+ cic,
677
+ omega0,
678
+ cis,
679
+ i0,
680
+ crc,
681
+ omega,
682
+ omegaDot,
683
+ idot
684
+ });
685
+ }
686
+ function decodeBdsD2Frame(pages, opts = {}) {
687
+ if (pages.length < 10 * BDS_SUBFRAME_BYTES) return null;
688
+ const b = pages;
689
+ let i = 8 * 38 * 0;
690
+ const pgn1 = getBitU(b, i + 42, 4);
691
+ const sow1 = getBitU2(b, i + 18, 8, i + 30, 12);
692
+ const svh = getBitU(b, i + 46, 1);
693
+ const week = getBitU(b, i + 64, 13);
694
+ const tocSec = getBitU2(b, i + 77, 5, i + 90, 12) * 8;
695
+ const tgd1 = getBitS(b, i + 102, 10) * 0.1 * 1e-9;
696
+ i = 8 * 38 * 2;
697
+ const pgn3 = getBitU(b, i + 42, 4);
698
+ const sow3 = getBitU2(b, i + 18, 8, i + 30, 12);
699
+ const af0 = getBitS2(b, i + 100, 12, i + 120, 12) * 2 ** -33;
700
+ const af1p3 = getBitS(b, i + 132, 4);
701
+ i = 8 * 38 * 3;
702
+ const pgn4 = getBitU(b, i + 42, 4);
703
+ const sow4 = getBitU2(b, i + 18, 8, i + 30, 12);
704
+ const af1p4 = getBitU2(b, i + 46, 6, i + 60, 12);
705
+ const af2 = getBitS2(b, i + 72, 10, i + 90, 1) * 2 ** -66;
706
+ const deltaN = getBitS(b, i + 96, 16) * 2 ** -43 * GPS_PI;
707
+ const cucp4 = getBitS(b, i + 120, 14);
708
+ i = 8 * 38 * 4;
709
+ const pgn5 = getBitU(b, i + 42, 4);
710
+ const sow5 = getBitU2(b, i + 18, 8, i + 30, 12);
711
+ const cucp5 = getBitU(b, i + 46, 4);
712
+ const m0 = getBitS3(b, i + 50, 2, i + 60, 22, i + 90, 8) * 2 ** -31 * GPS_PI;
713
+ const cus = getBitS2(b, i + 98, 14, i + 120, 4) * 2 ** -31;
714
+ const ep5 = getBitS(b, i + 124, 10);
715
+ i = 8 * 38 * 5;
716
+ const pgn6 = getBitU(b, i + 42, 4);
717
+ const sow6 = getBitU2(b, i + 18, 8, i + 30, 12);
718
+ const ep6 = getBitU2(b, i + 46, 6, i + 60, 16);
719
+ const sqrtA = getBitU3(b, i + 76, 6, i + 90, 22, i + 120, 4) * 2 ** -19;
720
+ const cicp6 = getBitS(b, i + 124, 10);
721
+ i = 8 * 38 * 6;
722
+ const pgn7 = getBitU(b, i + 42, 4);
723
+ const sow7 = getBitU2(b, i + 18, 8, i + 30, 12);
724
+ const cicp7 = getBitU2(b, i + 46, 6, i + 60, 2);
725
+ const cis = getBitS(b, i + 62, 18) * 2 ** -31;
726
+ const toes = getBitU2(b, i + 80, 2, i + 90, 15) * 8;
727
+ const i0p7 = getBitS2(b, i + 105, 7, i + 120, 14);
728
+ i = 8 * 38 * 7;
729
+ const pgn8 = getBitU(b, i + 42, 4);
730
+ const sow8 = getBitU2(b, i + 18, 8, i + 30, 12);
731
+ const i0p8 = getBitU2(b, i + 46, 6, i + 60, 5);
732
+ const crc = getBitS2(b, i + 65, 17, i + 90, 1) * 2 ** -6;
733
+ const crs = getBitS(b, i + 91, 18) * 2 ** -6;
734
+ const omegaDotP8 = getBitS2(b, i + 109, 3, i + 120, 16);
735
+ i = 8 * 38 * 8;
736
+ const pgn9 = getBitU(b, i + 42, 4);
737
+ const sow9 = getBitU2(b, i + 18, 8, i + 30, 12);
738
+ const omegaDotP9 = getBitU(b, i + 46, 5);
739
+ const omega0 = getBitS3(b, i + 51, 1, i + 60, 22, i + 90, 9) * 2 ** -31 * GPS_PI;
740
+ const omegaP9 = getBitS2(b, i + 99, 13, i + 120, 14);
741
+ i = 8 * 38 * 9;
742
+ const pgn10 = getBitU(b, i + 42, 4);
743
+ const sow10 = getBitU2(b, i + 18, 8, i + 30, 12);
744
+ const omegaP10 = getBitU(b, i + 46, 5);
745
+ const idot = getBitS2(b, i + 51, 1, i + 60, 13) * 2 ** -43 * GPS_PI;
746
+ if (pgn1 !== 1 || pgn3 !== 3 || pgn4 !== 4 || pgn5 !== 5 || pgn6 !== 6 || pgn7 !== 7 || pgn8 !== 8 || pgn9 !== 9 || pgn10 !== 10) {
747
+ return null;
748
+ }
749
+ if (sow3 !== sow1 + 6 || sow4 !== sow3 + 3 || sow5 !== sow4 + 3 || sow6 !== sow5 + 3 || sow7 !== sow6 + 3 || sow8 !== sow7 + 3 || sow9 !== sow8 + 3 || sow10 !== sow9 + 3) {
750
+ return null;
751
+ }
752
+ if (tocSec !== toes) return null;
753
+ return buildBdsEphemeris(opts.prn ?? "C00", {
754
+ week,
755
+ sow: sow1,
756
+ toes,
757
+ tocSec,
758
+ svh,
759
+ tgd1,
760
+ af0,
761
+ af1: mergeS(af1p3, af1p4, 18) * 2 ** -50,
762
+ af2,
763
+ crs,
764
+ deltaN,
765
+ m0,
766
+ cuc: mergeS(cucp4, cucp5, 4) * 2 ** -31,
767
+ e: mergeS(ep5, ep6, 22) * 2 ** -33,
768
+ cus,
769
+ sqrtA,
770
+ cic: mergeS(cicp6, cicp7, 8) * 2 ** -31,
771
+ omega0,
772
+ cis,
773
+ i0: mergeS(i0p7, i0p8, 11) * 2 ** -31 * GPS_PI,
774
+ crc,
775
+ omega: mergeS(omegaP9, omegaP10, 5) * 2 ** -31 * GPS_PI,
776
+ omegaDot: mergeS(omegaDotP8, omegaDotP9, 5) * 2 ** -43 * GPS_PI,
777
+ idot
778
+ });
779
+ }
780
+ function isBdsGeoPrn(prn) {
781
+ return prn < 6 || prn > 58;
782
+ }
783
+ var BdsAssembler = class {
784
+ sats = /* @__PURE__ */ new Map();
785
+ /**
786
+ * Push one 300-bit subframe (38+ bytes, bit 0 = first bit of the
787
+ * preamble) for the satellite `prn` ("C06"). Returns the newly
788
+ * completed ephemeris, or null.
789
+ */
790
+ push(prn, subframe) {
791
+ if (subframe.length < BDS_SUBFRAME_BYTES) return null;
792
+ const num = parseInt(prn.slice(1), 10);
793
+ if (!Number.isFinite(num) || num < 1 || num > 63) return null;
794
+ const id = getBitU(subframe, 15, 3);
795
+ if (id < 1 || id > 5) return null;
796
+ let sat = this.sats.get(prn);
797
+ if (!sat) {
798
+ sat = { buf: new Uint8Array(10 * BDS_SUBFRAME_BYTES) };
799
+ this.sats.set(prn, sat);
800
+ }
801
+ let eph = null;
802
+ if (!isBdsGeoPrn(num)) {
803
+ sat.buf.set(subframe.subarray(0, BDS_SUBFRAME_BYTES), (id - 1) * 38);
804
+ if (id === 3) eph = decodeBdsD1Frame(sat.buf, { prn });
805
+ } else {
806
+ if (id !== 1) return null;
807
+ const pgn = getBitU(subframe, 42, 4);
808
+ if (pgn < 1 || pgn > 10) return null;
809
+ sat.buf.set(subframe.subarray(0, BDS_SUBFRAME_BYTES), (pgn - 1) * 38);
810
+ if (pgn === 10) eph = decodeBdsD2Frame(sat.buf, { prn });
811
+ }
812
+ if (!eph) return null;
813
+ const key = `${eph.week}:${eph.toe}`;
814
+ if (key === sat.lastKey) return null;
815
+ sat.lastKey = key;
816
+ return eph;
817
+ }
818
+ };
819
+
820
+ // src/navbits/gal.ts
542
821
  var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
543
- var MS_PER_WEEK = 7 * 864e5;
822
+ var SEC_PER_WEEK4 = 7 * 86400;
823
+ var HALF_WEEK3 = 302400;
824
+ var GST_GPS_WEEK_OFFSET = 1024;
825
+ function galInavPageCrcOk(page) {
826
+ if (page.length < 28) return false;
827
+ return crc24q(page, 196) === getBitU(page, 196, 24);
828
+ }
829
+ function gpsDate2(week, sec) {
830
+ return new Date(GPS_EPOCH_MS3 + (week * SEC_PER_WEEK4 + sec) * 1e3);
831
+ }
832
+ function weekOfToe(week, tow, toe) {
833
+ if (toe - tow > HALF_WEEK3) return week - 1;
834
+ if (toe - tow < -HALF_WEEK3) return week + 1;
835
+ return week;
836
+ }
837
+ var two = (n) => String(n).padStart(2, "0");
838
+ function decodeGalInavWords(words) {
839
+ if (words.length < 96) return null;
840
+ const b = words;
841
+ let i = 128;
842
+ const type1 = getBitU(b, i, 6);
843
+ i += 6;
844
+ const iodNav1 = getBitU(b, i, 10);
845
+ i += 10;
846
+ const toes = getBitU(b, i, 14) * 60;
847
+ i += 14;
848
+ const m0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
849
+ i += 32;
850
+ const e = getBitU(b, i, 32) * 2 ** -33;
851
+ i += 32;
852
+ const sqrtA = getBitU(b, i, 32) * 2 ** -19;
853
+ i = 128 * 2;
854
+ const type2 = getBitU(b, i, 6);
855
+ i += 6;
856
+ const iodNav2 = getBitU(b, i, 10);
857
+ i += 10;
858
+ const omega0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
859
+ i += 32;
860
+ const i0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
861
+ i += 32;
862
+ const omega = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
863
+ i += 32;
864
+ const idot = getBitS(b, i, 14) * 2 ** -43 * GPS_PI;
865
+ i = 128 * 3;
866
+ const type3 = getBitU(b, i, 6);
867
+ i += 6;
868
+ const iodNav3 = getBitU(b, i, 10);
869
+ i += 10;
870
+ const omegaDot = getBitS(b, i, 24) * 2 ** -43 * GPS_PI;
871
+ i += 24;
872
+ const deltaN = getBitS(b, i, 16) * 2 ** -43 * GPS_PI;
873
+ i += 16;
874
+ const cuc = getBitS(b, i, 16) * 2 ** -29;
875
+ i += 16;
876
+ const cus = getBitS(b, i, 16) * 2 ** -29;
877
+ i += 16;
878
+ const crc = getBitS(b, i, 16) * 2 ** -5;
879
+ i += 16;
880
+ const crs = getBitS(b, i, 16) * 2 ** -5;
881
+ i = 128 * 4;
882
+ const type4 = getBitU(b, i, 6);
883
+ i += 6;
884
+ const iodNav4 = getBitU(b, i, 10);
885
+ i += 10;
886
+ const svid = getBitU(b, i, 6);
887
+ i += 6;
888
+ const cic = getBitS(b, i, 16) * 2 ** -29;
889
+ i += 16;
890
+ const cis = getBitS(b, i, 16) * 2 ** -29;
891
+ i += 16;
892
+ const tocs = getBitU(b, i, 14) * 60;
893
+ i += 14;
894
+ const af0 = getBitS(b, i, 31) * 2 ** -34;
895
+ i += 31;
896
+ const af1 = getBitS(b, i, 21) * 2 ** -46;
897
+ i += 21;
898
+ const af2 = getBitS(b, i, 6) * 2 ** -59;
899
+ i = 128 * 5;
900
+ const type5 = getBitU(b, i, 6);
901
+ i += 6 + 11 + 11 + 14 + 5;
902
+ const bgdE5a = getBitS(b, i, 10) * 2 ** -32;
903
+ i += 10;
904
+ i += 10;
905
+ const e5bHs = getBitU(b, i, 2);
906
+ i += 2;
907
+ const e1bHs = getBitU(b, i, 2);
908
+ i += 2;
909
+ const e5bDvs = getBitU(b, i, 1);
910
+ i += 1;
911
+ const e1bDvs = getBitU(b, i, 1);
912
+ i += 1;
913
+ const gstWeek = getBitU(b, i, 12);
914
+ i += 12;
915
+ const tow = getBitU(b, i, 20);
916
+ if (type1 !== 1 || type2 !== 2 || type3 !== 3 || type4 !== 4 || type5 !== 5)
917
+ return null;
918
+ if (iodNav1 !== iodNav2 || iodNav1 !== iodNav3 || iodNav1 !== iodNav4)
919
+ return null;
920
+ if (svid < 1 || svid > 36) return null;
921
+ const week = weekOfToe(gstWeek, tow, toes) + GST_GPS_WEEK_OFFSET;
922
+ const tocDate = gpsDate2(week, tocs);
923
+ return {
924
+ system: "E",
925
+ prn: `E${two(svid)}`,
926
+ toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK4,
927
+ tocDate,
928
+ af0,
929
+ af1,
930
+ af2,
931
+ iode: iodNav1,
932
+ crs,
933
+ deltaN,
934
+ m0,
935
+ cuc,
936
+ e,
937
+ cus,
938
+ sqrtA,
939
+ toe: toes,
940
+ cic,
941
+ omega0,
942
+ cis,
943
+ i0,
944
+ crc,
945
+ omega,
946
+ omegaDot,
947
+ idot,
948
+ week,
949
+ svHealth: e5bHs << 7 | e5bDvs << 6 | e1bHs << 1 | e1bDvs,
950
+ tgd: bgdE5a
951
+ };
952
+ }
953
+ var dedupKey = (eph) => `${eph.iode}:${eph.toe}:${eph.tocDate.getTime()}`;
954
+ var GalInavAssembler = class {
955
+ sats = /* @__PURE__ */ new Map();
956
+ /**
957
+ * Push one I/NAV page pair for satellite `prn` (1-36): the 234-bit
958
+ * even+odd concatenation, even part first with its 6 tail bits
959
+ * removed, bit 0 = the even part's Even/Odd bit (≥ 17 bytes).
960
+ * Returns the newly completed ephemeris, or null. Pages that are
961
+ * not an even/odd nominal pair, alert pages and word types outside
962
+ * 1-5 are ignored.
963
+ */
964
+ push(prn, page) {
965
+ if (prn < 1 || prn > 36 || page.length < 17) return null;
966
+ if (getBitU(page, 0, 1) !== 0 || getBitU(page, 114, 1) !== 1) return null;
967
+ if (getBitU(page, 1, 1) === 1 || getBitU(page, 115, 1) === 1) return null;
968
+ const type = getBitU(page, 2, 6);
969
+ if (type < 1 || type > 5) return null;
970
+ let sat = this.sats.get(prn);
971
+ if (!sat) {
972
+ sat = { words: new Uint8Array(96) };
973
+ this.sats.set(prn, sat);
974
+ }
975
+ for (let k = 0; k < 14; k++)
976
+ sat.words[type * 16 + k] = getBitU(page, 2 + 8 * k, 8);
977
+ sat.words[type * 16 + 14] = getBitU(page, 116, 8);
978
+ sat.words[type * 16 + 15] = getBitU(page, 124, 8);
979
+ if (type !== 5) return null;
980
+ const eph = decodeGalInavWords(sat.words);
981
+ if (!eph || eph.prn !== `E${two(prn)}`) return null;
982
+ const key = dedupKey(eph);
983
+ if (key === sat.lastKey) return null;
984
+ sat.lastKey = key;
985
+ return eph;
986
+ }
987
+ };
988
+
989
+ // src/constants/time.ts
990
+ var MILLISECONDS_IN_SECOND = 1e3;
991
+ var MILLISECONDS_GPS_TAI = 19e3;
992
+ var START_NTP_TIME = /* @__PURE__ */ new Date("1900-01-01T00:00:00Z");
993
+
994
+ // src/time/gnss.ts
995
+ function getTaiDate(date) {
996
+ return new Date(date.getTime() + MILLISECONDS_GPS_TAI);
997
+ }
998
+ function getNtpTime(date) {
999
+ return getTaiDate(date).getTime() - START_NTP_TIME.getTime();
1000
+ }
1001
+
1002
+ // src/time/utc.ts
1003
+ var LEAP_SECONDS_TABLE = [
1004
+ [3692217600, 37],
1005
+ // 1 Jan 2017
1006
+ [3644697600, 36],
1007
+ // 1 Jul 2015
1008
+ [3550089600, 35],
1009
+ // 1 Jul 2012
1010
+ [3439756800, 34],
1011
+ // 1 Jan 2009
1012
+ [3345062400, 33],
1013
+ // 1 Jan 2006
1014
+ [3124137600, 32],
1015
+ // 1 Jan 1999
1016
+ [3076704e3, 31],
1017
+ // 1 Jul 1997
1018
+ [3029443200, 30],
1019
+ // 1 Jan 1996
1020
+ [2982009600, 29],
1021
+ // 1 Jul 1994
1022
+ [2950473600, 28],
1023
+ // 1 Jul 1993
1024
+ [2918937600, 27],
1025
+ // 1 Jul 1992
1026
+ [2871676800, 26],
1027
+ // 1 Jan 1991
1028
+ [2840140800, 25],
1029
+ // 1 Jan 1990
1030
+ [2776982400, 24],
1031
+ // 1 Jan 1988
1032
+ [2698012800, 23],
1033
+ // 1 Jul 1985
1034
+ [2634854400, 22],
1035
+ // 1 Jul 1983
1036
+ [2603318400, 21],
1037
+ // 1 Jul 1982
1038
+ [2571782400, 20],
1039
+ // 1 Jul 1981
1040
+ [2524521600, 19],
1041
+ // 1 Jan 1980
1042
+ [2492985600, 18],
1043
+ // 1 Jan 1979
1044
+ [2461449600, 17],
1045
+ // 1 Jan 1978
1046
+ [2429913600, 16],
1047
+ // 1 Jan 1977
1048
+ [2398291200, 15],
1049
+ // 1 Jan 1976
1050
+ [2366755200, 14],
1051
+ // 1 Jan 1975
1052
+ [2335219200, 13],
1053
+ // 1 Jan 1974
1054
+ [2303683200, 12],
1055
+ // 1 Jan 1973
1056
+ [2287785600, 11],
1057
+ // 1 Jul 1972
1058
+ [2272060800, 10]
1059
+ // 1 Jan 1972
1060
+ ];
1061
+ function getLeap(date) {
1062
+ const ntp_time = getNtpTime(date);
1063
+ for (const [timestamp, leapSeconds] of LEAP_SECONDS_TABLE) {
1064
+ if (ntp_time / MILLISECONDS_IN_SECOND - leapSeconds >= timestamp) {
1065
+ return leapSeconds;
1066
+ }
1067
+ }
1068
+ return 8;
1069
+ }
1070
+ function getUtcDate(date) {
1071
+ const tai_date = getTaiDate(date);
1072
+ const leap_seconds = getLeap(date);
1073
+ return new Date(tai_date.getTime() - leap_seconds * MILLISECONDS_IN_SECOND);
1074
+ }
1075
+
1076
+ // src/navbits/glo.ts
1077
+ var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
1078
+ var SEC_PER_WEEK5 = 7 * 86400;
1079
+ var SEC_PER_DAY = 86400;
1080
+ var GLO_STRING_BYTES = 10;
1081
+ function getBitG(b, pos, len) {
1082
+ const value = getBitU(b, pos + 1, len - 1);
1083
+ return getBitU(b, pos, 1) && value !== 0 ? -value : value;
1084
+ }
1085
+ var HAMMING_MASKS = [
1086
+ [85, 85, 90, 170, 170, 170, 181, 85, 106, 216, 8],
1087
+ [102, 102, 108, 204, 204, 204, 217, 153, 179, 104, 16],
1088
+ [135, 135, 143, 15, 15, 15, 30, 30, 60, 112, 32],
1089
+ [7, 248, 15, 240, 15, 240, 31, 224, 63, 128, 64],
1090
+ [248, 0, 15, 255, 240, 0, 31, 255, 192, 0, 128],
1091
+ [0, 0, 15, 255, 255, 255, 224, 0, 0, 1, 0],
1092
+ [255, 255, 240, 0, 0, 0, 0, 0, 0, 2, 0],
1093
+ [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248]
1094
+ ];
1095
+ function testGloString(buff) {
1096
+ if (buff.length < 11) return false;
1097
+ let n = 0;
1098
+ let cs = 0;
1099
+ for (const mask of HAMMING_MASKS) {
1100
+ cs = 0;
1101
+ for (let j = 0; j < 11; j++) {
1102
+ let x = buff[j] & mask[j];
1103
+ x ^= x >> 4;
1104
+ x ^= x >> 2;
1105
+ x ^= x >> 1;
1106
+ cs ^= x & 1;
1107
+ }
1108
+ if (cs) n++;
1109
+ }
1110
+ return n === 0 || n === 2 && cs === 1;
1111
+ }
1112
+ function decodeGloStrings(strings, refDate, opts = {}) {
1113
+ if (strings.length < 4 * GLO_STRING_BYTES) return null;
1114
+ const b = strings;
1115
+ let i = 1;
1116
+ const frn1 = getBitU(b, i, 4);
1117
+ i += 4 + 2;
1118
+ i += 2;
1119
+ const tkH = getBitU(b, i, 5);
1120
+ i += 5;
1121
+ const tkM = getBitU(b, i, 6);
1122
+ i += 6;
1123
+ const tkS = getBitU(b, i, 1) * 30;
1124
+ i += 1;
1125
+ const xDot = getBitG(b, i, 24) * 2 ** -20;
1126
+ i += 24;
1127
+ const xAcc = getBitG(b, i, 5) * 2 ** -30;
1128
+ i += 5;
1129
+ const x = getBitG(b, i, 27) * 2 ** -11;
1130
+ i = 80 + 1;
1131
+ const frn2 = getBitU(b, i, 4);
1132
+ i += 4;
1133
+ const bn = getBitU(b, i, 1);
1134
+ i += 1 + 2;
1135
+ i += 1;
1136
+ const tb = getBitU(b, i, 7);
1137
+ i += 7 + 5;
1138
+ const yDot = getBitG(b, i, 24) * 2 ** -20;
1139
+ i += 24;
1140
+ const yAcc = getBitG(b, i, 5) * 2 ** -30;
1141
+ i += 5;
1142
+ const y = getBitG(b, i, 27) * 2 ** -11;
1143
+ i = 160 + 1;
1144
+ const frn3 = getBitU(b, i, 4);
1145
+ i += 4;
1146
+ i += 1;
1147
+ const gammaN = getBitG(b, i, 11) * 2 ** -40;
1148
+ i += 11 + 1;
1149
+ i += 2;
1150
+ i += 1;
1151
+ const zDot = getBitG(b, i, 24) * 2 ** -20;
1152
+ i += 24;
1153
+ const zAcc = getBitG(b, i, 5) * 2 ** -30;
1154
+ i += 5;
1155
+ const z = getBitG(b, i, 27) * 2 ** -11;
1156
+ i = 240 + 1;
1157
+ const frn4 = getBitU(b, i, 4);
1158
+ i += 4;
1159
+ const tauN = getBitG(b, i, 22) * 2 ** -30;
1160
+ i += 22;
1161
+ i += 5;
1162
+ i += 5;
1163
+ i += 14;
1164
+ i += 1;
1165
+ i += 4;
1166
+ i += 3;
1167
+ i += 11;
1168
+ const slot = getBitU(b, i, 5);
1169
+ if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3 || frn4 !== 4) return null;
1170
+ if (slot < 1 || slot > 27) return null;
1171
+ const utcSec = (getUtcDate(refDate).getTime() - GPS_EPOCH_MS4) / 1e3;
1172
+ const week = Math.floor(utcSec / SEC_PER_WEEK5);
1173
+ let tow = utcSec - week * SEC_PER_WEEK5;
1174
+ const tod = tow % SEC_PER_DAY;
1175
+ tow -= tod;
1176
+ let tof = tkH * 3600 + tkM * 60 + tkS - 10800;
1177
+ if (tof < tod - 43200) tof += SEC_PER_DAY;
1178
+ else if (tof > tod + 43200) tof -= SEC_PER_DAY;
1179
+ let toe = tb * 900 - 10800;
1180
+ if (toe < tod - 43200) toe += SEC_PER_DAY;
1181
+ else if (toe > tod + 43200) toe -= SEC_PER_DAY;
1182
+ return {
1183
+ system: "R",
1184
+ prn: `R${String(slot).padStart(2, "0")}`,
1185
+ // RINEX GLONASS epochs are UTC: build the UTC toe Date directly.
1186
+ tocDate: new Date(GPS_EPOCH_MS4 + (week * SEC_PER_WEEK5 + tow + toe) * 1e3),
1187
+ tauN: -tauN,
1188
+ // RINEX stores −τn; the SIS carries τn (ICD sign)
1189
+ gammaN,
1190
+ // v3 message frame time: seconds of the UTC week (RTKLIB tof).
1191
+ messageFrameTime: ((tow + tof) % SEC_PER_WEEK5 + SEC_PER_WEEK5) % SEC_PER_WEEK5,
1192
+ x,
1193
+ xDot,
1194
+ xAcc,
1195
+ y,
1196
+ yDot,
1197
+ yAcc,
1198
+ z,
1199
+ zDot,
1200
+ zAcc,
1201
+ // MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
1202
+ health: bn,
1203
+ freqNum: opts.freqNum ?? 0
1204
+ };
1205
+ }
1206
+ var GloStringAssembler = class {
1207
+ sats = /* @__PURE__ */ new Map();
1208
+ /**
1209
+ * Push one navigation string (10+ bytes, bit 0 = string bit 85) for
1210
+ * the satellite `prn` ("R09"), received at the GPS-scale `time`.
1211
+ * Returns the newly completed ephemeris, or null. The decoded
1212
+ * string-4 slot number must match `prn`, or nothing is emitted.
1213
+ */
1214
+ push(prn, str, time, freqNum = 0) {
1215
+ if (str.length < GLO_STRING_BYTES) return null;
1216
+ const m = getBitU(str, 1, 4);
1217
+ if (m < 1) return null;
1218
+ const sec = Math.floor(time.getTime() / 1e3);
1219
+ let sat = this.sats.get(prn);
1220
+ if (!sat) {
1221
+ sat = { buf: new Uint8Array(4 * GLO_STRING_BYTES), batchSec: sec };
1222
+ this.sats.set(prn, sat);
1223
+ } else if (Math.abs(sec - sat.batchSec) > 30) {
1224
+ sat.buf.fill(0);
1225
+ sat.batchSec = sec;
1226
+ }
1227
+ if (m > 4) return null;
1228
+ sat.buf.set(str.subarray(0, GLO_STRING_BYTES), (m - 1) * 10);
1229
+ if (m !== 4) return null;
1230
+ const eph = decodeGloStrings(sat.buf, time, { freqNum });
1231
+ if (!eph || eph.prn !== prn) return null;
1232
+ const key = `${eph.tocDate.getTime()}:${eph.health}`;
1233
+ if (key === sat.lastKey) return null;
1234
+ sat.lastKey = key;
1235
+ return eph;
1236
+ }
1237
+ };
1238
+
1239
+ // src/ubx/rawnav.ts
1240
+ var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
1241
+ var SEC_PER_WEEK6 = 7 * 86400;
1242
+ var two2 = (n) => String(n).padStart(2, "0");
1243
+ function parseUbxRawNav(data, opts = {}) {
1244
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
1245
+ const ephemerides = [];
1246
+ const counts = { gal: 0, bds: 0, glo: 0 };
1247
+ let badParity = 0;
1248
+ const inav = new GalInavAssembler();
1249
+ const bds = new BdsAssembler();
1250
+ const glo = new GloStringAssembler();
1251
+ let refDate = null;
1252
+ for (const f of ubxFrames(data)) {
1253
+ if (f.msgClass !== 2) continue;
1254
+ if (f.msgId === 21 && f.payload.length >= 16) {
1255
+ const rcvTow = view.getFloat64(f.payloadStart, true);
1256
+ let week = view.getUint16(f.payloadStart + 8, true);
1257
+ if (week === 0 && opts.refWeek !== void 0) week = opts.refWeek;
1258
+ if (week > 0) {
1259
+ refDate = new Date(
1260
+ GPS_EPOCH_MS5 + (week * SEC_PER_WEEK6 + rcvTow) * 1e3
1261
+ );
1262
+ }
1263
+ continue;
1264
+ }
1265
+ if (f.msgId !== 19) continue;
1266
+ const p = f.payload;
1267
+ if (p.length < 8) continue;
1268
+ const gnssId = p[0];
1269
+ const svId = p[1];
1270
+ const sigId = p[2];
1271
+ const base = f.payloadStart + 8;
1272
+ if (gnssId === 2) {
1273
+ if (sigId === 3 || sigId === 8) continue;
1274
+ if (p.length < 8 + 32) continue;
1275
+ counts.gal++;
1276
+ if (svId < 1 || svId > 36) continue;
1277
+ const buff = new Uint8Array(32);
1278
+ for (let k = 0; k < 8; k++) {
1279
+ const w = view.getUint32(base + 4 * k, true);
1280
+ buff[4 * k] = w >>> 24;
1281
+ buff[4 * k + 1] = w >>> 16 & 255;
1282
+ buff[4 * k + 2] = w >>> 8 & 255;
1283
+ buff[4 * k + 3] = w & 255;
1284
+ }
1285
+ const page = new Uint8Array(30);
1286
+ for (let k = 0; k < 14; k++) page[k] = buff[k];
1287
+ setBitU(page, 112, 2, getBitU(buff, 112, 2));
1288
+ for (let k = 0; k < 15; k++) {
1289
+ setBitU(page, 114 + 8 * k, 8, getBitU(buff, 128 + 8 * k, 8));
1290
+ }
1291
+ if (getBitU(page, 0, 1) !== 0 || getBitU(page, 114, 1) !== 1) continue;
1292
+ if (getBitU(page, 1, 1) === 1 || getBitU(page, 115, 1) === 1) continue;
1293
+ if (!galInavPageCrcOk(page)) {
1294
+ badParity++;
1295
+ continue;
1296
+ }
1297
+ const eph = inav.push(svId, page);
1298
+ if (eph) ephemerides.push(eph);
1299
+ } else if (gnssId === 3) {
1300
+ if (sigId === 6 || sigId === 8) continue;
1301
+ if (p.length < 8 + 40) continue;
1302
+ counts.bds++;
1303
+ if (svId < 1 || svId > 63) continue;
1304
+ const sf = new Uint8Array(38);
1305
+ for (let k = 0; k < 10; k++) {
1306
+ const w = view.getUint32(base + 4 * k, true);
1307
+ setBitU(sf, 30 * k, 30, w & 1073741823);
1308
+ }
1309
+ if (!bdsSubframeParityOk(sf)) {
1310
+ badParity++;
1311
+ continue;
1312
+ }
1313
+ const eph = bds.push(`C${two2(svId)}`, sf);
1314
+ if (eph) ephemerides.push(eph);
1315
+ } else if (gnssId === 6) {
1316
+ if (p.length < 8 + 16) continue;
1317
+ counts.glo++;
1318
+ if (svId < 1 || svId > 32) continue;
1319
+ const str = new Uint8Array(16);
1320
+ for (let k = 0; k < 4; k++) {
1321
+ const w = view.getUint32(base + 4 * k, true);
1322
+ str[4 * k] = w >>> 24;
1323
+ str[4 * k + 1] = w >>> 16 & 255;
1324
+ str[4 * k + 2] = w >>> 8 & 255;
1325
+ str[4 * k + 3] = w & 255;
1326
+ }
1327
+ if (!testGloString(str)) {
1328
+ badParity++;
1329
+ continue;
1330
+ }
1331
+ if (!refDate) continue;
1332
+ const eph = glo.push(`R${two2(svId)}`, str, refDate, p[3] - 7);
1333
+ if (eph) ephemerides.push(eph);
1334
+ }
1335
+ }
1336
+ return { ephemerides, counts, badParity };
1337
+ }
1338
+
1339
+ // src/ubx/index.ts
1340
+ var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
1341
+ var MS_PER_WEEK2 = 7 * 864e5;
544
1342
  var SIGNALS = {
545
1343
  0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
546
1344
  // GPS L1C/A, L2CL, L2CM
@@ -556,20 +1354,20 @@ var SIGNALS = {
556
1354
  // GLONASS L1OF, L2OF
557
1355
  };
558
1356
  function prnFor(gnssId, svId) {
559
- const two = (n) => String(n).padStart(2, "0");
1357
+ const two3 = (n) => String(n).padStart(2, "0");
560
1358
  switch (gnssId) {
561
1359
  case 0:
562
- return svId >= 1 && svId <= 32 ? `G${two(svId)}` : null;
1360
+ return svId >= 1 && svId <= 32 ? `G${two3(svId)}` : null;
563
1361
  case 1:
564
- return svId >= 120 && svId <= 158 ? `S${two(svId - 100)}` : null;
1362
+ return svId >= 120 && svId <= 158 ? `S${two3(svId - 100)}` : null;
565
1363
  case 2:
566
- return svId >= 1 && svId <= 36 ? `E${two(svId)}` : null;
1364
+ return svId >= 1 && svId <= 36 ? `E${two3(svId)}` : null;
567
1365
  case 3:
568
- return svId >= 1 && svId <= 63 ? `C${two(svId)}` : null;
1366
+ return svId >= 1 && svId <= 63 ? `C${two3(svId)}` : null;
569
1367
  case 5:
570
- return svId >= 1 && svId <= 10 ? `J${two(svId)}` : null;
1368
+ return svId >= 1 && svId <= 10 ? `J${two3(svId)}` : null;
571
1369
  case 6:
572
- return svId >= 1 && svId <= 32 ? `R${two(svId)}` : null;
1370
+ return svId >= 1 && svId <= 32 ? `R${two3(svId)}` : null;
573
1371
  default:
574
1372
  return null;
575
1373
  }
@@ -622,7 +1420,7 @@ function parseUbxRawx(data) {
622
1420
  if (!codes.includes(sig[1])) codes.push(sig[1]);
623
1421
  }
624
1422
  epochs.push({
625
- timeMs: GPS_EPOCH_MS3 + week * MS_PER_WEEK + Math.round(rcvTow * 1e3),
1423
+ timeMs: GPS_EPOCH_MS6 + week * MS_PER_WEEK2 + Math.round(rcvTow * 1e3),
626
1424
  leapS: leapValid ? leapS : null,
627
1425
  meas
628
1426
  });
@@ -635,6 +1433,7 @@ function parseUbxRawx(data) {
635
1433
  parseUbxCnav,
636
1434
  parseUbxIonoUtc,
637
1435
  parseUbxNav,
1436
+ parseUbxRawNav,
638
1437
  parseUbxRawx,
639
1438
  ubxFrames
640
1439
  });