gnss-js 1.50.0 → 1.52.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-XFPDTGSV.js → chunk-DE5IUULR.js} +6 -5
- package/dist/{chunk-VYC2FRDV.js → chunk-U32IU7RO.js} +114 -9
- package/dist/{chunk-2IM5JYKP.js → chunk-ZS5RLFWI.js} +2 -1
- package/dist/index.cjs +123 -15
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -3
- package/dist/novatel.cjs +2 -1
- package/dist/novatel.d.cts +7 -1
- package/dist/novatel.d.ts +7 -1
- package/dist/novatel.js +1 -1
- package/dist/positioning.cjs +115 -9
- package/dist/positioning.d.cts +52 -1
- package/dist/positioning.d.ts +52 -1
- package/dist/positioning.js +3 -1
- package/dist/sbf.cjs +6 -5
- package/dist/sbf.d.cts +9 -3
- package/dist/sbf.d.ts +9 -3
- package/dist/sbf.js +1 -1
- package/package.json +1 -1
|
@@ -705,7 +705,7 @@ function parseSbfGloNav(data) {
|
|
|
705
705
|
}
|
|
706
706
|
|
|
707
707
|
// src/sbf/rawnav-sbas.ts
|
|
708
|
-
function feedGeoBlock(view, b) {
|
|
708
|
+
function feedGeoBlock(view, b, onSbasMessage) {
|
|
709
709
|
const tow = view.getUint32(b + 8, true);
|
|
710
710
|
const wnc = view.getUint16(b + 12, true);
|
|
711
711
|
const svid = view.getUint8(b + 14);
|
|
@@ -721,10 +721,11 @@ function feedGeoBlock(view, b) {
|
|
|
721
721
|
}
|
|
722
722
|
if (!sbasCrcOk(msg)) return { badCrc: true };
|
|
723
723
|
const prn = Number(prnStr.slice(1)) + 100;
|
|
724
|
+
onSbasMessage?.(msg, prn, wnc, tow / 1e3);
|
|
724
725
|
const eph = decodeSbasGeoNav(msg, prn, wnc, tow / 1e3);
|
|
725
726
|
return eph ? { eph } : {};
|
|
726
727
|
}
|
|
727
|
-
function parseSbfGeoNav(data) {
|
|
728
|
+
function parseSbfGeoNav(data, opts = {}) {
|
|
728
729
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
729
730
|
const ephemerides = [];
|
|
730
731
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -733,7 +734,7 @@ function parseSbfGeoNav(data) {
|
|
|
733
734
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
734
735
|
if (id !== 4020 || len < 52) return;
|
|
735
736
|
messages++;
|
|
736
|
-
const r = feedGeoBlock(view, b);
|
|
737
|
+
const r = feedGeoBlock(view, b, opts.onSbasMessage);
|
|
737
738
|
if (r.eph) {
|
|
738
739
|
const key = `${r.eph.prn}|${r.eph.tocDate.getTime()}`;
|
|
739
740
|
if (!seen.has(key)) {
|
|
@@ -753,7 +754,7 @@ function ephKey(e) {
|
|
|
753
754
|
const source = "source" in e ? e.source : "";
|
|
754
755
|
return `${e.prn}|${e.tocDate.getTime()}|${source}`;
|
|
755
756
|
}
|
|
756
|
-
function decodeSbfNavigation(data) {
|
|
757
|
+
function decodeSbfNavigation(data, opts = {}) {
|
|
757
758
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
758
759
|
const ephemerides = [];
|
|
759
760
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -874,7 +875,7 @@ function decodeSbfNavigation(data) {
|
|
|
874
875
|
case 4020: {
|
|
875
876
|
if (len < 52) return;
|
|
876
877
|
counts.sbasRaw++;
|
|
877
|
-
const r = feedGeoBlock(view, b);
|
|
878
|
+
const r = feedGeoBlock(view, b, opts.onSbasMessage);
|
|
878
879
|
if (r.eph) addEph(r.eph);
|
|
879
880
|
else if (r.badCrc) counts.badFrames++;
|
|
880
881
|
return;
|
|
@@ -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]]);
|
|
@@ -3074,9 +3133,11 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3074
3133
|
convergenceM = 1e-4,
|
|
3075
3134
|
tgd = true,
|
|
3076
3135
|
iono,
|
|
3077
|
-
gim
|
|
3136
|
+
gim,
|
|
3137
|
+
sbas
|
|
3078
3138
|
} = opts;
|
|
3079
3139
|
const gpsTow = (timeMs - GPS_EPOCH_MS_SPP) / 1e3 % 604800;
|
|
3140
|
+
const gpsWeek = Math.floor((timeMs - GPS_EPOCH_MS_SPP) / 1e3 / 604800);
|
|
3080
3141
|
const all = [...pseudoranges.keys()].filter((p) => ephemerides.has(p));
|
|
3081
3142
|
const minSats = (list) => 3 + new Set(list.map((p) => p[0])).size;
|
|
3082
3143
|
if (all.length < minSats(all)) return null;
|
|
@@ -3102,7 +3163,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3102
3163
|
const sysRows = new Map(systems.map((s) => [s, 0]));
|
|
3103
3164
|
for (const k of Object.keys(residuals2)) delete residuals2[k];
|
|
3104
3165
|
const positionSaneIter = x9 * x9 + y2 * y2 + z2 * z2 > 1e12;
|
|
3105
|
-
const [
|
|
3166
|
+
const [rxLat2, rxLon2, rxHgt2] = positionSaneIter ? ecefToGeodetic(x9, y2, z2) : [0, 0, 0];
|
|
3106
3167
|
for (const prn of prns) {
|
|
3107
3168
|
const psr = pseudoranges.get(prn);
|
|
3108
3169
|
const eph = ephemerides.get(prn);
|
|
@@ -3113,7 +3174,16 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3113
3174
|
const sat = computeSatPosition(eph, tTx - dtsClock * 1e3);
|
|
3114
3175
|
if (!Number.isFinite(sat.x)) continue;
|
|
3115
3176
|
const isKepler = eph.system !== "R" && eph.system !== "S";
|
|
3116
|
-
|
|
3177
|
+
let dts = dtsClock - (tgd && isKepler ? eph.tgd : 0);
|
|
3178
|
+
if (sbas) {
|
|
3179
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow);
|
|
3180
|
+
if (sc) {
|
|
3181
|
+
sat.x += sc.dPos[0];
|
|
3182
|
+
sat.y += sc.dPos[1];
|
|
3183
|
+
sat.z += sc.dPos[2];
|
|
3184
|
+
dts += sc.dClkS;
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3117
3187
|
const travel = Math.hypot(sat.x - x9, sat.y - y2, sat.z - z2) / C_LIGHT;
|
|
3118
3188
|
const [sx, sy, sz] = sagnac2(sat, travel);
|
|
3119
3189
|
const rho = Math.hypot(sx - x9, sy - y2, sz - z2);
|
|
@@ -3132,14 +3202,26 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3132
3202
|
const sinEl = Math.max(Math.sin(elev), 0.1);
|
|
3133
3203
|
weight = sinEl * sinEl;
|
|
3134
3204
|
}
|
|
3135
|
-
const tropo = troposphere && positionSane ? tropoDelay2(elev,
|
|
3205
|
+
const tropo = troposphere && positionSane ? tropoDelay2(elev, rxLat2, rxHgt2) : 0;
|
|
3136
3206
|
let ionoM = 0;
|
|
3137
|
-
if ((gim || iono) && positionSane) {
|
|
3207
|
+
if ((sbas || gim || iono) && positionSane) {
|
|
3138
3208
|
let l1M = null;
|
|
3139
|
-
if (
|
|
3140
|
-
|
|
3209
|
+
if (sbas) {
|
|
3210
|
+
const si = sbas.ionoDelay(
|
|
3211
|
+
gpsWeek,
|
|
3212
|
+
gpsTow,
|
|
3213
|
+
rxLat2,
|
|
3214
|
+
rxLon2,
|
|
3215
|
+
rxHgt2,
|
|
3216
|
+
azim,
|
|
3217
|
+
elev
|
|
3218
|
+
);
|
|
3219
|
+
if (si) l1M = si.delayM;
|
|
3220
|
+
}
|
|
3221
|
+
if (l1M === null && gim)
|
|
3222
|
+
l1M = gimSlantIonoDelayL1(gim, rxLat2, rxLon2, azim, elev, timeMs);
|
|
3141
3223
|
if (l1M === null && iono) {
|
|
3142
|
-
l1M = C_LIGHT * klobucharDelay(iono,
|
|
3224
|
+
l1M = C_LIGHT * klobucharDelay(iono, rxLat2, rxLon2, azim, elev, gpsTow);
|
|
3143
3225
|
}
|
|
3144
3226
|
if (l1M !== null) {
|
|
3145
3227
|
const f = PRIMARY_FREQ_HZ[sys] ?? F_L12;
|
|
@@ -3212,6 +3294,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3212
3294
|
const { x, y, z, clock, residuals, iterations, converged } = inner;
|
|
3213
3295
|
const used = [];
|
|
3214
3296
|
const azels = [];
|
|
3297
|
+
const [rxLat, rxLon, rxHgt] = ecefToGeodetic(x, y, z);
|
|
3298
|
+
const sbasRows = [];
|
|
3215
3299
|
for (const prn of candidates) {
|
|
3216
3300
|
if (!(prn in residuals)) continue;
|
|
3217
3301
|
const eph = ephemerides.get(prn);
|
|
@@ -3221,8 +3305,27 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3221
3305
|
if (azel.el >= elevationMaskDeg * Math.PI / 180) {
|
|
3222
3306
|
used.push(prn);
|
|
3223
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
|
+
}
|
|
3224
3326
|
}
|
|
3225
3327
|
}
|
|
3328
|
+
const pl = sbas ? sbasProtectionLevels(sbasRows) : null;
|
|
3226
3329
|
return {
|
|
3227
3330
|
position: [x, y, z],
|
|
3228
3331
|
clockBias: Object.fromEntries(clock),
|
|
@@ -3231,7 +3334,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3231
3334
|
residuals,
|
|
3232
3335
|
dop: computeDop(azels),
|
|
3233
3336
|
iterations,
|
|
3234
|
-
converged
|
|
3337
|
+
converged,
|
|
3338
|
+
...pl ? { hpl: pl.hpl, vpl: pl.vpl, sbasSats: sbasRows.length } : sbas ? { sbasSats: sbasRows.length } : {}
|
|
3235
3339
|
};
|
|
3236
3340
|
}
|
|
3237
3341
|
|
|
@@ -3265,5 +3369,6 @@ export {
|
|
|
3265
3369
|
estimateNetworkFcbs,
|
|
3266
3370
|
satClockCorrection,
|
|
3267
3371
|
ionoFree,
|
|
3372
|
+
sbasProtectionLevels,
|
|
3268
3373
|
solveSpp
|
|
3269
3374
|
};
|
|
@@ -261,7 +261,7 @@ function decodeGpsQzssEphemeris(view, p, sys) {
|
|
|
261
261
|
tgd: view.getFloat64(p + 172, true)
|
|
262
262
|
};
|
|
263
263
|
}
|
|
264
|
-
function parseNovatelNav(data) {
|
|
264
|
+
function parseNovatelNav(data, opts = {}) {
|
|
265
265
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
266
266
|
const stats = { badCrc: 0 };
|
|
267
267
|
const ephemerides = [];
|
|
@@ -334,6 +334,7 @@ function parseNovatelNav(data) {
|
|
|
334
334
|
const prn = view.getUint32(p + 4, true);
|
|
335
335
|
const msg = new Uint8Array(32);
|
|
336
336
|
msg.set(data.subarray(p + 12, p + 41));
|
|
337
|
+
opts.onSbasMessage?.(msg, prn, frame.week, frame.towMs / 1e3);
|
|
337
338
|
const eph = decodeSbasGeoNav(msg, prn, frame.week, frame.towMs / 1e3);
|
|
338
339
|
if (!eph) continue;
|
|
339
340
|
const prev = lastGlo.get(eph.prn);
|
package/dist/index.cjs
CHANGED
|
@@ -294,6 +294,7 @@ __export(src_exports, {
|
|
|
294
294
|
rhumbLine: () => rhumbLine,
|
|
295
295
|
rtcm3Constellation: () => rtcm3Constellation,
|
|
296
296
|
satClockCorrection: () => satClockCorrection,
|
|
297
|
+
sbasProtectionLevels: () => sbasProtectionLevels,
|
|
297
298
|
sbfCrc16: () => crc16,
|
|
298
299
|
scanSbfFrames: () => scanSbfFrames,
|
|
299
300
|
selectEphemeris: () => selectEphemeris,
|
|
@@ -7727,7 +7728,7 @@ function parseSbfGloNav(data) {
|
|
|
7727
7728
|
}
|
|
7728
7729
|
|
|
7729
7730
|
// src/sbf/rawnav-sbas.ts
|
|
7730
|
-
function feedGeoBlock(view, b) {
|
|
7731
|
+
function feedGeoBlock(view, b, onSbasMessage) {
|
|
7731
7732
|
const tow = view.getUint32(b + 8, true);
|
|
7732
7733
|
const wnc = view.getUint16(b + 12, true);
|
|
7733
7734
|
const svid = view.getUint8(b + 14);
|
|
@@ -7743,10 +7744,11 @@ function feedGeoBlock(view, b) {
|
|
|
7743
7744
|
}
|
|
7744
7745
|
if (!sbasCrcOk(msg)) return { badCrc: true };
|
|
7745
7746
|
const prn = Number(prnStr.slice(1)) + 100;
|
|
7747
|
+
onSbasMessage?.(msg, prn, wnc, tow / 1e3);
|
|
7746
7748
|
const eph = decodeSbasGeoNav(msg, prn, wnc, tow / 1e3);
|
|
7747
7749
|
return eph ? { eph } : {};
|
|
7748
7750
|
}
|
|
7749
|
-
function parseSbfGeoNav(data) {
|
|
7751
|
+
function parseSbfGeoNav(data, opts = {}) {
|
|
7750
7752
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7751
7753
|
const ephemerides = [];
|
|
7752
7754
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7755,7 +7757,7 @@ function parseSbfGeoNav(data) {
|
|
|
7755
7757
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
7756
7758
|
if (id !== 4020 || len < 52) return;
|
|
7757
7759
|
messages++;
|
|
7758
|
-
const r = feedGeoBlock(view, b);
|
|
7760
|
+
const r = feedGeoBlock(view, b, opts.onSbasMessage);
|
|
7759
7761
|
if (r.eph) {
|
|
7760
7762
|
const key = `${r.eph.prn}|${r.eph.tocDate.getTime()}`;
|
|
7761
7763
|
if (!seen.has(key)) {
|
|
@@ -7775,7 +7777,7 @@ function ephKey(e) {
|
|
|
7775
7777
|
const source = "source" in e ? e.source : "";
|
|
7776
7778
|
return `${e.prn}|${e.tocDate.getTime()}|${source}`;
|
|
7777
7779
|
}
|
|
7778
|
-
function decodeSbfNavigation(data) {
|
|
7780
|
+
function decodeSbfNavigation(data, opts = {}) {
|
|
7779
7781
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7780
7782
|
const ephemerides = [];
|
|
7781
7783
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7896,7 +7898,7 @@ function decodeSbfNavigation(data) {
|
|
|
7896
7898
|
case 4020: {
|
|
7897
7899
|
if (len < 52) return;
|
|
7898
7900
|
counts.sbasRaw++;
|
|
7899
|
-
const r = feedGeoBlock(view, b);
|
|
7901
|
+
const r = feedGeoBlock(view, b, opts.onSbasMessage);
|
|
7900
7902
|
if (r.eph) addEph(r.eph);
|
|
7901
7903
|
else if (r.badCrc) counts.badFrames++;
|
|
7902
7904
|
return;
|
|
@@ -9368,7 +9370,7 @@ function decodeGpsQzssEphemeris(view, p, sys) {
|
|
|
9368
9370
|
tgd: view.getFloat64(p + 172, true)
|
|
9369
9371
|
};
|
|
9370
9372
|
}
|
|
9371
|
-
function parseNovatelNav(data) {
|
|
9373
|
+
function parseNovatelNav(data, opts = {}) {
|
|
9372
9374
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
9373
9375
|
const stats = { badCrc: 0 };
|
|
9374
9376
|
const ephemerides = [];
|
|
@@ -9441,6 +9443,7 @@ function parseNovatelNav(data) {
|
|
|
9441
9443
|
const prn = view.getUint32(p + 4, true);
|
|
9442
9444
|
const msg = new Uint8Array(32);
|
|
9443
9445
|
msg.set(data.subarray(p + 12, p + 41));
|
|
9446
|
+
opts.onSbasMessage?.(msg, prn, frame.week, frame.towMs / 1e3);
|
|
9444
9447
|
const eph = decodeSbasGeoNav(msg, prn, frame.week, frame.towMs / 1e3);
|
|
9445
9448
|
if (!eph) continue;
|
|
9446
9449
|
const prev = lastGlo.get(eph.prn);
|
|
@@ -14976,6 +14979,65 @@ function sagnac2(pos, travelTimeS) {
|
|
|
14976
14979
|
const s = Math.sin(theta);
|
|
14977
14980
|
return [pos.x * c + pos.y * s, -pos.x * s + pos.y * c, pos.z];
|
|
14978
14981
|
}
|
|
14982
|
+
function sbasAirTropoVar(elRad) {
|
|
14983
|
+
const elDeg = elRad * 180 / Math.PI;
|
|
14984
|
+
const sigMp = 0.13 + 0.53 * Math.exp(-elDeg / 10);
|
|
14985
|
+
const sigNoise = 0.15 + 0.43 * Math.exp(-elDeg / 6.9);
|
|
14986
|
+
const m = 1.001 / Math.sqrt(2001e-6 + Math.sin(elRad) * Math.sin(elRad));
|
|
14987
|
+
const sigTropo = 0.12 * m;
|
|
14988
|
+
return sigMp * sigMp + sigNoise * sigNoise + sigTropo * sigTropo;
|
|
14989
|
+
}
|
|
14990
|
+
function invertMatrix(A) {
|
|
14991
|
+
const n = A.length;
|
|
14992
|
+
const M = A.map((row, i) => [
|
|
14993
|
+
...row,
|
|
14994
|
+
...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
|
|
14995
|
+
]);
|
|
14996
|
+
for (let col = 0; col < n; col++) {
|
|
14997
|
+
let piv = col;
|
|
14998
|
+
for (let r = col + 1; r < n; r++)
|
|
14999
|
+
if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
|
|
15000
|
+
if (Math.abs(M[piv][col]) < 1e-12) return null;
|
|
15001
|
+
[M[col], M[piv]] = [M[piv], M[col]];
|
|
15002
|
+
const d = M[col][col];
|
|
15003
|
+
for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
|
|
15004
|
+
for (let r = 0; r < n; r++) {
|
|
15005
|
+
if (r === col) continue;
|
|
15006
|
+
const f = M[r][col];
|
|
15007
|
+
for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
|
|
15008
|
+
}
|
|
15009
|
+
}
|
|
15010
|
+
return M.map((row) => row.slice(n));
|
|
15011
|
+
}
|
|
15012
|
+
function sbasProtectionLevels(sats, opts = {}) {
|
|
15013
|
+
const kH = opts.kH ?? 6;
|
|
15014
|
+
const kV = opts.kV ?? 5.33;
|
|
15015
|
+
if (sats.length < 4) return null;
|
|
15016
|
+
const N = [0, 1, 2, 3].map(() => [0, 0, 0, 0]);
|
|
15017
|
+
for (const s of sats) {
|
|
15018
|
+
if (!(s.variance > 0)) continue;
|
|
15019
|
+
const ce = Math.cos(s.elRad);
|
|
15020
|
+
const g = [
|
|
15021
|
+
ce * Math.sin(s.azRad),
|
|
15022
|
+
ce * Math.cos(s.azRad),
|
|
15023
|
+
Math.sin(s.elRad),
|
|
15024
|
+
1
|
|
15025
|
+
];
|
|
15026
|
+
const w = 1 / s.variance;
|
|
15027
|
+
for (let i = 0; i < 4; i++)
|
|
15028
|
+
for (let j = 0; j < 4; j++) N[i][j] += w * g[i] * g[j];
|
|
15029
|
+
}
|
|
15030
|
+
const cov = invertMatrix(N);
|
|
15031
|
+
if (!cov) return null;
|
|
15032
|
+
const dE2 = cov[0][0];
|
|
15033
|
+
const dN2 = cov[1][1];
|
|
15034
|
+
const dEN = cov[0][1];
|
|
15035
|
+
const dU = Math.sqrt(Math.max(cov[2][2], 0));
|
|
15036
|
+
const dMajor = Math.sqrt(
|
|
15037
|
+
(dE2 + dN2) / 2 + Math.sqrt(((dE2 - dN2) / 2) ** 2 + dEN * dEN)
|
|
15038
|
+
);
|
|
15039
|
+
return { hpl: kH * dMajor, vpl: kV * dU, dMajor, dU };
|
|
15040
|
+
}
|
|
14979
15041
|
function solveLinear(A, b) {
|
|
14980
15042
|
const n = b.length;
|
|
14981
15043
|
const M = A.map((row, i) => [...row, b[i]]);
|
|
@@ -15002,9 +15064,11 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
15002
15064
|
convergenceM = 1e-4,
|
|
15003
15065
|
tgd = true,
|
|
15004
15066
|
iono,
|
|
15005
|
-
gim
|
|
15067
|
+
gim,
|
|
15068
|
+
sbas
|
|
15006
15069
|
} = opts;
|
|
15007
15070
|
const gpsTow = (timeMs - GPS_EPOCH_MS_SPP) / 1e3 % 604800;
|
|
15071
|
+
const gpsWeek = Math.floor((timeMs - GPS_EPOCH_MS_SPP) / 1e3 / 604800);
|
|
15008
15072
|
const all = [...pseudoranges.keys()].filter((p) => ephemerides.has(p));
|
|
15009
15073
|
const minSats = (list) => 3 + new Set(list.map((p) => p[0])).size;
|
|
15010
15074
|
if (all.length < minSats(all)) return null;
|
|
@@ -15030,7 +15094,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
15030
15094
|
const sysRows = new Map(systems.map((s) => [s, 0]));
|
|
15031
15095
|
for (const k of Object.keys(residuals2)) delete residuals2[k];
|
|
15032
15096
|
const positionSaneIter = x9 * x9 + y2 * y2 + z2 * z2 > 1e12;
|
|
15033
|
-
const [
|
|
15097
|
+
const [rxLat2, rxLon2, rxHgt2] = positionSaneIter ? ecefToGeodetic(x9, y2, z2) : [0, 0, 0];
|
|
15034
15098
|
for (const prn of prns) {
|
|
15035
15099
|
const psr = pseudoranges.get(prn);
|
|
15036
15100
|
const eph = ephemerides.get(prn);
|
|
@@ -15041,7 +15105,16 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
15041
15105
|
const sat = computeSatPosition(eph, tTx - dtsClock * 1e3);
|
|
15042
15106
|
if (!Number.isFinite(sat.x)) continue;
|
|
15043
15107
|
const isKepler3 = eph.system !== "R" && eph.system !== "S";
|
|
15044
|
-
|
|
15108
|
+
let dts = dtsClock - (tgd && isKepler3 ? eph.tgd : 0);
|
|
15109
|
+
if (sbas) {
|
|
15110
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow);
|
|
15111
|
+
if (sc) {
|
|
15112
|
+
sat.x += sc.dPos[0];
|
|
15113
|
+
sat.y += sc.dPos[1];
|
|
15114
|
+
sat.z += sc.dPos[2];
|
|
15115
|
+
dts += sc.dClkS;
|
|
15116
|
+
}
|
|
15117
|
+
}
|
|
15045
15118
|
const travel = Math.hypot(sat.x - x9, sat.y - y2, sat.z - z2) / C_LIGHT;
|
|
15046
15119
|
const [sx, sy, sz] = sagnac2(sat, travel);
|
|
15047
15120
|
const rho = Math.hypot(sx - x9, sy - y2, sz - z2);
|
|
@@ -15060,14 +15133,26 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
15060
15133
|
const sinEl = Math.max(Math.sin(elev), 0.1);
|
|
15061
15134
|
weight = sinEl * sinEl;
|
|
15062
15135
|
}
|
|
15063
|
-
const tropo = troposphere && positionSane ? tropoDelay2(elev,
|
|
15136
|
+
const tropo = troposphere && positionSane ? tropoDelay2(elev, rxLat2, rxHgt2) : 0;
|
|
15064
15137
|
let ionoM = 0;
|
|
15065
|
-
if ((gim || iono) && positionSane) {
|
|
15138
|
+
if ((sbas || gim || iono) && positionSane) {
|
|
15066
15139
|
let l1M = null;
|
|
15067
|
-
if (
|
|
15068
|
-
|
|
15140
|
+
if (sbas) {
|
|
15141
|
+
const si = sbas.ionoDelay(
|
|
15142
|
+
gpsWeek,
|
|
15143
|
+
gpsTow,
|
|
15144
|
+
rxLat2,
|
|
15145
|
+
rxLon2,
|
|
15146
|
+
rxHgt2,
|
|
15147
|
+
azim,
|
|
15148
|
+
elev
|
|
15149
|
+
);
|
|
15150
|
+
if (si) l1M = si.delayM;
|
|
15151
|
+
}
|
|
15152
|
+
if (l1M === null && gim)
|
|
15153
|
+
l1M = gimSlantIonoDelayL1(gim, rxLat2, rxLon2, azim, elev, timeMs);
|
|
15069
15154
|
if (l1M === null && iono) {
|
|
15070
|
-
l1M = C_LIGHT * klobucharDelay(iono,
|
|
15155
|
+
l1M = C_LIGHT * klobucharDelay(iono, rxLat2, rxLon2, azim, elev, gpsTow);
|
|
15071
15156
|
}
|
|
15072
15157
|
if (l1M !== null) {
|
|
15073
15158
|
const f = PRIMARY_FREQ_HZ[sys] ?? F_L12;
|
|
@@ -15140,6 +15225,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
15140
15225
|
const { x, y, z, clock, residuals, iterations, converged } = inner;
|
|
15141
15226
|
const used = [];
|
|
15142
15227
|
const azels = [];
|
|
15228
|
+
const [rxLat, rxLon, rxHgt] = ecefToGeodetic(x, y, z);
|
|
15229
|
+
const sbasRows = [];
|
|
15143
15230
|
for (const prn of candidates) {
|
|
15144
15231
|
if (!(prn in residuals)) continue;
|
|
15145
15232
|
const eph = ephemerides.get(prn);
|
|
@@ -15149,8 +15236,27 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
15149
15236
|
if (azel.el >= elevationMaskDeg * Math.PI / 180) {
|
|
15150
15237
|
used.push(prn);
|
|
15151
15238
|
azels.push(azel);
|
|
15239
|
+
if (sbas) {
|
|
15240
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow);
|
|
15241
|
+
const si = sbas.ionoDelay(
|
|
15242
|
+
gpsWeek,
|
|
15243
|
+
gpsTow,
|
|
15244
|
+
rxLat,
|
|
15245
|
+
rxLon,
|
|
15246
|
+
rxHgt,
|
|
15247
|
+
azel.az,
|
|
15248
|
+
azel.el
|
|
15249
|
+
);
|
|
15250
|
+
if (sc && si)
|
|
15251
|
+
sbasRows.push({
|
|
15252
|
+
azRad: azel.az,
|
|
15253
|
+
elRad: azel.el,
|
|
15254
|
+
variance: sc.varM2 + si.varM2 + sbasAirTropoVar(azel.el)
|
|
15255
|
+
});
|
|
15256
|
+
}
|
|
15152
15257
|
}
|
|
15153
15258
|
}
|
|
15259
|
+
const pl = sbas ? sbasProtectionLevels(sbasRows) : null;
|
|
15154
15260
|
return {
|
|
15155
15261
|
position: [x, y, z],
|
|
15156
15262
|
clockBias: Object.fromEntries(clock),
|
|
@@ -15159,7 +15265,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
15159
15265
|
residuals,
|
|
15160
15266
|
dop: computeDop(azels),
|
|
15161
15267
|
iterations,
|
|
15162
|
-
converged
|
|
15268
|
+
converged,
|
|
15269
|
+
...pl ? { hpl: pl.hpl, vpl: pl.vpl, sbasSats: sbasRows.length } : sbas ? { sbasSats: sbasRows.length } : {}
|
|
15163
15270
|
};
|
|
15164
15271
|
}
|
|
15165
15272
|
|
|
@@ -17697,6 +17804,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
17697
17804
|
rhumbLine,
|
|
17698
17805
|
rtcm3Constellation,
|
|
17699
17806
|
satClockCorrection,
|
|
17807
|
+
sbasProtectionLevels,
|
|
17700
17808
|
sbfCrc16,
|
|
17701
17809
|
scanSbfFrames,
|
|
17702
17810
|
selectEphemeris,
|
package/dist/index.d.cts
CHANGED
|
@@ -17,7 +17,7 @@ export { TrimbleEpoch, TrimbleFrame, TrimbleMeasurement, TrimbleNavResult, Trimb
|
|
|
17
17
|
export { BinexEpoch, BinexMeasurement, BinexParseResult, BinexRecord, binexCrc16, binexCrc32, binexCsum8, binexRecords, decodeBinexEph, getBnxi, parseBinex } from './binex.cjs';
|
|
18
18
|
export { AllPositionsData, AlmanacPosition, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris } from './orbit.cjs';
|
|
19
19
|
export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.cjs';
|
|
20
|
-
export { AntennaOffset, ArFixedSat, ArOptions, ArResult, ArSat, CorrectionResult, DgnssOptions, DgnssSolution, EphemerisSource, FixedPositionResult, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, NetworkFcbResult, NetworkFcbSummary, NlArc, NlFcbResult, PppAntennaModel, PppArc, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppFixState, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, RtkObsEpoch, RtkObsInput, RtkPostProcessOptions, RtkPostProcessResult, RtkTrackPoint, SatGeom, SbasIonoDelay, SbasProcessor, SbasSatCorrection, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength } from './positioning.cjs';
|
|
20
|
+
export { AntennaOffset, ArFixedSat, ArOptions, ArResult, ArSat, CorrectionResult, DgnssOptions, DgnssSolution, EphemerisSource, FixedPositionResult, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, NetworkFcbResult, NetworkFcbSummary, NlArc, NlFcbResult, PppAntennaModel, PppArc, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppFixState, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, RtkObsEpoch, RtkObsInput, RtkPostProcessOptions, RtkPostProcessResult, RtkTrackPoint, SatGeom, SbasCorrectionSource, SbasIonoDelay, SbasProcessor, SbasSatCorrection, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, resolvePppAmbiguities, satClockCorrection, sbasProtectionLevels, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength } from './positioning.cjs';
|
|
21
21
|
export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.cjs';
|
|
22
22
|
export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, IonoAccumulator, IonoDcbResult, IonoPoint, IonoRatePoint, IonoRateSeries, IonoResult, IonoSeries, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, SatDcbMap, analyzeQuality, applyIonoDcb, computeIonoRate, detrendIonoArcs, parseSinexBiasDcb } from './analysis.cjs';
|
|
23
23
|
export { AntennaEntry, AntexFile, FrequencyData, frequencyLabel, parseAntex } from './antex.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export { TrimbleEpoch, TrimbleFrame, TrimbleMeasurement, TrimbleNavResult, Trimb
|
|
|
17
17
|
export { BinexEpoch, BinexMeasurement, BinexParseResult, BinexRecord, binexCrc16, binexCrc32, binexCsum8, binexRecords, decodeBinexEph, getBnxi, parseBinex } from './binex.js';
|
|
18
18
|
export { AllPositionsData, AlmanacPosition, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris } from './orbit.js';
|
|
19
19
|
export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.js';
|
|
20
|
-
export { AntennaOffset, ArFixedSat, ArOptions, ArResult, ArSat, CorrectionResult, DgnssOptions, DgnssSolution, EphemerisSource, FixedPositionResult, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, NetworkFcbResult, NetworkFcbSummary, NlArc, NlFcbResult, PppAntennaModel, PppArc, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppFixState, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, RtkObsEpoch, RtkObsInput, RtkPostProcessOptions, RtkPostProcessResult, RtkTrackPoint, SatGeom, SbasIonoDelay, SbasProcessor, SbasSatCorrection, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength } from './positioning.js';
|
|
20
|
+
export { AntennaOffset, ArFixedSat, ArOptions, ArResult, ArSat, CorrectionResult, DgnssOptions, DgnssSolution, EphemerisSource, FixedPositionResult, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, NetworkFcbResult, NetworkFcbSummary, NlArc, NlFcbResult, PppAntennaModel, PppArc, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppFixState, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, RtkObsEpoch, RtkObsInput, RtkPostProcessOptions, RtkPostProcessResult, RtkTrackPoint, SatGeom, SbasCorrectionSource, SbasIonoDelay, SbasProcessor, SbasSatCorrection, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, resolvePppAmbiguities, satClockCorrection, sbasProtectionLevels, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength } from './positioning.js';
|
|
21
21
|
export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.js';
|
|
22
22
|
export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, IonoAccumulator, IonoDcbResult, IonoPoint, IonoRatePoint, IonoRateSeries, IonoResult, IonoSeries, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, SatDcbMap, analyzeQuality, applyIonoDcb, computeIonoRate, detrendIonoArcs, parseSinexBiasDcb } from './analysis.js';
|
|
23
23
|
export { AntennaEntry, AntexFile, FrequencyData, frequencyLabel, parseAntex } from './antex.js';
|
package/dist/index.js
CHANGED
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
parseSbfNav,
|
|
69
69
|
parseSbfReceiverSetup,
|
|
70
70
|
scanSbfFrames
|
|
71
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-DE5IUULR.js";
|
|
72
72
|
import "./chunk-FA2UTLEQ.js";
|
|
73
73
|
import {
|
|
74
74
|
OEM4_HLEN,
|
|
@@ -76,7 +76,7 @@ import {
|
|
|
76
76
|
oem4Frames,
|
|
77
77
|
parseNovatelNav,
|
|
78
78
|
parseNovatelRange
|
|
79
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-ZS5RLFWI.js";
|
|
80
80
|
import {
|
|
81
81
|
parseTrimble,
|
|
82
82
|
parseTrimbleNav,
|
|
@@ -157,6 +157,7 @@ import {
|
|
|
157
157
|
postProcessRtk,
|
|
158
158
|
resolvePppAmbiguities,
|
|
159
159
|
satClockCorrection,
|
|
160
|
+
sbasProtectionLevels,
|
|
160
161
|
solidEarthTide,
|
|
161
162
|
solveDgnss,
|
|
162
163
|
solvePpp,
|
|
@@ -164,7 +165,7 @@ import {
|
|
|
164
165
|
sunEcef,
|
|
165
166
|
toRtkEpoch,
|
|
166
167
|
wlWavelength
|
|
167
|
-
} from "./chunk-
|
|
168
|
+
} from "./chunk-U32IU7RO.js";
|
|
168
169
|
import "./chunk-WQX354KD.js";
|
|
169
170
|
import {
|
|
170
171
|
almanacEpochMs,
|
|
@@ -630,6 +631,7 @@ export {
|
|
|
630
631
|
rhumbLine,
|
|
631
632
|
rtcm3Constellation,
|
|
632
633
|
satClockCorrection,
|
|
634
|
+
sbasProtectionLevels,
|
|
633
635
|
crc16 as sbfCrc16,
|
|
634
636
|
scanSbfFrames,
|
|
635
637
|
selectEphemeris,
|
package/dist/novatel.cjs
CHANGED
|
@@ -729,7 +729,7 @@ function decodeGpsQzssEphemeris(view, p, sys) {
|
|
|
729
729
|
tgd: view.getFloat64(p + 172, true)
|
|
730
730
|
};
|
|
731
731
|
}
|
|
732
|
-
function parseNovatelNav(data) {
|
|
732
|
+
function parseNovatelNav(data, opts = {}) {
|
|
733
733
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
734
734
|
const stats = { badCrc: 0 };
|
|
735
735
|
const ephemerides = [];
|
|
@@ -802,6 +802,7 @@ function parseNovatelNav(data) {
|
|
|
802
802
|
const prn = view.getUint32(p + 4, true);
|
|
803
803
|
const msg = new Uint8Array(32);
|
|
804
804
|
msg.set(data.subarray(p + 12, p + 41));
|
|
805
|
+
opts.onSbasMessage?.(msg, prn, frame.week, frame.towMs / 1e3);
|
|
805
806
|
const eph = decodeSbasGeoNav(msg, prn, frame.week, frame.towMs / 1e3);
|
|
806
807
|
if (!eph) continue;
|
|
807
808
|
const prev = lastGlo.get(eph.prn);
|
package/dist/novatel.d.cts
CHANGED
|
@@ -105,7 +105,13 @@ interface NovatelNavResult {
|
|
|
105
105
|
* Galileo by IODNav + toe + toc per data source, BeiDou by toe + toc,
|
|
106
106
|
* GLONASS by reference time and health.
|
|
107
107
|
*/
|
|
108
|
-
declare function parseNovatelNav(data: Uint8Array
|
|
108
|
+
declare function parseNovatelNav(data: Uint8Array, opts?: {
|
|
109
|
+
/** Called for each SBAS L1 message (all types) from RAWSBASFRAME. The
|
|
110
|
+
* frame carries 232 bits — the correction fields fit, but the inner SBAS
|
|
111
|
+
* CRC does not; the outer OEM4 CRC-32 guarantees the bytes. Feed an
|
|
112
|
+
* {@link ../positioning/sbas.SbasProcessor}. */
|
|
113
|
+
onSbasMessage?: (msg: Uint8Array, prn: number, week: number, tow: number) => void;
|
|
114
|
+
}): NovatelNavResult;
|
|
109
115
|
|
|
110
116
|
/**
|
|
111
117
|
* NovAtel OEM4/6/7 raw-measurement decoding (RANGE log, message 43).
|
package/dist/novatel.d.ts
CHANGED
|
@@ -105,7 +105,13 @@ interface NovatelNavResult {
|
|
|
105
105
|
* Galileo by IODNav + toe + toc per data source, BeiDou by toe + toc,
|
|
106
106
|
* GLONASS by reference time and health.
|
|
107
107
|
*/
|
|
108
|
-
declare function parseNovatelNav(data: Uint8Array
|
|
108
|
+
declare function parseNovatelNav(data: Uint8Array, opts?: {
|
|
109
|
+
/** Called for each SBAS L1 message (all types) from RAWSBASFRAME. The
|
|
110
|
+
* frame carries 232 bits — the correction fields fit, but the inner SBAS
|
|
111
|
+
* CRC does not; the outer OEM4 CRC-32 guarantees the bytes. Feed an
|
|
112
|
+
* {@link ../positioning/sbas.SbasProcessor}. */
|
|
113
|
+
onSbasMessage?: (msg: Uint8Array, prn: number, week: number, tow: number) => void;
|
|
114
|
+
}): NovatelNavResult;
|
|
109
115
|
|
|
110
116
|
/**
|
|
111
117
|
* NovAtel OEM4/6/7 raw-measurement decoding (RANGE log, message 43).
|
package/dist/novatel.js
CHANGED
package/dist/positioning.cjs
CHANGED
|
@@ -43,6 +43,7 @@ __export(positioning_exports, {
|
|
|
43
43
|
postProcessRtk: () => postProcessRtk,
|
|
44
44
|
resolvePppAmbiguities: () => resolvePppAmbiguities,
|
|
45
45
|
satClockCorrection: () => satClockCorrection,
|
|
46
|
+
sbasProtectionLevels: () => sbasProtectionLevels,
|
|
46
47
|
solidEarthTide: () => solidEarthTide,
|
|
47
48
|
solveDgnss: () => solveDgnss,
|
|
48
49
|
solvePpp: () => solvePpp,
|
|
@@ -3593,6 +3594,65 @@ function sagnac2(pos, travelTimeS) {
|
|
|
3593
3594
|
const s = Math.sin(theta);
|
|
3594
3595
|
return [pos.x * c + pos.y * s, -pos.x * s + pos.y * c, pos.z];
|
|
3595
3596
|
}
|
|
3597
|
+
function sbasAirTropoVar(elRad) {
|
|
3598
|
+
const elDeg = elRad * 180 / Math.PI;
|
|
3599
|
+
const sigMp = 0.13 + 0.53 * Math.exp(-elDeg / 10);
|
|
3600
|
+
const sigNoise = 0.15 + 0.43 * Math.exp(-elDeg / 6.9);
|
|
3601
|
+
const m = 1.001 / Math.sqrt(2001e-6 + Math.sin(elRad) * Math.sin(elRad));
|
|
3602
|
+
const sigTropo = 0.12 * m;
|
|
3603
|
+
return sigMp * sigMp + sigNoise * sigNoise + sigTropo * sigTropo;
|
|
3604
|
+
}
|
|
3605
|
+
function invertMatrix(A) {
|
|
3606
|
+
const n = A.length;
|
|
3607
|
+
const M = A.map((row, i) => [
|
|
3608
|
+
...row,
|
|
3609
|
+
...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
|
|
3610
|
+
]);
|
|
3611
|
+
for (let col = 0; col < n; col++) {
|
|
3612
|
+
let piv = col;
|
|
3613
|
+
for (let r = col + 1; r < n; r++)
|
|
3614
|
+
if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
|
|
3615
|
+
if (Math.abs(M[piv][col]) < 1e-12) return null;
|
|
3616
|
+
[M[col], M[piv]] = [M[piv], M[col]];
|
|
3617
|
+
const d = M[col][col];
|
|
3618
|
+
for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
|
|
3619
|
+
for (let r = 0; r < n; r++) {
|
|
3620
|
+
if (r === col) continue;
|
|
3621
|
+
const f = M[r][col];
|
|
3622
|
+
for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
return M.map((row) => row.slice(n));
|
|
3626
|
+
}
|
|
3627
|
+
function sbasProtectionLevels(sats, opts = {}) {
|
|
3628
|
+
const kH = opts.kH ?? 6;
|
|
3629
|
+
const kV = opts.kV ?? 5.33;
|
|
3630
|
+
if (sats.length < 4) return null;
|
|
3631
|
+
const N = [0, 1, 2, 3].map(() => [0, 0, 0, 0]);
|
|
3632
|
+
for (const s of sats) {
|
|
3633
|
+
if (!(s.variance > 0)) continue;
|
|
3634
|
+
const ce = Math.cos(s.elRad);
|
|
3635
|
+
const g = [
|
|
3636
|
+
ce * Math.sin(s.azRad),
|
|
3637
|
+
ce * Math.cos(s.azRad),
|
|
3638
|
+
Math.sin(s.elRad),
|
|
3639
|
+
1
|
|
3640
|
+
];
|
|
3641
|
+
const w = 1 / s.variance;
|
|
3642
|
+
for (let i = 0; i < 4; i++)
|
|
3643
|
+
for (let j = 0; j < 4; j++) N[i][j] += w * g[i] * g[j];
|
|
3644
|
+
}
|
|
3645
|
+
const cov = invertMatrix(N);
|
|
3646
|
+
if (!cov) return null;
|
|
3647
|
+
const dE2 = cov[0][0];
|
|
3648
|
+
const dN2 = cov[1][1];
|
|
3649
|
+
const dEN = cov[0][1];
|
|
3650
|
+
const dU = Math.sqrt(Math.max(cov[2][2], 0));
|
|
3651
|
+
const dMajor = Math.sqrt(
|
|
3652
|
+
(dE2 + dN2) / 2 + Math.sqrt(((dE2 - dN2) / 2) ** 2 + dEN * dEN)
|
|
3653
|
+
);
|
|
3654
|
+
return { hpl: kH * dMajor, vpl: kV * dU, dMajor, dU };
|
|
3655
|
+
}
|
|
3596
3656
|
function solveLinear(A, b) {
|
|
3597
3657
|
const n = b.length;
|
|
3598
3658
|
const M = A.map((row, i) => [...row, b[i]]);
|
|
@@ -3619,9 +3679,11 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3619
3679
|
convergenceM = 1e-4,
|
|
3620
3680
|
tgd = true,
|
|
3621
3681
|
iono,
|
|
3622
|
-
gim
|
|
3682
|
+
gim,
|
|
3683
|
+
sbas
|
|
3623
3684
|
} = opts;
|
|
3624
3685
|
const gpsTow = (timeMs - GPS_EPOCH_MS_SPP) / 1e3 % 604800;
|
|
3686
|
+
const gpsWeek = Math.floor((timeMs - GPS_EPOCH_MS_SPP) / 1e3 / 604800);
|
|
3625
3687
|
const all = [...pseudoranges.keys()].filter((p) => ephemerides.has(p));
|
|
3626
3688
|
const minSats = (list) => 3 + new Set(list.map((p) => p[0])).size;
|
|
3627
3689
|
if (all.length < minSats(all)) return null;
|
|
@@ -3647,7 +3709,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3647
3709
|
const sysRows = new Map(systems.map((s) => [s, 0]));
|
|
3648
3710
|
for (const k of Object.keys(residuals2)) delete residuals2[k];
|
|
3649
3711
|
const positionSaneIter = x9 * x9 + y2 * y2 + z2 * z2 > 1e12;
|
|
3650
|
-
const [
|
|
3712
|
+
const [rxLat2, rxLon2, rxHgt2] = positionSaneIter ? ecefToGeodetic(x9, y2, z2) : [0, 0, 0];
|
|
3651
3713
|
for (const prn of prns) {
|
|
3652
3714
|
const psr = pseudoranges.get(prn);
|
|
3653
3715
|
const eph = ephemerides.get(prn);
|
|
@@ -3658,7 +3720,16 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3658
3720
|
const sat = computeSatPosition(eph, tTx - dtsClock * 1e3);
|
|
3659
3721
|
if (!Number.isFinite(sat.x)) continue;
|
|
3660
3722
|
const isKepler = eph.system !== "R" && eph.system !== "S";
|
|
3661
|
-
|
|
3723
|
+
let dts = dtsClock - (tgd && isKepler ? eph.tgd : 0);
|
|
3724
|
+
if (sbas) {
|
|
3725
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow);
|
|
3726
|
+
if (sc) {
|
|
3727
|
+
sat.x += sc.dPos[0];
|
|
3728
|
+
sat.y += sc.dPos[1];
|
|
3729
|
+
sat.z += sc.dPos[2];
|
|
3730
|
+
dts += sc.dClkS;
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3662
3733
|
const travel = Math.hypot(sat.x - x9, sat.y - y2, sat.z - z2) / C_LIGHT;
|
|
3663
3734
|
const [sx, sy, sz] = sagnac2(sat, travel);
|
|
3664
3735
|
const rho = Math.hypot(sx - x9, sy - y2, sz - z2);
|
|
@@ -3677,14 +3748,26 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3677
3748
|
const sinEl = Math.max(Math.sin(elev), 0.1);
|
|
3678
3749
|
weight = sinEl * sinEl;
|
|
3679
3750
|
}
|
|
3680
|
-
const tropo = troposphere && positionSane ? tropoDelay2(elev,
|
|
3751
|
+
const tropo = troposphere && positionSane ? tropoDelay2(elev, rxLat2, rxHgt2) : 0;
|
|
3681
3752
|
let ionoM = 0;
|
|
3682
|
-
if ((gim || iono) && positionSane) {
|
|
3753
|
+
if ((sbas || gim || iono) && positionSane) {
|
|
3683
3754
|
let l1M = null;
|
|
3684
|
-
if (
|
|
3685
|
-
|
|
3755
|
+
if (sbas) {
|
|
3756
|
+
const si = sbas.ionoDelay(
|
|
3757
|
+
gpsWeek,
|
|
3758
|
+
gpsTow,
|
|
3759
|
+
rxLat2,
|
|
3760
|
+
rxLon2,
|
|
3761
|
+
rxHgt2,
|
|
3762
|
+
azim,
|
|
3763
|
+
elev
|
|
3764
|
+
);
|
|
3765
|
+
if (si) l1M = si.delayM;
|
|
3766
|
+
}
|
|
3767
|
+
if (l1M === null && gim)
|
|
3768
|
+
l1M = gimSlantIonoDelayL1(gim, rxLat2, rxLon2, azim, elev, timeMs);
|
|
3686
3769
|
if (l1M === null && iono) {
|
|
3687
|
-
l1M = C_LIGHT * klobucharDelay(iono,
|
|
3770
|
+
l1M = C_LIGHT * klobucharDelay(iono, rxLat2, rxLon2, azim, elev, gpsTow);
|
|
3688
3771
|
}
|
|
3689
3772
|
if (l1M !== null) {
|
|
3690
3773
|
const f = PRIMARY_FREQ_HZ[sys] ?? F_L12;
|
|
@@ -3757,6 +3840,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3757
3840
|
const { x, y, z, clock, residuals, iterations, converged } = inner;
|
|
3758
3841
|
const used = [];
|
|
3759
3842
|
const azels = [];
|
|
3843
|
+
const [rxLat, rxLon, rxHgt] = ecefToGeodetic(x, y, z);
|
|
3844
|
+
const sbasRows = [];
|
|
3760
3845
|
for (const prn of candidates) {
|
|
3761
3846
|
if (!(prn in residuals)) continue;
|
|
3762
3847
|
const eph = ephemerides.get(prn);
|
|
@@ -3766,8 +3851,27 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3766
3851
|
if (azel.el >= elevationMaskDeg * Math.PI / 180) {
|
|
3767
3852
|
used.push(prn);
|
|
3768
3853
|
azels.push(azel);
|
|
3854
|
+
if (sbas) {
|
|
3855
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow);
|
|
3856
|
+
const si = sbas.ionoDelay(
|
|
3857
|
+
gpsWeek,
|
|
3858
|
+
gpsTow,
|
|
3859
|
+
rxLat,
|
|
3860
|
+
rxLon,
|
|
3861
|
+
rxHgt,
|
|
3862
|
+
azel.az,
|
|
3863
|
+
azel.el
|
|
3864
|
+
);
|
|
3865
|
+
if (sc && si)
|
|
3866
|
+
sbasRows.push({
|
|
3867
|
+
azRad: azel.az,
|
|
3868
|
+
elRad: azel.el,
|
|
3869
|
+
variance: sc.varM2 + si.varM2 + sbasAirTropoVar(azel.el)
|
|
3870
|
+
});
|
|
3871
|
+
}
|
|
3769
3872
|
}
|
|
3770
3873
|
}
|
|
3874
|
+
const pl = sbas ? sbasProtectionLevels(sbasRows) : null;
|
|
3771
3875
|
return {
|
|
3772
3876
|
position: [x, y, z],
|
|
3773
3877
|
clockBias: Object.fromEntries(clock),
|
|
@@ -3776,7 +3880,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3776
3880
|
residuals,
|
|
3777
3881
|
dop: computeDop(azels),
|
|
3778
3882
|
iterations,
|
|
3779
|
-
converged
|
|
3883
|
+
converged,
|
|
3884
|
+
...pl ? { hpl: pl.hpl, vpl: pl.vpl, sbasSats: sbasRows.length } : sbas ? { sbasSats: sbasRows.length } : {}
|
|
3780
3885
|
};
|
|
3781
3886
|
}
|
|
3782
3887
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -3804,6 +3909,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3804
3909
|
postProcessRtk,
|
|
3805
3910
|
resolvePppAmbiguities,
|
|
3806
3911
|
satClockCorrection,
|
|
3912
|
+
sbasProtectionLevels,
|
|
3807
3913
|
solidEarthTide,
|
|
3808
3914
|
solveDgnss,
|
|
3809
3915
|
solvePpp,
|
package/dist/positioning.d.cts
CHANGED
|
@@ -1223,6 +1223,28 @@ interface SppOptions {
|
|
|
1223
1223
|
* reference the broadcast clock already matches. Default true.
|
|
1224
1224
|
*/
|
|
1225
1225
|
tgd?: boolean;
|
|
1226
|
+
/**
|
|
1227
|
+
* SBAS wide-area corrections (WAAS/EGNOS/…). When given, each satellite in
|
|
1228
|
+
* the SBAS PRN mask has its position + clock adjusted by the fast/long-term
|
|
1229
|
+
* corrections, and — where the ionospheric pierce point is covered by the
|
|
1230
|
+
* SBAS grid — the SBAS slant ionosphere replaces the Klobuchar/GIM model.
|
|
1231
|
+
* Pass an {@link ./sbas.SbasProcessor} (or anything with these two methods).
|
|
1232
|
+
* Combine with single-frequency pseudoranges, not iono-free input.
|
|
1233
|
+
*/
|
|
1234
|
+
sbas?: SbasCorrectionSource;
|
|
1235
|
+
}
|
|
1236
|
+
/** The two SBAS corrections `solveSpp` consumes — {@link ./sbas.SbasProcessor}
|
|
1237
|
+
* satisfies this structurally. */
|
|
1238
|
+
interface SbasCorrectionSource {
|
|
1239
|
+
satCorrection(prn: string, week: number, tow: number): {
|
|
1240
|
+
dPos: [number, number, number];
|
|
1241
|
+
dClkS: number;
|
|
1242
|
+
varM2: number;
|
|
1243
|
+
} | null;
|
|
1244
|
+
ionoDelay(week: number, tow: number, latRad: number, lonRad: number, heightM: number, azRad: number, elRad: number): {
|
|
1245
|
+
delayM: number;
|
|
1246
|
+
varM2: number;
|
|
1247
|
+
} | null;
|
|
1226
1248
|
}
|
|
1227
1249
|
interface SppSolution {
|
|
1228
1250
|
/** Receiver position, ECEF meters. */
|
|
@@ -1239,6 +1261,15 @@ interface SppSolution {
|
|
|
1239
1261
|
dop: DopValues | null;
|
|
1240
1262
|
iterations: number;
|
|
1241
1263
|
converged: boolean;
|
|
1264
|
+
/**
|
|
1265
|
+
* SBAS integrity, present only when the `sbas` option was given and enough
|
|
1266
|
+
* corrected satellites were available. Horizontal/vertical protection levels
|
|
1267
|
+
* (m) — the DO-229 statistical error bounds — and the count of SBAS-corrected
|
|
1268
|
+
* satellites they were computed from.
|
|
1269
|
+
*/
|
|
1270
|
+
hpl?: number;
|
|
1271
|
+
vpl?: number;
|
|
1272
|
+
sbasSats?: number;
|
|
1242
1273
|
}
|
|
1243
1274
|
/**
|
|
1244
1275
|
* Broadcast satellite clock correction in seconds at time `tMs`
|
|
@@ -1251,6 +1282,26 @@ declare function satClockCorrection(eph: Ephemeris, tMs: number): number;
|
|
|
1251
1282
|
* Removes the first-order ionospheric delay.
|
|
1252
1283
|
*/
|
|
1253
1284
|
declare function ionoFree(p1: number, p2: number, f1: number, f2: number): number;
|
|
1285
|
+
/**
|
|
1286
|
+
* SBAS horizontal/vertical protection levels (m) from the per-satellite ENU
|
|
1287
|
+
* geometry and total error variances. HPL = K_H·d_major, VPL = K_V·d_U, where
|
|
1288
|
+
* d_major is the semi-major axis of the horizontal position-error ellipse and
|
|
1289
|
+
* d_U the vertical std, both from the variance-weighted least-squares
|
|
1290
|
+
* covariance (DO-229 §2.1.4.10). Needs ≥ 4 satellites.
|
|
1291
|
+
*/
|
|
1292
|
+
declare function sbasProtectionLevels(sats: readonly {
|
|
1293
|
+
azRad: number;
|
|
1294
|
+
elRad: number;
|
|
1295
|
+
variance: number;
|
|
1296
|
+
}[], opts?: {
|
|
1297
|
+
kH?: number;
|
|
1298
|
+
kV?: number;
|
|
1299
|
+
}): {
|
|
1300
|
+
hpl: number;
|
|
1301
|
+
vpl: number;
|
|
1302
|
+
dMajor: number;
|
|
1303
|
+
dU: number;
|
|
1304
|
+
} | null;
|
|
1254
1305
|
/**
|
|
1255
1306
|
* Solve receiver position from one epoch of pseudoranges.
|
|
1256
1307
|
*
|
|
@@ -1263,4 +1314,4 @@ declare function ionoFree(p1: number, p2: number, f1: number, f2: number): numbe
|
|
|
1263
1314
|
*/
|
|
1264
1315
|
declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
|
|
1265
1316
|
|
|
1266
|
-
export { type AntennaOffset, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type DgnssOptions, type DgnssSolution, type EphemerisSource, type FixedPositionResult, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type NetworkFcbResult, type NetworkFcbSummary, type NlArc, type NlFcbResult, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppFixState, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type RtkObsEpoch, type RtkObsInput, type RtkPostProcessOptions, type RtkPostProcessResult, type RtkTrackPoint, type SatGeom, type SbasIonoDelay, SbasProcessor, type SbasSatCorrection, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
|
|
1317
|
+
export { type AntennaOffset, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type DgnssOptions, type DgnssSolution, type EphemerisSource, type FixedPositionResult, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type NetworkFcbResult, type NetworkFcbSummary, type NlArc, type NlFcbResult, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppFixState, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type RtkObsEpoch, type RtkObsInput, type RtkPostProcessOptions, type RtkPostProcessResult, type RtkTrackPoint, type SatGeom, type SbasCorrectionSource, type SbasIonoDelay, SbasProcessor, type SbasSatCorrection, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, resolvePppAmbiguities, satClockCorrection, sbasProtectionLevels, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
|
package/dist/positioning.d.ts
CHANGED
|
@@ -1223,6 +1223,28 @@ interface SppOptions {
|
|
|
1223
1223
|
* reference the broadcast clock already matches. Default true.
|
|
1224
1224
|
*/
|
|
1225
1225
|
tgd?: boolean;
|
|
1226
|
+
/**
|
|
1227
|
+
* SBAS wide-area corrections (WAAS/EGNOS/…). When given, each satellite in
|
|
1228
|
+
* the SBAS PRN mask has its position + clock adjusted by the fast/long-term
|
|
1229
|
+
* corrections, and — where the ionospheric pierce point is covered by the
|
|
1230
|
+
* SBAS grid — the SBAS slant ionosphere replaces the Klobuchar/GIM model.
|
|
1231
|
+
* Pass an {@link ./sbas.SbasProcessor} (or anything with these two methods).
|
|
1232
|
+
* Combine with single-frequency pseudoranges, not iono-free input.
|
|
1233
|
+
*/
|
|
1234
|
+
sbas?: SbasCorrectionSource;
|
|
1235
|
+
}
|
|
1236
|
+
/** The two SBAS corrections `solveSpp` consumes — {@link ./sbas.SbasProcessor}
|
|
1237
|
+
* satisfies this structurally. */
|
|
1238
|
+
interface SbasCorrectionSource {
|
|
1239
|
+
satCorrection(prn: string, week: number, tow: number): {
|
|
1240
|
+
dPos: [number, number, number];
|
|
1241
|
+
dClkS: number;
|
|
1242
|
+
varM2: number;
|
|
1243
|
+
} | null;
|
|
1244
|
+
ionoDelay(week: number, tow: number, latRad: number, lonRad: number, heightM: number, azRad: number, elRad: number): {
|
|
1245
|
+
delayM: number;
|
|
1246
|
+
varM2: number;
|
|
1247
|
+
} | null;
|
|
1226
1248
|
}
|
|
1227
1249
|
interface SppSolution {
|
|
1228
1250
|
/** Receiver position, ECEF meters. */
|
|
@@ -1239,6 +1261,15 @@ interface SppSolution {
|
|
|
1239
1261
|
dop: DopValues | null;
|
|
1240
1262
|
iterations: number;
|
|
1241
1263
|
converged: boolean;
|
|
1264
|
+
/**
|
|
1265
|
+
* SBAS integrity, present only when the `sbas` option was given and enough
|
|
1266
|
+
* corrected satellites were available. Horizontal/vertical protection levels
|
|
1267
|
+
* (m) — the DO-229 statistical error bounds — and the count of SBAS-corrected
|
|
1268
|
+
* satellites they were computed from.
|
|
1269
|
+
*/
|
|
1270
|
+
hpl?: number;
|
|
1271
|
+
vpl?: number;
|
|
1272
|
+
sbasSats?: number;
|
|
1242
1273
|
}
|
|
1243
1274
|
/**
|
|
1244
1275
|
* Broadcast satellite clock correction in seconds at time `tMs`
|
|
@@ -1251,6 +1282,26 @@ declare function satClockCorrection(eph: Ephemeris, tMs: number): number;
|
|
|
1251
1282
|
* Removes the first-order ionospheric delay.
|
|
1252
1283
|
*/
|
|
1253
1284
|
declare function ionoFree(p1: number, p2: number, f1: number, f2: number): number;
|
|
1285
|
+
/**
|
|
1286
|
+
* SBAS horizontal/vertical protection levels (m) from the per-satellite ENU
|
|
1287
|
+
* geometry and total error variances. HPL = K_H·d_major, VPL = K_V·d_U, where
|
|
1288
|
+
* d_major is the semi-major axis of the horizontal position-error ellipse and
|
|
1289
|
+
* d_U the vertical std, both from the variance-weighted least-squares
|
|
1290
|
+
* covariance (DO-229 §2.1.4.10). Needs ≥ 4 satellites.
|
|
1291
|
+
*/
|
|
1292
|
+
declare function sbasProtectionLevels(sats: readonly {
|
|
1293
|
+
azRad: number;
|
|
1294
|
+
elRad: number;
|
|
1295
|
+
variance: number;
|
|
1296
|
+
}[], opts?: {
|
|
1297
|
+
kH?: number;
|
|
1298
|
+
kV?: number;
|
|
1299
|
+
}): {
|
|
1300
|
+
hpl: number;
|
|
1301
|
+
vpl: number;
|
|
1302
|
+
dMajor: number;
|
|
1303
|
+
dU: number;
|
|
1304
|
+
} | null;
|
|
1254
1305
|
/**
|
|
1255
1306
|
* Solve receiver position from one epoch of pseudoranges.
|
|
1256
1307
|
*
|
|
@@ -1263,4 +1314,4 @@ declare function ionoFree(p1: number, p2: number, f1: number, f2: number): numbe
|
|
|
1263
1314
|
*/
|
|
1264
1315
|
declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
|
|
1265
1316
|
|
|
1266
|
-
export { type AntennaOffset, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type DgnssOptions, type DgnssSolution, type EphemerisSource, type FixedPositionResult, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type NetworkFcbResult, type NetworkFcbSummary, type NlArc, type NlFcbResult, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppFixState, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type RtkObsEpoch, type RtkObsInput, type RtkPostProcessOptions, type RtkPostProcessResult, type RtkTrackPoint, type SatGeom, type SbasIonoDelay, SbasProcessor, type SbasSatCorrection, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
|
|
1317
|
+
export { type AntennaOffset, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type DgnssOptions, type DgnssSolution, type EphemerisSource, type FixedPositionResult, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type NetworkFcbResult, type NetworkFcbSummary, type NlArc, type NlFcbResult, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppFixState, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type RtkObsEpoch, type RtkObsInput, type RtkPostProcessOptions, type RtkPostProcessResult, type RtkTrackPoint, type SatGeom, type SbasCorrectionSource, type SbasIonoDelay, SbasProcessor, type SbasSatCorrection, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, postProcessRtk, resolvePppAmbiguities, satClockCorrection, sbasProtectionLevels, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
|
package/dist/positioning.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
postProcessRtk,
|
|
23
23
|
resolvePppAmbiguities,
|
|
24
24
|
satClockCorrection,
|
|
25
|
+
sbasProtectionLevels,
|
|
25
26
|
solidEarthTide,
|
|
26
27
|
solveDgnss,
|
|
27
28
|
solvePpp,
|
|
@@ -29,7 +30,7 @@ import {
|
|
|
29
30
|
sunEcef,
|
|
30
31
|
toRtkEpoch,
|
|
31
32
|
wlWavelength
|
|
32
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-U32IU7RO.js";
|
|
33
34
|
import "./chunk-WQX354KD.js";
|
|
34
35
|
import "./chunk-MHGNKSSH.js";
|
|
35
36
|
import "./chunk-MIIM4LDY.js";
|
|
@@ -65,6 +66,7 @@ export {
|
|
|
65
66
|
postProcessRtk,
|
|
66
67
|
resolvePppAmbiguities,
|
|
67
68
|
satClockCorrection,
|
|
69
|
+
sbasProtectionLevels,
|
|
68
70
|
solidEarthTide,
|
|
69
71
|
solveDgnss,
|
|
70
72
|
solvePpp,
|
package/dist/sbf.cjs
CHANGED
|
@@ -1981,7 +1981,7 @@ function decodeSbasGeoNav(msg, prn, week, tow) {
|
|
|
1981
1981
|
}
|
|
1982
1982
|
|
|
1983
1983
|
// src/sbf/rawnav-sbas.ts
|
|
1984
|
-
function feedGeoBlock(view, b) {
|
|
1984
|
+
function feedGeoBlock(view, b, onSbasMessage) {
|
|
1985
1985
|
const tow = view.getUint32(b + 8, true);
|
|
1986
1986
|
const wnc = view.getUint16(b + 12, true);
|
|
1987
1987
|
const svid = view.getUint8(b + 14);
|
|
@@ -1997,10 +1997,11 @@ function feedGeoBlock(view, b) {
|
|
|
1997
1997
|
}
|
|
1998
1998
|
if (!sbasCrcOk(msg)) return { badCrc: true };
|
|
1999
1999
|
const prn = Number(prnStr.slice(1)) + 100;
|
|
2000
|
+
onSbasMessage?.(msg, prn, wnc, tow / 1e3);
|
|
2000
2001
|
const eph = decodeSbasGeoNav(msg, prn, wnc, tow / 1e3);
|
|
2001
2002
|
return eph ? { eph } : {};
|
|
2002
2003
|
}
|
|
2003
|
-
function parseSbfGeoNav(data) {
|
|
2004
|
+
function parseSbfGeoNav(data, opts = {}) {
|
|
2004
2005
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
2005
2006
|
const ephemerides = [];
|
|
2006
2007
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2009,7 +2010,7 @@ function parseSbfGeoNav(data) {
|
|
|
2009
2010
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
2010
2011
|
if (id !== 4020 || len < 52) return;
|
|
2011
2012
|
messages++;
|
|
2012
|
-
const r = feedGeoBlock(view, b);
|
|
2013
|
+
const r = feedGeoBlock(view, b, opts.onSbasMessage);
|
|
2013
2014
|
if (r.eph) {
|
|
2014
2015
|
const key = `${r.eph.prn}|${r.eph.tocDate.getTime()}`;
|
|
2015
2016
|
if (!seen.has(key)) {
|
|
@@ -2029,7 +2030,7 @@ function ephKey(e) {
|
|
|
2029
2030
|
const source = "source" in e ? e.source : "";
|
|
2030
2031
|
return `${e.prn}|${e.tocDate.getTime()}|${source}`;
|
|
2031
2032
|
}
|
|
2032
|
-
function decodeSbfNavigation(data) {
|
|
2033
|
+
function decodeSbfNavigation(data, opts = {}) {
|
|
2033
2034
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
2034
2035
|
const ephemerides = [];
|
|
2035
2036
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2150,7 +2151,7 @@ function decodeSbfNavigation(data) {
|
|
|
2150
2151
|
case 4020: {
|
|
2151
2152
|
if (len < 52) return;
|
|
2152
2153
|
counts.sbasRaw++;
|
|
2153
|
-
const r = feedGeoBlock(view, b);
|
|
2154
|
+
const r = feedGeoBlock(view, b, opts.onSbasMessage);
|
|
2154
2155
|
if (r.eph) addEph(r.eph);
|
|
2155
2156
|
else if (r.badCrc) counts.badFrames++;
|
|
2156
2157
|
return;
|
package/dist/sbf.d.cts
CHANGED
|
@@ -265,7 +265,11 @@ interface SbfNavigation {
|
|
|
265
265
|
* F/NAV, GLONASS strings, BeiDou D1/D2, and the Klobuchar/NeQuick iono +
|
|
266
266
|
* GPS-UTC leap-second blocks. See the module header for merge semantics.
|
|
267
267
|
*/
|
|
268
|
-
declare function decodeSbfNavigation(data: Uint8Array
|
|
268
|
+
declare function decodeSbfNavigation(data: Uint8Array, opts?: {
|
|
269
|
+
/** Called for every CRC-valid SBAS L1 message (all types) — feed an
|
|
270
|
+
* {@link ../positioning/sbas.SbasProcessor} to build SBAS corrections. */
|
|
271
|
+
onSbasMessage?: (msg: Uint8Array, prn: number, week: number, tow: number) => void;
|
|
272
|
+
}): SbfNavigation;
|
|
269
273
|
|
|
270
274
|
/**
|
|
271
275
|
* Septentrio SBF raw Galileo navigation-page blocks: GALRawINAV (4023)
|
|
@@ -372,7 +376,7 @@ declare function parseSbfGloNav(data: Uint8Array): SbfGloNavResult;
|
|
|
372
376
|
* message type 9. Returns the ephemeris, `{ badCrc: true }` on a failed
|
|
373
377
|
* CRC, or `{}` for any other (non–type-9) message.
|
|
374
378
|
*/
|
|
375
|
-
declare function feedGeoBlock(view: DataView, b: number): {
|
|
379
|
+
declare function feedGeoBlock(view: DataView, b: number, onSbasMessage?: (msg: Uint8Array, prn: number, week: number, tow: number) => void): {
|
|
376
380
|
eph?: GlonassEphemeris;
|
|
377
381
|
badCrc?: boolean;
|
|
378
382
|
};
|
|
@@ -391,7 +395,9 @@ interface SbfGeoNavResult {
|
|
|
391
395
|
* the same blocks through {@link feedGeoBlock}; this standalone parser
|
|
392
396
|
* mirrors the per-constellation `parseSbf*` helpers.
|
|
393
397
|
*/
|
|
394
|
-
declare function parseSbfGeoNav(data: Uint8Array
|
|
398
|
+
declare function parseSbfGeoNav(data: Uint8Array, opts?: {
|
|
399
|
+
onSbasMessage?: (msg: Uint8Array, prn: number, week: number, tow: number) => void;
|
|
400
|
+
}): SbfGeoNavResult;
|
|
395
401
|
|
|
396
402
|
/** Constellations HAS corrects: GPS (GNSS ID 0), Galileo (GNSS ID 2). */
|
|
397
403
|
type HasSystem = 'G' | 'E';
|
package/dist/sbf.d.ts
CHANGED
|
@@ -265,7 +265,11 @@ interface SbfNavigation {
|
|
|
265
265
|
* F/NAV, GLONASS strings, BeiDou D1/D2, and the Klobuchar/NeQuick iono +
|
|
266
266
|
* GPS-UTC leap-second blocks. See the module header for merge semantics.
|
|
267
267
|
*/
|
|
268
|
-
declare function decodeSbfNavigation(data: Uint8Array
|
|
268
|
+
declare function decodeSbfNavigation(data: Uint8Array, opts?: {
|
|
269
|
+
/** Called for every CRC-valid SBAS L1 message (all types) — feed an
|
|
270
|
+
* {@link ../positioning/sbas.SbasProcessor} to build SBAS corrections. */
|
|
271
|
+
onSbasMessage?: (msg: Uint8Array, prn: number, week: number, tow: number) => void;
|
|
272
|
+
}): SbfNavigation;
|
|
269
273
|
|
|
270
274
|
/**
|
|
271
275
|
* Septentrio SBF raw Galileo navigation-page blocks: GALRawINAV (4023)
|
|
@@ -372,7 +376,7 @@ declare function parseSbfGloNav(data: Uint8Array): SbfGloNavResult;
|
|
|
372
376
|
* message type 9. Returns the ephemeris, `{ badCrc: true }` on a failed
|
|
373
377
|
* CRC, or `{}` for any other (non–type-9) message.
|
|
374
378
|
*/
|
|
375
|
-
declare function feedGeoBlock(view: DataView, b: number): {
|
|
379
|
+
declare function feedGeoBlock(view: DataView, b: number, onSbasMessage?: (msg: Uint8Array, prn: number, week: number, tow: number) => void): {
|
|
376
380
|
eph?: GlonassEphemeris;
|
|
377
381
|
badCrc?: boolean;
|
|
378
382
|
};
|
|
@@ -391,7 +395,9 @@ interface SbfGeoNavResult {
|
|
|
391
395
|
* the same blocks through {@link feedGeoBlock}; this standalone parser
|
|
392
396
|
* mirrors the per-constellation `parseSbf*` helpers.
|
|
393
397
|
*/
|
|
394
|
-
declare function parseSbfGeoNav(data: Uint8Array
|
|
398
|
+
declare function parseSbfGeoNav(data: Uint8Array, opts?: {
|
|
399
|
+
onSbasMessage?: (msg: Uint8Array, prn: number, week: number, tow: number) => void;
|
|
400
|
+
}): SbfGeoNavResult;
|
|
395
401
|
|
|
396
402
|
/** Constellations HAS corrects: GPS (GNSS ID 0), Galileo (GNSS ID 2). */
|
|
397
403
|
type HasSystem = 'G' | 'E';
|
package/dist/sbf.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gnss-js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.52.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,
|