gnss-js 1.35.0 → 1.37.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,18 @@ import {
7
7
  galFnavPageCrcOk,
8
8
  galInavPageCrcOk,
9
9
  testGloString
10
- } from "./chunk-5AUK42AL.js";
10
+ } from "./chunk-FA2UTLEQ.js";
11
11
  import {
12
12
  CnavAssembler,
13
13
  cnavCrcOk,
14
14
  crc24q
15
- } from "./chunk-726LQBGM.js";
15
+ } from "./chunk-YUW33VXK.js";
16
16
  import {
17
+ GpsLnavAssembler,
17
18
  getBitS,
18
- getBitU
19
- } from "./chunk-IQ5OIMQD.js";
19
+ getBitU,
20
+ setBitU
21
+ } from "./chunk-V2D5KUKH.js";
20
22
  import {
21
23
  getGpsLeap
22
24
  } from "./chunk-HVXYFUCB.js";
@@ -423,38 +425,78 @@ function parseSbfIonoUtc(data) {
423
425
  return { ionoCorrections, leapSeconds };
424
426
  }
425
427
 
428
+ // src/sbf/rawnav-gps.ts
429
+ var WNC_DNU = 65535;
430
+ function extractSbfLnavSubframe(view, b) {
431
+ const sf = new Uint8Array(30);
432
+ for (let k = 0; k < 10; k++) {
433
+ const w = view.getUint32(b + 20 + 4 * k, true);
434
+ setBitU(sf, 24 * k, 24, w >>> 6 & 16777215);
435
+ }
436
+ return sf;
437
+ }
438
+ function feedGpsLnavBlock(view, b, assembler) {
439
+ const prn = svidToPrn(view.getUint8(b + 14));
440
+ if (!prn || prn[0] !== "G" && prn[0] !== "J") return {};
441
+ if (view.getUint8(b + 15) !== 1) return {};
442
+ const wnc = view.getUint16(b + 12, true);
443
+ if (wnc === WNC_DNU) return {};
444
+ const r = assembler.push(prn, extractSbfLnavSubframe(view, b), wnc);
445
+ if (r.kind === "eph") return { eph: r.eph };
446
+ if (r.kind === "decodeFailed") return { badParity: true };
447
+ return {};
448
+ }
449
+ function parseSbfGpsNav(data) {
450
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
451
+ const ephemerides = [];
452
+ const assembler = new GpsLnavAssembler();
453
+ let badParity = 0;
454
+ let messages = 0;
455
+ scanSbfFrames(data, view, (id, b, len) => {
456
+ if (id !== 4017 && id !== 4066 || len < 60) return;
457
+ messages++;
458
+ const r = feedGpsLnavBlock(view, b, assembler);
459
+ if (r.eph) ephemerides.push(r.eph);
460
+ else if (r.badParity) badParity++;
461
+ });
462
+ return { ephemerides, badParity, messages };
463
+ }
464
+
426
465
  // src/sbf/rawnav.ts
427
- var SIGNAL_OF_BLOCK = {
466
+ var CNAV_SIGNAL_OF_BLOCK = {
428
467
  4018: "L2C",
429
468
  4019: "L5"
430
469
  };
470
+ function newCnavAssemblers() {
471
+ return { L2C: new CnavAssembler(), L5: new CnavAssembler() };
472
+ }
473
+ function feedCnavBlock(view, b, id, asm) {
474
+ const signal = CNAV_SIGNAL_OF_BLOCK[id];
475
+ if (!signal) return {};
476
+ const msg = new Uint8Array(40);
477
+ for (let k = 0; k < 10; k++) {
478
+ const w = view.getUint32(b + 20 + 4 * k, true);
479
+ msg[4 * k] = w >>> 24;
480
+ msg[4 * k + 1] = w >>> 16 & 255;
481
+ msg[4 * k + 2] = w >>> 8 & 255;
482
+ msg[4 * k + 3] = w & 255;
483
+ }
484
+ if (!cnavCrcOk(msg)) return { badCrc: true };
485
+ const eph = asm[signal].push(msg);
486
+ return eph ? { eph: { ...eph, signal } } : {};
487
+ }
431
488
  function parseSbfCnav(data) {
432
489
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
433
490
  const ephemerides = [];
434
- const assemblers = {
435
- L2C: new CnavAssembler(),
436
- L5: new CnavAssembler()
437
- };
491
+ const asm = newCnavAssemblers();
438
492
  let badCrc = 0;
439
493
  let messages = 0;
440
494
  scanSbfFrames(data, view, (id, b, len) => {
441
- const signal = SIGNAL_OF_BLOCK[id];
442
- if (!signal || len < 60) return;
495
+ if (!CNAV_SIGNAL_OF_BLOCK[id] || len < 60) return;
443
496
  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 });
497
+ const r = feedCnavBlock(view, b, id, asm);
498
+ if (r.eph) ephemerides.push(r.eph);
499
+ else if (r.badCrc) badCrc++;
458
500
  });
