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.
@@ -14,13 +14,29 @@ declare function deg2dms(deg: number): [number, number, number];
14
14
  * Vincenty's inverse formula — orthodromic (great-circle) distance,
15
15
  * initial bearing, and final bearing on the WGS84 ellipsoid.
16
16
  * All angles in radians.
17
- * @returns { distance (m), initialBearing (rad), finalBearing (rad) }
17
+ * @returns { distance (m), initialBearing (rad), finalBearing (rad),
18
+ * converged } — `converged` is false for near-antipodal pairs, where the
19
+ * iteration fails and a spherical approximation is returned instead
20
+ * (distance good to ~0.5 %, bearings unreliable).
18
21
  */
19
22
  declare function vincenty(lat1: number, lon1: number, lat2: number, lon2: number): {
20
23
  distance: number;
21
24
  initialBearing: number;
22
25
  finalBearing: number;
26
+ converged: boolean;
23
27
  };
28
+ /**
29
+ * Vincenty's direct formula — the point reached from (lat1, lon1) after
30
+ * `distance` metres along the geodesic leaving at `bearing` (radians,
31
+ * clockwise from north) on the WGS84 ellipsoid.
32
+ * @returns [lat, lon] in radians, lon wrapped to (-π, π]
33
+ */
34
+ declare function vincentyDirect(lat1: number, lon1: number, bearing: number, distance: number): [number, number];
35
+ /**
36
+ * Meridian arc length from the equator to geodetic latitude `lat` (radians)
37
+ * on WGS84, via the rectifying latitude (Helmert series in n, sub-mm).
38
+ */
39
+ declare function meridianArc(lat: number): number;
24
40
  /**
25
41
  * Loxodromic (rhumb line) distance and constant bearing on the WGS84 ellipsoid.
26
42
  * Uses the ellipsoidal rhumb-line formula with isometric latitudes.
@@ -41,7 +57,8 @@ declare function rhumbLine(lat1: number, lon1: number, lat2: number, lon2: numbe
41
57
  */
42
58
  declare function euclidean3D(x1: number, y1: number, z1: number, x2: number, y2: number, z2: number): number;
43
59
  /**
44
- * Geodetic midpoint along the great-circle arc.
60
+ * Midpoint of the geodesic (shortest path on the WGS84 ellipsoid) between
61
+ * two points: half the Vincenty distance along the initial bearing.
45
62
  * @returns [lat, lon] in radians
46
63
  */
47
64
  declare function greatCircleMidpoint(lat1: number, lon1: number, lat2: number, lon2: number): [number, number];
@@ -57,12 +74,18 @@ declare function horizonDistance(heightMeters: number): number;
57
74
  * Convert geodetic coordinates to UTM.
58
75
  * @param lat Latitude in radians
59
76
  * @param lon Longitude in radians
77
+ * @returns easting/northing (m), zone (1–60, with the Norway/Svalbard
78
+ * exceptions), hemisphere, MGRS latitude `band` letter (C–X), and
79
+ * `inRange` — false outside UTM's 80°S–84°N, where the values are
80
+ * computed at the clamped latitude and are not a valid UTM position.
60
81
  */
61
82
  declare function geodeticToUtm(lat: number, lon: number): {
62
83
  easting: number;
63
84
  northing: number;
64
85
  zone: number;
65
86
  hemisphere: 'N' | 'S';
87
+ band: string;
88
+ inRange: boolean;
66
89
  };
67
90
  /**
68
91
  * Maidenhead grid locator (6 characters) from geodetic coordinates.
@@ -78,4 +101,4 @@ declare function geodeticToMaidenhead(lat: number, lon: number): string;
78
101
  */
79
102
  declare function geodeticToGeohash(lat: number, lon: number, precision?: number): string;
80
103
 
