gnss-js 2.11.0 → 2.12.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/README.md +15 -0
- package/dist/analysis.cjs +24 -25
- package/dist/analysis.js +2 -2
- package/dist/{clk-DGVBGRzA.d.cts → bias-sinex-OGMw9xEZ.d.cts} +66 -1
- package/dist/{clk-DGVBGRzA.d.ts → bias-sinex-OGMw9xEZ.d.ts} +66 -1
- package/dist/{chunk-MDO77CCC.js → chunk-2DJ4XENO.js} +45 -0
- package/dist/{chunk-IQHQHUOB.js → chunk-2KQMCJ5Z.js} +5 -3
- package/dist/chunk-E3Y6YKQM.js +65 -0
- package/dist/{chunk-3U5AX7PY.js → chunk-FEABDV5P.js} +24 -25
- package/dist/{chunk-RFPR4LKM.js → chunk-HWHD5E6V.js} +19 -75
- package/dist/{chunk-KUSUN7DP.js → chunk-MFCPYUEI.js} +1 -1
- package/dist/{chunk-SSWFX3P3.js → chunk-NIJ6WFQJ.js} +6 -321
- package/dist/{chunk-UMINQTMK.js → chunk-RWFJEVLJ.js} +589 -295
- package/dist/chunk-TWGZTRBL.js +323 -0
- package/dist/index.cjs +655 -316
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -9
- package/dist/osnma.cjs +1336 -0
- package/dist/osnma.d.cts +273 -0
- package/dist/osnma.d.ts +273 -0
- package/dist/osnma.js +1216 -0
- package/dist/positioning.cjs +585 -291
- package/dist/positioning.d.cts +114 -4
- package/dist/positioning.d.ts +114 -4
- package/dist/positioning.js +1 -1
- package/dist/rinex.cjs +70 -25
- package/dist/rinex.d.cts +2 -50
- package/dist/rinex.d.ts +2 -50
- package/dist/rinex.js +4 -2
- package/dist/sbf.js +7 -4
- package/dist/ubx.js +3 -2
- package/package.json +6 -1
package/dist/index.cjs
CHANGED
|
@@ -106,6 +106,7 @@ __export(src_exports, {
|
|
|
106
106
|
SYSTEM_NAMES: () => SYSTEM_NAMES,
|
|
107
107
|
SYSTEM_ORDER: () => SYSTEM_ORDER,
|
|
108
108
|
SYS_SHORT: () => SYS_SHORT,
|
|
109
|
+
SatOsbTable: () => SatOsbTable,
|
|
109
110
|
SbasProcessor: () => SbasProcessor,
|
|
110
111
|
Scale: () => Scale,
|
|
111
112
|
Sp3EphemerisSource: () => Sp3EphemerisSource,
|
|
@@ -1374,20 +1375,14 @@ function geodeticToGeohash(lat, lon, precision = 8) {
|
|
|
1374
1375
|
// src/rinex/crx.ts
|
|
1375
1376
|
function crxRepair(old, diff) {
|
|
1376
1377
|
const chars = old.split("");
|
|
1377
|
-
|
|
1378
|
+
const n = Math.min(old.length, diff.length);
|
|
1379
|
+
for (let i = 0; i < n; i++) {
|
|
1378
1380
|
const dc = diff[i];
|
|
1379
|
-
if (dc === " ")
|
|
1380
|
-
|
|
1381
|
-
chars[i] = " ";
|
|
1382
|
-
} else {
|
|
1383
|
-
chars[i] = dc;
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
if (diff.length > chars.length) {
|
|
1387
|
-
for (let i = chars.length; i < diff.length; i++) {
|
|
1388
|
-
chars[i] = diff[i] === "&" ? " " : diff[i];
|
|
1389
|
-
}
|
|
1381
|
+
if (dc === " ") continue;
|
|
1382
|
+
chars[i] = dc === "&" ? " " : dc;
|
|
1390
1383
|
}
|
|
1384
|
+
for (let i = old.length; i < diff.length; i++)
|
|
1385
|
+
chars[i] = diff[i] === "&" ? " " : diff[i];
|
|
1391
1386
|
return chars.join("");
|
|
1392
1387
|
}
|
|
1393
1388
|
function parseCrxDataLine(line, ntype) {
|
|
@@ -2085,6 +2080,15 @@ function readObsValue(line, index) {
|
|
|
2085
2080
|
const v = parseFloat(raw);
|
|
2086
2081
|
return isNaN(v) ? null : v;
|
|
2087
2082
|
}
|
|
2083
|
+
function normalizePrn(id) {
|
|
2084
|
+
let sys = id[0];
|
|
2085
|
+
let num = id.substring(1);
|
|
2086
|
+
if (sys === " " || /\d/.test(sys)) {
|
|
2087
|
+
num = id;
|
|
2088
|
+
sys = "G";
|
|
2089
|
+
}
|
|
2090
|
+
return sys + num.replace(/\s/g, "").padStart(2, "0");
|
|
2091
|
+
}
|
|
2088
2092
|
function parseEpochLine3(line) {
|
|
2089
2093
|
if (line[0] !== ">") return null;
|
|
2090
2094
|
const yr = parseInt(line.substring(2, 6));
|
|
@@ -2234,7 +2238,7 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
2234
2238
|
function processSatLineV3(line) {
|
|
2235
2239
|
if (!header || line.length < 3) return;
|
|
2236
2240
|
const sys = line[0];
|
|
2237
|
-
const prn = line.substring(0, 3);
|
|
2241
|
+
const prn = normalizePrn(line.substring(0, 3));
|
|
2238
2242
|
trackSat(sys, prn);
|
|
2239
2243
|
const obsLine = line.substring(3);
|
|
2240
2244
|
const codes = header.obsTypes[sys] ?? [];
|
|
@@ -2259,10 +2263,8 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
2259
2263
|
let v2ObsAccum = [];
|
|
2260
2264
|
function processSatLineV2(line) {
|
|
2261
2265
|
if (!header || v2SatIndex >= v2SatIds.length) return;
|
|
2262
|
-
const
|
|
2263
|
-
|
|
2264
|
-
if (sys === " " || /\d/.test(sys)) sys = "G";
|
|
2265
|
-
const prn = sys + satId.substring(1).padStart(2, "0");
|
|
2266
|
+
const prn = normalizePrn(v2SatIds[v2SatIndex]);
|
|
2267
|
+
const sys = prn[0];
|
|
2266
2268
|
if (v2CurrentSatLine === 0) {
|
|
2267
2269
|
trackSat(sys, prn);
|
|
2268
2270
|
if (onSatObs) v2ObsAccum = [];
|
|
@@ -2353,6 +2355,7 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
2353
2355
|
} else {
|
|
2354
2356
|
epochLine = crxRepair(crxPrevEpochLine, line);
|
|
2355
2357
|
}
|
|
2358
|
+
epochLine = epochLine.replace(/ +$/, "");
|
|
2356
2359
|
if (isV3) {
|
|
2357
2360
|
if (epochLine[0] !== ">") return;
|
|
2358
2361
|
const info = parseEpochLine3(epochLine);
|
|
@@ -2370,10 +2373,11 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
2370
2373
|
return;
|
|
2371
2374
|
}
|
|
2372
2375
|
resetEpochAccum(info);
|
|
2373
|
-
const satPart = epochLine.substring(41).replace(/\s+$/, "");
|
|
2374
2376
|
crxSatList = [];
|
|
2375
|
-
for (let
|
|
2376
|
-
crxSatList.push(
|
|
2377
|
+
for (let k = 0; k < info.numSats; k++) {
|
|
2378
|
+
crxSatList.push(
|
|
2379
|
+
normalizePrn(epochLine.substring(41 + 3 * k, 44 + 3 * k))
|
|
2380
|
+
);
|
|
2377
2381
|
}
|
|
2378
2382
|
crxSatIndex = 0;
|
|
2379
2383
|
crxPrevEpochLine = epochLine;
|
|
@@ -2394,11 +2398,7 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
2394
2398
|
return;
|
|
2395
2399
|
}
|
|
2396
2400
|
resetEpochAccum(info);
|
|
2397
|
-
crxSatList = (info.satIds ?? []).map(
|
|
2398
|
-
let sys = id[0];
|
|
2399
|
-
if (sys === " " || /\d/.test(sys)) sys = "G";
|
|
2400
|
-
return sys + id.substring(1).padStart(2, "0");
|
|
2401
|
-
});
|
|
2401
|
+
crxSatList = (info.satIds ?? []).slice(0, info.numSats).map(normalizePrn);
|
|
2402
2402
|
crxSatIndex = 0;
|
|
2403
2403
|
crxPrevEpochLine = epochLine;
|
|
2404
2404
|
crxPhase = "clock";
|
|
@@ -4608,6 +4608,50 @@ function findSatBias(bias, prn, obs1, obs2, tMs) {
|
|
|
4608
4608
|
}
|
|
4609
4609
|
return null;
|
|
4610
4610
|
}
|
|
4611
|
+
var SatOsbTable = class {
|
|
4612
|
+
byKey = /* @__PURE__ */ new Map();
|
|
4613
|
+
constructor(bias) {
|
|
4614
|
+
const recs = Array.isArray(bias) ? bias : bias.records;
|
|
4615
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4616
|
+
for (const r of recs) {
|
|
4617
|
+
if (r.type !== "OSB" || !r.prn || r.station) continue;
|
|
4618
|
+
if (r.unit !== "ns") continue;
|
|
4619
|
+
const k = `${r.prn} ${r.obs1}`;
|
|
4620
|
+
let g = groups.get(k);
|
|
4621
|
+
if (!g) groups.set(k, g = []);
|
|
4622
|
+
g.push(r);
|
|
4623
|
+
}
|
|
4624
|
+
for (const [k, g] of groups) {
|
|
4625
|
+
g.sort((a, b) => a.startMs - b.startMs);
|
|
4626
|
+
this.byKey.set(k, {
|
|
4627
|
+
start: Float64Array.from(g, (r) => r.startMs),
|
|
4628
|
+
end: Float64Array.from(g, (r) => r.endMs),
|
|
4629
|
+
m: Float64Array.from(g, biasMetres)
|
|
4630
|
+
});
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4633
|
+
/** Whether any phase (L…) OSB is present — i.e. the product supports AR. */
|
|
4634
|
+
get hasPhase() {
|
|
4635
|
+
for (const k of this.byKey.keys())
|
|
4636
|
+
if (k.split(" ")[1][0] === "L") return true;
|
|
4637
|
+
return false;
|
|
4638
|
+
}
|
|
4639
|
+
/** OSB (m) of `prn`'s observable `obs` (e.g. 'C1W', 'L2W') at `tMs`;
|
|
4640
|
+
* null when the product has none valid then. */
|
|
4641
|
+
get(prn, obs, tMs) {
|
|
4642
|
+
const e = this.byKey.get(`${prn} ${obs}`) ?? this.byKey.get(`${prn} ${obs.slice(0, 2)}X`);
|
|
4643
|
+
if (!e) return null;
|
|
4644
|
+
let lo = 0;
|
|
4645
|
+
let hi = e.start.length - 1;
|
|
4646
|
+
if (tMs < e.start[0]) return null;
|
|
4647
|
+
while (lo < hi) {
|
|
4648
|
+
const mid = lo + hi + 1 >> 1;
|
|
4649
|
+
if (e.start[mid] <= tMs) lo = mid;
|
|
4650
|
+
else hi = mid - 1;
|
|
4651
|
+
}
|
|
4652
|
+
return tMs <= e.end[lo] ? e.m[lo] : null;
|
|
4653
|
+
}
|
|
4654
|
+
};
|
|
4611
4655
|
|
|
4612
4656
|
// src/rinex/obs-writer-common.ts
|
|
4613
4657
|
function createGzipLineSink() {
|
|
@@ -14090,6 +14134,12 @@ function solveZt(n, m, Z2, E) {
|
|
|
14090
14134
|
for (let cIdx = 0; cIdx < m; cIdx++) X[r + cIdx * n] = M[r * w + n + cIdx];
|
|
14091
14135
|
return X;
|
|
14092
14136
|
}
|
|
14137
|
+
function normCdf(x) {
|
|
14138
|
+
const z = Math.abs(x) / Math.SQRT2;
|
|
14139
|
+
const t = 1 / (1 + 0.3275911 * z);
|
|
14140
|
+
const erf = 1 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * Math.exp(-z * z);
|
|
14141
|
+
return x >= 0 ? 0.5 * (1 + erf) : 0.5 * (1 - erf);
|
|
14142
|
+
}
|
|
14093
14143
|
function lambdaSearch(a, Q, n, m = 2) {
|
|
14094
14144
|
if (n <= 0 || m <= 0 || a.length < n || Q.length < n * n) return null;
|
|
14095
14145
|
const L = new Float64Array(n * n);
|
|
@@ -14116,7 +14166,10 @@ function lambdaSearch(a, Q, n, m = 2) {
|
|
|
14116
14166
|
candidates.push(v);
|
|
14117
14167
|
}
|
|
14118
14168
|
const ratio = m >= 2 && s[0] > 0 ? s[1] / s[0] : Infinity;
|
|
14119
|
-
|
|
14169
|
+
let successRate = 1;
|
|
14170
|
+
for (let i = 0; i < n; i++)
|
|
14171
|
+
successRate *= 2 * normCdf(1 / (2 * Math.sqrt(D[i]))) - 1;
|
|
14172
|
+
return { candidates, residuals: s, ratio, successRate };
|
|
14120
14173
|
}
|
|
14121
14174
|
function lambdaReduction(Q, n) {
|
|
14122
14175
|
if (n <= 0 || Q.length < n * n) return null;
|
|
@@ -14131,7 +14184,7 @@ function lambdaReduction(Q, n) {
|
|
|
14131
14184
|
|
|
14132
14185
|
// src/positioning/ppp-antenna.ts
|
|
14133
14186
|
function parseAntexTime(s) {
|
|
14134
|
-
const p = s.trim().split(
|
|
14187
|
+
const p = s.trim().split(/[\s:-]+/).map(Number);
|
|
14135
14188
|
if (p.length < 3 || p.some((v) => Number.isNaN(v))) return null;
|
|
14136
14189
|
return Date.UTC(
|
|
14137
14190
|
p[0],
|
|
@@ -14181,11 +14234,21 @@ function buildPppAntenna(antex) {
|
|
|
14181
14234
|
satOffset(prn, dateMs, band1, band2, f1, f2) {
|
|
14182
14235
|
const list = sats.get(prn);
|
|
14183
14236
|
if (!list) return null;
|
|
14184
|
-
|
|
14185
|
-
|
|
14186
|
-
|
|
14187
|
-
|
|
14188
|
-
|
|
14237
|
+
let entry;
|
|
14238
|
+
let bestDist = Infinity;
|
|
14239
|
+
for (const e of list) {
|
|
14240
|
+
const from = parseAntexTime(e.validFrom) ?? -Infinity;
|
|
14241
|
+
const until = parseAntexTime(e.validUntil) ?? Infinity;
|
|
14242
|
+
if (dateMs >= from && dateMs <= until) {
|
|
14243
|
+
entry = e;
|
|
14244
|
+
break;
|
|
14245
|
+
}
|
|
14246
|
+
const dist = dateMs < from ? from - dateMs : dateMs - until;
|
|
14247
|
+
if (dist < bestDist) {
|
|
14248
|
+
bestDist = dist;
|
|
14249
|
+
entry = e;
|
|
14250
|
+
}
|
|
14251
|
+
}
|
|
14189
14252
|
if (!entry) return null;
|
|
14190
14253
|
const a = freqOf(entry, band1);
|
|
14191
14254
|
const b = freqOf(entry, band2);
|
|
@@ -14193,10 +14256,14 @@ function buildPppAntenna(antex) {
|
|
|
14193
14256
|
return withZen(entry, combineIf(a, b, f1, f2));
|
|
14194
14257
|
},
|
|
14195
14258
|
rcvOffset(antType, band1, band2, f1, f2) {
|
|
14196
|
-
const entry = rcvs.get(antType);
|
|
14259
|
+
const entry = rcvs.get(antType) ?? rcvs.get(antType.substring(0, 16).padEnd(16, " ") + "NONE");
|
|
14197
14260
|
if (!entry) return null;
|
|
14198
|
-
|
|
14199
|
-
|
|
14261
|
+
let a = freqOf(entry, band1);
|
|
14262
|
+
let b = freqOf(entry, band2);
|
|
14263
|
+
if (!a || !b) {
|
|
14264
|
+
a = freqOf(entry, "G01");
|
|
14265
|
+
b = freqOf(entry, "G02");
|
|
14266
|
+
}
|
|
14200
14267
|
if (!a || !b) return null;
|
|
14201
14268
|
return withZen(entry, combineIf(a, b, f1, f2));
|
|
14202
14269
|
}
|
|
@@ -14272,13 +14339,13 @@ function phaseWindup(prn, losRcvToSat, satFrame, latRad, lonRad, state) {
|
|
|
14272
14339
|
kx[2] - k[2] * kdotx - kcrossY[2]
|
|
14273
14340
|
];
|
|
14274
14341
|
const north = enuToEcef([0, 1, 0], latRad, lonRad);
|
|
14275
|
-
const
|
|
14342
|
+
const west = enuToEcef([-1, 0, 0], latRad, lonRad);
|
|
14276
14343
|
const kdotn = dot(k, north);
|
|
14277
|
-
const
|
|
14344
|
+
const kcrossW = cross(k, west);
|
|
14278
14345
|
const dR = [
|
|
14279
|
-
north[0] - k[0] * kdotn +
|
|
14280
|
-
north[1] - k[1] * kdotn +
|
|
14281
|
-
north[2] - k[2] * kdotn +
|
|
14346
|
+
north[0] - k[0] * kdotn + kcrossW[0],
|
|
14347
|
+
north[1] - k[1] * kdotn + kcrossW[1],
|
|
14348
|
+
north[2] - k[2] * kdotn + kcrossW[2]
|
|
14282
14349
|
];
|
|
14283
14350
|
const nS = Math.hypot(dS[0], dS[1], dS[2]) || 1;
|
|
14284
14351
|
const nR = Math.hypot(dR[0], dR[1], dR[2]) || 1;
|
|
@@ -14405,9 +14472,11 @@ function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, tropo
|
|
|
14405
14472
|
let baseLatRad = 0;
|
|
14406
14473
|
let baseLonRad = 0;
|
|
14407
14474
|
if (antenna?.base) {
|
|
14408
|
-
|
|
14409
|
-
|
|
14410
|
-
|
|
14475
|
+
[baseLatRad, baseLonRad] = ecefToGeodetic(
|
|
14476
|
+
basePos[0],
|
|
14477
|
+
basePos[1],
|
|
14478
|
+
basePos[2]
|
|
14479
|
+
);
|
|
14411
14480
|
}
|
|
14412
14481
|
for (const [prn, mR] of rover) {
|
|
14413
14482
|
const sys = prn[0];
|
|
@@ -14503,12 +14572,12 @@ function roverTerms(g, x, y, z, troposphere) {
|
|
|
14503
14572
|
dTropo = tropoDelay(Math.max(elR, 0.05)) - g.tropoB;
|
|
14504
14573
|
}
|
|
14505
14574
|
if (g.offR) {
|
|
14506
|
-
const [
|
|
14575
|
+
const [latRad, lonRad] = ecefToGeodetic(x, y, z);
|
|
14507
14576
|
rho += rcvAntennaRangeM(
|
|
14508
14577
|
g.offR,
|
|
14509
14578
|
[-u[0], -u[1], -u[2]],
|
|
14510
|
-
|
|
14511
|
-
|
|
14579
|
+
latRad,
|
|
14580
|
+
lonRad,
|
|
14512
14581
|
elR,
|
|
14513
14582
|
g.roverDelta
|
|
14514
14583
|
);
|
|
@@ -16323,6 +16392,234 @@ function hasToOrbitClock(orbit, clock2, refTimeMs, validitySeconds) {
|
|
|
16323
16392
|
};
|
|
16324
16393
|
}
|
|
16325
16394
|
|
|
16395
|
+
// src/positioning/ppp-ar.ts
|
|
16396
|
+
function wlWavelength(f1, f2) {
|
|
16397
|
+
return C_LIGHT / (f1 - f2);
|
|
16398
|
+
}
|
|
16399
|
+
function nlWavelength(f1, f2) {
|
|
16400
|
+
return C_LIGHT / (f1 + f2);
|
|
16401
|
+
}
|
|
16402
|
+
function resolvePppAmbiguities(sats, Qaif, opts = {}) {
|
|
16403
|
+
const wlThreshold = opts.wlThreshold ?? 0.25;
|
|
16404
|
+
const ratioThreshold = opts.ratioThreshold ?? 3;
|
|
16405
|
+
const minPartial = opts.minPartial ?? 4;
|
|
16406
|
+
const minSuccess = opts.minSuccessRate ?? 0;
|
|
16407
|
+
const n = sats.length;
|
|
16408
|
+
const none = (rejectedWl2, ratio = 0) => ({
|
|
16409
|
+
fixed: false,
|
|
16410
|
+
ratio,
|
|
16411
|
+
refPrn: null,
|
|
16412
|
+
refPrns: [],
|
|
16413
|
+
sats: [],
|
|
16414
|
+
rejectedWl: rejectedWl2
|
|
16415
|
+
});
|
|
16416
|
+
const elev = (i) => sats[i].elevDeg ?? -1;
|
|
16417
|
+
const wlOf = (i) => sats[i].mwCyc - (sats[i].wlBiasCyc ?? 0);
|
|
16418
|
+
const refOfSys = /* @__PURE__ */ new Map();
|
|
16419
|
+
for (let i = 0; i < n; i++) {
|
|
16420
|
+
const sys = sats[i].prn[0];
|
|
16421
|
+
const r = refOfSys.get(sys);
|
|
16422
|
+
if (r === void 0 || elev(i) > elev(r)) refOfSys.set(sys, i);
|
|
16423
|
+
}
|
|
16424
|
+
const nWlOf = /* @__PURE__ */ new Map();
|
|
16425
|
+
const refIdx = /* @__PURE__ */ new Map();
|
|
16426
|
+
const rejectedWl = [];
|
|
16427
|
+
for (const r of refOfSys.values()) {
|
|
16428
|
+
nWlOf.set(r, Math.round(wlOf(r)));
|
|
16429
|
+
refIdx.set(r, r);
|
|
16430
|
+
}
|
|
16431
|
+
for (let i = 0; i < n; i++) {
|
|
16432
|
+
if (refIdx.has(i)) continue;
|
|
16433
|
+
const r = refOfSys.get(sats[i].prn[0]);
|
|
16434
|
+
const sdWl = wlOf(i) - wlOf(r);
|
|
16435
|
+
const k = Math.round(sdWl);
|
|
16436
|
+
if (Math.abs(sdWl - k) <= wlThreshold) {
|
|
16437
|
+
nWlOf.set(i, nWlOf.get(r) + k);
|
|
16438
|
+
refIdx.set(i, r);
|
|
16439
|
+
} else rejectedWl.push(sats[i].prn);
|
|
16440
|
+
}
|
|
16441
|
+
const lamNl = (i) => nlWavelength(sats[i].f1, sats[i].f2);
|
|
16442
|
+
const n1Float = (i) => {
|
|
16443
|
+
const s = sats[i];
|
|
16444
|
+
return s.aIF / lamNl(i) - s.f2 / (s.f1 - s.f2) * nWlOf.get(i) - (s.nlBiasCyc ?? 0);
|
|
16445
|
+
};
|
|
16446
|
+
const qN1 = (a, b) => Qaif[a * n + b] / (lamNl(a) * lamNl(b));
|
|
16447
|
+
let cand = [...refIdx.keys()].filter((i) => refIdx.get(i) !== i).sort((a, b) => elev(b) - elev(a));
|
|
16448
|
+
if (cand.length < 1) return none(rejectedWl);
|
|
16449
|
+
let best = null;
|
|
16450
|
+
let firstRatio = 0;
|
|
16451
|
+
for (; ; ) {
|
|
16452
|
+
const m = cand.length;
|
|
16453
|
+
const sd2 = new Float64Array(m);
|
|
16454
|
+
const Qsd = new Float64Array(m * m);
|
|
16455
|
+
for (let a = 0; a < m; a++) {
|
|
16456
|
+
const ia = cand[a];
|
|
16457
|
+
const ra = refIdx.get(ia);
|
|
16458
|
+
sd2[a] = n1Float(ia) - n1Float(ra);
|
|
16459
|
+
for (let b = 0; b < m; b++) {
|
|
16460
|
+
const ib = cand[b];
|
|
16461
|
+
const rb = refIdx.get(ib);
|
|
16462
|
+
Qsd[a * m + b] = qN1(ia, ib) - qN1(ia, rb) - qN1(ra, ib) + qN1(ra, rb);
|
|
16463
|
+
}
|
|
16464
|
+
}
|
|
16465
|
+
const res = lambdaSearch(sd2, Qsd, m, 2);
|
|
16466
|
+
const ratio = res ? Math.min(res.ratio, 999.9) : 0;
|
|
16467
|
+
if (best === null && firstRatio === 0) firstRatio = ratio;
|
|
16468
|
+
if (res && ratio >= ratioThreshold && res.successRate >= minSuccess) {
|
|
16469
|
+
best = { set: cand, ratio, fix: Float64Array.from(res.candidates[0]) };
|
|
16470
|
+
break;
|
|
16471
|
+
}
|
|
16472
|
+
if (m <= minPartial) break;
|
|
16473
|
+
cand = cand.slice(0, -1);
|
|
16474
|
+
}
|
|
16475
|
+
if (!best) return { ...none(rejectedWl, firstRatio), refPrn: null };
|
|
16476
|
+
const refN1 = /* @__PURE__ */ new Map();
|
|
16477
|
+
for (const r of refOfSys.values()) refN1.set(r, Math.round(n1Float(r)));
|
|
16478
|
+
const out = [];
|
|
16479
|
+
const emit = (i, n1) => {
|
|
16480
|
+
const s = sats[i];
|
|
16481
|
+
const l = lamNl(i);
|
|
16482
|
+
const nWl = nWlOf.get(i);
|
|
16483
|
+
out.push({
|
|
16484
|
+
prn: s.prn,
|
|
16485
|
+
nWl,
|
|
16486
|
+
n1,
|
|
16487
|
+
aIF: l * n1 + l * (s.f2 / (s.f1 - s.f2)) * nWl,
|
|
16488
|
+
ref: sats[refIdx.get(i)].prn
|
|
16489
|
+
});
|
|
16490
|
+
};
|
|
16491
|
+
const usedRefs = new Set(best.set.map((i) => refIdx.get(i)));
|
|
16492
|
+
for (const r of usedRefs) emit(r, refN1.get(r));
|
|
16493
|
+
best.set.forEach(
|
|
16494
|
+
(i, a) => emit(i, refN1.get(refIdx.get(i)) + Math.round(best.fix[a]))
|
|
16495
|
+
);
|
|
16496
|
+
const refPrns = [...usedRefs].map((r) => sats[r].prn);
|
|
16497
|
+
return {
|
|
16498
|
+
fixed: true,
|
|
16499
|
+
ratio: best.ratio,
|
|
16500
|
+
refPrn: refPrns[0] ?? null,
|
|
16501
|
+
refPrns,
|
|
16502
|
+
sats: out,
|
|
16503
|
+
rejectedWl
|
|
16504
|
+
};
|
|
16505
|
+
}
|
|
16506
|
+
function solveSpd(A, b) {
|
|
16507
|
+
const n = b.length;
|
|
16508
|
+
const L = Array.from({ length: n }, () => new Array(n).fill(0));
|
|
16509
|
+
for (let i = 0; i < n; i++) {
|
|
16510
|
+
for (let j = 0; j <= i; j++) {
|
|
16511
|
+
let s = A[i][j];
|
|
16512
|
+
for (let k = 0; k < j; k++) s -= L[i][k] * L[j][k];
|
|
16513
|
+
if (i === j) {
|
|
16514
|
+
if (s <= 0) return null;
|
|
16515
|
+
L[i][j] = Math.sqrt(s);
|
|
16516
|
+
} else {
|
|
16517
|
+
L[i][j] = s / L[j][j];
|
|
16518
|
+
}
|
|
16519
|
+
}
|
|
16520
|
+
}
|
|
16521
|
+
const y = new Array(n).fill(0);
|
|
16522
|
+
for (let i = 0; i < n; i++) {
|
|
16523
|
+
let s = b[i];
|
|
16524
|
+
for (let k = 0; k < i; k++) s -= L[i][k] * y[k];
|
|
16525
|
+
y[i] = s / L[i][i];
|
|
16526
|
+
}
|
|
16527
|
+
const x = new Array(n).fill(0);
|
|
16528
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
16529
|
+
let s = y[i];
|
|
16530
|
+
for (let k = i + 1; k < n; k++) s -= L[k][i] * x[k];
|
|
16531
|
+
x[i] = s / L[i][i];
|
|
16532
|
+
}
|
|
16533
|
+
return x;
|
|
16534
|
+
}
|
|
16535
|
+
function fixPppPosition(state, fcb, opts = {}) {
|
|
16536
|
+
const floatPos = state.position;
|
|
16537
|
+
const noFix = {
|
|
16538
|
+
fixed: false,
|
|
16539
|
+
position: floatPos,
|
|
16540
|
+
floatPosition: floatPos,
|
|
16541
|
+
shift: [0, 0, 0],
|
|
16542
|
+
nFixed: 0,
|
|
16543
|
+
ratio: 0,
|
|
16544
|
+
refPrn: null
|
|
16545
|
+
};
|
|
16546
|
+
const amb = state.ambiguities;
|
|
16547
|
+
if (amb.length < 3) return noFix;
|
|
16548
|
+
const sats = amb.map((a) => ({
|
|
16549
|
+
prn: a.prn,
|
|
16550
|
+
aIF: a.aIF,
|
|
16551
|
+
mwCyc: a.mwCyc,
|
|
16552
|
+
f1: a.f1,
|
|
16553
|
+
f2: a.f2,
|
|
16554
|
+
wlBiasCyc: fcb.satWlFcb.get(a.prn) ?? 0,
|
|
16555
|
+
nlBiasCyc: fcb.satNlFcb.get(a.prn) ?? 0,
|
|
16556
|
+
elevDeg: a.elevDeg
|
|
16557
|
+
}));
|
|
16558
|
+
const n = sats.length;
|
|
16559
|
+
const Qaif = new Float64Array(n * n);
|
|
16560
|
+
for (let i = 0; i < n; i++)
|
|
16561
|
+
for (let j = 0; j < n; j++)
|
|
16562
|
+
Qaif[i * n + j] = state.covariance[amb[i].index][amb[j].index];
|
|
16563
|
+
const res = resolvePppAmbiguities(sats, Qaif, {
|
|
16564
|
+
...opts,
|
|
16565
|
+
minPartial: opts.minPartial ?? Math.max(4, opts.minFixed ?? 0)
|
|
16566
|
+
});
|
|
16567
|
+
if (!res.fixed || res.refPrn === null) {
|
|
16568
|
+
return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
|
|
16569
|
+
}
|
|
16570
|
+
const idxOfPrn = new Map(amb.map((a) => [a.prn, a.index]));
|
|
16571
|
+
const floatOfPrn = new Map(amb.map((a) => [a.prn, a.aIF]));
|
|
16572
|
+
const fixedOf = new Map(res.sats.map((s) => [s.prn, s.aIF]));
|
|
16573
|
+
const pairs = res.sats.filter(
|
|
16574
|
+
(s) => s.ref !== s.prn && idxOfPrn.has(s.prn) && idxOfPrn.has(s.ref)
|
|
16575
|
+
).map((s) => ({ prn: s.prn, ref: s.ref }));
|
|
16576
|
+
const m = pairs.length;
|
|
16577
|
+
if (m < Math.max(1, opts.minFixed ?? 1))
|
|
16578
|
+
return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
|
|
16579
|
+
const P = state.covariance;
|
|
16580
|
+
const ix = (prn) => idxOfPrn.get(prn);
|
|
16581
|
+
const qAA = (i, j) => P[ix(i)][ix(j)];
|
|
16582
|
+
const qXA = (r, i) => P[r][ix(i)];
|
|
16583
|
+
const Qzz = Array.from({ length: m }, () => new Array(m).fill(0));
|
|
16584
|
+
const z = new Array(m).fill(0);
|
|
16585
|
+
for (let a = 0; a < m; a++) {
|
|
16586
|
+
const { prn: ia, ref: ra } = pairs[a];
|
|
16587
|
+
z[a] = floatOfPrn.get(ia) - floatOfPrn.get(ra) - (fixedOf.get(ia) - fixedOf.get(ra));
|
|
16588
|
+
for (let b = 0; b < m; b++) {
|
|
16589
|
+
const { prn: ib, ref: rb } = pairs[b];
|
|
16590
|
+
Qzz[a][b] = qAA(ia, ib) - qAA(ia, rb) - qAA(ra, ib) + qAA(ra, rb);
|
|
16591
|
+
}
|
|
16592
|
+
}
|
|
16593
|
+
const Qxz = [0, 1, 2].map(
|
|
16594
|
+
(r) => pairs.map(({ prn, ref }) => qXA(r, prn) - qXA(r, ref))
|
|
16595
|
+
);
|
|
16596
|
+
const y = solveSpd(Qzz, z);
|
|
16597
|
+
if (!y) return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
|
|
16598
|
+
let chi2 = 0;
|
|
16599
|
+
for (let a = 0; a < m; a++) chi2 += z[a] * y[a];
|
|
16600
|
+
if (chi2 / m > (opts.maxChi2 ?? Infinity))
|
|
16601
|
+
return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
|
|
16602
|
+
const shift = [0, 0, 0];
|
|
16603
|
+
for (let r = 0; r < 3; r++) {
|
|
16604
|
+
let s = 0;
|
|
16605
|
+
for (let a = 0; a < m; a++) s += Qxz[r][a] * y[a];
|
|
16606
|
+
shift[r] = -s;
|
|
16607
|
+
}
|
|
16608
|
+
return {
|
|
16609
|
+
fixed: true,
|
|
16610
|
+
position: [
|
|
16611
|
+
floatPos[0] + shift[0],
|
|
16612
|
+
floatPos[1] + shift[1],
|
|
16613
|
+
floatPos[2] + shift[2]
|
|
16614
|
+
],
|
|
16615
|
+
floatPosition: floatPos,
|
|
16616
|
+
shift,
|
|
16617
|
+
nFixed: m,
|
|
16618
|
+
ratio: res.ratio,
|
|
16619
|
+
refPrn: res.refPrn
|
|
16620
|
+
};
|
|
16621
|
+
}
|
|
16622
|
+
|
|
16326
16623
|
// src/positioning/ppp-tropo.ts
|
|
16327
16624
|
var HYD_AVG_A = [
|
|
16328
16625
|
0.0012769934,
|
|
@@ -16530,13 +16827,11 @@ function createPppCorrections(cfg) {
|
|
|
16530
16827
|
let rangeM = 0;
|
|
16531
16828
|
let phaseWindupM = 0;
|
|
16532
16829
|
const e = geom.los;
|
|
16533
|
-
const [
|
|
16830
|
+
const [latRad, lonRad] = ecefToGeodetic(
|
|
16534
16831
|
geom.rcvEcef[0],
|
|
16535
16832
|
geom.rcvEcef[1],
|
|
16536
16833
|
geom.rcvEcef[2]
|
|
16537
16834
|
);
|
|
16538
|
-
const latRad = latDeg * Math.PI / 180;
|
|
16539
|
-
const lonRad = lonDeg * Math.PI / 180;
|
|
16540
16835
|
const frame = ctx ? satBodyFrame(geom.satEcef, ctx.sun) : null;
|
|
16541
16836
|
if (useSat && frame && geom.band1 && geom.band2) {
|
|
16542
16837
|
const off = cfg.antenna.satOffset(
|
|
@@ -16594,6 +16889,10 @@ function createPppCorrections(cfg) {
|
|
|
16594
16889
|
}
|
|
16595
16890
|
|
|
16596
16891
|
// src/positioning/ppp.ts
|
|
16892
|
+
var NO_FCB = {
|
|
16893
|
+
satWlFcb: /* @__PURE__ */ new Map(),
|
|
16894
|
+
satNlFcb: /* @__PURE__ */ new Map()
|
|
16895
|
+
};
|
|
16597
16896
|
function matVec(P, h) {
|
|
16598
16897
|
const n = P.length;
|
|
16599
16898
|
const out = new Array(n).fill(0);
|
|
@@ -16605,6 +16904,34 @@ function matVec(P, h) {
|
|
|
16605
16904
|
}
|
|
16606
16905
|
return out;
|
|
16607
16906
|
}
|
|
16907
|
+
function tdLsq(qs) {
|
|
16908
|
+
const M = [0, 1, 2, 3].map(() => [0, 0, 0, 0, 0]);
|
|
16909
|
+
for (const q of qs) {
|
|
16910
|
+
const a = [-q.v.e[0], -q.v.e[1], -q.v.e[2], 1];
|
|
16911
|
+
for (let i = 0; i < 4; i++) {
|
|
16912
|
+
M[i][4] += a[i] * q.d;
|
|
16913
|
+
for (let j = 0; j < 4; j++) M[i][j] += a[i] * a[j];
|
|
16914
|
+
}
|
|
16915
|
+
}
|
|
16916
|
+
for (let c = 0; c < 4; c++) {
|
|
16917
|
+
let p = c;
|
|
16918
|
+
for (let r = c + 1; r < 4; r++)
|
|
16919
|
+
if (Math.abs(M[r][c]) > Math.abs(M[p][c])) p = r;
|
|
16920
|
+
if (Math.abs(M[p][c]) < 1e-9) return null;
|
|
16921
|
+
[M[c], M[p]] = [M[p], M[c]];
|
|
16922
|
+
for (let r = 0; r < 4; r++) {
|
|
16923
|
+
if (r === c) continue;
|
|
16924
|
+
const f = M[r][c] / M[c][c];
|
|
16925
|
+
for (let k = c; k < 5; k++) M[r][k] -= f * M[c][k];
|
|
16926
|
+
}
|
|
16927
|
+
}
|
|
16928
|
+
return [0, 1, 2, 3].map((i) => M[i][4] / M[i][i]);
|
|
16929
|
+
}
|
|
16930
|
+
function tdResidual(q, s) {
|
|
16931
|
+
return q.d - (-q.v.e[0] * s[0] - q.v.e[1] * s[1] - q.v.e[2] * s[2] + s[3]);
|
|
16932
|
+
}
|
|
16933
|
+
var TD_SLIP_M = 0.08;
|
|
16934
|
+
var CODE_GATE_M = 15;
|
|
16608
16935
|
function ekfUpdateScalar(x, P, h, innovation, r, rejectK = 0) {
|
|
16609
16936
|
const n = x.length;
|
|
16610
16937
|
const Ph = matVec(P, h);
|
|
@@ -16637,10 +16964,17 @@ function ifCoeffs(f1, f2) {
|
|
|
16637
16964
|
return { g, lambdaIf };
|
|
16638
16965
|
}
|
|
16639
16966
|
var Sp3EphemerisSource = class {
|
|
16640
|
-
constructor(sp3, clk) {
|
|
16967
|
+
constructor(sp3, clk, osb) {
|
|
16641
16968
|
this.sp3 = sp3;
|
|
16642
16969
|
this.clk = clk;
|
|
16970
|
+
this.osb = osb;
|
|
16971
|
+
if (osb) {
|
|
16972
|
+
this.codeBias = (prn, code, t) => osb.get(prn, "C" + code, t);
|
|
16973
|
+
this.phaseBias = (prn, code, t) => osb.get(prn, "L" + code, t);
|
|
16974
|
+
}
|
|
16643
16975
|
}
|
|
16976
|
+
codeBias;
|
|
16977
|
+
phaseBias;
|
|
16644
16978
|
satState(prn, tEmitMs) {
|
|
16645
16979
|
const p = sp3Position(this.sp3, prn, tEmitMs);
|
|
16646
16980
|
if (!p || p.clk == null) return null;
|
|
@@ -16658,6 +16992,21 @@ var Sp3EphemerisSource = class {
|
|
|
16658
16992
|
return { x: p.x, y: p.y, z: p.z, vx, vy, vz, clkS };
|
|
16659
16993
|
}
|
|
16660
16994
|
};
|
|
16995
|
+
function applySatBiases(source, o, tMs) {
|
|
16996
|
+
if (!source.codeBias && !source.phaseBias) return o;
|
|
16997
|
+
const b1 = source.codeBias?.(o.prn, o.code1 ?? "", tMs) ?? 0;
|
|
16998
|
+
const b2 = source.codeBias?.(o.prn, o.code2 ?? "", tMs) ?? 0;
|
|
16999
|
+
let l1 = o.l1;
|
|
17000
|
+
let l2 = o.l2;
|
|
17001
|
+
if (source.phaseBias) {
|
|
17002
|
+
const p1 = source.phaseBias(o.prn, o.phase1 ?? o.code1 ?? "", tMs);
|
|
17003
|
+
const p2 = source.phaseBias(o.prn, o.phase2 ?? o.code2 ?? "", tMs);
|
|
17004
|
+
if (p1 == null || p2 == null) return null;
|
|
17005
|
+
l1 -= p1 / (C_LIGHT / o.f1);
|
|
17006
|
+
l2 -= p2 / (C_LIGHT / o.f2);
|
|
17007
|
+
}
|
|
17008
|
+
return { ...o, c1: o.c1 - b1, c2: o.c2 - b2, l1, l2 };
|
|
17009
|
+
}
|
|
16661
17010
|
function sagnacRotate(x, y, z, travelS) {
|
|
16662
17011
|
const theta = OMEGA_E * travelS;
|
|
16663
17012
|
const c = Math.cos(theta);
|
|
@@ -16719,6 +17068,16 @@ var PppEngine = class {
|
|
|
16719
17068
|
arcs;
|
|
16720
17069
|
arcSnap;
|
|
16721
17070
|
mwState;
|
|
17071
|
+
/** Previous epoch's phase terms per satellite, for the time-differenced
|
|
17072
|
+
* (clock-free) cycle-slip test. */
|
|
17073
|
+
tdPrev;
|
|
17074
|
+
/** Consecutive gated-out phase updates per satellite. */
|
|
17075
|
+
phaseRejects;
|
|
17076
|
+
/** PPP-AR: the latest accepted fix (null: float). */
|
|
17077
|
+
lastFix;
|
|
17078
|
+
lastArMs;
|
|
17079
|
+
firstFixMs;
|
|
17080
|
+
fixedEpochs;
|
|
16722
17081
|
series;
|
|
16723
17082
|
ei = 0;
|
|
16724
17083
|
t0 = 0;
|
|
@@ -16754,6 +17113,12 @@ var PppEngine = class {
|
|
|
16754
17113
|
this.arcs = [];
|
|
16755
17114
|
this.arcSnap = /* @__PURE__ */ new Map();
|
|
16756
17115
|
this.mwState = /* @__PURE__ */ new Map();
|
|
17116
|
+
this.tdPrev = /* @__PURE__ */ new Map();
|
|
17117
|
+
this.phaseRejects = /* @__PURE__ */ new Map();
|
|
17118
|
+
this.lastFix = null;
|
|
17119
|
+
this.lastArMs = -Infinity;
|
|
17120
|
+
this.firstFixMs = null;
|
|
17121
|
+
this.fixedEpochs = 0;
|
|
16757
17122
|
this.series = [];
|
|
16758
17123
|
this.ei = 0;
|
|
16759
17124
|
this.t0 = 0;
|
|
@@ -16847,7 +17212,9 @@ var PppEngine = class {
|
|
|
16847
17212
|
lastSeen,
|
|
16848
17213
|
arcSnap,
|
|
16849
17214
|
mwState,
|
|
16850
|
-
series
|
|
17215
|
+
series,
|
|
17216
|
+
tdPrev,
|
|
17217
|
+
phaseRejects
|
|
16851
17218
|
} = this;
|
|
16852
17219
|
const flushArc = this.flushArc.bind(this);
|
|
16853
17220
|
const detectSlip = this.detectSlip.bind(this);
|
|
@@ -16869,13 +17236,15 @@ var PppEngine = class {
|
|
|
16869
17236
|
for (const ci of clkIdx.values()) P[ci][ci] = CLK_VAR;
|
|
16870
17237
|
P[IDX_ZWD][IDX_ZWD] += ztdQ * (ei > 0 ? 1 : 0);
|
|
16871
17238
|
const rcv = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
|
|
16872
|
-
const [
|
|
16873
|
-
const latRad = latDeg * Math.PI / 180;
|
|
17239
|
+
const [latRad, , heightM] = ecefToGeodetic(rcv[0], rcv[1], rcv[2]);
|
|
16874
17240
|
const epochCtx = corrections?.epochContext?.(epoch.timeMs, rcv) ?? null;
|
|
16875
17241
|
const vis = [];
|
|
16876
17242
|
let epochZhd = 0;
|
|
16877
|
-
for (const
|
|
16878
|
-
if (
|
|
17243
|
+
for (const raw of epoch.obs) {
|
|
17244
|
+
if (raw.c1 === 0 || raw.c2 === 0 || raw.l1 === 0 || raw.l2 === 0)
|
|
17245
|
+
continue;
|
|
17246
|
+
const o = applySatBiases(source, raw, epoch.timeMs);
|
|
17247
|
+
if (!o) continue;
|
|
16879
17248
|
const slip = detectSlip(o, ei);
|
|
16880
17249
|
const sat = satStateAtEmission(source, o.prn, epoch.timeMs, rcv);
|
|
16881
17250
|
if (!sat) continue;
|
|
@@ -16897,9 +17266,7 @@ var PppEngine = class {
|
|
|
16897
17266
|
const lam1 = C_LIGHT / o.f1;
|
|
16898
17267
|
const lam2 = C_LIGHT / o.f2;
|
|
16899
17268
|
if (o.c1 === 0 || o.c2 === 0 || o.l1 === 0 || o.l2 === 0) continue;
|
|
16900
|
-
const
|
|
16901
|
-
const b2 = source.codeBias?.(o.prn, o.code2 ?? "", epoch.timeMs) ?? 0;
|
|
16902
|
-
const pIf = g * (o.c1 - b1) - (g - 1) * (o.c2 - b2);
|
|
17269
|
+
const pIf = g * o.c1 - (g - 1) * o.c2;
|
|
16903
17270
|
const lIf = g * (o.l1 * lam1) - (g - 1) * (o.l2 * lam2);
|
|
16904
17271
|
if (!Number.isFinite(pIf) || !Number.isFinite(lIf)) continue;
|
|
16905
17272
|
const trop = opts.troposphere ?? true ? niellMapping(elRad, latRad, heightM, epoch.timeMs) : { zhd: 0, mh: 0, mw: 0 };
|
|
@@ -16932,9 +17299,63 @@ var PppEngine = class {
|
|
|
16932
17299
|
windupM: corr.phaseWindupM,
|
|
16933
17300
|
slip,
|
|
16934
17301
|
f1: o.f1,
|
|
16935
|
-
f2: o.f2
|
|
17302
|
+
f2: o.f2,
|
|
17303
|
+
sat: [sat.state.x, sat.state.y, sat.state.z]
|
|
16936
17304
|
});
|
|
16937
17305
|
}
|
|
17306
|
+
{
|
|
17307
|
+
const dq = [];
|
|
17308
|
+
for (const v of vis) {
|
|
17309
|
+
const prev = tdPrev.get(v.prn);
|
|
17310
|
+
const model = -v.satClkM + v.tropoFixed + v.windupM;
|
|
17311
|
+
if (prev && prev.ei === ei - 1 && !v.slip) {
|
|
17312
|
+
const r0 = Math.hypot(
|
|
17313
|
+
prev.sat[0] - rcv[0],
|
|
17314
|
+
prev.sat[1] - rcv[1],
|
|
17315
|
+
prev.sat[2] - rcv[2]
|
|
17316
|
+
);
|
|
17317
|
+
dq.push({
|
|
17318
|
+
v,
|
|
17319
|
+
d: v.lIf - prev.lIf - (v.range - r0) - (model - prev.model)
|
|
17320
|
+
});
|
|
17321
|
+
}
|
|
17322
|
+
tdPrev.set(v.prn, { ei, lIf: v.lIf, sat: v.sat, model });
|
|
17323
|
+
}
|
|
17324
|
+
if (!kinematic) {
|
|
17325
|
+
if (dq.length >= 4) {
|
|
17326
|
+
const sorted = dq.map((q) => q.d).sort((a, b) => a - b);
|
|
17327
|
+
const med = sorted[Math.floor(sorted.length / 2)];
|
|
17328
|
+
for (const q of dq)
|
|
17329
|
+
if (Math.abs(q.d - med) > TD_SLIP_M) q.v.slip = true;
|
|
17330
|
+
}
|
|
17331
|
+
} else if (dq.length >= 6) {
|
|
17332
|
+
let use = dq;
|
|
17333
|
+
for (let iter = 0; iter < 6; iter++) {
|
|
17334
|
+
const sol = use.length >= 6 ? tdLsq(use) : null;
|
|
17335
|
+
if (!sol) {
|
|
17336
|
+
for (const q of dq) q.v.slip = true;
|
|
17337
|
+
break;
|
|
17338
|
+
}
|
|
17339
|
+
let worst = -1;
|
|
17340
|
+
let worstR = 0;
|
|
17341
|
+
use.forEach((q, k) => {
|
|
17342
|
+
const r = Math.abs(tdResidual(q, sol));
|
|
17343
|
+
if (r > worstR) {
|
|
17344
|
+
worstR = r;
|
|
17345
|
+
worst = k;
|
|
17346
|
+
}
|
|
17347
|
+
});
|
|
17348
|
+
if (worstR <= TD_SLIP_M || iter === 5) {
|
|
17349
|
+
for (const q of dq)
|
|
17350
|
+
if (Math.abs(tdResidual(q, sol)) > TD_SLIP_M) q.v.slip = true;
|
|
17351
|
+
break;
|
|
17352
|
+
}
|
|
17353
|
+
use = use.filter((_, k) => k !== worst);
|
|
17354
|
+
}
|
|
17355
|
+
} else {
|
|
17356
|
+
for (const q of dq) q.v.slip = true;
|
|
17357
|
+
}
|
|
17358
|
+
}
|
|
16938
17359
|
const zwd0 = x[IDX_ZWD];
|
|
16939
17360
|
const bySys = /* @__PURE__ */ new Map();
|
|
16940
17361
|
for (const v of vis) {
|
|
@@ -16944,6 +17365,20 @@ var PppEngine = class {
|
|
|
16944
17365
|
if (arr) arr.push(r);
|
|
16945
17366
|
else bySys.set(sys, [r]);
|
|
16946
17367
|
}
|
|
17368
|
+
const posSigma = Math.sqrt(
|
|
17369
|
+
P[IDX_X][IDX_X] + P[IDX_Y][IDX_Y] + P[IDX_Z][IDX_Z]
|
|
17370
|
+
);
|
|
17371
|
+
const codeGate = CODE_GATE_M + 3 * posSigma;
|
|
17372
|
+
const screened = /* @__PURE__ */ new Set();
|
|
17373
|
+
for (const [sys, res] of bySys) {
|
|
17374
|
+
if (res.length < 4) continue;
|
|
17375
|
+
const med = [...res].sort((a, b) => a - b)[Math.floor(res.length / 2)];
|
|
17376
|
+
for (const v of vis) {
|
|
17377
|
+
if (v.prn[0] !== sys) continue;
|
|
17378
|
+
const r = v.pIf - (v.range - v.satClkM + v.tropoFixed + zwd0 * v.mw);
|
|
17379
|
+
if (Math.abs(r - med) > codeGate) screened.add(v.prn);
|
|
17380
|
+
}
|
|
17381
|
+
}
|
|
16947
17382
|
for (const [sys, res] of bySys) {
|
|
16948
17383
|
const ci = clockOf(sys);
|
|
16949
17384
|
if (!clockSeeded.has(sys)) {
|
|
@@ -16954,9 +17389,11 @@ var PppEngine = class {
|
|
|
16954
17389
|
P[ci][ci] = CLK_VAR;
|
|
16955
17390
|
}
|
|
16956
17391
|
let nSats = 0;
|
|
16957
|
-
|
|
17392
|
+
const live = vis.filter((v) => !screened.has(v.prn));
|
|
17393
|
+
const rangeNow = (v) => v.range - (v.e[0] * (x[IDX_X] - rcv[0]) + v.e[1] * (x[IDX_Y] - rcv[1]) + v.e[2] * (x[IDX_Z] - rcv[2]));
|
|
17394
|
+
const prepAmbiguity = (v) => {
|
|
16958
17395
|
const ci = clockOf(v.prn[0]);
|
|
16959
|
-
const common = v
|
|
17396
|
+
const common = rangeNow(v) + x[ci] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
|
|
16960
17397
|
if (v.slip) flushArc(v.prn);
|
|
16961
17398
|
let ai = ambIdx.get(v.prn);
|
|
16962
17399
|
if (ai === void 0 || v.slip) {
|
|
@@ -16973,67 +17410,91 @@ var PppEngine = class {
|
|
|
16973
17410
|
}
|
|
16974
17411
|
ambIdx.set(v.prn, ai);
|
|
16975
17412
|
}
|
|
16976
|
-
|
|
17413
|
+
return ai;
|
|
17414
|
+
};
|
|
17415
|
+
const codeUpdate = (v) => {
|
|
17416
|
+
const ci = clockOf(v.prn[0]);
|
|
17417
|
+
const common = rangeNow(v) + x[ci] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
|
|
17418
|
+
const sinEl = Math.max(Math.sin(v.elRad), 0.1);
|
|
17419
|
+
const h = new Array(x.length).fill(0);
|
|
17420
|
+
h[IDX_X] = -v.e[0];
|
|
17421
|
+
h[IDX_Y] = -v.e[1];
|
|
17422
|
+
h[IDX_Z] = -v.e[2];
|
|
17423
|
+
h[ci] = 1;
|
|
17424
|
+
h[IDX_ZWD] = v.mw;
|
|
17425
|
+
const innov = v.pIf - common;
|
|
17426
|
+
if (Math.abs(innov) < 1e6)
|
|
17427
|
+
ekfUpdateScalar(x, P, h, innov, (codeSigma / sinEl) ** 2, 4);
|
|
17428
|
+
};
|
|
17429
|
+
const phaseUpdate = (v, ai) => {
|
|
17430
|
+
const ci = clockOf(v.prn[0]);
|
|
16977
17431
|
const sinEl = Math.max(Math.sin(v.elRad), 0.1);
|
|
16978
|
-
const rCode = (codeSigma / sinEl) ** 2;
|
|
16979
17432
|
const rPhase = (phaseSigma / sinEl) ** 2;
|
|
16980
|
-
|
|
16981
|
-
|
|
17433
|
+
const commonPh = rangeNow(v) + x[ci] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
|
|
17434
|
+
const innov = v.lIf - (commonPh + v.windupM + x[ai]);
|
|
17435
|
+
const reinit = (phaseRejects.get(v.prn) ?? 0) >= 3;
|
|
17436
|
+
let used = false;
|
|
17437
|
+
if (!reinit) {
|
|
17438
|
+
const h = new Array(x.length).fill(0);
|
|
16982
17439
|
h[IDX_X] = -v.e[0];
|
|
16983
17440
|
h[IDX_Y] = -v.e[1];
|
|
16984
17441
|
h[IDX_Z] = -v.e[2];
|
|
16985
17442
|
h[ci] = 1;
|
|
16986
17443
|
h[IDX_ZWD] = v.mw;
|
|
16987
|
-
|
|
16988
|
-
|
|
17444
|
+
h[ai] = 1;
|
|
17445
|
+
used = ekfUpdateScalar(x, P, h, innov, rPhase, 4);
|
|
16989
17446
|
}
|
|
16990
|
-
{
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
16994
|
-
|
|
16995
|
-
|
|
16996
|
-
for (let j = 0; j < P.length; j++) {
|
|
16997
|
-
P[ai][j] = 0;
|
|
16998
|
-
P[j][ai] = 0;
|
|
16999
|
-
}
|
|
17000
|
-
P[ai][ai] = 100 * 100;
|
|
17001
|
-
} else {
|
|
17002
|
-
const h = new Array(n).fill(0);
|
|
17003
|
-
h[IDX_X] = -v.e[0];
|
|
17004
|
-
h[IDX_Y] = -v.e[1];
|
|
17005
|
-
h[IDX_Z] = -v.e[2];
|
|
17006
|
-
h[ci] = 1;
|
|
17007
|
-
h[IDX_ZWD] = v.mw;
|
|
17008
|
-
h[ai] = 1;
|
|
17009
|
-
ekfUpdateScalar(x, P, h, innov, rPhase, 4);
|
|
17010
|
-
}
|
|
17011
|
-
}
|
|
17012
|
-
{
|
|
17013
|
-
const elevDeg = v.elRad * 180 / Math.PI;
|
|
17014
|
-
const mwMean = mwState.get(v.prn)?.mean ?? 0;
|
|
17015
|
-
const s = arcSnap.get(v.prn);
|
|
17016
|
-
if (s) {
|
|
17017
|
-
s.aIF = x[ai];
|
|
17018
|
-
s.mwCyc = mwMean;
|
|
17019
|
-
s.elevSum += elevDeg;
|
|
17020
|
-
s.nEpochs++;
|
|
17021
|
-
s.endMs = epoch.timeMs;
|
|
17022
|
-
} else {
|
|
17023
|
-
arcSnap.set(v.prn, {
|
|
17024
|
-
aIF: x[ai],
|
|
17025
|
-
mwCyc: mwMean,
|
|
17026
|
-
f1: v.f1,
|
|
17027
|
-
f2: v.f2,
|
|
17028
|
-
elevSum: elevDeg,
|
|
17029
|
-
nEpochs: 1,
|
|
17030
|
-
startMs: epoch.timeMs,
|
|
17031
|
-
endMs: epoch.timeMs
|
|
17032
|
-
});
|
|
17447
|
+
if (reinit) {
|
|
17448
|
+
flushArc(v.prn);
|
|
17449
|
+
x[ai] = v.lIf - (commonPh + v.windupM);
|
|
17450
|
+
for (let j = 0; j < P.length; j++) {
|
|
17451
|
+
P[ai][j] = 0;
|
|
17452
|
+
P[j][ai] = 0;
|
|
17033
17453
|
}
|
|
17454
|
+
P[ai][ai] = 100 * 100;
|
|
17455
|
+
phaseRejects.delete(v.prn);
|
|
17456
|
+
} else if (used) phaseRejects.delete(v.prn);
|
|
17457
|
+
else phaseRejects.set(v.prn, (phaseRejects.get(v.prn) ?? 0) + 1);
|
|
17458
|
+
};
|
|
17459
|
+
const snapshot = (v, ai) => {
|
|
17460
|
+
const elevDeg = v.elRad * 180 / Math.PI;
|
|
17461
|
+
const mwMean = mwState.get(v.prn)?.mean ?? 0;
|
|
17462
|
+
const s = arcSnap.get(v.prn);
|
|
17463
|
+
if (s) {
|
|
17464
|
+
s.aIF = x[ai];
|
|
17465
|
+
s.mwCyc = mwMean;
|
|
17466
|
+
s.elevSum += elevDeg;
|
|
17467
|
+
s.nEpochs++;
|
|
17468
|
+
s.endMs = epoch.timeMs;
|
|
17469
|
+
} else {
|
|
17470
|
+
arcSnap.set(v.prn, {
|
|
17471
|
+
aIF: x[ai],
|
|
17472
|
+
mwCyc: mwMean,
|
|
17473
|
+
f1: v.f1,
|
|
17474
|
+
f2: v.f2,
|
|
17475
|
+
elevSum: elevDeg,
|
|
17476
|
+
nEpochs: 1,
|
|
17477
|
+
startMs: epoch.timeMs,
|
|
17478
|
+
endMs: epoch.timeMs
|
|
17479
|
+
});
|
|
17034
17480
|
}
|
|
17035
17481
|
lastSeen.set(v.prn, ei);
|
|
17036
17482
|
nSats++;
|
|
17483
|
+
};
|
|
17484
|
+
if (kinematic) {
|
|
17485
|
+
for (const v of live) codeUpdate(v);
|
|
17486
|
+
for (const v of live) {
|
|
17487
|
+
const ai = prepAmbiguity(v);
|
|
17488
|
+
phaseUpdate(v, ai);
|
|
17489
|
+
snapshot(v, ai);
|
|
17490
|
+
}
|
|
17491
|
+
} else {
|
|
17492
|
+
for (const v of live) {
|
|
17493
|
+
const ai = prepAmbiguity(v);
|
|
17494
|
+
codeUpdate(v);
|
|
17495
|
+
phaseUpdate(v, ai);
|
|
17496
|
+
snapshot(v, ai);
|
|
17497
|
+
}
|
|
17037
17498
|
}
|
|
17038
17499
|
let sumPhaseResSq = 0;
|
|
17039
17500
|
let nPhaseRes = 0;
|
|
@@ -17044,7 +17505,7 @@ var PppEngine = class {
|
|
|
17044
17505
|
if (ai === void 0) continue;
|
|
17045
17506
|
const sys = v.prn[0];
|
|
17046
17507
|
const ci = clockOf(sys);
|
|
17047
|
-
const commonPh = v
|
|
17508
|
+
const commonPh = rangeNow(v) + x[ci] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
|
|
17048
17509
|
const innov = v.lIf - (commonPh + v.windupM + x[ai]);
|
|
17049
17510
|
if (Math.abs(innov) >= 0.15) continue;
|
|
17050
17511
|
const arr = bySysRes.get(sys);
|
|
@@ -17068,6 +17529,8 @@ var PppEngine = class {
|
|
|
17068
17529
|
flushArc(prn);
|
|
17069
17530
|
}
|
|
17070
17531
|
}
|
|
17532
|
+
this.tryFix(epoch.timeMs, vis);
|
|
17533
|
+
if (this.lastFix) this.fixedEpochs++;
|
|
17071
17534
|
const pos = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
|
|
17072
17535
|
let enu = null;
|
|
17073
17536
|
let error3d = null;
|
|
@@ -17103,12 +17566,75 @@ var PppEngine = class {
|
|
|
17103
17566
|
nSats,
|
|
17104
17567
|
ztdWet: x[IDX_ZWD],
|
|
17105
17568
|
ztdHydrostatic: epochZhd,
|
|
17106
|
-
phaseResRms: nPhaseRes > 0 ? Math.sqrt(sumPhaseResSq / nPhaseRes) : 0
|
|
17569
|
+
phaseResRms: nPhaseRes > 0 ? Math.sqrt(sumPhaseResSq / nPhaseRes) : 0,
|
|
17570
|
+
nFixed: this.lastFix?.nFixed ?? 0,
|
|
17571
|
+
fixedPosition: this.lastFix?.position ?? null
|
|
17107
17572
|
};
|
|
17108
17573
|
series.push(result);
|
|
17109
17574
|
this.ei = ei + 1;
|
|
17110
17575
|
return result;
|
|
17111
17576
|
}
|
|
17577
|
+
/**
|
|
17578
|
+
* PPP-AR. Every `everyS`, resolve the converged ambiguities of the
|
|
17579
|
+
* satellites in view (between satellites of one constellation) and, when
|
|
17580
|
+
* the fix passes the ratio, success-rate and χ² tests, condition the
|
|
17581
|
+
* float position on it ({@link fixPppPosition}). The float filter itself
|
|
17582
|
+
* is never constrained — a wrong fix can't contaminate later epochs, and
|
|
17583
|
+
* the next attempt starts clean. Between attempts the last fix carries
|
|
17584
|
+
* over (static) or is dropped (kinematic).
|
|
17585
|
+
*/
|
|
17586
|
+
tryFix(tMs, vis) {
|
|
17587
|
+
const ar = this.opts.ar;
|
|
17588
|
+
if (!ar || !this.source.phaseBias) return;
|
|
17589
|
+
const cfg = ar === true ? {} : ar;
|
|
17590
|
+
if (tMs - this.lastArMs < (cfg.everyS ?? 120) * 1e3) {
|
|
17591
|
+
if (this.kinematic) this.lastFix = null;
|
|
17592
|
+
return;
|
|
17593
|
+
}
|
|
17594
|
+
this.lastArMs = tMs;
|
|
17595
|
+
this.lastFix = null;
|
|
17596
|
+
const { x, P, ambIdx, arcSnap, mwState } = this;
|
|
17597
|
+
const minArcMs = (cfg.minArcS ?? 600) * 1e3;
|
|
17598
|
+
const ambiguities = [];
|
|
17599
|
+
for (const v of vis) {
|
|
17600
|
+
const index = ambIdx.get(v.prn);
|
|
17601
|
+
const snap = arcSnap.get(v.prn);
|
|
17602
|
+
const mw = mwState.get(v.prn);
|
|
17603
|
+
if (index === void 0 || !snap || !mw) continue;
|
|
17604
|
+
if (tMs - snap.startMs < minArcMs || snap.nEpochs < 20) continue;
|
|
17605
|
+
ambiguities.push({
|
|
17606
|
+
prn: v.prn,
|
|
17607
|
+
index,
|
|
17608
|
+
aIF: x[index],
|
|
17609
|
+
mwCyc: mw.mean,
|
|
17610
|
+
f1: v.f1,
|
|
17611
|
+
f2: v.f2,
|
|
17612
|
+
elevDeg: v.elRad * 180 / Math.PI
|
|
17613
|
+
});
|
|
17614
|
+
}
|
|
17615
|
+
const fx = fixPppPosition(
|
|
17616
|
+
{
|
|
17617
|
+
position: [x[IDX_X], x[IDX_Y], x[IDX_Z]],
|
|
17618
|
+
covariance: P,
|
|
17619
|
+
ambiguities
|
|
17620
|
+
},
|
|
17621
|
+
NO_FCB,
|
|
17622
|
+
{
|
|
17623
|
+
ratioThreshold: cfg.ratio ?? 3,
|
|
17624
|
+
wlThreshold: cfg.wlThreshold ?? 0.15,
|
|
17625
|
+
minSuccessRate: cfg.successRate ?? 0.999,
|
|
17626
|
+
minFixed: cfg.minFixed ?? 5,
|
|
17627
|
+
maxChi2: cfg.maxChi2 ?? 20
|
|
17628
|
+
}
|
|
17629
|
+
);
|
|
17630
|
+
if (!fx.fixed) return;
|
|
17631
|
+
this.lastFix = {
|
|
17632
|
+
position: fx.position,
|
|
17633
|
+
nFixed: fx.nFixed,
|
|
17634
|
+
ratio: fx.ratio
|
|
17635
|
+
};
|
|
17636
|
+
this.firstFixMs ??= tMs;
|
|
17637
|
+
}
|
|
17112
17638
|
/** Finalise the session: capture the AR state, flush open arcs, assemble. */
|
|
17113
17639
|
solution() {
|
|
17114
17640
|
const { opts, x, P, ambIdx, arcSnap, mwState, arcs, series, truth } = this;
|
|
@@ -17141,7 +17667,12 @@ var PppEngine = class {
|
|
|
17141
17667
|
x[IDX_Y],
|
|
17142
17668
|
x[IDX_Z]
|
|
17143
17669
|
];
|
|
17144
|
-
const
|
|
17670
|
+
const geo = ecefToGeodetic(finalPos[0], finalPos[1], finalPos[2]);
|
|
17671
|
+
const llh = [
|
|
17672
|
+
geo[0] * 180 / Math.PI,
|
|
17673
|
+
geo[1] * 180 / Math.PI,
|
|
17674
|
+
geo[2]
|
|
17675
|
+
];
|
|
17145
17676
|
const finalError3d = truth ? Math.hypot(
|
|
17146
17677
|
finalPos[0] - truth[0],
|
|
17147
17678
|
finalPos[1] - truth[1],
|
|
@@ -17157,218 +17688,25 @@ var PppEngine = class {
|
|
|
17157
17688
|
convergenceSec: this.convergenceSec,
|
|
17158
17689
|
finalError3d,
|
|
17159
17690
|
arcs,
|
|
17160
|
-
finalState
|
|
17691
|
+
finalState,
|
|
17692
|
+
...opts.ar && this.source.phaseBias ? {
|
|
17693
|
+
ar: {
|
|
17694
|
+
firstFixSec: this.firstFixMs === null || !series.length ? null : (this.firstFixMs - series[0].timeMs) / 1e3,
|
|
17695
|
+
nFixedEnd: this.lastFix?.nFixed ?? 0,
|
|
17696
|
+
position: this.lastFix?.position ?? null,
|
|
17697
|
+
fixedEpochShare: series.length ? this.fixedEpochs / series.length : 0
|
|
17698
|
+
}
|
|
17699
|
+
} : {}
|
|
17161
17700
|
};
|
|
17162
17701
|
}
|
|
17163
17702
|
};
|
|
17164
17703
|
function solvePpp(epochs, sp3OrSource, opts) {
|
|
17165
|
-
const source = "satState" in sp3OrSource ? sp3OrSource : new Sp3EphemerisSource(sp3OrSource, opts.clk);
|
|
17704
|
+
const source = "satState" in sp3OrSource ? sp3OrSource : new Sp3EphemerisSource(sp3OrSource, opts.clk, opts.osb);
|
|
17166
17705
|
const eng = new PppEngine(opts, source);
|
|
17167
17706
|
for (const epoch of epochs) eng.process(epoch);
|
|
17168
17707
|
return eng.solution();
|
|
17169
17708
|
}
|
|
17170
17709
|
|
|
17171
|
-
// src/positioning/ppp-ar.ts
|
|
17172
|
-
function wlWavelength(f1, f2) {
|
|
17173
|
-
return C_LIGHT / (f1 - f2);
|
|
17174
|
-
}
|
|
17175
|
-
function nlWavelength(f1, f2) {
|
|
17176
|
-
return C_LIGHT / (f1 + f2);
|
|
17177
|
-
}
|
|
17178
|
-
function resolvePppAmbiguities(sats, Qaif, opts = {}) {
|
|
17179
|
-
const wlThreshold = opts.wlThreshold ?? 0.25;
|
|
17180
|
-
const ratioThreshold = opts.ratioThreshold ?? 3;
|
|
17181
|
-
const kept = [];
|
|
17182
|
-
const nWl = [];
|
|
17183
|
-
const rejectedWl = [];
|
|
17184
|
-
for (let i = 0; i < sats.length; i++) {
|
|
17185
|
-
const s = sats[i];
|
|
17186
|
-
const wlFloat = s.mwCyc - (s.wlBiasCyc ?? 0);
|
|
17187
|
-
const r = Math.round(wlFloat);
|
|
17188
|
-
if (Math.abs(wlFloat - r) <= wlThreshold) {
|
|
17189
|
-
kept.push(i);
|
|
17190
|
-
nWl.push(r);
|
|
17191
|
-
} else {
|
|
17192
|
-
rejectedWl.push(s.prn);
|
|
17193
|
-
}
|
|
17194
|
-
}
|
|
17195
|
-
if (kept.length < 2) {
|
|
17196
|
-
return {
|
|
17197
|
-
fixed: false,
|
|
17198
|
-
ratio: 0,
|
|
17199
|
-
refPrn: null,
|
|
17200
|
-
sats: [],
|
|
17201
|
-
rejectedWl
|
|
17202
|
-
};
|
|
17203
|
-
}
|
|
17204
|
-
const n1Float = [];
|
|
17205
|
-
const lamNl = [];
|
|
17206
|
-
for (let k = 0; k < kept.length; k++) {
|
|
17207
|
-
const s = sats[kept[k]];
|
|
17208
|
-
const lNl = nlWavelength(s.f1, s.f2);
|
|
17209
|
-
lamNl.push(lNl);
|
|
17210
|
-
const n1 = s.aIF / lNl - s.f2 / (s.f1 - s.f2) * nWl[k] - (s.nlBiasCyc ?? 0);
|
|
17211
|
-
n1Float.push(n1);
|
|
17212
|
-
}
|
|
17213
|
-
let ref = 0;
|
|
17214
|
-
for (let k = 1; k < kept.length; k++) {
|
|
17215
|
-
const e = sats[kept[k]].elevDeg ?? -1;
|
|
17216
|
-
if (e > (sats[kept[ref]].elevDeg ?? -1)) ref = k;
|
|
17217
|
-
}
|
|
17218
|
-
const m = kept.length - 1;
|
|
17219
|
-
const sd2 = new Float64Array(m);
|
|
17220
|
-
const Qsd = new Float64Array(m * m);
|
|
17221
|
-
const others = [];
|
|
17222
|
-
for (let k = 0; k < kept.length; k++) if (k !== ref) others.push(k);
|
|
17223
|
-
const n = sats.length;
|
|
17224
|
-
const qN1 = (ka, kb) => Qaif[kept[ka] * n + kept[kb]] / (lamNl[ka] * lamNl[kb]);
|
|
17225
|
-
for (let a = 0; a < m; a++) {
|
|
17226
|
-
const ka = others[a];
|
|
17227
|
-
sd2[a] = n1Float[ka] - n1Float[ref];
|
|
17228
|
-
for (let b = 0; b < m; b++) {
|
|
17229
|
-
const kb = others[b];
|
|
17230
|
-
Qsd[a * m + b] = qN1(ka, kb) - qN1(ka, ref) - qN1(ref, kb) + qN1(ref, ref);
|
|
17231
|
-
}
|
|
17232
|
-
}
|
|
17233
|
-
const res = lambdaSearch(sd2, Qsd, m, 2);
|
|
17234
|
-
const ratio = res ? Math.min(res.ratio, 999.9) : 0;
|
|
17235
|
-
const fixed = !!res && ratio >= ratioThreshold;
|
|
17236
|
-
const refN1 = Math.round(n1Float[ref]);
|
|
17237
|
-
const sdFix = res?.candidates[0];
|
|
17238
|
-
const outSats = [];
|
|
17239
|
-
for (let k = 0; k < kept.length; k++) {
|
|
17240
|
-
const s = sats[kept[k]];
|
|
17241
|
-
let n1;
|
|
17242
|
-
if (k === ref) {
|
|
17243
|
-
n1 = refN1;
|
|
17244
|
-
} else {
|
|
17245
|
-
const a = others.indexOf(k);
|
|
17246
|
-
n1 = fixed && sdFix ? refN1 + Math.round(sdFix[a]) : Math.round(n1Float[k]);
|
|
17247
|
-
}
|
|
17248
|
-
const lNl = lamNl[k];
|
|
17249
|
-
const aIF = lNl * n1 + lNl * (s.f2 / (s.f1 - s.f2)) * nWl[k];
|
|
17250
|
-
outSats.push({ prn: s.prn, nWl: nWl[k], n1, aIF });
|
|
17251
|
-
}
|
|
17252
|
-
return {
|
|
17253
|
-
fixed,
|
|
17254
|
-
ratio,
|
|
17255
|
-
refPrn: sats[kept[ref]].prn,
|
|
17256
|
-
sats: outSats,
|
|
17257
|
-
rejectedWl
|
|
17258
|
-
};
|
|
17259
|
-
}
|
|
17260
|
-
function solveSpd(A, b) {
|
|
17261
|
-
const n = b.length;
|
|
17262
|
-
const L = Array.from({ length: n }, () => new Array(n).fill(0));
|
|
17263
|
-
for (let i = 0; i < n; i++) {
|
|
17264
|
-
for (let j = 0; j <= i; j++) {
|
|
17265
|
-
let s = A[i][j];
|
|
17266
|
-
for (let k = 0; k < j; k++) s -= L[i][k] * L[j][k];
|
|
17267
|
-
if (i === j) {
|
|
17268
|
-
if (s <= 0) return null;
|
|
17269
|
-
L[i][j] = Math.sqrt(s);
|
|
17270
|
-
} else {
|
|
17271
|
-
L[i][j] = s / L[j][j];
|
|
17272
|
-
}
|
|
17273
|
-
}
|
|
17274
|
-
}
|
|
17275
|
-
const y = new Array(n).fill(0);
|
|
17276
|
-
for (let i = 0; i < n; i++) {
|
|
17277
|
-
let s = b[i];
|
|
17278
|
-
for (let k = 0; k < i; k++) s -= L[i][k] * y[k];
|
|
17279
|
-
y[i] = s / L[i][i];
|
|
17280
|
-
}
|
|
17281
|
-
const x = new Array(n).fill(0);
|
|
17282
|
-
for (let i = n - 1; i >= 0; i--) {
|
|
17283
|
-
let s = y[i];
|
|
17284
|
-
for (let k = i + 1; k < n; k++) s -= L[k][i] * x[k];
|
|
17285
|
-
x[i] = s / L[i][i];
|
|
17286
|
-
}
|
|
17287
|
-
return x;
|
|
17288
|
-
}
|
|
17289
|
-
function fixPppPosition(state, fcb, opts = {}) {
|
|
17290
|
-
const floatPos = state.position;
|
|
17291
|
-
const noFix = {
|
|
17292
|
-
fixed: false,
|
|
17293
|
-
position: floatPos,
|
|
17294
|
-
floatPosition: floatPos,
|
|
17295
|
-
shift: [0, 0, 0],
|
|
17296
|
-
nFixed: 0,
|
|
17297
|
-
ratio: 0,
|
|
17298
|
-
refPrn: null
|
|
17299
|
-
};
|
|
17300
|
-
const amb = state.ambiguities;
|
|
17301
|
-
if (amb.length < 3) return noFix;
|
|
17302
|
-
const sats = amb.map((a) => ({
|
|
17303
|
-
prn: a.prn,
|
|
17304
|
-
aIF: a.aIF,
|
|
17305
|
-
mwCyc: a.mwCyc,
|
|
17306
|
-
f1: a.f1,
|
|
17307
|
-
f2: a.f2,
|
|
17308
|
-
wlBiasCyc: fcb.satWlFcb.get(a.prn) ?? 0,
|
|
17309
|
-
nlBiasCyc: fcb.satNlFcb.get(a.prn) ?? 0,
|
|
17310
|
-
elevDeg: a.elevDeg
|
|
17311
|
-
}));
|
|
17312
|
-
const n = sats.length;
|
|
17313
|
-
const Qaif = new Float64Array(n * n);
|
|
17314
|
-
for (let i = 0; i < n; i++)
|
|
17315
|
-
for (let j = 0; j < n; j++)
|
|
17316
|
-
Qaif[i * n + j] = state.covariance[amb[i].index][amb[j].index];
|
|
17317
|
-
const res = resolvePppAmbiguities(sats, Qaif, opts);
|
|
17318
|
-
if (!res.fixed || res.refPrn === null) {
|
|
17319
|
-
return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
|
|
17320
|
-
}
|
|
17321
|
-
const idxOfPrn = new Map(amb.map((a) => [a.prn, a.index]));
|
|
17322
|
-
const floatOfPrn = new Map(amb.map((a) => [a.prn, a.aIF]));
|
|
17323
|
-
const kept = res.sats.filter((s) => idxOfPrn.has(s.prn));
|
|
17324
|
-
const refPos = kept.findIndex((s) => s.prn === res.refPrn);
|
|
17325
|
-
if (refPos < 0 || kept.length < 2) {
|
|
17326
|
-
return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
|
|
17327
|
-
}
|
|
17328
|
-
const ambIdx = kept.map((s) => idxOfPrn.get(s.prn));
|
|
17329
|
-
const aFloat = kept.map((s) => floatOfPrn.get(s.prn));
|
|
17330
|
-
const aFixed = kept.map((s) => s.aIF);
|
|
17331
|
-
const others = kept.map((_, i) => i).filter((i) => i !== refPos);
|
|
17332
|
-
const m = others.length;
|
|
17333
|
-
const P = state.covariance;
|
|
17334
|
-
const qAA = (i, j) => P[ambIdx[i]][ambIdx[j]];
|
|
17335
|
-
const qXA = (r, i) => P[r][ambIdx[i]];
|
|
17336
|
-
const Qzz = Array.from({ length: m }, () => new Array(m).fill(0));
|
|
17337
|
-
const z = new Array(m).fill(0);
|
|
17338
|
-
for (let a = 0; a < m; a++) {
|
|
17339
|
-
const ia = others[a];
|
|
17340
|
-
z[a] = aFloat[ia] - aFloat[refPos] - (aFixed[ia] - aFixed[refPos]);
|
|
17341
|
-
for (let b = 0; b < m; b++) {
|
|
17342
|
-
const ib = others[b];
|
|
17343
|
-
Qzz[a][b] = qAA(ia, ib) - qAA(ia, refPos) - qAA(refPos, ib) + qAA(refPos, refPos);
|
|
17344
|
-
}
|
|
17345
|
-
}
|
|
17346
|
-
const Qxz = [0, 1, 2].map(
|
|
17347
|
-
(r) => others.map((ia) => qXA(r, ia) - qXA(r, refPos))
|
|
17348
|
-
);
|
|
17349
|
-
const y = solveSpd(Qzz, z);
|
|
17350
|
-
if (!y) return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
|
|
17351
|
-
const shift = [0, 0, 0];
|
|
17352
|
-
for (let r = 0; r < 3; r++) {
|
|
17353
|
-
let s = 0;
|
|
17354
|
-
for (let a = 0; a < m; a++) s += Qxz[r][a] * y[a];
|
|
17355
|
-
shift[r] = -s;
|
|
17356
|
-
}
|
|
17357
|
-
return {
|
|
17358
|
-
fixed: true,
|
|
17359
|
-
position: [
|
|
17360
|
-
floatPos[0] + shift[0],
|
|
17361
|
-
floatPos[1] + shift[1],
|
|
17362
|
-
floatPos[2] + shift[2]
|
|
17363
|
-
],
|
|
17364
|
-
floatPosition: floatPos,
|
|
17365
|
-
shift,
|
|
17366
|
-
nFixed: m,
|
|
17367
|
-
ratio: res.ratio,
|
|
17368
|
-
refPrn: res.refPrn
|
|
17369
|
-
};
|
|
17370
|
-
}
|
|
17371
|
-
|
|
17372
17710
|
// src/positioning/ppp-fcb.ts
|
|
17373
17711
|
function extractWidelaneArcs(station, epochs, opts = {}) {
|
|
17374
17712
|
const slipCyc = opts.slipCyc ?? 4;
|
|
@@ -20228,6 +20566,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
20228
20566
|
SYSTEM_NAMES,
|
|
20229
20567
|
SYSTEM_ORDER,
|
|
20230
20568
|
SYS_SHORT,
|
|
20569
|
+
SatOsbTable,
|
|
20231
20570
|
SbasProcessor,
|
|
20232
20571
|
Scale,
|
|
20233
20572
|
Sp3EphemerisSource,
|