gnss-js 1.23.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.
@@ -43,6 +43,207 @@ function klobucharDelay(coeffs, latRad, lonRad, azRad, elRad, gpsTowSec) {
43
43
  return F * (5e-9 + amp * (1 - x2 / 2 + x2 * x2 / 24));
44
44
  }
45
45
 
46
+ // src/positioning/lambda.ts
47
+ var LOOPMAX = 1e4;
48
+ var ROUND = (x) => Math.floor(x + 0.5);
49
+ var SGN = (x) => x <= 0 ? -1 : 1;
50
+ function ldFactor(n, Q, L, D) {
51
+ const A = Float64Array.from(Q);
52
+ for (let i = n - 1; i >= 0; i--) {
53
+ D[i] = A[i + i * n];
54
+ if (D[i] <= 0) return false;
55
+ const a = Math.sqrt(D[i]);
56
+ for (let j = 0; j <= i; j++) L[i + j * n] = A[i + j * n] / a;
57
+ for (let j = 0; j <= i - 1; j++)
58
+ for (let k = 0; k <= j; k++) A[j + k * n] -= L[i + k * n] * L[i + j * n];
59
+ for (let j = 0; j <= i; j++) L[i + j * n] /= L[i + i * n];
60
+ }
61
+ return true;
62
+ }
63
+ function gauss(n, L, Z, i, j) {
64
+ const mu = ROUND(L[i + j * n]);
65
+ if (mu !== 0) {
66
+ for (let k = i; k < n; k++) L[k + n * j] -= mu * L[k + i * n];
67
+ for (let k = 0; k < n; k++) Z[k + n * j] -= mu * Z[k + i * n];
68
+ }
69
+ }
70
+ function perm(n, L, D, j, del, Z) {
71
+ const eta = D[j] / del;
72
+ const lam = D[j + 1] * L[j + 1 + j * n] / del;
73
+ D[j] = eta * D[j + 1];
74
+ D[j + 1] = del;
75
+ for (let k = 0; k <= j - 1; k++) {
76
+ const a0 = L[j + k * n];
77
+ const a1 = L[j + 1 + k * n];
78
+ L[j + k * n] = -L[j + 1 + j * n] * a0 + a1;
79
+ L[j + 1 + k * n] = eta * a0 + lam * a1;
80
+ }
81
+ L[j + 1 + j * n] = lam;
82
+ for (let k = j + 2; k < n; k++) {
83
+ const t = L[k + j * n];
84
+ L[k + j * n] = L[k + (j + 1) * n];
85
+ L[k + (j + 1) * n] = t;
86
+ }
87
+ for (let k = 0; k < n; k++) {
88
+ const t = Z[k + j * n];
89
+ Z[k + j * n] = Z[k + (j + 1) * n];
90
+ Z[k + (j + 1) * n] = t;
91
+ }
92
+ }
93
+ function reduction(n, L, D, Z) {
94
+ let j = n - 2;
95
+ let k = n - 2;
96
+ while (j >= 0) {
97
+ if (j <= k) for (let i = j + 1; i < n; i++) gauss(n, L, Z, i, j);
98
+ const del = D[j] + L[j + 1 + j * n] * L[j + 1 + j * n] * D[j + 1];
99
+ if (del + 1e-6 < D[j + 1]) {
100
+ perm(n, L, D, j, del, Z);
101
+ k = j;
102
+ j = n - 2;
103
+ } else j--;
104
+ }
105
+ }
106
+ function search(n, m, L, D, zs, zn, s) {
107
+ let nn = 0;
108
+ let imax = 0;
109
+ let maxdist = 1e99;
110
+ const S = new Float64Array(n * n);
111
+ const dist = new Float64Array(n);
112
+ const zb = new Float64Array(n);
113
+ const z = new Float64Array(n);
114
+ const step = new Float64Array(n);
115
+ let k = n - 1;
116
+ dist[k] = 0;
117
+ zb[k] = zs[k];
118
+ z[k] = ROUND(zb[k]);
119
+ let y = zb[k] - z[k];
120
+ step[k] = SGN(y);
121
+ let c = 0;
122
+ for (; c < LOOPMAX; c++) {
123
+ const newdist = dist[k] + y * y / D[k];
124
+ if (newdist < maxdist) {
125
+ if (k !== 0) {
126
+ dist[--k] = newdist;
127
+ for (let i = 0; i <= k; i++)
128
+ S[k + i * n] = S[k + 1 + i * n] + (z[k + 1] - zb[k + 1]) * L[k + 1 + i * n];
129
+ zb[k] = zs[k] + S[k + k * n];
130
+ z[k] = ROUND(zb[k]);
131
+ y = zb[k] - z[k];
132
+ step[k] = SGN(y);
133
+ } else {
134
+ if (nn < m) {
135
+ if (nn === 0 || newdist > s[imax]) imax = nn;
136
+ for (let i = 0; i < n; i++) zn[i + nn * n] = z[i];
137
+ s[nn++] = newdist;
138
+ } else {
139
+ if (newdist < s[imax]) {
140
+ for (let i = 0; i < n; i++) zn[i + imax * n] = z[i];
141
+ s[imax] = newdist;
142
+ imax = 0;
143
+ for (let i = 0; i < m; i++) if (s[imax] < s[i]) imax = i;
144
+ }
145
+ maxdist = s[imax];
146
+ }
147
+ z[0] += step[0];
148
+ y = zb[0] - z[0];
149
+ step[0] = -step[0] - SGN(step[0]);
150
+ }
151
+ } else {
152
+ if (k === n - 1) break;
153
+ k++;
154
+ z[k] += step[k];
155
+ y = zb[k] - z[k];
156
+ step[k] = -step[k] - SGN(step[k]);
157
+ }
158
+ }
159
+ for (let i = 0; i < m - 1; i++) {
160
+ for (let j = i + 1; j < m; j++) {
161
+ if (s[i] < s[j]) continue;
162
+ const t = s[i];
163
+ s[i] = s[j];
164
+ s[j] = t;
165
+ for (let l = 0; l < n; l++) {
166
+ const u = zn[l + i * n];
167
+ zn[l + i * n] = zn[l + j * n];
168
+ zn[l + j * n] = u;
169
+ }
170
+ }
171
+ }
172
+ return c < LOOPMAX;
173
+ }
174
+ function solveZt(n, m, Z, E) {
175
+ const w = n + m;
176
+ const M = new Float64Array(n * w);
177
+ for (let r = 0; r < n; r++) {
178
+ for (let cIdx = 0; cIdx < n; cIdx++) M[r * w + cIdx] = Z[cIdx + r * n];
179
+ for (let cIdx = 0; cIdx < m; cIdx++) M[r * w + n + cIdx] = E[r + cIdx * n];
180
+ }
181
+ for (let col = 0; col < n; col++) {
182
+ let piv = col;
183
+ for (let r = col + 1; r < n; r++)
184
+ if (Math.abs(M[r * w + col]) > Math.abs(M[piv * w + col])) piv = r;
185
+ if (Math.abs(M[piv * w + col]) < 1e-12) return null;
186
+ if (piv !== col) {
187
+ for (let cIdx = col; cIdx < w; cIdx++) {
188
+ const t = M[col * w + cIdx];
189
+ M[col * w + cIdx] = M[piv * w + cIdx];
190
+ M[piv * w + cIdx] = t;
191
+ }
192
+ }
193
+ const d = M[col * w + col];
194
+ for (let cIdx = col; cIdx < w; cIdx++) M[col * w + cIdx] /= d;
195
+ for (let r = 0; r < n; r++) {
196
+ if (r === col) continue;
197
+ const f = M[r * w + col];
198
+ if (f === 0) continue;
199
+ for (let cIdx = col; cIdx < w; cIdx++)
200
+ M[r * w + cIdx] -= f * M[col * w + cIdx];
201
+ }
202
+ }
203
+ const X = new Float64Array(n * m);
204
+ for (let r = 0; r < n; r++)
205
+ for (let cIdx = 0; cIdx < m; cIdx++) X[r + cIdx * n] = M[r * w + n + cIdx];
206
+ return X;
207
+ }
208
+ function lambdaSearch(a, Q, n, m = 2) {
209
+ if (n <= 0 || m <= 0 || a.length < n || Q.length < n * n) return null;
210
+ const L = new Float64Array(n * n);
211
+ const D = new Float64Array(n);
212
+ if (!ldFactor(n, Q, L, D)) return null;
213
+ const Z = new Float64Array(n * n);
214
+ for (let i = 0; i < n; i++) Z[i + i * n] = 1;
215
+ reduction(n, L, D, Z);
216
+ const z = new Float64Array(n);
217
+ for (let j = 0; j < n; j++) {
218
+ let sum = 0;
219
+ for (let k = 0; k < n; k++) sum += Z[k + j * n] * a[k];
220
+ z[j] = sum;
221
+ }
222
+ const E = new Float64Array(n * m);
223
+ const s = new Float64Array(m);
224
+ if (!search(n, m, L, D, z, E, s)) return null;
225
+ const F = solveZt(n, m, Z, E);
226
+ if (!F) return null;
227
+ const candidates = [];
228
+ for (let c = 0; c < m; c++) {
229
+ const v = new Float64Array(n);
230
+ for (let i = 0; i < n; i++) v[i] = ROUND(F[i + c * n]);
231
+ candidates.push(v);
232
+ }
233
+ const ratio = m >= 2 && s[0] > 0 ? s[1] / s[0] : Infinity;
234
+ return { candidates, residuals: s, ratio };
235
+ }
236
+ function lambdaReduction(Q, n) {
237
+ if (n <= 0 || Q.length < n * n) return null;
238
+ const L = new Float64Array(n * n);
239
+ const D = new Float64Array(n);
240
+ if (!ldFactor(n, Q, L, D)) return null;
241
+ const Z = new Float64Array(n * n);
242
+ for (let i = 0; i < n; i++) Z[i + i * n] = 1;
243
+ reduction(n, L, D, Z);
244
+ return Z;
245
+ }
246
+
46
247
  // src/positioning/rtk.ts
