gnss-js 2.7.0 → 2.8.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.
@@ -13,6 +13,30 @@ import {
13
13
  OMEGA_E
14
14
  } from "./chunk-FIEWO4J4.js";
15
15
 
16
+ // src/orbit/matrix.ts
17
+ function invertMatrix(A) {
18
+ const n = A.length;
19
+ const M = A.map((row, i) => [
20
+ ...row,
21
+ ...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
22
+ ]);
23
+ for (let col = 0; col < n; col++) {
24
+ let piv = col;
25
+ for (let r = col + 1; r < n; r++)
26
+ if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
27
+ if (Math.abs(M[piv][col]) < 1e-12) return null;
28
+ [M[col], M[piv]] = [M[piv], M[col]];
29
+ const d = M[col][col];
30
+ for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
31
+ for (let r = 0; r < n; r++) {
32
+ if (r === col) continue;
33
+ const f = M[r][col];
34
+ for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
35
+ }
36
+ }
37
+ return M.map((row) => row.slice(n));
38
+ }
39
+
16
40
  // src/orbit/almanac.ts
17
41
  var GM_GPS = 3986005e8;
18
42
  var GM_GAL = 3986004418e5;
@@ -382,15 +406,20 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
382
406
  }
383
407
  function computeDop(satAzEls) {
384
408
  const n = satAzEls.length;
385
- if (n < 4) return null;
409
+ const clocks = [...new Set(satAzEls.map((s) => s.prn?.[0] ?? ""))];
410
+ const m = 3 + clocks.length;
411
+ if (n < Math.max(4, m)) return null;
386
412
  const H = [];
387
- for (const { az, el } of satAzEls) {
413
+ for (const { az, el, prn } of satAzEls) {
388
414
  const cosEl = Math.cos(el);
389
- H.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
415
+ const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
416
+ const sys = prn?.[0] ?? "";
417
+ for (const c of clocks) row.push(c === sys ? 1 : 0);
418
+ H.push(row);
390
419
  }
391
- const HtH = Array.from({ length: 4 }, () => new Array(4).fill(0));
392
- for (let i = 0; i < 4; i++) {
393
- for (let j = 0; j < 4; j++) {
420
+ const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
421
+ for (let i = 0; i < m; i++) {
422
+ for (let j = 0; j < m; j++) {
394
423
  let sum = 0;
395
424
  for (let k = 0; k < n; k++) {
396
425
  sum += H[k][i] * H[k][j];
@@ -398,46 +427,18 @@ function computeDop(satAzEls) {
398
427
  HtH[i][j] = sum;
399
428
  }
400
429
  }
401
- const Q = invert4x4(HtH);
430
+ const Q = invertMatrix(HtH);
402
431
  if (!Q) return null;
403
- const gdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2] + Q[3][3]);
404
- const pdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2]);
405
- const hdop = Math.sqrt(Q[0][0] + Q[1][1]);
406
- const vdop = Math.sqrt(Q[2][2]);
407
- return { gdop, pdop, hdop, vdop };
408
- }
409
- function invert4x4(m) {
410
- const a = m.flat();
411
- const inv = new Array(16).fill(0);
412
- inv[0] = a[5] * a[10] * a[15] - a[5] * a[11] * a[14] - a[9] * a[6] * a[15] + a[9] * a[7] * a[14] + a[13] * a[6] * a[11] - a[13] * a[7] * a[10];
413
- inv[4] = -a[4] * a[10] * a[15] + a[4] * a[11] * a[14] + a[8] * a[6] * a[15] - a[8] * a[7] * a[14] - a[12] * a[6] * a[11] + a[12] * a[7] * a[10];
414
- inv[8] = a[4] * a[9] * a[15] - a[4] * a[11] * a[13] - a[8] * a[5] * a[15] + a[8] * a[7] * a[13] + a[12] * a[5] * a[11] - a[12] * a[7] * a[9];
415
- inv[12] = -a[4] * a[9] * a[14] + a[4] * a[10] * a[13] + a[8] * a[5] * a[14] - a[8] * a[6] * a[13] - a[12] * a[5] * a[10] + a[12] * a[6] * a[9];
416
- inv[1] = -a[1] * a[10] * a[15] + a[1] * a[11] * a[14] + a[9] * a[2] * a[15] - a[9] * a[3] * a[14] - a[13] * a[2] * a[11] + a[13] * a[3] * a[10];
417
- inv[5] = a[0] * a[10] * a[15] - a[0] * a[11] * a[14] - a[8] * a[2] * a[15] + a[8] * a[3] * a[14] + a[12] * a[2] * a[11] - a[12] * a[3] * a[10];
418
- inv[9] = -a[0] * a[9] * a[15] + a[0] * a[11] * a[13] + a[8] * a[1] * a[15] - a[8] * a[3] * a[13] - a[12] * a[1] * a[11] + a[12] * a[3] * a[9];
419
- inv[13] = a[0] * a[9] * a[14] - a[0] * a[10] * a[13] - a[8] * a[1] * a[14] + a[8] * a[2] * a[13] + a[12] * a[1] * a[10] - a[12] * a[2] * a[9];
420
- inv[2] = a[1] * a[6] * a[15] - a[1] * a[7] * a[14] - a[5] * a[2] * a[15] + a[5] * a[3] * a[14] + a[13] * a[2] * a[7] - a[13] * a[3] * a[6];
421
- inv[6] = -a[0] * a[6] * a[15] + a[0] * a[7] * a[14] + a[4] * a[2] * a[15] - a[4] * a[3] * a[14] - a[12] * a[2] * a[7] + a[12] * a[3] * a[6];
422
- inv[10] = a[0] * a[5] * a[15] - a[0] * a[7] * a[13] - a[4] * a[1] * a[15] + a[4] * a[3] * a[13] + a[12] * a[1] * a[7] - a[12] * a[3] * a[5];
423
- inv[14] = -a[0] * a[5] * a[14] + a[0] * a[6] * a[13] + a[4] * a[1] * a[14] - a[4] * a[2] * a[13] - a[12] * a[1] * a[6] + a[12] * a[2] * a[5];
424
- inv[3] = -a[1] * a[6] * a[11] + a[1] * a[7] * a[10] + a[5] * a[2] * a[11] - a[5] * a[3] * a[10] - a[9] * a[2] * a[7] + a[9] * a[3] * a[6];
425
- inv[7] = a[0] * a[6] * a[11] - a[0] * a[7] * a[10] - a[4] * a[2] * a[11] + a[4] * a[3] * a[10] + a[8] * a[2] * a[7] - a[8] * a[3] * a[6];
426
- inv[11] = -a[0] * a[5] * a[11] + a[0] * a[7] * a[9] + a[4] * a[1] * a[11] - a[4] * a[3] * a[9] - a[8] * a[1] * a[7] + a[8] * a[3] * a[5];
427
- inv[15] = a[0] * a[5] * a[10] - a[0] * a[6] * a[9] - a[4] * a[1] * a[10] + a[4] * a[2] * a[9] + a[8] * a[1] * a[6] - a[8] * a[2] * a[5];
428
- let det = a[0] * inv[0] + a[1] * inv[4] + a[2] * inv[8] + a[3] * inv[12];
429
- if (Math.abs(det) < 1e-20) return null;
430
- det = 1 / det;
431
- const result = Array.from(
432
- { length: 4 },
433
- () => new Array(4).fill(0)
434
- );
435
- for (let i = 0; i < 4; i++) {
436
- for (let j = 0; j < 4; j++) {
437
- result[i][j] = inv[i * 4 + j] * det;
438
- }
439
- }
440
- return result;
432
+ let clockVar = 0;
433
+ for (let i = 3; i < m; i++) clockVar += Q[i][i];
434
+ const posVar = Q[0][0] + Q[1][1] + Q[2][2];
435
+ return {
436
+ gdop: Math.sqrt(posVar + clockVar),
437
+ pdop: Math.sqrt(posVar),
438
+ hdop: Math.sqrt(Q[0][0] + Q[1][1]),
439
+ vdop: Math.sqrt(Q[2][2]),
440
+ tdop: Math.sqrt(clockVar)
441
+ };
441
442
  }
442
443
  function selectEphemeris(ephemerides, prn, timeMs) {
443
444
  return selectBest(
@@ -697,6 +698,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
697
698
  const gdop = new Array(times.length).fill(null);
698
699
  const hdop = new Array(times.length).fill(null);
699
700
  const vdop = new Array(times.length).fill(null);
701
+ const tdop = new Array(times.length).fill(null);
700
702
  const visiblePerEpoch = times.map(() => []);
701
703
  for (const prn of all.prns) {
702
704
  const series = all.positions[prn];
@@ -710,7 +712,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
710
712
  visibleCount[i]++;
711
713
  const sys = prn[0];
712
714
  (visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
713
- visiblePerEpoch[i].push({ az: p.az, el: p.el });
715
+ visiblePerEpoch[i].push({ az: p.az, el: p.el, prn });
714
716
  }
715
717
  }
716
718
  }
@@ -720,6 +722,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
720
722
  gdop[i] = dop ? dop.gdop : null;
721
723
  hdop[i] = dop ? dop.hdop : null;
722
724
  vdop[i] = dop ? dop.vdop : null;
725
+ tdop[i] = dop ? dop.tdop : null;
723
726
  }
724
727
  const passes = [];
725
728
  for (const prn of all.prns) {
@@ -766,11 +769,13 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
766
769
  gdop,
767
770
  hdop,
768
771
  vdop,
772
+ tdop,
769
773
  passes
770
774
  };
771
775
  }
