gnss-js 2.0.0 → 2.1.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.
@@ -1918,6 +1918,55 @@ var SbasProcessor = class {
1918
1918
  }
1919
1919
  };
1920
1920
 
1921
+ // src/positioning/ssr-apply.ts
1922
+ var cross = (a, b) => [
1923
+ a[1] * b[2] - a[2] * b[1],
1924
+ a[2] * b[0] - a[0] * b[2],
1925
+ a[0] * b[1] - a[1] * b[0]
1926
+ ];
1927
+ var norm = (a) => Math.hypot(a[0], a[1], a[2]);
1928
+ var unit = (a) => {
1929
+ const n = norm(a);
1930
+ return n > 0 ? [a[0] / n, a[1] / n, a[2] / n] : [0, 0, 0];
1931
+ };
1932
+ function applyOrbitClock(eph, corr, timeMs) {
1933
+ if (eph.system === "R" || eph.system === "S") return null;
1934
+ if (eph.iode !== corr.iod) return null;
1935
+ const age = (timeMs - corr.refTimeMs) / 1e3;
1936
+ if (corr.validitySeconds != null && (age < -1 || age > corr.validitySeconds))
1937
+ return null;
1938
+ const s = computeSatPosition(eph, timeMs);
1939
+ if (!Number.isFinite(s.x)) return null;
1940
+ const p = [s.x, s.y, s.z];
1941
+ const v = [s.vx, s.vy, s.vz];
1942
+ const er = unit(p);
1943
+ const en = unit(cross(p, v));
1944
+ const ea = cross(en, er);
1945
+ const dr = corr.radial + (corr.dotRadial ?? 0) * age;
1946
+ const da = corr.along + (corr.dotAlong ?? 0) * age;
1947
+ const dc = corr.cross + (corr.dotCross ?? 0) * age;
1948
+ const x = s.x + dr * er[0] + da * ea[0] + dc * en[0];
1949
+ const y = s.y + dr * er[1] + da * ea[1] + dc * en[1];
1950
+ const z = s.z + dr * er[2] + da * ea[2] + dc * en[2];
1951
+ const clockM = corr.clockC0 + (corr.clockC1 ?? 0) * age + (corr.clockC2 ?? 0) * age * age + (corr.highRateClock ?? 0);
1952
+ return { x, y, z, clockOffsetS: clockM / C_LIGHT };
1953
+ }
1954
+ function hasToOrbitClock(orbit, clock, refTimeMs, validitySeconds) {
1955
+ if (orbit.deltaRadial == null || orbit.deltaInTrack == null || orbit.deltaCrossTrack == null)
1956
+ return null;
1957
+ const clockC0 = clock && clock.deltaClock != null && !clock.notUsable ? clock.deltaClock : 0;
1958
+ return {
1959
+ prn: orbit.prn,
1960
+ iod: orbit.gnssIod,
1961
+ refTimeMs,
1962
+ radial: orbit.deltaRadial,
1963
+ along: orbit.deltaInTrack,
1964
+ cross: orbit.deltaCrossTrack,
1965
+ clockC0,
1966
+ validitySeconds
1967
+ };
1968
+ }
1969
+
1921
1970
  // src/positioning/ppp-tropo.ts
1922
1971
  var HYD_AVG_A = [
1923
1972
  0.0012769934,
@@ -2175,14 +2224,14 @@ function buildPppAntenna(antex) {
2175
2224
  }
2176
2225
  };
2177
2226
  }
