gnss-js 2.3.0 → 2.4.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-TYJNN77C.js → chunk-B2XWO4RP.js} +62 -4
- package/dist/index.cjs +62 -4
- package/dist/index.js +1 -1
- 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/package.json +1 -1
|
@@ -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,
|
package/dist/index.cjs
CHANGED
|
@@ -14576,6 +14576,10 @@ var SbasProcessor = class {
|
|
|
14576
14576
|
tlat = 0;
|
|
14577
14577
|
degr = null;
|
|
14578
14578
|
mt27 = null;
|
|
14579
|
+
/** MT28 clock-ephemeris covariance per PRN: the upper-triangular Cholesky
|
|
14580
|
+
* factor E (10 elements [E11,E22,E33,E44,E12,E13,E14,E23,E24,E34]) and its
|
|
14581
|
+
* scale exponent. Keyed by PRN; cleared on a mask change. */
|
|
14582
|
+
mt28 = /* @__PURE__ */ new Map();
|
|
14579
14583
|
ion = [];
|
|
14580
14584
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
14581
14585
|
geoPrns = /* @__PURE__ */ new Set();
|
|
@@ -14606,6 +14610,8 @@ var SbasProcessor = class {
|
|
|
14606
14610
|
return this.decodeMt10(msg) ? type : -1;
|
|
14607
14611
|
case 27:
|
|
14608
14612
|
return this.decodeMt27(msg) ? type : -1;
|
|
14613
|
+
case 28:
|
|
14614
|
+
return this.decodeMt28(msg) ? type : -1;
|
|
14609
14615
|
case 18:
|
|
14610
14616
|
return this.decodeIgpMask(msg) ? type : -1;
|
|
14611
14617
|
case 24:
|
|
@@ -14632,6 +14638,7 @@ var SbasProcessor = class {
|
|
|
14632
14638
|
return true;
|
|
14633
14639
|
this.iodp = iodp;
|
|
14634
14640
|
this.sats = prns.map((prn) => ({ prn }));
|
|
14641
|
+
this.mt28.clear();
|
|
14635
14642
|
this.satIdx.clear();
|
|
14636
14643
|
this.sats.forEach((s, k) => {
|
|
14637
14644
|
if (s.prn) this.satIdx.set(s.prn, k);
|
|
@@ -14697,7 +14704,9 @@ var SbasProcessor = class {
|
|
|
14697
14704
|
iiono: u(117, 9) || 1,
|
|
14698
14705
|
cionoRamp: u(126, 10) * 5e-6,
|
|
14699
14706
|
rssUdre: u(136, 1) === 1,
|
|
14700
|
-
rssIono: u(137, 1) === 1
|
|
14707
|
+
rssIono: u(137, 1) === 1,
|
|
14708
|
+
ccovariance: u(138, 7) * 0.1
|
|
14709
|
+
// Table B-49: 0..12.7 m, 0.1 m
|
|
14701
14710
|
};
|
|
14702
14711
|
return true;
|
|
14703
14712
|
}
|
|
@@ -14749,6 +14758,51 @@ var SbasProcessor = class {
|
|
|
14749
14758
|
}
|
|
14750
14759
|
return best ?? DELTA_UDRE[s.udreOutside] ?? 1;
|
|
14751
14760
|
}
|
|
14761
|
+
/** MT28 clock-ephemeris covariance (ICAO Annex 10 Vol I §3.5.4.7 / Table B-51):
|
|
14762
|
+
* two satellites per message, each an upper-triangular Cholesky factor E and a
|
|
14763
|
+
* scale exponent that together define the relative clock-ephemeris covariance
|
|
14764
|
+
* used for a geometry-dependent δUDRE (§3.5.5.6.2.5). Slots are indexed into
|
|
14765
|
+
* the current MT1 mask, so a mismatched IODP is dropped. */
|
|
14766
|
+
decodeMt28(msg) {
|
|
14767
|
+
if (this.iodp !== getBitU(msg, 14, 2)) return false;
|
|
14768
|
+
for (const base of [16, 121]) {
|
|
14769
|
+
const slot = getBitU(msg, base, 6);
|
|
14770
|
+
const prn = this.sats[slot]?.prn;
|
|
14771
|
+
const scaleExp = getBitU(msg, base + 6, 3);
|
|
14772
|
+
const e = [
|
|
14773
|
+
getBitU(msg, base + 9, 9),
|
|
14774
|
+
getBitU(msg, base + 18, 9),
|
|
14775
|
+
getBitU(msg, base + 27, 9),
|
|
14776
|
+
getBitU(msg, base + 36, 9),
|
|
14777
|
+
getBitS(msg, base + 45, 10),
|
|
14778
|
+
getBitS(msg, base + 55, 10),
|
|
14779
|
+
getBitS(msg, base + 65, 10),
|
|
14780
|
+
getBitS(msg, base + 75, 10),
|
|
14781
|
+
getBitS(msg, base + 85, 10),
|
|
14782
|
+
getBitS(msg, base + 95, 10)
|
|
14783
|
+
];
|
|
14784
|
+
if (prn && e.some((v) => v !== 0)) this.mt28.set(prn, { scaleExp, e });
|
|
14785
|
+
}
|
|
14786
|
+
return true;
|
|
14787
|
+
}
|
|
14788
|
+
/** Geometry-dependent δUDRE multiplier from MT28 (§3.5.5.6.2.5):
|
|
14789
|
+
* δUDRE = √(Iᵀ·C·I) + ε_c, with C = Rᵀ·R, R = E·2^(scaleExp−5),
|
|
14790
|
+
* I = [î_user→sat, 1], ε_c = C_covariance·2^(scaleExp−5). Since C = RᵀR,
|
|
14791
|
+
* Iᵀ·C·I = ‖R·I‖², so we form R·I directly. Null when no MT28 for `prn`. */
|
|
14792
|
+
mt28DeltaUdre(prn, los) {
|
|
14793
|
+
const m = this.mt28.get(prn);
|
|
14794
|
+
if (!m) return null;
|
|
14795
|
+
const sf = 2 ** (m.scaleExp - 5);
|
|
14796
|
+
const [e11, e22, e33, e44, e12, e13, e14, e23, e24, e34] = m.e;
|
|
14797
|
+
const [ix, iy, iz] = los;
|
|
14798
|
+
const v0 = (e11 * ix + e12 * iy + e13 * iz + e14) * sf;
|
|
14799
|
+
const v1 = (e22 * iy + e23 * iz + e24) * sf;
|
|
14800
|
+
const v2 = (e33 * iz + e34) * sf;
|
|
14801
|
+
const v3 = e44 * sf;
|
|
14802
|
+
const iCi = v0 * v0 + v1 * v1 + v2 * v2 + v3 * v3;
|
|
14803
|
+
const ec = (this.degr?.ccovariance ?? 0) * sf;
|
|
14804
|
+
return Math.sqrt(iCi) + ec;
|
|
14805
|
+
}
|
|
14752
14806
|
decodeMixed(msg, week, tow) {
|
|
14753
14807
|
if (this.iodp !== getBitU(msg, 110, 2)) return false;
|
|
14754
14808
|
const blk = getBitU(msg, 112, 2);
|
|
@@ -14953,7 +15007,7 @@ var SbasProcessor = class {
|
|
|
14953
15007
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
14954
15008
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
14955
15009
|
*/
|
|
14956
|
-
satCorrection(prn, week, tow, userLatRad, userLonRad) {
|
|
15010
|
+
satCorrection(prn, week, tow, userLatRad, userLonRad, losUnitEcef) {
|
|
14957
15011
|
const idx = this.satIdx.get(prn);
|
|
14958
15012
|
if (idx === void 0) return null;
|
|
14959
15013
|
const s = this.sats[idx];
|
|
@@ -14975,7 +15029,8 @@ var SbasProcessor = class {
|
|
|
14975
15029
|
const tf = now - s.fcorr.t0s + this.tlat;
|
|
14976
15030
|
if (Math.abs(tf) > MAXSBSAGEF || s.fcorr.udre >= 15) return null;
|
|
14977
15031
|
const prc = s.fcorr.prc;
|
|
14978
|
-
const
|
|
15032
|
+
const mt28 = losUnitEcef ? this.mt28DeltaUdre(prn, losUnitEcef) : null;
|
|
15033
|
+
const dudre = mt28 != null ? mt28 : userLatRad != null && userLonRad != null ? this.deltaUdre(
|
|
14979
15034
|
userLatRad * 180 / Math.PI,
|
|
14980
15035
|
userLonRad * 180 / Math.PI
|
|
14981
15036
|
) : 1;
|
|
@@ -16695,7 +16750,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
16695
16750
|
used.push(prn);
|
|
16696
16751
|
azels.push(azel);
|
|
16697
16752
|
if (sbas) {
|
|
16698
|
-
const
|
|
16753
|
+
const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
|
|
16754
|
+
const dr = Math.hypot(dx, dy, dz) || 1;
|
|
16755
|
+
const los = [dx / dr, dy / dr, dz / dr];
|
|
16756
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow, rxLat, rxLon, los);
|
|
16699
16757
|
const si = sbas.ionoDelay(
|
|
16700
16758
|
gpsWeek,
|
|
16701
16759
|
gpsTow,
|
package/dist/index.js
CHANGED
package/dist/positioning.cjs
CHANGED
|
@@ -2132,6 +2132,10 @@ var SbasProcessor = class {
|
|
|
2132
2132
|
tlat = 0;
|
|
2133
2133
|
degr = null;
|
|
2134
2134
|
mt27 = null;
|
|
2135
|
+
/** MT28 clock-ephemeris covariance per PRN: the upper-triangular Cholesky
|
|
2136
|
+
* factor E (10 elements [E11,E22,E33,E44,E12,E13,E14,E23,E24,E34]) and its
|
|
2137
|
+
* scale exponent. Keyed by PRN; cleared on a mask change. */
|
|
2138
|
+
mt28 = /* @__PURE__ */ new Map();
|
|
2135
2139
|
ion = [];
|
|
2136
2140
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
2137
2141
|
geoPrns = /* @__PURE__ */ new Set();
|
|
@@ -2162,6 +2166,8 @@ var SbasProcessor = class {
|
|
|
2162
2166
|
return this.decodeMt10(msg) ? type : -1;
|
|
2163
2167
|
case 27:
|
|
2164
2168
|
return this.decodeMt27(msg) ? type : -1;
|
|
2169
|
+
case 28:
|
|
2170
|
+
return this.decodeMt28(msg) ? type : -1;
|
|
2165
2171
|
case 18:
|
|
2166
2172
|
return this.decodeIgpMask(msg) ? type : -1;
|
|
2167
2173
|
case 24:
|
|
@@ -2188,6 +2194,7 @@ var SbasProcessor = class {
|
|
|
2188
2194
|
return true;
|
|
2189
2195
|
this.iodp = iodp;
|
|
2190
2196
|
this.sats = prns.map((prn) => ({ prn }));
|
|
2197
|
+
this.mt28.clear();
|
|
2191
2198
|
this.satIdx.clear();
|
|
2192
2199
|
this.sats.forEach((s, k) => {
|
|
2193
2200
|
if (s.prn) this.satIdx.set(s.prn, k);
|
|
@@ -2253,7 +2260,9 @@ var SbasProcessor = class {
|
|
|
2253
2260
|
iiono: u(117, 9) || 1,
|
|
2254
2261
|
cionoRamp: u(126, 10) * 5e-6,
|
|
2255
2262
|
rssUdre: u(136, 1) === 1,
|
|
2256
|
-
rssIono: u(137, 1) === 1
|
|
2263
|
+
rssIono: u(137, 1) === 1,
|
|
2264
|
+
ccovariance: u(138, 7) * 0.1
|
|
2265
|
+
// Table B-49: 0..12.7 m, 0.1 m
|
|
2257
2266
|
};
|
|
2258
2267
|
return true;
|
|
2259
2268
|
}
|
|
@@ -2305,6 +2314,51 @@ var SbasProcessor = class {
|
|
|
2305
2314
|
}
|
|
2306
2315
|
return best ?? DELTA_UDRE[s.udreOutside] ?? 1;
|
|
2307
2316
|
}
|
|
2317
|
+
/** MT28 clock-ephemeris covariance (ICAO Annex 10 Vol I §3.5.4.7 / Table B-51):
|
|
2318
|
+
* two satellites per message, each an upper-triangular Cholesky factor E and a
|
|
2319
|
+
* scale exponent that together define the relative clock-ephemeris covariance
|
|
2320
|
+
* used for a geometry-dependent δUDRE (§3.5.5.6.2.5). Slots are indexed into
|
|
2321
|
+
* the current MT1 mask, so a mismatched IODP is dropped. */
|
|
2322
|
+
decodeMt28(msg) {
|
|
2323
|
+
if (this.iodp !== getBitU(msg, 14, 2)) return false;
|
|
2324
|
+
for (const base of [16, 121]) {
|
|
2325
|
+
const slot = getBitU(msg, base, 6);
|
|
2326
|
+
const prn = this.sats[slot]?.prn;
|
|
2327
|
+
const scaleExp = getBitU(msg, base + 6, 3);
|
|
2328
|
+
const e = [
|
|
2329
|
+
getBitU(msg, base + 9, 9),
|
|
2330
|
+
getBitU(msg, base + 18, 9),
|
|
2331
|
+
getBitU(msg, base + 27, 9),
|
|
2332
|
+
getBitU(msg, base + 36, 9),
|
|
2333
|
+
getBitS(msg, base + 45, 10),
|
|
2334
|
+
getBitS(msg, base + 55, 10),
|
|
2335
|
+
getBitS(msg, base + 65, 10),
|
|
2336
|
+
getBitS(msg, base + 75, 10),
|
|
2337
|
+
getBitS(msg, base + 85, 10),
|
|
2338
|
+
getBitS(msg, base + 95, 10)
|
|
2339
|
+
];
|
|
2340
|
+
if (prn && e.some((v) => v !== 0)) this.mt28.set(prn, { scaleExp, e });
|
|
2341
|
+
}
|
|
2342
|
+
return true;
|
|
2343
|
+
}
|
|
2344
|
+
/** Geometry-dependent δUDRE multiplier from MT28 (§3.5.5.6.2.5):
|
|
2345
|
+
* δUDRE = √(Iᵀ·C·I) + ε_c, with C = Rᵀ·R, R = E·2^(scaleExp−5),
|
|
2346
|
+
* I = [î_user→sat, 1], ε_c = C_covariance·2^(scaleExp−5). Since C = RᵀR,
|
|
2347
|
+
* Iᵀ·C·I = ‖R·I‖², so we form R·I directly. Null when no MT28 for `prn`. */
|
|
2348
|
+
mt28DeltaUdre(prn, los) {
|
|
2349
|
+
const m = this.mt28.get(prn);
|
|
2350
|
+
if (!m) return null;
|
|
2351
|
+
const sf = 2 ** (m.scaleExp - 5);
|
|
2352
|
+
const [e11, e22, e33, e44, e12, e13, e14, e23, e24, e34] = m.e;
|
|
2353
|
+
const [ix, iy, iz] = los;
|
|
2354
|
+
const v0 = (e11 * ix + e12 * iy + e13 * iz + e14) * sf;
|
|
2355
|
+
const v1 = (e22 * iy + e23 * iz + e24) * sf;
|
|
2356
|
+
const v2 = (e33 * iz + e34) * sf;
|
|
2357
|
+
const v3 = e44 * sf;
|
|
2358
|
+
const iCi = v0 * v0 + v1 * v1 + v2 * v2 + v3 * v3;
|
|
2359
|
+
const ec = (this.degr?.ccovariance ?? 0) * sf;
|
|
2360
|
+
return Math.sqrt(iCi) + ec;
|
|
2361
|
+
}
|
|
2308
2362
|
decodeMixed(msg, week, tow) {
|
|
2309
2363
|
if (this.iodp !== getBitU(msg, 110, 2)) return false;
|
|
2310
2364
|
const blk = getBitU(msg, 112, 2);
|
|
@@ -2509,7 +2563,7 @@ var SbasProcessor = class {
|
|
|
2509
2563
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
2510
2564
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
2511
2565
|
*/
|
|
2512
|
-
satCorrection(prn, week, tow, userLatRad, userLonRad) {
|
|
2566
|
+
satCorrection(prn, week, tow, userLatRad, userLonRad, losUnitEcef) {
|
|
2513
2567
|
const idx = this.satIdx.get(prn);
|
|
2514
2568
|
if (idx === void 0) return null;
|
|
2515
2569
|
const s = this.sats[idx];
|
|
@@ -2531,7 +2585,8 @@ var SbasProcessor = class {
|
|
|
2531
2585
|
const tf = now - s.fcorr.t0s + this.tlat;
|
|
2532
2586
|
if (Math.abs(tf) > MAXSBSAGEF || s.fcorr.udre >= 15) return null;
|
|
2533
2587
|
const prc = s.fcorr.prc;
|
|
2534
|
-
const
|
|
2588
|
+
const mt28 = losUnitEcef ? this.mt28DeltaUdre(prn, losUnitEcef) : null;
|
|
2589
|
+
const dudre = mt28 != null ? mt28 : userLatRad != null && userLonRad != null ? this.deltaUdre(
|
|
2535
2590
|
userLatRad * 180 / Math.PI,
|
|
2536
2591
|
userLonRad * 180 / Math.PI
|
|
2537
2592
|
) : 1;
|
|
@@ -4323,7 +4378,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
4323
4378
|
used.push(prn);
|
|
4324
4379
|
azels.push(azel);
|
|
4325
4380
|
if (sbas) {
|
|
4326
|
-
const
|
|
4381
|
+
const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
|
|
4382
|
+
const dr = Math.hypot(dx, dy, dz) || 1;
|
|
4383
|
+
const los = [dx / dr, dy / dr, dz / dr];
|
|
4384
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow, rxLat, rxLon, los);
|
|
4327
4385
|
const si = sbas.ionoDelay(
|
|
4328
4386
|
gpsWeek,
|
|
4329
4387
|
gpsTow,
|
package/dist/positioning.d.cts
CHANGED
|
@@ -668,6 +668,8 @@ interface Degradation {
|
|
|
668
668
|
cionoRamp: number;
|
|
669
669
|
rssUdre: boolean;
|
|
670
670
|
rssIono: boolean;
|
|
671
|
+
/** Cᶜᵒᵛᵃʳⁱᵃⁿᶜᵉ — the MT28 δUDRE quantization term (m), 0 when no MT28. */
|
|
672
|
+
ccovariance: number;
|
|
671
673
|
}
|
|
672
674
|
/** A per-satellite SBAS correction, ready to apply in an SPP solve. */
|
|
673
675
|
interface SbasSatCorrection {
|
|
@@ -715,6 +717,10 @@ declare class SbasProcessor {
|
|
|
715
717
|
private tlat;
|
|
716
718
|
private degr;
|
|
717
719
|
private mt27;
|
|
720
|
+
/** MT28 clock-ephemeris covariance per PRN: the upper-triangular Cholesky
|
|
721
|
+
* factor E (10 elements [E11,E22,E33,E44,E12,E13,E14,E23,E24,E34]) and its
|
|
722
|
+
* scale exponent. Keyed by PRN; cleared on a mask change. */
|
|
723
|
+
private mt28;
|
|
718
724
|
private ion;
|
|
719
725
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
720
726
|
readonly geoPrns: Set<string>;
|
|
@@ -739,6 +745,17 @@ declare class SbasProcessor {
|
|
|
739
745
|
* set: the highest-priority containing region wins (ties → lower δUDRE),
|
|
740
746
|
* else the outside value. 1 when no MT27 has been received. */
|
|
741
747
|
deltaUdre(latDeg: number, lonDeg: number): number;
|
|
748
|
+
/** MT28 clock-ephemeris covariance (ICAO Annex 10 Vol I §3.5.4.7 / Table B-51):
|
|
749
|
+
* two satellites per message, each an upper-triangular Cholesky factor E and a
|
|
750
|
+
* scale exponent that together define the relative clock-ephemeris covariance
|
|
751
|
+
* used for a geometry-dependent δUDRE (§3.5.5.6.2.5). Slots are indexed into
|
|
752
|
+
* the current MT1 mask, so a mismatched IODP is dropped. */
|
|
753
|
+
private decodeMt28;
|
|
754
|
+
/** Geometry-dependent δUDRE multiplier from MT28 (§3.5.5.6.2.5):
|
|
755
|
+
* δUDRE = √(Iᵀ·C·I) + ε_c, with C = Rᵀ·R, R = E·2^(scaleExp−5),
|
|
756
|
+
* I = [î_user→sat, 1], ε_c = C_covariance·2^(scaleExp−5). Since C = RᵀR,
|
|
757
|
+
* Iᵀ·C·I = ‖R·I‖², so we form R·I directly. Null when no MT28 for `prn`. */
|
|
758
|
+
private mt28DeltaUdre;
|
|
742
759
|
private decodeMixed;
|
|
743
760
|
private decodeLongCorrHalf;
|
|
744
761
|
private decodeLong0;
|
|
@@ -782,7 +799,11 @@ declare class SbasProcessor {
|
|
|
782
799
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
783
800
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
784
801
|
*/
|
|
785
|
-
satCorrection(prn: string, week: number, tow: number, userLatRad?: number, userLonRad?: number
|
|
802
|
+
satCorrection(prn: string, week: number, tow: number, userLatRad?: number, userLonRad?: number,
|
|
803
|
+
/** Unit line-of-sight vector user→satellite in ECEF, for the MT28
|
|
804
|
+
* geometry-dependent δUDRE (final protection-level pass). When given and
|
|
805
|
+
* MT28 data exists for the PRN, it supersedes the MT27 region δUDRE. */
|
|
806
|
+
losUnitEcef?: readonly [number, number, number]): SbasSatCorrection | null;
|
|
786
807
|
/** The last-decoded MT10 degradation factors, or null if none seen. */
|
|
787
808
|
get degradation(): Readonly<Degradation> | null;
|
|
788
809
|
/**
|
|
@@ -1557,7 +1578,10 @@ interface SbasCorrectionSource {
|
|
|
1557
1578
|
satCorrection(prn: string, week: number, tow: number,
|
|
1558
1579
|
/** User geodetic lat/lon (rad) for the MT27 service-region δUDRE, when
|
|
1559
1580
|
* known (the final protection-level pass). Omitted during iteration. */
|
|
1560
|
-
userLatRad?: number, userLonRad?: number
|
|
1581
|
+
userLatRad?: number, userLonRad?: number,
|
|
1582
|
+
/** Unit LOS user→satellite (ECEF) for the MT28 geometry-dependent δUDRE
|
|
1583
|
+
* (final protection-level pass). */
|
|
1584
|
+
losUnitEcef?: readonly [number, number, number]): {
|
|
1561
1585
|
dPos: [number, number, number];
|
|
1562
1586
|
dClkS: number;
|
|
1563
1587
|
varM2: number;
|
package/dist/positioning.d.ts
CHANGED
|
@@ -668,6 +668,8 @@ interface Degradation {
|
|
|
668
668
|
cionoRamp: number;
|
|
669
669
|
rssUdre: boolean;
|
|
670
670
|
rssIono: boolean;
|
|
671
|
+
/** Cᶜᵒᵛᵃʳⁱᵃⁿᶜᵉ — the MT28 δUDRE quantization term (m), 0 when no MT28. */
|
|
672
|
+
ccovariance: number;
|
|
671
673
|
}
|
|
672
674
|
/** A per-satellite SBAS correction, ready to apply in an SPP solve. */
|
|
673
675
|
interface SbasSatCorrection {
|
|
@@ -715,6 +717,10 @@ declare class SbasProcessor {
|
|
|
715
717
|
private tlat;
|
|
716
718
|
private degr;
|
|
717
719
|
private mt27;
|
|
720
|
+
/** MT28 clock-ephemeris covariance per PRN: the upper-triangular Cholesky
|
|
721
|
+
* factor E (10 elements [E11,E22,E33,E44,E12,E13,E14,E23,E24,E34]) and its
|
|
722
|
+
* scale exponent. Keyed by PRN; cleared on a mask change. */
|
|
723
|
+
private mt28;
|
|
718
724
|
private ion;
|
|
719
725
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
720
726
|
readonly geoPrns: Set<string>;
|
|
@@ -739,6 +745,17 @@ declare class SbasProcessor {
|
|
|
739
745
|
* set: the highest-priority containing region wins (ties → lower δUDRE),
|
|
740
746
|
* else the outside value. 1 when no MT27 has been received. */
|
|
741
747
|
deltaUdre(latDeg: number, lonDeg: number): number;
|
|
748
|
+
/** MT28 clock-ephemeris covariance (ICAO Annex 10 Vol I §3.5.4.7 / Table B-51):
|
|
749
|
+
* two satellites per message, each an upper-triangular Cholesky factor E and a
|
|
750
|
+
* scale exponent that together define the relative clock-ephemeris covariance
|
|
751
|
+
* used for a geometry-dependent δUDRE (§3.5.5.6.2.5). Slots are indexed into
|
|
752
|
+
* the current MT1 mask, so a mismatched IODP is dropped. */
|
|
753
|
+
private decodeMt28;
|
|
754
|
+
/** Geometry-dependent δUDRE multiplier from MT28 (§3.5.5.6.2.5):
|
|
755
|
+
* δUDRE = √(Iᵀ·C·I) + ε_c, with C = Rᵀ·R, R = E·2^(scaleExp−5),
|
|
756
|
+
* I = [î_user→sat, 1], ε_c = C_covariance·2^(scaleExp−5). Since C = RᵀR,
|
|
757
|
+
* Iᵀ·C·I = ‖R·I‖², so we form R·I directly. Null when no MT28 for `prn`. */
|
|
758
|
+
private mt28DeltaUdre;
|
|
742
759
|
private decodeMixed;
|
|
743
760
|
private decodeLongCorrHalf;
|
|
744
761
|
private decodeLong0;
|
|
@@ -782,7 +799,11 @@ declare class SbasProcessor {
|
|
|
782
799
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
783
800
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
784
801
|
*/
|
|
785
|
-
satCorrection(prn: string, week: number, tow: number, userLatRad?: number, userLonRad?: number
|
|
802
|
+
satCorrection(prn: string, week: number, tow: number, userLatRad?: number, userLonRad?: number,
|
|
803
|
+
/** Unit line-of-sight vector user→satellite in ECEF, for the MT28
|
|
804
|
+
* geometry-dependent δUDRE (final protection-level pass). When given and
|
|
805
|
+
* MT28 data exists for the PRN, it supersedes the MT27 region δUDRE. */
|
|
806
|
+
losUnitEcef?: readonly [number, number, number]): SbasSatCorrection | null;
|
|
786
807
|
/** The last-decoded MT10 degradation factors, or null if none seen. */
|
|
787
808
|
get degradation(): Readonly<Degradation> | null;
|
|
788
809
|
/**
|
|
@@ -1557,7 +1578,10 @@ interface SbasCorrectionSource {
|
|
|
1557
1578
|
satCorrection(prn: string, week: number, tow: number,
|
|
1558
1579
|
/** User geodetic lat/lon (rad) for the MT27 service-region δUDRE, when
|
|
1559
1580
|
* known (the final protection-level pass). Omitted during iteration. */
|
|
1560
|
-
userLatRad?: number, userLonRad?: number
|
|
1581
|
+
userLatRad?: number, userLonRad?: number,
|
|
1582
|
+
/** Unit LOS user→satellite (ECEF) for the MT28 geometry-dependent δUDRE
|
|
1583
|
+
* (final protection-level pass). */
|
|
1584
|
+
losUnitEcef?: readonly [number, number, number]): {
|
|
1561
1585
|
dPos: [number, number, number];
|
|
1562
1586
|
dClkS: number;
|
|
1563
1587
|
varM2: number;
|
package/dist/positioning.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gnss-js",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Comprehensive GNSS library for JavaScript — time scales, coordinates, RINEX parsing, RTCM3 decoding, orbit computation, and signal analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|