772
776
 
773
777
  export {
778
+ invertMatrix,
774
779
  almanacEpochMs,
775
780
  almanacSatPosition,
776
781
  glonassAlmanacEpochMs,
@@ -5,8 +5,9 @@ import {
5
5
  computeDop,
6
6
  computeSatPosition,
7
7
  ecefToAzEl,
8
+ invertMatrix,
8
9
  selectEphemeris
9
- } from "./chunk-JYWYQ7DW.js";
10
+ } from "./chunk-GLMQRZDL.js";
10
11
  import {
11
12
  ecefToGeodetic,
12
13
  getAer,
@@ -3730,28 +3731,6 @@ function sbasAirTropoVar(elRad) {
3730
3731
  const sigTropo = 0.12 * m;
3731
3732
  return sigMp * sigMp + NOISE_DIVG_AADA * NOISE_DIVG_AADA + sigTropo * sigTropo;
3732
3733
  }
3733
- function invertMatrix(A) {
3734
- const n = A.length;
3735
- const M = A.map((row, i) => [
3736
- ...row,
3737
- ...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
3738
- ]);
3739
- for (let col = 0; col < n; col++) {
3740
- let piv = col;
3741
- for (let r = col + 1; r < n; r++)
3742
- if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
3743
- if (Math.abs(M[piv][col]) < 1e-12) return null;
3744
- [M[col], M[piv]] = [M[piv], M[col]];
3745
- const d = M[col][col];
3746
- for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
3747
- for (let r = 0; r < n; r++) {
3748
- if (r === col) continue;
3749
- const f = M[r][col];
3750
- for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
3751
- }
3752
- }
3753
- return M.map((row) => row.slice(n));
3754
- }
3755
3734
  function sbasProtectionLevels(sats, opts = {}) {
3756
3735
  const kH = opts.kH ?? 6;
3757
3736
  const kV = opts.kV ?? 5.33;
@@ -3978,7 +3957,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
3978
3957
  const azel = ecefToAzEl(x, y, z, sat.x, sat.y, sat.z);
3979
3958
  if (azel.el >= elevationMaskDeg * Math.PI / 180) {
3980
3959
  used.push(prn);
3981
- azels.push(azel);
3960
+ azels.push({ ...azel, prn });
3982
3961
  if (sbas) {
3983
3962
  const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
3984
3963
  const dr = Math.hypot(dx, dy, dz) || 1;
package/dist/index.cjs CHANGED
@@ -12288,6 +12288,30 @@ function parseBinex(data) {
12288
12288
  return { epochs, ephemerides, messageCounts, obsCodes, badCrc: stats.badCrc };
12289
12289
  }
12290
12290
 
12291
+ // src/orbit/matrix.ts
12292
+ function invertMatrix(A) {
12293
+ const n = A.length;
12294
+ const M = A.map((row, i) => [
12295
+ ...row,
12296
+ ...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
12297
+ ]);
12298
+ for (let col = 0; col < n; col++) {
12299
+ let piv = col;
12300
+ for (let r = col + 1; r < n; r++)
12301
+ if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
12302
+ if (Math.abs(M[piv][col]) < 1e-12) return null;
12303
+ [M[col], M[piv]] = [M[piv], M[col]];
12304
+ const d = M[col][col];
12305
+ for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
12306
+ for (let r = 0; r < n; r++) {
12307
+ if (r === col) continue;
12308
+ const f = M[r][col];
12309
+ for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
12310
+ }
12311
+ }
12312
+ return M.map((row) => row.slice(n));
12313
+ }
12314
+
12291
12315
  // src/orbit/almanac.ts
12292
12316
  var GM_GPS = 3986005e8;
12293
12317
  var GM_GAL = 3986004418e5;
@@ -12657,15 +12681,20 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
12657
12681
  }
12658
12682
  function computeDop(satAzEls) {
12659
12683
  const n = satAzEls.length;
12660
- if (n < 4) return null;
12684
+ const clocks = [...new Set(satAzEls.map((s) => s.prn?.[0] ?? ""))];
12685
+ const m = 3 + clocks.length;
12686
+ if (n < Math.max(4, m)) return null;
12661
12687
  const H3 = [];
12662
- for (const { az, el } of satAzEls) {
12688
+ for (const { az, el, prn } of satAzEls) {
12663
12689
  const cosEl = Math.cos(el);
12664
- H3.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
12665
- }
12666
- const HtH = Array.from({ length: 4 }, () => new Array(4).fill(0));
12667
- for (let i = 0; i < 4; i++) {
12668
- for (let j = 0; j < 4; j++) {
12690
+ const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
12691
+ const sys = prn?.[0] ?? "";
12692
+ for (const c of clocks) row.push(c === sys ? 1 : 0);
12693
+ H3.push(row);
12694
+ }
12695
+ const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
12696
+ for (let i = 0; i < m; i++) {
12697
+ for (let j = 0; j < m; j++) {
12669
12698
  let sum = 0;
12670
12699
  for (let k = 0; k < n; k++) {
12671
12700
  sum += H3[k][i] * H3[k][j];
@@ -12673,46 +12702,18 @@ function computeDop(satAzEls) {
12673
12702
  HtH[i][j] = sum;
12674
12703
  }
12675
12704
  }
12676
- const Q = invert4x4(HtH);
12705
+ const Q = invertMatrix(HtH);
12677
12706
  if (!Q) return null;
12678
- const gdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2] + Q[3][3]);
12679
- const pdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2]);
12680
- const hdop = Math.sqrt(Q[0][0] + Q[1][1]);
12681
- const vdop = Math.sqrt(Q[2][2]);
12682
- return { gdop, pdop, hdop, vdop };
12683
- }
12684
- function invert4x4(m) {
12685
- const a = m.flat();
12686
- const inv = new Array(16).fill(0);
12687
- inv[0] = a[5] * a[10] * a[15] - a[5] * a[11] * a[14] - a[9] * a[6] * a[15] + a[9] * a[7] * a[14] + a[13] * a[6] * a[11] - a[13] * a[7] * a[10];
12688
- inv[4] = -a[4] * a[10] * a[15] + a[4] * a[11] * a[14] + a[8] * a[6] * a[15] - a[8] * a[7] * a[14] - a[12] * a[6] * a[11] + a[12] * a[7] * a[10];
12689
- inv[8] = a[4] * a[9] * a[15] - a[4] * a[11] * a[13] - a[8] * a[5] * a[15] + a[8] * a[7] * a[13] + a[12] * a[5] * a[11] - a[12] * a[7] * a[9];
12690
- inv[12] = -a[4] * a[9] * a[14] + a[4] * a[10] * a[13] + a[8] * a[5] * a[14] - a[8] * a[6] * a[13] - a[12] * a[5] * a[10] + a[12] * a[6] * a[9];
12691
- inv[1] = -a[1] * a[10] * a[15] + a[1] * a[11] * a[14] + a[9] * a[2] * a[15] - a[9] * a[3] * a[14] - a[13] * a[2] * a[11] + a[13] * a[3] * a[10];
12692
- inv[5] = a[0] * a[10] * a[15] - a[0] * a[11] * a[14] - a[8] * a[2] * a[15] + a[8] * a[3] * a[14] + a[12] * a[2] * a[11] - a[12] * a[3] * a[10];
12693
- inv[9] = -a[0] * a[9] * a[15] + a[0] * a[11] * a[13] + a[8] * a[1] * a[15] - a[8] * a[3] * a[13] - a[12] * a[1] * a[11] + a[12] * a[3] * a[9];
12694
- inv[13] = a[0] * a[9] * a[14] - a[0] * a[10] * a[13] - a[8] * a[1] * a[14] + a[8] * a[2] * a[13] + a[12] * a[1] * a[10] - a[12] * a[2] * a[9];
12695
- inv[2] = a[1] * a[6] * a[15] - a[1] * a[7] * a[14] - a[5] * a[2] * a[15] + a[5] * a[3] * a[14] + a[13] * a[2] * a[7] - a[13] * a[3] * a[6];
12696
- inv[6] = -a[0] * a[6] * a[15] + a[0] * a[7] * a[14] + a[4] * a[2] * a[15] - a[4] * a[3] * a[14] - a[12] * a[2] * a[7] + a[12] * a[3] * a[6];
12697
- inv[10] = a[0] * a[5] * a[15] - a[0] * a[7] * a[13] - a[4] * a[1] * a[15] + a[4] * a[3] * a[13] + a[12] * a[1] * a[7] - a[12] * a[3] * a[5];
12698
- inv[14] = -a[0] * a[5] * a[14] + a[0] * a[6] * a[13] + a[4] * a[1] * a[14] - a[4] * a[2] * a[13] - a[12] * a[1] * a[6] + a[12] * a[2] * a[5];
12699
- inv[3] = -a[1] * a[6] * a[11] + a[1] * a[7] * a[10] + a[5] * a[2] * a[11] - a[5] * a[3] * a[10] - a[9] * a[2] * a[7] + a[9] * a[3] * a[6];
12700
- inv[7] = a[0] * a[6] * a[11] - a[0] * a[7] * a[10] - a[4] * a[2] * a[11] + a[4] * a[3] * a[10] + a[8] * a[2] * a[7] - a[8] * a[3] * a[6];
12701
- inv[11] = -a[0] * a[5] * a[11] + a[0] * a[7] * a[9] + a[4] * a[1] * a[11] - a[4] * a[3] * a[9] - a[8] * a[1] * a[7] + a[8] * a[3] * a[5];
12702
- inv[15] = a[0] * a[5] * a[10] - a[0] * a[6] * a[9] - a[4] * a[1] * a[10] + a[4] * a[2] * a[9] + a[8] * a[1] * a[6] - a[8] * a[2] * a[5];
12703
- let det = a[0] * inv[0] + a[1] * inv[4] + a[2] * inv[8] + a[3] * inv[12];
12704
- if (Math.abs(det) < 1e-20) return null;
12705
- det = 1 / det;
12706
- const result = Array.from(
12707
- { length: 4 },
12708
- () => new Array(4).fill(0)
12709
- );
12710
- for (let i = 0; i < 4; i++) {
12711
- for (let j = 0; j < 4; j++) {
12712
- result[i][j] = inv[i * 4 + j] * det;
12713
- }
12714
- }
12715
- return result;
12707
+ let clockVar = 0;
12708
+ for (let i = 3; i < m; i++) clockVar += Q[i][i];
12709
+ const posVar = Q[0][0] + Q[1][1] + Q[2][2];
12710
+ return {
12711
+ gdop: Math.sqrt(posVar + clockVar),
12712
+ pdop: Math.sqrt(posVar),
12713
+ hdop: Math.sqrt(Q[0][0] + Q[1][1]),
12714
+ vdop: Math.sqrt(Q[2][2]),
12715
+ tdop: Math.sqrt(clockVar)
12716
+ };
12716
12717
  }
12717
12718
  function selectEphemeris(ephemerides, prn, timeMs) {
12718
12719
  return selectBest(
@@ -12972,6 +12973,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
12972
12973
  const gdop = new Array(times.length).fill(null);
12973
12974
  const hdop = new Array(times.length).fill(null);
12974
12975
  const vdop = new Array(times.length).fill(null);
12976
+ const tdop = new Array(times.length).fill(null);
12975
12977
  const visiblePerEpoch = times.map(() => []);
12976
12978
  for (const prn of all.prns) {
12977
12979
  const series = all.positions[prn];
@@ -12985,7 +12987,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
12985
12987
  visibleCount[i]++;
12986
12988
  const sys = prn[0];
12987
12989
  (visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
12988
- visiblePerEpoch[i].push({ az: p.az, el: p.el });
12990
+ visiblePerEpoch[i].push({ az: p.az, el: p.el, prn });
12989
12991
  }
12990
12992
  }
12991
12993
  }
@@ -12995,6 +12997,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
12995
12997
  gdop[i] = dop ? dop.gdop : null;
12996
12998
  hdop[i] = dop ? dop.hdop : null;
12997
12999
  vdop[i] = dop ? dop.vdop : null;
13000
+ tdop[i] = dop ? dop.tdop : null;
12998
13001
  }
12999
13002
  const passes = [];
13000
13003
  for (const prn of all.prns) {
@@ -13041,6 +13044,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
13041
13044
  gdop,
13042
13045
  hdop,
13043
13046
  vdop,
13047
+ tdop,
13044
13048
  passes
13045
13049
  };
13046
13050
  }
@@ -16966,28 +16970,6 @@ function sbasAirTropoVar(elRad) {
16966
16970
  const sigTropo = 0.12 * m;
16967
16971
  return sigMp * sigMp + NOISE_DIVG_AADA * NOISE_DIVG_AADA + sigTropo * sigTropo;
16968
16972
  }
16969
- function invertMatrix(A) {
16970
- const n = A.length;
16971
- const M = A.map((row, i) => [
16972
- ...row,
16973
- ...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
16974
- ]);
16975
- for (let col = 0; col < n; col++) {
16976
- let piv = col;
16977
- for (let r = col + 1; r < n; r++)
16978
- if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
16979
- if (Math.abs(M[piv][col]) < 1e-12) return null;
16980
- [M[col], M[piv]] = [M[piv], M[col]];
16981
- const d = M[col][col];
16982
- for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
16983
- for (let r = 0; r < n; r++) {
16984
- if (r === col) continue;
16985
- const f = M[r][col];
16986
- for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
16987
- }
16988
- }
16989
- return M.map((row) => row.slice(n));
16990
- }
16991
16973
  function sbasProtectionLevels(sats, opts = {}) {
16992
16974
  const kH = opts.kH ?? 6;
16993
16975
  const kV = opts.kV ?? 5.33;
@@ -17214,7 +17196,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
17214
17196
  const azel = ecefToAzEl(x, y, z, sat.x, sat.y, sat.z);
17215
17197
  if (azel.el >= elevationMaskDeg * Math.PI / 180) {
17216
17198
  used.push(prn);
17217
- azels.push(azel);
17199
+ azels.push({ ...azel, prn });
17218
17200
  if (sbas) {
17219
17201
  const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
17220
17202
  const dr = Math.hypot(dx, dy, dz) || 1;
package/dist/index.js CHANGED
@@ -213,7 +213,7 @@ import {
213
213
  sunEcef,
214
214
  toRtkEpoch,
215
215
  wlWavelength
216
- } from "./chunk-BWLLHLLS.js";
216
+ } from "./chunk-PHS7OFHN.js";
217
217
  import "./chunk-WQX354KD.js";
218
218
  import {
219
219
  almanacEpochMs,
@@ -235,7 +235,7 @@ import {
235
235
  navTimesFromEph,
236
236
  rangeRate,
237
237
  selectEphemeris
238
- } from "./chunk-JYWYQ7DW.js";
238
+ } from "./chunk-GLMQRZDL.js";
239
239
  import {
240
240
  getBdsTime,
241
241
  getDateFromBdsTime,
package/dist/orbit.cjs CHANGED
@@ -174,6 +174,30 @@ function getGpsLeap(date) {
174
174
  return leap_seconds - 19;
175
175
  }
176
176
 
177
+ // src/orbit/matrix.ts
178
+ function invertMatrix(A) {
179
+ const n = A.length;
180
+ const M = A.map((row, i) => [
181
+ ...row,
182
+ ...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
183
+ ]);
184
+ for (let col = 0; col < n; col++) {
185
+ let piv = col;
186
+ for (let r = col + 1; r < n; r++)
187
+ if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
188
+ if (Math.abs(M[piv][col]) < 1e-12) return null;
189
+ [M[col], M[piv]] = [M[piv], M[col]];
190
+ const d = M[col][col];
191
+ for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
192
+ for (let r = 0; r < n; r++) {
193
+ if (r === col) continue;
194
+ const f = M[r][col];
195
+ for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
196
+ }
197
+ }
198
+ return M.map((row) => row.slice(n));
199
+ }
200
+
177
201
  // src/orbit/almanac.ts
178
202
  var GM_GPS = 3986005e8;
179
203
  var GM_GAL = 3986004418e5;
@@ -543,15 +567,20 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
543
567
  }
544
568
  function computeDop(satAzEls) {
545
569
  const n = satAzEls.length;
546
- if (n < 4) return null;
570
+ const clocks = [...new Set(satAzEls.map((s) => s.prn?.[0] ?? ""))];
571
+ const m = 3 + clocks.length;
572
+ if (n < Math.max(4, m)) return null;
547
573
  const H = [];
548
- for (const { az, el } of satAzEls) {
574
+ for (const { az, el, prn } of satAzEls) {
549
575
  const cosEl = Math.cos(el);
550
- H.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
576
+ const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
577
+ const sys = prn?.[0] ?? "";
578
+ for (const c of clocks) row.push(c === sys ? 1 : 0);
579
+ H.push(row);
551
580
  }
552
- const HtH = Array.from({ length: 4 }, () => new Array(4).fill(0));
553
- for (let i = 0; i < 4; i++) {
554
- for (let j = 0; j < 4; j++) {
581
+ const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
582
+ for (let i = 0; i < m; i++) {
583
+ for (let j = 0; j < m; j++) {
555
584
  let sum = 0;
556
585
  for (let k = 0; k < n; k++) {
557
586
  sum += H[k][i] * H[k][j];
@@ -559,46 +588,18 @@ function computeDop(satAzEls) {
559
588
  HtH[i][j] = sum;
560
589
  }
561
590
  }
562
- const Q = invert4x4(HtH);
591
+ const Q = invertMatrix(HtH);
563
592
  if (!Q) return null;
564
- const gdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2] + Q[3][3]);
565
- const pdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2]);
566
- const hdop = Math.sqrt(Q[0][0] + Q[1][1]);
567
- const vdop = Math.sqrt(Q[2][2]);
568
- return { gdop, pdop, hdop, vdop };
569
- }
570
- function invert4x4(m) {
571
- const a = m.flat();
572
- const inv = new Array(16).fill(0);
573
- inv[0] = a[5] * a[10] * a[15] - a[5] * a[11] * a[14] - a[9] * a[6] * a[15] + a[9] * a[7] * a[14] + a[13] * a[6] * a[11] - a[13] * a[7] * a[10];
574
- inv[4] = -a[4] * a[10] * a[15] + a[4] * a[11] * a[14] + a[8] * a[6] * a[15] - a[8] * a[7] * a[14] - a[12] * a[6] * a[11] + a[12] * a[7] * a[10];
575
- inv[8] = a[4] * a[9] * a[15] - a[4] * a[11] * a[13] - a[8] * a[5] * a[15] + a[8] * a[7] * a[13] + a[12] * a[5] * a[11] - a[12] * a[7] * a[9];
576
- inv[12] = -a[4] * a[9] * a[14] + a[4] * a[10] * a[13] + a[8] * a[5] * a[14] - a[8] * a[6] * a[13] - a[12] * a[5] * a[10] + a[12] * a[6] * a[9];
577
- inv[1] = -a[1] * a[10] * a[15] + a[1] * a[11] * a[14] + a[9] * a[2] * a[15] - a[9] * a[3] * a[14] - a[13] * a[2] * a[11] + a[13] * a[3] * a[10];
578
- inv[5] = a[0] * a[10] * a[15] - a[0] * a[11] * a[14] - a[8] * a[2] * a[15] + a[8] * a[3] * a[14] + a[12] * a[2] * a[11] - a[12] * a[3] * a[10];
579
- inv[9] = -a[0] * a[9] * a[15] + a[0] * a[11] * a[13] + a[8] * a[1] * a[15] - a[8] * a[3] * a[13] - a[12] * a[1] * a[11] + a[12] * a[3] * a[9];
580
- inv[13] = a[0] * a[9] * a[14] - a[0] * a[10] * a[13] - a[8] * a[1] * a[14] + a[8] * a[2] * a[13] + a[12] * a[1] * a[10] - a[12] * a[2] * a[9];
581
- inv[2] = a[1] * a[6] * a[15] - a[1] * a[7] * a[14] - a[5] * a[2] * a[15] + a[5] * a[3] * a[14] + a[13] * a[2] * a[7] - a[13] * a[3] * a[6];
582
- inv[6] = -a[0] * a[6] * a[15] + a[0] * a[7] * a[14] + a[4] * a[2] * a[15] - a[4] * a[3] * a[14] - a[12] * a[2] * a[7] + a[12] * a[3] * a[6];
583
- inv[10] = a[0] * a[5] * a[15] - a[0] * a[7] * a[13] - a[4] * a[1] * a[15] + a[4] * a[3] * a[13] + a[12] * a[1] * a[7] - a[12] * a[3] * a[5];
584
- inv[14] = -a[0] * a[5] * a[14] + a[0] * a[6] * a[13] + a[4] * a[1] * a[14] - a[4] * a[2] * a[13] - a[12] * a[1] * a[6] + a[12] * a[2] * a[5];
585
- inv[3] = -a[1] * a[6] * a[11] + a[1] * a[7] * a[10] + a[5] * a[2] * a[11] - a[5] * a[3] * a[10] - a[9] * a[2] * a[7] + a[9] * a[3] * a[6];
586
- inv[7] = a[0] * a[6] * a[11] - a[0] * a[7] * a[10] - a[4] * a[2] * a[11] + a[4] * a[3] * a[10] + a[8] * a[2] * a[7] - a[8] * a[3] * a[6];
587
- inv[11] = -a[0] * a[5] * a[11] + a[0] * a[7] * a[9] + a[4] * a[1] * a[11] - a[4] * a[3] * a[9] - a[8] * a[1] * a[7] + a[8] * a[3] * a[5];
588
- inv[15] = a[0] * a[5] * a[10] - a[0] * a[6] * a[9] - a[4] * a[1] * a[10] + a[4] * a[2] * a[9] + a[8] * a[1] * a[6] - a[8] * a[2] * a[5];
589
- let det = a[0] * inv[0] + a[1] * inv[4] + a[2] * inv[8] + a[3] * inv[12];
590
- if (Math.abs(det) < 1e-20) return null;
591
- det = 1 / det;
592
- const result = Array.from(
593
- { length: 4 },
594
- () => new Array(4).fill(0)
595
- );
596
- for (let i = 0; i < 4; i++) {
597
- for (let j = 0; j < 4; j++) {
598
- result[i][j] = inv[i * 4 + j] * det;
599
- }
600
- }
601
- return result;
593
+ let clockVar = 0;
594
+ for (let i = 3; i < m; i++) clockVar += Q[i][i];
595
+ const posVar = Q[0][0] + Q[1][1] + Q[2][2];
596
+ return {
597
+ gdop: Math.sqrt(posVar + clockVar),
598
+ pdop: Math.sqrt(posVar),
599
+ hdop: Math.sqrt(Q[0][0] + Q[1][1]),
600
+ vdop: Math.sqrt(Q[2][2]),
601
+ tdop: Math.sqrt(clockVar)
602
+ };
602
603
  }
603
604
  function selectEphemeris(ephemerides, prn, timeMs) {
604
605
  return selectBest(
@@ -858,6 +859,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
858
859
  const gdop = new Array(times.length).fill(null);
859
860
  const hdop = new Array(times.length).fill(null);
860
861
  const vdop = new Array(times.length).fill(null);
862
+ const tdop = new Array(times.length).fill(null);
861
863
  const visiblePerEpoch = times.map(() => []);
862
864
  for (const prn of all.prns) {
863
865
  const series = all.positions[prn];
@@ -871,7 +873,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
871
873
  visibleCount[i]++;
872
874
  const sys = prn[0];
873
875
  (visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
874
- visiblePerEpoch[i].push({ az: p.az, el: p.el });
876
+ visiblePerEpoch[i].push({ az: p.az, el: p.el, prn });
875
877
  }
876
878
  }
877
879
  }
@@ -881,6 +883,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
881
883
  gdop[i] = dop ? dop.gdop : null;
882
884
  hdop[i] = dop ? dop.hdop : null;
883
885
  vdop[i] = dop ? dop.vdop : null;
886
+ tdop[i] = dop ? dop.tdop : null;
884
887
  }
885
888
  const passes = [];
886
889
  for (const prn of all.prns) {
@@ -927,6 +930,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
927
930
  gdop,
928
931
  hdop,
929
932
  vdop,
933
+ tdop,
930
934
  passes
931
935
  };
932
936
  }
package/dist/orbit.d.cts CHANGED
@@ -118,6 +118,11 @@ interface DopValues {
118
118
  pdop: number;
119
119
  hdop: number;
120
120
  vdop: number;
121
+ /**
122
+ * Time DOP: root of the summed receiver-clock variances (one clock per
123
+ * constellation in view), so GDOP² = PDOP² + TDOP².
124
+ */
125
+ tdop: number;
121
126
  }
122
127
  /**
123
128
  * Compute satellite ECEF position and velocity from Keplerian ephemeris.
@@ -154,12 +159,22 @@ declare function ecefToAzEl(rxX: number, rxY: number, rxZ: number, satX: number,
154
159
  };
155
160
  /**
156
161
  * Compute DOP values from a set of satellite az/el.
157
- * @param satAzEls Array of satellite azimuth/elevation pairs (radians)
158
- * @returns DOP values (GDOP, PDOP, HDOP, VDOP), or null if fewer than 4 satellites
162
+ *
163
+ * Satellites tagged with a `prn` get one receiver-clock unknown per
164
+ * constellation (the PRN's system letter), matching how a multi-GNSS
165
+ * receiver solves and how `solveSpp` does: each system runs on its own
166
+ * time scale, so every extra constellation costs one observation. Untagged
167
+ * satellites share a single clock (the classic single-system model).
168
+ *
169
+ * @param satAzEls Satellite azimuth/elevation pairs (radians), optionally
170
+ * tagged with their PRN.
171
+ * @returns DOP values, or null when there are fewer satellites than
172
+ * unknowns (3 + one clock per constellation) or the geometry is singular.
159
173
  */
160
174
  declare function computeDop(satAzEls: {
161
175
  az: number;
162
176
  el: number;
177
+ prn?: string;
163
178
  }[]): DopValues | null;
164
179
  /**
165
180
  * Find the best ephemeris for a given PRN at a given time.
@@ -242,14 +257,16 @@ interface VisibilityResult {
242
257
  visibleCount: number[];
243
258
  /** Per-system visible counts per epoch (system letter → counts). */
244
259
  visibleBySystem: Record<string, number[]>;
245
- /** PDOP per epoch (null when < 4 satellites above the mask). */
260
+ /** PDOP per epoch (null when fewer satellites than unknowns). */
246
261
  pdop: (number | null)[];
247
- /** GDOP per epoch (null when < 4 satellites above the mask). */
262
+ /** GDOP per epoch (null when fewer satellites than unknowns). */
248
263
  gdop: (number | null)[];
249
- /** HDOP per epoch (null when < 4 satellites above the mask). */
264
+ /** HDOP per epoch (null when fewer satellites than unknowns). */
250
265
  hdop: (number | null)[];
251
- /** VDOP per epoch (null when < 4 satellites above the mask). */
266
+ /** VDOP per epoch (null when fewer satellites than unknowns). */
252
267
  vdop: (number | null)[];
268
+ /** TDOP per epoch (all constellation clocks; null like the others). */
269
+ tdop: (number | null)[];
253
270
  /** Discrete above-mask passes, sorted by rise time. */
254
271
  passes: VisibilityPass[];
255
272
  }
package/dist/orbit.d.ts CHANGED
@@ -118,6 +118,11 @@ interface DopValues {
118
118
  pdop: number;
119
119
  hdop: number;
120
120
  vdop: number;
121
+ /**
122
+ * Time DOP: root of the summed receiver-clock variances (one clock per
123
+ * constellation in view), so GDOP² = PDOP² + TDOP².
124
+ */
125
+ tdop: number;
121
126
  }
122
127
  /**
123
128
  * Compute satellite ECEF position and velocity from Keplerian ephemeris.
@@ -154,12 +159,22 @@ declare function ecefToAzEl(rxX: number, rxY: number, rxZ: number, satX: number,
154
159
  };
155
160
  /**
156
161
  * Compute DOP values from a set of satellite az/el.
157
- * @param satAzEls Array of satellite azimuth/elevation pairs (radians)
158
- * @returns DOP values (GDOP, PDOP, HDOP, VDOP), or null if fewer than 4 satellites
162
+ *
163
+ * Satellites tagged with a `prn` get one receiver-clock unknown per
164
+ * constellation (the PRN's system letter), matching how a multi-GNSS
165
+ * receiver solves and how `solveSpp` does: each system runs on its own
166
+ * time scale, so every extra constellation costs one observation. Untagged
167
+ * satellites share a single clock (the classic single-system model).
168
+ *
169
+ * @param satAzEls Satellite azimuth/elevation pairs (radians), optionally
170
+ * tagged with their PRN.
171
+ * @returns DOP values, or null when there are fewer satellites than
172
+ * unknowns (3 + one clock per constellation) or the geometry is singular.
159
173
  */
160
174
  declare function computeDop(satAzEls: {
161
175
  az: number;
162
176
  el: number;
177
+ prn?: string;
163
178
  }[]): DopValues | null;
164
179
  /**
165
180
  * Find the best ephemeris for a given PRN at a given time.
@@ -242,14 +257,16 @@ interface VisibilityResult {
242
257
  visibleCount: number[];
243
258
  /** Per-system visible counts per epoch (system letter → counts). */
244
259
  visibleBySystem: Record<string, number[]>;
245
- /** PDOP per epoch (null when < 4 satellites above the mask). */
260
+ /** PDOP per epoch (null when fewer satellites than unknowns). */
246
261
  pdop: (number | null)[];
247
- /** GDOP per epoch (null when < 4 satellites above the mask). */
262
+ /** GDOP per epoch (null when fewer satellites than unknowns). */
248
263
  gdop: (number | null)[];
249
- /** HDOP per epoch (null when < 4 satellites above the mask). */
264
+ /** HDOP per epoch (null when fewer satellites than unknowns). */
250
265
  hdop: (number | null)[];
251
- /** VDOP per epoch (null when < 4 satellites above the mask). */
266
+ /** VDOP per epoch (null when fewer satellites than unknowns). */
252
267
  vdop: (number | null)[];
268
+ /** TDOP per epoch (all constellation clocks; null like the others). */
269
+ tdop: (number | null)[];
253
270
  /** Discrete above-mask passes, sorted by rise time. */
254
271
  passes: VisibilityPass[];
255
272
  }
package/dist/orbit.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  navTimesFromEph,
19
19
  rangeRate,
20
20
  selectEphemeris
21
- } from "./chunk-JYWYQ7DW.js";
21
+ } from "./chunk-GLMQRZDL.js";
22
22
  import "./chunk-HVXYFUCB.js";