81
- export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, rad2deg, rhumbLine, vincenty };
104
+ export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, meridianArc, rad2deg, rhumbLine, vincenty, vincentyDirect };
@@ -7,10 +7,12 @@ import {
7
7
  geodeticToUtm,
8
8
  greatCircleMidpoint,
9
9
  horizonDistance,
10
+ meridianArc,
10
11
  rad2deg,
11
12
  rhumbLine,
12
- vincenty
13
- } from "./chunk-MIIM4LDY.js";
13
+ vincenty,
14
+ vincentyDirect
15
+ } from "./chunk-CVCTRIWU.js";
14
16
  import {
15
17
  clampUnit,
16
18
  ecefToGeodetic,
@@ -33,7 +35,9 @@ export {
33
35
  getEnuDifference,
34
36
  greatCircleMidpoint,
35
37
  horizonDistance,
38
+ meridianArc,
36
39
  rad2deg,
37
40
  rhumbLine,
38
- vincenty
41
+ vincenty,
42
+ vincentyDirect
39
43
  };
package/dist/index.cjs CHANGED
@@ -261,6 +261,7 @@ __export(src_exports, {
261
261
  lambdaSearch: () => lambdaSearch,
262
262
  looksLikeRtcm2: () => looksLikeRtcm2,
263
263
  maskRadForAzimuth: () => maskRadForAzimuth,
264
+ meridianArc: () => meridianArc,
264
265
  moonEcef: () => moonEcef,
265
266
  navTimesFromEph: () => navTimesFromEph,
266
267
  niellMapping: () => niellMapping,
@@ -355,6 +356,7 @@ __export(src_exports, {
355
356
  updateStreamStats: () => updateStreamStats,
356
357
  verifyChecksum: () => verifyChecksum,
357
358
  vincenty: () => vincenty,
359
+ vincentyDirect: () => vincentyDirect,
358
360
  wlWavelength: () => wlWavelength,
359
361
  writeCrx: () => writeCrx,
360
362
  writeModernObsBlob: () => writeModernObsBlob,
@@ -1125,7 +1127,12 @@ function vincenty(lat1, lon1, lat2, lon2) {
1125
1127
  (cosU2 * sinLambda2) ** 2 + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda2) ** 2
1126
1128
  );
1127
1129
  if (sinSigma === 0) {
1128
- return { distance: 0, initialBearing: 0, finalBearing: 0 };
1130
+ return {
1131
+ distance: 0,
1132
+ initialBearing: 0,
1133
+ finalBearing: 0,
1134
+ converged: true
1135
+ };
1129
1136
  }
1130
1137
  cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda2;
1131
1138
  sigma = Math.atan2(sinSigma, cosSigma);
@@ -1155,7 +1162,7 @@ function vincenty(lat1, lon1, lat2, lon2) {
1155
1162
  cosLat1 * Math.sin(-dLon),
1156
1163
  -sinLat1 * cosLat2 + cosLat1 * sinLat2 * Math.cos(-dLon)
1157
1164
  );
1158
- return { distance: distance2, initialBearing: ib, finalBearing: fb };
1165
+ return { distance: distance2, initialBearing: ib, finalBearing: fb, converged: false };
1159
1166
  }
1160
1167
  const uSq = cos2Alpha * (WGS84_SEMI_MAJOR_AXIS ** 2 - WGS84_SEMI_MINOR_AXIS ** 2) / WGS84_SEMI_MINOR_AXIS ** 2;
1161
1168
  const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
@@ -1172,10 +1179,53 @@ function vincenty(lat1, lon1, lat2, lon2) {
1172
1179
  cosU1 * sinLambda,
1173
1180
  -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda
1174
1181
  );
1175
- return { distance, initialBearing, finalBearing };
1182
+ return { distance, initialBearing, finalBearing, converged: true };
1183
+ }
1184
+ function vincentyDirect(lat1, lon1, bearing, distance) {
1185
+ const a = WGS84_SEMI_MAJOR_AXIS, b = WGS84_SEMI_MINOR_AXIS, f = WGS84_FLATTENING;
1186
+ const sinA1 = Math.sin(bearing), cosA1 = Math.cos(bearing);
1187
+ const tanU1 = (1 - f) * Math.tan(lat1);
1188
+ const cosU1 = 1 / Math.sqrt(1 + tanU1 ** 2), sinU1 = tanU1 * cosU1;
1189
+ const sigma1 = Math.atan2(tanU1, cosA1);
1190
+ const sinAlpha = cosU1 * sinA1;
1191
+ const cos2Alpha = 1 - sinAlpha ** 2;
1192
+ const uSq = cos2Alpha * (a ** 2 - b ** 2) / b ** 2;
1193
+ const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
1194
+ const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
1195
+ let sigma = distance / (b * A);
1196
+ let sigmaPrev;
1197
+ let cos2SigmaM, sinSigma, cosSigma;
1198
+ let iter = 0;
1199
+ do {
1200
+ cos2SigmaM = Math.cos(2 * sigma1 + sigma);
1201
+ sinSigma = Math.sin(sigma);
1202
+ cosSigma = Math.cos(sigma);
1203
+ const deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM ** 2) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma ** 2) * (-3 + 4 * cos2SigmaM ** 2)));
1204
+ sigmaPrev = sigma;
1205
+ sigma = distance / (b * A) + deltaSigma;
1206
+ } while (Math.abs(sigma - sigmaPrev) > 1e-12 && ++iter < 200);
1207
+ const tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosA1;
1208
+ const lat2 = Math.atan2(
1209
+ sinU1 * cosSigma + cosU1 * sinSigma * cosA1,
1210
+ (1 - f) * Math.sqrt(sinAlpha ** 2 + tmp ** 2)
1211
+ );
1212
+ const lambda = Math.atan2(
1213
+ sinSigma * sinA1,
1214
+ cosU1 * cosSigma - sinU1 * sinSigma * cosA1
1215
+ );
1216
+ const C2 = f / 16 * cos2Alpha * (4 + f * (4 - 3 * cos2Alpha));
1217
+ const L = lambda - (1 - C2) * f * sinAlpha * (sigma + C2 * sinSigma * (cos2SigmaM + C2 * cosSigma * (-1 + 2 * cos2SigmaM ** 2)));
1218
+ let lon2 = lon1 + L;
1219
+ lon2 = Math.atan2(Math.sin(lon2), Math.cos(lon2));
1220
+ return [lat2, lon2];
1221
+ }
1222
+ function meridianArc(lat) {
1223
+ const n = WGS84_FLATTENING / (2 - WGS84_FLATTENING);
1224
+ const n2 = n * n, n3 = n2 * n, n4 = n3 * n;
1225
+ const mu = lat + (-3 * n / 2 + 9 * n3 / 16) * Math.sin(2 * lat) + (15 * n2 / 16 - 15 * n4 / 32) * Math.sin(4 * lat) + -35 * n3 / 48 * Math.sin(6 * lat) + 315 * n4 / 512 * Math.sin(8 * lat);
1226
+ return WGS84_SEMI_MAJOR_AXIS / (1 + n) * (1 + n2 / 4 + n4 / 64) * mu;
1176
1227
  }
