gnss-js 2.2.0 → 2.4.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-DWUQRJ37.js → chunk-B2XWO4RP.js} +337 -182
- package/dist/index.cjs +339 -181
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -1
- package/dist/positioning.cjs +339 -181
- package/dist/positioning.d.cts +117 -25
- package/dist/positioning.d.ts +117 -25
- package/dist/positioning.js +7 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -116,6 +116,7 @@ __export(src_exports, {
|
|
|
116
116
|
almanacEpochMs: () => almanacEpochMs,
|
|
117
117
|
almanacSatPosition: () => almanacSatPosition,
|
|
118
118
|
analyzeQuality: () => analyzeQuality,
|
|
119
|
+
antexFreqOfGroup: () => antexFreqOfGroup,
|
|
119
120
|
applyHelmert: () => applyHelmert,
|
|
120
121
|
applyIonoDcb: () => applyIonoDcb,
|
|
121
122
|
applyOrbitClock: () => applyOrbitClock,
|
|
@@ -127,6 +128,7 @@ __export(src_exports, {
|
|
|
127
128
|
buildGloChannelMap: () => buildGloChannelMap,
|
|
128
129
|
buildObsIndices: () => buildObsIndices,
|
|
129
130
|
buildPppAntenna: () => buildPppAntenna,
|
|
131
|
+
buildRtkAntenna: () => buildRtkAntenna,
|
|
130
132
|
clampUnit: () => clampUnit,
|
|
131
133
|
clkBias: () => clkBias,
|
|
132
134
|
computeAllPositions: () => computeAllPositions,
|
|
@@ -309,6 +311,7 @@ __export(src_exports, {
|
|
|
309
311
|
postProcessRtk: () => postProcessRtk,
|
|
310
312
|
rad2deg: () => rad2deg,
|
|
311
313
|
rangeRate: () => rangeRate,
|
|
314
|
+
rcvAntennaRangeM: () => rcvAntennaRangeM,
|
|
312
315
|
readString: () => readString,
|
|
313
316
|
reportDecodeError: () => reportDecodeError,
|
|
314
317
|
resetGloFreqCache: () => resetGloFreqCache,
|
|
@@ -13226,6 +13229,211 @@ function lambdaReduction(Q, n) {
|
|
|
13226
13229
|
return Z2;
|
|
13227
13230
|
}
|
|
13228
13231
|
|
|
13232
|
+
// src/positioning/ppp-antenna.ts
|
|
13233
|
+
function parseAntexTime(s) {
|
|
13234
|
+
const p = s.trim().split(/\s+/).map(Number);
|
|
13235
|
+
if (p.length < 3 || p.some((v) => Number.isNaN(v))) return null;
|
|
13236
|
+
return Date.UTC(
|
|
13237
|
+
p[0],
|
|
13238
|
+
(p[1] || 1) - 1,
|
|
13239
|
+
p[2] || 1,
|
|
13240
|
+
p[3] ?? 0,
|
|
13241
|
+
p[4] ?? 0,
|
|
13242
|
+
Math.floor(p[5] ?? 0)
|
|
13243
|
+
);
|
|
13244
|
+
}
|
|
13245
|
+
function freqOf(entry, code) {
|
|
13246
|
+
return entry.frequencies.find((f) => f.frequency === code);
|
|
13247
|
+
}
|
|
13248
|
+
function combineIf(fA, fB, f1, f2) {
|
|
13249
|
+
const g = f1 * f1 / (f1 * f1 - f2 * f2);
|
|
13250
|
+
const mm = 1e-3;
|
|
13251
|
+
const pco = [
|
|
13252
|
+
(g * fA.pcoN - (g - 1) * fB.pcoN) * mm,
|
|
13253
|
+
(g * fA.pcoE - (g - 1) * fB.pcoE) * mm,
|
|
13254
|
+
(g * fA.pcoU - (g - 1) * fB.pcoU) * mm
|
|
13255
|
+
];
|
|
13256
|
+
const nz = Math.min(fA.pcvNoazi.length, fB.pcvNoazi.length);
|
|
13257
|
+
const pcvNoazi = new Array(nz);
|
|
13258
|
+
for (let i = 0; i < nz; i++) {
|
|
13259
|
+
pcvNoazi[i] = (g * fA.pcvNoazi[i] - (g - 1) * fB.pcvNoazi[i]) * mm;
|
|
13260
|
+
}
|
|
13261
|
+
return { pco, pcvZen1Deg: 0, pcvDzenDeg: 0, pcvNoazi };
|
|
13262
|
+
}
|
|
13263
|
+
function buildPppAntenna(antex) {
|
|
13264
|
+
const sats = /* @__PURE__ */ new Map();
|
|
13265
|
+
const rcvs = /* @__PURE__ */ new Map();
|
|
13266
|
+
for (const a of antex.antennas) {
|
|
13267
|
+
if (a.isSatellite) {
|
|
13268
|
+
const list = sats.get(a.serialNo) ?? [];
|
|
13269
|
+
list.push(a);
|
|
13270
|
+
sats.set(a.serialNo, list);
|
|
13271
|
+
} else if (!rcvs.has(a.type)) {
|
|
13272
|
+
rcvs.set(a.type, a);
|
|
13273
|
+
}
|
|
13274
|
+
}
|
|
13275
|
+
const withZen = (entry, off) => ({
|
|
13276
|
+
...off,
|
|
13277
|
+
pcvZen1Deg: entry.zen1,
|
|
13278
|
+
pcvDzenDeg: entry.dzen
|
|
13279
|
+
});
|
|
13280
|
+
return {
|
|
13281
|
+
satOffset(prn, dateMs, band1, band2, f1, f2) {
|
|
13282
|
+
const list = sats.get(prn);
|
|
13283
|
+
if (!list) return null;
|
|
13284
|
+
const entry = list.find((e) => {
|
|
13285
|
+
const from = parseAntexTime(e.validFrom);
|
|
13286
|
+
const until = parseAntexTime(e.validUntil);
|
|
13287
|
+
return (from == null || dateMs >= from) && (until == null || dateMs <= until);
|
|
13288
|
+
});
|
|
13289
|
+
if (!entry) return null;
|
|
13290
|
+
const a = freqOf(entry, band1);
|
|
13291
|
+
const b = freqOf(entry, band2);
|
|
13292
|
+
if (!a || !b) return null;
|
|
13293
|
+
return withZen(entry, combineIf(a, b, f1, f2));
|
|
13294
|
+
},
|
|
13295
|
+
rcvOffset(antType, band1, band2, f1, f2) {
|
|
13296
|
+
const entry = rcvs.get(antType);
|
|
13297
|
+
if (!entry) return null;
|
|
13298
|
+
const a = freqOf(entry, band1);
|
|
13299
|
+
const b = freqOf(entry, band2);
|
|
13300
|
+
if (!a || !b) return null;
|
|
13301
|
+
return withZen(entry, combineIf(a, b, f1, f2));
|
|
13302
|
+
}
|
|
13303
|
+
};
|
|
13304
|
+
}
|
|
13305
|
+
function cross(a, b) {
|
|
13306
|
+
return [
|
|
13307
|
+
a[1] * b[2] - a[2] * b[1],
|
|
13308
|
+
a[2] * b[0] - a[0] * b[2],
|
|
13309
|
+
a[0] * b[1] - a[1] * b[0]
|
|
13310
|
+
];
|
|
13311
|
+
}
|
|
13312
|
+
function norm(a) {
|
|
13313
|
+
const n = Math.hypot(a[0], a[1], a[2]) || 1;
|
|
13314
|
+
return [a[0] / n, a[1] / n, a[2] / n];
|
|
13315
|
+
}
|
|
13316
|
+
function dot(a, b) {
|
|
13317
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
13318
|
+
}
|
|
13319
|
+
function satBodyFrame(satEcef, sunEcef2) {
|
|
13320
|
+
const z = norm([-satEcef[0], -satEcef[1], -satEcef[2]]);
|
|
13321
|
+
const eSun = norm([
|
|
13322
|
+
sunEcef2[0] - satEcef[0],
|
|
13323
|
+
sunEcef2[1] - satEcef[1],
|
|
13324
|
+
sunEcef2[2] - satEcef[2]
|
|
13325
|
+
]);
|
|
13326
|
+
const y = norm(cross(z, eSun));
|
|
13327
|
+
const x = norm(cross(y, z));
|
|
13328
|
+
return { x, y, z };
|
|
13329
|
+
}
|
|
13330
|
+
function satPcoToEcef(pcoBody, frame) {
|
|
13331
|
+
return [
|
|
13332
|
+
pcoBody[0] * frame.x[0] + pcoBody[1] * frame.y[0] + pcoBody[2] * frame.z[0],
|
|
13333
|
+
pcoBody[0] * frame.x[1] + pcoBody[1] * frame.y[1] + pcoBody[2] * frame.z[1],
|
|
13334
|
+
pcoBody[0] * frame.x[2] + pcoBody[1] * frame.y[2] + pcoBody[2] * frame.z[2]
|
|
13335
|
+
];
|
|
13336
|
+
}
|
|
13337
|
+
function enuToEcef(enu, latRad, lonRad) {
|
|
13338
|
+
const sl = Math.sin(latRad);
|
|
13339
|
+
const cl = Math.cos(latRad);
|
|
13340
|
+
const so = Math.sin(lonRad);
|
|
13341
|
+
const co = Math.cos(lonRad);
|
|
13342
|
+
const [e, n, u] = enu;
|
|
13343
|
+
return [
|
|
13344
|
+
-so * e - sl * co * n + cl * co * u,
|
|
13345
|
+
co * e - sl * so * n + cl * so * u,
|
|
13346
|
+
cl * n + sl * u
|
|
13347
|
+
];
|
|
13348
|
+
}
|
|
13349
|
+
function interpPcv(off, angleDeg) {
|
|
13350
|
+
const { pcvZen1Deg, pcvDzenDeg, pcvNoazi } = off;
|
|
13351
|
+
if (pcvDzenDeg <= 0 || pcvNoazi.length < 2) return 0;
|
|
13352
|
+
const x = (angleDeg - pcvZen1Deg) / pcvDzenDeg;
|
|
13353
|
+
if (x <= 0) return pcvNoazi[0];
|
|
13354
|
+
if (x >= pcvNoazi.length - 1) return pcvNoazi[pcvNoazi.length - 1];
|
|
13355
|
+
const i = Math.floor(x);
|
|
13356
|
+
const t = x - i;
|
|
13357
|
+
return pcvNoazi[i] * (1 - t) + pcvNoazi[i + 1] * t;
|
|
13358
|
+
}
|
|
13359
|
+
function phaseWindup(prn, losRcvToSat, satFrame, latRad, lonRad, state) {
|
|
13360
|
+
const k = [
|
|
13361
|
+
-losRcvToSat[0],
|
|
13362
|
+
-losRcvToSat[1],
|
|
13363
|
+
-losRcvToSat[2]
|
|
13364
|
+
];
|
|
13365
|
+
const kx = satFrame.x;
|
|
13366
|
+
const ky = satFrame.y;
|
|
13367
|
+
const kdotx = dot(k, kx);
|
|
13368
|
+
const kcrossY = cross(k, ky);
|
|
13369
|
+
const dS = [
|
|
13370
|
+
kx[0] - k[0] * kdotx - kcrossY[0],
|
|
13371
|
+
kx[1] - k[1] * kdotx - kcrossY[1],
|
|
13372
|
+
kx[2] - k[2] * kdotx - kcrossY[2]
|
|
13373
|
+
];
|
|
13374
|
+
const north = enuToEcef([0, 1, 0], latRad, lonRad);
|
|
13375
|
+
const east = enuToEcef([1, 0, 0], latRad, lonRad);
|
|
13376
|
+
const kdotn = dot(k, north);
|
|
13377
|
+
const kcrossE = cross(k, east);
|
|
13378
|
+
const dR = [
|
|
13379
|
+
north[0] - k[0] * kdotn + kcrossE[0],
|
|
13380
|
+
north[1] - k[1] * kdotn + kcrossE[1],
|
|
13381
|
+
north[2] - k[2] * kdotn + kcrossE[2]
|
|
13382
|
+
];
|
|
13383
|
+
const nS = Math.hypot(dS[0], dS[1], dS[2]) || 1;
|
|
13384
|
+
const nR = Math.hypot(dR[0], dR[1], dR[2]) || 1;
|
|
13385
|
+
let cosPhi = dot(dS, dR) / (nS * nR);
|
|
13386
|
+
cosPhi = Math.max(-1, Math.min(1, cosPhi));
|
|
13387
|
+
const zeta = cross(dS, dR);
|
|
13388
|
+
const sign = dot(k, zeta) >= 0 ? 1 : -1;
|
|
13389
|
+
const dphi = sign * Math.acos(cosPhi) / (2 * Math.PI);
|
|
13390
|
+
const prev = state.get(prn);
|
|
13391
|
+
const wu = prev == null ? dphi : dphi + Math.round(prev - dphi);
|
|
13392
|
+
state.set(prn, wu);
|
|
13393
|
+
return wu;
|
|
13394
|
+
}
|
|
13395
|
+
|
|
13396
|
+
// src/positioning/rtk-antenna.ts
|
|
13397
|
+
function freqOf2(entry, code) {
|
|
13398
|
+
return entry.frequencies.find((f) => f.frequency === code);
|
|
13399
|
+
}
|
|
13400
|
+
function buildRtkAntenna(antex) {
|
|
13401
|
+
const rcvs = /* @__PURE__ */ new Map();
|
|
13402
|
+
for (const a of antex.antennas) {
|
|
13403
|
+
if (!a.isSatellite && !rcvs.has(a.type)) rcvs.set(a.type, a);
|
|
13404
|
+
}
|
|
13405
|
+
const mm = 1e-3;
|
|
13406
|
+
return {
|
|
13407
|
+
rcvOffset(antType, freq) {
|
|
13408
|
+
const entry = rcvs.get(antType);
|
|
13409
|
+
if (!entry) return null;
|
|
13410
|
+
const f = freqOf2(entry, freq);
|
|
13411
|
+
if (!f) return null;
|
|
13412
|
+
return {
|
|
13413
|
+
pco: [f.pcoN * mm, f.pcoE * mm, f.pcoU * mm],
|
|
13414
|
+
pcvZen1Deg: entry.zen1,
|
|
13415
|
+
pcvDzenDeg: entry.dzen,
|
|
13416
|
+
pcvNoazi: f.pcvNoazi.map((v) => v * mm)
|
|
13417
|
+
};
|
|
13418
|
+
}
|
|
13419
|
+
};
|
|
13420
|
+
}
|
|
13421
|
+
function rcvAntennaRangeM(off, losRcvToSat, latRad, lonRad, elRad, antDeltaEnu = [0, 0, 0]) {
|
|
13422
|
+
const total = [
|
|
13423
|
+
off.pco[0] + antDeltaEnu[0],
|
|
13424
|
+
off.pco[1] + antDeltaEnu[1],
|
|
13425
|
+
off.pco[2] + antDeltaEnu[2]
|
|
13426
|
+
];
|
|
13427
|
+
const ecef = enuToEcef(total, latRad, lonRad);
|
|
13428
|
+
let m = -dot(losRcvToSat, ecef);
|
|
13429
|
+
const zenithDeg = 90 - elRad * 180 / Math.PI;
|
|
13430
|
+
m += interpPcv(off, zenithDeg);
|
|
13431
|
+
return m;
|
|
13432
|
+
}
|
|
13433
|
+
function antexFreqOfGroup(group) {
|
|
13434
|
+
return `${group[0]}0${group[1]}`;
|
|
13435
|
+
}
|
|
13436
|
+
|
|
13229
13437
|
// src/positioning/rtk.ts
|
|
13230
13438
|
var L1_CODES = {
|
|
13231
13439
|
G: ["1C"],
|
|
@@ -13291,8 +13499,16 @@ function resolveEphemeris(src, prn, timeMs) {
|
|
|
13291
13499
|
return selectEphemeris(src, prn, timeMs);
|
|
13292
13500
|
return src.get(prn) ?? null;
|
|
13293
13501
|
}
|
|
13294
|
-
|
|
13502
|
+
var ZERO_DELTA = [0, 0, 0];
|
|
13503
|
+
function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, troposphere, antenna = null) {
|
|
13295
13504
|
const out = [];
|
|
13505
|
+
let baseLatRad = 0;
|
|
13506
|
+
let baseLonRad = 0;
|
|
13507
|
+
if (antenna?.base) {
|
|
13508
|
+
const [latDeg, lonDeg] = ecefToGeodetic(basePos[0], basePos[1], basePos[2]);
|
|
13509
|
+
baseLatRad = latDeg * Math.PI / 180;
|
|
13510
|
+
baseLonRad = lonDeg * Math.PI / 180;
|
|
13511
|
+
}
|
|
13296
13512
|
for (const [prn, mR] of rover) {
|
|
13297
13513
|
const sys = prn[0];
|
|
13298
13514
|
if (!"GERCJ".includes(sys)) continue;
|
|
@@ -13317,14 +13533,43 @@ function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, tropo
|
|
|
13317
13533
|
satB.z - basePos[2]
|
|
13318
13534
|
) / C_LIGHT;
|
|
13319
13535
|
const [bx, by, bz] = sagnac(satB, travelB);
|
|
13320
|
-
|
|
13536
|
+
let rhoB = Math.hypot(bx - basePos[0], by - basePos[1], bz - basePos[2]);
|
|
13321
13537
|
const elB = ecefToAzEl(basePos[0], basePos[1], basePos[2], bx, by, bz).el;
|
|
13322
13538
|
if (elB < maskRad) continue;
|
|
13539
|
+
const group = sys + mR.code;
|
|
13540
|
+
let offR = null;
|
|
13541
|
+
let roverDelta = ZERO_DELTA;
|
|
13542
|
+
if (antenna) {
|
|
13543
|
+
const antexFreq = antexFreqOfGroup(group);
|
|
13544
|
+
if (antenna.base) {
|
|
13545
|
+
const offB = antenna.model.rcvOffset(antenna.base.type, antexFreq);
|
|
13546
|
+
if (offB) {
|
|
13547
|
+
const inv = 1 / rhoB;
|
|
13548
|
+
const losB = [
|
|
13549
|
+
(bx - basePos[0]) * inv,
|
|
13550
|
+
(by - basePos[1]) * inv,
|
|
13551
|
+
(bz - basePos[2]) * inv
|
|
13552
|
+
];
|
|
13553
|
+
rhoB += rcvAntennaRangeM(
|
|
13554
|
+
offB,
|
|
13555
|
+
losB,
|
|
13556
|
+
baseLatRad,
|
|
13557
|
+
baseLonRad,
|
|
13558
|
+
elB,
|
|
13559
|
+
antenna.base.deltaEnu
|
|
13560
|
+
);
|
|
13561
|
+
}
|
|
13562
|
+
}
|
|
13563
|
+
if (antenna.rover) {
|
|
13564
|
+
offR = antenna.model.rcvOffset(antenna.rover.type, antexFreq);
|
|
13565
|
+
roverDelta = antenna.rover.deltaEnu ?? ZERO_DELTA;
|
|
13566
|
+
}
|
|
13567
|
+
}
|
|
13323
13568
|
const gloK = sys === "R" ? mR.gloChannel ?? mB.gloChannel ?? eph.freqNum ?? null : null;
|
|
13324
13569
|
const freq = carrierFreqHz(sys, mR.code, gloK);
|
|
13325
13570
|
out.push({
|
|
13326
13571
|
prn,
|
|
13327
|
-
group
|
|
13572
|
+
group,
|
|
13328
13573
|
lambda: freq > 0 ? C_LIGHT / freq : 0,
|
|
13329
13574
|
satR,
|
|
13330
13575
|
rhoB,
|
|
@@ -13335,7 +13580,9 @@ function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, tropo
|
|
|
13335
13580
|
cpR: mR.cp ?? null,
|
|
13336
13581
|
cpB: mB.cp ?? null,
|
|
13337
13582
|
lockR: mR.lockTimeMs,
|
|
13338
|
-
lockB: mB.lockTimeMs
|
|
13583
|
+
lockB: mB.lockTimeMs,
|
|
13584
|
+
offR,
|
|
13585
|
+
roverDelta
|
|
13339
13586
|
});
|
|
13340
13587
|
}
|
|
13341
13588
|
return out;
|
|
@@ -13343,17 +13590,29 @@ function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, tropo
|
|
|
13343
13590
|
function roverTerms(g, x, y, z, troposphere) {
|
|
13344
13591
|
const travel = Math.hypot(g.satR.x - x, g.satR.y - y, g.satR.z - z) / C_LIGHT;
|
|
13345
13592
|
const [sx, sy, sz] = sagnac(g.satR, travel);
|
|
13346
|
-
|
|
13593
|
+
let rho = Math.hypot(sx - x, sy - y, sz - z);
|
|
13347
13594
|
const u = [
|
|
13348
13595
|
(x - sx) / rho,
|
|
13349
13596
|
(y - sy) / rho,
|
|
13350
13597
|
(z - sz) / rho
|
|
13351
13598
|
];
|
|
13599
|
+
const needEl = troposphere || g.offR != null;
|
|
13600
|
+
const elR = needEl ? ecefToAzEl(x, y, z, sx, sy, sz).el : 0;
|
|
13352
13601
|
let dTropo = 0;
|
|
13353
13602
|
if (troposphere) {
|
|
13354
|
-
const elR = ecefToAzEl(x, y, z, sx, sy, sz).el;
|
|
13355
13603
|
dTropo = tropoDelay(Math.max(elR, 0.05)) - g.tropoB;
|
|
13356
13604
|
}
|
|
13605
|
+
if (g.offR) {
|
|
13606
|
+
const [latDeg, lonDeg] = ecefToGeodetic(x, y, z);
|
|
13607
|
+
rho += rcvAntennaRangeM(
|
|
13608
|
+
g.offR,
|
|
13609
|
+
[-u[0], -u[1], -u[2]],
|
|
13610
|
+
latDeg * Math.PI / 180,
|
|
13611
|
+
lonDeg * Math.PI / 180,
|
|
13612
|
+
elR,
|
|
13613
|
+
g.roverDelta
|
|
13614
|
+
);
|
|
13615
|
+
}
|
|
13357
13616
|
return { rho, u, dTropo };
|
|
13358
13617
|
}
|
|
13359
13618
|
function sdVariance(sigmaM, elRad) {
|
|
@@ -13592,7 +13851,8 @@ var RtkFloatEngine = class {
|
|
|
13592
13851
|
arElevationMaskDeg: opts.arElevationMaskDeg ?? 0,
|
|
13593
13852
|
glonassAr: opts.glonassAr ?? false,
|
|
13594
13853
|
partialFixing: opts.partialFixing ?? true,
|
|
13595
|
-
minFixAmbiguities: opts.minFixAmbiguities ?? 4
|
|
13854
|
+
minFixAmbiguities: opts.minFixAmbiguities ?? 4,
|
|
13855
|
+
antenna: opts.antenna ?? null
|
|
13596
13856
|
};
|
|
13597
13857
|
}
|
|
13598
13858
|
/** Clear all filter state (position, ambiguities, lock history). */
|
|
@@ -13715,7 +13975,8 @@ var RtkFloatEngine = class {
|
|
|
13715
13975
|
this.ephemerides,
|
|
13716
13976
|
timeMs,
|
|
13717
13977
|
o.elevationMaskDeg * Math.PI / 180,
|
|
13718
|
-
o.troposphere
|
|
13978
|
+
o.troposphere,
|
|
13979
|
+
o.antenna
|
|
13719
13980
|
);
|
|
13720
13981
|
const byPrn = new Map(geom.map((g) => [g.prn, g]));
|
|
13721
13982
|
const dtS = this.lastMs !== null ? (timeMs - this.lastMs) / 1e3 : 0;
|
|
@@ -14315,6 +14576,10 @@ var SbasProcessor = class {
|
|
|
14315
14576
|
tlat = 0;
|
|
14316
14577
|
degr = null;
|
|
14317
14578
|
mt27 = null;
|
|
14579
|
+
/** MT28 clock-ephemeris covariance per PRN: the upper-triangular Cholesky
|
|
14580
|
+
* factor E (10 elements [E11,E22,E33,E44,E12,E13,E14,E23,E24,E34]) and its
|
|
14581
|
+
* scale exponent. Keyed by PRN; cleared on a mask change. */
|
|
14582
|
+
mt28 = /* @__PURE__ */ new Map();
|
|
14318
14583
|
ion = [];
|
|
14319
14584
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
14320
14585
|
geoPrns = /* @__PURE__ */ new Set();
|
|
@@ -14345,6 +14610,8 @@ var SbasProcessor = class {
|
|
|
14345
14610
|
return this.decodeMt10(msg) ? type : -1;
|
|
14346
14611
|
case 27:
|
|
14347
14612
|
return this.decodeMt27(msg) ? type : -1;
|
|
14613
|
+
case 28:
|
|
14614
|
+
return this.decodeMt28(msg) ? type : -1;
|
|
14348
14615
|
case 18:
|
|
14349
14616
|
return this.decodeIgpMask(msg) ? type : -1;
|
|
14350
14617
|
case 24:
|
|
@@ -14371,6 +14638,7 @@ var SbasProcessor = class {
|
|
|
14371
14638
|
return true;
|
|
14372
14639
|
this.iodp = iodp;
|
|
14373
14640
|
this.sats = prns.map((prn) => ({ prn }));
|
|
14641
|
+
this.mt28.clear();
|
|
14374
14642
|
this.satIdx.clear();
|
|
14375
14643
|
this.sats.forEach((s, k) => {
|
|
14376
14644
|
if (s.prn) this.satIdx.set(s.prn, k);
|
|
@@ -14436,7 +14704,9 @@ var SbasProcessor = class {
|
|
|
14436
14704
|
iiono: u(117, 9) || 1,
|
|
14437
14705
|
cionoRamp: u(126, 10) * 5e-6,
|
|
14438
14706
|
rssUdre: u(136, 1) === 1,
|
|
14439
|
-
rssIono: u(137, 1) === 1
|
|
14707
|
+
rssIono: u(137, 1) === 1,
|
|
14708
|
+
ccovariance: u(138, 7) * 0.1
|
|
14709
|
+
// Table B-49: 0..12.7 m, 0.1 m
|
|
14440
14710
|
};
|
|
14441
14711
|
return true;
|
|
14442
14712
|
}
|
|
@@ -14488,6 +14758,51 @@ var SbasProcessor = class {
|
|
|
14488
14758
|
}
|
|
14489
14759
|
return best ?? DELTA_UDRE[s.udreOutside] ?? 1;
|
|
14490
14760
|
}
|
|
14761
|
+
/** MT28 clock-ephemeris covariance (ICAO Annex 10 Vol I §3.5.4.7 / Table B-51):
|
|
14762
|
+
* two satellites per message, each an upper-triangular Cholesky factor E and a
|
|
14763
|
+
* scale exponent that together define the relative clock-ephemeris covariance
|
|
14764
|
+
* used for a geometry-dependent δUDRE (§3.5.5.6.2.5). Slots are indexed into
|
|
14765
|
+
* the current MT1 mask, so a mismatched IODP is dropped. */
|
|
14766
|
+
decodeMt28(msg) {
|
|
14767
|
+
if (this.iodp !== getBitU(msg, 14, 2)) return false;
|
|
14768
|
+
for (const base of [16, 121]) {
|
|
14769
|
+
const slot = getBitU(msg, base, 6);
|
|
14770
|
+
const prn = this.sats[slot]?.prn;
|
|
14771
|
+
const scaleExp = getBitU(msg, base + 6, 3);
|
|
14772
|
+
const e = [
|
|
14773
|
+
getBitU(msg, base + 9, 9),
|
|
14774
|
+
getBitU(msg, base + 18, 9),
|
|
14775
|
+
getBitU(msg, base + 27, 9),
|
|
14776
|
+
getBitU(msg, base + 36, 9),
|
|
14777
|
+
getBitS(msg, base + 45, 10),
|
|
14778
|
+
getBitS(msg, base + 55, 10),
|
|
14779
|
+
getBitS(msg, base + 65, 10),
|
|
14780
|
+
getBitS(msg, base + 75, 10),
|
|
14781
|
+
getBitS(msg, base + 85, 10),
|
|
14782
|
+
getBitS(msg, base + 95, 10)
|
|
14783
|
+
];
|
|
14784
|
+
if (prn && e.some((v) => v !== 0)) this.mt28.set(prn, { scaleExp, e });
|
|
14785
|
+
}
|
|
14786
|
+
return true;
|
|
14787
|
+
}
|
|
14788
|
+
/** Geometry-dependent δUDRE multiplier from MT28 (§3.5.5.6.2.5):
|
|
14789
|
+
* δUDRE = √(Iᵀ·C·I) + ε_c, with C = Rᵀ·R, R = E·2^(scaleExp−5),
|
|
14790
|
+
* I = [î_user→sat, 1], ε_c = C_covariance·2^(scaleExp−5). Since C = RᵀR,
|
|
14791
|
+
* Iᵀ·C·I = ‖R·I‖², so we form R·I directly. Null when no MT28 for `prn`. */
|
|
14792
|
+
mt28DeltaUdre(prn, los) {
|
|
14793
|
+
const m = this.mt28.get(prn);
|
|
14794
|
+
if (!m) return null;
|
|
14795
|
+
const sf = 2 ** (m.scaleExp - 5);
|
|
14796
|
+
const [e11, e22, e33, e44, e12, e13, e14, e23, e24, e34] = m.e;
|
|
14797
|
+
const [ix, iy, iz] = los;
|
|
14798
|
+
const v0 = (e11 * ix + e12 * iy + e13 * iz + e14) * sf;
|
|
14799
|
+
const v1 = (e22 * iy + e23 * iz + e24) * sf;
|
|
14800
|
+
const v2 = (e33 * iz + e34) * sf;
|
|
14801
|
+
const v3 = e44 * sf;
|
|
14802
|
+
const iCi = v0 * v0 + v1 * v1 + v2 * v2 + v3 * v3;
|
|
14803
|
+
const ec = (this.degr?.ccovariance ?? 0) * sf;
|
|
14804
|
+
return Math.sqrt(iCi) + ec;
|
|
14805
|
+
}
|
|
14491
14806
|
decodeMixed(msg, week, tow) {
|
|
14492
14807
|
if (this.iodp !== getBitU(msg, 110, 2)) return false;
|
|
14493
14808
|
const blk = getBitU(msg, 112, 2);
|
|
@@ -14692,7 +15007,7 @@ var SbasProcessor = class {
|
|
|
14692
15007
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
14693
15008
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
14694
15009
|
*/
|
|
14695
|
-
satCorrection(prn, week, tow, userLatRad, userLonRad) {
|
|
15010
|
+
satCorrection(prn, week, tow, userLatRad, userLonRad, losUnitEcef) {
|
|
14696
15011
|
const idx = this.satIdx.get(prn);
|
|
14697
15012
|
if (idx === void 0) return null;
|
|
14698
15013
|
const s = this.sats[idx];
|
|
@@ -14714,7 +15029,8 @@ var SbasProcessor = class {
|
|
|
14714
15029
|
const tf = now - s.fcorr.t0s + this.tlat;
|
|
14715
15030
|
if (Math.abs(tf) > MAXSBSAGEF || s.fcorr.udre >= 15) return null;
|
|
14716
15031
|
const prc = s.fcorr.prc;
|
|
14717
|
-
const
|
|
15032
|
+
const mt28 = losUnitEcef ? this.mt28DeltaUdre(prn, losUnitEcef) : null;
|
|
15033
|
+
const dudre = mt28 != null ? mt28 : userLatRad != null && userLonRad != null ? this.deltaUdre(
|
|
14718
15034
|
userLatRad * 180 / Math.PI,
|
|
14719
15035
|
userLonRad * 180 / Math.PI
|
|
14720
15036
|
) : 1;
|
|
@@ -14843,14 +15159,14 @@ var SbasProcessor = class {
|
|
|
14843
15159
|
};
|
|
14844
15160
|
|
|
14845
15161
|
// src/positioning/ssr-apply.ts
|
|
14846
|
-
var
|
|
15162
|
+
var cross2 = (a, b) => [
|
|
14847
15163
|
a[1] * b[2] - a[2] * b[1],
|
|
14848
15164
|
a[2] * b[0] - a[0] * b[2],
|
|
14849
15165
|
a[0] * b[1] - a[1] * b[0]
|
|
14850
15166
|
];
|
|
14851
|
-
var
|
|
15167
|
+
var norm2 = (a) => Math.hypot(a[0], a[1], a[2]);
|
|
14852
15168
|
var unit = (a) => {
|
|
14853
|
-
const n =
|
|
15169
|
+
const n = norm2(a);
|
|
14854
15170
|
return n > 0 ? [a[0] / n, a[1] / n, a[2] / n] : [0, 0, 0];
|
|
14855
15171
|
};
|
|
14856
15172
|
function applyOrbitClock(eph, corr, timeMs) {
|
|
@@ -14864,8 +15180,8 @@ function applyOrbitClock(eph, corr, timeMs) {
|
|
|
14864
15180
|
const p = [s.x, s.y, s.z];
|
|
14865
15181
|
const v = [s.vx, s.vy, s.vz];
|
|
14866
15182
|
const er = unit(p);
|
|
14867
|
-
const en = unit(
|
|
14868
|
-
const ea =
|
|
15183
|
+
const en = unit(cross2(p, v));
|
|
15184
|
+
const ea = cross2(en, er);
|
|
14869
15185
|
const dr = corr.radial + (corr.dotRadial ?? 0) * age;
|
|
14870
15186
|
const da = corr.along + (corr.dotAlong ?? 0) * age;
|
|
14871
15187
|
const dc = corr.cross + (corr.dotCross ?? 0) * age;
|
|
@@ -15075,170 +15391,6 @@ function solidEarthTide(rcv, sun, moon) {
|
|
|
15075
15391
|
return disp;
|
|
15076
15392
|
}
|
|
15077
15393
|
|
|
15078
|
-
// src/positioning/ppp-antenna.ts
|
|
15079
|
-
function parseAntexTime(s) {
|
|
15080
|
-
const p = s.trim().split(/\s+/).map(Number);
|
|
15081
|
-
if (p.length < 3 || p.some((v) => Number.isNaN(v))) return null;
|
|
15082
|
-
return Date.UTC(
|
|
15083
|
-
p[0],
|
|
15084
|
-
(p[1] || 1) - 1,
|
|
15085
|
-
p[2] || 1,
|
|
15086
|
-
p[3] ?? 0,
|
|
15087
|
-
p[4] ?? 0,
|
|
15088
|
-
Math.floor(p[5] ?? 0)
|
|
15089
|
-
);
|
|
15090
|
-
}
|
|
15091
|
-
function freqOf(entry, code) {
|
|
15092
|
-
return entry.frequencies.find((f) => f.frequency === code);
|
|
15093
|
-
}
|
|
15094
|
-
function combineIf(fA, fB, f1, f2) {
|
|
15095
|
-
const g = f1 * f1 / (f1 * f1 - f2 * f2);
|
|
15096
|
-
const mm = 1e-3;
|
|
15097
|
-
const pco = [
|
|
15098
|
-
(g * fA.pcoN - (g - 1) * fB.pcoN) * mm,
|
|
15099
|
-
(g * fA.pcoE - (g - 1) * fB.pcoE) * mm,
|
|
15100
|
-
(g * fA.pcoU - (g - 1) * fB.pcoU) * mm
|
|
15101
|
-
];
|
|
15102
|
-
const nz = Math.min(fA.pcvNoazi.length, fB.pcvNoazi.length);
|
|
15103
|
-
const pcvNoazi = new Array(nz);
|
|
15104
|
-
for (let i = 0; i < nz; i++) {
|
|
15105
|
-
pcvNoazi[i] = (g * fA.pcvNoazi[i] - (g - 1) * fB.pcvNoazi[i]) * mm;
|
|
15106
|
-
}
|
|
15107
|
-
return { pco, pcvZen1Deg: 0, pcvDzenDeg: 0, pcvNoazi };
|
|
15108
|
-
}
|
|
15109
|
-
function buildPppAntenna(antex) {
|
|
15110
|
-
const sats = /* @__PURE__ */ new Map();
|
|
15111
|
-
const rcvs = /* @__PURE__ */ new Map();
|
|
15112
|
-
for (const a of antex.antennas) {
|
|
15113
|
-
if (a.isSatellite) {
|
|
15114
|
-
const list = sats.get(a.serialNo) ?? [];
|
|
15115
|
-
list.push(a);
|
|
15116
|
-
sats.set(a.serialNo, list);
|
|
15117
|
-
} else if (!rcvs.has(a.type)) {
|
|
15118
|
-
rcvs.set(a.type, a);
|
|
15119
|
-
}
|
|
15120
|
-
}
|
|
15121
|
-
const withZen = (entry, off) => ({
|
|
15122
|
-
...off,
|
|
15123
|
-
pcvZen1Deg: entry.zen1,
|
|
15124
|
-
pcvDzenDeg: entry.dzen
|
|
15125
|
-
});
|
|
15126
|
-
return {
|
|
15127
|
-
satOffset(prn, dateMs, band1, band2, f1, f2) {
|
|
15128
|
-
const list = sats.get(prn);
|
|
15129
|
-
if (!list) return null;
|
|
15130
|
-
const entry = list.find((e) => {
|
|
15131
|
-
const from = parseAntexTime(e.validFrom);
|
|
15132
|
-
const until = parseAntexTime(e.validUntil);
|
|
15133
|
-
return (from == null || dateMs >= from) && (until == null || dateMs <= until);
|
|
15134
|
-
});
|
|
15135
|
-
if (!entry) return null;
|
|
15136
|
-
const a = freqOf(entry, band1);
|
|
15137
|
-
const b = freqOf(entry, band2);
|
|
15138
|
-
if (!a || !b) return null;
|
|
15139
|
-
return withZen(entry, combineIf(a, b, f1, f2));
|
|
15140
|
-
},
|
|
15141
|
-
rcvOffset(antType, band1, band2, f1, f2) {
|
|
15142
|
-
const entry = rcvs.get(antType);
|
|
15143
|
-
if (!entry) return null;
|
|
15144
|
-
const a = freqOf(entry, band1);
|
|
15145
|
-
const b = freqOf(entry, band2);
|
|
15146
|
-
if (!a || !b) return null;
|
|
15147
|
-
return withZen(entry, combineIf(a, b, f1, f2));
|
|
15148
|
-
}
|
|
15149
|
-
};
|
|
15150
|
-
}
|
|
15151
|
-
function cross2(a, b) {
|
|
15152
|
-
return [
|
|
15153
|
-
a[1] * b[2] - a[2] * b[1],
|
|
15154
|
-
a[2] * b[0] - a[0] * b[2],
|
|
15155
|
-
a[0] * b[1] - a[1] * b[0]
|
|
15156
|
-
];
|
|
15157
|
-
}
|
|
15158
|
-
function norm2(a) {
|
|
15159
|
-
const n = Math.hypot(a[0], a[1], a[2]) || 1;
|
|
15160
|
-
return [a[0] / n, a[1] / n, a[2] / n];
|
|
15161
|
-
}
|
|
15162
|
-
function dot(a, b) {
|
|
15163
|
-
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
15164
|
-
}
|
|
15165
|
-
function satBodyFrame(satEcef, sunEcef2) {
|
|
15166
|
-
const z = norm2([-satEcef[0], -satEcef[1], -satEcef[2]]);
|
|
15167
|
-
const eSun = norm2([
|
|
15168
|
-
sunEcef2[0] - satEcef[0],
|
|
15169
|
-
sunEcef2[1] - satEcef[1],
|
|
15170
|
-
sunEcef2[2] - satEcef[2]
|
|
15171
|
-
]);
|
|
15172
|
-
const y = norm2(cross2(z, eSun));
|
|
15173
|
-
const x = norm2(cross2(y, z));
|
|
15174
|
-
return { x, y, z };
|
|
15175
|
-
}
|
|
15176
|
-
function satPcoToEcef(pcoBody, frame) {
|
|
15177
|
-
return [
|
|
15178
|
-
pcoBody[0] * frame.x[0] + pcoBody[1] * frame.y[0] + pcoBody[2] * frame.z[0],
|
|
15179
|
-
pcoBody[0] * frame.x[1] + pcoBody[1] * frame.y[1] + pcoBody[2] * frame.z[1],
|
|
15180
|
-
pcoBody[0] * frame.x[2] + pcoBody[1] * frame.y[2] + pcoBody[2] * frame.z[2]
|
|
15181
|
-
];
|
|
15182
|
-
}
|
|
15183
|
-
function enuToEcef(enu, latRad, lonRad) {
|
|
15184
|
-
const sl = Math.sin(latRad);
|
|
15185
|
-
const cl = Math.cos(latRad);
|
|
15186
|
-
const so = Math.sin(lonRad);
|
|
15187
|
-
const co = Math.cos(lonRad);
|
|
15188
|
-
const [e, n, u] = enu;
|
|
15189
|
-
return [
|
|
15190
|
-
-so * e - sl * co * n + cl * co * u,
|
|
15191
|
-
co * e - sl * so * n + cl * so * u,
|
|
15192
|
-
cl * n + sl * u
|
|
15193
|
-
];
|
|
15194
|
-
}
|
|
15195
|
-
function interpPcv(off, angleDeg) {
|
|
15196
|
-
const { pcvZen1Deg, pcvDzenDeg, pcvNoazi } = off;
|
|
15197
|
-
if (pcvDzenDeg <= 0 || pcvNoazi.length < 2) return 0;
|
|
15198
|
-
const x = (angleDeg - pcvZen1Deg) / pcvDzenDeg;
|
|
15199
|
-
if (x <= 0) return pcvNoazi[0];
|
|
15200
|
-
if (x >= pcvNoazi.length - 1) return pcvNoazi[pcvNoazi.length - 1];
|
|
15201
|
-
const i = Math.floor(x);
|
|
15202
|
-
const t = x - i;
|
|
15203
|
-
return pcvNoazi[i] * (1 - t) + pcvNoazi[i + 1] * t;
|
|
15204
|
-
}
|
|
15205
|
-
function phaseWindup(prn, losRcvToSat, satFrame, latRad, lonRad, state) {
|
|
15206
|
-
const k = [
|
|
15207
|
-
-losRcvToSat[0],
|
|
15208
|
-
-losRcvToSat[1],
|
|
15209
|
-
-losRcvToSat[2]
|
|
15210
|
-
];
|
|
15211
|
-
const kx = satFrame.x;
|
|
15212
|
-
const ky = satFrame.y;
|
|
15213
|
-
const kdotx = dot(k, kx);
|
|
15214
|
-
const kcrossY = cross2(k, ky);
|
|
15215
|
-
const dS = [
|
|
15216
|
-
kx[0] - k[0] * kdotx - kcrossY[0],
|
|
15217
|
-
kx[1] - k[1] * kdotx - kcrossY[1],
|
|
15218
|
-
kx[2] - k[2] * kdotx - kcrossY[2]
|
|
15219
|
-
];
|
|
15220
|
-
const north = enuToEcef([0, 1, 0], latRad, lonRad);
|
|
15221
|
-
const east = enuToEcef([1, 0, 0], latRad, lonRad);
|
|
15222
|
-
const kdotn = dot(k, north);
|
|
15223
|
-
const kcrossE = cross2(k, east);
|
|
15224
|
-
const dR = [
|
|
15225
|
-
north[0] - k[0] * kdotn + kcrossE[0],
|
|
15226
|
-
north[1] - k[1] * kdotn + kcrossE[1],
|
|
15227
|
-
north[2] - k[2] * kdotn + kcrossE[2]
|
|
15228
|
-
];
|
|
15229
|
-
const nS = Math.hypot(dS[0], dS[1], dS[2]) || 1;
|
|
15230
|
-
const nR = Math.hypot(dR[0], dR[1], dR[2]) || 1;
|
|
15231
|
-
let cosPhi = dot(dS, dR) / (nS * nR);
|
|
15232
|
-
cosPhi = Math.max(-1, Math.min(1, cosPhi));
|
|
15233
|
-
const zeta = cross2(dS, dR);
|
|
15234
|
-
const sign = dot(k, zeta) >= 0 ? 1 : -1;
|
|
15235
|
-
const dphi = sign * Math.acos(cosPhi) / (2 * Math.PI);
|
|
15236
|
-
const prev = state.get(prn);
|
|
15237
|
-
const wu = prev == null ? dphi : dphi + Math.round(prev - dphi);
|
|
15238
|
-
state.set(prn, wu);
|
|
15239
|
-
return wu;
|
|
15240
|
-
}
|
|
15241
|
-
|
|
15242
15394
|
// src/positioning/ppp-corrections.ts
|
|
15243
15395
|
function applyCorrections(corrections, geom, ctx) {
|
|
15244
15396
|
if (corrections.evaluate) return corrections.evaluate(geom, ctx);
|
|
@@ -16598,7 +16750,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
16598
16750
|
used.push(prn);
|
|
16599
16751
|
azels.push(azel);
|
|
16600
16752
|
if (sbas) {
|
|
16601
|
-
const
|
|
16753
|
+
const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
|
|
16754
|
+
const dr = Math.hypot(dx, dy, dz) || 1;
|
|
16755
|
+
const los = [dx / dr, dy / dr, dz / dr];
|
|
16756
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow, rxLat, rxLon, los);
|
|
16602
16757
|
const si = sbas.ionoDelay(
|
|
16603
16758
|
gpsWeek,
|
|
16604
16759
|
gpsTow,
|
|
@@ -18989,6 +19144,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
18989
19144
|
almanacEpochMs,
|
|
18990
19145
|
almanacSatPosition,
|
|
18991
19146
|
analyzeQuality,
|
|
19147
|
+
antexFreqOfGroup,
|
|
18992
19148
|
applyHelmert,
|
|
18993
19149
|
applyIonoDcb,
|
|
18994
19150
|
applyOrbitClock,
|
|
@@ -19000,6 +19156,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
19000
19156
|
buildGloChannelMap,
|
|
19001
19157
|
buildObsIndices,
|
|
19002
19158
|
buildPppAntenna,
|
|
19159
|
+
buildRtkAntenna,
|
|
19003
19160
|
clampUnit,
|
|
19004
19161
|
clkBias,
|
|
19005
19162
|
computeAllPositions,
|
|
@@ -19182,6 +19339,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
19182
19339
|
postProcessRtk,
|
|
19183
19340
|
rad2deg,
|
|
19184
19341
|
rangeRate,
|
|
19342
|
+
rcvAntennaRangeM,
|
|
19185
19343
|
readString,
|
|
19186
19344
|
reportDecodeError,
|
|
19187
19345
|
resetGloFreqCache,
|