gnss-js 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,8 +25,10 @@ __export(positioning_exports, {
25
25
  RtkFloatEngine: () => RtkFloatEngine,
26
26
  SbasProcessor: () => SbasProcessor,
27
27
  Sp3EphemerisSource: () => Sp3EphemerisSource,
28
+ antexFreqOfGroup: () => antexFreqOfGroup,
28
29
  applyOrbitClock: () => applyOrbitClock,
29
30
  buildPppAntenna: () => buildPppAntenna,
31
+ buildRtkAntenna: () => buildRtkAntenna,
30
32
  createPppCorrections: () => createPppCorrections,
31
33
  estimateNarrowlaneFcb: () => estimateNarrowlaneFcb,
32
34
  estimateNetworkFcbs: () => estimateNetworkFcbs,
@@ -45,6 +47,7 @@ __export(positioning_exports, {
45
47
  niellMapping: () => niellMapping,
46
48
  nlWavelength: () => nlWavelength,
47
49
  postProcessRtk: () => postProcessRtk,
50
+ rcvAntennaRangeM: () => rcvAntennaRangeM,
48
51
  resolvePppAmbiguities: () => resolvePppAmbiguities,
49
52
  satClockCorrection: () => satClockCorrection,
50
53
  sbasIonoDeg: () => sbasIonoDeg,
@@ -759,6 +762,211 @@ function lambdaReduction(Q, n) {
759
762
  return Z;
760
763
  }
761
764
 
765
+ // src/positioning/ppp-antenna.ts
766
+ function parseAntexTime(s) {
767
+ const p = s.trim().split(/\s+/).map(Number);
768
+ if (p.length < 3 || p.some((v) => Number.isNaN(v))) return null;
769
+ return Date.UTC(
770
+ p[0],
771
+ (p[1] || 1) - 1,
772
+ p[2] || 1,
773
+ p[3] ?? 0,
774
+ p[4] ?? 0,
775
+ Math.floor(p[5] ?? 0)
776
+ );
777
+ }
778
+ function freqOf(entry, code) {
779
+ return entry.frequencies.find((f) => f.frequency === code);
780
+ }
781
+ function combineIf(fA, fB, f1, f2) {
782
+ const g = f1 * f1 / (f1 * f1 - f2 * f2);
783
+ const mm = 1e-3;
784
+ const pco = [
785
+ (g * fA.pcoN - (g - 1) * fB.pcoN) * mm,
786
+ (g * fA.pcoE - (g - 1) * fB.pcoE) * mm,
787
+ (g * fA.pcoU - (g - 1) * fB.pcoU) * mm
788
+ ];
789
+ const nz = Math.min(fA.pcvNoazi.length, fB.pcvNoazi.length);
790
+ const pcvNoazi = new Array(nz);
791
+ for (let i = 0; i < nz; i++) {
792
+ pcvNoazi[i] = (g * fA.pcvNoazi[i] - (g - 1) * fB.pcvNoazi[i]) * mm;
793
+ }
794
+ return { pco, pcvZen1Deg: 0, pcvDzenDeg: 0, pcvNoazi };
795
+ }
796
+ function buildPppAntenna(antex) {
797
+ const sats = /* @__PURE__ */ new Map();
798
+ const rcvs = /* @__PURE__ */ new Map();
799
+ for (const a of antex.antennas) {
800
+ if (a.isSatellite) {
801
+ const list = sats.get(a.serialNo) ?? [];
802
+ list.push(a);
803
+ sats.set(a.serialNo, list);
804
+ } else if (!rcvs.has(a.type)) {
805
+ rcvs.set(a.type, a);
806
+ }
807
+ }
808
+ const withZen = (entry, off) => ({
809
+ ...off,
810
+ pcvZen1Deg: entry.zen1,
811
+ pcvDzenDeg: entry.dzen
812
+ });
813
+ return {
814
+ satOffset(prn, dateMs, band1, band2, f1, f2) {
815
+ const list = sats.get(prn);
816
+ if (!list) return null;
817
+ const entry = list.find((e) => {
818
+ const from = parseAntexTime(e.validFrom);
819
+ const until = parseAntexTime(e.validUntil);
820
+ return (from == null || dateMs >= from) && (until == null || dateMs <= until);
821
+ });
822
+ if (!entry) return null;
823
+ const a = freqOf(entry, band1);
824
+ const b = freqOf(entry, band2);
825
+ if (!a || !b) return null;
826
+ return withZen(entry, combineIf(a, b, f1, f2));
827
+ },
828
+ rcvOffset(antType, band1, band2, f1, f2) {
829
+ const entry = rcvs.get(antType);
830
+ if (!entry) return null;
831
+ const a = freqOf(entry, band1);
832
+ const b = freqOf(entry, band2);
833
+ if (!a || !b) return null;
834
+ return withZen(entry, combineIf(a, b, f1, f2));
835
+ }
836
+ };
837
+ }
838
+ function cross(a, b) {
839
+ return [
840
+ a[1] * b[2] - a[2] * b[1],
841
+ a[2] * b[0] - a[0] * b[2],
842
+ a[0] * b[1] - a[1] * b[0]
843
+ ];
844
+ }
845
+ function norm(a) {
846
+ const n = Math.hypot(a[0], a[1], a[2]) || 1;
847
+ return [a[0] / n, a[1] / n, a[2] / n];
848
+ }
849
+ function dot(a, b) {
850
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
851
+ }
852
+ function satBodyFrame(satEcef, sunEcef2) {
853
+ const z = norm([-satEcef[0], -satEcef[1], -satEcef[2]]);
854
+ const eSun = norm([
855
+ sunEcef2[0] - satEcef[0],
856
+ sunEcef2[1] - satEcef[1],
857
+ sunEcef2[2] - satEcef[2]
858
+ ]);
859
+ const y = norm(cross(z, eSun));
860
+ const x = norm(cross(y, z));
861
+ return { x, y, z };
862
+ }
863
+ function satPcoToEcef(pcoBody, frame) {
864
+ return [
865
+ pcoBody[0] * frame.x[0] + pcoBody[1] * frame.y[0] + pcoBody[2] * frame.z[0],
866
+ pcoBody[0] * frame.x[1] + pcoBody[1] * frame.y[1] + pcoBody[2] * frame.z[1],
867
+ pcoBody[0] * frame.x[2] + pcoBody[1] * frame.y[2] + pcoBody[2] * frame.z[2]
868
+ ];
869
+ }
870
+ function enuToEcef(enu, latRad, lonRad) {
871
+ const sl = Math.sin(latRad);
872
+ const cl = Math.cos(latRad);
873
+ const so = Math.sin(lonRad);
874
+ const co = Math.cos(lonRad);
875
+ const [e, n, u] = enu;
876
+ return [
877
+ -so * e - sl * co * n + cl * co * u,
878
+ co * e - sl * so * n + cl * so * u,
879
+ cl * n + sl * u
880
+ ];
881
+ }
882
+ function interpPcv(off, angleDeg) {
883
+ const { pcvZen1Deg, pcvDzenDeg, pcvNoazi } = off;
884
+ if (pcvDzenDeg <= 0 || pcvNoazi.length < 2) return 0;
885
+ const x = (angleDeg - pcvZen1Deg) / pcvDzenDeg;
886
+ if (x <= 0) return pcvNoazi[0];
887
+ if (x >= pcvNoazi.length - 1) return pcvNoazi[pcvNoazi.length - 1];
888
+ const i = Math.floor(x);
889
+ const t = x - i;
890
+ return pcvNoazi[i] * (1 - t) + pcvNoazi[i + 1] * t;
891
+ }
892
+ function phaseWindup(prn, losRcvToSat, satFrame, latRad, lonRad, state) {
893
+ const k = [
894
+ -losRcvToSat[0],
895
+ -losRcvToSat[1],
896
+ -losRcvToSat[2]
897
+ ];
898
+ const kx = satFrame.x;
899
+ const ky = satFrame.y;
900
+ const kdotx = dot(k, kx);
901
+ const kcrossY = cross(k, ky);
902
+ const dS = [
903
+ kx[0] - k[0] * kdotx - kcrossY[0],
904
+ kx[1] - k[1] * kdotx - kcrossY[1],
905
+ kx[2] - k[2] * kdotx - kcrossY[2]
906
+ ];
907
+ const north = enuToEcef([0, 1, 0], latRad, lonRad);
908
+ const east = enuToEcef([1, 0, 0], latRad, lonRad);
909
+ const kdotn = dot(k, north);
910
+ const kcrossE = cross(k, east);
911
+ const dR = [
912
+ north[0] - k[0] * kdotn + kcrossE[0],
913
+ north[1] - k[1] * kdotn + kcrossE[1],
914
+ north[2] - k[2] * kdotn + kcrossE[2]
915
+ ];
916
+ const nS = Math.hypot(dS[0], dS[1], dS[2]) || 1;
917
+ const nR = Math.hypot(dR[0], dR[1], dR[2]) || 1;
918
+ let cosPhi = dot(dS, dR) / (nS * nR);
919
+ cosPhi = Math.max(-1, Math.min(1, cosPhi));
920
+ const zeta = cross(dS, dR);
921
+ const sign = dot(k, zeta) >= 0 ? 1 : -1;
922
+ const dphi = sign * Math.acos(cosPhi) / (2 * Math.PI);
923
+ const prev = state.get(prn);
924
+ const wu = prev == null ? dphi : dphi + Math.round(prev - dphi);
925
+ state.set(prn, wu);
926
+ return wu;
927
+ }
928
+
929
+ // src/positioning/rtk-antenna.ts
930
+ function freqOf2(entry, code) {
931
+ return entry.frequencies.find((f) => f.frequency === code);
932
+ }
933
+ function buildRtkAntenna(antex) {
934
+ const rcvs = /* @__PURE__ */ new Map();
935
+ for (const a of antex.antennas) {
936
+ if (!a.isSatellite && !rcvs.has(a.type)) rcvs.set(a.type, a);
937
+ }
938
+ const mm = 1e-3;
939
+ return {
940
+ rcvOffset(antType, freq) {
941
+ const entry = rcvs.get(antType);
942
+ if (!entry) return null;
943
+ const f = freqOf2(entry, freq);
944
+ if (!f) return null;
945
+ return {
946
+ pco: [f.pcoN * mm, f.pcoE * mm, f.pcoU * mm],
947
+ pcvZen1Deg: entry.zen1,
948
+ pcvDzenDeg: entry.dzen,
949
+ pcvNoazi: f.pcvNoazi.map((v) => v * mm)
950
+ };
951
+ }
952
+ };
953
+ }
954
+ function rcvAntennaRangeM(off, losRcvToSat, latRad, lonRad, elRad, antDeltaEnu = [0, 0, 0]) {
955
+ const total = [
956
+ off.pco[0] + antDeltaEnu[0],
957
+ off.pco[1] + antDeltaEnu[1],
958
+ off.pco[2] + antDeltaEnu[2]
959
+ ];
960
+ const ecef = enuToEcef(total, latRad, lonRad);
961
+ let m = -dot(losRcvToSat, ecef);
962
+ const zenithDeg = 90 - elRad * 180 / Math.PI;
963
+ m += interpPcv(off, zenithDeg);
964
+ return m;
965
+ }
966
+ function antexFreqOfGroup(group) {
967
+ return `${group[0]}0${group[1]}`;
968
+ }
969
+
762
970
  // src/positioning/rtk.ts
763
971
  var L1_CODES = {
764
972
  G: ["1C"],
@@ -824,8 +1032,16 @@ function resolveEphemeris(src, prn, timeMs) {
824
1032
  return selectEphemeris(src, prn, timeMs);
825
1033
  return src.get(prn) ?? null;
826
1034
  }
827
- function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, troposphere) {
1035
+ var ZERO_DELTA = [0, 0, 0];
1036
+ function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, troposphere, antenna = null) {
828
1037
  const out = [];
1038
+ let baseLatRad = 0;
1039
+ let baseLonRad = 0;
1040
+ if (antenna?.base) {
1041
+ const [latDeg, lonDeg] = ecefToGeodetic(basePos[0], basePos[1], basePos[2]);
1042
+ baseLatRad = latDeg * Math.PI / 180;
1043
+ baseLonRad = lonDeg * Math.PI / 180;
1044
+ }
829
1045
  for (const [prn, mR] of rover) {
830
1046
  const sys = prn[0];
831
1047
  if (!"GERCJ".includes(sys)) continue;
@@ -850,14 +1066,43 @@ function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, tropo
850
1066
  satB.z - basePos[2]
851
1067
  ) / C_LIGHT;
852
1068
  const [bx, by, bz] = sagnac(satB, travelB);
853
- const rhoB = Math.hypot(bx - basePos[0], by - basePos[1], bz - basePos[2]);
1069
+ let rhoB = Math.hypot(bx - basePos[0], by - basePos[1], bz - basePos[2]);
854
1070
  const elB = ecefToAzEl(basePos[0], basePos[1], basePos[2], bx, by, bz).el;
855
1071
  if (elB < maskRad) continue;
1072
+ const group = sys + mR.code;
1073
+ let offR = null;
1074
+ let roverDelta = ZERO_DELTA;
1075
+ if (antenna) {
1076
+ const antexFreq = antexFreqOfGroup(group);
1077
+ if (antenna.base) {
1078
+ const offB = antenna.model.rcvOffset(antenna.base.type, antexFreq);
1079
+ if (offB) {
1080
+ const inv = 1 / rhoB;
1081
+ const losB = [
1082
+ (bx - basePos[0]) * inv,
1083
+ (by - basePos[1]) * inv,
1084
+ (bz - basePos[2]) * inv
1085
+ ];
1086
+ rhoB += rcvAntennaRangeM(
1087
+ offB,
1088
+ losB,
1089
+ baseLatRad,
1090
+ baseLonRad,
1091
+ elB,
1092
+ antenna.base.deltaEnu
1093
+ );
1094
+ }
1095
+ }
1096
+ if (antenna.rover) {
1097
+ offR = antenna.model.rcvOffset(antenna.rover.type, antexFreq);
1098
+ roverDelta = antenna.rover.deltaEnu ?? ZERO_DELTA;
1099
+ }
1100
+ }
856
1101
  const gloK = sys === "R" ? mR.gloChannel ?? mB.gloChannel ?? eph.freqNum ?? null : null;
857
1102
  const freq = carrierFreqHz(sys, mR.code, gloK);
858
1103
  out.push({
859
1104
  prn,
860
- group: sys + mR.code,
1105
+ group,
861
1106
  lambda: freq > 0 ? C_LIGHT / freq : 0,
862
1107
  satR,
863
1108
  rhoB,
@@ -868,7 +1113,9 @@ function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, tropo
868
1113
  cpR: mR.cp ?? null,
869
1114
  cpB: mB.cp ?? null,
870
1115
  lockR: mR.lockTimeMs,
871
- lockB: mB.lockTimeMs
1116
+ lockB: mB.lockTimeMs,
1117
+ offR,
1118
+ roverDelta
872
1119
  });
873
1120
  }
874
1121
  return out;
@@ -876,17 +1123,29 @@ function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, tropo
876
1123
  function roverTerms(g, x, y, z, troposphere) {
877
1124
  const travel = Math.hypot(g.satR.x - x, g.satR.y - y, g.satR.z - z) / C_LIGHT;
878
1125
  const [sx, sy, sz] = sagnac(g.satR, travel);
879
- const rho = Math.hypot(sx - x, sy - y, sz - z);
1126
+ let rho = Math.hypot(sx - x, sy - y, sz - z);
880
1127
  const u = [
881
1128
  (x - sx) / rho,
882
1129
  (y - sy) / rho,
883
1130
  (z - sz) / rho
884
1131
  ];
1132
+ const needEl = troposphere || g.offR != null;
1133
+ const elR = needEl ? ecefToAzEl(x, y, z, sx, sy, sz).el : 0;
885
1134
  let dTropo = 0;
886
1135
  if (troposphere) {
887
- const elR = ecefToAzEl(x, y, z, sx, sy, sz).el;
888
1136
  dTropo = tropoDelay(Math.max(elR, 0.05)) - g.tropoB;
889
1137
  }
1138
+ if (g.offR) {
1139
+ const [latDeg, lonDeg] = ecefToGeodetic(x, y, z);
1140
+ rho += rcvAntennaRangeM(
1141
+ g.offR,
1142
+ [-u[0], -u[1], -u[2]],
1143
+ latDeg * Math.PI / 180,
1144
+ lonDeg * Math.PI / 180,
1145
+ elR,
1146
+ g.roverDelta
1147
+ );
1148
+ }
890
1149
  return { rho, u, dTropo };
891
1150
  }
892
1151
  function sdVariance(sigmaM, elRad) {
@@ -1125,7 +1384,8 @@ var RtkFloatEngine = class {
1125
1384
  arElevationMaskDeg: opts.arElevationMaskDeg ?? 0,
1126
1385
  glonassAr: opts.glonassAr ?? false,
1127
1386
  partialFixing: opts.partialFixing ?? true,
1128
- minFixAmbiguities: opts.minFixAmbiguities ?? 4
1387
+ minFixAmbiguities: opts.minFixAmbiguities ?? 4,
1388
+ antenna: opts.antenna ?? null
1129
1389
  };
1130
1390
  }
1131
1391
  /** Clear all filter state (position, ambiguities, lock history). */
@@ -1248,7 +1508,8 @@ var RtkFloatEngine = class {
1248
1508
  this.ephemerides,
1249
1509
  timeMs,
1250
1510
  o.elevationMaskDeg * Math.PI / 180,
1251
- o.troposphere
1511
+ o.troposphere,
1512
+ o.antenna
1252
1513
  );
1253
1514
  const byPrn = new Map(geom.map((g) => [g.prn, g]));
1254
1515
  const dtS = this.lastMs !== null ? (timeMs - this.lastMs) / 1e3 : 0;
@@ -1871,6 +2132,10 @@ var SbasProcessor = class {
1871
2132
  tlat = 0;
1872
2133
  degr = null;
1873
2134
  mt27 = null;
2135
+ /** MT28 clock-ephemeris covariance per PRN: the upper-triangular Cholesky
2136
+ * factor E (10 elements [E11,E22,E33,E44,E12,E13,E14,E23,E24,E34]) and its
2137
+ * scale exponent. Keyed by PRN; cleared on a mask change. */
2138
+ mt28 = /* @__PURE__ */ new Map();
1874
2139
  ion = [];
1875
2140
  /** GEO PRN(s) whose messages have been ingested. */
1876
2141
  geoPrns = /* @__PURE__ */ new Set();
@@ -1901,6 +2166,8 @@ var SbasProcessor = class {
1901
2166
  return this.decodeMt10(msg) ? type : -1;
1902
2167
  case 27:
1903
2168
  return this.decodeMt27(msg) ? type : -1;
2169
+ case 28:
2170
+ return this.decodeMt28(msg) ? type : -1;
1904
2171
  case 18:
1905
2172
  return this.decodeIgpMask(msg) ? type : -1;
1906
2173
  case 24:
@@ -1927,6 +2194,7 @@ var SbasProcessor = class {
1927
2194
  return true;
1928
2195
  this.iodp = iodp;
1929
2196
  this.sats = prns.map((prn) => ({ prn }));
2197
+ this.mt28.clear();
1930
2198
  this.satIdx.clear();
1931
2199
  this.sats.forEach((s, k) => {
1932
2200
  if (s.prn) this.satIdx.set(s.prn, k);
@@ -1992,7 +2260,9 @@ var SbasProcessor = class {
1992
2260
  iiono: u(117, 9) || 1,
1993
2261
  cionoRamp: u(126, 10) * 5e-6,
1994
2262
  rssUdre: u(136, 1) === 1,
1995
- rssIono: u(137, 1) === 1
2263
+ rssIono: u(137, 1) === 1,
2264
+ ccovariance: u(138, 7) * 0.1
2265
+ // Table B-49: 0..12.7 m, 0.1 m
1996
2266
  };
1997
2267
  return true;
1998
2268
  }
@@ -2044,6 +2314,51 @@ var SbasProcessor = class {
2044
2314
  }
2045
2315
  return best ?? DELTA_UDRE[s.udreOutside] ?? 1;
2046
2316
  }
2317
+ /** MT28 clock-ephemeris covariance (ICAO Annex 10 Vol I §3.5.4.7 / Table B-51):
2318
+ * two satellites per message, each an upper-triangular Cholesky factor E and a
2319
+ * scale exponent that together define the relative clock-ephemeris covariance
2320
+ * used for a geometry-dependent δUDRE (§3.5.5.6.2.5). Slots are indexed into
2321
+ * the current MT1 mask, so a mismatched IODP is dropped. */
2322
+ decodeMt28(msg) {
2323
+ if (this.iodp !== getBitU(msg, 14, 2)) return false;
2324
+ for (const base of [16, 121]) {
2325
+ const slot = getBitU(msg, base, 6);
2326
+ const prn = this.sats[slot]?.prn;
2327
+ const scaleExp = getBitU(msg, base + 6, 3);
2328
+ const e = [
2329
+ getBitU(msg, base + 9, 9),
2330
+ getBitU(msg, base + 18, 9),
2331
+ getBitU(msg, base + 27, 9),
2332
+ getBitU(msg, base + 36, 9),
2333
+ getBitS(msg, base + 45, 10),
2334
+ getBitS(msg, base + 55, 10),
2335
+ getBitS(msg, base + 65, 10),
2336
+ getBitS(msg, base + 75, 10),
2337
+ getBitS(msg, base + 85, 10),
2338
+ getBitS(msg, base + 95, 10)
2339
+ ];
2340
+ if (prn && e.some((v) => v !== 0)) this.mt28.set(prn, { scaleExp, e });
2341
+ }
2342
+ return true;
2343
+ }
2344
+ /** Geometry-dependent δUDRE multiplier from MT28 (§3.5.5.6.2.5):
2345
+ * δUDRE = √(Iᵀ·C·I) + ε_c, with C = Rᵀ·R, R = E·2^(scaleExp−5),
2346
+ * I = [î_user→sat, 1], ε_c = C_covariance·2^(scaleExp−5). Since C = RᵀR,
2347
+ * Iᵀ·C·I = ‖R·I‖², so we form R·I directly. Null when no MT28 for `prn`. */
2348
+ mt28DeltaUdre(prn, los) {
2349
+ const m = this.mt28.get(prn);
2350
+ if (!m) return null;
2351
+ const sf = 2 ** (m.scaleExp - 5);
2352
+ const [e11, e22, e33, e44, e12, e13, e14, e23, e24, e34] = m.e;
2353
+ const [ix, iy, iz] = los;
2354
+ const v0 = (e11 * ix + e12 * iy + e13 * iz + e14) * sf;
2355
+ const v1 = (e22 * iy + e23 * iz + e24) * sf;
2356
+ const v2 = (e33 * iz + e34) * sf;
2357
+ const v3 = e44 * sf;
2358
+ const iCi = v0 * v0 + v1 * v1 + v2 * v2 + v3 * v3;
2359
+ const ec = (this.degr?.ccovariance ?? 0) * sf;
2360
+ return Math.sqrt(iCi) + ec;
2361
+ }
2047
2362
  decodeMixed(msg, week, tow) {
2048
2363
  if (this.iodp !== getBitU(msg, 110, 2)) return false;
2049
2364
  const blk = getBitU(msg, 112, 2);
@@ -2248,7 +2563,7 @@ var SbasProcessor = class {
2248
2563
  * no valid (unexpired) long-term + fast correction is available. Add `dPos`
2249
2564
  * to the satellite ECEF position and `dClkS` to its clock bias.
2250
2565
  */
2251
- satCorrection(prn, week, tow, userLatRad, userLonRad) {
2566
+ satCorrection(prn, week, tow, userLatRad, userLonRad, losUnitEcef) {
2252
2567
  const idx = this.satIdx.get(prn);
2253
2568
  if (idx === void 0) return null;
2254
2569
  const s = this.sats[idx];
@@ -2270,7 +2585,8 @@ var SbasProcessor = class {
2270
2585
  const tf = now - s.fcorr.t0s + this.tlat;
2271
2586
  if (Math.abs(tf) > MAXSBSAGEF || s.fcorr.udre >= 15) return null;
2272
2587
  const prc = s.fcorr.prc;
2273
- const dudre = userLatRad != null && userLonRad != null ? this.deltaUdre(
2588
+ const mt28 = losUnitEcef ? this.mt28DeltaUdre(prn, losUnitEcef) : null;
2589
+ const dudre = mt28 != null ? mt28 : userLatRad != null && userLonRad != null ? this.deltaUdre(
2274
2590
  userLatRad * 180 / Math.PI,
2275
2591
  userLonRad * 180 / Math.PI
2276
2592
  ) : 1;
@@ -2399,14 +2715,14 @@ var SbasProcessor = class {
2399
2715
  };
2400
2716
 
2401
2717
  // src/positioning/ssr-apply.ts
2402
- var cross = (a, b) => [
2718
+ var cross2 = (a, b) => [
2403
2719
  a[1] * b[2] - a[2] * b[1],
2404
2720
  a[2] * b[0] - a[0] * b[2],
2405
2721
  a[0] * b[1] - a[1] * b[0]
2406
2722
  ];
2407
- var norm = (a) => Math.hypot(a[0], a[1], a[2]);
2723
+ var norm2 = (a) => Math.hypot(a[0], a[1], a[2]);
2408
2724
  var unit = (a) => {
2409
- const n = norm(a);
2725
+ const n = norm2(a);
2410
2726
  return n > 0 ? [a[0] / n, a[1] / n, a[2] / n] : [0, 0, 0];
2411
2727
  };
2412
2728
  function applyOrbitClock(eph, corr, timeMs) {
@@ -2420,8 +2736,8 @@ function applyOrbitClock(eph, corr, timeMs) {
2420
2736
  const p = [s.x, s.y, s.z];
2421
2737
  const v = [s.vx, s.vy, s.vz];
2422
2738
  const er = unit(p);
2423
- const en = unit(cross(p, v));
2424
- const ea = cross(en, er);
2739
+ const en = unit(cross2(p, v));
2740
+ const ea = cross2(en, er);
2425
2741
  const dr = corr.radial + (corr.dotRadial ?? 0) * age;
2426
2742
  const da = corr.along + (corr.dotAlong ?? 0) * age;
2427
2743
  const dc = corr.cross + (corr.dotCross ?? 0) * age;
@@ -2703,170 +3019,6 @@ function solidEarthTide(rcv, sun, moon) {
2703
3019
  return disp;
2704
3020
  }
2705
3021
 
2706
- // src/positioning/ppp-antenna.ts
2707
- function parseAntexTime(s) {
2708
- const p = s.trim().split(/\s+/).map(Number);
2709
- if (p.length < 3 || p.some((v) => Number.isNaN(v))) return null;
2710
- return Date.UTC(
2711
- p[0],
2712
- (p[1] || 1) - 1,
2713
- p[2] || 1,
2714
- p[3] ?? 0,
2715
- p[4] ?? 0,
2716
- Math.floor(p[5] ?? 0)
2717
- );
2718
- }
2719
- function freqOf(entry, code) {
2720
- return entry.frequencies.find((f) => f.frequency === code);
2721
- }
2722
- function combineIf(fA, fB, f1, f2) {
2723
- const g = f1 * f1 / (f1 * f1 - f2 * f2);
2724
- const mm = 1e-3;
2725
- const pco = [
2726
- (g * fA.pcoN - (g - 1) * fB.pcoN) * mm,
2727
- (g * fA.pcoE - (g - 1) * fB.pcoE) * mm,
2728
- (g * fA.pcoU - (g - 1) * fB.pcoU) * mm
2729
- ];
2730
- const nz = Math.min(fA.pcvNoazi.length, fB.pcvNoazi.length);
2731
- const pcvNoazi = new Array(nz);
2732
- for (let i = 0; i < nz; i++) {
2733
- pcvNoazi[i] = (g * fA.pcvNoazi[i] - (g - 1) * fB.pcvNoazi[i]) * mm;
2734
- }
2735
- return { pco, pcvZen1Deg: 0, pcvDzenDeg: 0, pcvNoazi };
2736
- }
2737
- function buildPppAntenna(antex) {
2738
- const sats = /* @__PURE__ */ new Map();
2739
- const rcvs = /* @__PURE__ */ new Map();
2740
- for (const a of antex.antennas) {
2741
- if (a.isSatellite) {
2742
- const list = sats.get(a.serialNo) ?? [];
2743
- list.push(a);
2744
- sats.set(a.serialNo, list);
2745
- } else if (!rcvs.has(a.type)) {
2746
- rcvs.set(a.type, a);
2747
- }
2748
- }
2749
- const withZen = (entry, off) => ({
2750
- ...off,
2751
- pcvZen1Deg: entry.zen1,
2752
- pcvDzenDeg: entry.dzen
2753
- });
2754
- return {
2755
- satOffset(prn, dateMs, band1, band2, f1, f2) {
2756
- const list = sats.get(prn);
2757
- if (!list) return null;
2758
- const entry = list.find((e) => {
2759
- const from = parseAntexTime(e.validFrom);
2760
- const until = parseAntexTime(e.validUntil);
2761
- return (from == null || dateMs >= from) && (until == null || dateMs <= until);
2762
- });
2763
- if (!entry) return null;
2764
- const a = freqOf(entry, band1);
2765
- const b = freqOf(entry, band2);
2766
- if (!a || !b) return null;
2767
- return withZen(entry, combineIf(a, b, f1, f2));
2768
- },
2769
- rcvOffset(antType, band1, band2, f1, f2) {
2770
- const entry = rcvs.get(antType);
2771
- if (!entry) return null;
2772
- const a = freqOf(entry, band1);
2773
- const b = freqOf(entry, band2);
2774
- if (!a || !b) return null;
2775
- return withZen(entry, combineIf(a, b, f1, f2));
2776
- }
2777
- };
2778
- }
2779
- function cross2(a, b) {
2780
- return [
2781
- a[1] * b[2] - a[2] * b[1],
2782
- a[2] * b[0] - a[0] * b[2],
2783
- a[0] * b[1] - a[1] * b[0]
2784
- ];
2785
- }
2786
- function norm2(a) {
2787
- const n = Math.hypot(a[0], a[1], a[2]) || 1;
2788
- return [a[0] / n, a[1] / n, a[2] / n];
2789
- }
2790
- function dot(a, b) {
2791
- return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2792
- }
2793
- function satBodyFrame(satEcef, sunEcef2) {
2794
- const z = norm2([-satEcef[0], -satEcef[1], -satEcef[2]]);
2795
- const eSun = norm2([
2796
- sunEcef2[0] - satEcef[0],
2797
- sunEcef2[1] - satEcef[1],
2798
- sunEcef2[2] - satEcef[2]
2799
- ]);
2800
- const y = norm2(cross2(z, eSun));
2801
- const x = norm2(cross2(y, z));
2802
- return { x, y, z };
2803
- }
2804
- function satPcoToEcef(pcoBody, frame) {
2805
- return [
2806
- pcoBody[0] * frame.x[0] + pcoBody[1] * frame.y[0] + pcoBody[2] * frame.z[0],
2807
- pcoBody[0] * frame.x[1] + pcoBody[1] * frame.y[1] + pcoBody[2] * frame.z[1],
2808
- pcoBody[0] * frame.x[2] + pcoBody[1] * frame.y[2] + pcoBody[2] * frame.z[2]
2809
- ];
2810
- }
2811
- function enuToEcef(enu, latRad, lonRad) {
2812
- const sl = Math.sin(latRad);
2813
- const cl = Math.cos(latRad);
2814
- const so = Math.sin(lonRad);
2815
- const co = Math.cos(lonRad);
2816
- const [e, n, u] = enu;
2817
- return [
2818
- -so * e - sl * co * n + cl * co * u,
2819
- co * e - sl * so * n + cl * so * u,
2820
- cl * n + sl * u
2821
- ];
2822
- }
2823
- function interpPcv(off, angleDeg) {
2824
- const { pcvZen1Deg, pcvDzenDeg, pcvNoazi } = off;
2825
- if (pcvDzenDeg <= 0 || pcvNoazi.length < 2) return 0;
2826
- const x = (angleDeg - pcvZen1Deg) / pcvDzenDeg;
2827
- if (x <= 0) return pcvNoazi[0];
2828
- if (x >= pcvNoazi.length - 1) return pcvNoazi[pcvNoazi.length - 1];
2829
- const i = Math.floor(x);
2830
- const t = x - i;
2831
- return pcvNoazi[i] * (1 - t) + pcvNoazi[i + 1] * t;
2832
- }
2833
- function phaseWindup(prn, losRcvToSat, satFrame, latRad, lonRad, state) {
2834
- const k = [
2835
- -losRcvToSat[0],
2836
- -losRcvToSat[1],
2837
- -losRcvToSat[2]
2838
- ];
2839
- const kx = satFrame.x;
2840
- const ky = satFrame.y;
2841
- const kdotx = dot(k, kx);
2842
- const kcrossY = cross2(k, ky);
2843
- const dS = [
2844
- kx[0] - k[0] * kdotx - kcrossY[0],
2845
- kx[1] - k[1] * kdotx - kcrossY[1],
2846
- kx[2] - k[2] * kdotx - kcrossY[2]
2847
- ];
2848
- const north = enuToEcef([0, 1, 0], latRad, lonRad);
2849
- const east = enuToEcef([1, 0, 0], latRad, lonRad);
2850
- const kdotn = dot(k, north);
2851
- const kcrossE = cross2(k, east);
2852
- const dR = [
2853
- north[0] - k[0] * kdotn + kcrossE[0],
2854
- north[1] - k[1] * kdotn + kcrossE[1],
2855
- north[2] - k[2] * kdotn + kcrossE[2]
2856
- ];
2857
- const nS = Math.hypot(dS[0], dS[1], dS[2]) || 1;
2858
- const nR = Math.hypot(dR[0], dR[1], dR[2]) || 1;
2859
- let cosPhi = dot(dS, dR) / (nS * nR);
2860
- cosPhi = Math.max(-1, Math.min(1, cosPhi));
2861
- const zeta = cross2(dS, dR);
2862
- const sign = dot(k, zeta) >= 0 ? 1 : -1;
2863
- const dphi = sign * Math.acos(cosPhi) / (2 * Math.PI);
2864
- const prev = state.get(prn);
2865
- const wu = prev == null ? dphi : dphi + Math.round(prev - dphi);
2866
- state.set(prn, wu);
2867
- return wu;
2868
- }
2869
-
2870
3022
  // src/positioning/ppp-corrections.ts
2871
3023
  function applyCorrections(corrections, geom, ctx) {
2872
3024
  if (corrections.evaluate) return corrections.evaluate(geom, ctx);
@@ -4226,7 +4378,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
4226
4378
  used.push(prn);
4227
4379
  azels.push(azel);
4228
4380
  if (sbas) {
4229
- const sc = sbas.satCorrection(prn, gpsWeek, gpsTow, rxLat, rxLon);
4381
+ const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
4382
+ const dr = Math.hypot(dx, dy, dz) || 1;
4383
+ const los = [dx / dr, dy / dr, dz / dr];
4384
+ const sc = sbas.satCorrection(prn, gpsWeek, gpsTow, rxLat, rxLon, los);
4230
4385
  const si = sbas.ionoDelay(
4231
4386
  gpsWeek,
4232
4387
  gpsTow,
@@ -4265,8 +4420,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
4265
4420
  RtkFloatEngine,
4266
4421
  SbasProcessor,
4267
4422
  Sp3EphemerisSource,
4423
+ antexFreqOfGroup,
4268
4424
  applyOrbitClock,
4269
4425
  buildPppAntenna,
4426
+ buildRtkAntenna,
4270
4427
  createPppCorrections,
4271
4428
  estimateNarrowlaneFcb,
4272
4429
  estimateNetworkFcbs,
@@ -4285,6 +4442,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
4285
4442
  niellMapping,
4286
4443
  nlWavelength,
4287
4444
  postProcessRtk,
4445
+ rcvAntennaRangeM,
4288
4446
  resolvePppAmbiguities,
4289
4447
  satClockCorrection,
4290
4448
  sbasIonoDeg,