1177
1228
  function rhumbLine(lat1, lon1, lat2, lon2) {
1178
- const dLat = lat2 - lat1;
1179
1229
  let dLon = lon2 - lon1;
1180
1230
  const MAX_LAT = Math.PI / 2 - 1e-10;
1181
1231
  const isometricLat = (lat) => {
@@ -1189,33 +1239,28 @@ function rhumbLine(lat1, lon1, lat2, lon2) {
1189
1239
  const psi1 = isometricLat(lat1);
1190
1240
  const psi2 = isometricLat(lat2);
1191
1241
  const dPsi = psi2 - psi1;
1192
- const q = Math.abs(dPsi) > 1e-12 ? dLat / dPsi : Math.cos(lat1);
1193
1242
  if (Math.abs(dLon) > Math.PI) {
1194
1243
  dLon = dLon > 0 ? -(2 * Math.PI - dLon) : 2 * Math.PI + dLon;
1195
1244
  }
1196
1245
  const bearing = Math.atan2(dLon, dPsi);
1197
- const e22 = WGS84_ECCENTRICITY_SQUARED;
1198
- const e4 = e22 * e22;
1199
- const e6 = e4 * e22;
1200
- const meridionalRadius = WGS84_SEMI_MAJOR_AXIS * (1 - e22) * (1 + 3 / 4 * e22 + 45 / 64 * e4 + 175 / 256 * e6);
1201
- const distance = Math.sqrt(dLat ** 2 + q ** 2 * dLon ** 2) * meridionalRadius;
1246
+ const dM = meridianArc(lat2) - meridianArc(lat1);
1247
+ let q;
1248
+ if (Math.abs(dPsi) > 1e-6) {
1249
+ q = dM / dPsi;
1250
+ } else {
1251
+ const latM = (lat1 + lat2) / 2;
1252
+ q = WGS84_SEMI_MAJOR_AXIS * Math.cos(latM) / Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED * Math.sin(latM) ** 2);
1253
+ }
1254
+ const distance = Math.sqrt(dM ** 2 + (q * dLon) ** 2);
1202
1255
  return { distance, bearing };
1203
1256
  }
1204
1257
  function euclidean3D(x12, y1, z1, x22, y2, z2) {
1205
1258
  return Math.sqrt((x22 - x12) ** 2 + (y2 - y1) ** 2 + (z2 - z1) ** 2);
1206
1259
  }
1207
1260
  function greatCircleMidpoint(lat1, lon1, lat2, lon2) {
1208
- const dLon = lon2 - lon1;
1209
- const Bx = Math.cos(lat2) * Math.cos(dLon);
1210
- const By = Math.cos(lat2) * Math.sin(dLon);
1211
- const sumX = Math.cos(lat1) + Bx;
1212
- const denom = Math.sqrt(sumX ** 2 + By ** 2);
1213
- if (denom < 1e-15) {
1214
- return [(lat1 + lat2) / 2, lon1];
1215
- }
1216
- const latMid = Math.atan2(Math.sin(lat1) + Math.sin(lat2), denom);
1217
- const lonMid = lon1 + Math.atan2(By, sumX);
1218
- return [latMid, lonMid];
1261
+ const { distance, initialBearing } = vincenty(lat1, lon1, lat2, lon2);
1262
+ if (distance === 0) return [lat1, lon1];
1263
+ return vincentyDirect(lat1, lon1, initialBearing, distance / 2);
1219
1264
  }
1220
1265
  function horizonDistance(heightMeters) {
1221
1266
  if (heightMeters <= 0) return 0;
@@ -1228,18 +1273,36 @@ function horizonDistance(heightMeters) {
1228
1273
  // src/coordinates/projections.ts
1229
1274
  var e2 = WGS84_ECCENTRICITY_SQUARED;
1230
1275
  var k0 = 0.9996;
1276
+ var UTM_BANDS = "CDEFGHJKLMNPQRSTUVWX";
1277
+ function utmZone(latDeg, lonDeg) {
1278
+ let zone = Math.floor((lonDeg + 180) / 6) + 1;
1279
+ if (zone > 60) zone = 1;
1280
+ if (latDeg >= 56 && latDeg < 64 && lonDeg >= 3 && lonDeg < 12) return 32;
1281
+ if (latDeg >= 72 && latDeg <= 84) {
1282
+ if (lonDeg >= 0 && lonDeg < 9) return 31;
1283
+ if (lonDeg >= 9 && lonDeg < 21) return 33;
1284
+ if (lonDeg >= 21 && lonDeg < 33) return 35;
1285
+ if (lonDeg >= 33 && lonDeg < 42) return 37;
1286
+ }
1287
+ return zone;
1288
+ }
1231
1289
  function geodeticToUtm(lat, lon) {
1290
+ const rawLatDeg = lat * 180 / Math.PI;
1291
+ const inRange = rawLatDeg >= -80 && rawLatDeg <= 84;
1232
1292
  const MAX_UTM_LAT = 84 * (Math.PI / 180);
1233
1293
  lat = Math.max(-MAX_UTM_LAT, Math.min(MAX_UTM_LAT, lat));
1234
1294
  const latDeg = lat * 180 / Math.PI;
1295
+ lon = Math.atan2(Math.sin(lon), Math.cos(lon));
1235
1296
  const lonDeg = lon * 180 / Math.PI;
1236
- const zone = Math.floor((lonDeg + 180) / 6) + 1;
1297
+ const zone = utmZone(latDeg, lonDeg);
1298
+ const band = UTM_BANDS[Math.max(0, Math.min(19, Math.floor((latDeg + 80) / 8)))];
1237
1299
  const lon0 = ((zone - 1) * 6 - 180 + 3) * (Math.PI / 180);
1238
1300
  const ep2 = e2 / (1 - e2);
1239
1301
  const N = WGS84_SEMI_MAJOR_AXIS / Math.sqrt(1 - e2 * Math.sin(lat) ** 2);
1240
1302
  const T = Math.tan(lat) ** 2;
1241
1303
  const C2 = ep2 * Math.cos(lat) ** 2;
1242
- const A = Math.cos(lat) * (lon - lon0);
1304
+ const dLon0 = Math.atan2(Math.sin(lon - lon0), Math.cos(lon - lon0));
1305
+ const A = Math.cos(lat) * dLon0;
1243
1306
  const M = WGS84_SEMI_MAJOR_AXIS * ((1 - e2 / 4 - 3 * e2 ** 2 / 64 - 5 * e2 ** 3 / 256) * lat - (3 * e2 / 8 + 3 * e2 ** 2 / 32 + 45 * e2 ** 3 / 1024) * Math.sin(2 * lat) + (15 * e2 ** 2 / 256 + 45 * e2 ** 3 / 1024) * Math.sin(4 * lat) - 35 * e2 ** 3 / 3072 * Math.sin(6 * lat));
1244
1307
  const easting = k0 * N * (A + (1 - T + C2) * A ** 3 / 6 + (5 - 18 * T + T ** 2 + 72 * C2 - 58 * ep2) * A ** 5 / 120) + 5e5;
1245
1308
  let northing = k0 * (M + N * Math.tan(lat) * (A ** 2 / 2 + (5 - T + 9 * C2 + 4 * C2 ** 2) * A ** 4 / 24 + (61 - 58 * T + T ** 2 + 600 * C2 - 330 * ep2) * A ** 6 / 720));
@@ -1247,7 +1310,7 @@ function geodeticToUtm(lat, lon) {
1247
1310
  if (hemisphere === "S") {
1248
1311
  northing += 1e7;
1249
1312
  }
1250
- return { easting, northing, zone, hemisphere };
1313
+ return { easting, northing, zone, hemisphere, band, inRange };
1251
1314
  }
1252
1315
  function geodeticToMaidenhead(lat, lon) {
1253
1316
  const latDeg = Math.max(0, Math.min(179.999999, lat * 180 / Math.PI + 90));
@@ -12225,6 +12288,30 @@ function parseBinex(data) {
12225
12288
  return { epochs, ephemerides, messageCounts, obsCodes, badCrc: stats.badCrc };
12226
12289
  }
12227
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
+
12228
12315
  // src/orbit/almanac.ts
12229
12316
  var GM_GPS = 3986005e8;
12230
12317
  var GM_GAL = 3986004418e5;
@@ -12594,15 +12681,20 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
12594
12681
  }
12595
12682
  function computeDop(satAzEls) {
12596
12683
  const n = satAzEls.length;
12597
- 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;
12598
12687
  const H3 = [];
12599
- for (const { az, el } of satAzEls) {
12688
+ for (const { az, el, prn } of satAzEls) {
12600
12689
  const cosEl = Math.cos(el);
12601
- H3.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
12602
- }
12603
- const HtH = Array.from({ length: 4 }, () => new Array(4).fill(0));
12604
- for (let i = 0; i < 4; i++) {
12605
- 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++) {
12606
12698
  let sum = 0;
12607
12699
  for (let k = 0; k < n; k++) {
12608
12700
  sum += H3[k][i] * H3[k][j];
@@ -12610,46 +12702,18 @@ function computeDop(satAzEls) {
12610
12702
  HtH[i][j] = sum;
12611
12703
  }
12612
12704
  }
12613
- const Q = invert4x4(HtH);
12705
+ const Q = invertMatrix(HtH);
12614
12706
  if (!Q) return null;
12615
- const gdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2] + Q[3][3]);
12616
- const pdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2]);
12617
- const hdop = Math.sqrt(Q[0][0] + Q[1][1]);
12618
- const vdop = Math.sqrt(Q[2][2]);
12619
- return { gdop, pdop, hdop, vdop };
12620
- }
12621
- function invert4x4(m) {
12622
- const a = m.flat();
12623
- const inv = new Array(16).fill(0);
12624
- 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];
12625
- 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];
12626
- 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];
12627
- 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];
12628
- 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];
12629
- 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];
12630
- 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];
12631
- 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];
12632
- 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];
12633
- 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];
12634
- 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];
12635
- 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];
12636
- 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];
12637
- 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];
12638
- 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];
12639
- 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];
12640
- let det = a[0] * inv[0] + a[1] * inv[4] + a[2] * inv[8] + a[3] * inv[12];
12641
- if (Math.abs(det) < 1e-20) return null;
12642
- det = 1 / det;
12643
- const result = Array.from(
12644
- { length: 4 },
12645
- () => new Array(4).fill(0)
12646
- );
12647
- for (let i = 0; i < 4; i++) {
12648
- for (let j = 0; j < 4; j++) {
12649
- result[i][j] = inv[i * 4 + j] * det;
12650
- }
12651
- }
12652
- 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
+ };
12653
12717
  }
