gnss-js 1.34.0 → 1.36.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.
@@ -1959,6 +1959,28 @@ function solvePpp(epochs, sp3, opts) {
1959
1959
  phaseResRms: nPhaseRes > 0 ? Math.sqrt(sumPhaseResSq / nPhaseRes) : 0
1960
1960
  });
1961
1961
  }
1962
+ let finalState;
1963
+ if (opts.exposeState) {
1964
+ const ambs = [];
1965
+ for (const [prn, index] of ambIdx) {
1966
+ const s = arcSnap.get(prn);
1967
+ if (!s || s.nEpochs < 10) continue;
1968
+ ambs.push({
1969
+ prn,
1970
+ index,
1971
+ aIF: x[index],
1972
+ mwCyc: mwState.get(prn)?.mean ?? s.mwCyc,
1973
+ f1: s.f1,
1974
+ f2: s.f2,
1975
+ elevDeg: s.elevSum / s.nEpochs
1976
+ });
1977
+ }
1978
+ finalState = {
1979
+ position: [x[IDX_X], x[IDX_Y], x[IDX_Z]],
1980
+ covariance: P.map((row) => row.slice()),
1981
+ ambiguities: ambs
1982
+ };
1983
+ }
1962
1984
  for (const prn of [...arcSnap.keys()]) flushArc(prn);
1963
1985
  const finalPos = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
1964
1986
  const llh = ecefToGeodetic(finalPos[0], finalPos[1], finalPos[2]);
@@ -1975,7 +1997,8 @@ function solvePpp(epochs, sp3, opts) {
1975
1997
  epochsUsed: epochs.length,
1976
1998
  convergenceSec,
1977
1999
  finalError3d,
1978
- arcs
2000
+ arcs,
2001
+ finalState
1979
2002
  };
1980
2003
  }
1981
2004
 
@@ -2068,6 +2091,117 @@ function resolvePppAmbiguities(sats, Qaif, opts = {}) {
2068
2091
  rejectedWl
2069
2092
  };
2070
2093
  }
2094
+ function solveSpd(A, b) {
2095
+ const n = b.length;
2096
+ const L = Array.from({ length: n }, () => new Array(n).fill(0));
2097
+ for (let i = 0; i < n; i++) {
2098
+ for (let j = 0; j <= i; j++) {
2099
+ let s = A[i][j];
2100
+ for (let k = 0; k < j; k++) s -= L[i][k] * L[j][k];
2101
+ if (i === j) {
2102
+ if (s <= 0) return null;
2103
+ L[i][j] = Math.sqrt(s);
2104
+ } else {
2105
+ L[i][j] = s / L[j][j];
2106
+ }
2107
+ }
2108
+ }
2109
+ const y = new Array(n).fill(0);
2110
+ for (let i = 0; i < n; i++) {
2111
+ let s = b[i];
2112
+ for (let k = 0; k < i; k++) s -= L[i][k] * y[k];
2113
+ y[i] = s / L[i][i];
2114
+ }
2115
+ const x = new Array(n).fill(0);
2116
+ for (let i = n - 1; i >= 0; i--) {
2117
+ let s = y[i];
2118
+ for (let k = i + 1; k < n; k++) s -= L[k][i] * x[k];
2119
+ x[i] = s / L[i][i];
2120
+ }
2121
+ return x;
2122
+ }
2123
+ function fixPppPosition(state, fcb, opts = {}) {
2124
+ const floatPos = state.position;
2125
+ const noFix = {
2126
+ fixed: false,
2127
+ position: floatPos,
2128
+ floatPosition: floatPos,
2129
+ shift: [0, 0, 0],
2130
+ nFixed: 0,
2131
+ ratio: 0,
2132
+ refPrn: null
2133
+ };
2134
+ const amb = state.ambiguities;
2135
+ if (amb.length < 3) return noFix;
2136
+ const sats = amb.map((a) => ({
2137
+ prn: a.prn,
2138
+ aIF: a.aIF,
2139
+ mwCyc: a.mwCyc,
2140
+ f1: a.f1,
2141
+ f2: a.f2,
2142
+ wlBiasCyc: fcb.satWlFcb.get(a.prn) ?? 0,
2143
+ nlBiasCyc: fcb.satNlFcb.get(a.prn) ?? 0,
2144
+ elevDeg: a.elevDeg
2145
+ }));
2146
+ const n = sats.length;
2147
+ const Qaif = new Float64Array(n * n);
2148
+ for (let i = 0; i < n; i++)
2149
+ for (let j = 0; j < n; j++)
2150
+ Qaif[i * n + j] = state.covariance[amb[i].index][amb[j].index];
2151
+ const res = resolvePppAmbiguities(sats, Qaif, opts);
2152
+ if (!res.fixed || res.refPrn === null) {
2153
+ return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
2154
+ }
2155
+ const idxOfPrn = new Map(amb.map((a) => [a.prn, a.index]));
2156
+ const floatOfPrn = new Map(amb.map((a) => [a.prn, a.aIF]));
2157
+ const kept = res.sats.filter((s) => idxOfPrn.has(s.prn));
2158
+ const refPos = kept.findIndex((s) => s.prn === res.refPrn);
2159
+ if (refPos < 0 || kept.length < 2) {
2160
+ return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
2161
+ }
2162
+ const ambIdx = kept.map((s) => idxOfPrn.get(s.prn));
2163
+ const aFloat = kept.map((s) => floatOfPrn.get(s.prn));
2164
+ const aFixed = kept.map((s) => s.aIF);
2165
+ const others = kept.map((_, i) => i).filter((i) => i !== refPos);
2166
+ const m = others.length;
2167
+ const P = state.covariance;
2168
+ const qAA = (i, j) => P[ambIdx[i]][ambIdx[j]];
2169
+ const qXA = (r, i) => P[r][ambIdx[i]];
2170
+ const Qzz = Array.from({ length: m }, () => new Array(m).fill(0));
2171
+ const z = new Array(m).fill(0);
2172
+ for (let a = 0; a < m; a++) {
2173
+ const ia = others[a];
2174
+ z[a] = aFloat[ia] - aFloat[refPos] - (aFixed[ia] - aFixed[refPos]);
2175
+ for (let b = 0; b < m; b++) {
2176
+ const ib = others[b];
2177
+ Qzz[a][b] = qAA(ia, ib) - qAA(ia, refPos) - qAA(refPos, ib) + qAA(refPos, refPos);
2178
+ }
2179
+ }
2180
+ const Qxz = [0, 1, 2].map(
2181
+ (r) => others.map((ia) => qXA(r, ia) - qXA(r, refPos))
2182
+ );
2183
+ const y = solveSpd(Qzz, z);
2184
+ if (!y) return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
2185
+ const shift = [0, 0, 0];
2186
+ for (let r = 0; r < 3; r++) {
2187
+ let s = 0;
2188
+ for (let a = 0; a < m; a++) s += Qxz[r][a] * y[a];
2189
+ shift[r] = -s;
2190
+ }
2191
+ return {
2192
+ fixed: true,
2193
+ position: [
2194
+ floatPos[0] + shift[0],
2195
+ floatPos[1] + shift[1],
2196
+ floatPos[2] + shift[2]
2197
+ ],
2198
+ floatPosition: floatPos,
2199
+ shift,
2200
+ nFixed: m,
2201
+ ratio: res.ratio,
2202
+ refPrn: res.refPrn
2203
+ };
2204
+ }
2071
2205
 