459
501
  return { ephemerides, badCrc, messages };
460
502
  }
@@ -462,50 +504,51 @@ function parseSbfCnav(data) {
462
504
  // src/sbf/rawnav-gal.ts
463
505
  var INAV_SOURCES = [17, 21, 22];
464
506
  var FNAV_SOURCES = [20, 22];
507
+ function newGalAssemblers() {
508
+ return { inav: new GalInavAssembler(), fnav: new GalFnavAssembler() };
509
+ }
510
+ function feedGalBlock(data, view, b, id, asm) {
511
+ const prnStr = svidToPrn(data[b + 14]);
512
+ if (!prnStr || prnStr[0] !== "E") return {};
513
+ const prn = parseInt(prnStr.slice(1), 10);
514
+ const source = data[b + 17] & 31;
515
+ const page = new Uint8Array(32);
516
+ for (let k = 0; k < 8; k++) {
517
+ const w = view.getUint32(b + 20 + 4 * k, true);
518
+ page[4 * k] = w >>> 24;
519
+ page[4 * k + 1] = w >>> 16 & 255;
520
+ page[4 * k + 2] = w >>> 8 & 255;
521
+ page[4 * k + 3] = w & 255;
522
+ }
523
+ let eph;
524
+ let src;
525
+ if (id === 4023) {
526
+ src = "inav";
527
+ if (!INAV_SOURCES.includes(source)) return {};
528
+ if (getBitU2(page, 0) !== 0 || getBitU2(page, 114) !== 1) return {};
529
+ if (!galInavPageCrcOk(page)) return { badCrc: true };
530
+ eph = asm.inav.push(prn, page);
531
+ } else {
532
+ src = "fnav";
533
+ if (!FNAV_SOURCES.includes(source)) return {};
534
+ if (isFnavDummy(page)) return {};
535
+ if (!galFnavPageCrcOk(page)) return { badCrc: true };
536
+ eph = asm.fnav.push(prn, page);
537
+ }
538
+ return eph ? { eph: { ...eph, source: src } } : {};
539
+ }
465
540
  function parseSbfGalNav(data) {
466
541
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
467
542
  const ephemerides = [];
468
- const inav = new GalInavAssembler();
469
- const fnav = new GalFnavAssembler();
543
+ const asm = newGalAssemblers();
470
544
  let badCrc = 0;
471
545
  let messages = 0;
472
546
  scanSbfFrames(data, view, (id, b, len) => {
473
547
  if (id !== 4022 && id !== 4023 || len < 52) return;
474
548
  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 });
549
+ const r = feedGalBlock(data, view, b, id, asm);
550
+ if (r.eph) ephemerides.push(r.eph);
551
+ else if (r.badCrc) badCrc++;
509
552
  });
510
553
  return { ephemerides, badCrc, messages };
511
554
  }