12654
12718
  function selectEphemeris(ephemerides, prn, timeMs) {
12655
12719
  return selectBest(
@@ -12909,6 +12973,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
12909
12973
  const gdop = new Array(times.length).fill(null);
12910
12974
  const hdop = new Array(times.length).fill(null);
12911
12975
  const vdop = new Array(times.length).fill(null);
12976
+ const tdop = new Array(times.length).fill(null);
12912
12977
  const visiblePerEpoch = times.map(() => []);
12913
12978
  for (const prn of all.prns) {
12914
12979
  const series = all.positions[prn];
@@ -12922,7 +12987,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
12922
12987
  visibleCount[i]++;
12923
12988
  const sys = prn[0];
12924
12989
  (visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
12925
- visiblePerEpoch[i].push({ az: p.az, el: p.el });
12990
+ visiblePerEpoch[i].push({ az: p.az, el: p.el, prn });
12926
12991
  }
12927
12992
  }
12928
12993
  }
@@ -12932,6 +12997,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
12932
12997
  gdop[i] = dop ? dop.gdop : null;
12933
12998
  hdop[i] = dop ? dop.hdop : null;
12934
12999
  vdop[i] = dop ? dop.vdop : null;
13000
+ tdop[i] = dop ? dop.tdop : null;
12935
13001
  }
