gnss-js 1.22.0 → 1.24.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-JNFV5BYB.js → chunk-MHGNKSSH.js} +2 -3
- package/dist/chunk-T2ZMNRCY.js +1274 -0
- package/dist/index.cjs +1022 -7
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +13 -3
- package/dist/orbit.cjs +2 -3
- package/dist/orbit.js +1 -1
- package/dist/positioning.cjs +1044 -9
- package/dist/positioning.d.cts +400 -1
- package/dist/positioning.d.ts +400 -1
- package/dist/positioning.js +14 -4
- package/package.json +1 -1
- package/dist/chunk-B5LU4746.js +0 -262
package/dist/index.cjs
CHANGED
|
@@ -82,6 +82,7 @@ __export(src_exports, {
|
|
|
82
82
|
RINEX_CODES: () => RINEX_CODES,
|
|
83
83
|
RTCM3_MESSAGE_NAMES: () => RTCM3_MESSAGE_NAMES,
|
|
84
84
|
Rtcm3Decoder: () => Rtcm3Decoder,
|
|
85
|
+
RtkFloatEngine: () => RtkFloatEngine,
|
|
85
86
|
SECONDS_IN_DAY: () => SECONDS_IN_DAY,
|
|
86
87
|
SECONDS_IN_HOUR: () => SECONDS_IN_HOUR,
|
|
87
88
|
SECONDS_IN_MINUTE: () => SECONDS_IN_MINUTE,
|
|
@@ -211,6 +212,8 @@ __export(src_exports, {
|
|
|
211
212
|
ionoFree: () => ionoFree,
|
|
212
213
|
keplerPosition: () => keplerPosition,
|
|
213
214
|
klobucharDelay: () => klobucharDelay,
|
|
215
|
+
lambdaReduction: () => lambdaReduction,
|
|
216
|
+
lambdaSearch: () => lambdaSearch,
|
|
214
217
|
maskRadForAzimuth: () => maskRadForAzimuth,
|
|
215
218
|
msmEpochToDate: () => msmEpochToDate,
|
|
216
219
|
navTimesFromEph: () => navTimesFromEph,
|
|
@@ -260,11 +263,13 @@ __export(src_exports, {
|
|
|
260
263
|
selectEphemeris: () => selectEphemeris,
|
|
261
264
|
setGloFreqNumber: () => setGloFreqNumber,
|
|
262
265
|
setRtcm3DebugHandler: () => setRtcm3DebugHandler,
|
|
266
|
+
solveDgnss: () => solveDgnss,
|
|
263
267
|
solveSpp: () => solveSpp,
|
|
264
268
|
sp3Position: () => sp3Position,
|
|
265
269
|
stationHeaderLines: () => stationHeaderLines,
|
|
266
270
|
systemCmp: () => systemCmp,
|
|
267
271
|
systemName: () => systemName,
|
|
272
|
+
toRtkEpoch: () => toRtkEpoch,
|
|
268
273
|
transformFrame: () => transformFrame,
|
|
269
274
|
ubxFrames: () => ubxFrames,
|
|
270
275
|
updateStationMeta: () => updateStationMeta,
|
|
@@ -8851,8 +8856,7 @@ var GM_GPS2 = 3986005e8;
|
|
|
8851
8856
|
var GM_GAL2 = 3986004418e5;
|
|
8852
8857
|
var GM_BDS2 = 3986004418e5;
|
|
8853
8858
|
var GM_GLO = 39860044e7;
|
|
8854
|
-
var
|
|
8855
|
-
var BDS_GEO_MIN_SEMIMAJOR_AXIS_M = 4e7;
|
|
8859
|
+
var BDS_GEO_PRNS2 = /* @__PURE__ */ new Set([1, 2, 3, 4, 5, 59, 60, 61, 62, 63]);
|
|
8856
8860
|
var AE_GLO = 6378136;
|
|
8857
8861
|
var J2_GLO = 108263e-8;
|
|
8858
8862
|
var TWO_PI = 2 * Math.PI;
|
|
@@ -8899,7 +8903,7 @@ function keplerPosition(eph, t) {
|
|
|
8899
8903
|
const yp = rk * Math.sin(uk);
|
|
8900
8904
|
const xpDot = drkDot * Math.cos(uk) - rk * ukDot * Math.sin(uk);
|
|
8901
8905
|
const ypDot = drkDot * Math.sin(uk) + rk * ukDot * Math.cos(uk);
|
|
8902
|
-
const isBdsGeo = eph.system === "C" &&
|
|
8906
|
+
const isBdsGeo = eph.system === "C" && BDS_GEO_PRNS2.has(Number(eph.prn.slice(1)));
|
|
8903
8907
|
if (isBdsGeo) {
|
|
8904
8908
|
const omegak2 = eph.omega0 + eph.omegaDot * tk - OMEGA_E * eph.toe;
|
|
8905
8909
|
const cosO2 = Math.cos(omegak2);
|
|
@@ -9678,6 +9682,1012 @@ function klobucharDelay(coeffs, latRad, lonRad, azRad, elRad, gpsTowSec) {
|
|
|
9678
9682
|
return F * (5e-9 + amp * (1 - x2 / 2 + x2 * x2 / 24));
|
|
9679
9683
|
}
|
|
9680
9684
|
|
|
9685
|
+
// src/positioning/lambda.ts
|
|
9686
|
+
var LOOPMAX = 1e4;
|
|
9687
|
+
var ROUND = (x) => Math.floor(x + 0.5);
|
|
9688
|
+
var SGN = (x) => x <= 0 ? -1 : 1;
|
|
9689
|
+
function ldFactor(n, Q, L, D) {
|
|
9690
|
+
const A = Float64Array.from(Q);
|
|
9691
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
9692
|
+
D[i] = A[i + i * n];
|
|
9693
|
+
if (D[i] <= 0) return false;
|
|
9694
|
+
const a = Math.sqrt(D[i]);
|
|
9695
|
+
for (let j = 0; j <= i; j++) L[i + j * n] = A[i + j * n] / a;
|
|
9696
|
+
for (let j = 0; j <= i - 1; j++)
|
|
9697
|
+
for (let k = 0; k <= j; k++) A[j + k * n] -= L[i + k * n] * L[i + j * n];
|
|
9698
|
+
for (let j = 0; j <= i; j++) L[i + j * n] /= L[i + i * n];
|
|
9699
|
+
}
|
|
9700
|
+
return true;
|
|
9701
|
+
}
|
|
9702
|
+
function gauss(n, L, Z2, i, j) {
|
|
9703
|
+
const mu = ROUND(L[i + j * n]);
|
|
9704
|
+
if (mu !== 0) {
|
|
9705
|
+
for (let k = i; k < n; k++) L[k + n * j] -= mu * L[k + i * n];
|
|
9706
|
+
for (let k = 0; k < n; k++) Z2[k + n * j] -= mu * Z2[k + i * n];
|
|
9707
|
+
}
|
|
9708
|
+
}
|
|
9709
|
+
function perm(n, L, D, j, del, Z2) {
|
|
9710
|
+
const eta = D[j] / del;
|
|
9711
|
+
const lam = D[j + 1] * L[j + 1 + j * n] / del;
|
|
9712
|
+
D[j] = eta * D[j + 1];
|
|
9713
|
+
D[j + 1] = del;
|
|
9714
|
+
for (let k = 0; k <= j - 1; k++) {
|
|
9715
|
+
const a0 = L[j + k * n];
|
|
9716
|
+
const a1 = L[j + 1 + k * n];
|
|
9717
|
+
L[j + k * n] = -L[j + 1 + j * n] * a0 + a1;
|
|
9718
|
+
L[j + 1 + k * n] = eta * a0 + lam * a1;
|
|
9719
|
+
}
|
|
9720
|
+
L[j + 1 + j * n] = lam;
|
|
9721
|
+
for (let k = j + 2; k < n; k++) {
|
|
9722
|
+
const t = L[k + j * n];
|
|
9723
|
+
L[k + j * n] = L[k + (j + 1) * n];
|
|
9724
|
+
L[k + (j + 1) * n] = t;
|
|
9725
|
+
}
|
|
9726
|
+
for (let k = 0; k < n; k++) {
|
|
9727
|
+
const t = Z2[k + j * n];
|
|
9728
|
+
Z2[k + j * n] = Z2[k + (j + 1) * n];
|
|
9729
|
+
Z2[k + (j + 1) * n] = t;
|
|
9730
|
+
}
|
|
9731
|
+
}
|
|
9732
|
+
function reduction(n, L, D, Z2) {
|
|
9733
|
+
let j = n - 2;
|
|
9734
|
+
let k = n - 2;
|
|
9735
|
+
while (j >= 0) {
|
|
9736
|
+
if (j <= k) for (let i = j + 1; i < n; i++) gauss(n, L, Z2, i, j);
|
|
9737
|
+
const del = D[j] + L[j + 1 + j * n] * L[j + 1 + j * n] * D[j + 1];
|
|
9738
|
+
if (del + 1e-6 < D[j + 1]) {
|
|
9739
|
+
perm(n, L, D, j, del, Z2);
|
|
9740
|
+
k = j;
|
|
9741
|
+
j = n - 2;
|
|
9742
|
+
} else j--;
|
|
9743
|
+
}
|
|
9744
|
+
}
|
|
9745
|
+
function search(n, m, L, D, zs, zn, s) {
|
|
9746
|
+
let nn = 0;
|
|
9747
|
+
let imax = 0;
|
|
9748
|
+
let maxdist = 1e99;
|
|
9749
|
+
const S = new Float64Array(n * n);
|
|
9750
|
+
const dist = new Float64Array(n);
|
|
9751
|
+
const zb = new Float64Array(n);
|
|
9752
|
+
const z = new Float64Array(n);
|
|
9753
|
+
const step = new Float64Array(n);
|
|
9754
|
+
let k = n - 1;
|
|
9755
|
+
dist[k] = 0;
|
|
9756
|
+
zb[k] = zs[k];
|
|
9757
|
+
z[k] = ROUND(zb[k]);
|
|
9758
|
+
let y = zb[k] - z[k];
|
|
9759
|
+
step[k] = SGN(y);
|
|
9760
|
+
let c = 0;
|
|
9761
|
+
for (; c < LOOPMAX; c++) {
|
|
9762
|
+
const newdist = dist[k] + y * y / D[k];
|
|
9763
|
+
if (newdist < maxdist) {
|
|
9764
|
+
if (k !== 0) {
|
|
9765
|
+
dist[--k] = newdist;
|
|
9766
|
+
for (let i = 0; i <= k; i++)
|
|
9767
|
+
S[k + i * n] = S[k + 1 + i * n] + (z[k + 1] - zb[k + 1]) * L[k + 1 + i * n];
|
|
9768
|
+
zb[k] = zs[k] + S[k + k * n];
|
|
9769
|
+
z[k] = ROUND(zb[k]);
|
|
9770
|
+
y = zb[k] - z[k];
|
|
9771
|
+
step[k] = SGN(y);
|
|
9772
|
+
} else {
|
|
9773
|
+
if (nn < m) {
|
|
9774
|
+
if (nn === 0 || newdist > s[imax]) imax = nn;
|
|
9775
|
+
for (let i = 0; i < n; i++) zn[i + nn * n] = z[i];
|
|
9776
|
+
s[nn++] = newdist;
|
|
9777
|
+
} else {
|
|
9778
|
+
if (newdist < s[imax]) {
|
|
9779
|
+
for (let i = 0; i < n; i++) zn[i + imax * n] = z[i];
|
|
9780
|
+
s[imax] = newdist;
|
|
9781
|
+
imax = 0;
|
|
9782
|
+
for (let i = 0; i < m; i++) if (s[imax] < s[i]) imax = i;
|
|
9783
|
+
}
|
|
9784
|
+
maxdist = s[imax];
|
|
9785
|
+
}
|
|
9786
|
+
z[0] += step[0];
|
|
9787
|
+
y = zb[0] - z[0];
|
|
9788
|
+
step[0] = -step[0] - SGN(step[0]);
|
|
9789
|
+
}
|
|
9790
|
+
} else {
|
|
9791
|
+
if (k === n - 1) break;
|
|
9792
|
+
k++;
|
|
9793
|
+
z[k] += step[k];
|
|
9794
|
+
y = zb[k] - z[k];
|
|
9795
|
+
step[k] = -step[k] - SGN(step[k]);
|
|
9796
|
+
}
|
|
9797
|
+
}
|
|
9798
|
+
for (let i = 0; i < m - 1; i++) {
|
|
9799
|
+
for (let j = i + 1; j < m; j++) {
|
|
9800
|
+
if (s[i] < s[j]) continue;
|
|
9801
|
+
const t = s[i];
|
|
9802
|
+
s[i] = s[j];
|
|
9803
|
+
s[j] = t;
|
|
9804
|
+
for (let l = 0; l < n; l++) {
|
|
9805
|
+
const u = zn[l + i * n];
|
|
9806
|
+
zn[l + i * n] = zn[l + j * n];
|
|
9807
|
+
zn[l + j * n] = u;
|
|
9808
|
+
}
|
|
9809
|
+
}
|
|
9810
|
+
}
|
|
9811
|
+
return c < LOOPMAX;
|
|
9812
|
+
}
|
|
9813
|
+
function solveZt(n, m, Z2, E) {
|
|
9814
|
+
const w = n + m;
|
|
9815
|
+
const M = new Float64Array(n * w);
|
|
9816
|
+
for (let r = 0; r < n; r++) {
|
|
9817
|
+
for (let cIdx = 0; cIdx < n; cIdx++) M[r * w + cIdx] = Z2[cIdx + r * n];
|
|
9818
|
+
for (let cIdx = 0; cIdx < m; cIdx++) M[r * w + n + cIdx] = E[r + cIdx * n];
|
|
9819
|
+
}
|
|
9820
|
+
for (let col = 0; col < n; col++) {
|
|
9821
|
+
let piv = col;
|
|
9822
|
+
for (let r = col + 1; r < n; r++)
|
|
9823
|
+
if (Math.abs(M[r * w + col]) > Math.abs(M[piv * w + col])) piv = r;
|
|
9824
|
+
if (Math.abs(M[piv * w + col]) < 1e-12) return null;
|
|
9825
|
+
if (piv !== col) {
|
|
9826
|
+
for (let cIdx = col; cIdx < w; cIdx++) {
|
|
9827
|
+
const t = M[col * w + cIdx];
|
|
9828
|
+
M[col * w + cIdx] = M[piv * w + cIdx];
|
|
9829
|
+
M[piv * w + cIdx] = t;
|
|
9830
|
+
}
|
|
9831
|
+
}
|
|
9832
|
+
const d = M[col * w + col];
|
|
9833
|
+
for (let cIdx = col; cIdx < w; cIdx++) M[col * w + cIdx] /= d;
|
|
9834
|
+
for (let r = 0; r < n; r++) {
|
|
9835
|
+
if (r === col) continue;
|
|
9836
|
+
const f = M[r * w + col];
|
|
9837
|
+
if (f === 0) continue;
|
|
9838
|
+
for (let cIdx = col; cIdx < w; cIdx++)
|
|
9839
|
+
M[r * w + cIdx] -= f * M[col * w + cIdx];
|
|
9840
|
+
}
|
|
9841
|
+
}
|
|
9842
|
+
const X = new Float64Array(n * m);
|
|
9843
|
+
for (let r = 0; r < n; r++)
|
|
9844
|
+
for (let cIdx = 0; cIdx < m; cIdx++) X[r + cIdx * n] = M[r * w + n + cIdx];
|
|
9845
|
+
return X;
|
|
9846
|
+
}
|
|
9847
|
+
function lambdaSearch(a, Q, n, m = 2) {
|
|
9848
|
+
if (n <= 0 || m <= 0 || a.length < n || Q.length < n * n) return null;
|
|
9849
|
+
const L = new Float64Array(n * n);
|
|
9850
|
+
const D = new Float64Array(n);
|
|
9851
|
+
if (!ldFactor(n, Q, L, D)) return null;
|
|
9852
|
+
const Z2 = new Float64Array(n * n);
|
|
9853
|
+
for (let i = 0; i < n; i++) Z2[i + i * n] = 1;
|
|
9854
|
+
reduction(n, L, D, Z2);
|
|
9855
|
+
const z = new Float64Array(n);
|
|
9856
|
+
for (let j = 0; j < n; j++) {
|
|
9857
|
+
let sum = 0;
|
|
9858
|
+
for (let k = 0; k < n; k++) sum += Z2[k + j * n] * a[k];
|
|
9859
|
+
z[j] = sum;
|
|
9860
|
+
}
|
|
9861
|
+
const E = new Float64Array(n * m);
|
|
9862
|
+
const s = new Float64Array(m);
|
|
9863
|
+
if (!search(n, m, L, D, z, E, s)) return null;
|
|
9864
|
+
const F = solveZt(n, m, Z2, E);
|
|
9865
|
+
if (!F) return null;
|
|
9866
|
+
const candidates = [];
|
|
9867
|
+
for (let c = 0; c < m; c++) {
|
|
9868
|
+
const v = new Float64Array(n);
|
|
9869
|
+
for (let i = 0; i < n; i++) v[i] = ROUND(F[i + c * n]);
|
|
9870
|
+
candidates.push(v);
|
|
9871
|
+
}
|
|
9872
|
+
const ratio = m >= 2 && s[0] > 0 ? s[1] / s[0] : Infinity;
|
|
9873
|
+
return { candidates, residuals: s, ratio };
|
|
9874
|
+
}
|
|
9875
|
+
function lambdaReduction(Q, n) {
|
|
9876
|
+
if (n <= 0 || Q.length < n * n) return null;
|
|
9877
|
+
const L = new Float64Array(n * n);
|
|
9878
|
+
const D = new Float64Array(n);
|
|
9879
|
+
if (!ldFactor(n, Q, L, D)) return null;
|
|
9880
|
+
const Z2 = new Float64Array(n * n);
|
|
9881
|
+
for (let i = 0; i < n; i++) Z2[i + i * n] = 1;
|
|
9882
|
+
reduction(n, L, D, Z2);
|
|
9883
|
+
return Z2;
|
|
9884
|
+
}
|
|
9885
|
+
|
|
9886
|
+
// src/positioning/rtk.ts
|
|
9887
|
+
var L1_CODES = {
|
|
9888
|
+
G: ["1C"],
|
|
9889
|
+
E: ["1C"],
|
|
9890
|
+
R: ["1C"],
|
|
9891
|
+
J: ["1C"],
|
|
9892
|
+
C: ["2I", "1P"]
|
|
9893
|
+
// B1I first (classic B1), then B1C
|
|
9894
|
+
};
|
|
9895
|
+
function toRtkEpoch(meas) {
|
|
9896
|
+
const out = /* @__PURE__ */ new Map();
|
|
9897
|
+
const rank = /* @__PURE__ */ new Map();
|
|
9898
|
+
for (const m of meas) {
|
|
9899
|
+
const prefs = L1_CODES[m.prn[0]];
|
|
9900
|
+
if (!prefs || m.pr === null || !Number.isFinite(m.pr)) continue;
|
|
9901
|
+
const r = prefs.indexOf(m.code);
|
|
9902
|
+
if (r < 0) continue;
|
|
9903
|
+
const prev = rank.get(m.prn);
|
|
9904
|
+
if (prev !== void 0 && prev <= r) continue;
|
|
9905
|
+
rank.set(m.prn, r);
|
|
9906
|
+
out.set(m.prn, {
|
|
9907
|
+
code: m.code,
|
|
9908
|
+
pr: m.pr,
|
|
9909
|
+
cp: m.cp ?? null,
|
|
9910
|
+
lockTimeMs: m.lockTimeS !== void 0 ? Math.round(m.lockTimeS * 1e3) : void 0,
|
|
9911
|
+
gloChannel: m.gloChannel ?? null
|
|
9912
|
+
});
|
|
9913
|
+
}
|
|
9914
|
+
return out;
|
|
9915
|
+
}
|
|
9916
|
+
function carrierFreqHz(sys, code, gloChannel) {
|
|
9917
|
+
const band = code[0];
|
|
9918
|
+
switch (sys) {
|
|
9919
|
+
case "G":
|
|
9920
|
+
case "J":
|
|
9921
|
+
return band === "1" ? 157542e4 : band === "2" ? 12276e5 : 117645e4;
|
|
9922
|
+
case "E":
|
|
9923
|
+
return band === "1" ? 157542e4 : band === "5" ? 117645e4 : band === "7" ? 120714e4 : band === "8" ? 1191795e3 : 127875e4;
|
|
9924
|
+
case "C":
|
|
9925
|
+
return band === "1" ? 157542e4 : band === "2" ? 1561098e3 : band === "5" ? 117645e4 : band === "7" ? 120714e4 : 126852e4;
|
|
9926
|
+
case "R": {
|
|
9927
|
+
if (gloChannel === null) return 0;
|
|
9928
|
+
if (band === "1") return 1602e6 + gloChannel * 562500;
|
|
9929
|
+
if (band === "2") return 1246e6 + gloChannel * 437500;
|
|
9930
|
+
return 1202025e3;
|
|
9931
|
+
}
|
|
9932
|
+
default:
|
|
9933
|
+
return 0;
|
|
9934
|
+
}
|
|
9935
|
+
}
|
|
9936
|
+
function sagnac(pos, travelTimeS) {
|
|
9937
|
+
const theta = OMEGA_E * travelTimeS;
|
|
9938
|
+
const c = Math.cos(theta);
|
|
9939
|
+
const s = Math.sin(theta);
|
|
9940
|
+
return [pos.x * c + pos.y * s, -pos.x * s + pos.y * c, pos.z];
|
|
9941
|
+
}
|
|
9942
|
+
function tropoDelay(elevationRad) {
|
|
9943
|
+
const sinEl = Math.sin(elevationRad);
|
|
9944
|
+
return 2.47 / (sinEl + 0.0121);
|
|
9945
|
+
}
|
|
9946
|
+
function resolveEphemeris(src, prn, timeMs) {
|
|
9947
|
+
if (Array.isArray(src))
|
|
9948
|
+
return selectEphemeris(src, prn, timeMs);
|
|
9949
|
+
return src.get(prn) ?? null;
|
|
9950
|
+
}
|
|
9951
|
+
function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, troposphere) {
|
|
9952
|
+
const out = [];
|
|
9953
|
+
for (const [prn, mR] of rover) {
|
|
9954
|
+
const sys = prn[0];
|
|
9955
|
+
if (!"GERCJ".includes(sys)) continue;
|
|
9956
|
+
const mB = base.get(prn);
|
|
9957
|
+
if (!mB || mB.code !== mR.code) continue;
|
|
9958
|
+
if (mR.pr === null || mB.pr === null || !Number.isFinite(mR.pr) || !Number.isFinite(mB.pr))
|
|
9959
|
+
continue;
|
|
9960
|
+
const eph = resolveEphemeris(ephemerides, prn, timeMs);
|
|
9961
|
+
if (!eph) continue;
|
|
9962
|
+
const satAt = (pr) => {
|
|
9963
|
+
const tTx = timeMs - pr / C_LIGHT * 1e3;
|
|
9964
|
+
const dts = satClockCorrection(eph, tTx);
|
|
9965
|
+
const sat = computeSatPosition(eph, tTx - dts * 1e3);
|
|
9966
|
+
return Number.isFinite(sat.x) ? sat : null;
|
|
9967
|
+
};
|
|
9968
|
+
const satB = satAt(mB.pr);
|
|
9969
|
+
const satR = satAt(mR.pr);
|
|
9970
|
+
if (!satB || !satR) continue;
|
|
9971
|
+
const travelB = Math.hypot(
|
|
9972
|
+
satB.x - basePos[0],
|
|
9973
|
+
satB.y - basePos[1],
|
|
9974
|
+
satB.z - basePos[2]
|
|
9975
|
+
) / C_LIGHT;
|
|
9976
|
+
const [bx, by, bz] = sagnac(satB, travelB);
|
|
9977
|
+
const rhoB = Math.hypot(bx - basePos[0], by - basePos[1], bz - basePos[2]);
|
|
9978
|
+
const elB = ecefToAzEl(basePos[0], basePos[1], basePos[2], bx, by, bz).el;
|
|
9979
|
+
if (elB < maskRad) continue;
|
|
9980
|
+
const gloK = sys === "R" ? mR.gloChannel ?? mB.gloChannel ?? eph.freqNum ?? null : null;
|
|
9981
|
+
const freq = carrierFreqHz(sys, mR.code, gloK);
|
|
9982
|
+
out.push({
|
|
9983
|
+
prn,
|
|
9984
|
+
group: sys + mR.code,
|
|
9985
|
+
lambda: freq > 0 ? C_LIGHT / freq : 0,
|
|
9986
|
+
satR,
|
|
9987
|
+
rhoB,
|
|
9988
|
+
elB,
|
|
9989
|
+
tropoB: troposphere ? tropoDelay(elB) : 0,
|
|
9990
|
+
prR: mR.pr,
|
|
9991
|
+
prB: mB.pr,
|
|
9992
|
+
cpR: mR.cp ?? null,
|
|
9993
|
+
cpB: mB.cp ?? null,
|
|
9994
|
+
lockR: mR.lockTimeMs,
|
|
9995
|
+
lockB: mB.lockTimeMs
|
|
9996
|
+
});
|
|
9997
|
+
}
|
|
9998
|
+
return out;
|
|
9999
|
+
}
|
|
10000
|
+
function roverTerms(g, x, y, z, troposphere) {
|
|
10001
|
+
const travel = Math.hypot(g.satR.x - x, g.satR.y - y, g.satR.z - z) / C_LIGHT;
|
|
10002
|
+
const [sx, sy, sz] = sagnac(g.satR, travel);
|
|
10003
|
+
const rho = Math.hypot(sx - x, sy - y, sz - z);
|
|
10004
|
+
const u = [
|
|
10005
|
+
(x - sx) / rho,
|
|
10006
|
+
(y - sy) / rho,
|
|
10007
|
+
(z - sz) / rho
|
|
10008
|
+
];
|
|
10009
|
+
let dTropo = 0;
|
|
10010
|
+
if (troposphere) {
|
|
10011
|
+
const elR = ecefToAzEl(x, y, z, sx, sy, sz).el;
|
|
10012
|
+
dTropo = tropoDelay(Math.max(elR, 0.05)) - g.tropoB;
|
|
10013
|
+
}
|
|
10014
|
+
return { rho, u, dTropo };
|
|
10015
|
+
}
|
|
10016
|
+
function sdVariance(sigmaM, elRad) {
|
|
10017
|
+
const sinEl = Math.max(Math.sin(elRad), 0.05);
|
|
10018
|
+
return 2 * sigmaM * sigmaM * (1 + 1 / (sinEl * sinEl));
|
|
10019
|
+
}
|
|
10020
|
+
function zeros(n, m) {
|
|
10021
|
+
return Array.from({ length: n }, () => new Array(m).fill(0));
|
|
10022
|
+
}
|
|
10023
|
+
function matInv(A) {
|
|
10024
|
+
const n = A.length;
|
|
10025
|
+
const M = A.map((row, i) => {
|
|
10026
|
+
const r = [...row, ...new Array(n).fill(0)];
|
|
10027
|
+
r[n + i] = 1;
|
|
10028
|
+
return r;
|
|
10029
|
+
});
|
|
10030
|
+
for (let col = 0; col < n; col++) {
|
|
10031
|
+
let pivot = col;
|
|
10032
|
+
for (let r = col + 1; r < n; r++) {
|
|
10033
|
+
if (Math.abs(M[r][col]) > Math.abs(M[pivot][col])) pivot = r;
|
|
10034
|
+
}
|
|
10035
|
+
if (Math.abs(M[pivot][col]) < 1e-13) return null;
|
|
10036
|
+
[M[col], M[pivot]] = [M[pivot], M[col]];
|
|
10037
|
+
const d = M[col][col];
|
|
10038
|
+
for (let c = 0; c < 2 * n; c++) M[col][c] /= d;
|
|
10039
|
+
for (let r = 0; r < n; r++) {
|
|
10040
|
+
if (r === col) continue;
|
|
10041
|
+
const f = M[r][col];
|
|
10042
|
+
if (f === 0) continue;
|
|
10043
|
+
for (let c = 0; c < 2 * n; c++) M[r][c] -= f * M[col][c];
|
|
10044
|
+
}
|
|
10045
|
+
}
|
|
10046
|
+
return M.map((row) => row.slice(n));
|
|
10047
|
+
}
|
|
10048
|
+
function matMul(A, B) {
|
|
10049
|
+
const n = A.length;
|
|
10050
|
+
const k = B.length;
|
|
10051
|
+
const m = B[0]?.length ?? 0;
|
|
10052
|
+
const C = zeros(n, m);
|
|
10053
|
+
for (let i = 0; i < n; i++) {
|
|
10054
|
+
for (let l = 0; l < k; l++) {
|
|
10055
|
+
const a = A[i][l];
|
|
10056
|
+
if (a === 0) continue;
|
|
10057
|
+
for (let j = 0; j < m; j++) C[i][j] += a * B[l][j];
|
|
10058
|
+
}
|
|
10059
|
+
}
|
|
10060
|
+
return C;
|
|
10061
|
+
}
|
|
10062
|
+
function transpose(A) {
|
|
10063
|
+
const n = A.length;
|
|
10064
|
+
const m = A[0]?.length ?? 0;
|
|
10065
|
+
const T = zeros(m, n);
|
|
10066
|
+
for (let i = 0; i < n; i++) for (let j = 0; j < m; j++) T[j][i] = A[i][j];
|
|
10067
|
+
return T;
|
|
10068
|
+
}
|
|
10069
|
+
function groupEntries(geom) {
|
|
10070
|
+
const groups = /* @__PURE__ */ new Map();
|
|
10071
|
+
for (const g of geom) {
|
|
10072
|
+
const list = groups.get(g.group);
|
|
10073
|
+
if (list) list.push(g);
|
|
10074
|
+
else groups.set(g.group, [g]);
|
|
10075
|
+
}
|
|
10076
|
+
return groups;
|
|
10077
|
+
}
|
|
10078
|
+
function ddCovariance(rows, sigmaCode, sigmaPhase) {
|
|
10079
|
+
const n = rows.length;
|
|
10080
|
+
const R = zeros(n, n);
|
|
10081
|
+
const sd2 = (r, g) => sdVariance(r.kind === "code" ? sigmaCode : sigmaPhase, g.elB);
|
|
10082
|
+
for (let i = 0; i < n; i++) {
|
|
10083
|
+
const ri = rows[i];
|
|
10084
|
+
R[i][i] = sd2(ri, ri.g) + sd2(ri, ri.ref);
|
|
10085
|
+
for (let j = i + 1; j < n; j++) {
|
|
10086
|
+
const rj = rows[j];
|
|
10087
|
+
if (ri.ref.prn === rj.ref.prn && ri.kind === rj.kind && ri.g.group === rj.g.group) {
|
|
10088
|
+
R[i][j] = R[j][i] = sd2(ri, ri.ref);
|
|
10089
|
+
}
|
|
10090
|
+
}
|
|
10091
|
+
}
|
|
10092
|
+
return R;
|
|
10093
|
+
}
|
|
10094
|
+
function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
|
|
10095
|
+
const {
|
|
10096
|
+
elevationMaskDeg = 10,
|
|
10097
|
+
troposphere = true,
|
|
10098
|
+
maxIterations = 15,
|
|
10099
|
+
convergenceM = 1e-4,
|
|
10100
|
+
codeSigmaM = 0.3,
|
|
10101
|
+
rejectThresholdM = 20,
|
|
10102
|
+
initialPosition
|
|
10103
|
+
} = opts;
|
|
10104
|
+
let geom = buildGeometry(
|
|
10105
|
+
rover,
|
|
10106
|
+
base,
|
|
10107
|
+
basePos,
|
|
10108
|
+
ephemerides,
|
|
10109
|
+
timeMs,
|
|
10110
|
+
elevationMaskDeg * Math.PI / 180,
|
|
10111
|
+
troposphere
|
|
10112
|
+
);
|
|
10113
|
+
const rejected = [];
|
|
10114
|
+
for (; ; ) {
|
|
10115
|
+
const groups = groupEntries(geom);
|
|
10116
|
+
const rows = [];
|
|
10117
|
+
const refSatellites = {};
|
|
10118
|
+
for (const [group, list] of groups) {
|
|
10119
|
+
if (list.length < 2) continue;
|
|
10120
|
+
const ref = list.reduce((a, b) => b.elB > a.elB ? b : a);
|
|
10121
|
+
refSatellites[group] = ref.prn;
|
|
10122
|
+
for (const g of list) {
|
|
10123
|
+
if (g === ref) continue;
|
|
10124
|
+
rows.push({
|
|
10125
|
+
g,
|
|
10126
|
+
ref,
|
|
10127
|
+
kind: "code",
|
|
10128
|
+
z: g.prR - g.prB - (ref.prR - ref.prB)
|
|
10129
|
+
});
|
|
10130
|
+
}
|
|
10131
|
+
}
|
|
10132
|
+
if (rows.length < 3) return null;
|
|
10133
|
+
const W = matInv(ddCovariance(rows, codeSigmaM, codeSigmaM));
|
|
10134
|
+
if (!W) return null;
|
|
10135
|
+
let [x, y, z] = initialPosition ?? basePos;
|
|
10136
|
+
let iterations = 0;
|
|
10137
|
+
let converged = false;
|
|
10138
|
+
const residuals = {};
|
|
10139
|
+
let cov = null;
|
|
10140
|
+
for (let iter = 0; iter < maxIterations; iter++) {
|
|
10141
|
+
iterations = iter + 1;
|
|
10142
|
+
const H2 = [];
|
|
10143
|
+
const v = [];
|
|
10144
|
+
const terms = /* @__PURE__ */ new Map();
|
|
10145
|
+
const termOf = (g) => {
|
|
10146
|
+
let t = terms.get(g.prn);
|
|
10147
|
+
if (!t) {
|
|
10148
|
+
t = roverTerms(g, x, y, z, troposphere);
|
|
10149
|
+
terms.set(g.prn, t);
|
|
10150
|
+
}
|
|
10151
|
+
return t;
|
|
10152
|
+
};
|
|
10153
|
+
for (const row of rows) {
|
|
10154
|
+
const ts = termOf(row.g);
|
|
10155
|
+
const tr = termOf(row.ref);
|
|
10156
|
+
const pred = ts.rho - row.g.rhoB + ts.dTropo - (tr.rho - row.ref.rhoB + tr.dTropo);
|
|
10157
|
+
v.push(row.z - pred);
|
|
10158
|
+
H2.push([ts.u[0] - tr.u[0], ts.u[1] - tr.u[1], ts.u[2] - tr.u[2]]);
|
|
10159
|
+
}
|
|
10160
|
+
const Ht = transpose(H2);
|
|
10161
|
+
const HtW = matMul(Ht, W);
|
|
10162
|
+
const N = matMul(HtW, H2);
|
|
10163
|
+
const b = matMul(
|
|
10164
|
+
HtW,
|
|
10165
|
+
v.map((s) => [s])
|
|
10166
|
+
);
|
|
10167
|
+
const Ninv = matInv(N);
|
|
10168
|
+
if (!Ninv) return null;
|
|
10169
|
+
const dx = matMul(
|
|
10170
|
+
Ninv,
|
|
10171
|
+
b.map((r) => [r[0]])
|
|
10172
|
+
);
|
|
10173
|
+
x += dx[0][0];
|
|
10174
|
+
y += dx[1][0];
|
|
10175
|
+
z += dx[2][0];
|
|
10176
|
+
cov = Ninv;
|
|
10177
|
+
rows.forEach((row, i) => {
|
|
10178
|
+
residuals[row.g.prn] = v[i] - (H2[i][0] * dx[0][0] + H2[i][1] * dx[1][0] + H2[i][2] * dx[2][0]);
|
|
10179
|
+
});
|
|
10180
|
+
if (Math.hypot(dx[0][0], dx[1][0], dx[2][0]) < convergenceM) {
|
|
10181
|
+
converged = true;
|
|
10182
|
+
break;
|
|
10183
|
+
}
|
|
10184
|
+
}
|
|
10185
|
+
let worst = null;
|
|
10186
|
+
let worstAbs = rejectThresholdM;
|
|
10187
|
+
for (const [prn, r] of Object.entries(residuals)) {
|
|
10188
|
+
if (Math.abs(r) > worstAbs) {
|
|
10189
|
+
worstAbs = Math.abs(r);
|
|
10190
|
+
worst = prn;
|
|
10191
|
+
}
|
|
10192
|
+
}
|
|
10193
|
+
if (worst && geom.length > 4) {
|
|
10194
|
+
rejected.push(worst);
|
|
10195
|
+
geom = geom.filter((g) => g.prn !== worst);
|
|
10196
|
+
continue;
|
|
10197
|
+
}
|
|
10198
|
+
const usedSatellites = [
|
|
10199
|
+
...new Set(rows.flatMap((r) => [r.g.prn, r.ref.prn]))
|
|
10200
|
+
].sort();
|
|
10201
|
+
return {
|
|
10202
|
+
position: [x, y, z],
|
|
10203
|
+
baseline: [x - basePos[0], y - basePos[1], z - basePos[2]],
|
|
10204
|
+
sigmas: cov ? [
|
|
10205
|
+
Math.sqrt(Math.max(cov[0][0], 0)),
|
|
10206
|
+
Math.sqrt(Math.max(cov[1][1], 0)),
|
|
10207
|
+
Math.sqrt(Math.max(cov[2][2], 0))
|
|
10208
|
+
] : [0, 0, 0],
|
|
10209
|
+
usedSatellites,
|
|
10210
|
+
refSatellites,
|
|
10211
|
+
residuals,
|
|
10212
|
+
rejectedSatellites: rejected,
|
|
10213
|
+
nSats: usedSatellites.length,
|
|
10214
|
+
iterations,
|
|
10215
|
+
converged
|
|
10216
|
+
};
|
|
10217
|
+
}
|
|
10218
|
+
}
|
|
10219
|
+
var RtkFloatEngine = class {
|
|
10220
|
+
basePos;
|
|
10221
|
+
ephemerides;
|
|
10222
|
+
o;
|
|
10223
|
+
/** State vector [x, y, z, N₁…] and covariance; null before init. */
|
|
10224
|
+
x = null;
|
|
10225
|
+
P = [];
|
|
10226
|
+
amb = [];
|
|
10227
|
+
refs = /* @__PURE__ */ new Map();
|
|
10228
|
+
track = /* @__PURE__ */ new Map();
|
|
10229
|
+
lastMs = null;
|
|
10230
|
+
constructor(basePos, ephemerides, opts = {}) {
|
|
10231
|
+
this.basePos = basePos;
|
|
10232
|
+
this.ephemerides = ephemerides;
|
|
10233
|
+
this.o = {
|
|
10234
|
+
mode: opts.mode ?? "kinematic",
|
|
10235
|
+
elevationMaskDeg: opts.elevationMaskDeg ?? 10,
|
|
10236
|
+
troposphere: opts.troposphere ?? true,
|
|
10237
|
+
codeSigmaM: opts.codeSigmaM ?? 0.3,
|
|
10238
|
+
phaseSigmaM: opts.phaseSigmaM ?? 3e-3,
|
|
10239
|
+
processNoisePosM: opts.processNoisePosM ?? 0,
|
|
10240
|
+
kinematicSigmaM: opts.kinematicSigmaM ?? 30,
|
|
10241
|
+
ambProcessNoiseCycles: opts.ambProcessNoiseCycles ?? 1e-4,
|
|
10242
|
+
ambInitSigmaCycles: opts.ambInitSigmaCycles ?? 30,
|
|
10243
|
+
maxGapMs: opts.maxGapMs ?? 1e4,
|
|
10244
|
+
codeGateM: opts.codeGateM ?? 30,
|
|
10245
|
+
slipGateM: opts.slipGateM ?? 5,
|
|
10246
|
+
updateIterations: opts.updateIterations ?? 2,
|
|
10247
|
+
ambiguityResolution: opts.ambiguityResolution ?? "off",
|
|
10248
|
+
ratioThreshold: opts.ratioThreshold ?? 3,
|
|
10249
|
+
arElevationMaskDeg: opts.arElevationMaskDeg ?? 0,
|
|
10250
|
+
glonassAr: opts.glonassAr ?? false,
|
|
10251
|
+
partialFixing: opts.partialFixing ?? true,
|
|
10252
|
+
minFixAmbiguities: opts.minFixAmbiguities ?? 4
|
|
10253
|
+
};
|
|
10254
|
+
}
|
|
10255
|
+
/** Clear all filter state (position, ambiguities, lock history). */
|
|
10256
|
+
reset() {
|
|
10257
|
+
this.x = null;
|
|
10258
|
+
this.P = [];
|
|
10259
|
+
this.amb = [];
|
|
10260
|
+
this.refs.clear();
|
|
10261
|
+
this.track.clear();
|
|
10262
|
+
this.lastMs = null;
|
|
10263
|
+
}
|
|
10264
|
+
ambIndex(prn) {
|
|
10265
|
+
return this.amb.findIndex((a) => a.prn === prn);
|
|
10266
|
+
}
|
|
10267
|
+
/** Remove one ambiguity state (row/col) from x and P. */
|
|
10268
|
+
dropAmb(idx) {
|
|
10269
|
+
const s = 3 + idx;
|
|
10270
|
+
this.amb.splice(idx, 1);
|
|
10271
|
+
this.x.splice(s, 1);
|
|
10272
|
+
this.P.splice(s, 1);
|
|
10273
|
+
for (const row of this.P) row.splice(s, 1);
|
|
10274
|
+
}
|
|
10275
|
+
/** Append an ambiguity state with the given value and variance. */
|
|
10276
|
+
addAmb(entry, value, variance) {
|
|
10277
|
+
this.amb.push(entry);
|
|
10278
|
+
this.x.push(value);
|
|
10279
|
+
const n = this.x.length;
|
|
10280
|
+
for (const row of this.P) row.push(0);
|
|
10281
|
+
const newRow = new Array(n).fill(0);
|
|
10282
|
+
newRow[n - 1] = variance;
|
|
10283
|
+
this.P.push(newRow);
|
|
10284
|
+
}
|
|
10285
|
+
/**
|
|
10286
|
+
* Re-map a group's DD ambiguities from the old reference r to the
|
|
10287
|
+
* new reference r' (which must hold a state): N_i' = N_i − N_r' and
|
|
10288
|
+
* the old reference's slot becomes N_r = −N_r'. Exact linear
|
|
10289
|
+
* transform of state and covariance (P' = T P Tᵀ).
|
|
10290
|
+
*/
|
|
10291
|
+
retarget(group, oldRef, newRefIdx) {
|
|
10292
|
+
const n = this.x.length;
|
|
10293
|
+
const j = 3 + newRefIdx;
|
|
10294
|
+
const T = zeros(n, n);
|
|
10295
|
+
for (let i = 0; i < n; i++) T[i][i] = 1;
|
|
10296
|
+
for (let k = 0; k < this.amb.length; k++) {
|
|
10297
|
+
if (this.amb[k].group !== group) continue;
|
|
10298
|
+
const s = 3 + k;
|
|
10299
|
+
if (s === j) T[s][s] = -1;
|
|
10300
|
+
else T[s][j] -= 1;
|
|
10301
|
+
}
|
|
10302
|
+
this.x = matMul(
|
|
10303
|
+
T,
|
|
10304
|
+
this.x.map((v) => [v])
|
|
10305
|
+
).map((r) => r[0]);
|
|
10306
|
+
this.P = matMul(matMul(T, this.P), transpose(T));
|
|
10307
|
+
const entry = this.amb[newRefIdx];
|
|
10308
|
+
entry.prn = oldRef;
|
|
10309
|
+
}
|
|
10310
|
+
/**
|
|
10311
|
+
* Try to fix the ambiguity subset `idxs` (indices into `this.amb`):
|
|
10312
|
+
* extract the float subvector b and covariance Q_b from the filter,
|
|
10313
|
+
* run MLAMBDA and, without touching the filter state, condition the
|
|
10314
|
+
* position on the best integer candidate,
|
|
10315
|
+
* xa = x − P_xb·Q_b⁻¹·(b − ǎ)
|
|
10316
|
+
* (RTKLIB rtkpos.c `resamb_LAMBDA`). Returns null when Q_b is not
|
|
10317
|
+
* positive definite / invertible or the search fails; ratio
|
|
10318
|
+
* validation is the caller's job.
|
|
10319
|
+
*/
|
|
10320
|
+
tryFix(idxs) {
|
|
10321
|
+
const k = idxs.length;
|
|
10322
|
+
const b = new Float64Array(k);
|
|
10323
|
+
const Qb = new Float64Array(k * k);
|
|
10324
|
+
const QbRows = [];
|
|
10325
|
+
for (let i = 0; i < k; i++) {
|
|
10326
|
+
const si = 3 + idxs[i];
|
|
10327
|
+
b[i] = this.x[si];
|
|
10328
|
+
const row = [];
|
|
10329
|
+
for (let j = 0; j < k; j++) {
|
|
10330
|
+
const sj = 3 + idxs[j];
|
|
10331
|
+
const q = (this.P[si][sj] + this.P[sj][si]) / 2;
|
|
10332
|
+
Qb[i + j * k] = q;
|
|
10333
|
+
row.push(q);
|
|
10334
|
+
}
|
|
10335
|
+
QbRows.push(row);
|
|
10336
|
+
}
|
|
10337
|
+
const res = lambdaSearch(b, Qb, k, 2);
|
|
10338
|
+
if (!res) return null;
|
|
10339
|
+
const best = res.candidates[0];
|
|
10340
|
+
const QbInv = matInv(QbRows);
|
|
10341
|
+
if (!QbInv) return null;
|
|
10342
|
+
const db = [];
|
|
10343
|
+
for (let i = 0; i < k; i++) db.push(b[i] - best[i]);
|
|
10344
|
+
const position = [
|
|
10345
|
+
this.x[0],
|
|
10346
|
+
this.x[1],
|
|
10347
|
+
this.x[2]
|
|
10348
|
+
];
|
|
10349
|
+
for (let r = 0; r < 3; r++) {
|
|
10350
|
+
let dx = 0;
|
|
10351
|
+
for (let i = 0; i < k; i++) {
|
|
10352
|
+
let yi = 0;
|
|
10353
|
+
for (let j = 0; j < k; j++) yi += QbInv[i][j] * db[j];
|
|
10354
|
+
dx += this.P[r][3 + idxs[i]] * yi;
|
|
10355
|
+
}
|
|
10356
|
+
position[r] -= dx;
|
|
10357
|
+
}
|
|
10358
|
+
return { ratio: res.ratio, position, intAmb: [...best] };
|
|
10359
|
+
}
|
|
10360
|
+
/**
|
|
10361
|
+
* Process one synchronized epoch (same nominal `timeMs` for both
|
|
10362
|
+
* receivers). Returns null until a first position can be
|
|
10363
|
+
* initialised (via an internal DGNSS solve) or when the epoch has
|
|
10364
|
+
* fewer than 3 usable DD rows.
|
|
10365
|
+
*/
|
|
10366
|
+
process(rover, base, timeMs) {
|
|
10367
|
+
const o = this.o;
|
|
10368
|
+
const geom = buildGeometry(
|
|
10369
|
+
rover,
|
|
10370
|
+
base,
|
|
10371
|
+
this.basePos,
|
|
10372
|
+
this.ephemerides,
|
|
10373
|
+
timeMs,
|
|
10374
|
+
o.elevationMaskDeg * Math.PI / 180,
|
|
10375
|
+
o.troposphere
|
|
10376
|
+
);
|
|
10377
|
+
const byPrn = new Map(geom.map((g) => [g.prn, g]));
|
|
10378
|
+
const dtS = this.lastMs !== null ? (timeMs - this.lastMs) / 1e3 : 0;
|
|
10379
|
+
if (this.x) {
|
|
10380
|
+
for (let i = this.amb.length - 1; i >= 0; i--) {
|
|
10381
|
+
const prn = this.amb[i].prn;
|
|
10382
|
+
const tr = this.track.get(prn);
|
|
10383
|
+
const seen = byPrn.get(prn);
|
|
10384
|
+
const hasPhase = seen && seen.cpR !== null && seen.cpB !== null;
|
|
10385
|
+
const staleMs = tr ? timeMs - tr.lastMs : Infinity;
|
|
10386
|
+
if (!hasPhase && staleMs > o.maxGapMs) this.dropAmb(i);
|
|
10387
|
+
}
|
|
10388
|
+
}
|
|
10389
|
+
const slipped = /* @__PURE__ */ new Set();
|
|
10390
|
+
for (const g of geom) {
|
|
10391
|
+
if (g.cpR === null || g.cpB === null) continue;
|
|
10392
|
+
const tr = this.track.get(g.prn);
|
|
10393
|
+
if (!tr) continue;
|
|
10394
|
+
if (timeMs - tr.lastMs > o.maxGapMs) slipped.add(g.prn);
|
|
10395
|
+
else if (g.lockR !== void 0 && tr.lockR !== void 0 && g.lockR < tr.lockR || g.lockB !== void 0 && tr.lockB !== void 0 && g.lockB < tr.lockB)
|
|
10396
|
+
slipped.add(g.prn);
|
|
10397
|
+
}
|
|
10398
|
+
if (!this.x) {
|
|
10399
|
+
const dg = solveDgnss(
|
|
10400
|
+
rover,
|
|
10401
|
+
base,
|
|
10402
|
+
this.basePos,
|
|
10403
|
+
this.ephemerides,
|
|
10404
|
+
timeMs,
|
|
10405
|
+
{
|
|
10406
|
+
elevationMaskDeg: o.elevationMaskDeg,
|
|
10407
|
+
troposphere: o.troposphere,
|
|
10408
|
+
codeSigmaM: o.codeSigmaM
|
|
10409
|
+
}
|
|
10410
|
+
);
|
|
10411
|
+
if (!dg) return null;
|
|
10412
|
+
this.x = [...dg.position];
|
|
10413
|
+
this.P = zeros(3, 3);
|
|
10414
|
+
for (let i = 0; i < 3; i++) {
|
|
10415
|
+
const s = Math.max(dg.sigmas[i], 1);
|
|
10416
|
+
this.P[i][i] = 25 * s * s;
|
|
10417
|
+
}
|
|
10418
|
+
this.amb = [];
|
|
10419
|
+
} else if (o.mode === "kinematic") {
|
|
10420
|
+
for (let i = 0; i < 3; i++) {
|
|
10421
|
+
for (let j = 0; j < this.x.length; j++) {
|
|
10422
|
+
this.P[i][j] = 0;
|
|
10423
|
+
this.P[j][i] = 0;
|
|
10424
|
+
}
|
|
10425
|
+
this.P[i][i] = o.kinematicSigmaM * o.kinematicSigmaM;
|
|
10426
|
+
}
|
|
10427
|
+
} else {
|
|
10428
|
+
const q = o.processNoisePosM * o.processNoisePosM * dtS;
|
|
10429
|
+
for (let i = 0; i < 3; i++) this.P[i][i] += q;
|
|
10430
|
+
}
|
|
10431
|
+
const qAmb = o.ambProcessNoiseCycles * o.ambProcessNoiseCycles * dtS;
|
|
10432
|
+
for (let k = 0; k < this.amb.length; k++) this.P[3 + k][3 + k] += qAmb;
|
|
10433
|
+
const groups = groupEntries(geom);
|
|
10434
|
+
const newRefs = /* @__PURE__ */ new Map();
|
|
10435
|
+
for (const [group, list] of groups) {
|
|
10436
|
+
if (list.length < 2) continue;
|
|
10437
|
+
const phase = list.filter(
|
|
10438
|
+
(g) => g.cpR !== null && g.cpB !== null && !slipped.has(g.prn)
|
|
10439
|
+
);
|
|
10440
|
+
const pool = phase.length >= 2 ? phase : list;
|
|
10441
|
+
const prevRef = this.refs.get(group);
|
|
10442
|
+
const best = pool.reduce((a, b) => b.elB > a.elB ? b : a);
|
|
10443
|
+
let ref = best;
|
|
10444
|
+
if (best.prn !== prevRef) {
|
|
10445
|
+
const anyState = this.amb.some((a) => a.group === group);
|
|
10446
|
+
if (anyState) {
|
|
10447
|
+
if (this.ambIndex(best.prn) >= 0) {
|
|
10448
|
+
this.retarget(group, prevRef ?? best.prn, this.ambIndex(best.prn));
|
|
10449
|
+
} else if (prevRef && pool.some((g) => g.prn === prevRef)) {
|
|
10450
|
+
ref = pool.find((g) => g.prn === prevRef);
|
|
10451
|
+
} else {
|
|
10452
|
+
for (let i = this.amb.length - 1; i >= 0; i--)
|
|
10453
|
+
if (this.amb[i].group === group) this.dropAmb(i);
|
|
10454
|
+
}
|
|
10455
|
+
}
|
|
10456
|
+
}
|
|
10457
|
+
newRefs.set(group, ref);
|
|
10458
|
+
this.refs.set(group, ref.prn);
|
|
10459
|
+
}
|
|
10460
|
+
for (const group of [...this.refs.keys()])
|
|
10461
|
+
if (!newRefs.has(group)) this.refs.delete(group);
|
|
10462
|
+
for (const prn of slipped) {
|
|
10463
|
+
const i = this.ambIndex(prn);
|
|
10464
|
+
if (i >= 0) this.dropAmb(i);
|
|
10465
|
+
}
|
|
10466
|
+
const initAmb = (g, ref) => {
|
|
10467
|
+
if (g.lambda <= 0 || ref.lambda <= 0) return;
|
|
10468
|
+
const zPhi = g.lambda * (g.cpR - g.cpB) - ref.lambda * (ref.cpR - ref.cpB);
|
|
10469
|
+
const zP = g.prR - g.prB - (ref.prR - ref.prB);
|
|
10470
|
+
const n0 = (zPhi - zP) / g.lambda;
|
|
10471
|
+
this.addAmb(
|
|
10472
|
+
{ prn: g.prn, group: g.group, lambda: g.lambda },
|
|
10473
|
+
n0,
|
|
10474
|
+
o.ambInitSigmaCycles * o.ambInitSigmaCycles
|
|
10475
|
+
);
|
|
10476
|
+
};
|
|
10477
|
+
for (const [group, ref] of newRefs) {
|
|
10478
|
+
for (const g of groups.get(group)) {
|
|
10479
|
+
if (g === ref || g.cpR === null || g.cpB === null) continue;
|
|
10480
|
+
if (ref.cpR === null || ref.cpB === null) continue;
|
|
10481
|
+
const i = this.ambIndex(g.prn);
|
|
10482
|
+
if (i >= 0) {
|
|
10483
|
+
this.amb[i].lambda = g.lambda;
|
|
10484
|
+
this.amb[i].group = g.group;
|
|
10485
|
+
continue;
|
|
10486
|
+
}
|
|
10487
|
+
initAmb(g, ref);
|
|
10488
|
+
}
|
|
10489
|
+
}
|
|
10490
|
+
for (let i = this.amb.length - 1; i >= 0; i--) {
|
|
10491
|
+
const a = this.amb[i];
|
|
10492
|
+
if (newRefs.get(a.group)?.prn === a.prn) this.dropAmb(i);
|
|
10493
|
+
}
|
|
10494
|
+
let rows = [];
|
|
10495
|
+
for (const [group, ref] of newRefs) {
|
|
10496
|
+
for (const g of groups.get(group)) {
|
|
10497
|
+
if (g === ref) continue;
|
|
10498
|
+
rows.push({
|
|
10499
|
+
g,
|
|
10500
|
+
ref,
|
|
10501
|
+
kind: "code",
|
|
10502
|
+
z: g.prR - g.prB - (ref.prR - ref.prB),
|
|
10503
|
+
ambIdx: -1
|
|
10504
|
+
});
|
|
10505
|
+
const i = this.ambIndex(g.prn);
|
|
10506
|
+
if (i >= 0 && g.cpR !== null && g.cpB !== null && ref.cpR !== null && ref.cpB !== null && g.lambda > 0 && ref.lambda > 0) {
|
|
10507
|
+
rows.push({
|
|
10508
|
+
g,
|
|
10509
|
+
ref,
|
|
10510
|
+
kind: "phase",
|
|
10511
|
+
z: g.lambda * (g.cpR - g.cpB) - ref.lambda * (ref.cpR - ref.cpB),
|
|
10512
|
+
ambIdx: i
|
|
10513
|
+
});
|
|
10514
|
+
}
|
|
10515
|
+
}
|
|
10516
|
+
}
|
|
10517
|
+
if (rows.length < 3) {
|
|
10518
|
+
this.lastMs = timeMs;
|
|
10519
|
+
return null;
|
|
10520
|
+
}
|
|
10521
|
+
const predict = (row, xs) => {
|
|
10522
|
+
const ts = roverTerms(row.g, xs[0], xs[1], xs[2], o.troposphere);
|
|
10523
|
+
const tr = roverTerms(row.ref, xs[0], xs[1], xs[2], o.troposphere);
|
|
10524
|
+
let pred = ts.rho - row.g.rhoB + ts.dTropo - (tr.rho - row.ref.rhoB + tr.dTropo);
|
|
10525
|
+
const h = new Array(this.x.length).fill(0);
|
|
10526
|
+
h[0] = ts.u[0] - tr.u[0];
|
|
10527
|
+
h[1] = ts.u[1] - tr.u[1];
|
|
10528
|
+
h[2] = ts.u[2] - tr.u[2];
|
|
10529
|
+
if (row.ambIdx >= 0) {
|
|
10530
|
+
pred += this.amb[row.ambIdx].lambda * this.x[3 + row.ambIdx];
|
|
10531
|
+
h[3 + row.ambIdx] = this.amb[row.ambIdx].lambda;
|
|
10532
|
+
}
|
|
10533
|
+
return { pred, h };
|
|
10534
|
+
};
|
|
10535
|
+
const dropPrns = /* @__PURE__ */ new Set();
|
|
10536
|
+
const codeInn = /* @__PURE__ */ new Map();
|
|
10537
|
+
for (const row of rows) {
|
|
10538
|
+
if (row.kind !== "code") continue;
|
|
10539
|
+
const v = row.z - predict(row, this.x).pred;
|
|
10540
|
+
codeInn.set(row.g.prn, v);
|
|
10541
|
+
if (Math.abs(v) > o.codeGateM) dropPrns.add(row.g.prn);
|
|
10542
|
+
}
|
|
10543
|
+
for (const row of rows) {
|
|
10544
|
+
if (row.kind !== "phase" || dropPrns.has(row.g.prn)) continue;
|
|
10545
|
+
const v = row.z - predict(row, this.x).pred;
|
|
10546
|
+
const vc = codeInn.get(row.g.prn) ?? 0;
|
|
10547
|
+
if (Math.abs(v - vc) > o.slipGateM) {
|
|
10548
|
+
const zP = row.g.prR - row.g.prB - (row.ref.prR - row.ref.prB);
|
|
10549
|
+
const lam = this.amb[row.ambIdx].lambda;
|
|
10550
|
+
this.x[3 + row.ambIdx] = (row.z - zP) / lam;
|
|
10551
|
+
const s = 3 + row.ambIdx;
|
|
10552
|
+
for (let j = 0; j < this.x.length; j++) {
|
|
10553
|
+
this.P[s][j] = 0;
|
|
10554
|
+
this.P[j][s] = 0;
|
|
10555
|
+
}
|
|
10556
|
+
this.P[s][s] = o.ambInitSigmaCycles * o.ambInitSigmaCycles;
|
|
10557
|
+
}
|
|
10558
|
+
}
|
|
10559
|
+
if (dropPrns.size) rows = rows.filter((r) => !dropPrns.has(r.g.prn));
|
|
10560
|
+
if (rows.length < 3) {
|
|
10561
|
+
this.lastMs = timeMs;
|
|
10562
|
+
return null;
|
|
10563
|
+
}
|
|
10564
|
+
const n = this.x.length;
|
|
10565
|
+
const R = ddCovariance(rows, o.codeSigmaM, o.phaseSigmaM);
|
|
10566
|
+
const xPrior = [...this.x];
|
|
10567
|
+
const PPrior = this.P;
|
|
10568
|
+
let xi = [...this.x];
|
|
10569
|
+
let K = [];
|
|
10570
|
+
let H2 = [];
|
|
10571
|
+
for (let it = 0; it < Math.max(o.updateIterations, 1); it++) {
|
|
10572
|
+
H2 = [];
|
|
10573
|
+
const v = [];
|
|
10574
|
+
for (const row of rows) {
|
|
10575
|
+
const { pred, h } = predict(row, xi);
|
|
10576
|
+
let corr = 0;
|
|
10577
|
+
for (let j = 0; j < n; j++) corr += h[j] * (xPrior[j] - xi[j]);
|
|
10578
|
+
v.push(row.z - pred - corr);
|
|
10579
|
+
H2.push(h);
|
|
10580
|
+
}
|
|
10581
|
+
const Ht = transpose(H2);
|
|
10582
|
+
const S = matMul(matMul(H2, PPrior), Ht);
|
|
10583
|
+
for (let i = 0; i < rows.length; i++)
|
|
10584
|
+
for (let j = 0; j < rows.length; j++) S[i][j] += R[i][j];
|
|
10585
|
+
const Sinv = matInv(S);
|
|
10586
|
+
if (!Sinv) {
|
|
10587
|
+
this.lastMs = timeMs;
|
|
10588
|
+
return null;
|
|
10589
|
+
}
|
|
10590
|
+
K = matMul(matMul(PPrior, Ht), Sinv);
|
|
10591
|
+
const dx = matMul(
|
|
10592
|
+
K,
|
|
10593
|
+
v.map((s) => [s])
|
|
10594
|
+
);
|
|
10595
|
+
xi = xPrior.map((s, i) => s + dx[i][0]);
|
|
10596
|
+
}
|
|
10597
|
+
this.x = xi;
|
|
10598
|
+
const KH = matMul(K, H2);
|
|
10599
|
+
const IKH = zeros(n, n);
|
|
10600
|
+
for (let i = 0; i < n; i++)
|
|
10601
|
+
for (let j = 0; j < n; j++) IKH[i][j] = (i === j ? 1 : 0) - KH[i][j];
|
|
10602
|
+
const Pnew = matMul(IKH, PPrior);
|
|
10603
|
+
for (let i = 0; i < n; i++)
|
|
10604
|
+
for (let j = i; j < n; j++) {
|
|
10605
|
+
const s = (Pnew[i][j] + Pnew[j][i]) / 2;
|
|
10606
|
+
Pnew[i][j] = s;
|
|
10607
|
+
Pnew[j][i] = s;
|
|
10608
|
+
}
|
|
10609
|
+
this.P = Pnew;
|
|
10610
|
+
let status = rows.some(
|
|
10611
|
+
(r) => r.kind === "phase"
|
|
10612
|
+
) ? "float" : "dgnss";
|
|
10613
|
+
let ratio;
|
|
10614
|
+
let nFixed;
|
|
10615
|
+
let fixedPos = null;
|
|
10616
|
+
let fixedAmbiguities;
|
|
10617
|
+
if (o.ambiguityResolution === "instant") {
|
|
10618
|
+
const cands = [];
|
|
10619
|
+
for (const row of rows) {
|
|
10620
|
+
if (row.kind !== "phase") continue;
|
|
10621
|
+
if (row.g.prn[0] === "R" && !o.glonassAr) continue;
|
|
10622
|
+
if (row.g.elB < o.arElevationMaskDeg * Math.PI / 180) continue;
|
|
10623
|
+
cands.push({ idx: row.ambIdx, el: row.g.elB });
|
|
10624
|
+
}
|
|
10625
|
+
cands.sort((a, b) => b.el - a.el);
|
|
10626
|
+
const floor = Math.max(
|
|
10627
|
+
o.minFixAmbiguities,
|
|
10628
|
+
Math.ceil(cands.length / 2),
|
|
10629
|
+
1
|
|
10630
|
+
);
|
|
10631
|
+
while (cands.length >= floor) {
|
|
10632
|
+
const fix = this.tryFix(cands.map((c) => c.idx));
|
|
10633
|
+
if (!fix) break;
|
|
10634
|
+
const capped = Math.min(fix.ratio, 999.9);
|
|
10635
|
+
ratio ??= capped;
|
|
10636
|
+
if (fix.ratio >= o.ratioThreshold) {
|
|
10637
|
+
ratio = capped;
|
|
10638
|
+
status = "fixed";
|
|
10639
|
+
nFixed = cands.length;
|
|
10640
|
+
fixedPos = fix.position;
|
|
10641
|
+
fixedAmbiguities = {};
|
|
10642
|
+
cands.forEach((c, i) => {
|
|
10643
|
+
fixedAmbiguities[this.amb[c.idx].prn] = fix.intAmb[i];
|
|
10644
|
+
});
|
|
10645
|
+
break;
|
|
10646
|
+
}
|
|
10647
|
+
if (!o.partialFixing) break;
|
|
10648
|
+
cands.pop();
|
|
10649
|
+
}
|
|
10650
|
+
}
|
|
10651
|
+
for (const g of geom) {
|
|
10652
|
+
if (g.cpR === null || g.cpB === null) continue;
|
|
10653
|
+
this.track.set(g.prn, { lockR: g.lockR, lockB: g.lockB, lastMs: timeMs });
|
|
10654
|
+
}
|
|
10655
|
+
this.lastMs = timeMs;
|
|
10656
|
+
const ambiguities = {};
|
|
10657
|
+
for (let k = 0; k < this.amb.length; k++)
|
|
10658
|
+
ambiguities[this.amb[k].prn] = this.x[3 + k];
|
|
10659
|
+
const refSatellites = {};
|
|
10660
|
+
for (const [group, ref] of newRefs) refSatellites[group] = ref.prn;
|
|
10661
|
+
const nSats = new Set(rows.flatMap((r) => [r.g.prn, r.ref.prn])).size;
|
|
10662
|
+
return {
|
|
10663
|
+
timeMs,
|
|
10664
|
+
position: fixedPos ?? [this.x[0], this.x[1], this.x[2]],
|
|
10665
|
+
floatBaseline: [
|
|
10666
|
+
this.x[0] - this.basePos[0],
|
|
10667
|
+
this.x[1] - this.basePos[1],
|
|
10668
|
+
this.x[2] - this.basePos[2]
|
|
10669
|
+
],
|
|
10670
|
+
status,
|
|
10671
|
+
nSats,
|
|
10672
|
+
ratio,
|
|
10673
|
+
nFixed,
|
|
10674
|
+
fixedBaseline: fixedPos ? [
|
|
10675
|
+
fixedPos[0] - this.basePos[0],
|
|
10676
|
+
fixedPos[1] - this.basePos[1],
|
|
10677
|
+
fixedPos[2] - this.basePos[2]
|
|
10678
|
+
] : void 0,
|
|
10679
|
+
fixedAmbiguities,
|
|
10680
|
+
sigmas: [
|
|
10681
|
+
Math.sqrt(Math.max(this.P[0][0], 0)),
|
|
10682
|
+
Math.sqrt(Math.max(this.P[1][1], 0)),
|
|
10683
|
+
Math.sqrt(Math.max(this.P[2][2], 0))
|
|
10684
|
+
],
|
|
10685
|
+
ambiguities,
|
|
10686
|
+
refSatellites
|
|
10687
|
+
};
|
|
10688
|
+
}
|
|
10689
|
+
};
|
|
10690
|
+
|
|
9681
10691
|
// src/positioning/index.ts
|
|
9682
10692
|
var GM_GPS3 = 3986005e8;
|
|
9683
10693
|
var F_REL = -4442807633e-19;
|
|
@@ -9714,11 +10724,11 @@ var PRIMARY_FREQ_HZ = {
|
|
|
9714
10724
|
R: 1602e6
|
|
9715
10725
|
};
|
|
9716
10726
|
var F_L1 = 157542e4;
|
|
9717
|
-
function
|
|
10727
|
+
function tropoDelay2(elevationRad) {
|
|
9718
10728
|
const sinEl = Math.sin(elevationRad);
|
|
9719
10729
|
return 2.47 / (sinEl + 0.0121);
|
|
9720
10730
|
}
|
|
9721
|
-
function
|
|
10731
|
+
function sagnac2(pos, travelTimeS) {
|
|
9722
10732
|
const theta = OMEGA_E * travelTimeS;
|
|
9723
10733
|
const c = Math.cos(theta);
|
|
9724
10734
|
const s = Math.sin(theta);
|
|
@@ -9789,7 +10799,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
9789
10799
|
const isKepler3 = eph.system !== "R" && eph.system !== "S";
|
|
9790
10800
|
const dts = dtsClock - (tgd && isKepler3 ? eph.tgd : 0);
|
|
9791
10801
|
const travel = Math.hypot(sat.x - x2, sat.y - y2, sat.z - z2) / C_LIGHT;
|
|
9792
|
-
const [sx, sy, sz] =
|
|
10802
|
+
const [sx, sy, sz] = sagnac2(sat, travel);
|
|
9793
10803
|
const rho = Math.hypot(sx - x2, sy - y2, sz - z2);
|
|
9794
10804
|
const ux = (x2 - sx) / rho;
|
|
9795
10805
|
const uy = (y2 - sy) / rho;
|
|
@@ -9806,7 +10816,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
9806
10816
|
const sinEl = Math.max(Math.sin(elev), 0.1);
|
|
9807
10817
|
weight = sinEl * sinEl;
|
|
9808
10818
|
}
|
|
9809
|
-
const tropo = troposphere && positionSane ?
|
|
10819
|
+
const tropo = troposphere && positionSane ? tropoDelay2(elev) : 0;
|
|
9810
10820
|
let ionoM = 0;
|
|
9811
10821
|
if (iono && positionSane) {
|
|
9812
10822
|
const f = PRIMARY_FREQ_HZ[sys] ?? F_L1;
|
|
@@ -12212,6 +13222,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
12212
13222
|
RINEX_CODES,
|
|
12213
13223
|
RTCM3_MESSAGE_NAMES,
|
|
12214
13224
|
Rtcm3Decoder,
|
|
13225
|
+
RtkFloatEngine,
|
|
12215
13226
|
SECONDS_IN_DAY,
|
|
12216
13227
|
SECONDS_IN_HOUR,
|
|
12217
13228
|
SECONDS_IN_MINUTE,
|
|
@@ -12341,6 +13352,8 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
12341
13352
|
ionoFree,
|
|
12342
13353
|
keplerPosition,
|
|
12343
13354
|
klobucharDelay,
|
|
13355
|
+
lambdaReduction,
|
|
13356
|
+
lambdaSearch,
|
|
12344
13357
|
maskRadForAzimuth,
|
|
12345
13358
|
msmEpochToDate,
|
|
12346
13359
|
navTimesFromEph,
|
|
@@ -12390,11 +13403,13 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
12390
13403
|
selectEphemeris,
|
|
12391
13404
|
setGloFreqNumber,
|
|
12392
13405
|
setRtcm3DebugHandler,
|
|
13406
|
+
solveDgnss,
|
|
12393
13407
|
solveSpp,
|
|
12394
13408
|
sp3Position,
|
|
12395
13409
|
stationHeaderLines,
|
|
12396
13410
|
systemCmp,
|
|
12397
13411
|
systemName,
|
|
13412
|
+
toRtkEpoch,
|
|
12398
13413
|
transformFrame,
|
|
12399
13414
|
ubxFrames,
|
|
12400
13415
|
updateStationMeta,
|