2072
2206
  // src/positioning/ppp-fcb.ts
2073
2207
  function extractWidelaneArcs(station, epochs, opts = {}) {
@@ -2221,6 +2355,39 @@ function estimateNarrowlaneFcb(arcs, wl, opts = {}) {
2221
2355
  const res = decomposeFcb(entries, { fixWindow: 0.15, ...opts });
2222
2356
  return { ...res, usedArcs: entries.length, wlRejected };
2223
2357
  }
2358
+ function estimateNetworkFcbs(arcs, opts = {}) {
2359
+ const minArcEpochs = opts.minArcEpochs ?? 120;
2360
+ const minWlObs = opts.minWlObs ?? 40;
2361
+ const fixWindow = opts.fixWindow ?? 0.15;
2362
+ const satWlFcb = /* @__PURE__ */ new Map();
2363
+ const satNlFcb = /* @__PURE__ */ new Map();
2364
+ const perSystem = {};
2365
+ const systems = [...new Set(arcs.map((a) => a.prn[0]))].sort();
2366
+ for (const sys of systems) {
2367
+ const sysArcs = arcs.filter((a) => a.prn[0] === sys);
2368
+ const wlArcs = sysArcs.filter((a) => a.nEpochs >= minWlObs).map((a) => ({
2369
+ station: a.station,
2370
+ prn: a.prn,
2371
+ wlFloat: a.mwCyc,
2372
+ nObs: a.nEpochs
2373
+ }));
2374
+ if (wlArcs.length < 3) continue;
2375
+ const wl = estimateWidelaneFcb(wlArcs, { fixWindow });
2376
+ const nlInput = sysArcs.filter((a) => a.nEpochs >= minArcEpochs);
2377
+ const nl = estimateNarrowlaneFcb(nlInput, wl, { fixWindow });
2378
+ for (const [prn, v] of wl.satFcb) satWlFcb.set(prn, v);
2379
+ for (const [prn, v] of nl.satFcb) satNlFcb.set(prn, v);
2380
+ perSystem[sys] = {
2381
+ wlFixRate: wl.fixRate,
2382
+ wlResidRms: wl.residRms,
2383
+ nlUsedArcs: nl.usedArcs,
2384
+ nlFixRate: nl.fixRate,
2385
+ nlResidRms: nl.residRms,
2386
+ wlRejected: nl.wlRejected
2387
+ };
2388
+ }
2389
+ return { satWlFcb, satNlFcb, perSystem };
2390
+ }
2224
2391
 
2225
2392
  // src/positioning/index.ts
2226
2393
  var GM_GPS = 3986005e8;
@@ -2471,9 +2638,11 @@ export {
2471
2638
  wlWavelength,
2472
2639
  nlWavelength,
2473
2640
  resolvePppAmbiguities,
2641
+ fixPppPosition,
2474
2642
  extractWidelaneArcs,
2475
2643
  estimateWidelaneFcb,
2476
2644
  estimateNarrowlaneFcb,
2645
+ estimateNetworkFcbs,
2477
2646
  satClockCorrection,
2478
2647
  ionoFree,
2479
2648
  solveSpp
package/dist/index.cjs CHANGED
@@ -150,11 +150,13 @@ __export(src_exports, {
150
150
  ecefToGeodetic: () => ecefToGeodetic,
151
151
  ephInfoToEphemeris: () => ephInfoToEphemeris,
152
152
  estimateNarrowlaneFcb: () => estimateNarrowlaneFcb,
153
+ estimateNetworkFcbs: () => estimateNetworkFcbs,
153
154
  estimateWidelaneFcb: () => estimateWidelaneFcb,
154
155
  euclidean3D: () => euclidean3D,
155
156
  extractWidelaneArcs: () => extractWidelaneArcs,
156
157
  fetchSourcetable: () => fetchSourcetable,
157
158
  findSatBias: () => findSatBias,
159
+ fixPppPosition: () => fixPppPosition,
158
160
  fmtD: () => fmtD,
159
161
  fmtF: () => fmtF,
160
162
  formatUTCTime: () => formatUTCTime,
@@ -13356,6 +13358,28 @@ function solvePpp(epochs, sp3, opts) {
13356
13358
  phaseResRms: nPhaseRes > 0 ? Math.sqrt(sumPhaseResSq / nPhaseRes) : 0
13357
13359
  });
13358
13360
  }
13361
+ let finalState;
13362
+ if (opts.exposeState) {
13363
+ const ambs = [];
13364
+ for (const [prn, index] of ambIdx) {
13365
+ const s = arcSnap.get(prn);
13366
+ if (!s || s.nEpochs < 10) continue;
13367
+ ambs.push({
13368
+ prn,
13369
+ index,
13370
+ aIF: x[index],
13371
+ mwCyc: mwState.get(prn)?.mean ?? s.mwCyc,
13372
+ f1: s.f1,
13373
+ f2: s.f2,
13374
+ elevDeg: s.elevSum / s.nEpochs
13375
+ });
13376
+ }
13377
+ finalState = {
13378
+ position: [x[IDX_X], x[IDX_Y], x[IDX_Z]],
13379
+ covariance: P.map((row) => row.slice()),
13380
+ ambiguities: ambs
13381
+ };
13382
+ }
13359
13383
  for (const prn of [...arcSnap.keys()]) flushArc(prn);
13360
13384
  const finalPos = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
13361
13385
  const llh = ecefToGeodetic(finalPos[0], finalPos[1], finalPos[2]);
@@ -13372,7 +13396,8 @@ function solvePpp(epochs, sp3, opts) {
13372
13396
  epochsUsed: epochs.length,
13373
13397
  convergenceSec,
13374
13398
  finalError3d,
13375
- arcs
13399
+ arcs,
13400
+ finalState
13376
13401
  };
13377
13402
  }
13378
13403
 
@@ -13465,6 +13490,117 @@ function resolvePppAmbiguities(sats, Qaif, opts = {}) {
13465
13490
  rejectedWl
13466
13491
  };
13467
13492
  }