12936
13002
  const passes = [];
12937
13003
  for (const prn of all.prns) {
@@ -12978,6 +13044,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
12978
13044
  gdop,
12979
13045
  hdop,
12980
13046
  vdop,
13047
+ tdop,
12981
13048
  passes
12982
13049
  };
12983
13050
  }
@@ -16903,28 +16970,6 @@ function sbasAirTropoVar(elRad) {
16903
16970
  const sigTropo = 0.12 * m;
16904
16971
  return sigMp * sigMp + NOISE_DIVG_AADA * NOISE_DIVG_AADA + sigTropo * sigTropo;
16905
16972
  }
16906
- function invertMatrix(A) {
16907
- const n = A.length;
16908
- const M = A.map((row, i) => [
16909
- ...row,
16910
- ...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
16911
- ]);
16912
- for (let col = 0; col < n; col++) {
16913
- let piv = col;
16914
- for (let r = col + 1; r < n; r++)
16915
- if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
16916
- if (Math.abs(M[piv][col]) < 1e-12) return null;
16917
- [M[col], M[piv]] = [M[piv], M[col]];
16918
- const d = M[col][col];
16919
- for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
16920
- for (let r = 0; r < n; r++) {
16921
- if (r === col) continue;
16922
- const f = M[r][col];
16923
- for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
16924
- }
16925
- }
16926
- return M.map((row) => row.slice(n));
16927
- }
16928
16973
  function sbasProtectionLevels(sats, opts = {}) {
16929
16974
  const kH = opts.kH ?? 6;
16930
16975
  const kV = opts.kV ?? 5.33;
@@ -17151,7 +17196,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
17151
17196
  const azel = ecefToAzEl(x, y, z, sat.x, sat.y, sat.z);
17152
17197
  if (azel.el >= elevationMaskDeg * Math.PI / 180) {
17153
17198
  used.push(prn);
17154
- azels.push(azel);
17199
+ azels.push({ ...azel, prn });
17155
17200
  if (sbas) {
17156
17201
  const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
17157
17202
  const dr = Math.hypot(dx, dy, dz) || 1;
@@ -19692,6 +19737,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
19692
19737
  lambdaSearch,
19693
19738
  looksLikeRtcm2,
19694
19739
  maskRadForAzimuth,
19740
+ meridianArc,
19695
19741
  moonEcef,
19696
19742
  navTimesFromEph,
19697
19743
  niellMapping,
@@ -19786,6 +19832,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
19786
19832
  updateStreamStats,
19787
19833
  verifyChecksum,
19788
19834
  vincenty,
19835
+ vincentyDirect,
19789
19836
  wlWavelength,
19790
19837
  writeCrx,
19791
19838
  writeModernObsBlob,
package/dist/index.d.cts CHANGED
@@ -3,7 +3,7 @@ export { D as DAYS_MJD2000_MJD, a as DAYS_MJD_JULIAN, H as HourCode, M as MILLIS
3
3
  export { WGS84_ECCENTRICITY_SQUARED, WGS84_FLATTENING, WGS84_SEMI_MAJOR_AXIS, WGS84_SEMI_MINOR_AXIS } from './constants.cjs';
4
4
  export { A as ARC_GAP_FACTOR, B as BAND_LABELS, a as BDS_SATELLITES, b as BdsSatellite, C as C_LIGHT, D as DEFAULT_ELEV_MASK_DEG, c as DUAL_FREQ_PAIRS, F as FREQ, G as GLO_CHANNEL_FALLBACK, d as GLO_F1_BASE, e as GLO_F1_STEP, f as GLO_F2_BASE, g as GLO_F2_STEP, h as GLO_F3, O as OMEGA_E, i as OnSlipDetected, S as SYSTEM_NAMES, j as SYS_SHORT, k as buildGloChannelMap, l as buildObsIndices, m as formatUTCTime, n as getFreq, o as gloFreq } from './gnss-BtXrG3zH.cjs';
5
5
  export { Scale, getBdsTime, getDateFromBdsTime, getDateFromDayOfWeek, getDateFromDayOfYear, getDateFromGalTime, getDateFromGloN, getDateFromGpsData, getDateFromGpsTime, getDateFromHourCode, getDateFromJulianDate, getDateFromMJD, getDateFromMJD2000, getDateFromRINEX, getDateFromTai, getDateFromTimeOfDay, getDateFromTt, getDateFromUnixTime, getDateFromUtc, getDayOfWeek, getDayOfYear, getGalTime, getGloN4, getGloNA, getGpsLeap, getGpsTime, getHourCode, getHoursFromTimeDifference, getJulianDate, getLeap, getMJD, getMJD2000, getMinutesFromTimeDifference, getNtpTime, getRINEX, getSecondsFromTimeDifference, getTaiDate, getTimeDifference, getTimeDifferenceFromDays, getTimeDifferenceFromHours, getTimeDifferenceFromMinutes, getTimeDifferenceFromObject, getTimeDifferenceFromSeconds, getTimeOfDay, getTimeOfWeek, getTotalDaysFromTimeDifference, getTtDate, getUnixTime, getUtcDate, getWeekNumber, getWeekOfYear } from './time.cjs';
6
- export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, rad2deg, rhumbLine, vincenty } from './coordinates.cjs';
6
+ export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, meridianArc, rad2deg, rhumbLine, vincenty, vincentyDirect } from './coordinates.cjs';
7
7
  export { c as clampUnit, e as ecefToGeodetic, g as geodeticToEcef, a as getAer, b as getEnuDifference } from './ecef-CF0uAysr.cjs';
8
8
  export { BiasRecord, BiasSinex, CompactEpoch, CrxField, DiffState, EMPTY_WARNINGS, Nav4Input, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, WriteCrxOptions, biasMetres, createGzipLineSink, crxDecompress, crxRepair, findSatBias, fmtD, fmtF, hdrLine, padL, padR, parseBiasSinex, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob } from './rinex.cjs';
9
9
  export { E as EpochSummary, P as ParseOptions, R as RinexHeader, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.cjs';
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { D as DAYS_MJD2000_MJD, a as DAYS_MJD_JULIAN, H as HourCode, M as MILLIS
3
3
  export { WGS84_ECCENTRICITY_SQUARED, WGS84_FLATTENING, WGS84_SEMI_MAJOR_AXIS, WGS84_SEMI_MINOR_AXIS } from './constants.js';
4
4
  export { A as ARC_GAP_FACTOR, B as BAND_LABELS, a as BDS_SATELLITES, b as BdsSatellite, C as C_LIGHT, D as DEFAULT_ELEV_MASK_DEG, c as DUAL_FREQ_PAIRS, F as FREQ, G as GLO_CHANNEL_FALLBACK, d as GLO_F1_BASE, e as GLO_F1_STEP, f as GLO_F2_BASE, g as GLO_F2_STEP, h as GLO_F3, O as OMEGA_E, i as OnSlipDetected, S as SYSTEM_NAMES, j as SYS_SHORT, k as buildGloChannelMap, l as buildObsIndices, m as formatUTCTime, n as getFreq, o as gloFreq } from './gnss-DhnEr0Dy.js';
5
5
  export { Scale, getBdsTime, getDateFromBdsTime, getDateFromDayOfWeek, getDateFromDayOfYear, getDateFromGalTime, getDateFromGloN, getDateFromGpsData, getDateFromGpsTime, getDateFromHourCode, getDateFromJulianDate, getDateFromMJD, getDateFromMJD2000, getDateFromRINEX, getDateFromTai, getDateFromTimeOfDay, getDateFromTt, getDateFromUnixTime, getDateFromUtc, getDayOfWeek, getDayOfYear, getGalTime, getGloN4, getGloNA, getGpsLeap, getGpsTime, getHourCode, getHoursFromTimeDifference, getJulianDate, getLeap, getMJD, getMJD2000, getMinutesFromTimeDifference, getNtpTime, getRINEX, getSecondsFromTimeDifference, getTaiDate, getTimeDifference, getTimeDifferenceFromDays, getTimeDifferenceFromHours, getTimeDifferenceFromMinutes, getTimeDifferenceFromObject, getTimeDifferenceFromSeconds, getTimeOfDay, getTimeOfWeek, getTotalDaysFromTimeDifference, getTtDate, getUnixTime, getUtcDate, getWeekNumber, getWeekOfYear } from './time.js';
6
- export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, rad2deg, rhumbLine, vincenty } from './coordinates.js';
6
+ export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, meridianArc, rad2deg, rhumbLine, vincenty, vincentyDirect } from './coordinates.js';
7
7
  export { c as clampUnit, e as ecefToGeodetic, g as geodeticToEcef, a as getAer, b as getEnuDifference } from './ecef-CF0uAysr.js';
8
8
  export { BiasRecord, BiasSinex, CompactEpoch, CrxField, DiffState, EMPTY_WARNINGS, Nav4Input, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, WriteCrxOptions, biasMetres, createGzipLineSink, crxDecompress, crxRepair, findSatBias, fmtD, fmtF, hdrLine, padL, padR, parseBiasSinex, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob } from './rinex.js';
9
9
  export { E as EpochSummary, P as ParseOptions, R as RinexHeader, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.js';
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,
@@ -280,10 +280,12 @@ import {
280
280
  geodeticToUtm,
281
281
  greatCircleMidpoint,
282
282
  horizonDistance,
283
+ meridianArc,
283
284
  rad2deg,
284
285
  rhumbLine,
285
- vincenty
286
- } from "./chunk-MIIM4LDY.js";
286
+ vincenty,
287
+ vincentyDirect
288
+ } from "./chunk-CVCTRIWU.js";
287
289
  import {
288
290
  clampUnit,
289
291
  ecefToGeodetic,
@@ -638,6 +640,7 @@ export {
638
640
  lambdaSearch,
639
641
  looksLikeRtcm2,
640
642
  maskRadForAzimuth,
643
+ meridianArc,
641
644
  moonEcef,
642
645
  navTimesFromEph,
643
646
  niellMapping,
@@ -732,6 +735,7 @@ export {
732
735
  updateStreamStats,
733
736
  verifyChecksum,
734
737
  vincenty,
738
+ vincentyDirect,
735
739
  wlWavelength,
736
740
  writeCrx,
737
741
  writeModernObsBlob,