23
23
  import {
24
24
  ecefToGeodetic,
@@ -230,6 +230,30 @@ function getGpsLeap(date) {
230
230
  return leap_seconds - 19;
231
231
  }
232
232
 
233
+ // src/orbit/matrix.ts
234
+ function invertMatrix(A) {
235
+ const n = A.length;
236
+ const M = A.map((row, i) => [
237
+ ...row,
238
+ ...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
239
+ ]);
240
+ for (let col = 0; col < n; col++) {
241
+ let piv = col;
242
+ for (let r = col + 1; r < n; r++)
243
+ if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
244
+ if (Math.abs(M[piv][col]) < 1e-12) return null;
245
+ [M[col], M[piv]] = [M[piv], M[col]];
246
+ const d = M[col][col];
247
+ for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
248
+ for (let r = 0; r < n; r++) {
249
+ if (r === col) continue;
250
+ const f = M[r][col];
251
+ for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
252
+ }
253
+ }
254
+ return M.map((row) => row.slice(n));
255
+ }
256
+
233
257
  // src/orbit/index.ts
234
258
  var GM_GPS = 3986005e8;
235
259
  var GM_GAL = 3986004418e5;
@@ -415,15 +439,20 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
415
439
  }
416
440
  function computeDop(satAzEls) {
417
441
  const n = satAzEls.length;
418
- if (n < 4) return null;
442
+ const clocks = [...new Set(satAzEls.map((s) => s.prn?.[0] ?? ""))];
443
+ const m = 3 + clocks.length;
444
+ if (n < Math.max(4, m)) return null;
419
445
  const H = [];
420
- for (const { az, el } of satAzEls) {
446
+ for (const { az, el, prn } of satAzEls) {
421
447
  const cosEl = Math.cos(el);
422
- H.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
423
- }
424
- const HtH = Array.from({ length: 4 }, () => new Array(4).fill(0));
425
- for (let i = 0; i < 4; i++) {
426
- for (let j = 0; j < 4; j++) {
448
+ const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
449
+ const sys = prn?.[0] ?? "";
450
+ for (const c of clocks) row.push(c === sys ? 1 : 0);
451
+ H.push(row);
452
+ }
453
+ const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
454
+ for (let i = 0; i < m; i++) {
455
+ for (let j = 0; j < m; j++) {
427
456
  let sum = 0;
428
457
  for (let k = 0; k < n; k++) {
429
458
  sum += H[k][i] * H[k][j];
@@ -431,46 +460,18 @@ function computeDop(satAzEls) {
431
460
  HtH[i][j] = sum;
432
461
  }
433
462
  }
434
- const Q = invert4x4(HtH);
463
+ const Q = invertMatrix(HtH);
435
464
  if (!Q) return null;
436
- const gdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2] + Q[3][3]);
437
- const pdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2]);
438
- const hdop = Math.sqrt(Q[0][0] + Q[1][1]);
439
- const vdop = Math.sqrt(Q[2][2]);
440
- return { gdop, pdop, hdop, vdop };
441
- }
442
- function invert4x4(m) {
443
- const a = m.flat();
444
- const inv = new Array(16).fill(0);
445
- inv[0] = a[5] * a[10] * a[15] - a[5] * a[11] * a[14] - a[9] * a[6] * a[15] + a[9] * a[7] * a[14] + a[13] * a[6] * a[11] - a[13] * a[7] * a[10];
446
- inv[4] = -a[4] * a[10] * a[15] + a[4] * a[11] * a[14] + a[8] * a[6] * a[15] - a[8] * a[7] * a[14] - a[12] * a[6] * a[11] + a[12] * a[7] * a[10];
447
- inv[8] = a[4] * a[9] * a[15] - a[4] * a[11] * a[13] - a[8] * a[5] * a[15] + a[8] * a[7] * a[13] + a[12] * a[5] * a[11] - a[12] * a[7] * a[9];
448
- inv[12] = -a[4] * a[9] * a[14] + a[4] * a[10] * a[13] + a[8] * a[5] * a[14] - a[8] * a[6] * a[13] - a[12] * a[5] * a[10] + a[12] * a[6] * a[9];
449
- inv[1] = -a[1] * a[10] * a[15] + a[1] * a[11] * a[14] + a[9] * a[2] * a[15] - a[9] * a[3] * a[14] - a[13] * a[2] * a[11] + a[13] * a[3] * a[10];
450
- inv[5] = a[0] * a[10] * a[15] - a[0] * a[11] * a[14] - a[8] * a[2] * a[15] + a[8] * a[3] * a[14] + a[12] * a[2] * a[11] - a[12] * a[3] * a[10];
451
- inv[9] = -a[0] * a[9] * a[15] + a[0] * a[11] * a[13] + a[8] * a[1] * a[15] - a[8] * a[3] * a[13] - a[12] * a[1] * a[11] + a[12] * a[3] * a[9];
452
- inv[13] = a[0] * a[9] * a[14] - a[0] * a[10] * a[13] - a[8] * a[1] * a[14] + a[8] * a[2] * a[13] + a[12] * a[1] * a[10] - a[12] * a[2] * a[9];
453
- inv[2] = a[1] * a[6] * a[15] - a[1] * a[7] * a[14] - a[5] * a[2] * a[15] + a[5] * a[3] * a[14] + a[13] * a[2] * a[7] - a[13] * a[3] * a[6];
454
- inv[6] = -a[0] * a[6] * a[15] + a[0] * a[7] * a[14] + a[4] * a[2] * a[15] - a[4] * a[3] * a[14] - a[12] * a[2] * a[7] + a[12] * a[3] * a[6];
455
- inv[10] = a[0] * a[5] * a[15] - a[0] * a[7] * a[13] - a[4] * a[1] * a[15] + a[4] * a[3] * a[13] + a[12] * a[1] * a[7] - a[12] * a[3] * a[5];
456
- inv[14] = -a[0] * a[5] * a[14] + a[0] * a[6] * a[13] + a[4] * a[1] * a[14] - a[4] * a[2] * a[13] - a[12] * a[1] * a[6] + a[12] * a[2] * a[5];
457
- inv[3] = -a[1] * a[6] * a[11] + a[1] * a[7] * a[10] + a[5] * a[2] * a[11] - a[5] * a[3] * a[10] - a[9] * a[2] * a[7] + a[9] * a[3] * a[6];
458
- inv[7] = a[0] * a[6] * a[11] - a[0] * a[7] * a[10] - a[4] * a[2] * a[11] + a[4] * a[3] * a[10] + a[8] * a[2] * a[7] - a[8] * a[3] * a[6];
459
- inv[11] = -a[0] * a[5] * a[11] + a[0] * a[7] * a[9] + a[4] * a[1] * a[11] - a[4] * a[3] * a[9] - a[8] * a[1] * a[7] + a[8] * a[3] * a[5];
460
- inv[15] = a[0] * a[5] * a[10] - a[0] * a[6] * a[9] - a[4] * a[1] * a[10] + a[4] * a[2] * a[9] + a[8] * a[1] * a[6] - a[8] * a[2] * a[5];
461
- let det = a[0] * inv[0] + a[1] * inv[4] + a[2] * inv[8] + a[3] * inv[12];
462
- if (Math.abs(det) < 1e-20) return null;
463
- det = 1 / det;
464
- const result = Array.from(
465
- { length: 4 },
466
- () => new Array(4).fill(0)
467
- );
468
- for (let i = 0; i < 4; i++) {
469
- for (let j = 0; j < 4; j++) {
470
- result[i][j] = inv[i * 4 + j] * det;
471
- }
472
- }
473
- return result;
465
+ let clockVar = 0;
466
+ for (let i = 3; i < m; i++) clockVar += Q[i][i];
467
+ const posVar = Q[0][0] + Q[1][1] + Q[2][2];
468
+ return {
469
+ gdop: Math.sqrt(posVar + clockVar),
470
+ pdop: Math.sqrt(posVar),
471
+ hdop: Math.sqrt(Q[0][0] + Q[1][1]),
472
+ vdop: Math.sqrt(Q[2][2]),
473
+ tdop: Math.sqrt(clockVar)
474
+ };
474
475
  }
