gnss-js 1.51.0 → 1.53.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/{chunk-E3QCNJL4.js → chunk-U32IU7RO.js} +90 -8
- package/dist/{chunk-DE5IUULR.js → chunk-ZPNMIUNZ.js} +57 -4
- package/dist/index.cjs +174 -37
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -2
- package/dist/positioning.cjs +91 -8
- package/dist/positioning.d.cts +30 -1
- package/dist/positioning.d.ts +30 -1
- package/dist/positioning.js +3 -1
- package/dist/sbf.cjs +70 -16
- package/dist/sbf.d.cts +30 -1
- package/dist/sbf.d.ts +30 -1
- package/dist/sbf.js +3 -1
- package/package.json +1 -1
|
@@ -3048,6 +3048,65 @@ function sagnac2(pos, travelTimeS) {
|
|
|
3048
3048
|
const s = Math.sin(theta);
|
|
3049
3049
|
return [pos.x * c + pos.y * s, -pos.x * s + pos.y * c, pos.z];
|
|
3050
3050
|
}
|
|
3051
|
+
function sbasAirTropoVar(elRad) {
|
|
3052
|
+
const elDeg = elRad * 180 / Math.PI;
|
|
3053
|
+
const sigMp = 0.13 + 0.53 * Math.exp(-elDeg / 10);
|
|
3054
|
+
const sigNoise = 0.15 + 0.43 * Math.exp(-elDeg / 6.9);
|
|
3055
|
+
const m = 1.001 / Math.sqrt(2001e-6 + Math.sin(elRad) * Math.sin(elRad));
|
|
3056
|
+
const sigTropo = 0.12 * m;
|
|
3057
|
+
return sigMp * sigMp + sigNoise * sigNoise + sigTropo * sigTropo;
|
|
3058
|
+
}
|
|
3059
|
+
function invertMatrix(A) {
|
|
3060
|
+
const n = A.length;
|
|
3061
|
+
const M = A.map((row, i) => [
|
|
3062
|
+
...row,
|
|
3063
|
+
...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
|
|
3064
|
+
]);
|
|
3065
|
+
for (let col = 0; col < n; col++) {
|
|
3066
|
+
let piv = col;
|
|
3067
|
+
for (let r = col + 1; r < n; r++)
|
|
3068
|
+
if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
|
|
3069
|
+
if (Math.abs(M[piv][col]) < 1e-12) return null;
|
|
3070
|
+
[M[col], M[piv]] = [M[piv], M[col]];
|
|
3071
|
+
const d = M[col][col];
|
|
3072
|
+
for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
|
|
3073
|
+
for (let r = 0; r < n; r++) {
|
|
3074
|
+
if (r === col) continue;
|
|
3075
|
+
const f = M[r][col];
|
|
3076
|
+
for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
return M.map((row) => row.slice(n));
|
|
3080
|
+
}
|
|
3081
|
+
function sbasProtectionLevels(sats, opts = {}) {
|
|
3082
|
+
const kH = opts.kH ?? 6;
|
|
3083
|
+
const kV = opts.kV ?? 5.33;
|
|
3084
|
+
if (sats.length < 4) return null;
|
|
3085
|
+
const N = [0, 1, 2, 3].map(() => [0, 0, 0, 0]);
|
|
3086
|
+
for (const s of sats) {
|
|
3087
|
+
if (!(s.variance > 0)) continue;
|
|
3088
|
+
const ce = Math.cos(s.elRad);
|
|
3089
|
+
const g = [
|
|
3090
|
+
ce * Math.sin(s.azRad),
|
|
3091
|
+
ce * Math.cos(s.azRad),
|
|
3092
|
+
Math.sin(s.elRad),
|
|
3093
|
+
1
|
|
3094
|
+
];
|
|
3095
|
+
const w = 1 / s.variance;
|
|
3096
|
+
for (let i = 0; i < 4; i++)
|
|
3097
|
+
for (let j = 0; j < 4; j++) N[i][j] += w * g[i] * g[j];
|
|
3098
|
+
}
|
|
3099
|
+
const cov = invertMatrix(N);
|
|
3100
|
+
if (!cov) return null;
|
|
3101
|
+
const dE2 = cov[0][0];
|
|
3102
|
+
const dN2 = cov[1][1];
|
|
3103
|
+
const dEN = cov[0][1];
|
|
3104
|
+
const dU = Math.sqrt(Math.max(cov[2][2], 0));
|
|
3105
|
+
const dMajor = Math.sqrt(
|
|
3106
|
+
(dE2 + dN2) / 2 + Math.sqrt(((dE2 - dN2) / 2) ** 2 + dEN * dEN)
|
|
3107
|
+
);
|
|
3108
|
+
return { hpl: kH * dMajor, vpl: kV * dU, dMajor, dU };
|
|
3109
|
+
}
|
|
3051
3110
|
function solveLinear(A, b) {
|
|
3052
3111
|
const n = b.length;
|
|
3053
3112
|
const M = A.map((row, i) => [...row, b[i]]);
|
|
@@ -3104,7 +3163,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3104
3163
|
const sysRows = new Map(systems.map((s) => [s, 0]));
|
|
3105
3164
|
for (const k of Object.keys(residuals2)) delete residuals2[k];
|
|
3106
3165
|
const positionSaneIter = x9 * x9 + y2 * y2 + z2 * z2 > 1e12;
|
|
3107
|
-
const [
|
|
3166
|
+
const [rxLat2, rxLon2, rxHgt2] = positionSaneIter ? ecefToGeodetic(x9, y2, z2) : [0, 0, 0];
|
|
3108
3167
|
for (const prn of prns) {
|
|
3109
3168
|
const psr = pseudoranges.get(prn);
|
|
3110
3169
|
const eph = ephemerides.get(prn);
|
|
@@ -3143,7 +3202,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3143
3202
|
const sinEl = Math.max(Math.sin(elev), 0.1);
|
|
3144
3203
|
weight = sinEl * sinEl;
|
|
3145
3204
|
}
|
|
3146
|
-
const tropo = troposphere && positionSane ? tropoDelay2(elev,
|
|
3205
|
+
const tropo = troposphere && positionSane ? tropoDelay2(elev, rxLat2, rxHgt2) : 0;
|
|
3147
3206
|
let ionoM = 0;
|
|
3148
3207
|
if ((sbas || gim || iono) && positionSane) {
|
|
3149
3208
|
let l1M = null;
|
|
@@ -3151,18 +3210,18 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3151
3210
|
const si = sbas.ionoDelay(
|
|
3152
3211
|
gpsWeek,
|
|
3153
3212
|
gpsTow,
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3213
|
+
rxLat2,
|
|
3214
|
+
rxLon2,
|
|
3215
|
+
rxHgt2,
|
|
3157
3216
|
azim,
|
|
3158
3217
|
elev
|
|
3159
3218
|
);
|
|
3160
3219
|
if (si) l1M = si.delayM;
|
|
3161
3220
|
}
|
|
3162
3221
|
if (l1M === null && gim)
|
|
3163
|
-
l1M = gimSlantIonoDelayL1(gim,
|
|
3222
|
+
l1M = gimSlantIonoDelayL1(gim, rxLat2, rxLon2, azim, elev, timeMs);
|
|
3164
3223
|
if (l1M === null && iono) {
|
|
3165
|
-
l1M = C_LIGHT * klobucharDelay(iono,
|
|
3224
|
+
l1M = C_LIGHT * klobucharDelay(iono, rxLat2, rxLon2, azim, elev, gpsTow);
|
|
3166
3225
|
}
|
|
3167
3226
|
if (l1M !== null) {
|
|
3168
3227
|
const f = PRIMARY_FREQ_HZ[sys] ?? F_L12;
|
|
@@ -3235,6 +3294,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3235
3294
|
const { x, y, z, clock, residuals, iterations, converged } = inner;
|
|
3236
3295
|
const used = [];
|
|
3237
3296
|
const azels = [];
|
|
3297
|
+
const [rxLat, rxLon, rxHgt] = ecefToGeodetic(x, y, z);
|
|
3298
|
+
const sbasRows = [];
|
|
3238
3299
|
for (const prn of candidates) {
|
|
3239
3300
|
if (!(prn in residuals)) continue;
|
|
3240
3301
|
const eph = ephemerides.get(prn);
|
|
@@ -3244,8 +3305,27 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3244
3305
|
if (azel.el >= elevationMaskDeg * Math.PI / 180) {
|
|
3245
3306
|
used.push(prn);
|
|
3246
3307
|
azels.push(azel);
|
|
3308
|
+
if (sbas) {
|
|
3309
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow);
|
|
3310
|
+
const si = sbas.ionoDelay(
|
|
3311
|
+
gpsWeek,
|
|
3312
|
+
gpsTow,
|
|
3313
|
+
rxLat,
|
|
3314
|
+
rxLon,
|
|
3315
|
+
rxHgt,
|
|
3316
|
+
azel.az,
|
|
3317
|
+
azel.el
|
|
3318
|
+
);
|
|
3319
|
+
if (sc && si)
|
|
3320
|
+
sbasRows.push({
|
|
3321
|
+
azRad: azel.az,
|
|
3322
|
+
elRad: azel.el,
|
|
3323
|
+
variance: sc.varM2 + si.varM2 + sbasAirTropoVar(azel.el)
|
|
3324
|
+
});
|
|
3325
|
+
}
|
|
3247
3326
|
}
|
|
3248
3327
|
}
|
|
3328
|
+
const pl = sbas ? sbasProtectionLevels(sbasRows) : null;
|
|
3249
3329
|
return {
|
|
3250
3330
|
position: [x, y, z],
|
|
3251
3331
|
clockBias: Object.fromEntries(clock),
|
|
@@ -3254,7 +3334,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3254
3334
|
residuals,
|
|
3255
3335
|
dop: computeDop(azels),
|
|
3256
3336
|
iterations,
|
|
3257
|
-
converged
|
|
3337
|
+
converged,
|
|
3338
|
+
...pl ? { hpl: pl.hpl, vpl: pl.vpl, sbasSats: sbasRows.length } : sbas ? { sbasSats: sbasRows.length } : {}
|
|
3258
3339
|
};
|
|
3259
3340
|
}
|
|
3260
3341
|
|
|
@@ -3288,5 +3369,6 @@ export {
|
|
|
3288
3369
|
estimateNetworkFcbs,
|
|
3289
3370
|
satClockCorrection,
|
|
3290
3371
|
ionoFree,
|
|
3372
|
+
sbasProtectionLevels,
|
|
3291
3373
|
solveSpp
|
|
3292
3374
|
};
|
|
@@ -486,6 +486,58 @@ function parseSbfReceiverSetup(data) {
|
|
|
486
486
|
return out;
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
+
// src/sbf/pvt.ts
|
|
490
|
+
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
491
|
+
var RAD2DEG = 180 / Math.PI;
|
|
492
|
+
var DNU_F8 = -2e10;
|
|
493
|
+
var MODE_LABELS = {
|
|
494
|
+
0: "no-pvt",
|
|
495
|
+
1: "standalone",
|
|
496
|
+
2: "differential",
|
|
497
|
+
3: "fixed-location",
|
|
498
|
+
4: "rtk-fixed",
|
|
499
|
+
5: "rtk-float",
|
|
500
|
+
6: "sbas-aided",
|
|
501
|
+
7: "moving-base-rtk-fixed",
|
|
502
|
+
8: "moving-base-rtk-float",
|
|
503
|
+
10: "ppp"
|
|
504
|
+
};
|
|
505
|
+
function parseSbfPvt(data) {
|
|
506
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
507
|
+
const records = [];
|
|
508
|
+
let messages = 0;
|
|
509
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
510
|
+
if (id !== 4007 || len < 96) return;
|
|
511
|
+
messages++;
|
|
512
|
+
const tow = view.getUint32(b + 8, true);
|
|
513
|
+
const wnc = view.getUint16(b + 12, true);
|
|
514
|
+
if (tow === 4294967295 || wnc === 65535) return;
|
|
515
|
+
const modeByte = view.getUint8(b + 14);
|
|
516
|
+
const modeType = modeByte & 15;
|
|
517
|
+
const lat = view.getFloat64(b + 16, true);
|
|
518
|
+
const lon = view.getFloat64(b + 24, true);
|
|
519
|
+
const hgt = view.getFloat64(b + 32, true);
|
|
520
|
+
const nrSV = view.getUint8(b + 74);
|
|
521
|
+
const hAcc = view.getUint16(b + 90, true);
|
|
522
|
+
const vAcc = view.getUint16(b + 92, true);
|
|
523
|
+
const hasFix = lat > DNU_F8 && lon > DNU_F8 && hgt > DNU_F8;
|
|
524
|
+
records.push({
|
|
525
|
+
timeMs: GPS_EPOCH_MS2 + wnc * 6048e5 + tow,
|
|
526
|
+
week: wnc,
|
|
527
|
+
tow: tow / 1e3,
|
|
528
|
+
modeType,
|
|
529
|
+
mode: MODE_LABELS[modeType] ?? `mode-${modeType}`,
|
|
530
|
+
latDeg: hasFix ? lat * RAD2DEG : null,
|
|
531
|
+
lonDeg: hasFix ? lon * RAD2DEG : null,
|
|
532
|
+
heightM: hasFix ? hgt : null,
|
|
533
|
+
nrSV: nrSV === 255 ? null : nrSV,
|
|
534
|
+
hAccuracyM: hAcc === 65535 ? null : hAcc / 100,
|
|
535
|
+
vAccuracyM: vAcc === 65535 ? null : vAcc / 100
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
return { records, messages };
|
|
539
|
+
}
|
|
540
|
+
|
|
489
541
|
// src/sbf/rawnav-gps.ts
|
|
490
542
|
var WNC_DNU = 65535;
|
|
491
543
|
function extractSbfLnavSubframe(view, b) {
|
|
@@ -621,7 +673,7 @@ function isFnavDummy(page) {
|
|
|
621
673
|
}
|
|
622
674
|
|
|
623
675
|
// src/sbf/rawnav-bds.ts
|
|
624
|
-
var
|
|
676
|
+
var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
|
|
625
677
|
var MS_PER_WEEK2 = 7 * 86400 * 1e3;
|
|
626
678
|
var TOW_DNU = 4294967295;
|
|
627
679
|
var WNC_DNU2 = 65535;
|
|
@@ -682,7 +734,7 @@ function feedGloBlock(view, b, assembler) {
|
|
|
682
734
|
const eph = assembler.push(
|
|
683
735
|
prn,
|
|
684
736
|
str2,
|
|
685
|
-
new Date(
|
|
737
|
+
new Date(GPS_EPOCH_MS3 + wnc * MS_PER_WEEK2 + towMs),
|
|
686
738
|
view.getUint8(b + 18) - 8
|
|
687
739
|
// FreqNr, offset by 8
|
|
688
740
|
);
|
|
@@ -1432,7 +1484,7 @@ function parseSbfHas(data) {
|
|
|
1432
1484
|
|
|
1433
1485
|
// src/sbf/index.ts
|
|
1434
1486
|
var CLIGHT = 299792458;
|
|
1435
|
-
var
|
|
1487
|
+
var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
|
|
1436
1488
|
var MS_PER_WEEK3 = 7 * 864e5;
|
|
1437
1489
|
var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
|
|
1438
1490
|
var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
|
|
@@ -2086,7 +2138,7 @@ function parseSbfMeas(data) {
|
|
|
2086
2138
|
const tow = view.getUint32(i + 8, true);
|
|
2087
2139
|
const wnc = view.getUint16(i + 12, true);
|
|
2088
2140
|
if (tow !== 4294967295 && wnc !== 65535) {
|
|
2089
|
-
ensureEpoch(
|
|
2141
|
+
ensureEpoch(GPS_EPOCH_MS4 + wnc * MS_PER_WEEK3 + tow);
|
|
2090
2142
|
if (id === 4109) decodeMeas3Ranges(i, tow);
|
|
2091
2143
|
else if (id === 4110) decodeMeas3CN0(i, len);
|
|
2092
2144
|
else if (id === 4111) decodeMeas3Doppler(i, len);
|
|
@@ -2105,6 +2157,7 @@ export {
|
|
|
2105
2157
|
parseSbfAlmanac,
|
|
2106
2158
|
parseSbfIonoUtc,
|
|
2107
2159
|
parseSbfReceiverSetup,
|
|
2160
|
+
parseSbfPvt,
|
|
2108
2161
|
parseSbfGpsNav,
|
|
2109
2162
|
parseSbfCnav,
|
|
2110
2163
|
parseSbfGalNav,
|