@@ -520,7 +563,26 @@ function isFnavDummy(page) {
520
563
  var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
521
564
  var MS_PER_WEEK2 = 7 * 86400 * 1e3;
522
565
  var TOW_DNU = 4294967295;
523
- var WNC_DNU = 65535;
566
+ var WNC_DNU2 = 65535;
567
+ function feedBdsBlock(view, b, assembler) {
568
+ const prn = svidToPrn(view.getUint8(b + 14));
569
+ if (!prn || prn[0] !== "C") return {};
570
+ const sf = new Uint8Array(38);
571
+ for (let k = 0; k < 10; k++) {
572
+ const w = view.getUint32(b + 20 + 4 * k, true);
573
+ sf[4 * k] = w >>> 24;
574
+ if (k < 9) {
575
+ sf[4 * k + 1] = w >>> 16 & 255;
576
+ sf[4 * k + 2] = w >>> 8 & 255;
577
+ sf[4 * k + 3] = w & 255;
578
+ } else {
579
+ sf[37] = w >>> 16 & 240;
580
+ }
581
+ }
582
+ if (!bdsSubframeParityOk(sf)) return { badCrc: true };
583
+ const eph = assembler.push(prn, sf);
584
+ return eph ? { eph } : {};
585
+ }
524
586
  function parseSbfBdsNav(data) {
525
587
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
526
588
  const ephemerides = [];
@@ -530,29 +592,41 @@ function parseSbfBdsNav(data) {
530
592
  scanSbfFrames(data, view, (id, b, len) => {
531
593
  if (id !== 4047 || len < 60) return;
532
594
  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);
595
+ const r = feedBdsBlock(view, b, assembler);
596
+ if (r.eph) ephemerides.push(r.eph);
597
+ else if (r.badCrc) badCrc++;
553
598
  });
554
599
  return { ephemerides, badCrc, messages };
555
600
  }
601
+ function feedGloBlock(view, b, assembler) {
602
+ const prn = svidToPrn(view.getUint8(b + 14));
603
+ if (!prn || prn[0] !== "R") return {};
604
+ const towMs = view.getUint32(b + 8, true);
605
+ const wnc = view.getUint16(b + 12, true);
606
+ if (towMs === TOW_DNU || wnc === WNC_DNU2) return {};
607
+ const str = new Uint8Array(11);
608
+ for (let k = 0; k < 3; k++) {
609
+ const w = view.getUint32(b + 20 + 4 * k, true);
610
+ str[4 * k] = w >>> 24;
611
+ if (k < 2) {
612
+ str[4 * k + 1] = w >>> 16 & 255;
613
+ str[4 * k + 2] = w >>> 8 & 255;
614
+ str[4 * k + 3] = w & 255;
615
+ } else {
616
+ str[9] = w >>> 16 & 255;
617
+ str[10] = w >>> 8 & 248;
618
+ }
619
+ }
620
+ if (!testGloString(str)) return { badCrc: true };
621
+ const eph = assembler.push(
622
+ prn,
623
+ str,
624
+ new Date(GPS_EPOCH_MS2 + wnc * MS_PER_WEEK2 + towMs),
625
+ view.getUint8(b + 18) - 8
626
+ // FreqNr, offset by 8
627
+ );
628
+ return eph ? { eph } : {};
629
+ }
556
630
  function parseSbfGloNav(data) {
557
631
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
558
632
  const ephemerides = [];
@@ -562,38 +636,161 @@ function parseSbfGloNav(data) {
562
636
  scanSbfFrames(data, view, (id, b, len) => {
563
637
  if (id !== 4026 || len < 32) return;
564
638
  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
- }
639
+ const r = feedGloBlock(view, b, assembler);
640
+ if (r.eph) ephemerides.push(r.eph);
641
+ else if (r.badCrc) badCrc++;
642
+ });
643
+ return { ephemerides, badCrc, messages };
644
+ }
645
+
646
+ // src/sbf/navigation.ts
647
+ var F4_DNU3 = -2e10;
648
+ function ephKey(e) {
649
+ const source = "source" in e ? e.source : "";
650
+ return `${e.prn}|${e.tocDate.getTime()}|${source}`;
651
+ }
652
+ function decodeSbfNavigation(data) {
653
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
654
+ const ephemerides = [];
655
+ const seen = /* @__PURE__ */ new Set();
656
+ const cnav = [];
657
+ const ionoCorrections = {};
658
+ let leapSeconds = null;
659
+ const counts = {
660
+ decodedNav: 0,
661
+ gpsLnavRaw: 0,
662
+ cnavRaw: 0,
663
+ galRaw: 0,
664
+ gloRaw: 0,
665
+ bdsRaw: 0,
666
+ badFrames: 0
667
+ };
668
+ const cnavAsm = newCnavAssemblers();
669
+ const galAsm = newGalAssemblers();
670
+ const gloAsm = new GloStringAssembler();
671
+ const bdsAsm = new BdsAssembler();
672
+ const lnavAsm = new GpsLnavAssembler();
673
+ const addEph = (e) => {
674
+ if (!e) return;
675
+ const key = ephKey(e);
676
+ if (seen.has(key)) return;
677
+ seen.add(key);
678
+ ephemerides.push(e);
679
+ };
680
+ const f4s = (b, n) => {
681
+ const out = [];
682
+ for (let k = 0; k < n; k++) {
683
+ const v = view.getFloat32(b + 4 * k, true);
684
+ if (v === F4_DNU3) return null;
685
+ out.push(v);
582
686
  }
583
- if (!testGloString(str)) {
584
- badCrc++;
585
- return;
687
+ return out;
688
+ };
689
+ scanSbfFrames(data, view, (id, b, len) => {
690
+ switch (id) {
691
+ /* ---- decoded ephemeris blocks ---- */
692
+ case 5891:
693
+ if (len >= 140) {
694
+ counts.decodedNav++;
695
+ addEph(decodeGpsQzsNav(view, b, "G"));
696
+ }
697
+ return;
698
+ case 4095:
699
+ if (len >= 140) {
700
+ counts.decodedNav++;
701
+ addEph(decodeGpsQzsNav(view, b, "J"));
702
+ }
703
+ return;
704
+ case 4002:
705
+ if (len >= 149) {
706
+ counts.decodedNav++;
707
+ addEph(decodeGalNav(view, b));
708
+ }
709
+ return;
710
+ case 4081:
711
+ if (len >= 140) {
712
+ counts.decodedNav++;
713
+ addEph(decodeBdsNav(view, b));
714
+ }
715
+ return;
716
+ case 4004:
717
+ if (len >= 96) {
718
+ counts.decodedNav++;
719
+ addEph(decodeGloNav(view, b));
720
+ }
721
+ return;
722
+ /* ---- raw navigation-frame blocks ---- */
723
+ case 4017:
724
+ // GPSRawCA
725
+ case 4066: {
726
+ if (len < 60) return;
727
+ counts.gpsLnavRaw++;
728
+ const r = feedGpsLnavBlock(view, b, lnavAsm);
729
+ if (r.eph) addEph(r.eph);
730
+ else if (r.badParity) counts.badFrames++;
731
+ return;
732
+ }
733
+ case 4018:
734
+ // GPSRawL2C
735
+ case 4019: {
736
+ if (len < 60) return;
737
+ counts.cnavRaw++;
738
+ const r = feedCnavBlock(view, b, id, cnavAsm);
739
+ if (r.eph) cnav.push(r.eph);
740
+ else if (r.badCrc) counts.badFrames++;
741
+ return;
742
+ }
743
+ case 4022:
744
+ // GALRawFNAV
745
+ case 4023: {
746
+ if (len < 52) return;
747
+ counts.galRaw++;
748
+ const r = feedGalBlock(data, view, b, id, galAsm);
749
+ if (r.eph) addEph(r.eph);
750
+ else if (r.badCrc) counts.badFrames++;
751
+ return;
752
+ }
753
+ case 4026: {
754
+ if (len < 32) return;
755
+ counts.gloRaw++;
756
+ const r = feedGloBlock(view, b, gloAsm);
757
+ if (r.eph) addEph(r.eph);
758
+ else if (r.badCrc) counts.badFrames++;
759
+ return;
760
+ }
761
+ case 4047: {
762
+ if (len < 60) return;
763
+ counts.bdsRaw++;
764
+ const r = feedBdsBlock(view, b, bdsAsm);
765
+ if (r.eph) addEph(r.eph);
766
+ else if (r.badCrc) counts.badFrames++;
767
+ return;
768
+ }
769
+ /* ---- iono / UTC ---- */
770
+ case 5893:
771
+ // GPSIon
772
+ case 4120: {
773
+ if (len < 48) return;
774
+ const alpha = f4s(b + 16, 4);
775
+ const beta = f4s(b + 32, 4);
776
+ if (!alpha || !beta) return;
777
+ const sys = id === 5893 ? "GPS" : "BDS";
778
+ ionoCorrections[`${sys}A`] = alpha;
779
+ ionoCorrections[`${sys}B`] = beta;
780
+ return;
781
+ }
782
+ case 4030: {
783
+ if (len < 28) return;
784
+ const ai = f4s(b + 16, 3);
785
+ if (ai) ionoCorrections["GAL"] = ai;
786
+ return;
787
+ }
788
+ case 5894:
789
+ if (len >= 37) leapSeconds = view.getInt8(b + 33);
790
+ return;
586
791
  }
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
792
  });
596
- return { ephemerides, badCrc, messages };
793
+ return { ephemerides, cnav, ionoCorrections, leapSeconds, counts };
597
794
  }
598
795
 
599
796
  // src/navbits/rs255.ts
@@ -1793,10 +1990,12 @@ export {
1793
1990
  parseSbfNav,
1794
1991
  parseSbfAlmanac,
1795
1992
  parseSbfIonoUtc,
1993
+ parseSbfGpsNav,
1796
1994
  parseSbfCnav,
1797
1995
  parseSbfGalNav,
1798
1996
  parseSbfBdsNav,
1799
1997
  parseSbfGloNav,
1998
+ decodeSbfNavigation,
1800
1999
  parseSbfHas,
1801
2000
  parseSbfMeas
1802
2001
  };
@@ -1959,6 +1959,28 @@ function solvePpp(epochs, sp3, opts) {
1959
1959
  phaseResRms: nPhaseRes > 0 ? Math.sqrt(sumPhaseResSq / nPhaseRes) : 0
1960
1960
  });
1961
1961
  }
