gnss-js 2.3.0 → 2.5.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/{chunk-WW3DKCDN.js → chunk-74IVYOQV.js} +187 -6
- package/dist/{chunk-XPQ4BU6W.js → chunk-7GNRNKBR.js} +66 -0
- package/dist/{chunk-TYJNN77C.js → chunk-B2XWO4RP.js} +62 -4
- package/dist/index.cjs +356 -49
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -3
- package/dist/positioning.cjs +62 -4
- package/dist/positioning.d.cts +26 -2
- package/dist/positioning.d.ts +26 -2
- package/dist/positioning.js +1 -1
- package/dist/rtcm3.cjs +66 -0
- package/dist/rtcm3.js +1 -1
- package/dist/sbf.cjs +206 -23
- package/dist/sbf.d.cts +62 -1
- package/dist/sbf.d.ts +62 -1
- package/dist/sbf.js +7 -1
- package/package.json +1 -1
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
crc24q
|
|
25
25
|
} from "./chunk-YUW33VXK.js";
|
|
26
26
|
import {
|
|
27
|
+
GPS_PI,
|
|
27
28
|
GpsLnavAssembler,
|
|
28
29
|
getBitS,
|
|
29
30
|
getBitU,
|
|
@@ -575,6 +576,173 @@ function parseSbfGpsNav(data) {
|
|
|
575
576
|
return { ephemerides, badParity, messages };
|
|
576
577
|
}
|
|
577
578
|
|
|
579
|
+
// src/navbits/navic.ts
|
|
580
|
+
var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
|
|
581
|
+
var SEC_PER_WEEK2 = 604800;
|
|
582
|
+
var DATA = 30;
|
|
583
|
+
function navicSubframeCrcOk(sf) {
|
|
584
|
+
return crc24q(sf, 262) === getBitU(sf, 262, 24);
|
|
585
|
+
}
|
|
586
|
+
function navicSubframeId(sf) {
|
|
587
|
+
return getBitU(sf, 27, 2) + 1;
|
|
588
|
+
}
|
|
589
|
+
function decodeNavicSubframes(sf1, sf2, opts = {}) {
|
|
590
|
+
let i = DATA;
|
|
591
|
+
getBitU(sf1, i, 10);
|
|
592
|
+
i += 10;
|
|
593
|
+
const af0 = getBitS(sf1, i, 22) * 2 ** -31;
|
|
594
|
+
i += 22;
|
|
595
|
+
const af1 = getBitS(sf1, i, 16) * 2 ** -43;
|
|
596
|
+
i += 16;
|
|
597
|
+
const af2 = getBitS(sf1, i, 8) * 2 ** -55;
|
|
598
|
+
i += 8;
|
|
599
|
+
i += 4;
|
|
600
|
+
const tocSec = getBitU(sf1, i, 16) * 16;
|
|
601
|
+
i += 16;
|
|
602
|
+
const tgd = getBitS(sf1, i, 8) * 2 ** -31;
|
|
603
|
+
i += 8;
|
|
604
|
+
const deltaN = getBitS(sf1, i, 22) * 2 ** -41 * GPS_PI;
|
|
605
|
+
i += 22;
|
|
606
|
+
const iode = getBitU(sf1, i, 8);
|
|
607
|
+
i += 8;
|
|
608
|
+
i += 10;
|
|
609
|
+
const l5Health = getBitU(sf1, i, 1);
|
|
610
|
+
i += 1;
|
|
611
|
+
const sHealth = getBitU(sf1, i, 1);
|
|
612
|
+
i += 1;
|
|
613
|
+
const cuc = getBitS(sf1, i, 15) * 2 ** -28;
|
|
614
|
+
i += 15;
|
|
615
|
+
const cus = getBitS(sf1, i, 15) * 2 ** -28;
|
|
616
|
+
i += 15;
|
|
617
|
+
const cic = getBitS(sf1, i, 15) * 2 ** -28;
|
|
618
|
+
i += 15;
|
|
619
|
+
const cis = getBitS(sf1, i, 15) * 2 ** -28;
|
|
620
|
+
i += 15;
|
|
621
|
+
const crc = getBitS(sf1, i, 15) * 2 ** -4;
|
|
622
|
+
i += 15;
|
|
623
|
+
const crs = getBitS(sf1, i, 15) * 2 ** -4;
|
|
624
|
+
i += 15;
|
|
625
|
+
const idot = getBitS(sf1, i, 14) * 2 ** -43 * GPS_PI;
|
|
626
|
+
let j = DATA;
|
|
627
|
+
const m0 = getBitS(sf2, j, 32) * 2 ** -31 * GPS_PI;
|
|
628
|
+
j += 32;
|
|
629
|
+
const toeSec = getBitU(sf2, j, 16) * 16;
|
|
630
|
+
j += 16;
|
|
631
|
+
const e = getBitU(sf2, j, 32) * 2 ** -33;
|
|
632
|
+
j += 32;
|
|
633
|
+
const sqrtA = getBitU(sf2, j, 32) * 2 ** -19;
|
|
634
|
+
j += 32;
|
|
635
|
+
const omega0 = getBitS(sf2, j, 32) * 2 ** -31 * GPS_PI;
|
|
636
|
+
j += 32;
|
|
637
|
+
const omega = getBitS(sf2, j, 32) * 2 ** -31 * GPS_PI;
|
|
638
|
+
j += 32;
|
|
639
|
+
const omegaDot = getBitS(sf2, j, 22) * 2 ** -41 * GPS_PI;
|
|
640
|
+
j += 22;
|
|
641
|
+
const i0 = getBitS(sf2, j, 32) * 2 ** -31 * GPS_PI;
|
|
642
|
+
if (sqrtA <= 0 || e >= 1) return null;
|
|
643
|
+
const week = opts.refWeek ?? Math.floor((Date.now() - GPS_EPOCH_MS3) / 1e3 / SEC_PER_WEEK2);
|
|
644
|
+
const prn = opts.prn ?? "I00";
|
|
645
|
+
const tocDate = new Date(
|
|
646
|
+
GPS_EPOCH_MS3 + (week * SEC_PER_WEEK2 + tocSec) * 1e3
|
|
647
|
+
);
|
|
648
|
+
return {
|
|
649
|
+
system: "I",
|
|
650
|
+
prn,
|
|
651
|
+
tocDate,
|
|
652
|
+
toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK2,
|
|
653
|
+
af0,
|
|
654
|
+
af1,
|
|
655
|
+
af2,
|
|
656
|
+
iode,
|
|
657
|
+
crs,
|
|
658
|
+
deltaN,
|
|
659
|
+
m0,
|
|
660
|
+
cuc,
|
|
661
|
+
e,
|
|
662
|
+
cus,
|
|
663
|
+
sqrtA,
|
|
664
|
+
toe: toeSec,
|
|
665
|
+
cic,
|
|
666
|
+
omega0,
|
|
667
|
+
cis,
|
|
668
|
+
i0,
|
|
669
|
+
crc,
|
|
670
|
+
omega,
|
|
671
|
+
omegaDot,
|
|
672
|
+
idot,
|
|
673
|
+
week,
|
|
674
|
+
svHealth: l5Health << 1 | sHealth,
|
|
675
|
+
tgd
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
var NavicAssembler = class {
|
|
679
|
+
sf1 = /* @__PURE__ */ new Map();
|
|
680
|
+
last = /* @__PURE__ */ new Map();
|
|
681
|
+
/**
|
|
682
|
+
* Push one CRC-valid subframe. Subframe 1 opens a master frame (subframe 2
|
|
683
|
+
* immediately follows it in the NavIC frame order), so an ephemeris is only
|
|
684
|
+
* emitted when a subframe 2 pairs with the subframe 1 that opened the *same*
|
|
685
|
+
* frame — never a stale subframe 1 left over across CRC drops.
|
|
686
|
+
*/
|
|
687
|
+
push(prn, subframe, refWeek) {
|
|
688
|
+
if (getBitU(subframe, 25, 1)) return null;
|
|
689
|
+
const id = navicSubframeId(subframe);
|
|
690
|
+
if (id === 1) {
|
|
691
|
+
this.sf1.set(prn, subframe);
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
if (id !== 2) return null;
|
|
695
|
+
const a = this.sf1.get(prn);
|
|
696
|
+
if (!a) return null;
|
|
697
|
+
this.sf1.delete(prn);
|
|
698
|
+
const eph = decodeNavicSubframes(a, subframe, { prn, refWeek });
|
|
699
|
+
if (!eph) return null;
|
|
700
|
+
const key = `${eph.toc}|${eph.iode}`;
|
|
701
|
+
if (this.last.get(prn) === key) return null;
|
|
702
|
+
this.last.set(prn, key);
|
|
703
|
+
return eph;
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
// src/sbf/rawnav-navic.ts
|
|
708
|
+
var WNC_DNU2 = 65535;
|
|
709
|
+
function extractSbfNavicSubframe(view, b) {
|
|
710
|
+
const sf = new Uint8Array(40);
|
|
711
|
+
for (let k = 0; k < 10; k++) {
|
|
712
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
713
|
+
sf[4 * k] = w >>> 24;
|
|
714
|
+
sf[4 * k + 1] = w >>> 16 & 255;
|
|
715
|
+
sf[4 * k + 2] = w >>> 8 & 255;
|
|
716
|
+
sf[4 * k + 3] = w & 255;
|
|
717
|
+
}
|
|
718
|
+
return sf;
|
|
719
|
+
}
|
|
720
|
+
function feedNavicBlock(view, b, assembler) {
|
|
721
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
722
|
+
if (!prn || prn[0] !== "I") return {};
|
|
723
|
+
const wnc = view.getUint16(b + 12, true);
|
|
724
|
+
if (wnc === WNC_DNU2) return {};
|
|
725
|
+
const sf = extractSbfNavicSubframe(view, b);
|
|
726
|
+
if (!navicSubframeCrcOk(sf)) return { badCrc: true };
|
|
727
|
+
const eph = assembler.push(prn, sf, wnc);
|
|
728
|
+
return eph ? { eph } : {};
|
|
729
|
+
}
|
|
730
|
+
function parseSbfNavic(data) {
|
|
731
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
732
|
+
const ephemerides = [];
|
|
733
|
+
const assembler = new NavicAssembler();
|
|
734
|
+
let badCrc = 0;
|
|
735
|
+
let messages = 0;
|
|
736
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
737
|
+
if (id !== 4093 || len < 60) return;
|
|
738
|
+
messages++;
|
|
739
|
+
const r = feedNavicBlock(view, b, assembler);
|
|
740
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
741
|
+
else if (r.badCrc) badCrc++;
|
|
742
|
+
});
|
|
743
|
+
return { ephemerides, badCrc, messages };
|
|
744
|
+
}
|
|
745
|
+
|
|
578
746
|
// src/sbf/rawnav.ts
|
|
579
747
|
var CNAV_SIGNAL_OF_BLOCK = {
|
|
580
748
|
4018: "L2C",
|
|
@@ -673,10 +841,10 @@ function isFnavDummy(page) {
|
|
|
673
841
|
}
|
|
674
842
|
|
|
675
843
|
// src/sbf/rawnav-bds.ts
|
|
676
|
-
var
|
|
844
|
+
var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
|
|
677
845
|
var MS_PER_WEEK2 = 7 * 86400 * 1e3;
|
|
678
846
|
var TOW_DNU = 4294967295;
|
|
679
|
-
var
|
|
847
|
+
var WNC_DNU3 = 65535;
|
|
680
848
|
function feedBdsBlock(view, b, assembler) {
|
|
681
849
|
const prn = svidToPrn(view.getUint8(b + 14));
|
|
682
850
|
if (!prn || prn[0] !== "C") return {};
|
|
@@ -716,7 +884,7 @@ function feedGloBlock(view, b, assembler) {
|
|
|
716
884
|
if (!prn || prn[0] !== "R") return {};
|
|
717
885
|
const towMs = view.getUint32(b + 8, true);
|
|
718
886
|
const wnc = view.getUint16(b + 12, true);
|
|
719
|
-
if (towMs === TOW_DNU || wnc ===
|
|
887
|
+
if (towMs === TOW_DNU || wnc === WNC_DNU3) return {};
|
|
720
888
|
const str2 = new Uint8Array(11);
|
|
721
889
|
for (let k = 0; k < 3; k++) {
|
|
722
890
|
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
@@ -734,7 +902,7 @@ function feedGloBlock(view, b, assembler) {
|
|
|
734
902
|
const eph = assembler.push(
|
|
735
903
|
prn,
|
|
736
904
|
str2,
|
|
737
|
-
new Date(
|
|
905
|
+
new Date(GPS_EPOCH_MS4 + wnc * MS_PER_WEEK2 + towMs),
|
|
738
906
|
view.getUint8(b + 18) - 8
|
|
739
907
|
// FreqNr, offset by 8
|
|
740
908
|
);
|
|
@@ -909,6 +1077,7 @@ function decodeSbfNavigation(data, opts = {}) {
|
|
|
909
1077
|
bdsRaw: 0,
|
|
910
1078
|
sbasRaw: 0,
|
|
911
1079
|
sbasL5Raw: 0,
|
|
1080
|
+
navicRaw: 0,
|
|
912
1081
|
badFrames: 0
|
|
913
1082
|
};
|
|
914
1083
|
const dfmcByType = {};
|
|
@@ -918,6 +1087,7 @@ function decodeSbfNavigation(data, opts = {}) {
|
|
|
918
1087
|
const gloAsm = new GloStringAssembler();
|
|
919
1088
|
const bdsAsm = new BdsAssembler();
|
|
920
1089
|
const lnavAsm = new GpsLnavAssembler();
|
|
1090
|
+
const navicAsm = new NavicAssembler();
|
|
921
1091
|
const addEph = (e) => {
|
|
922
1092
|
if (!e) return;
|
|
923
1093
|
const key = ephKey(e);
|
|
@@ -978,6 +1148,14 @@ function decodeSbfNavigation(data, opts = {}) {
|
|
|
978
1148
|
else if (r.badParity) counts.badFrames++;
|
|
979
1149
|
return;
|
|
980
1150
|
}
|
|
1151
|
+
case 4093: {
|
|
1152
|
+
if (len < 60) return;
|
|
1153
|
+
counts.navicRaw++;
|
|
1154
|
+
const r = feedNavicBlock(view, b, navicAsm);
|
|
1155
|
+
if (r.eph) addEph(r.eph);
|
|
1156
|
+
else if (r.badCrc) counts.badFrames++;
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
981
1159
|
case 4018:
|
|
982
1160
|
// GPSRawL2C
|
|
983
1161
|
case 4019: {
|
|
@@ -1598,7 +1776,7 @@ function parseSbfHas(data) {
|
|
|
1598
1776
|
|
|
1599
1777
|
// src/sbf/index.ts
|
|
1600
1778
|
var CLIGHT = 299792458;
|
|
1601
|
-
var
|
|
1779
|
+
var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
|
|
1602
1780
|
var MS_PER_WEEK3 = 7 * 864e5;
|
|
1603
1781
|
var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
|
|
1604
1782
|
var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
|
|
@@ -2252,7 +2430,7 @@ function parseSbfMeas(data) {
|
|
|
2252
2430
|
const tow = view.getUint32(i + 8, true);
|
|
2253
2431
|
const wnc = view.getUint16(i + 12, true);
|
|
2254
2432
|
if (tow !== 4294967295 && wnc !== 65535) {
|
|
2255
|
-
ensureEpoch(
|
|
2433
|
+
ensureEpoch(GPS_EPOCH_MS5 + wnc * MS_PER_WEEK3 + tow);
|
|
2256
2434
|
if (id === 4109) decodeMeas3Ranges(i, tow);
|
|
2257
2435
|
else if (id === 4110) decodeMeas3CN0(i, len);
|
|
2258
2436
|
else if (id === 4111) decodeMeas3Doppler(i, len);
|
|
@@ -2273,6 +2451,9 @@ export {
|
|
|
2273
2451
|
parseSbfReceiverSetup,
|
|
2274
2452
|
parseSbfPvt,
|
|
2275
2453
|
parseSbfGpsNav,
|
|
2454
|
+
extractSbfNavicSubframe,
|
|
2455
|
+
feedNavicBlock,
|
|
2456
|
+
parseSbfNavic,
|
|
2276
2457
|
parseSbfCnav,
|
|
2277
2458
|
parseSbfGalNav,
|
|
2278
2459
|
parseSbfBdsNav,
|
|
@@ -534,6 +534,70 @@ function decodeQzssEphemeris(payload) {
|
|
|
534
534
|
fitInterval
|
|
535
535
|
};
|
|
536
536
|
}
|
|
537
|
+
function decodeNavicEphemeris(payload) {
|
|
538
|
+
if (payload.length < 60) return null;
|
|
539
|
+
const r = new BitReader(payload);
|
|
540
|
+
r.skip(12);
|
|
541
|
+
const svId = r.readU(6);
|
|
542
|
+
const week = r.readU(10);
|
|
543
|
+
const af0 = r.readS(22) * 2 ** -31;
|
|
544
|
+
const af1 = r.readS(16) * 2 ** -43;
|
|
545
|
+
const af2 = r.readS(8) * 2 ** -55;
|
|
546
|
+
const ura = r.readU(4);
|
|
547
|
+
const toc = r.readU(16) * 16;
|
|
548
|
+
const tgd = r.readS(8) * 2 ** -31;
|
|
549
|
+
const deltaN = r.readS(22) * 2 ** -41 * PI;
|
|
550
|
+
const iode = r.readU(8);
|
|
551
|
+
r.skip(10);
|
|
552
|
+
const l5Health = r.readU(1);
|
|
553
|
+
const sHealth = r.readU(1);
|
|
554
|
+
const cuc = r.readS(15) * 2 ** -28;
|
|
555
|
+
const cus = r.readS(15) * 2 ** -28;
|
|
556
|
+
const cic = r.readS(15) * 2 ** -28;
|
|
557
|
+
const cis = r.readS(15) * 2 ** -28;
|
|
558
|
+
const crc = r.readS(15) * 2 ** -4;
|
|
559
|
+
const crs = r.readS(15) * 2 ** -4;
|
|
560
|
+
const idot = r.readS(14) * 2 ** -43 * PI;
|
|
561
|
+
const m0 = r.readS(32) * 2 ** -31 * PI;
|
|
562
|
+
const toe = r.readU(16) * 16;
|
|
563
|
+
const e = r.readU(32) * 2 ** -33;
|
|
564
|
+
const sqrtA = r.readU(32) * 2 ** -19;
|
|
565
|
+
const omega0 = r.readS(32) * 2 ** -31 * PI;
|
|
566
|
+
const argPerigee = r.readS(32) * 2 ** -31 * PI;
|
|
567
|
+
const omegaDot = r.readS(22) * 2 ** -41 * PI;
|
|
568
|
+
const i0 = r.readS(32) * 2 ** -31 * PI;
|
|
569
|
+
return {
|
|
570
|
+
prn: `I${String(svId).padStart(2, "0")}`,
|
|
571
|
+
constellation: "NavIC",
|
|
572
|
+
health: l5Health << 1 | sHealth,
|
|
573
|
+
lastReceived: Date.now(),
|
|
574
|
+
messageType: 1041,
|
|
575
|
+
week,
|
|
576
|
+
ura,
|
|
577
|
+
toc,
|
|
578
|
+
toe,
|
|
579
|
+
sqrtA,
|
|
580
|
+
eccentricity: e,
|
|
581
|
+
inclination: i0,
|
|
582
|
+
omega0,
|
|
583
|
+
omegaDot,
|
|
584
|
+
argPerigee,
|
|
585
|
+
meanAnomaly: m0,
|
|
586
|
+
deltaN,
|
|
587
|
+
idot,
|
|
588
|
+
crs,
|
|
589
|
+
crc,
|
|
590
|
+
cuc,
|
|
591
|
+
cus,
|
|
592
|
+
cic,
|
|
593
|
+
cis,
|
|
594
|
+
af0,
|
|
595
|
+
af1,
|
|
596
|
+
af2,
|
|
597
|
+
iode,
|
|
598
|
+
tgd
|
|
599
|
+
};
|
|
600
|
+
}
|
|
537
601
|
function decodeEphemeris(frame) {
|
|
538
602
|
try {
|
|
539
603
|
switch (frame.messageType) {
|
|
@@ -541,6 +605,8 @@ function decodeEphemeris(frame) {
|
|
|
541
605
|
return decodeGpsEphemeris(frame.payload);
|
|
542
606
|
case 1020:
|
|
543
607
|
return decodeGlonassEphemeris(frame.payload);
|
|
608
|
+
case 1041:
|
|
609
|
+
return decodeNavicEphemeris(frame.payload);
|
|
544
610
|
case 1042:
|
|
545
611
|
return decodeBdsEphemeris(frame.payload);
|
|
546
612
|
case 1043:
|
|
@@ -1649,6 +1649,10 @@ var SbasProcessor = class {
|
|
|
1649
1649
|
tlat = 0;
|
|
1650
1650
|
degr = null;
|
|
1651
1651
|
mt27 = null;
|
|
1652
|
+
/** MT28 clock-ephemeris covariance per PRN: the upper-triangular Cholesky
|
|
1653
|
+
* factor E (10 elements [E11,E22,E33,E44,E12,E13,E14,E23,E24,E34]) and its
|
|
1654
|
+
* scale exponent. Keyed by PRN; cleared on a mask change. */
|
|
1655
|
+
mt28 = /* @__PURE__ */ new Map();
|
|
1652
1656
|
ion = [];
|
|
1653
1657
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
1654
1658
|
geoPrns = /* @__PURE__ */ new Set();
|
|
@@ -1679,6 +1683,8 @@ var SbasProcessor = class {
|
|
|
1679
1683
|
return this.decodeMt10(msg) ? type : -1;
|
|
1680
1684
|
case 27:
|
|
1681
1685
|
return this.decodeMt27(msg) ? type : -1;
|
|
1686
|
+
case 28:
|
|
1687
|
+
return this.decodeMt28(msg) ? type : -1;
|
|
1682
1688
|
case 18:
|
|
1683
1689
|
return this.decodeIgpMask(msg) ? type : -1;
|
|
1684
1690
|
case 24:
|
|
@@ -1705,6 +1711,7 @@ var SbasProcessor = class {
|
|
|
1705
1711
|
return true;
|
|
1706
1712
|
this.iodp = iodp;
|
|
1707
1713
|
this.sats = prns.map((prn) => ({ prn }));
|
|
1714
|
+
this.mt28.clear();
|
|
1708
1715
|
this.satIdx.clear();
|
|
1709
1716
|
this.sats.forEach((s, k) => {
|
|
1710
1717
|
if (s.prn) this.satIdx.set(s.prn, k);
|
|
@@ -1770,7 +1777,9 @@ var SbasProcessor = class {
|
|
|
1770
1777
|
iiono: u(117, 9) || 1,
|
|
1771
1778
|
cionoRamp: u(126, 10) * 5e-6,
|
|
1772
1779
|
rssUdre: u(136, 1) === 1,
|
|
1773
|
-
rssIono: u(137, 1) === 1
|
|
1780
|
+
rssIono: u(137, 1) === 1,
|
|
1781
|
+
ccovariance: u(138, 7) * 0.1
|
|
1782
|
+
// Table B-49: 0..12.7 m, 0.1 m
|
|
1774
1783
|
};
|
|
1775
1784
|
return true;
|
|
1776
1785
|
}
|
|
@@ -1822,6 +1831,51 @@ var SbasProcessor = class {
|
|
|
1822
1831
|
}
|
|
1823
1832
|
return best ?? DELTA_UDRE[s.udreOutside] ?? 1;
|
|
1824
1833
|
}
|
|
1834
|
+
/** MT28 clock-ephemeris covariance (ICAO Annex 10 Vol I §3.5.4.7 / Table B-51):
|
|
1835
|
+
* two satellites per message, each an upper-triangular Cholesky factor E and a
|
|
1836
|
+
* scale exponent that together define the relative clock-ephemeris covariance
|
|
1837
|
+
* used for a geometry-dependent δUDRE (§3.5.5.6.2.5). Slots are indexed into
|
|
1838
|
+
* the current MT1 mask, so a mismatched IODP is dropped. */
|
|
1839
|
+
decodeMt28(msg) {
|
|
1840
|
+
if (this.iodp !== getBitU(msg, 14, 2)) return false;
|
|
1841
|
+
for (const base of [16, 121]) {
|
|
1842
|
+
const slot = getBitU(msg, base, 6);
|
|
1843
|
+
const prn = this.sats[slot]?.prn;
|
|
1844
|
+
const scaleExp = getBitU(msg, base + 6, 3);
|
|
1845
|
+
const e = [
|
|
1846
|
+
getBitU(msg, base + 9, 9),
|
|
1847
|
+
getBitU(msg, base + 18, 9),
|
|
1848
|
+
getBitU(msg, base + 27, 9),
|
|
1849
|
+
getBitU(msg, base + 36, 9),
|
|
1850
|
+
getBitS(msg, base + 45, 10),
|
|
1851
|
+
getBitS(msg, base + 55, 10),
|
|
1852
|
+
getBitS(msg, base + 65, 10),
|
|
1853
|
+
getBitS(msg, base + 75, 10),
|
|
1854
|
+
getBitS(msg, base + 85, 10),
|
|
1855
|
+
getBitS(msg, base + 95, 10)
|
|
1856
|
+
];
|
|
1857
|
+
if (prn && e.some((v) => v !== 0)) this.mt28.set(prn, { scaleExp, e });
|
|
1858
|
+
}
|
|
1859
|
+
return true;
|
|
1860
|
+
}
|
|
1861
|
+
/** Geometry-dependent δUDRE multiplier from MT28 (§3.5.5.6.2.5):
|
|
1862
|
+
* δUDRE = √(Iᵀ·C·I) + ε_c, with C = Rᵀ·R, R = E·2^(scaleExp−5),
|
|
1863
|
+
* I = [î_user→sat, 1], ε_c = C_covariance·2^(scaleExp−5). Since C = RᵀR,
|
|
1864
|
+
* Iᵀ·C·I = ‖R·I‖², so we form R·I directly. Null when no MT28 for `prn`. */
|
|
1865
|
+
mt28DeltaUdre(prn, los) {
|
|
1866
|
+
const m = this.mt28.get(prn);
|
|
1867
|
+
if (!m) return null;
|
|
1868
|
+
const sf = 2 ** (m.scaleExp - 5);
|
|
1869
|
+
const [e11, e22, e33, e44, e12, e13, e14, e23, e24, e34] = m.e;
|
|
1870
|
+
const [ix, iy, iz] = los;
|
|
1871
|
+
const v0 = (e11 * ix + e12 * iy + e13 * iz + e14) * sf;
|
|
1872
|
+
const v1 = (e22 * iy + e23 * iz + e24) * sf;
|
|
1873
|
+
const v2 = (e33 * iz + e34) * sf;
|
|
1874
|
+
const v3 = e44 * sf;
|
|
1875
|
+
const iCi = v0 * v0 + v1 * v1 + v2 * v2 + v3 * v3;
|
|
1876
|
+
const ec = (this.degr?.ccovariance ?? 0) * sf;
|
|
1877
|
+
return Math.sqrt(iCi) + ec;
|
|
1878
|
+
}
|
|
1825
1879
|
decodeMixed(msg, week, tow) {
|
|
1826
1880
|
if (this.iodp !== getBitU(msg, 110, 2)) return false;
|
|
1827
1881
|
const blk = getBitU(msg, 112, 2);
|
|
@@ -2026,7 +2080,7 @@ var SbasProcessor = class {
|
|
|
2026
2080
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
2027
2081
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
2028
2082
|
*/
|
|
2029
|
-
satCorrection(prn, week, tow, userLatRad, userLonRad) {
|
|
2083
|
+
satCorrection(prn, week, tow, userLatRad, userLonRad, losUnitEcef) {
|
|
2030
2084
|
const idx = this.satIdx.get(prn);
|
|
2031
2085
|
if (idx === void 0) return null;
|
|
2032
2086
|
const s = this.sats[idx];
|
|
@@ -2048,7 +2102,8 @@ var SbasProcessor = class {
|
|
|
2048
2102
|
const tf = now - s.fcorr.t0s + this.tlat;
|
|
2049
2103
|
if (Math.abs(tf) > MAXSBSAGEF || s.fcorr.udre >= 15) return null;
|
|
2050
2104
|
const prc = s.fcorr.prc;
|
|
2051
|
-
const
|
|
2105
|
+
const mt28 = losUnitEcef ? this.mt28DeltaUdre(prn, losUnitEcef) : null;
|
|
2106
|
+
const dudre = mt28 != null ? mt28 : userLatRad != null && userLonRad != null ? this.deltaUdre(
|
|
2052
2107
|
userLatRad * 180 / Math.PI,
|
|
2053
2108
|
userLonRad * 180 / Math.PI
|
|
2054
2109
|
) : 1;
|
|
@@ -3768,7 +3823,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3768
3823
|
used.push(prn);
|
|
3769
3824
|
azels.push(azel);
|
|
3770
3825
|
if (sbas) {
|
|
3771
|
-
const
|
|
3826
|
+
const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
|
|
3827
|
+
const dr = Math.hypot(dx, dy, dz) || 1;
|
|
3828
|
+
const los = [dx / dr, dy / dr, dz / dr];
|
|
3829
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow, rxLat, rxLon, los);
|
|
3772
3830
|
const si = sbas.ionoDelay(
|
|
3773
3831
|
gpsWeek,
|
|
3774
3832
|
gpsTow,
|