13493
+ function solveSpd(A, b) {
13494
+ const n = b.length;
13495
+ const L = Array.from({ length: n }, () => new Array(n).fill(0));
13496
+ for (let i = 0; i < n; i++) {
13497
+ for (let j = 0; j <= i; j++) {
13498
+ let s = A[i][j];
13499
+ for (let k = 0; k < j; k++) s -= L[i][k] * L[j][k];
13500
+ if (i === j) {
13501
+ if (s <= 0) return null;
13502
+ L[i][j] = Math.sqrt(s);
13503
+ } else {
13504
+ L[i][j] = s / L[j][j];
13505
+ }
13506
+ }
13507
+ }
13508
+ const y = new Array(n).fill(0);
13509
+ for (let i = 0; i < n; i++) {
13510
+ let s = b[i];
13511
+ for (let k = 0; k < i; k++) s -= L[i][k] * y[k];
13512
+ y[i] = s / L[i][i];
13513
+ }
13514
+ const x = new Array(n).fill(0);
13515
+ for (let i = n - 1; i >= 0; i--) {
13516
+ let s = y[i];
13517
+ for (let k = i + 1; k < n; k++) s -= L[k][i] * x[k];
13518
+ x[i] = s / L[i][i];
13519
+ }
13520
+ return x;
13521
+ }
13522
+ function fixPppPosition(state, fcb, opts = {}) {
13523
+ const floatPos = state.position;
13524
+ const noFix = {
13525
+ fixed: false,
13526
+ position: floatPos,
13527
+ floatPosition: floatPos,
13528
+ shift: [0, 0, 0],
13529
+ nFixed: 0,
13530
+ ratio: 0,
13531
+ refPrn: null
13532
+ };
13533
+ const amb = state.ambiguities;
13534
+ if (amb.length < 3) return noFix;
13535
+ const sats = amb.map((a) => ({
13536
+ prn: a.prn,
13537
+ aIF: a.aIF,
13538
+ mwCyc: a.mwCyc,
13539
+ f1: a.f1,
13540
+ f2: a.f2,
13541
+ wlBiasCyc: fcb.satWlFcb.get(a.prn) ?? 0,
13542
+ nlBiasCyc: fcb.satNlFcb.get(a.prn) ?? 0,
13543
+ elevDeg: a.elevDeg
13544
+ }));
13545
+ const n = sats.length;
13546
+ const Qaif = new Float64Array(n * n);
13547
+ for (let i = 0; i < n; i++)
13548
+ for (let j = 0; j < n; j++)
13549
+ Qaif[i * n + j] = state.covariance[amb[i].index][amb[j].index];
13550
+ const res = resolvePppAmbiguities(sats, Qaif, opts);
13551
+ if (!res.fixed || res.refPrn === null) {
13552
+ return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
13553
+ }
13554
+ const idxOfPrn = new Map(amb.map((a) => [a.prn, a.index]));
13555
+ const floatOfPrn = new Map(amb.map((a) => [a.prn, a.aIF]));
13556
+ const kept = res.sats.filter((s) => idxOfPrn.has(s.prn));
13557
+ const refPos = kept.findIndex((s) => s.prn === res.refPrn);
13558
+ if (refPos < 0 || kept.length < 2) {
13559
+ return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
13560
+ }
13561
+ const ambIdx = kept.map((s) => idxOfPrn.get(s.prn));
13562
+ const aFloat = kept.map((s) => floatOfPrn.get(s.prn));
13563
+ const aFixed = kept.map((s) => s.aIF);
13564
+ const others = kept.map((_, i) => i).filter((i) => i !== refPos);
13565
+ const m = others.length;
13566
+ const P = state.covariance;
13567
+ const qAA = (i, j) => P[ambIdx[i]][ambIdx[j]];
13568
+ const qXA = (r, i) => P[r][ambIdx[i]];
13569
+ const Qzz = Array.from({ length: m }, () => new Array(m).fill(0));
13570
+ const z = new Array(m).fill(0);
13571
+ for (let a = 0; a < m; a++) {
13572
+ const ia = others[a];
13573
+ z[a] = aFloat[ia] - aFloat[refPos] - (aFixed[ia] - aFixed[refPos]);
13574
+ for (let b = 0; b < m; b++) {
13575
+ const ib = others[b];
13576
+ Qzz[a][b] = qAA(ia, ib) - qAA(ia, refPos) - qAA(refPos, ib) + qAA(refPos, refPos);
13577
+ }
13578
+ }
13579
+ const Qxz = [0, 1, 2].map(
13580
+ (r) => others.map((ia) => qXA(r, ia) - qXA(r, refPos))
13581
+ );
13582
+ const y = solveSpd(Qzz, z);
13583
+ if (!y) return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
13584
+ const shift = [0, 0, 0];
13585
+ for (let r = 0; r < 3; r++) {
13586
+ let s = 0;
13587
+ for (let a = 0; a < m; a++) s += Qxz[r][a] * y[a];
13588
+ shift[r] = -s;
13589
+ }
13590
+ return {
13591
+ fixed: true,
13592
+ position: [
13593
+ floatPos[0] + shift[0],
13594
+ floatPos[1] + shift[1],
13595
+ floatPos[2] + shift[2]
13596
+ ],
13597
+ floatPosition: floatPos,
13598
+ shift,
13599
+ nFixed: m,
13600
+ ratio: res.ratio,
13601
+ refPrn: res.refPrn
13602
+ };
13603
+ }
13468
13604
 
13469
13605
  // src/positioning/ppp-fcb.ts