1962
+ let finalState;
1963
+ if (opts.exposeState) {
1964
+ const ambs = [];
1965
+ for (const [prn, index] of ambIdx) {
1966
+ const s = arcSnap.get(prn);
1967
+ if (!s || s.nEpochs < 10) continue;
1968
+ ambs.push({
1969
+ prn,
1970
+ index,
1971
+ aIF: x[index],
1972
+ mwCyc: mwState.get(prn)?.mean ?? s.mwCyc,
1973
+ f1: s.f1,
1974
+ f2: s.f2,
1975
+ elevDeg: s.elevSum / s.nEpochs
1976
+ });
1977
+ }
1978
+ finalState = {
1979
+ position: [x[IDX_X], x[IDX_Y], x[IDX_Z]],
1980
+ covariance: P.map((row) => row.slice()),
1981
+ ambiguities: ambs
1982
+ };
1983
+ }
1962
1984
  for (const prn of [...arcSnap.keys()]) flushArc(prn);
1963
1985
  const finalPos = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
1964
1986
  const llh = ecefToGeodetic(finalPos[0], finalPos[1], finalPos[2]);
@@ -1975,7 +1997,8 @@ function solvePpp(epochs, sp3, opts) {
1975
1997
  epochsUsed: epochs.length,
1976
1998
  convergenceSec,
1977
1999
  finalError3d,
1978
- arcs
2000
+ arcs,
2001
+ finalState
1979
2002
  };