475
476
  function selectEphemeris(ephemerides, prn, timeMs) {
476
477
  return selectBest(
@@ -4307,28 +4308,6 @@ function sbasAirTropoVar(elRad) {
4307
4308
  const sigTropo = 0.12 * m;
4308
4309
  return sigMp * sigMp + NOISE_DIVG_AADA * NOISE_DIVG_AADA + sigTropo * sigTropo;
4309
4310
  }
4310
- function invertMatrix(A) {
4311
- const n = A.length;
4312
- const M = A.map((row, i) => [
4313
- ...row,
4314
- ...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
4315
- ]);
4316
- for (let col = 0; col < n; col++) {
4317
- let piv = col;
4318
- for (let r = col + 1; r < n; r++)
4319
- if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
4320
- if (Math.abs(M[piv][col]) < 1e-12) return null;
4321
- [M[col], M[piv]] = [M[piv], M[col]];
4322
- const d = M[col][col];
4323
- for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
4324
- for (let r = 0; r < n; r++) {
4325
- if (r === col) continue;
4326
- const f = M[r][col];
4327
- for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
4328
- }
4329
- }
4330
- return M.map((row) => row.slice(n));
4331
- }
4332
4311
  function sbasProtectionLevels(sats, opts = {}) {
4333
4312
  const kH = opts.kH ?? 6;
4334
4313
  const kV = opts.kV ?? 5.33;
@@ -4555,7 +4534,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
4555
4534
  const azel = ecefToAzEl(x, y, z, sat.x, sat.y, sat.z);
4556
4535
  if (azel.el >= elevationMaskDeg * Math.PI / 180) {
4557
4536
  used.push(prn);
4558
- azels.push(azel);
4537
+ azels.push({ ...azel, prn });
4559
4538
  if (sbas) {
4560
4539
  const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
4561
4540
  const dr = Math.hypot(dx, dy, dz) || 1;
@@ -40,9 +40,9 @@ import {
40
40
  sunEcef,
41
41
  toRtkEpoch,
42
42
  wlWavelength
43
- } from "./chunk-BWLLHLLS.js";
43
+ } from "./chunk-PHS7OFHN.js";
44
44
  import "./chunk-WQX354KD.js";
45
- import "./chunk-JYWYQ7DW.js";
45
+ import "./chunk-GLMQRZDL.js";
46
46
  import "./chunk-HVXYFUCB.js";
47
47
  import "./chunk-CVCTRIWU.js";
48
48
  import "./chunk-37QNKGTC.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "2.7.0",
3
+ "version": "2.8.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,