47
248
  var L1_CODES = {
48
249
  G: ["1C"],
@@ -403,7 +604,13 @@ var RtkFloatEngine = class {
403
604
  maxGapMs: opts.maxGapMs ?? 1e4,
404
605
  codeGateM: opts.codeGateM ?? 30,
405
606
  slipGateM: opts.slipGateM ?? 5,
406
- updateIterations: opts.updateIterations ?? 2
607
+ updateIterations: opts.updateIterations ?? 2,
608
+ ambiguityResolution: opts.ambiguityResolution ?? "off",
609
+ ratioThreshold: opts.ratioThreshold ?? 3,
610
+ arElevationMaskDeg: opts.arElevationMaskDeg ?? 0,
611
+ glonassAr: opts.glonassAr ?? false,
612
+ partialFixing: opts.partialFixing ?? true,
613
+ minFixAmbiguities: opts.minFixAmbiguities ?? 4
407
614
  };
408
615
  }
409
616
  /** Clear all filter state (position, ambiguities, lock history). */
@@ -461,6 +668,56 @@ var RtkFloatEngine = class {
461
668
  const entry = this.amb[newRefIdx];
462
669
  entry.prn = oldRef;
463
670
  }
671
+ /**
672
+ * Try to fix the ambiguity subset `idxs` (indices into `this.amb`):
673
+ * extract the float subvector b and covariance Q_b from the filter,
674
+ * run MLAMBDA and, without touching the filter state, condition the
675
+ * position on the best integer candidate,
676
+ * xa = x − P_xb·Q_b⁻¹·(b − ǎ)
677
+ * (RTKLIB rtkpos.c `resamb_LAMBDA`). Returns null when Q_b is not
678
+ * positive definite / invertible or the search fails; ratio
679
+ * validation is the caller's job.
680
+ */
681
+ tryFix(idxs) {
682
+ const k = idxs.length;
683
+ const b = new Float64Array(k);
684
+ const Qb = new Float64Array(k * k);
685
+ const QbRows = [];
686
+ for (let i = 0; i < k; i++) {
687
+ const si = 3 + idxs[i];
688
+ b[i] = this.x[si];
689
+ const row = [];
690
+ for (let j = 0; j < k; j++) {
691
+ const sj = 3 + idxs[j];
692
+ const q = (this.P[si][sj] + this.P[sj][si]) / 2;
693
+ Qb[i + j * k] = q;
694
+ row.push(q);
695
+ }
696
+ QbRows.push(row);
697
+ }
698
+ const res = lambdaSearch(b, Qb, k, 2);
699
+ if (!res) return null;
700
+ const best = res.candidates[0];
701
+ const QbInv = matInv(QbRows);
702
+ if (!QbInv) return null;
703
+ const db = [];
704
+ for (let i = 0; i < k; i++) db.push(b[i] - best[i]);
705
+ const position = [
706
+ this.x[0],
707
+ this.x[1],
708
+ this.x[2]
709
+ ];
710
+ for (let r = 0; r < 3; r++) {
711
+ let dx = 0;
712
+ for (let i = 0; i < k; i++) {
713
+ let yi = 0;
714
+ for (let j = 0; j < k; j++) yi += QbInv[i][j] * db[j];
715
+ dx += this.P[r][3 + idxs[i]] * yi;
716
+ }
717
+ position[r] -= dx;
718
+ }
719
+ return { ratio: res.ratio, position, intAmb: [...best] };
720
+ }
464
721
  /**
465
722
  * Process one synchronized epoch (same nominal `timeMs` for both
466
723
  * receivers). Returns null until a first position can be
@@ -711,6 +968,47 @@ var RtkFloatEngine = class {
711
968
  Pnew[j][i] = s;
712
969
  }
713
970
  this.P = Pnew;
971
+ let status = rows.some(
972
+ (r) => r.kind === "phase"
973
+ ) ? "float" : "dgnss";
974
+ let ratio;
975
+ let nFixed;
976
+ let fixedPos = null;
977
+ let fixedAmbiguities;
978
+ if (o.ambiguityResolution === "instant") {
979
+ const cands = [];
980
+ for (const row of rows) {
981
+ if (row.kind !== "phase") continue;
982
+ if (row.g.prn[0] === "R" && !o.glonassAr) continue;
983
+ if (row.g.elB < o.arElevationMaskDeg * Math.PI / 180) continue;
984
+ cands.push({ idx: row.ambIdx, el: row.g.elB });
985
+ }
986
+ cands.sort((a, b) => b.el - a.el);
987
+ const floor = Math.max(
988
+ o.minFixAmbiguities,
989
+ Math.ceil(cands.length / 2),
990
+ 1
991
+ );
992
+ while (cands.length >= floor) {
993
+ const fix = this.tryFix(cands.map((c) => c.idx));
994
+ if (!fix) break;
995
+ const capped = Math.min(fix.ratio, 999.9);
996
+ ratio ??= capped;
997
+ if (fix.ratio >= o.ratioThreshold) {
998
+ ratio = capped;
999
+ status = "fixed";
1000
+ nFixed = cands.length;
1001
+ fixedPos = fix.position;
1002
+ fixedAmbiguities = {};
1003
+ cands.forEach((c, i) => {
1004
+ fixedAmbiguities[this.amb[c.idx].prn] = fix.intAmb[i];
1005
+ });
1006
+ break;
1007
+ }
1008
+ if (!o.partialFixing) break;
1009
+ cands.pop();
1010
+ }
1011
+ }
714
1012
  for (const g of geom) {
715
1013
  if (g.cpR === null || g.cpB === null) continue;
716
1014
  this.track.set(g.prn, { lockR: g.lockR, lockB: g.lockB, lastMs: timeMs });
@@ -724,14 +1022,22 @@ var RtkFloatEngine = class {
724
1022
  const nSats = new Set(rows.flatMap((r) => [r.g.prn, r.ref.prn])).size;
725
1023
  return {
726
1024
  timeMs,
727
- position: [this.x[0], this.x[1], this.x[2]],
1025
+ position: fixedPos ?? [this.x[0], this.x[1], this.x[2]],
728
1026
  floatBaseline: [
729
1027
  this.x[0] - this.basePos[0],
730
1028
  this.x[1] - this.basePos[1],
731
1029
  this.x[2] - this.basePos[2]
732
1030
  ],
1031
+ status,
733
1032
  nSats,
734
- ratio: void 0,
1033
+ ratio,
1034
+ nFixed,
1035
+ fixedBaseline: fixedPos ? [
1036
+ fixedPos[0] - this.basePos[0],
1037
+ fixedPos[1] - this.basePos[1],
1038
+ fixedPos[2] - this.basePos[2]
1039
+ ] : void 0,
1040
+ fixedAmbiguities,
735
1041
  sigmas: [
736
1042
  Math.sqrt(Math.max(this.P[0][0], 0)),
737
1043
  Math.sqrt(Math.max(this.P[1][1], 0)),
@@ -957,6 +1263,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
957
1263
 
958
1264
  export {
959
1265
  klobucharDelay,
1266
+ lambdaSearch,
1267
+ lambdaReduction,
960
1268
  toRtkEpoch,
961
1269
  solveDgnss,
962
1270
  RtkFloatEngine,