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 CHANGED
@@ -6,8 +6,8 @@ import {
6
6
  decodeBinexEph,
7
7
  getBnxi,
8
8
  parseBinex
9
- } from "./chunk-OK4GGNOO.js";
10
- import "./chunk-IQ5OIMQD.js";
9
+ } from "./chunk-D25WNE43.js";
10
+ import "./chunk-V2D5KUKH.js";
11
11
  export {
12
12
  binexCrc16,
13
13
  binexCrc32,
@@ -7,16 +7,21 @@ import {
7
7
  galFnavPageCrcOk,
8
8
  galInavPageCrcOk,
9
9
  testGloString
10
- } from "./chunk-5AUK42AL.js";
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-726LQBGM.js";
18
+ } from "./chunk-YUW33VXK.js";
16
19
  import {
20
+ GpsLnavAssembler,
17
21
  getBitS,
18
- getBitU
19
- } from "./chunk-IQ5OIMQD.js";
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 SIGNAL_OF_BLOCK = {
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 assemblers = {
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
- const signal = SIGNAL_OF_BLOCK[id];
442
- if (!signal || len < 60) return;
552
+ if (!CNAV_SIGNAL_OF_BLOCK[id] || len < 60) return;
443
553
  messages++;
444
- const msg = new Uint8Array(40);
445
- for (let k = 0; k < 10; k++) {
446
- const w = view.getUint32(b + 20 + 4 * k, true);
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 inav = new GalInavAssembler();
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 prnStr = svidToPrn(data[b + 14]);
476
- if (!prnStr || prnStr[0] !== "E") return;
477
- const prn = parseInt(prnStr.slice(1), 10);
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 WNC_DNU = 65535;
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 prn = svidToPrn(view.getUint8(b + 14));
534
- if (!prn || prn[0] !== "C") return;
535
- const sf = new Uint8Array(38);
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 prn = svidToPrn(view.getUint8(b + 14));
566
- if (!prn || prn[0] !== "R") return;
567
- const towMs = view.getUint32(b + 8, true);
568
- const wnc = view.getUint16(b + 12, true);
569
- if (towMs === TOW_DNU || wnc === WNC_DNU) return;
570
- const str = new Uint8Array(11);
571
- for (let k = 0; k < 3; k++) {
572
- const w = view.getUint32(b + 20 + 4 * k, true);
573
- str[4 * k] = w >>> 24;
574
- if (k < 2) {
575
- str[4 * k + 1] = w >>> 16 & 255;
576
- str[4 * k + 2] = w >>> 8 & 255;
577
- str[4 * k + 3] = w & 255;
578
- } else {
579
- str[9] = w >>> 16 & 255;
580
- str[10] = w >>> 8 & 248;
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
- if (!testGloString(str)) {
584
- badCrc++;
585
- return;
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, badCrc, messages };
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
  };
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  CnavAssembler,
3
3
  cnavCrcOk
4
- } from "./chunk-726LQBGM.js";
4
+ } from "./chunk-YUW33VXK.js";
5
5
  import {
6
6
  decodeGpsLnavFrame
7
- } from "./chunk-IQ5OIMQD.js";
7
+ } from "./chunk-V2D5KUKH.js";
8
8
  import {
9
9
  getUtcDate
10
10
  } from "./chunk-HVXYFUCB.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  CNAV_A_REF,
3
3
  CNAV_OMEGA_DOT_REF
4
- } from "./chunk-726LQBGM.js";
4
+ } from "./chunk-YUW33VXK.js";
5
5
  import {
6
6
  GPS_PI
7
- } from "./chunk-IQ5OIMQD.js";
7
+ } from "./chunk-V2D5KUKH.js";
8
8
  import {
9
9
  C_LIGHT
10
10
  } from "./chunk-FIEWO4J4.js";
@@ -2,7 +2,7 @@ import {
2
2
  GPS_PI,
3
3
  getBitS,
4
4
  getBitU
5
- } from "./chunk-IQ5OIMQD.js";
5
+ } from "./chunk-V2D5KUKH.js";
6
6
 
7
7
  // src/binex/frame.ts
8
8
  var CRC16_TABLE = (() => {
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  crc24q
3
- } from "./chunk-726LQBGM.js";
3
+ } from "./chunk-YUW33VXK.js";
4
4
  import {
5
5
  GPS_PI,
6
6
  getBitS,
7
7
  getBitU
8
- } from "./chunk-IQ5OIMQD.js";
8
+ } from "./chunk-V2D5KUKH.js";
9
9
  import {
10
10
  getUtcDate
11
11
  } from "./chunk-HVXYFUCB.js";
@@ -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
  };
@@ -2,7 +2,7 @@ import {
2
2
  GPS_PI,
3
3
  getBitS,
4
4
  getBitU
5
- } from "./chunk-IQ5OIMQD.js";
5
+ } from "./chunk-V2D5KUKH.js";
6
6
 
7
7
  // src/navbits/cnav.ts
8
8
  var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);