2178
- function cross(a, b) {
2227
+ function cross2(a, b) {
2179
2228
  return [
2180
2229
  a[1] * b[2] - a[2] * b[1],
2181
2230
  a[2] * b[0] - a[0] * b[2],
2182
2231
  a[0] * b[1] - a[1] * b[0]
2183
2232
  ];
2184
2233
  }
2185
- function norm(a) {
2234
+ function norm2(a) {
2186
2235
  const n = Math.hypot(a[0], a[1], a[2]) || 1;
2187
2236
  return [a[0] / n, a[1] / n, a[2] / n];
2188
2237
  }
@@ -2190,14 +2239,14 @@ function dot(a, b) {
2190
2239
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2191
2240
  }
2192
2241
  function satBodyFrame(satEcef, sunEcef2) {
2193
- const z = norm([-satEcef[0], -satEcef[1], -satEcef[2]]);
2194
- const eSun = norm([
2242
+ const z = norm2([-satEcef[0], -satEcef[1], -satEcef[2]]);
2243
+ const eSun = norm2([
2195
2244
  sunEcef2[0] - satEcef[0],
2196
2245
  sunEcef2[1] - satEcef[1],
2197
2246
  sunEcef2[2] - satEcef[2]
2198
2247
  ]);
2199
- const y = norm(cross(z, eSun));
2200
- const x = norm(cross(y, z));
2248
+ const y = norm2(cross2(z, eSun));
2249
+ const x = norm2(cross2(y, z));
2201
2250
  return { x, y, z };
2202
2251
  }
2203
2252
  function satPcoToEcef(pcoBody, frame) {
@@ -2238,7 +2287,7 @@ function phaseWindup(prn, losRcvToSat, satFrame, latRad, lonRad, state) {
2238
2287
  const kx = satFrame.x;
2239
2288
  const ky = satFrame.y;
2240
2289
  const kdotx = dot(k, kx);
2241
- const kcrossY = cross(k, ky);
2290
+ const kcrossY = cross2(k, ky);
2242
2291
  const dS = [
2243
2292
  kx[0] - k[0] * kdotx - kcrossY[0],
2244
2293
  kx[1] - k[1] * kdotx - kcrossY[1],
@@ -2247,7 +2296,7 @@ function phaseWindup(prn, losRcvToSat, satFrame, latRad, lonRad, state) {
2247
2296
  const north = enuToEcef([0, 1, 0], latRad, lonRad);
2248
2297
  const east = enuToEcef([1, 0, 0], latRad, lonRad);
2249
2298
  const kdotn = dot(k, north);
2250
- const kcrossE = cross(k, east);
2299
+ const kcrossE = cross2(k, east);
2251
2300
  const dR = [
2252
2301
  north[0] - k[0] * kdotn + kcrossE[0],
2253
2302
  north[1] - k[1] * kdotn + kcrossE[1],
@@ -2257,7 +2306,7 @@ function phaseWindup(prn, losRcvToSat, satFrame, latRad, lonRad, state) {
2257
2306
  const nR = Math.hypot(dR[0], dR[1], dR[2]) || 1;
2258
2307
  let cosPhi = dot(dS, dR) / (nS * nR);
2259
2308
  cosPhi = Math.max(-1, Math.min(1, cosPhi));
2260
- const zeta = cross(dS, dR);
2309
+ const zeta = cross2(dS, dR);
2261
2310
  const sign = dot(k, zeta) >= 0 ? 1 : -1;
2262
2311
  const dphi = sign * Math.acos(cosPhi) / (2 * Math.PI);
2263
2312
  const prev = state.get(prn);
@@ -2395,13 +2444,35 @@ function ifCoeffs(f1, f2) {
2395
2444
  const lambdaIf = C_LIGHT / (f1 + f2);
2396
2445
  return { g, lambdaIf };
2397
2446
  }
2447
+ var Sp3EphemerisSource = class {
2448
+ constructor(sp3, clk) {
2449
+ this.sp3 = sp3;
2450
+ this.clk = clk;
2451
+ }
2452
+ satState(prn, tEmitMs) {
2453
+ const p = sp3Position(this.sp3, prn, tEmitMs);
2454
+ if (!p || p.clk == null) return null;
2455
+ const pPlus = sp3Position(this.sp3, prn, tEmitMs + 500);
2456
+ const pMinus = sp3Position(this.sp3, prn, tEmitMs - 500);
2457
+ let vx = 0;
2458
+ let vy = 0;
2459
+ let vz = 0;
2460
+ if (pPlus && pMinus) {
2461
+ vx = pPlus.x - pMinus.x;
2462
+ vy = pPlus.y - pMinus.y;
2463
+ vz = pPlus.z - pMinus.z;
2464
+ }
2465
+ const clkS = (this.clk ? clkBias(this.clk, prn, tEmitMs) : null) ?? p.clk;
2466
+ return { x: p.x, y: p.y, z: p.z, vx, vy, vz, clkS };
2467
+ }
2468
+ };
2398
2469
  function sagnacRotate(x, y, z, travelS) {
2399
2470
  const theta = OMEGA_E * travelS;
2400
2471
  const c = Math.cos(theta);
2401
2472
  const s = Math.sin(theta);
2402
2473
  return [x * c + y * s, -x * s + y * c, z];
2403
2474
  }
2404
- function satStateAtEmission(sp3, prn, recvTimeMs, rcv, clk) {
2475
+ function satStateAtEmission(source, prn, recvTimeMs, rcv) {
2405
2476
  let travelS = 0.075;
2406
2477
  let sx = 0;
2407
2478
  let sy = 0;
@@ -2410,18 +2481,11 @@ function satStateAtEmission(sp3, prn, recvTimeMs, rcv, clk) {
2410
2481
  let velDotPos = 0;
2411
2482
  for (let iter = 0; iter < 3; iter++) {
2412
2483
  const tEmit = recvTimeMs - travelS * 1e3;
2413
- const p = sp3Position(sp3, prn, tEmit);
2414
- if (!p || p.clk == null) return null;
2415
- const pPlus = sp3Position(sp3, prn, tEmit + 500);
2416
- const pMinus = sp3Position(sp3, prn, tEmit - 500);
2417
- if (pPlus && pMinus) {
2418
- const vx = (pPlus.x - pMinus.x) / 1;
2419
- const vy = (pPlus.y - pMinus.y) / 1;
2420
- const vz = (pPlus.z - pMinus.z) / 1;
2421
- velDotPos = p.x * vx + p.y * vy + p.z * vz;
2422
- }
2423
- clkS = (clk && clkBias(clk, prn, tEmit)) ?? p.clk;
2424
- const [rx, ry, rz] = sagnacRotate(p.x, p.y, p.z, travelS);
2484
+ const s = source.satState(prn, tEmit);
2485
+ if (!s) return null;
2486
+ velDotPos = s.x * s.vx + s.y * s.vy + s.z * s.vz;
2487
+ clkS = s.clkS;
2488
+ const [rx, ry, rz] = sagnacRotate(s.x, s.y, s.z, travelS);
2425
2489
  sx = rx;
2426
2490
  sy = ry;
2427
2491
  sz = rz;
@@ -2443,39 +2507,72 @@ var IDX_ZWD = 3;
2443
2507
  var NBASE = 4;
2444
2508
  var CLK_VAR = 1e10;
2445
2509
  var POS_VAR = 60 * 60;
2446
- function solvePpp(epochs, sp3, opts) {
2447
- const elevMask = (opts.elevationMaskDeg ?? 10) * Math.PI / 180;
2448
- const codeSigma = opts.codeSigma ?? 3;
2449
- const phaseSigma = opts.phaseSigma ?? 0.01;
2450
- const ztdQ = opts.ztdProcessNoise ?? 1e-8;
2451
- const kinematic = opts.mode === "kinematic";
2452
- const corrections = opts.corrections;
2453
- const x = [
2454
- opts.aprioriPos[0],
2455
- opts.aprioriPos[1],
2456
- opts.aprioriPos[2],
2457
- 0.1
2458
- // zenith wet delay (m) a priori
2459
- ];
2460
- const P = [];
2461
- for (let i = 0; i < NBASE; i++) {
2462
- P.push(new Array(NBASE).fill(0));
2463
- }
2464
- P[IDX_X][IDX_X] = 100 * 100;
2465
- P[IDX_Y][IDX_Y] = 100 * 100;
2466
- P[IDX_Z][IDX_Z] = 100 * 100;
2467
- P[IDX_ZWD][IDX_ZWD] = 0.5 * 0.5;
2468
- const clkIdx = /* @__PURE__ */ new Map();
2469
- const clockSeeded = /* @__PURE__ */ new Set();
2470
- const ambIdx = /* @__PURE__ */ new Map();
2471
- const lastSeen = /* @__PURE__ */ new Map();
2472
- const arcs = [];
2473
- const arcSnap = /* @__PURE__ */ new Map();
2474
- const flushArc = (prn, minEpochs = 10) => {
2475
- const s = arcSnap.get(prn);
2476
- arcSnap.delete(prn);
2510
+ var PppEngine = class {
2511
+ source;
2512
+ opts;
2513
+ elevMask;
2514
+ codeSigma;
2515
+ phaseSigma;
2516
+ ztdQ;
2517
+ kinematic;
2518
+ corrections;
2519
+ truth;
2520
+ // Filter state (reset() initialises; mutated in place across epochs).
2521
+ x;
2522
+ P;
2523
+ clkIdx;
2524
+ clockSeeded;
2525
+ ambIdx;
2526
+ lastSeen;
2527
+ arcs;
2528
+ arcSnap;
2529
+ mwState;
2530
+ series;
2531
+ ei = 0;
2532
+ t0 = 0;
2533
+ convergenceSec = null;
2534
+ convergedStreak = 0;
2535
+ constructor(opts, source) {
2536
+ this.opts = opts;
2537
+ this.source = source;
2538
+ this.elevMask = (opts.elevationMaskDeg ?? 10) * Math.PI / 180;
2539
+ this.codeSigma = opts.codeSigma ?? 3;
2540
+ this.phaseSigma = opts.phaseSigma ?? 0.01;
2541
+ this.ztdQ = opts.ztdProcessNoise ?? 1e-8;
2542
+ this.kinematic = opts.mode === "kinematic";
2543
+ this.corrections = opts.corrections;
2544
+ this.truth = opts.groundTruth;
2545
+ this.reset();
2546
+ }
2547
+ /** Reinitialise the filter (fresh session). */
2548
+ reset() {
2549
+ const { opts } = this;
2550
+ this.x = [opts.aprioriPos[0], opts.aprioriPos[1], opts.aprioriPos[2], 0.1];
2551
+ const P = [];
2552
+ for (let i = 0; i < NBASE; i++) P.push(new Array(NBASE).fill(0));
2553
+ P[IDX_X][IDX_X] = 100 * 100;
2554
+ P[IDX_Y][IDX_Y] = 100 * 100;
2555
+ P[IDX_Z][IDX_Z] = 100 * 100;
2556
+ P[IDX_ZWD][IDX_ZWD] = 0.5 * 0.5;
2557
+ this.P = P;
2558
+ this.clkIdx = /* @__PURE__ */ new Map();
2559
+ this.clockSeeded = /* @__PURE__ */ new Set();
2560
+ this.ambIdx = /* @__PURE__ */ new Map();
2561
+ this.lastSeen = /* @__PURE__ */ new Map();
2562
+ this.arcs = [];
2563
+ this.arcSnap = /* @__PURE__ */ new Map();
2564
+ this.mwState = /* @__PURE__ */ new Map();
2565
+ this.series = [];
2566
+ this.ei = 0;
2567
+ this.t0 = 0;
2568
+ this.convergenceSec = null;
2569
+ this.convergedStreak = 0;
2570
+ }
2571
+ flushArc(prn, minEpochs = 10) {
2572
+ const s = this.arcSnap.get(prn);
2573
+ this.arcSnap.delete(prn);
2477
2574
  if (!s || s.nEpochs < minEpochs) return;
2478
- arcs.push({
2575
+ this.arcs.push({
2479
2576
  prn,
2480
2577
  aIF: s.aIF,
2481
2578
  mwCyc: s.mwCyc,
@@ -2486,64 +2583,88 @@ function solvePpp(epochs, sp3, opts) {
2486
2583
  startMs: s.startMs,
2487
2584
  endMs: s.endMs
2488
2585
  });
2489
- };
2490
- const mwState = /* @__PURE__ */ new Map();
2491
- const detectSlip = (o, ei) => {
2586
+ }
2587
+ /** Detect a cycle slip via the Melbourne–Wübbena wide-lane ambiguity. */
2588
+ detectSlip(o, ei) {
2492
2589
  const lamW = C_LIGHT / (o.f1 - o.f2);
2493
2590
  const lw = o.l1 - o.l2;
2494
2591
  const pn = (o.f1 * o.c1 + o.f2 * o.c2) / (o.f1 + o.f2);
2495
2592
  const mw = lw - pn / lamW;
2496
- const st = mwState.get(o.prn);
2593
+ const st = this.mwState.get(o.prn);
2497
2594
  let slip = o.slip;
2498
2595
  if (!st || ei - st.lastEi > 2)
2499
2596
  slip = true;
2500
2597
  else if (Math.abs(mw - st.mean) > 4) slip = true;
2501
- if (slip) mwState.set(o.prn, { mean: mw, n: 1, lastEi: ei });
2598
+ if (slip) this.mwState.set(o.prn, { mean: mw, n: 1, lastEi: ei });
2502
2599
  else {
2503
2600
  const n = st.n + 1;
2504
- mwState.set(o.prn, {
2601
+ this.mwState.set(o.prn, {
2505
2602
  mean: st.mean + (mw - st.mean) / Math.min(n, 100),
2506
2603
  n,
2507
2604
  lastEi: ei
2508
2605
  });
2509
2606
  }
2510
2607
  return slip;
2511
- };
2512
- const growState = (initVal, initVar) => {
2513
- const idx = x.length;
2514
- x.push(initVal);
2515
- for (const row of P) row.push(0);
2516
- P.push(new Array(x.length).fill(0));
2517
- P[idx][idx] = initVar;
2608
+ }
2609
+ growState(initVal, initVar) {
2610
+ const idx = this.x.length;
2611
+ this.x.push(initVal);
2612
+ for (const row of this.P) row.push(0);
2613
+ this.P.push(new Array(this.x.length).fill(0));
2614
+ this.P[idx][idx] = initVar;
2518
2615
  return idx;
2519
- };
2520
- const dropState = (idx) => {
2521
- x.splice(idx, 1);
2522
- P.splice(idx, 1);
2523
- for (const row of P) row.splice(idx, 1);
2524
- for (const [prn, i] of ambIdx) {
2525
- if (i === idx) ambIdx.delete(prn);
2526
- else if (i > idx) ambIdx.set(prn, i - 1);
2616
+ }
2617
+ dropState(idx) {
2618
+ this.x.splice(idx, 1);
2619
+ this.P.splice(idx, 1);
2620
+ for (const row of this.P) row.splice(idx, 1);
2621
+ for (const [prn, i] of this.ambIdx) {
2622
+ if (i === idx) this.ambIdx.delete(prn);
2623
+ else if (i > idx) this.ambIdx.set(prn, i - 1);
2527
2624
  }
2528
- for (const [sys, i] of clkIdx) {
2529
- if (i > idx) clkIdx.set(sys, i - 1);
2625
+ for (const [sys, i] of this.clkIdx) {
2626
+ if (i > idx) this.clkIdx.set(sys, i - 1);
2530
2627
  }
2531
- };
2532
- const clockOf = (sys) => {
2533
- let i = clkIdx.get(sys);
2628
+ }
2629
+ /** State index of a constellation's receiver clock, creating it lazily. */
2630
+ clockOf(sys) {
2631
+ let i = this.clkIdx.get(sys);
2534
2632
  if (i === void 0) {
2535
- i = growState(0, CLK_VAR);
2536
- clkIdx.set(sys, i);
2633
+ i = this.growState(0, CLK_VAR);
2634
+ this.clkIdx.set(sys, i);
2537
2635
  }
2538
2636
  return i;
2539
- };
2540
- const series = [];
2541
- const truth = opts.groundTruth;
2542
- const t0 = epochs[0]?.timeMs ?? 0;
2543
- let convergenceSec = null;
2544
- let convergedStreak = 0;
2545
- for (let ei = 0; ei < epochs.length; ei++) {
2546
- const epoch = epochs[ei];
2637
+ }
2638
+ /** Process one epoch, carrying the filter forward; returns its result. */
2639
+ process(epoch) {
2640
+ const {
2641
+ source,
2642
+ opts,
2643
+ elevMask,
2644
+ codeSigma,
2645
+ phaseSigma,
2646
+ ztdQ,
2647
+ kinematic,
2648
+ corrections,
2649
+ truth,
2650
+ x,
2651
+ P,
2652
+ clkIdx,
2653
+ clockSeeded,
2654
+ ambIdx,
2655
+ lastSeen,
2656
+ arcSnap,
2657
+ mwState,
2658
+ series
2659
+ } = this;
2660
+ const flushArc = this.flushArc.bind(this);
2661
+ const detectSlip = this.detectSlip.bind(this);
2662
+ const growState = this.growState.bind(this);
2663
+ const dropState = this.dropState.bind(this);
2664
+ const clockOf = this.clockOf.bind(this);
2665
+ const ei = this.ei;
2666
+ if (ei === 0) this.t0 = epoch.timeMs;
2667
+ const t0 = this.t0;
2547
2668
  if (kinematic && ei > 0) {
2548
2669
  for (const p of [IDX_X, IDX_Y, IDX_Z]) {
2549
2670
  for (let j = 0; j < x.length; j++) {
@@ -2564,7 +2685,7 @@ function solvePpp(epochs, sp3, opts) {
2564
2685
  for (const o of epoch.obs) {
2565
2686
  if (o.c1 === 0 || o.c2 === 0 || o.l1 === 0 || o.l2 === 0) continue;
2566
2687
  const slip = detectSlip(o, ei);
2567
- const sat = satStateAtEmission(sp3, o.prn, epoch.timeMs, rcv, opts.clk);
2688
+ const sat = satStateAtEmission(source, o.prn, epoch.timeMs, rcv);
2568
2689
  if (!sat) continue;
2569
2690
  const [elRad, azRad] = getAer(
2570
2691
  sat.state.x,
@@ -2584,7 +2705,9 @@ function solvePpp(epochs, sp3, opts) {
2584
2705
  const lam1 = C_LIGHT / o.f1;
2585
2706
  const lam2 = C_LIGHT / o.f2;
2586
2707
  if (o.c1 === 0 || o.c2 === 0 || o.l1 === 0 || o.l2 === 0) continue;
2587
- const pIf = g * o.c1 - (g - 1) * o.c2;
2708
+ const b1 = source.codeBias?.(o.prn, o.code1 ?? "", epoch.timeMs) ?? 0;
2709
+ const b2 = source.codeBias?.(o.prn, o.code2 ?? "", epoch.timeMs) ?? 0;
2710
+ const pIf = g * (o.c1 - b1) - (g - 1) * (o.c2 - b2);
2588
2711
  const lIf = g * (o.l1 * lam1) - (g - 1) * (o.l2 * lam2);
2589
2712
  if (!Number.isFinite(pIf) || !Number.isFinite(lIf)) continue;
2590
2713
  const trop = opts.troposphere ?? true ? niellMapping(elRad, latRad, heightM, epoch.timeMs) : { zhd: 0, mh: 0, mw: 0 };
@@ -2771,16 +2894,16 @@ function solvePpp(epochs, sp3, opts) {
2771
2894
  pos[2] - truth[2]
2772
2895
  );
2773
2896
  if (error3d < 0.1) {
2774
- convergedStreak++;
2775
- if (convergedStreak >= 10 && convergenceSec == null) {
2776
- convergenceSec = (epoch.timeMs - t0) / 1e3;
2897
+ this.convergedStreak++;
2898
+ if (this.convergedStreak >= 10 && this.convergenceSec == null) {
2899
+ this.convergenceSec = (epoch.timeMs - t0) / 1e3;
2777
2900
  }
2778
2901
  } else {
2779
- convergedStreak = 0;
2780
- convergenceSec = null;
2902
+ this.convergedStreak = 0;
2903
+ this.convergenceSec = null;
2781
2904
  }
2782
2905
  }
2783
- series.push({
2906
+ const result = {
2784
2907
  timeMs: epoch.timeMs,
2785
2908
  enu,
2786
2909
  error3d,
@@ -2789,50 +2912,68 @@ function solvePpp(epochs, sp3, opts) {
2789
2912
  ztdWet: x[IDX_ZWD],
2790
2913
  ztdHydrostatic: epochZhd,
2791
2914
  phaseResRms: nPhaseRes > 0 ? Math.sqrt(sumPhaseResSq / nPhaseRes) : 0
2792
- });
2793
- }
2794
- let finalState;
2795
- if (opts.exposeState) {
2796
- const ambs = [];
2797
- for (const [prn, index] of ambIdx) {
2798
- const s = arcSnap.get(prn);
2799
- if (!s || s.nEpochs < 10) continue;
2800
- ambs.push({
2801
- prn,
2802
- index,
2803
- aIF: x[index],
2804
- mwCyc: mwState.get(prn)?.mean ?? s.mwCyc,
2805
- f1: s.f1,
2806
- f2: s.f2,
2807
- elevDeg: s.elevSum / s.nEpochs
2808
- });
2915
+ };
2916
+ series.push(result);
2917
+ this.ei = ei + 1;
2918
+ return result;
2919
+ }
2920
+ /** Finalise the session: capture the AR state, flush open arcs, assemble. */
2921
+ solution() {
2922
+ const { opts, x, P, ambIdx, arcSnap, mwState, arcs, series, truth } = this;
2923
+ const flushArc = this.flushArc.bind(this);
2924
+ let finalState;
2925
+ if (opts.exposeState) {
2926
+ const ambs = [];
2927
+ for (const [prn, index] of ambIdx) {
2928
+ const s = arcSnap.get(prn);
2929
+ if (!s || s.nEpochs < 10) continue;
2930
+ ambs.push({
2931
+ prn,
2932
+ index,
2933
+ aIF: x[index],
2934
+ mwCyc: mwState.get(prn)?.mean ?? s.mwCyc,
2935
+ f1: s.f1,
2936
+ f2: s.f2,
2937
+ elevDeg: s.elevSum / s.nEpochs
2938
+ });
2939
+ }
2940
+ finalState = {
2941
+ position: [x[IDX_X], x[IDX_Y], x[IDX_Z]],
2942
+ covariance: P.map((row) => row.slice()),
2943
+ ambiguities: ambs
2944
+ };
2809
2945
  }
2810
- finalState = {
2811
- position: [x[IDX_X], x[IDX_Y], x[IDX_Z]],
2812
- covariance: P.map((row) => row.slice()),
2813
- ambiguities: ambs
2946
+ for (const prn of [...arcSnap.keys()]) flushArc(prn);
2947
+ const finalPos = [
2948
+ x[IDX_X],
2949
+ x[IDX_Y],
2950
+ x[IDX_Z]
2951
+ ];
2952
+ const llh = ecefToGeodetic(finalPos[0], finalPos[1], finalPos[2]);
2953
+ const finalError3d = truth ? Math.hypot(
2954
+ finalPos[0] - truth[0],
2955
+ finalPos[1] - truth[1],
2956
+ finalPos[2] - truth[2]
2957
+ ) : null;
2958
+ return {
2959
+ position: finalPos,
2960
+ llh,
2961
+ series,
2962
+ ztdWet: x[IDX_ZWD],
2963
+ ztdHydrostatic: series.length ? series[series.length - 1].ztdHydrostatic : 0,
2964
+ epochsUsed: series.length,
2965
+ convergenceSec: this.convergenceSec,
2966
+ finalError3d,
2967
+ arcs,
2968
+ finalState
2814
2969
  };
2815
2970
  }
2816
- for (const prn of [...arcSnap.keys()]) flushArc(prn);
2817
- const finalPos = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
2818
- const llh = ecefToGeodetic(finalPos[0], finalPos[1], finalPos[2]);
2819
- const finalError3d = truth ? Math.hypot(
2820
- finalPos[0] - truth[0],
2821
- finalPos[1] - truth[1],
2822
- finalPos[2] - truth[2]
2823
- ) : null;
2824
- return {
2825
- position: finalPos,
2826
- llh,
2827
- series,
2828
- ztdWet: x[IDX_ZWD],
2829
- ztdHydrostatic: series.length ? series[series.length - 1].ztdHydrostatic : 0,
2830
- epochsUsed: epochs.length,
2831
- convergenceSec,
2832
- finalError3d,
2833
- arcs,
2834
- finalState
2835
- };
2971
+ };
2972
+ function solvePpp(epochs, sp3OrSource, opts) {
2973
+ const source = "satState" in sp3OrSource ? sp3OrSource : new Sp3EphemerisSource(sp3OrSource, opts.clk);
2974
+ const eng = new PppEngine(opts, source);
2975
+ for (const epoch of epochs) eng.process(epoch);
2976
+ return eng.solution();
2836
2977
  }
2837
2978
 
2838
2979
  // src/positioning/ppp-ar.ts
@@ -3580,6 +3721,8 @@ export {
3580
3721
  sbasLongTermDeg,
3581
3722
  sbasIonoDeg,
3582
3723
  SbasProcessor,
3724
+ applyOrbitClock,
3725
+ hasToOrbitClock,
3583
3726
  niellMapping,
3584
3727
  gmst,
3585
3728
  sunEcef,
@@ -3587,6 +3730,8 @@ export {
3587
3730
  solidEarthTide,
3588
3731
  buildPppAntenna,
3589
3732
  createPppCorrections,
3733
+ Sp3EphemerisSource,
3734
+ PppEngine,
3590
3735
  solvePpp,
3591
3736
  wlWavelength,
3592
3737
  nlWavelength,