1980
2003
  }
1981
2004
 
@@ -2068,6 +2091,117 @@ function resolvePppAmbiguities(sats, Qaif, opts = {}) {
2068
2091
  rejectedWl
2069
2092
  };
2070
2093
  }
2094
+ function solveSpd(A, b) {
2095
+ const n = b.length;
2096
+ const L = Array.from({ length: n }, () => new Array(n).fill(0));
2097
+ for (let i = 0; i < n; i++) {
2098
+ for (let j = 0; j <= i; j++) {
2099
+ let s = A[i][j];
2100
+ for (let k = 0; k < j; k++) s -= L[i][k] * L[j][k];
2101
+ if (i === j) {
2102
+ if (s <= 0) return null;
2103
+ L[i][j] = Math.sqrt(s);
2104
+ } else {
2105
+ L[i][j] = s / L[j][j];
2106
+ }
2107
+ }
2108
+ }
2109
+ const y = new Array(n).fill(0);
2110
+ for (let i = 0; i < n; i++) {
2111
+ let s = b[i];
2112
+ for (let k = 0; k < i; k++) s -= L[i][k] * y[k];
2113
+ y[i] = s / L[i][i];
2114
+ }
2115
+ const x = new Array(n).fill(0);
2116
+ for (let i = n - 1; i >= 0; i--) {
2117
+ let s = y[i];
2118
+ for (let k = i + 1; k < n; k++) s -= L[k][i] * x[k];
2119
+ x[i] = s / L[i][i];
2120
+ }
2121
+ return x;
2122
+ }
2123
+ function fixPppPosition(state, fcb, opts = {}) {
2124
+ const floatPos = state.position;
2125
+ const noFix = {
2126
+ fixed: false,
2127
+ position: floatPos,
2128
+ floatPosition: floatPos,
2129
+ shift: [0, 0, 0],
2130
+ nFixed: 0,
2131
+ ratio: 0,
2132
+ refPrn: null
2133
+ };
2134
+ const amb = state.ambiguities;
2135
+ if (amb.length < 3) return noFix;
2136
+ const sats = amb.map((a) => ({
2137
+ prn: a.prn,
2138
+ aIF: a.aIF,
2139
+ mwCyc: a.mwCyc,
2140
+ f1: a.f1,
2141
+ f2: a.f2,
2142
+ wlBiasCyc: fcb.satWlFcb.get(a.prn) ?? 0,
2143
+ nlBiasCyc: fcb.satNlFcb.get(a.prn) ?? 0,
2144
+ elevDeg: a.elevDeg
2145
+ }));
2146
+ const n = sats.length;
2147
+ const Qaif = new Float64Array(n * n);
2148
+ for (let i = 0; i < n; i++)
2149
+ for (let j = 0; j < n; j++)
2150
+ Qaif[i * n + j] = state.covariance[amb[i].index][amb[j].index];
2151
+ const res = resolvePppAmbiguities(sats, Qaif, opts);
2152
+ if (!res.fixed || res.refPrn === null) {
2153
+ return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
2154
+ }
2155
+ const idxOfPrn = new Map(amb.map((a) => [a.prn, a.index]));
2156
+ const floatOfPrn = new Map(amb.map((a) => [a.prn, a.aIF]));
2157
+ const kept = res.sats.filter((s) => idxOfPrn.has(s.prn));
2158
+ const refPos = kept.findIndex((s) => s.prn === res.refPrn);
2159
+ if (refPos < 0 || kept.length < 2) {
2160
+ return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
2161
+ }
2162
+ const ambIdx = kept.map((s) => idxOfPrn.get(s.prn));
2163
+ const aFloat = kept.map((s) => floatOfPrn.get(s.prn));
2164
+ const aFixed = kept.map((s) => s.aIF);
2165
+ const others = kept.map((_, i) => i).filter((i) => i !== refPos);
2166
+ const m = others.length;
2167
+ const P = state.covariance;
2168
+ const qAA = (i, j) => P[ambIdx[i]][ambIdx[j]];
2169
+ const qXA = (r, i) => P[r][ambIdx[i]];
2170
+ const Qzz = Array.from({ length: m }, () => new Array(m).fill(0));
2171
+ const z = new Array(m).fill(0);
2172
+ for (let a = 0; a < m; a++) {
2173
+ const ia = others[a];
2174
+ z[a] = aFloat[ia] - aFloat[refPos] - (aFixed[ia] - aFixed[refPos]);
2175
+ for (let b = 0; b < m; b++) {
2176
+ const ib = others[b];
2177
+ Qzz[a][b] = qAA(ia, ib) - qAA(ia, refPos) - qAA(refPos, ib) + qAA(refPos, refPos);
2178
+ }
2179
+ }
2180
+ const Qxz = [0, 1, 2].map(
2181
+ (r) => others.map((ia) => qXA(r, ia) - qXA(r, refPos))
2182
+ );
2183
+ const y = solveSpd(Qzz, z);
2184
+ if (!y) return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
2185
+ const shift = [0, 0, 0];
2186
+ for (let r = 0; r < 3; r++) {
2187
+ let s = 0;
2188
+ for (let a = 0; a < m; a++) s += Qxz[r][a] * y[a];
2189
+ shift[r] = -s;
2190
+ }
2191
+ return {
2192
+ fixed: true,
2193
+ position: [
2194
+ floatPos[0] + shift[0],
2195
+ floatPos[1] + shift[1],
2196
+ floatPos[2] + shift[2]
2197
+ ],
2198
+ floatPosition: floatPos,
2199
+ shift,
2200
+ nFixed: m,
2201
+ ratio: res.ratio,
2202
+ refPrn: res.refPrn
2203
+ };
2204
+ }
2071
2205
 
2072
2206
  // src/positioning/ppp-fcb.ts
2073
2207
  function extractWidelaneArcs(station, epochs, opts = {}) {
@@ -2504,6 +2638,7 @@ export {
2504
2638
  wlWavelength,
2505
2639
  nlWavelength,
2506
2640
  resolvePppAmbiguities,
2641
+ fixPppPosition,
2507
2642
  extractWidelaneArcs,
2508
2643
  estimateWidelaneFcb,
2509
2644
  estimateNarrowlaneFcb,
@@ -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";