gnss-js 1.31.0 → 1.32.1

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.
@@ -1788,8 +1788,6 @@ function solvePpp(epochs, sp3, opts) {
1788
1788
  P[ci][ci] = CLK_VAR;
1789
1789
  }
1790
1790
  let nSats = 0;
1791
- let sumPhaseResSq = 0;
1792
- let nPhaseRes = 0;
1793
1791
  for (const v of vis) {
1794
1792
  const ci = clockOf(v.prn[0]);
1795
1793
  const common = v.range + x[ci] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
@@ -1840,15 +1838,37 @@ function solvePpp(epochs, sp3, opts) {
1840
1838
  h[ci] = 1;
1841
1839
  h[IDX_ZWD] = v.mw;
1842
1840
  h[ai] = 1;
1843
- if (ekfUpdateScalar(x, P, h, innov, rPhase, 4)) {
1844
- sumPhaseResSq += innov * innov;
1845
- nPhaseRes++;
1846
- }
1841
+ ekfUpdateScalar(x, P, h, innov, rPhase, 4);
1847
1842
  }
1848
1843
  }
1849
1844
  lastSeen.set(v.prn, ei);
1850
1845
  nSats++;
1851
1846
  }
1847
+ let sumPhaseResSq = 0;
1848
+ let nPhaseRes = 0;
1849
+ {
1850
+ const bySysRes = /* @__PURE__ */ new Map();
1851
+ for (const v of vis) {
1852
+ const ai = ambIdx.get(v.prn);
1853
+ if (ai === void 0) continue;
1854
+ const sys = v.prn[0];
1855
+ const ci = clockOf(sys);
1856
+ const commonPh = v.range + x[ci] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
1857
+ const innov = v.lIf - (commonPh + v.windupM + x[ai]);
1858
+ if (Math.abs(innov) >= 0.15) continue;
1859
+ const arr = bySysRes.get(sys);
1860
+ if (arr) arr.push(innov);
1861
+ else bySysRes.set(sys, [innov]);
1862
+ }
1863
+ for (const res of bySysRes.values()) {
1864
+ const mean = res.reduce((a, b) => a + b, 0) / res.length;
1865
+ for (const r of res) {
1866
+ const d = r - mean;
1867
+ sumPhaseResSq += d * d;
1868
+ nPhaseRes++;
1869
+ }
1870
+ }
1871
+ }
1852
1872
  for (const [prn, seen] of [...lastSeen]) {
1853
1873
  if (ei - seen > 20) {
1854
1874
  const idx = ambIdx.get(prn);
@@ -2001,6 +2021,122 @@ function resolvePppAmbiguities(sats, Qaif, opts = {}) {
2001
2021
  };
2002
2022
  }
2003
2023
 
2024
+ // src/positioning/ppp-fcb.ts
2025
+ function extractWidelaneArcs(station, epochs, opts = {}) {
2026
+ const slipCyc = opts.slipCyc ?? 4;
2027
+ const gapMs = opts.gapMs ?? 12e4;
2028
+ const minObs = opts.minObs ?? 20;
2029
+ const state = /* @__PURE__ */ new Map();
2030
+ const out = [];
2031
+ const flush = (prn) => {
2032
+ const s = state.get(prn);
2033
+ if (s && s.n >= minObs)
2034
+ out.push({ station, prn, wlFloat: s.sum / s.n, nObs: s.n });
2035
+ };
2036
+ for (const ep of epochs) {
2037
+ for (const o of ep.obs) {
2038
+ if (!o.c1 || !o.c2 || !o.l1 || !o.l2) continue;
2039
+ const lamW = C_LIGHT / (o.f1 - o.f2);
2040
+ const codeNl = (o.f1 * o.c1 + o.f2 * o.c2) / (o.f1 + o.f2);
2041
+ const mw = o.l1 - o.l2 - codeNl / lamW;
2042
+ const s = state.get(o.prn);
2043
+ if (!s || ep.timeMs - s.lastT > gapMs || Math.abs(mw - s.mean) > slipCyc) {
2044
+ if (s) flush(o.prn);
2045
+ state.set(o.prn, { sum: mw, n: 1, mean: mw, lastT: ep.timeMs });
2046
+ } else {
2047
+ s.sum += mw;
2048
+ s.n += 1;
2049
+ s.mean = s.sum / s.n;
2050
+ s.lastT = ep.timeMs;
2051
+ }
2052
+ }
2053
+ }
2054
+ for (const prn of state.keys()) flush(prn);
2055
+ return out;
2056
+ }
2057
+ function wrapHalf(x) {
2058
+ return x - Math.round(x);
2059
+ }
2060
+ function circMean(vals, weights) {
2061
+ let s = 0;
2062
+ let c = 0;
2063
+ for (let i = 0; i < vals.length; i++) {
2064
+ const a = 2 * Math.PI * vals[i];
2065
+ s += weights[i] * Math.sin(a);
2066
+ c += weights[i] * Math.cos(a);
2067
+ }
2068
+ if (s === 0 && c === 0) return 0;
2069
+ return wrapHalf(Math.atan2(s, c) / (2 * Math.PI));
2070
+ }
2071
+ function estimateWidelaneFcb(arcs, opts = {}) {
2072
+ const maxIter = opts.maxIter ?? 50;
2073
+ const tol = opts.tol ?? 1e-4;
2074
+ const fixWindow = opts.fixWindow ?? 0.25;
2075
+ const sats = [...new Set(arcs.map((a) => a.prn))].sort();
2076
+ const stations = [...new Set(arcs.map((a) => a.station))].sort();
2077
+ const refStation = stations[0] ?? "";
2078
+ const satFcb = new Map(sats.map((s) => [s, 0]));
2079
+ const rcvBias = new Map(stations.map((s) => [s, 0]));
2080
+ const nInt = new Array(arcs.length).fill(0);
2081
+ let iterations = 0;
2082
+ for (let it = 0; it < maxIter; it++) {
2083
+ iterations = it + 1;
2084
+ for (let i = 0; i < arcs.length; i++) {
2085
+ const a = arcs[i];
2086
+ nInt[i] = Math.round(
2087
+ a.wlFloat - rcvBias.get(a.station) - satFcb.get(a.prn)
2088
+ );
2089
+ }
2090
+ let maxChange = 0;
2091
+ for (const s of sats) {
2092
+ const vals = [];
2093
+ const w = [];
2094
+ for (let i = 0; i < arcs.length; i++) {
2095
+ const a = arcs[i];
2096
+ if (a.prn !== s) continue;
2097
+ vals.push(a.wlFloat - nInt[i] - rcvBias.get(a.station));
2098
+ w.push(a.nObs);
2099
+ }
2100
+ if (!vals.length) continue;
2101
+ const nv = circMean(vals, w);
2102
+ maxChange = Math.max(maxChange, Math.abs(wrapHalf(nv - satFcb.get(s))));
2103
+ satFcb.set(s, nv);
2104
+ }
2105
+ for (const r of stations) {
2106
+ const vals = [];
2107
+ const w = [];
2108
+ for (let i = 0; i < arcs.length; i++) {
2109
+ const a = arcs[i];
2110
+ if (a.station !== r) continue;
2111
+ vals.push(a.wlFloat - nInt[i] - satFcb.get(a.prn));
2112
+ w.push(a.nObs);
2113
+ }
2114
+ if (!vals.length) continue;
2115
+ rcvBias.set(r, circMean(vals, w));
2116
+ }
2117
+ const shift = rcvBias.get(refStation);
2118
+ if (shift !== 0) {
2119
+ for (const r of stations)
2120
+ rcvBias.set(r, wrapHalf(rcvBias.get(r) - shift));
2121
+ for (const s of sats) satFcb.set(s, wrapHalf(satFcb.get(s) + shift));
2122
+ }
2123
+ if (maxChange < tol) break;
2124
+ }
2125
+ let sumSq = 0;
2126
+ let fixed = 0;
2127
+ for (let i = 0; i < arcs.length; i++) {
2128
+ const a = arcs[i];
2129
+ const resid = wrapHalf(
2130
+ a.wlFloat - nInt[i] - rcvBias.get(a.station) - satFcb.get(a.prn)
2131
+ );
2132
+ sumSq += resid * resid;
2133
+ if (Math.abs(resid) <= fixWindow) fixed++;
2134
+ }
2135
+ const residRms = arcs.length ? Math.sqrt(sumSq / arcs.length) : 0;
2136
+ const fixRate = arcs.length ? fixed / arcs.length : 0;
2137
+ return { satFcb, rcvBias, residRms, fixRate, refStation, iterations };
2138
+ }
2139
+
2004
2140
  // src/positioning/index.ts
2005
2141
  var GM_GPS = 3986005e8;
2006
2142
  var F_REL = -4442807633e-19;
@@ -2250,6 +2386,8 @@ export {
2250
2386
  wlWavelength,
2251
2387
  nlWavelength,
2252
2388
  resolvePppAmbiguities,
2389
+ extractWidelaneArcs,
2390
+ estimateWidelaneFcb,
2253
2391
  satClockCorrection,
2254
2392
  ionoFree,
2255
2393
  solveSpp
package/dist/index.cjs CHANGED
@@ -149,7 +149,9 @@ __export(src_exports, {
149
149
  ecefToAzEl: () => ecefToAzEl,
150
150
  ecefToGeodetic: () => ecefToGeodetic,
151
151
  ephInfoToEphemeris: () => ephInfoToEphemeris,
152
+ estimateWidelaneFcb: () => estimateWidelaneFcb,
152
153
  euclidean3D: () => euclidean3D,
154
+ extractWidelaneArcs: () => extractWidelaneArcs,
153
155
  fetchSourcetable: () => fetchSourcetable,
154
156
  findSatBias: () => findSatBias,
155
157
  fmtD: () => fmtD,
@@ -13182,8 +13184,6 @@ function solvePpp(epochs, sp3, opts) {
13182
13184
  P[ci][ci] = CLK_VAR;
13183
13185
  }
13184
13186
  let nSats = 0;
13185
- let sumPhaseResSq = 0;
13186
- let nPhaseRes = 0;
13187
13187
  for (const v of vis) {
13188
13188
  const ci = clockOf(v.prn[0]);
13189
13189
  const common = v.range + x[ci] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
@@ -13234,15 +13234,37 @@ function solvePpp(epochs, sp3, opts) {
13234
13234
  h[ci] = 1;
13235
13235
  h[IDX_ZWD] = v.mw;
13236
13236
  h[ai] = 1;
13237
- if (ekfUpdateScalar(x, P, h, innov, rPhase, 4)) {
13238
- sumPhaseResSq += innov * innov;
13239
- nPhaseRes++;
13240
- }
13237
+ ekfUpdateScalar(x, P, h, innov, rPhase, 4);
13241
13238
  }
13242
13239
  }
13243
13240
  lastSeen.set(v.prn, ei);
13244
13241
  nSats++;
13245
13242
  }
13243
+ let sumPhaseResSq = 0;
13244
+ let nPhaseRes = 0;
13245
+ {
13246
+ const bySysRes = /* @__PURE__ */ new Map();
13247
+ for (const v of vis) {
13248
+ const ai = ambIdx.get(v.prn);
13249
+ if (ai === void 0) continue;
13250
+ const sys = v.prn[0];
13251
+ const ci = clockOf(sys);
13252
+ const commonPh = v.range + x[ci] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
13253
+ const innov = v.lIf - (commonPh + v.windupM + x[ai]);
13254
+ if (Math.abs(innov) >= 0.15) continue;
13255
+ const arr = bySysRes.get(sys);
13256
+ if (arr) arr.push(innov);
13257
+ else bySysRes.set(sys, [innov]);
13258
+ }
13259
+ for (const res of bySysRes.values()) {
13260
+ const mean = res.reduce((a, b) => a + b, 0) / res.length;
13261
+ for (const r of res) {
13262
+ const d = r - mean;
13263
+ sumPhaseResSq += d * d;
13264
+ nPhaseRes++;
13265
+ }
13266
+ }
13267
+ }
13246
13268
  for (const [prn, seen] of [...lastSeen]) {
13247
13269
  if (ei - seen > 20) {
13248
13270
  const idx = ambIdx.get(prn);
@@ -13395,6 +13417,122 @@ function resolvePppAmbiguities(sats, Qaif, opts = {}) {
13395
13417
  };
13396
13418
  }
13397
13419
 
13420
+ // src/positioning/ppp-fcb.ts
13421
+ function extractWidelaneArcs(station, epochs, opts = {}) {
13422
+ const slipCyc = opts.slipCyc ?? 4;
13423
+ const gapMs = opts.gapMs ?? 12e4;
13424
+ const minObs = opts.minObs ?? 20;
13425
+ const state = /* @__PURE__ */ new Map();
13426
+ const out = [];
13427
+ const flush = (prn) => {
13428
+ const s = state.get(prn);
13429
+ if (s && s.n >= minObs)
13430
+ out.push({ station, prn, wlFloat: s.sum / s.n, nObs: s.n });
13431
+ };
13432
+ for (const ep of epochs) {
13433
+ for (const o of ep.obs) {
13434
+ if (!o.c1 || !o.c2 || !o.l1 || !o.l2) continue;
13435
+ const lamW = C_LIGHT / (o.f1 - o.f2);
13436
+ const codeNl = (o.f1 * o.c1 + o.f2 * o.c2) / (o.f1 + o.f2);
13437
+ const mw = o.l1 - o.l2 - codeNl / lamW;
13438
+ const s = state.get(o.prn);
13439
+ if (!s || ep.timeMs - s.lastT > gapMs || Math.abs(mw - s.mean) > slipCyc) {
13440
+ if (s) flush(o.prn);
13441
+ state.set(o.prn, { sum: mw, n: 1, mean: mw, lastT: ep.timeMs });
13442
+ } else {
13443
+ s.sum += mw;
13444
+ s.n += 1;
13445
+ s.mean = s.sum / s.n;
13446
+ s.lastT = ep.timeMs;
13447
+ }
13448
+ }
13449
+ }
13450
+ for (const prn of state.keys()) flush(prn);
13451
+ return out;
13452
+ }
13453
+ function wrapHalf(x) {
13454
+ return x - Math.round(x);
13455
+ }
13456
+ function circMean(vals, weights) {
13457
+ let s = 0;
13458
+ let c = 0;
13459
+ for (let i = 0; i < vals.length; i++) {
13460
+ const a = 2 * Math.PI * vals[i];
13461
+ s += weights[i] * Math.sin(a);
13462
+ c += weights[i] * Math.cos(a);
13463
+ }
13464
+ if (s === 0 && c === 0) return 0;
13465
+ return wrapHalf(Math.atan2(s, c) / (2 * Math.PI));
13466
+ }
13467
+ function estimateWidelaneFcb(arcs, opts = {}) {
13468
+ const maxIter = opts.maxIter ?? 50;
13469
+ const tol = opts.tol ?? 1e-4;
13470
+ const fixWindow = opts.fixWindow ?? 0.25;
13471
+ const sats = [...new Set(arcs.map((a) => a.prn))].sort();
13472
+ const stations = [...new Set(arcs.map((a) => a.station))].sort();
13473
+ const refStation = stations[0] ?? "";
13474
+ const satFcb = new Map(sats.map((s) => [s, 0]));
13475
+ const rcvBias = new Map(stations.map((s) => [s, 0]));
13476
+ const nInt = new Array(arcs.length).fill(0);
13477
+ let iterations = 0;
13478
+ for (let it = 0; it < maxIter; it++) {
13479
+ iterations = it + 1;
13480
+ for (let i = 0; i < arcs.length; i++) {
13481
+ const a = arcs[i];
13482
+ nInt[i] = Math.round(
13483
+ a.wlFloat - rcvBias.get(a.station) - satFcb.get(a.prn)
13484
+ );
13485
+ }
13486
+ let maxChange = 0;
13487
+ for (const s of sats) {
13488
+ const vals = [];
13489
+ const w = [];
13490
+ for (let i = 0; i < arcs.length; i++) {
13491
+ const a = arcs[i];
13492
+ if (a.prn !== s) continue;
13493
+ vals.push(a.wlFloat - nInt[i] - rcvBias.get(a.station));
13494
+ w.push(a.nObs);
13495
+ }
13496
+ if (!vals.length) continue;
13497
+ const nv = circMean(vals, w);
13498
+ maxChange = Math.max(maxChange, Math.abs(wrapHalf(nv - satFcb.get(s))));
13499
+ satFcb.set(s, nv);
13500
+ }
13501
+ for (const r of stations) {
13502
+ const vals = [];
13503
+ const w = [];
13504
+ for (let i = 0; i < arcs.length; i++) {
13505
+ const a = arcs[i];
13506
+ if (a.station !== r) continue;
13507
+ vals.push(a.wlFloat - nInt[i] - satFcb.get(a.prn));
13508
+ w.push(a.nObs);
13509
+ }
13510
+ if (!vals.length) continue;
13511
+ rcvBias.set(r, circMean(vals, w));
13512
+ }
13513
+ const shift = rcvBias.get(refStation);
13514
+ if (shift !== 0) {
13515
+ for (const r of stations)
13516
+ rcvBias.set(r, wrapHalf(rcvBias.get(r) - shift));
13517
+ for (const s of sats) satFcb.set(s, wrapHalf(satFcb.get(s) + shift));
13518
+ }
13519
+ if (maxChange < tol) break;
13520
+ }
13521
+ let sumSq = 0;
13522
+ let fixed = 0;
13523
+ for (let i = 0; i < arcs.length; i++) {
13524
+ const a = arcs[i];
13525
+ const resid = wrapHalf(
13526
+ a.wlFloat - nInt[i] - rcvBias.get(a.station) - satFcb.get(a.prn)
13527
+ );
13528
+ sumSq += resid * resid;
13529
+ if (Math.abs(resid) <= fixWindow) fixed++;
13530
+ }
13531
+ const residRms = arcs.length ? Math.sqrt(sumSq / arcs.length) : 0;
13532
+ const fixRate = arcs.length ? fixed / arcs.length : 0;
13533
+ return { satFcb, rcvBias, residRms, fixRate, refStation, iterations };
13534
+ }
13535
+
13398
13536
  // src/positioning/index.ts
13399
13537
  var GM_GPS3 = 3986005e8;
13400
13538
  var F_REL = -4442807633e-19;
@@ -16012,7 +16150,9 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
16012
16150
  ecefToAzEl,
16013
16151
  ecefToGeodetic,
16014
16152
  ephInfoToEphemeris,
16153
+ estimateWidelaneFcb,
16015
16154
  euclidean3D,
16155
+ extractWidelaneArcs,
16016
16156
  fetchSourcetable,
16017
16157
  findSatBias,
16018
16158
  fmtD,
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, PppAntennaModel, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, SatGeom, SppOptions, SppSolution, TropoMapping, buildPppAntenna, createPppCorrections, 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, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, PppAntennaModel, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, SatGeom, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, buildPppAntenna, createPppCorrections, estimateWidelaneFcb, extractWidelaneArcs, 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, PppAntennaModel, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, SatGeom, SppOptions, SppSolution, TropoMapping, buildPppAntenna, createPppCorrections, 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, IONO_L1_M_PER_TECU, KlobucharCoeffs, LambdaResult, PppAntennaModel, PppCorrectionConfig, PppCorrections, PppEpoch, PppEpochContext, PppEpochResult, PppOptions, PppSatObs, PppSolution, RawObservation, RtkEpochMeasurements, RtkFloatEngine, RtkFloatOptions, RtkFloatSolution, RtkMeasurement, SatGeom, SppOptions, SppSolution, TropoMapping, WlArc, WlFcbOptions, WlFcbResult, WlObs, buildPppAntenna, createPppCorrections, estimateWidelaneFcb, extractWidelaneArcs, 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
@@ -146,6 +146,8 @@ import {
146
146
  RtkFloatEngine,
147
147
  buildPppAntenna,
148
148
  createPppCorrections,
149
+ estimateWidelaneFcb,
150
+ extractWidelaneArcs,
149
151
  gimSlantIonoDelayL1,
150
152
  gimVerticalTec,
151
153
  gmst,
@@ -165,7 +167,7 @@ import {
165
167
  sunEcef,
166
168
  toRtkEpoch,
167
169
  wlWavelength
168
- } from "./chunk-EVFD4UNZ.js";
170
+ } from "./chunk-KD2KJGLQ.js";
169
171
  import {
170
172
  almanacEpochMs,
171
173
  almanacSatPosition,
@@ -472,7 +474,9 @@ export {
472
474
  ecefToAzEl,
473
475
  ecefToGeodetic,
474
476
  ephInfoToEphemeris,
477
+ estimateWidelaneFcb,
475
478
  euclidean3D,
479
+ extractWidelaneArcs,
476
480
  fetchSourcetable,
477
481
  findSatBias,
478
482
  fmtD,
@@ -24,6 +24,8 @@ __export(positioning_exports, {
24
24
  RtkFloatEngine: () => RtkFloatEngine,
25
25
  buildPppAntenna: () => buildPppAntenna,
26
26
  createPppCorrections: () => createPppCorrections,
27
+ estimateWidelaneFcb: () => estimateWidelaneFcb,
28
+ extractWidelaneArcs: () => extractWidelaneArcs,
27
29
  gimSlantIonoDelayL1: () => gimSlantIonoDelayL1,
28
30
  gimVerticalTec: () => gimVerticalTec,
29
31
  gmst: () => gmst,
@@ -2310,8 +2312,6 @@ function solvePpp(epochs, sp3, opts) {
2310
2312
  P[ci][ci] = CLK_VAR;
2311
2313
  }
2312
2314
  let nSats = 0;
2313
- let sumPhaseResSq = 0;
2314
- let nPhaseRes = 0;
2315
2315
  for (const v of vis) {
2316
2316
  const ci = clockOf(v.prn[0]);
2317
2317
  const common = v.range + x[ci] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
@@ -2362,15 +2362,37 @@ function solvePpp(epochs, sp3, opts) {
2362
2362
  h[ci] = 1;
2363
2363
  h[IDX_ZWD] = v.mw;
2364
2364
  h[ai] = 1;
2365
- if (ekfUpdateScalar(x, P, h, innov, rPhase, 4)) {
2366
- sumPhaseResSq += innov * innov;
2367
- nPhaseRes++;
2368
- }
2365
+ ekfUpdateScalar(x, P, h, innov, rPhase, 4);
2369
2366
  }
2370
2367
  }
2371
2368
  lastSeen.set(v.prn, ei);
2372
2369
  nSats++;
2373
2370
  }
2371
+ let sumPhaseResSq = 0;
2372
+ let nPhaseRes = 0;
2373
+ {
2374
+ const bySysRes = /* @__PURE__ */ new Map();
2375
+ for (const v of vis) {
2376
+ const ai = ambIdx.get(v.prn);
2377
+ if (ai === void 0) continue;
2378
+ const sys = v.prn[0];
2379
+ const ci = clockOf(sys);
2380
+ const commonPh = v.range + x[ci] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
2381
+ const innov = v.lIf - (commonPh + v.windupM + x[ai]);
2382
+ if (Math.abs(innov) >= 0.15) continue;
2383
+ const arr = bySysRes.get(sys);
2384
+ if (arr) arr.push(innov);
2385
+ else bySysRes.set(sys, [innov]);
2386
+ }
2387
+ for (const res of bySysRes.values()) {
2388
+ const mean = res.reduce((a, b) => a + b, 0) / res.length;
2389
+ for (const r of res) {
2390
+ const d = r - mean;
2391
+ sumPhaseResSq += d * d;
2392
+ nPhaseRes++;
2393
+ }
2394
+ }
2395
+ }
2374
2396
  for (const [prn, seen] of [...lastSeen]) {
2375
2397
  if (ei - seen > 20) {
2376
2398
  const idx = ambIdx.get(prn);
@@ -2523,6 +2545,122 @@ function resolvePppAmbiguities(sats, Qaif, opts = {}) {
2523
2545
  };
2524
2546
  }
2525
2547
 
2548
+ // src/positioning/ppp-fcb.ts
2549
+ function extractWidelaneArcs(station, epochs, opts = {}) {
2550
+ const slipCyc = opts.slipCyc ?? 4;
2551
+ const gapMs = opts.gapMs ?? 12e4;
2552
+ const minObs = opts.minObs ?? 20;
2553
+ const state = /* @__PURE__ */ new Map();
2554
+ const out = [];
2555
+ const flush = (prn) => {
2556
+ const s = state.get(prn);
2557
+ if (s && s.n >= minObs)
2558
+ out.push({ station, prn, wlFloat: s.sum / s.n, nObs: s.n });
2559
+ };
2560
+ for (const ep of epochs) {
2561
+ for (const o of ep.obs) {
2562
+ if (!o.c1 || !o.c2 || !o.l1 || !o.l2) continue;
2563
+ const lamW = C_LIGHT / (o.f1 - o.f2);
2564
+ const codeNl = (o.f1 * o.c1 + o.f2 * o.c2) / (o.f1 + o.f2);
2565
+ const mw = o.l1 - o.l2 - codeNl / lamW;
2566
+ const s = state.get(o.prn);
2567
+ if (!s || ep.timeMs - s.lastT > gapMs || Math.abs(mw - s.mean) > slipCyc) {
2568
+ if (s) flush(o.prn);
2569
+ state.set(o.prn, { sum: mw, n: 1, mean: mw, lastT: ep.timeMs });
2570
+ } else {
2571
+ s.sum += mw;
2572
+ s.n += 1;
2573
+ s.mean = s.sum / s.n;
2574
+ s.lastT = ep.timeMs;
2575
+ }
2576
+ }
2577
+ }
2578
+ for (const prn of state.keys()) flush(prn);
2579
+ return out;
2580
+ }
2581
+ function wrapHalf(x) {
2582
+ return x - Math.round(x);
2583
+ }
2584
+ function circMean(vals, weights) {
2585
+ let s = 0;
2586
+ let c = 0;
2587
+ for (let i = 0; i < vals.length; i++) {
2588
+ const a = 2 * Math.PI * vals[i];
2589
+ s += weights[i] * Math.sin(a);
2590
+ c += weights[i] * Math.cos(a);
2591
+ }
2592
+ if (s === 0 && c === 0) return 0;
2593
+ return wrapHalf(Math.atan2(s, c) / (2 * Math.PI));
2594
+ }
2595
+ function estimateWidelaneFcb(arcs, opts = {}) {
2596
+ const maxIter = opts.maxIter ?? 50;
2597
+ const tol = opts.tol ?? 1e-4;
2598
+ const fixWindow = opts.fixWindow ?? 0.25;
2599
+ const sats = [...new Set(arcs.map((a) => a.prn))].sort();
2600
+ const stations = [...new Set(arcs.map((a) => a.station))].sort();
2601
+ const refStation = stations[0] ?? "";
2602
+ const satFcb = new Map(sats.map((s) => [s, 0]));
2603
+ const rcvBias = new Map(stations.map((s) => [s, 0]));
2604
+ const nInt = new Array(arcs.length).fill(0);
2605
+ let iterations = 0;
2606
+ for (let it = 0; it < maxIter; it++) {
2607
+ iterations = it + 1;
2608
+ for (let i = 0; i < arcs.length; i++) {
2609
+ const a = arcs[i];
2610
+ nInt[i] = Math.round(
2611
+ a.wlFloat - rcvBias.get(a.station) - satFcb.get(a.prn)
2612
+ );
2613
+ }
2614
+ let maxChange = 0;
2615
+ for (const s of sats) {
2616
+ const vals = [];
2617
+ const w = [];
2618
+ for (let i = 0; i < arcs.length; i++) {
2619
+ const a = arcs[i];
2620
+ if (a.prn !== s) continue;
2621
+ vals.push(a.wlFloat - nInt[i] - rcvBias.get(a.station));
2622
+ w.push(a.nObs);
2623
+ }
2624
+ if (!vals.length) continue;
2625
+ const nv = circMean(vals, w);
2626
+ maxChange = Math.max(maxChange, Math.abs(wrapHalf(nv - satFcb.get(s))));
2627
+ satFcb.set(s, nv);
2628
+ }
2629
+ for (const r of stations) {
2630
+ const vals = [];
2631
+ const w = [];
2632
+ for (let i = 0; i < arcs.length; i++) {
2633
+ const a = arcs[i];
2634
+ if (a.station !== r) continue;
2635
+ vals.push(a.wlFloat - nInt[i] - satFcb.get(a.prn));
2636
+ w.push(a.nObs);
2637
+ }
2638
+ if (!vals.length) continue;
2639
+ rcvBias.set(r, circMean(vals, w));
2640
+ }
2641
+ const shift = rcvBias.get(refStation);
2642
+ if (shift !== 0) {
2643
+ for (const r of stations)
2644
+ rcvBias.set(r, wrapHalf(rcvBias.get(r) - shift));
2645
+ for (const s of sats) satFcb.set(s, wrapHalf(satFcb.get(s) + shift));
2646
+ }
2647
+ if (maxChange < tol) break;
2648
+ }
2649
+ let sumSq = 0;
2650
+ let fixed = 0;
2651
+ for (let i = 0; i < arcs.length; i++) {
2652
+ const a = arcs[i];
2653
+ const resid = wrapHalf(
2654
+ a.wlFloat - nInt[i] - rcvBias.get(a.station) - satFcb.get(a.prn)
2655
+ );
2656
+ sumSq += resid * resid;
2657
+ if (Math.abs(resid) <= fixWindow) fixed++;
2658
+ }
2659
+ const residRms = arcs.length ? Math.sqrt(sumSq / arcs.length) : 0;
2660
+ const fixRate = arcs.length ? fixed / arcs.length : 0;
2661
+ return { satFcb, rcvBias, residRms, fixRate, refStation, iterations };
2662
+ }
2663
+
2526
2664
  // src/positioning/index.ts
2527
2665
  var GM_GPS2 = 3986005e8;
2528
2666
  var F_REL = -4442807633e-19;
@@ -2756,6 +2894,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2756
2894
  RtkFloatEngine,
2757
2895
  buildPppAntenna,
2758
2896
  createPppCorrections,
2897
+ estimateWidelaneFcb,
2898
+ extractWidelaneArcs,
2759
2899
  gimSlantIonoDelayL1,
2760
2900
  gimVerticalTec,
2761
2901
  gmst,
@@ -793,6 +793,93 @@ interface ArResult {
793
793
  */
794
794
  declare function resolvePppAmbiguities(sats: ArSat[], Qaif: Float64Array, opts?: ArOptions): ArResult;
795
795
 
796
+ /**
797
+ * Wide-lane fractional-cycle bias (FCB) estimation for PPP-AR — the
798
+ * self-contained route to integer ambiguities that needs no external
799
+ * phase-bias product, only a small network of stations.
800
+ *
801
+ * A single receiver cannot resolve ambiguities because its Melbourne–Wübbena
802
+ * wide-lane float is `Ñ_wl = N (integer) + b_rcv + b_sat`, and the integer
803
+ * and the satellite fractional bias `b_sat` are entangled. Across a *network*
804
+ * they separate: every station sees the same `b_sat` for a given satellite
805
+ * but its own integers and receiver bias, so the fractional part common to
806
+ * all stations *is* the satellite FCB. This is the classic Ge/Gendt method.
807
+ *
808
+ * The estimator alternates:
809
+ * 1. round each arc to its integer N = round(Ñ_wl − b_rcv − b_sat);
810
+ * 2. update each satellite FCB = circular-mean over stations of
811
+ * (Ñ_wl − N − b_rcv);
812
+ * 3. update each receiver bias = circular-mean over satellites of
813
+ * (Ñ_wl − N − b_sat);
814
+ * 4. pin the datum (b_rcv of a reference station ≡ 0) — receiver and
815
+ * satellite biases otherwise trade off by an arbitrary constant.
816
+ *
817
+ * The output `satFcb` feeds `resolvePppAmbiguities`' `wlBiasCyc`.
818
+ */
819
+ /** Minimal per-satellite observation for wide-lane extraction. */
820
+ interface WlObs {
821
+ prn: string;
822
+ f1: number;
823
+ f2: number;
824
+ /** Code on f1/f2 (m) and carrier phase on f1/f2 (cycles). */
825
+ c1: number;
826
+ c2: number;
827
+ l1: number;
828
+ l2: number;
829
+ }
830
+ /**
831
+ * Extract arc-averaged Melbourne–Wübbena wide-lanes for one station from its
832
+ * epoch stream. The MW combination
833
+ * MW = (L1 − L2) − [(f1·C1 + f2·C2)/(f1+f2)] / (c/(f1−f2)) [cycles]
834
+ * is geometry-, clock- and (first-order) ionosphere-free, so it holds the
835
+ * wide-lane ambiguity plus biases and is near-constant within an arc. Arcs
836
+ * are split on a running-mean jump (cycle slip) or a time gap.
837
+ */
838
+ declare function extractWidelaneArcs(station: string, epochs: {
839
+ timeMs: number;
840
+ obs: WlObs[];
841
+ }[], opts?: {
842
+ slipCyc?: number;
843
+ gapMs?: number;
844
+ minObs?: number;
845
+ }): WlArc[];
846
+ /** One continuous wide-lane arc at a station. */
847
+ interface WlArc {
848
+ station: string;
849
+ prn: string;
850
+ /** Arc-averaged Melbourne–Wübbena wide-lane float (cycles). */
851
+ wlFloat: number;
852
+ /** Number of epochs averaged (used as the weight). */
853
+ nObs: number;
854
+ }
855
+ interface WlFcbOptions {
856
+ /** Max iterations. Default 50. */
857
+ maxIter?: number;
858
+ /** Convergence threshold on the largest bias change (cycles). Default 1e-4. */
859
+ tol?: number;
860
+ /** Fix-rate acceptance window |resid| (cycles). Default 0.25. */
861
+ fixWindow?: number;
862
+ }
863
+ interface WlFcbResult {
864
+ /** Per-satellite wide-lane FCB (cycles, wrapped to [−0.5, 0.5)). */
865
+ satFcb: Map<string, number>;
866
+ /** Per-station wide-lane bias (cycles); the reference station is 0. */
867
+ rcvBias: Map<string, number>;
868
+ /** RMS of the post-fit arc residuals Ñ_wl − N − b_rcv − b_sat (cycles). */
869
+ residRms: number;
870
+ /** Fraction of arcs whose residual lies within `fixWindow` of an integer. */
871
+ fixRate: number;
872
+ /** Reference station pinning the datum. */
873
+ refStation: string;
874
+ iterations: number;
875
+ }
876
+ /**
877
+ * Estimate per-satellite wide-lane FCBs from a network of stations' arc
878
+ * wide-lane floats. Needs ≥2 stations sharing satellites to break the
879
+ * integer/bias entanglement.
880
+ */
881
+ declare function estimateWidelaneFcb(arcs: WlArc[], opts?: WlFcbOptions): WlFcbResult;
882
+
796
883
  /**
797
884
  * Single-point positioning (SPP) from pseudoranges and broadcast
798
885
  * ephemerides.
@@ -882,4 +969,4 @@ declare function ionoFree(p1: number, p2: number, f1: number, f2: number): numbe
882
969
  */
883
970
  declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
884
971
 
885
- 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 PppAntennaModel, 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, buildPppAntenna, createPppCorrections, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
972
+ 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 PppAntennaModel, 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, estimateWidelaneFcb, extractWidelaneArcs, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
@@ -793,6 +793,93 @@ interface ArResult {
793
793
  */
794
794
  declare function resolvePppAmbiguities(sats: ArSat[], Qaif: Float64Array, opts?: ArOptions): ArResult;
795
795
 
796
+ /**
797
+ * Wide-lane fractional-cycle bias (FCB) estimation for PPP-AR — the
798
+ * self-contained route to integer ambiguities that needs no external
799
+ * phase-bias product, only a small network of stations.
800
+ *
801
+ * A single receiver cannot resolve ambiguities because its Melbourne–Wübbena
802
+ * wide-lane float is `Ñ_wl = N (integer) + b_rcv + b_sat`, and the integer
803
+ * and the satellite fractional bias `b_sat` are entangled. Across a *network*
804
+ * they separate: every station sees the same `b_sat` for a given satellite
805
+ * but its own integers and receiver bias, so the fractional part common to
806
+ * all stations *is* the satellite FCB. This is the classic Ge/Gendt method.
807
+ *
808
+ * The estimator alternates:
809
+ * 1. round each arc to its integer N = round(Ñ_wl − b_rcv − b_sat);
810
+ * 2. update each satellite FCB = circular-mean over stations of
811
+ * (Ñ_wl − N − b_rcv);
812
+ * 3. update each receiver bias = circular-mean over satellites of
813
+ * (Ñ_wl − N − b_sat);
814
+ * 4. pin the datum (b_rcv of a reference station ≡ 0) — receiver and
815
+ * satellite biases otherwise trade off by an arbitrary constant.
816
+ *
817
+ * The output `satFcb` feeds `resolvePppAmbiguities`' `wlBiasCyc`.
818
+ */
819
+ /** Minimal per-satellite observation for wide-lane extraction. */
820
+ interface WlObs {
821
+ prn: string;
822
+ f1: number;
823
+ f2: number;
824
+ /** Code on f1/f2 (m) and carrier phase on f1/f2 (cycles). */
825
+ c1: number;
826
+ c2: number;
827
+ l1: number;
828
+ l2: number;
829
+ }
830
+ /**
831
+ * Extract arc-averaged Melbourne–Wübbena wide-lanes for one station from its
832
+ * epoch stream. The MW combination
833
+ * MW = (L1 − L2) − [(f1·C1 + f2·C2)/(f1+f2)] / (c/(f1−f2)) [cycles]
834
+ * is geometry-, clock- and (first-order) ionosphere-free, so it holds the
835
+ * wide-lane ambiguity plus biases and is near-constant within an arc. Arcs
836
+ * are split on a running-mean jump (cycle slip) or a time gap.
837
+ */
838
+ declare function extractWidelaneArcs(station: string, epochs: {
839
+ timeMs: number;
840
+ obs: WlObs[];
841
+ }[], opts?: {
842
+ slipCyc?: number;
843
+ gapMs?: number;
844
+ minObs?: number;
845
+ }): WlArc[];
846
+ /** One continuous wide-lane arc at a station. */
847
+ interface WlArc {
848
+ station: string;
849
+ prn: string;
850
+ /** Arc-averaged Melbourne–Wübbena wide-lane float (cycles). */
851
+ wlFloat: number;
852
+ /** Number of epochs averaged (used as the weight). */
853
+ nObs: number;
854
+ }
855
+ interface WlFcbOptions {
856
+ /** Max iterations. Default 50. */
857
+ maxIter?: number;
858
+ /** Convergence threshold on the largest bias change (cycles). Default 1e-4. */
859
+ tol?: number;
860
+ /** Fix-rate acceptance window |resid| (cycles). Default 0.25. */
861
+ fixWindow?: number;
862
+ }
863
+ interface WlFcbResult {
864
+ /** Per-satellite wide-lane FCB (cycles, wrapped to [−0.5, 0.5)). */
865
+ satFcb: Map<string, number>;
866
+ /** Per-station wide-lane bias (cycles); the reference station is 0. */
867
+ rcvBias: Map<string, number>;
868
+ /** RMS of the post-fit arc residuals Ñ_wl − N − b_rcv − b_sat (cycles). */
869
+ residRms: number;
870
+ /** Fraction of arcs whose residual lies within `fixWindow` of an integer. */
871
+ fixRate: number;
872
+ /** Reference station pinning the datum. */
873
+ refStation: string;
874
+ iterations: number;
875
+ }
876
+ /**
877
+ * Estimate per-satellite wide-lane FCBs from a network of stations' arc
878
+ * wide-lane floats. Needs ≥2 stations sharing satellites to break the
879
+ * integer/bias entanglement.
880
+ */
881
+ declare function estimateWidelaneFcb(arcs: WlArc[], opts?: WlFcbOptions): WlFcbResult;
882
+
796
883
  /**
797
884
  * Single-point positioning (SPP) from pseudoranges and broadcast
798
885
  * ephemerides.
@@ -882,4 +969,4 @@ declare function ionoFree(p1: number, p2: number, f1: number, f2: number): numbe
882
969
  */
883
970
  declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
884
971
 
885
- 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 PppAntennaModel, 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, buildPppAntenna, createPppCorrections, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
972
+ 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 PppAntennaModel, 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, estimateWidelaneFcb, extractWidelaneArcs, gimSlantIonoDelayL1, gimVerticalTec, gmst, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, moonEcef, niellMapping, nlWavelength, resolvePppAmbiguities, satClockCorrection, solidEarthTide, solveDgnss, solvePpp, solveSpp, sunEcef, toRtkEpoch, wlWavelength };
@@ -3,6 +3,8 @@ import {
3
3
  RtkFloatEngine,
4
4
  buildPppAntenna,
5
5
  createPppCorrections,
6
+ estimateWidelaneFcb,
7
+ extractWidelaneArcs,
6
8
  gimSlantIonoDelayL1,
7
9
  gimVerticalTec,
8
10
  gmst,
@@ -22,7 +24,7 @@ import {
22
24
  sunEcef,
23
25
  toRtkEpoch,
24
26
  wlWavelength
25
- } from "./chunk-EVFD4UNZ.js";
27
+ } from "./chunk-KD2KJGLQ.js";
26
28
  import "./chunk-MHGNKSSH.js";
27
29
  import "./chunk-37QNKGTC.js";
28
30
  import "./chunk-6FAL6P4G.js";
@@ -35,6 +37,8 @@ export {
35
37
  RtkFloatEngine,
36
38
  buildPppAntenna,
37
39
  createPppCorrections,
40
+ estimateWidelaneFcb,
41
+ extractWidelaneArcs,
38
42
  gimSlantIonoDelayL1,
39
43
  gimVerticalTec,
40
44
  gmst,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "1.31.0",
3
+ "version": "1.32.1",
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,