gnss-js 2.6.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.
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,11 +40,11 @@ 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
- import "./chunk-MIIM4LDY.js";
47
+ import "./chunk-CVCTRIWU.js";
48
48
  import "./chunk-37QNKGTC.js";
49
49
  import "./chunk-LEEU5OIO.js";
50
50
  import "./chunk-6FAL6P4G.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "2.6.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,