13470
13606
  function extractWidelaneArcs(station, epochs, opts = {}) {
@@ -13618,6 +13754,39 @@ function estimateNarrowlaneFcb(arcs, wl, opts = {}) {
13618
13754
  const res = decomposeFcb(entries, { fixWindow: 0.15, ...opts });
13619
13755
  return { ...res, usedArcs: entries.length, wlRejected };
13620
13756
  }
13757
+ function estimateNetworkFcbs(arcs, opts = {}) {
13758
+ const minArcEpochs = opts.minArcEpochs ?? 120;
13759
+ const minWlObs = opts.minWlObs ?? 40;
13760
+ const fixWindow = opts.fixWindow ?? 0.15;
13761
+ const satWlFcb = /* @__PURE__ */ new Map();
13762
+ const satNlFcb = /* @__PURE__ */ new Map();
13763
+ const perSystem = {};
13764
+ const systems = [...new Set(arcs.map((a) => a.prn[0]))].sort();
13765
+ for (const sys of systems) {
13766
+ const sysArcs = arcs.filter((a) => a.prn[0] === sys);
13767
+ const wlArcs = sysArcs.filter((a) => a.nEpochs >= minWlObs).map((a) => ({
13768
+ station: a.station,
13769
+ prn: a.prn,
13770
+ wlFloat: a.mwCyc,
13771
+ nObs: a.nEpochs
13772
+ }));
13773
+ if (wlArcs.length < 3) continue;
13774
+ const wl = estimateWidelaneFcb(wlArcs, { fixWindow });
13775
+ const nlInput = sysArcs.filter((a) => a.nEpochs >= minArcEpochs);
13776
+ const nl = estimateNarrowlaneFcb(nlInput, wl, { fixWindow });
13777
+ for (const [prn, v] of wl.satFcb) satWlFcb.set(prn, v);
13778
+ for (const [prn, v] of nl.satFcb) satNlFcb.set(prn, v);
13779
+ perSystem[sys] = {
13780
+ wlFixRate: wl.fixRate,
13781
+ wlResidRms: wl.residRms,
13782
+ nlUsedArcs: nl.usedArcs,
13783
+ nlFixRate: nl.fixRate,
13784
+ nlResidRms: nl.residRms,
13785
+ wlRejected: nl.wlRejected
13786
+ };
13787
+ }
13788
+ return { satWlFcb, satNlFcb, perSystem };
13789
+ }
13621
13790
 
13622
13791
  // src/positioning/index.ts
13623
13792
  var GM_GPS3 = 3986005e8;
@@ -16237,11 +16406,13 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
16237
16406
  ecefToGeodetic,
16238
16407
  ephInfoToEphemeris,
16239
16408
  estimateNarrowlaneFcb,
16409
+ estimateNetworkFcbs,
16240
16410
  estimateWidelaneFcb,
16241
16411
  euclidean3D,
16242
16412
  extractWidelaneArcs,
16243
16413
  fetchSourcetable,
16244
16414
  findSatBias,
16415
+ fixPppPosition,
16245
16416
  fmtD,
16246
16417
  fmtF,
16247
16418
  formatUTCTime,
package/dist/index.d.cts CHANGED
@@ -17,7 +17,7 @@ export { TrimbleEpoch, TrimbleFrame, TrimbleMeasurement, TrimbleNavResult, Trimb
17
17
  export { BinexEpoch, BinexMeasurement, BinexParseResult, BinexRecord, binexCrc16, binexCrc32, binexCsum8, binexRecords, decodeBinexEph, getBnxi, parseBinex } from './binex.cjs';
18
18
  export { AllPositionsData, AlmanacPosition, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris } from './orbit.cjs';
19
19
  export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.cjs';
20
- export { AntennaOffset, ArFixedSat, ArOptions, ArResult, ArSat, CorrectionResult, DgnssOptions, DgnssSolution, EphemerisSource, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, NlArc, NlFcbResult, PppAntennaModel, PppArc, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, SatGeom, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateWidelaneFcb, extractWidelaneArcs, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength } from './positioning.cjs';
20
+ export { AntennaOffset, ArFixedSat, ArOptions, ArResult, ArSat, CorrectionResult, DgnssOptions, DgnssSolution, EphemerisSource, FixedPositionResult, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, NetworkFcbResult, NetworkFcbSummary, NlArc, NlFcbResult, PppAntennaModel, PppArc, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppFixState, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, SatGeom, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength } from './positioning.cjs';
21
21
  export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.cjs';
22
22
  export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, IonoAccumulator, IonoDcbResult, IonoPoint, IonoRatePoint, IonoRateSeries, IonoResult, IonoSeries, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, SatDcbMap, analyzeQuality, applyIonoDcb, computeIonoRate, detrendIonoArcs, parseSinexBiasDcb } from './analysis.cjs';
23
23
  export { AntennaEntry, AntexFile, FrequencyData, frequencyLabel, parseAntex } from './antex.cjs';
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@ export { TrimbleEpoch, TrimbleFrame, TrimbleMeasurement, TrimbleNavResult, Trimb
17
17
  export { BinexEpoch, BinexMeasurement, BinexParseResult, BinexRecord, binexCrc16, binexCrc32, binexCsum8, binexRecords, decodeBinexEph, getBnxi, parseBinex } from './binex.js';
18
18
  export { AllPositionsData, AlmanacPosition, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, almanacEpochMs, almanacSatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, computeVisibilityFromPositions, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassAlmanacEpochMs, glonassAlmanacPosition, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris } from './orbit.js';
19
19
  export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.js';
20
- export { AntennaOffset, ArFixedSat, ArOptions, ArResult, ArSat, CorrectionResult, DgnssOptions, DgnssSolution, EphemerisSource, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, NlArc, NlFcbResult, PppAntennaModel, PppArc, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, SatGeom, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateWidelaneFcb, extractWidelaneArcs, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength } from './positioning.js';
20
+ export { AntennaOffset, ArFixedSat, ArOptions, ArResult, ArSat, CorrectionResult, DgnssOptions, DgnssSolution, EphemerisSource, FixedPositionResult, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, NetworkFcbResult, NetworkFcbSummary, NlArc, NlFcbResult, PppAntennaModel, PppArc, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppFixState, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, SatGeom, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength } from './positioning.js';
21
21
  export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.js';
22
22
  export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, IonoAccumulator, IonoDcbResult, IonoPoint, IonoRatePoint, IonoRateSeries, IonoResult, IonoSeries, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, SatDcbMap, analyzeQuality, applyIonoDcb, computeIonoRate, detrendIonoArcs, parseSinexBiasDcb } from './analysis.js';
23
23
  export { AntennaEntry, AntexFile, FrequencyData, frequencyLabel, parseAntex } from './antex.js';
package/dist/index.js CHANGED
@@ -147,8 +147,10 @@ import {
147
147
  buildPppAntenna,
148
148
  createPppCorrections,
149
149
  estimateNarrowlaneFcb,
150
+ estimateNetworkFcbs,
150
151
  estimateWidelaneFcb,
151
152
  extractWidelaneArcs,
153
+ fixPppPosition,
152
154
  gimSlantIonoDelayL1,
153
155
  gimVerticalTec,
154
156
  gmst,
@@ -168,7 +170,7 @@ import {
168
170
  sunEcef,
169
171
  toRtkEpoch,
170
172
  wlWavelength
171
- } from "./chunk-H73PZB5C.js";
173
+ } from "./chunk-3FG4XVCB.js";
172
174
  import {
173
175
  almanacEpochMs,
174
176
  almanacSatPosition,
@@ -476,11 +478,13 @@ export {
476
478
  ecefToGeodetic,
477
479
  ephInfoToEphemeris,
478
480
  estimateNarrowlaneFcb,
481
+ estimateNetworkFcbs,
479
482
  estimateWidelaneFcb,
480
483
  euclidean3D,
481
484
  extractWidelaneArcs,
482
485
  fetchSourcetable,
483
486
  findSatBias,
487
+ fixPppPosition,
484
488
  fmtD,
485
489
  fmtF,
486
490
  formatUTCTime,
@@ -25,8 +25,10 @@ __export(positioning_exports, {
25
25
  buildPppAntenna: () => buildPppAntenna,
26
26
  createPppCorrections: () => createPppCorrections,
27
27
  estimateNarrowlaneFcb: () => estimateNarrowlaneFcb,
28
+ estimateNetworkFcbs: () => estimateNetworkFcbs,
28
29
  estimateWidelaneFcb: () => estimateWidelaneFcb,
29
30
  extractWidelaneArcs: () => extractWidelaneArcs,
31
+ fixPppPosition: () => fixPppPosition,
30
32
  gimSlantIonoDelayL1: () => gimSlantIonoDelayL1,
31
33
  gimVerticalTec: () => gimVerticalTec,
32
34
  gmst: () => gmst,
@@ -2484,6 +2486,28 @@ function solvePpp(epochs, sp3, opts) {
2484
2486
  phaseResRms: nPhaseRes > 0 ? Math.sqrt(sumPhaseResSq / nPhaseRes) : 0
2485
2487
  });
2486
2488
  }
2489
+ let finalState;
2490
+ if (opts.exposeState) {
2491
+ const ambs = [];
2492
+ for (const [prn, index] of ambIdx) {
2493
+ const s = arcSnap.get(prn);
2494
+ if (!s || s.nEpochs < 10) continue;
2495
+ ambs.push({
2496
+ prn,
2497
+ index,
2498
+ aIF: x[index],
2499
+ mwCyc: mwState.get(prn)?.mean ?? s.mwCyc,
2500
+ f1: s.f1,
2501
+ f2: s.f2,
2502
+ elevDeg: s.elevSum / s.nEpochs
2503
+ });
2504
+ }
2505
+ finalState = {
2506
+ position: [x[IDX_X], x[IDX_Y], x[IDX_Z]],
2507
+ covariance: P.map((row) => row.slice()),
2508
+ ambiguities: ambs
2509
+ };
2510
+ }
2487
2511
  for (const prn of [...arcSnap.keys()]) flushArc(prn);
2488
2512
  const finalPos = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
2489
2513
  const llh = ecefToGeodetic(finalPos[0], finalPos[1], finalPos[2]);
@@ -2500,7 +2524,8 @@ function solvePpp(epochs, sp3, opts) {
2500
2524
  epochsUsed: epochs.length,
2501
2525
  convergenceSec,
2502
2526
  finalError3d,
2503
- arcs
2527
+ arcs,
2528
+ finalState
2504
2529
  };
2505
2530
  }
2506
2531
 
@@ -2593,6 +2618,117 @@ function resolvePppAmbiguities(sats, Qaif, opts = {}) {
2593
2618
  rejectedWl
2594
2619
  };
2595
2620
  }
2621
+ function solveSpd(A, b) {
2622
+ const n = b.length;
2623
+ const L = Array.from({ length: n }, () => new Array(n).fill(0));
2624
+ for (let i = 0; i < n; i++) {
2625
+ for (let j = 0; j <= i; j++) {
2626
+ let s = A[i][j];
2627
+ for (let k = 0; k < j; k++) s -= L[i][k] * L[j][k];
2628
+ if (i === j) {
2629
+ if (s <= 0) return null;
2630
+ L[i][j] = Math.sqrt(s);
2631
+ } else {
2632
+ L[i][j] = s / L[j][j];
2633
+ }
2634
+ }
2635
+ }
2636
+ const y = new Array(n).fill(0);
2637
+ for (let i = 0; i < n; i++) {
2638
+ let s = b[i];
2639
+ for (let k = 0; k < i; k++) s -= L[i][k] * y[k];
2640
+ y[i] = s / L[i][i];
2641
+ }
2642
+ const x = new Array(n).fill(0);
2643
+ for (let i = n - 1; i >= 0; i--) {
2644
+ let s = y[i];
2645
+ for (let k = i + 1; k < n; k++) s -= L[k][i] * x[k];
2646
+ x[i] = s / L[i][i];
2647
+ }
2648
+ return x;
2649
+ }
2650
+ function fixPppPosition(state, fcb, opts = {}) {
2651
+ const floatPos = state.position;
2652
+ const noFix = {
2653
+ fixed: false,
2654
+ position: floatPos,
2655
+ floatPosition: floatPos,
2656
+ shift: [0, 0, 0],
2657
+ nFixed: 0,
2658
+ ratio: 0,
2659
+ refPrn: null
2660
+ };
2661
+ const amb = state.ambiguities;
2662
+ if (amb.length < 3) return noFix;
2663
+ const sats = amb.map((a) => ({
2664
+ prn: a.prn,
2665
+ aIF: a.aIF,
2666
+ mwCyc: a.mwCyc,
2667
+ f1: a.f1,
2668
+ f2: a.f2,
2669
+ wlBiasCyc: fcb.satWlFcb.get(a.prn) ?? 0,
2670
+ nlBiasCyc: fcb.satNlFcb.get(a.prn) ?? 0,
2671
+ elevDeg: a.elevDeg
2672
+ }));
2673
+ const n = sats.length;
2674
+ const Qaif = new Float64Array(n * n);
2675
+ for (let i = 0; i < n; i++)
2676
+ for (let j = 0; j < n; j++)
2677
+ Qaif[i * n + j] = state.covariance[amb[i].index][amb[j].index];
2678
+ const res = resolvePppAmbiguities(sats, Qaif, opts);
2679
+ if (!res.fixed || res.refPrn === null) {
2680
+ return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
2681
+ }
2682
+ const idxOfPrn = new Map(amb.map((a) => [a.prn, a.index]));
2683
+ const floatOfPrn = new Map(amb.map((a) => [a.prn, a.aIF]));
2684
+ const kept = res.sats.filter((s) => idxOfPrn.has(s.prn));
2685
+ const refPos = kept.findIndex((s) => s.prn === res.refPrn);
2686
+ if (refPos < 0 || kept.length < 2) {
2687
+ return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
2688
+ }
2689
+ const ambIdx = kept.map((s) => idxOfPrn.get(s.prn));
2690
+ const aFloat = kept.map((s) => floatOfPrn.get(s.prn));
2691
+ const aFixed = kept.map((s) => s.aIF);
2692
+ const others = kept.map((_, i) => i).filter((i) => i !== refPos);
2693
+ const m = others.length;
2694
+ const P = state.covariance;
2695
+ const qAA = (i, j) => P[ambIdx[i]][ambIdx[j]];
2696
+ const qXA = (r, i) => P[r][ambIdx[i]];
2697
+ const Qzz = Array.from({ length: m }, () => new Array(m).fill(0));
2698
+ const z = new Array(m).fill(0);
2699
+ for (let a = 0; a < m; a++) {
2700
+ const ia = others[a];
2701
+ z[a] = aFloat[ia] - aFloat[refPos] - (aFixed[ia] - aFixed[refPos]);
2702
+ for (let b = 0; b < m; b++) {
2703
+ const ib = others[b];
2704
+ Qzz[a][b] = qAA(ia, ib) - qAA(ia, refPos) - qAA(refPos, ib) + qAA(refPos, refPos);
2705
+ }
2706
+ }
2707
+ const Qxz = [0, 1, 2].map(
2708
+ (r) => others.map((ia) => qXA(r, ia) - qXA(r, refPos))
2709
+ );
2710
+ const y = solveSpd(Qzz, z);
2711
+ if (!y) return { ...noFix, ratio: res.ratio, refPrn: res.refPrn };
2712
+ const shift = [0, 0, 0];
2713
+ for (let r = 0; r < 3; r++) {
2714
+ let s = 0;
2715
+ for (let a = 0; a < m; a++) s += Qxz[r][a] * y[a];
2716
+ shift[r] = -s;
2717
+ }
2718
+ return {
2719
+ fixed: true,
2720
+ position: [
2721
+ floatPos[0] + shift[0],
2722
+ floatPos[1] + shift[1],
2723
+ floatPos[2] + shift[2]
2724
+ ],
2725
+ floatPosition: floatPos,
2726
+ shift,
2727
+ nFixed: m,
2728
+ ratio: res.ratio,
2729
+ refPrn: res.refPrn
2730
+ };
2731
+ }
2596
2732
 
2597
2733
  // src/positioning/ppp-fcb.ts
2598
2734
  function extractWidelaneArcs(station, epochs, opts = {}) {
@@ -2746,6 +2882,39 @@ function estimateNarrowlaneFcb(arcs, wl, opts = {}) {
2746
2882
  const res = decomposeFcb(entries, { fixWindow: 0.15, ...opts });
2747
2883
  return { ...res, usedArcs: entries.length, wlRejected };
2748
2884
  }
2885
+ function estimateNetworkFcbs(arcs, opts = {}) {
2886
+ const minArcEpochs = opts.minArcEpochs ?? 120;
2887
+ const minWlObs = opts.minWlObs ?? 40;
2888
+ const fixWindow = opts.fixWindow ?? 0.15;
2889
+ const satWlFcb = /* @__PURE__ */ new Map();
2890
+ const satNlFcb = /* @__PURE__ */ new Map();
2891
+ const perSystem = {};
2892
+ const systems = [...new Set(arcs.map((a) => a.prn[0]))].sort();
2893
+ for (const sys of systems) {
2894
+ const sysArcs = arcs.filter((a) => a.prn[0] === sys);
2895
+ const wlArcs = sysArcs.filter((a) => a.nEpochs >= minWlObs).map((a) => ({
2896
+ station: a.station,
2897
+ prn: a.prn,
2898
+ wlFloat: a.mwCyc,
2899
+ nObs: a.nEpochs
2900
+ }));
2901
+ if (wlArcs.length < 3) continue;
2902
+ const wl = estimateWidelaneFcb(wlArcs, { fixWindow });
2903
+ const nlInput = sysArcs.filter((a) => a.nEpochs >= minArcEpochs);
2904
+ const nl = estimateNarrowlaneFcb(nlInput, wl, { fixWindow });
2905
+ for (const [prn, v] of wl.satFcb) satWlFcb.set(prn, v);
2906
+ for (const [prn, v] of nl.satFcb) satNlFcb.set(prn, v);
2907
+ perSystem[sys] = {
2908
+ wlFixRate: wl.fixRate,
2909
+ wlResidRms: wl.residRms,
2910
+ nlUsedArcs: nl.usedArcs,
2911
+ nlFixRate: nl.fixRate,
2912
+ nlResidRms: nl.residRms,
2913
+ wlRejected: nl.wlRejected
2914
+ };
2915
+ }
2916
+ return { satWlFcb, satNlFcb, perSystem };
2917
+ }
2749
2918
 
2750
2919
  // src/positioning/index.ts
2751
2920
  var GM_GPS2 = 3986005e8;
@@ -2981,8 +3150,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2981
3150
  buildPppAntenna,
2982
3151
  createPppCorrections,
2983
3152
  estimateNarrowlaneFcb,
3153
+ estimateNetworkFcbs,
2984
3154
  estimateWidelaneFcb,
2985
3155
  extractWidelaneArcs,
3156
+ fixPppPosition,
2986
3157
  gimSlantIonoDelayL1,
2987
3158
  gimVerticalTec,
2988
3159
  gmst,
@@ -631,6 +631,9 @@ interface PppOptions {
631
631
  ztdProcessNoise?: number;
632
632
  /** Model + estimate the troposphere. Default true. */
633
633
  troposphere?: boolean;
634
+ /** Expose the final EKF state (position + ambiguities + covariance) in the
635
+ * solution for ambiguity-resolved positioning. Default false. */
636
+ exposeState?: boolean;
634
637
  /** High-rate precise satellite clocks (RINEX CLOCK, typically 30 s). When
635
638
  * given, satellite clock offsets come from this by linear interpolation
636
639
  * instead of the SP3 5-minute clocks — the dominant PPP accuracy term.
@@ -669,6 +672,31 @@ interface PppSolution {
669
672
  /** Per-arc converged float ambiguities — one per continuous satellite
670
673
  * tracking arc — the raw material for ambiguity resolution (PPP-AR). */
671
674
  arcs: PppArc[];
675
+ /** Final EKF state (position + active ambiguities + full covariance) for
676
+ * ambiguity-resolved (fixed) positioning. Present only when
677
+ * `PppOptions.exposeState` is set. */
678
+ finalState?: PppFixState;
679
+ }
680
+ /** Final float state exposed for PPP-AR position fixing (`fixPppPosition`). */
681
+ interface PppFixState {
682
+ /** Float position (m); rows 0–2 of the covariance. */
683
+ position: [number, number, number];
684
+ /** Full EKF covariance (n×n). State order: X,Y,Z,ZWD, clocks…, ambiguities. */
685
+ covariance: number[][];
686
+ /** Active ambiguities at the final epoch. */
687
+ ambiguities: {
688
+ prn: string;
689
+ /** Row/column of this ambiguity in `covariance`. */
690
+ index: number;
691
+ /** Float ionosphere-free ambiguity (m). */
692
+ aIF: number;
693
+ /** Arc-averaged Melbourne–Wübbena wide-lane (cycles). */
694
+ mwCyc: number;
695
+ f1: number;
696
+ f2: number;
697
+ /** Mean satellite elevation over the arc (deg). */
698
+ elevDeg: number;
699
+ }[];
672
700
  }
673
701
  /** A converged carrier-phase arc: the float ionosphere-free ambiguity plus
674
702
  * the wide-lane, for integer resolution / FCB estimation. */
@@ -759,6 +787,7 @@ declare function solidEarthTide(rcv: [number, number, number], sun: [number, num
759
787
  * The relation between the IF ambiguity (metres) and the integers is
760
788
  * A_IF = λ_NL·N1 + λ_NL·(f2/(f1−f2))·N_WL, λ_NL = c/(f1+f2).
761
789
  */
790
+
762
791
  /** Wide-lane wavelength c/(f1−f2) (m). */
763
792
  declare function wlWavelength(f1: number, f2: number): number;
764
793
  /** Narrow-lane wavelength c/(f1+f2) (m). */
@@ -812,6 +841,38 @@ interface ArResult {
812
841
  * confident fix (keep the float solution).
813
842
  */
814
843
  declare function resolvePppAmbiguities(sats: ArSat[], Qaif: Float64Array, opts?: ArOptions): ArResult;
844
+ interface FixedPositionResult {
845
+ /** Whether the narrow-lane fix passed the ratio test. */
846
+ fixed: boolean;
847
+ /** Ambiguity-fixed position (m); equals `floatPosition` when not fixed. */
848
+ position: [number, number, number];
849
+ /** The input float position (m). */
850
+ floatPosition: [number, number, number];
851
+ /** Displacement fixed − float (m), ENU-agnostic ECEF. */
852
+ shift: [number, number, number];
853
+ /** Satellites whose ambiguities were fixed (excl. the SD reference). */
854
+ nFixed: number;
855
+ ratio: number;
856
+ refPrn: string | null;
857
+ }
858
+ /**
859
+ * Ambiguity-fixed (PPP-AR) position from a float `solvePpp` final state and
860
+ * the satellite fractional-cycle biases (from `estimateNetworkFcbs`).
861
+ *
862
+ * Resolves the integers (`resolvePppAmbiguities`), then **conditions the
863
+ * float position on the fixed between-satellite ambiguities** — the standard
864
+ * constrained solution
865
+ * x_fixed = x_float − Q_xz · Q_zz⁻¹ · (z_float − ẑ),
866
+ * where z = D·a are the single-differenced ambiguities (D removes the
867
+ * reference satellite, cancelling the receiver term), Q_zz = D·Q_aa·Dᵀ and
868
+ * Q_xz = Q_xa·Dᵀ come from the EKF covariance. Only satellite-to-satellite
869
+ * integers constrain the position; the reference (and the receiver clock it
870
+ * absorbs) stays free. Returns the float position unchanged if the fix fails.
871
+ */
872
+ declare function fixPppPosition(state: PppFixState, fcb: {
873
+ satWlFcb: Map<string, number>;
874
+ satNlFcb: Map<string, number>;
875
+ }, opts?: ArOptions): FixedPositionResult;
815
876
 
816
877
  /**
817
878
  * Wide-lane fractional-cycle bias (FCB) estimation for PPP-AR — the
@@ -932,6 +993,40 @@ interface NlFcbResult extends WlFcbResult {
932
993
  * — the fix rate is the honest measure of how close the network is to cm.
933
994
  */
934
995
  declare function estimateNarrowlaneFcb(arcs: NlArc[], wl: WlFcbResult, opts?: WlFcbOptions): NlFcbResult;
996
+ interface NetworkFcbSummary {
997
+ wlFixRate: number;
998
+ wlResidRms: number;
999
+ nlUsedArcs: number;
1000
+ nlFixRate: number;
1001
+ nlResidRms: number;
1002
+ wlRejected: number;
1003
+ }
1004
+ interface NetworkFcbResult {
1005
+ /** Per-satellite wide-lane FCB (cycles), all constellations merged. */
1006
+ satWlFcb: Map<string, number>;
1007
+ /** Per-satellite narrow-lane FCB (cycles), all constellations merged. */
1008
+ satNlFcb: Map<string, number>;
1009
+ /** Per-constellation fix-rate / residual summary, keyed by system letter. */
1010
+ perSystem: Record<string, NetworkFcbSummary>;
1011
+ }
1012
+ /**
1013
+ * Full network FCB calibration for PPP-AR from a set of `solvePpp` arcs (each
1014
+ * tagged with its station), **per constellation** — the receiver wide- and
1015
+ * narrow-lane biases are system-specific, so lumping GPS/Galileo/BeiDou under
1016
+ * one datum corrupts the fit. Runs the wide-lane then narrow-lane
1017
+ * decomposition for each system and merges the per-satellite biases.
1018
+ *
1019
+ * The satellite FCBs (`satWlFcb`/`satNlFcb`) are the reusable product: a rover
1020
+ * applies them and solves only its own receiver bias. Multi-GNSS input
1021
+ * multiplies the fixable arcs (Galileo roughly doubles GPS-only) without
1022
+ * changing per-arc behaviour — each satellite's arc length is independent of
1023
+ * how many constellations are present.
1024
+ */
1025
+ declare function estimateNetworkFcbs(arcs: NlArc[], opts?: {
1026
+ minArcEpochs?: number;
1027
+ minWlObs?: number;
1028
+ fixWindow?: number;
1029
+ }): NetworkFcbResult;
935
1030
 
936
1031
  /**
937
1032
  * Single-point positioning (SPP) from pseudoranges and broadcast
@@ -1022,4 +1117,4 @@ declare function ionoFree(p1: number, p2: number, f1: number, f2: number): numbe
1022
1117
  */
1023
1118
  declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
1024
1119
 
1025
- export { type AntennaOffset, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type DgnssOptions, type DgnssSolution, type EphemerisSource, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type NlArc, type NlFcbResult, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type SatGeom, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateWidelaneFcb, extractWidelaneArcs, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
1120
+ export { type AntennaOffset, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type DgnssOptions, type DgnssSolution, type EphemerisSource, type FixedPositionResult, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type NetworkFcbResult, type NetworkFcbSummary, type NlArc, type NlFcbResult, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppFixState, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type SatGeom, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
@@ -631,6 +631,9 @@ interface PppOptions {
631
631
  ztdProcessNoise?: number;
632
632
  /** Model + estimate the troposphere. Default true. */
633
633
  troposphere?: boolean;
634
+ /** Expose the final EKF state (position + ambiguities + covariance) in the
635
+ * solution for ambiguity-resolved positioning. Default false. */
636
+ exposeState?: boolean;
634
637
  /** High-rate precise satellite clocks (RINEX CLOCK, typically 30 s). When
635
638
  * given, satellite clock offsets come from this by linear interpolation
636
639
  * instead of the SP3 5-minute clocks — the dominant PPP accuracy term.
@@ -669,6 +672,31 @@ interface PppSolution {
669
672
  /** Per-arc converged float ambiguities — one per continuous satellite
670
673
  * tracking arc — the raw material for ambiguity resolution (PPP-AR). */
671
674
  arcs: PppArc[];
675
+ /** Final EKF state (position + active ambiguities + full covariance) for
676
+ * ambiguity-resolved (fixed) positioning. Present only when
677
+ * `PppOptions.exposeState` is set. */
678
+ finalState?: PppFixState;
679
+ }
680
+ /** Final float state exposed for PPP-AR position fixing (`fixPppPosition`). */
681
+ interface PppFixState {
682
+ /** Float position (m); rows 0–2 of the covariance. */
683
+ position: [number, number, number];
684
+ /** Full EKF covariance (n×n). State order: X,Y,Z,ZWD, clocks…, ambiguities. */
685
+ covariance: number[][];
686
+ /** Active ambiguities at the final epoch. */
687
+ ambiguities: {
688
+ prn: string;
689
+ /** Row/column of this ambiguity in `covariance`. */
690
+ index: number;
691
+ /** Float ionosphere-free ambiguity (m). */
692
+ aIF: number;
693
+ /** Arc-averaged Melbourne–Wübbena wide-lane (cycles). */
694
+ mwCyc: number;
695
+ f1: number;
696
+ f2: number;
697
+ /** Mean satellite elevation over the arc (deg). */
698
+ elevDeg: number;
699
+ }[];
672
700
  }
673
701
  /** A converged carrier-phase arc: the float ionosphere-free ambiguity plus
674
702
  * the wide-lane, for integer resolution / FCB estimation. */
@@ -759,6 +787,7 @@ declare function solidEarthTide(rcv: [number, number, number], sun: [number, num
759
787
  * The relation between the IF ambiguity (metres) and the integers is
760
788
  * A_IF = λ_NL·N1 + λ_NL·(f2/(f1−f2))·N_WL, λ_NL = c/(f1+f2).
761
789
  */
790
+
762
791
  /** Wide-lane wavelength c/(f1−f2) (m). */
763
792
  declare function wlWavelength(f1: number, f2: number): number;
764
793
  /** Narrow-lane wavelength c/(f1+f2) (m). */
@@ -812,6 +841,38 @@ interface ArResult {
812
841
  * confident fix (keep the float solution).
813
842
  */
814
843
  declare function resolvePppAmbiguities(sats: ArSat[], Qaif: Float64Array, opts?: ArOptions): ArResult;
844
+ interface FixedPositionResult {
845
+ /** Whether the narrow-lane fix passed the ratio test. */
846
+ fixed: boolean;
847
+ /** Ambiguity-fixed position (m); equals `floatPosition` when not fixed. */
848
+ position: [number, number, number];
849
+ /** The input float position (m). */
850
+ floatPosition: [number, number, number];
851
+ /** Displacement fixed − float (m), ENU-agnostic ECEF. */
852
+ shift: [number, number, number];
853
+ /** Satellites whose ambiguities were fixed (excl. the SD reference). */
854
+ nFixed: number;
855
+ ratio: number;
856
+ refPrn: string | null;
857
+ }
858
+ /**
859
+ * Ambiguity-fixed (PPP-AR) position from a float `solvePpp` final state and
860
+ * the satellite fractional-cycle biases (from `estimateNetworkFcbs`).
861
+ *
862
+ * Resolves the integers (`resolvePppAmbiguities`), then **conditions the
863
+ * float position on the fixed between-satellite ambiguities** — the standard
864
+ * constrained solution
865
+ * x_fixed = x_float − Q_xz · Q_zz⁻¹ · (z_float − ẑ),
866
+ * where z = D·a are the single-differenced ambiguities (D removes the
867
+ * reference satellite, cancelling the receiver term), Q_zz = D·Q_aa·Dᵀ and
868
+ * Q_xz = Q_xa·Dᵀ come from the EKF covariance. Only satellite-to-satellite
869
+ * integers constrain the position; the reference (and the receiver clock it
870
+ * absorbs) stays free. Returns the float position unchanged if the fix fails.
871
+ */
872
+ declare function fixPppPosition(state: PppFixState, fcb: {
873
+ satWlFcb: Map<string, number>;
874
+ satNlFcb: Map<string, number>;
875
+ }, opts?: ArOptions): FixedPositionResult;
815
876
 
816
877
  /**
817
878
  * Wide-lane fractional-cycle bias (FCB) estimation for PPP-AR — the
@@ -932,6 +993,40 @@ interface NlFcbResult extends WlFcbResult {
932
993
  * — the fix rate is the honest measure of how close the network is to cm.
933
994
  */
934
995
  declare function estimateNarrowlaneFcb(arcs: NlArc[], wl: WlFcbResult, opts?: WlFcbOptions): NlFcbResult;
996
+ interface NetworkFcbSummary {
997
+ wlFixRate: number;
998
+ wlResidRms: number;
999
+ nlUsedArcs: number;
1000
+ nlFixRate: number;
1001
+ nlResidRms: number;
1002
+ wlRejected: number;
1003
+ }
1004
+ interface NetworkFcbResult {
1005
+ /** Per-satellite wide-lane FCB (cycles), all constellations merged. */
1006
+ satWlFcb: Map<string, number>;
1007
+ /** Per-satellite narrow-lane FCB (cycles), all constellations merged. */
1008
+ satNlFcb: Map<string, number>;
1009
+ /** Per-constellation fix-rate / residual summary, keyed by system letter. */
1010
+ perSystem: Record<string, NetworkFcbSummary>;
1011
+ }
1012
+ /**
1013
+ * Full network FCB calibration for PPP-AR from a set of `solvePpp` arcs (each
1014
+ * tagged with its station), **per constellation** — the receiver wide- and
1015
+ * narrow-lane biases are system-specific, so lumping GPS/Galileo/BeiDou under
1016
+ * one datum corrupts the fit. Runs the wide-lane then narrow-lane
1017
+ * decomposition for each system and merges the per-satellite biases.
1018
+ *
1019
+ * The satellite FCBs (`satWlFcb`/`satNlFcb`) are the reusable product: a rover
1020
+ * applies them and solves only its own receiver bias. Multi-GNSS input
1021
+ * multiplies the fixable arcs (Galileo roughly doubles GPS-only) without
1022
+ * changing per-arc behaviour — each satellite's arc length is independent of
1023
+ * how many constellations are present.
1024
+ */
1025
+ declare function estimateNetworkFcbs(arcs: NlArc[], opts?: {
1026
+ minArcEpochs?: number;
1027
+ minWlObs?: number;
1028
+ fixWindow?: number;
1029
+ }): NetworkFcbResult;
935
1030
 
936
1031
  /**
937
1032
  * Single-point positioning (SPP) from pseudoranges and broadcast
@@ -1022,4 +1117,4 @@ declare function ionoFree(p1: number, p2: number, f1: number, f2: number): numbe
1022
1117
  */
1023
1118
  declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
1024
1119
 
1025
- export { type AntennaOffset, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type DgnssOptions, type DgnssSolution, type EphemerisSource, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type NlArc, type NlFcbResult, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type SatGeom, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateWidelaneFcb, extractWidelaneArcs, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
1120
+ export { type AntennaOffset, type ArFixedSat, type ArOptions, type ArResult, type ArSat, type CorrectionResult, type DgnssOptions, type DgnssSolution, type EphemerisSource, type FixedPositionResult, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type NetworkFcbResult, type NetworkFcbSummary, type NlArc, type NlFcbResult, type PppAntennaModel, type PppArc, type PppCorrectionConfig, type PppCorrections, type PppEpoch, type PppEpochContext, type PppEpochResult, type PppFixState, type PppOptions, type PppSatObs, type PppSolution, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type SatGeom, type SppOptions, type SppSolution, type TropoMapping, type WlArc, type WlFcbOptions, type WlFcbResult, type WlObs, buildPppAntenna, createPppCorrections, estimateNarrowlaneFcb, estimateNetworkFcbs, estimateWidelaneFcb, extractWidelaneArcs, fixPppPosition, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
@@ -4,8 +4,10 @@ import {
4
4
  buildPppAntenna,
5
5
  createPppCorrections,
6
6
  estimateNarrowlaneFcb,
7
+ estimateNetworkFcbs,
7
8
  estimateWidelaneFcb,
8
9
  extractWidelaneArcs,
10
+ fixPppPosition,
9
11
  gimSlantIonoDelayL1,
10
12
  gimVerticalTec,
11
13
  gmst,
@@ -25,7 +27,7 @@ import {
25
27
  sunEcef,
26
28
  toRtkEpoch,
27
29
  wlWavelength
28
- } from "./chunk-H73PZB5C.js";
30
+ } from "./chunk-3FG4XVCB.js";
29
31
  import "./chunk-MHGNKSSH.js";
30
32
  import "./chunk-37QNKGTC.js";
31
33
  import "./chunk-6FAL6P4G.js";
@@ -39,8 +41,10 @@ export {
39
41
  buildPppAntenna,
40
42
  createPppCorrections,
41
43
  estimateNarrowlaneFcb,
44
+ estimateNetworkFcbs,
42
45
  estimateWidelaneFcb,
43
46
  extractWidelaneArcs,
47
+ fixPppPosition,
44
48
  gimSlantIonoDelayL1,
45
49
  gimVerticalTec,
46
50
  gmst,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "1.34.0",
3
+ "version": "1.36.0",
4
4
  "description": "Comprehensive GNSS library for JavaScript — time scales, coordinates, RINEX parsing, RTCM3 decoding, orbit computation, and signal analysis",
5
5
  "type": "module",
6
6
  "sideEffects": false,