gnss-js 2.7.0 → 2.8.1

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.
@@ -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-T5M3LVBJ.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;
@@ -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;
@@ -380,17 +404,26 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
380
404
  const el = Math.atan2(up, Math.sqrt(east * east + north * north));
381
405
  return { az: (az + TWO_PI) % TWO_PI, el };
382
406
  }
407
+ function dopClock(prn) {
408
+ const sys = prn?.[0] ?? "";
409
+ return sys === "J" || sys === "S" ? "G" : sys;
410
+ }
383
411
  function computeDop(satAzEls) {
384
412
  const n = satAzEls.length;
385
- if (n < 4) return null;
413
+ const clocks = [...new Set(satAzEls.map((s) => dopClock(s.prn)))];
414
+ const m = 3 + clocks.length;
415
+ if (n < Math.max(4, m)) return null;
386
416
  const H = [];
387
- for (const { az, el } of satAzEls) {
417
+ for (const { az, el, prn } of satAzEls) {
388
418
  const cosEl = Math.cos(el);
389
- H.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
419
+ const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
420
+ const clock = dopClock(prn);
421
+ for (const c of clocks) row.push(c === clock ? 1 : 0);
422
+ H.push(row);
390
423
  }
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++) {
424
+ const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
425
+ for (let i = 0; i < m; i++) {
426
+ for (let j = 0; j < m; j++) {
394
427
  let sum = 0;
395
428
  for (let k = 0; k < n; k++) {
396
429
  sum += H[k][i] * H[k][j];
@@ -398,46 +431,18 @@ function computeDop(satAzEls) {
398
431
  HtH[i][j] = sum;
399
432
  }
400
433
  }
401
- const Q = invert4x4(HtH);
434
+ const Q = invertMatrix(HtH);
402
435
  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;
436
+ let clockVar = 0;
437
+ for (let i = 3; i < m; i++) clockVar += Q[i][i];
438
+ const posVar = Q[0][0] + Q[1][1] + Q[2][2];
439
+ return {
440
+ gdop: Math.sqrt(posVar + clockVar),
441
+ pdop: Math.sqrt(posVar),
442
+ hdop: Math.sqrt(Q[0][0] + Q[1][1]),
443
+ vdop: Math.sqrt(Q[2][2]),
444
+ tdop: Math.sqrt(clockVar)
445
+ };
441
446
  }
442
447
  function selectEphemeris(ephemerides, prn, timeMs) {
443
448
  return selectBest(
@@ -697,6 +702,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
697
702
  const gdop = new Array(times.length).fill(null);
698
703
  const hdop = new Array(times.length).fill(null);
699
704
  const vdop = new Array(times.length).fill(null);
705
+ const tdop = new Array(times.length).fill(null);
700
706
  const visiblePerEpoch = times.map(() => []);
701
707
  for (const prn of all.prns) {
702
708
  const series = all.positions[prn];
@@ -710,7 +716,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
710
716
  visibleCount[i]++;
711
717
  const sys = prn[0];
712
718
  (visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
713
- visiblePerEpoch[i].push({ az: p.az, el: p.el });
719
+ visiblePerEpoch[i].push({ az: p.az, el: p.el, prn });
714
720
  }
715
721
  }
716
722
  }
@@ -720,6 +726,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
720
726
  gdop[i] = dop ? dop.gdop : null;
721
727
  hdop[i] = dop ? dop.hdop : null;
722
728
  vdop[i] = dop ? dop.vdop : null;
729
+ tdop[i] = dop ? dop.tdop : null;
723
730
  }
724
731
  const passes = [];
725
732
  for (const prn of all.prns) {
@@ -766,11 +773,13 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
766
773
  gdop,
767
774
  hdop,
768
775
  vdop,
776
+ tdop,
769
777
  passes
770
778
  };
771
779
  }
772
780
 
773
781
  export {
782
+ invertMatrix,
774
783
  almanacEpochMs,
775
784
  almanacSatPosition,
776
785
  glonassAlmanacEpochMs,
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;
@@ -12655,17 +12679,26 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
12655
12679
  const el = Math.atan2(up, Math.sqrt(east * east + north * north));
12656
12680
  return { az: (az + TWO_PI) % TWO_PI, el };
12657
12681
  }
12682
+ function dopClock(prn) {
12683
+ const sys = prn?.[0] ?? "";
12684
+ return sys === "J" || sys === "S" ? "G" : sys;
12685
+ }
12658
12686
  function computeDop(satAzEls) {
12659
12687
  const n = satAzEls.length;
12660
- if (n < 4) return null;
12688
+ const clocks = [...new Set(satAzEls.map((s) => dopClock(s.prn)))];
12689
+ const m = 3 + clocks.length;
12690
+ if (n < Math.max(4, m)) return null;
12661
12691
  const H3 = [];
12662
- for (const { az, el } of satAzEls) {
12692
+ for (const { az, el, prn } of satAzEls) {
12663
12693
  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++) {
12694
+ const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
12695
+ const clock = dopClock(prn);
12696
+ for (const c of clocks) row.push(c === clock ? 1 : 0);
12697
+ H3.push(row);
12698
+ }
12699
+ const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
12700
+ for (let i = 0; i < m; i++) {
12701
+ for (let j = 0; j < m; j++) {
12669
12702
  let sum = 0;
12670
12703
  for (let k = 0; k < n; k++) {
12671
12704
  sum += H3[k][i] * H3[k][j];
@@ -12673,46 +12706,18 @@ function computeDop(satAzEls) {
12673
12706
  HtH[i][j] = sum;
12674
12707
  }
12675
12708
  }
12676
- const Q = invert4x4(HtH);
12709
+ const Q = invertMatrix(HtH);
12677
12710
  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;
12711
+ let clockVar = 0;
12712
+ for (let i = 3; i < m; i++) clockVar += Q[i][i];
12713
+ const posVar = Q[0][0] + Q[1][1] + Q[2][2];
12714
+ return {
12715
+ gdop: Math.sqrt(posVar + clockVar),
12716
+ pdop: Math.sqrt(posVar),
12717
+ hdop: Math.sqrt(Q[0][0] + Q[1][1]),
12718
+ vdop: Math.sqrt(Q[2][2]),
12719
+ tdop: Math.sqrt(clockVar)
12720
+ };
12716
12721
  }
12717
12722
  function selectEphemeris(ephemerides, prn, timeMs) {
12718
12723
  return selectBest(
@@ -12972,6 +12977,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
12972
12977
  const gdop = new Array(times.length).fill(null);
12973
12978
  const hdop = new Array(times.length).fill(null);
12974
12979
  const vdop = new Array(times.length).fill(null);
12980
+ const tdop = new Array(times.length).fill(null);
12975
12981
  const visiblePerEpoch = times.map(() => []);
12976
12982
  for (const prn of all.prns) {
12977
12983
  const series = all.positions[prn];
@@ -12985,7 +12991,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
12985
12991
  visibleCount[i]++;
12986
12992
  const sys = prn[0];
12987
12993
  (visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
12988
- visiblePerEpoch[i].push({ az: p.az, el: p.el });
12994
+ visiblePerEpoch[i].push({ az: p.az, el: p.el, prn });
12989
12995
  }
12990
12996
  }
12991
12997
  }
@@ -12995,6 +13001,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
12995
13001
  gdop[i] = dop ? dop.gdop : null;
12996
13002
  hdop[i] = dop ? dop.hdop : null;
12997
13003
  vdop[i] = dop ? dop.vdop : null;
13004
+ tdop[i] = dop ? dop.tdop : null;
12998
13005
  }
12999
13006
  const passes = [];
13000
13007
  for (const prn of all.prns) {
@@ -13041,6 +13048,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
13041
13048
  gdop,
13042
13049
  hdop,
13043
13050
  vdop,
13051
+ tdop,
13044
13052
  passes
13045
13053
  };
13046
13054
  }
@@ -16966,28 +16974,6 @@ function sbasAirTropoVar(elRad) {
16966
16974
  const sigTropo = 0.12 * m;
16967
16975
  return sigMp * sigMp + NOISE_DIVG_AADA * NOISE_DIVG_AADA + sigTropo * sigTropo;
16968
16976
  }
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
16977
  function sbasProtectionLevels(sats, opts = {}) {
16992
16978
  const kH = opts.kH ?? 6;
16993
16979
  const kV = opts.kV ?? 5.33;
@@ -17214,7 +17200,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
17214
17200
  const azel = ecefToAzEl(x, y, z, sat.x, sat.y, sat.z);
17215
17201
  if (azel.el >= elevationMaskDeg * Math.PI / 180) {
17216
17202
  used.push(prn);
17217
- azels.push(azel);
17203
+ azels.push({ ...azel, prn });
17218
17204
  if (sbas) {
17219
17205
  const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
17220
17206
  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-F6KOFOXN.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-T5M3LVBJ.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;
@@ -541,17 +565,26 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
541
565
  const el = Math.atan2(up, Math.sqrt(east * east + north * north));
542
566
  return { az: (az + TWO_PI) % TWO_PI, el };
543
567
  }
568
+ function dopClock(prn) {
569
+ const sys = prn?.[0] ?? "";
570
+ return sys === "J" || sys === "S" ? "G" : sys;
571
+ }
544
572
  function computeDop(satAzEls) {
545
573
  const n = satAzEls.length;
546
- if (n < 4) return null;
574
+ const clocks = [...new Set(satAzEls.map((s) => dopClock(s.prn)))];
575
+ const m = 3 + clocks.length;
576
+ if (n < Math.max(4, m)) return null;
547
577
  const H = [];
548
- for (const { az, el } of satAzEls) {
578
+ for (const { az, el, prn } of satAzEls) {
549
579
  const cosEl = Math.cos(el);
550
- H.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
580
+ const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
581
+ const clock = dopClock(prn);
582
+ for (const c of clocks) row.push(c === clock ? 1 : 0);
583
+ H.push(row);
551
584
  }
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++) {
585
+ const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
586
+ for (let i = 0; i < m; i++) {
587
+ for (let j = 0; j < m; j++) {
555
588
  let sum = 0;
556
589
  for (let k = 0; k < n; k++) {
557
590
  sum += H[k][i] * H[k][j];
@@ -559,46 +592,18 @@ function computeDop(satAzEls) {
559
592
  HtH[i][j] = sum;
560
593
  }
561
594
  }
562
- const Q = invert4x4(HtH);
595
+ const Q = invertMatrix(HtH);
563
596
  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;
597
+ let clockVar = 0;
598
+ for (let i = 3; i < m; i++) clockVar += Q[i][i];
599
+ const posVar = Q[0][0] + Q[1][1] + Q[2][2];
600
+ return {
601
+ gdop: Math.sqrt(posVar + clockVar),
602
+ pdop: Math.sqrt(posVar),
603
+ hdop: Math.sqrt(Q[0][0] + Q[1][1]),
604
+ vdop: Math.sqrt(Q[2][2]),
605
+ tdop: Math.sqrt(clockVar)
606
+ };
602
607
  }
603
608
  function selectEphemeris(ephemerides, prn, timeMs) {
604
609
  return selectBest(
@@ -858,6 +863,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
858
863
  const gdop = new Array(times.length).fill(null);
859
864
  const hdop = new Array(times.length).fill(null);
860
865
  const vdop = new Array(times.length).fill(null);
866
+ const tdop = new Array(times.length).fill(null);
861
867
  const visiblePerEpoch = times.map(() => []);
862
868
  for (const prn of all.prns) {
863
869
  const series = all.positions[prn];
@@ -871,7 +877,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
871
877
  visibleCount[i]++;
872
878
  const sys = prn[0];
873
879
  (visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
874
- visiblePerEpoch[i].push({ az: p.az, el: p.el });
880
+ visiblePerEpoch[i].push({ az: p.az, el: p.el, prn });
875
881
  }
876
882
  }
877
883
  }
@@ -881,6 +887,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
881
887
  gdop[i] = dop ? dop.gdop : null;
882
888
  hdop[i] = dop ? dop.hdop : null;
883
889
  vdop[i] = dop ? dop.vdop : null;
890
+ tdop[i] = dop ? dop.tdop : null;
884
891
  }
885
892
  const passes = [];
886
893
  for (const prn of all.prns) {
@@ -927,6 +934,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
927
934
  gdop,
928
935
  hdop,
929
936
  vdop,
937
+ tdop,
930
938
  passes
931
939
  };
932
940
  }
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,23 @@ 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 time scale, as a multi-GNSS receiver solves: every extra
165
+ * time scale costs one observation. QZSS and SBAS share the GPS clock —
166
+ * QZSST and SBAS network time are steered to GPST (RTKLIB pntpos does the
167
+ * same) — so a lone SBAS GEO doesn't inflate TDOP/GDOP. Untagged
168
+ * satellites share a single clock (the classic single-system model).
169
+ *
170
+ * @param satAzEls Satellite azimuth/elevation pairs (radians), optionally
171
+ * tagged with their PRN.
172
+ * @returns DOP values, or null when there are fewer satellites than
173
+ * unknowns (3 + one clock per constellation) or the geometry is singular.
159
174
  */
160
175
  declare function computeDop(satAzEls: {
161
176
  az: number;
162
177
  el: number;
178
+ prn?: string;
163
179
  }[]): DopValues | null;
164
180
  /**
165
181
  * Find the best ephemeris for a given PRN at a given time.
@@ -242,14 +258,16 @@ interface VisibilityResult {
242
258
  visibleCount: number[];
243
259
  /** Per-system visible counts per epoch (system letter → counts). */
244
260
  visibleBySystem: Record<string, number[]>;
245
- /** PDOP per epoch (null when < 4 satellites above the mask). */
261
+ /** PDOP per epoch (null when fewer satellites than unknowns). */
246
262
  pdop: (number | null)[];
247
- /** GDOP per epoch (null when < 4 satellites above the mask). */
263
+ /** GDOP per epoch (null when fewer satellites than unknowns). */
248
264
  gdop: (number | null)[];
249
- /** HDOP per epoch (null when < 4 satellites above the mask). */
265
+ /** HDOP per epoch (null when fewer satellites than unknowns). */
250
266
  hdop: (number | null)[];
251
- /** VDOP per epoch (null when < 4 satellites above the mask). */
267
+ /** VDOP per epoch (null when fewer satellites than unknowns). */
252
268
  vdop: (number | null)[];
269
+ /** TDOP per epoch (all constellation clocks; null like the others). */
270
+ tdop: (number | null)[];
253
271
  /** Discrete above-mask passes, sorted by rise time. */
254
272
  passes: VisibilityPass[];
255
273
  }
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,23 @@ 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 time scale, as a multi-GNSS receiver solves: every extra
165
+ * time scale costs one observation. QZSS and SBAS share the GPS clock —
166
+ * QZSST and SBAS network time are steered to GPST (RTKLIB pntpos does the
167
+ * same) — so a lone SBAS GEO doesn't inflate TDOP/GDOP. Untagged
168
+ * satellites share a single clock (the classic single-system model).
169
+ *
170
+ * @param satAzEls Satellite azimuth/elevation pairs (radians), optionally
171
+ * tagged with their PRN.
172
+ * @returns DOP values, or null when there are fewer satellites than
173
+ * unknowns (3 + one clock per constellation) or the geometry is singular.
159
174
  */
160
175
  declare function computeDop(satAzEls: {
161
176
  az: number;
162
177
  el: number;
178
+ prn?: string;
163
179
  }[]): DopValues | null;
164
180
  /**
165
181
  * Find the best ephemeris for a given PRN at a given time.
@@ -242,14 +258,16 @@ interface VisibilityResult {
242
258
  visibleCount: number[];
243
259
  /** Per-system visible counts per epoch (system letter → counts). */
244
260
  visibleBySystem: Record<string, number[]>;
245
- /** PDOP per epoch (null when < 4 satellites above the mask). */
261
+ /** PDOP per epoch (null when fewer satellites than unknowns). */
246
262
  pdop: (number | null)[];
247
- /** GDOP per epoch (null when < 4 satellites above the mask). */
263
+ /** GDOP per epoch (null when fewer satellites than unknowns). */
248
264
  gdop: (number | null)[];
249
- /** HDOP per epoch (null when < 4 satellites above the mask). */
265
+ /** HDOP per epoch (null when fewer satellites than unknowns). */
250
266
  hdop: (number | null)[];
251
- /** VDOP per epoch (null when < 4 satellites above the mask). */
267
+ /** VDOP per epoch (null when fewer satellites than unknowns). */
252
268
  vdop: (number | null)[];
269
+ /** TDOP per epoch (all constellation clocks; null like the others). */
270
+ tdop: (number | null)[];
253
271
  /** Discrete above-mask passes, sorted by rise time. */
254
272
  passes: VisibilityPass[];
255
273
  }
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-T5M3LVBJ.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;
@@ -413,17 +437,26 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
413
437
  const el = Math.atan2(up, Math.sqrt(east * east + north * north));
414
438
  return { az: (az + TWO_PI) % TWO_PI, el };
415
439
  }
440
+ function dopClock(prn) {
441
+ const sys = prn?.[0] ?? "";
442
+ return sys === "J" || sys === "S" ? "G" : sys;
443
+ }
416
444
  function computeDop(satAzEls) {
417
445
  const n = satAzEls.length;
418
- if (n < 4) return null;
446
+ const clocks = [...new Set(satAzEls.map((s) => dopClock(s.prn)))];
447
+ const m = 3 + clocks.length;
448
+ if (n < Math.max(4, m)) return null;
419
449
  const H = [];
420
- for (const { az, el } of satAzEls) {
450
+ for (const { az, el, prn } of satAzEls) {
421
451
  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++) {
452
+ const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
453
+ const clock = dopClock(prn);
454
+ for (const c of clocks) row.push(c === clock ? 1 : 0);
455
+ H.push(row);
456
+ }
457
+ const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
458
+ for (let i = 0; i < m; i++) {
459
+ for (let j = 0; j < m; j++) {
427
460
  let sum = 0;
428
461
  for (let k = 0; k < n; k++) {
429
462
  sum += H[k][i] * H[k][j];
@@ -431,46 +464,18 @@ function computeDop(satAzEls) {
431
464
  HtH[i][j] = sum;
432
465
  }
433
466
  }
434
- const Q = invert4x4(HtH);
467
+ const Q = invertMatrix(HtH);
435
468
  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;
469
+ let clockVar = 0;
470
+ for (let i = 3; i < m; i++) clockVar += Q[i][i];
471
+ const posVar = Q[0][0] + Q[1][1] + Q[2][2];
472
+ return {
473
+ gdop: Math.sqrt(posVar + clockVar),
474
+ pdop: Math.sqrt(posVar),
475
+ hdop: Math.sqrt(Q[0][0] + Q[1][1]),
476
+ vdop: Math.sqrt(Q[2][2]),
477
+ tdop: Math.sqrt(clockVar)
478
+ };
474
479
  }
475
480
  function selectEphemeris(ephemerides, prn, timeMs) {
476
481
  return selectBest(
@@ -4307,28 +4312,6 @@ function sbasAirTropoVar(elRad) {
4307
4312
  const sigTropo = 0.12 * m;
4308
4313
  return sigMp * sigMp + NOISE_DIVG_AADA * NOISE_DIVG_AADA + sigTropo * sigTropo;
4309
4314
  }
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
4315
  function sbasProtectionLevels(sats, opts = {}) {
4333
4316
  const kH = opts.kH ?? 6;
4334
4317
  const kV = opts.kV ?? 5.33;
@@ -4555,7 +4538,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
4555
4538
  const azel = ecefToAzEl(x, y, z, sat.x, sat.y, sat.z);
4556
4539
  if (azel.el >= elevationMaskDeg * Math.PI / 180) {
4557
4540
  used.push(prn);
4558
- azels.push(azel);
4541
+ azels.push({ ...azel, prn });
4559
4542
  if (sbas) {
4560
4543
  const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
4561
4544
  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-F6KOFOXN.js";
44
44
  import "./chunk-WQX354KD.js";
45
- import "./chunk-JYWYQ7DW.js";
45
+ import "./chunk-T5M3LVBJ.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.1",
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,