gnss-js 1.2.0 → 1.3.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.
package/dist/index.cjs CHANGED
@@ -75,6 +75,7 @@ __export(src_exports, {
75
75
  MILLISECONDS_IN_WEEK: () => MILLISECONDS_IN_WEEK,
76
76
  MILLISECONDS_TT_TAI: () => MILLISECONDS_TT_TAI,
77
77
  MultipathAccumulator: () => MultipathAccumulator,
78
+ REFERENCE_FRAMES: () => REFERENCE_FRAMES,
78
79
  RINEX_CODES: () => RINEX_CODES,
79
80
  RTCM3_MESSAGE_NAMES: () => RTCM3_MESSAGE_NAMES,
80
81
  Rtcm3Decoder: () => Rtcm3Decoder,
@@ -101,6 +102,7 @@ __export(src_exports, {
101
102
  WGS84_SEMI_MINOR_AXIS: () => WGS84_SEMI_MINOR_AXIS,
102
103
  WarningAccumulator: () => WarningAccumulator,
103
104
  analyzeQuality: () => analyzeQuality,
105
+ applyHelmert: () => applyHelmert,
104
106
  buildGloChannelMap: () => buildGloChannelMap,
105
107
  buildObsIndices: () => buildObsIndices,
106
108
  clampUnit: () => clampUnit,
@@ -110,17 +112,20 @@ __export(src_exports, {
110
112
  computePsdDb: () => computePsdDb,
111
113
  computeSatPosition: () => computeSatPosition,
112
114
  computeStats: () => computeStats2,
115
+ computeVisibility: () => computeVisibility,
113
116
  connectToMountpoint: () => connectToMountpoint,
114
117
  createStationMeta: () => createStationMeta,
115
118
  createStreamStats: () => createStreamStats,
116
119
  crxDecompress: () => crxDecompress,
117
120
  crxRepair: () => crxRepair,
121
+ dateToEpoch: () => dateToEpoch,
118
122
  decodeEphemeris: () => decodeEphemeris,
119
123
  decodeMsmFull: () => decodeMsmFull,
120
124
  deg2dms: () => deg2dms,
121
125
  deg2rad: () => deg2rad,
122
126
  ecefToAzEl: () => ecefToAzEl,
123
127
  ecefToGeodetic: () => ecefToGeodetic,
128
+ ephInfoToEphemeris: () => ephInfoToEphemeris,
124
129
  euclidean3D: () => euclidean3D,
125
130
  fetchSourcetable: () => fetchSourcetable,
126
131
  fmtF: () => fmtF,
@@ -188,6 +193,7 @@ __export(src_exports, {
188
193
  greatCircleMidpoint: () => greatCircleMidpoint,
189
194
  hdrLine: () => hdrLine,
190
195
  horizonDistance: () => horizonDistance,
196
+ ionoFree: () => ionoFree,
191
197
  keplerPosition: () => keplerPosition,
192
198
  msmEpochToDate: () => msmEpochToDate,
193
199
  navTimesFromEph: () => navTimesFromEph,
@@ -210,11 +216,14 @@ __export(src_exports, {
210
216
  resetGloFreqCache: () => resetGloFreqCache,
211
217
  rhumbLine: () => rhumbLine,
212
218
  rtcm3Constellation: () => rtcm3Constellation,
219
+ satClockCorrection: () => satClockCorrection,
213
220
  selectEphemeris: () => selectEphemeris,
214
221
  setGloFreqNumber: () => setGloFreqNumber,
215
222
  setRtcm3DebugHandler: () => setRtcm3DebugHandler,
223
+ solveSpp: () => solveSpp,
216
224
  systemCmp: () => systemCmp,
217
225
  systemName: () => systemName,
226
+ transformFrame: () => transformFrame,
218
227
  updateStationMeta: () => updateStationMeta,
219
228
  updateStreamStats: () => updateStreamStats,
220
229
  verifyChecksum: () => verifyChecksum,
@@ -4128,17 +4137,17 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
4128
4137
  function computeDop(satAzEls) {
4129
4138
  const n = satAzEls.length;
4130
4139
  if (n < 4) return null;
4131
- const H = [];
4140
+ const H2 = [];
4132
4141
  for (const { az, el } of satAzEls) {
4133
4142
  const cosEl = Math.cos(el);
4134
- H.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
4143
+ H2.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
4135
4144
  }
4136
4145
  const HtH = Array.from({ length: 4 }, () => new Array(4).fill(0));
4137
4146
  for (let i = 0; i < 4; i++) {
4138
4147
  for (let j = 0; j < 4; j++) {
4139
4148
  let sum = 0;
4140
4149
  for (let k = 0; k < n; k++) {
4141
- sum += H[k][i] * H[k][j];
4150
+ sum += H2[k][i] * H2[k][j];
4142
4151
  }
4143
4152
  HtH[i][j] = sum;
4144
4153
  }
@@ -4191,7 +4200,8 @@ function selectEphemeris(ephemerides, prn, timeMs) {
4191
4200
  if (eph.prn !== prn) continue;
4192
4201
  const ephTime = eph.tocDate.getTime();
4193
4202
  const dt = Math.abs(timeMs - ephTime);
4194
- if (dt > 4 * 3600 * 1e3) continue;
4203
+ const maxAge = eph.system === "R" || eph.system === "S" ? 18e5 : 4 * 36e5;
4204
+ if (dt > maxAge) continue;
4195
4205
  if (dt < bestDt) {
4196
4206
  bestDt = dt;
4197
4207
  best = eph;
@@ -4299,13 +4309,13 @@ function ephInfoToEphemeris(info) {
4299
4309
  return null;
4300
4310
  if (info.vx === void 0 || info.vy === void 0 || info.vz === void 0)
4301
4311
  return null;
4302
- const now = new Date(info.lastReceived);
4312
+ const moscow = new Date(info.lastReceived + 3 * 3600 * 1e3);
4303
4313
  const utcMidnight = Date.UTC(
4304
- now.getUTCFullYear(),
4305
- now.getUTCMonth(),
4306
- now.getUTCDate()
4314
+ moscow.getUTCFullYear(),
4315
+ moscow.getUTCMonth(),
4316
+ moscow.getUTCDate()
4307
4317
  );
4308
- const tbSec = (info.tb ?? 0) * 900;
4318
+ const tbSec = (info.tb ?? 0) * 60;
4309
4319
  const moscowOffset = 3 * 3600;
4310
4320
  const tocMs = utcMidnight + (tbSec - moscowOffset) * 1e3;
4311
4321
  return {
@@ -4408,6 +4418,474 @@ function computeLiveSkyPositions(ephemerides, rxPos, cn0Map) {
4408
4418
  }
4409
4419
  return result;
4410
4420
  }
4421
+ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, elevationMaskDeg = 10) {
4422
+ const stepMs = stepSec * 1e3;
4423
+ const times = [];
4424
+ for (let t = startMs; t <= endMs; t += stepMs) times.push(t);
4425
+ const maskRad = elevationMaskDeg * Math.PI / 180;
4426
+ const all = computeAllPositions(ephemerides, times, rxPos);
4427
+ const elevation = {};
4428
+ const visibleCount = new Array(times.length).fill(0);
4429
+ const pdop = new Array(times.length).fill(null);
4430
+ const visiblePerEpoch = times.map(() => []);
4431
+ for (const prn of all.prns) {
4432
+ const series = all.positions[prn];
4433
+ elevation[prn] = series.map((p) => p ? p.el : null);
4434
+ for (let i = 0; i < series.length; i++) {
4435
+ const p = series[i];
4436
+ if (p && p.el >= maskRad) {
4437
+ visibleCount[i]++;
4438
+ visiblePerEpoch[i].push({ az: p.az, el: p.el });
4439
+ }
4440
+ }
4441
+ }
4442
+ for (let i = 0; i < times.length; i++) {
4443
+ const dop = computeDop(visiblePerEpoch[i]);
4444
+ pdop[i] = dop ? dop.pdop : null;
4445
+ }
4446
+ const passes = [];
4447
+ for (const prn of all.prns) {
4448
+ const els = elevation[prn];
4449
+ let start = -1;
4450
+ let peakEl = -Infinity;
4451
+ let peakTime = 0;
4452
+ for (let i = 0; i <= els.length; i++) {
4453
+ const el = i < els.length ? els[i] : null;
4454
+ const above = el !== null && el !== void 0 && el >= maskRad;
4455
+ if (above) {
4456
+ if (start === -1) {
4457
+ start = i;
4458
+ peakEl = -Infinity;
4459
+ }
4460
+ if (el > peakEl) {
4461
+ peakEl = el;
4462
+ peakTime = times[i];
4463
+ }
4464
+ } else if (start !== -1) {
4465
+ passes.push({
4466
+ prn,
4467
+ system: prn[0],
4468
+ rise: times[start],
4469
+ set: times[i - 1],
4470
+ peakTime,
4471
+ peakEl
4472
+ });
4473
+ start = -1;
4474
+ }
4475
+ }
4476
+ }
4477
+ passes.sort((a, b) => a.rise - b.rise);
4478
+ return { times, elevation, visibleCount, pdop, passes };
4479
+ }
4480
+
4481
+ // src/frames/index.ts
4482
+ var H = (t, d, r, tDot, dDot, rDot, epoch) => ({ t, d, r, tDot, dDot, rDot, epoch });
4483
+ var Z = [0, 0, 0];
4484
+ var FROM_ITRF2020 = {
4485
+ ITRF2014: H([-1.4, -0.9, 1.4], -0.42, Z, [0, -0.1, 0.2], 0, Z, 2015),
4486
+ ITRF2008: H([0.2, 1, 3.3], -0.29, Z, [0, -0.1, 0.1], 0.03, Z, 2015),
4487
+ ITRF2005: H([2.7, 0.1, -1.4], 0.65, Z, [0.3, -0.1, 0.1], 0.03, Z, 2015),
4488
+ ITRF2000: H([-0.2, 0.8, -34.2], 2.25, Z, [0.1, 0, -1.7], 0.11, Z, 2015),
4489
+ ITRF97: H(
4490
+ [6.5, -3.9, -77.9],
4491
+ 3.98,
4492
+ [0, 0, 0.36],
4493
+ [0.1, -0.6, -3.1],
4494
+ 0.12,
4495
+ [0, 0, 0.02],
4496
+ 2015
4497
+ ),
4498
+ ITRF96: H(
4499
+ [6.5, -3.9, -77.9],
4500
+ 3.98,
4501
+ [0, 0, 0.36],
4502
+ [0.1, -0.6, -3.1],
4503
+ 0.12,
4504
+ [0, 0, 0.02],
4505
+ 2015
4506
+ ),
4507
+ ITRF94: H(
4508
+ [6.5, -3.9, -77.9],
4509
+ 3.98,
4510
+ [0, 0, 0.36],
4511
+ [0.1, -0.6, -3.1],
4512
+ 0.12,
4513
+ [0, 0, 0.02],
4514
+ 2015
4515
+ ),
4516
+ ITRF93: H(
4517
+ [-65.8, 1.9, -71.3],
4518
+ 4.47,
4519
+ [-3.36, -4.33, 0.75],
4520
+ [-2.8, -0.2, -2.3],
4521
+ 0.12,
4522
+ [-0.11, -0.19, 0.07],
4523
+ 2015
4524
+ ),
4525
+ ITRF92: H(
4526
+ [14.5, -1.9, -85.9],
4527
+ 3.27,
4528
+ [0, 0, 0.36],
4529
+ [0.1, -0.6, -3.1],
4530
+ 0.12,
4531
+ [0, 0, 0.02],
4532
+ 2015
4533
+ ),
4534
+ ITRF91: H(
4535
+ [26.5, 12.1, -91.9],
4536
+ 4.67,
4537
+ [0, 0, 0.36],
4538
+ [0.1, -0.6, -3.1],
4539
+ 0.12,
4540
+ [0, 0, 0.02],
4541
+ 2015
4542
+ ),
4543
+ ITRF90: H(
4544
+ [24.5, 8.1, -107.9],
4545
+ 4.97,
4546
+ [0, 0, 0.36],
4547
+ [0.1, -0.6, -3.1],
4548
+ 0.12,
4549
+ [0, 0, 0.02],
4550
+ 2015
4551
+ ),
4552
+ ITRF89: H(
4553
+ [29.5, 32.1, -145.9],
4554
+ 8.37,
4555
+ [0, 0, 0.36],
4556
+ [0.1, -0.6, -3.1],
4557
+ 0.12,
4558
+ [0, 0, 0.02],
4559
+ 2015
4560
+ ),
4561
+ ITRF88: H(
4562
+ [24.5, -3.9, -169.9],
4563
+ 11.47,
4564
+ [0.1, 0, 0.36],
4565
+ [0.1, -0.6, -3.1],
4566
+ 0.12,
4567
+ [0, 0, 0.02],
4568
+ 2015
4569
+ ),
4570
+ /** ITRF2020 → ETRF2020, EPSG:10572 (epoch-1989 variant) */
4571
+ ETRF2020: H(Z, 0, Z, Z, 0, [0.086, 0.519, -0.753], 1989)
4572
+ };
4573
+ var ITRF2014_TO_ETRF2014 = H(Z, 0, Z, Z, 0, [0.085, 0.531, -0.77], 1989);
4574
+ var ITRF2014_TO_ETRF2000 = H(
4575
+ [54.7, 52.2, -74.1],
4576
+ 2.12,
4577
+ [1.701, 10.29, -16.632],
4578
+ [0.1, 0.1, -1.9],
4579
+ 0.11,
4580
+ [0.081, 0.49, -0.792],
4581
+ 2010
4582
+ );
4583
+ var ITRF2014_TO_NAD83_2011 = H(
4584
+ [1005.3, -1909.21, -541.57],
4585
+ 0.36891,
4586
+ [-26.78138, 0.42027, -10.93206],
4587
+ [0.79, -0.6, -1.44],
4588
+ -0.07201,
4589
+ [-0.06667, 0.75744, 0.05133],
4590
+ 2010
4591
+ );
4592
+ var MAS_TO_RAD = Math.PI / (180 * 3600 * 1e3);
4593
+ function applyHelmert(xyz, p, epoch, inverse = false) {
4594
+ const dt = epoch - p.epoch;
4595
+ const sign = inverse ? -1 : 1;
4596
+ const tx = sign * (p.t[0] + p.tDot[0] * dt) / 1e3;
4597
+ const ty = sign * (p.t[1] + p.tDot[1] * dt) / 1e3;
4598
+ const tz = sign * (p.t[2] + p.tDot[2] * dt) / 1e3;
4599
+ const d = sign * (p.d + p.dDot * dt) * 1e-9;
4600
+ const rx = sign * (p.r[0] + p.rDot[0] * dt) * MAS_TO_RAD;
4601
+ const ry = sign * (p.r[1] + p.rDot[1] * dt) * MAS_TO_RAD;
4602
+ const rz = sign * (p.r[2] + p.rDot[2] * dt) * MAS_TO_RAD;
4603
+ const [x, y, z] = xyz;
4604
+ return [
4605
+ x + tx + d * x - rz * y + ry * z,
4606
+ y + ty + rz * x + d * y - rx * z,
4607
+ z + tz - ry * x + rx * y + d * z
4608
+ ];
4609
+ }
4610
+ var EDGES = [
4611
+ ...Object.entries(FROM_ITRF2020).map(([to, p]) => ({
4612
+ from: "ITRF2020",
4613
+ to,
4614
+ p
4615
+ })),
4616
+ { from: "ITRF2014", to: "ETRF2014", p: ITRF2014_TO_ETRF2014 },
4617
+ { from: "ITRF2014", to: "ETRF2000", p: ITRF2014_TO_ETRF2000 },
4618
+ { from: "ITRF2014", to: "NAD83(2011)", p: ITRF2014_TO_NAD83_2011 }
4619
+ ];
4620
+ var REFERENCE_FRAMES = [
4621
+ "WGS84",
4622
+ "ITRF2020",
4623
+ "ITRF2014",
4624
+ "ITRF2008",
4625
+ "ITRF2005",
4626
+ "ITRF2000",
4627
+ "ITRF97",
4628
+ "ITRF96",
4629
+ "ITRF94",
4630
+ "ITRF93",
4631
+ "ITRF92",
4632
+ "ITRF91",
4633
+ "ITRF90",
4634
+ "ITRF89",
4635
+ "ITRF88",
4636
+ "ETRF2020",
4637
+ "ETRF2014",
4638
+ "ETRF2000",
4639
+ "NAD83(2011)"
4640
+ ];
4641
+ function resolveAlias(f) {
4642
+ return f === "WGS84" ? "ITRF2020" : f;
4643
+ }
4644
+ function findPath(from, to) {
4645
+ if (from === to) return [];
4646
+ const queue = [from];
4647
+ const cameFrom = /* @__PURE__ */ new Map();
4648
+ const seen = /* @__PURE__ */ new Set([from]);
4649
+ while (queue.length > 0) {
4650
+ const cur = queue.shift();
4651
+ for (const edge of EDGES) {
4652
+ let next = null;
4653
+ let inverse = false;
4654
+ if (edge.from === cur) {
4655
+ next = edge.to;
4656
+ } else if (edge.to === cur) {
4657
+ next = edge.from;
4658
+ inverse = true;
4659
+ }
4660
+ if (next === null || seen.has(next)) continue;
4661
+ seen.add(next);
4662
+ cameFrom.set(next, { prev: cur, edge, inverse });
4663
+ if (next === to) {
4664
+ const path = [];
4665
+ let node = to;
4666
+ while (node !== from) {
4667
+ const step = cameFrom.get(node);
4668
+ path.unshift({ edge: step.edge, inverse: step.inverse });
4669
+ node = step.prev;
4670
+ }
4671
+ return path;
4672
+ }
4673
+ queue.push(next);
4674
+ }
4675
+ }
4676
+ return null;
4677
+ }
4678
+ function transformFrame(xyz, from, to, epoch) {
4679
+ const src = resolveAlias(from);
4680
+ const dst = resolveAlias(to);
4681
+ const path = findPath(src, dst);
4682
+ if (!path) {
4683
+ throw new Error(`No transformation path from ${from} to ${to}`);
4684
+ }
4685
+ let out = [xyz[0], xyz[1], xyz[2]];
4686
+ for (const { edge, inverse } of path) {
4687
+ out = applyHelmert(out, edge.p, epoch, inverse);
4688
+ }
4689
+ return out;
4690
+ }
4691
+ function dateToEpoch(date) {
4692
+ const year = date.getUTCFullYear();
4693
+ const start = Date.UTC(year, 0, 1);
4694
+ const end = Date.UTC(year + 1, 0, 1);
4695
+ return year + (date.getTime() - start) / (end - start);
4696
+ }
4697
+
4698
+ // src/positioning/index.ts
4699
+ var GM_GPS2 = 3986005e8;
4700
+ var F_REL = -4442807633e-19;
4701
+ function satClockCorrection(eph, tMs) {
4702
+ if (eph.system === "R" || eph.system === "S") {
4703
+ const dt2 = (tMs - eph.tocDate.getTime()) / 1e3;
4704
+ return eph.tauN + eph.gammaN * dt2;
4705
+ }
4706
+ const k = eph;
4707
+ const dt = (tMs - k.tocDate.getTime()) / 1e3;
4708
+ const poly = k.af0 + k.af1 * dt + k.af2 * dt * dt;
4709
+ const a = k.sqrtA * k.sqrtA;
4710
+ const n = Math.sqrt(GM_GPS2 / (a * a * a)) + k.deltaN;
4711
+ const tk = dt;
4712
+ const Mk = k.m0 + n * tk;
4713
+ let Ek = Mk;
4714
+ for (let i = 0; i < 8; i++) {
4715
+ Ek = Mk + k.e * Math.sin(Ek);
4716
+ }
4717
+ const rel = F_REL * k.e * k.sqrtA * Math.sin(Ek);
4718
+ return poly + rel;
4719
+ }
4720
+ function ionoFree(p1, p2, f1, f2) {
4721
+ const g = f1 * f1 / (f1 * f1 - f2 * f2);
4722
+ return g * p1 - (g - 1) * p2;
4723
+ }
4724
+ function tropoDelay(elevationRad) {
4725
+ const sinEl = Math.sin(elevationRad);
4726
+ return 2.47 / (sinEl + 0.0121);
4727
+ }
4728
+ function sagnac(pos, travelTimeS) {
4729
+ const OMEGA_E2 = 72921151467e-15;
4730
+ const theta = OMEGA_E2 * travelTimeS;
4731
+ const c = Math.cos(theta);
4732
+ const s = Math.sin(theta);
4733
+ return [pos.x * c + pos.y * s, -pos.x * s + pos.y * c, pos.z];
4734
+ }
4735
+ function solveLinear(A, b) {
4736
+ const n = b.length;
4737
+ const M = A.map((row, i) => [...row, b[i]]);
4738
+ for (let col = 0; col < n; col++) {
4739
+ let pivot = col;
4740
+ for (let r = col + 1; r < n; r++) {
4741
+ if (Math.abs(M[r][col]) > Math.abs(M[pivot][col])) pivot = r;
4742
+ }
4743
+ if (Math.abs(M[pivot][col]) < 1e-12) return null;
4744
+ [M[col], M[pivot]] = [M[pivot], M[col]];
4745
+ for (let r = 0; r < n; r++) {
4746
+ if (r === col) continue;
4747
+ const f = M[r][col] / M[col][col];
4748
+ for (let c = col; c <= n; c++) M[r][c] -= f * M[col][c];
4749
+ }
4750
+ }
4751
+ return M.map((row, i) => row[n] / M[i][i]);
4752
+ }
4753
+ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
4754
+ const {
4755
+ elevationMaskDeg = 10,
4756
+ troposphere = true,
4757
+ maxIterations = 15,
4758
+ convergenceM = 1e-4,
4759
+ tgd = true
4760
+ } = opts;
4761
+ const all = [...pseudoranges.keys()].filter((p) => ephemerides.has(p));
4762
+ const minSats = (list) => 3 + new Set(list.map((p) => p[0])).size;
4763
+ if (all.length < minSats(all)) return null;
4764
+ const gaussNewton = (prns) => {
4765
+ const systems = [...new Set(prns.map((p) => p[0]))].sort();
4766
+ const dim = 3 + systems.length;
4767
+ const sysIndex = new Map(systems.map((s, i) => [s, 3 + i]));
4768
+ let x2 = 0;
4769
+ let y2 = 0;
4770
+ let z2 = 0;
4771
+ const clock2 = new Map(systems.map((s) => [s, 0]));
4772
+ const residuals2 = {};
4773
+ let iterations2 = 0;
4774
+ let converged2 = false;
4775
+ for (let iter = 0; iter < maxIterations; iter++) {
4776
+ iterations2 = iter + 1;
4777
+ const HtH = Array.from(
4778
+ { length: dim },
4779
+ () => new Array(dim).fill(0)
4780
+ );
4781
+ const Htv = new Array(dim).fill(0);
4782
+ let rows = 0;
4783
+ for (const k of Object.keys(residuals2)) delete residuals2[k];
4784
+ for (const prn of prns) {
4785
+ const psr = pseudoranges.get(prn);
4786
+ const eph = ephemerides.get(prn);
4787
+ const sys = prn[0];
4788
+ const clk = clock2.get(sys);
4789
+ const tTx = timeMs - psr / C_LIGHT * 1e3;
4790
+ const dtsClock = satClockCorrection(eph, tTx);
4791
+ const sat = computeSatPosition(eph, tTx - dtsClock * 1e3);
4792
+ if (!Number.isFinite(sat.x)) continue;
4793
+ const isKepler2 = eph.system !== "R" && eph.system !== "S";
4794
+ const dts = dtsClock - (tgd && isKepler2 ? eph.tgd : 0);
4795
+ const travel = Math.hypot(sat.x - x2, sat.y - y2, sat.z - z2) / C_LIGHT;
4796
+ const [sx, sy, sz] = sagnac(sat, travel);
4797
+ const rho = Math.hypot(sx - x2, sy - y2, sz - z2);
4798
+ const ux = (x2 - sx) / rho;
4799
+ const uy = (y2 - sy) / rho;
4800
+ const uz = (z2 - sz) / rho;
4801
+ let elev = Math.PI / 2;
4802
+ let weight = 1;
4803
+ const positionSane = x2 * x2 + y2 * y2 + z2 * z2 > 1e12;
4804
+ if (positionSane) {
4805
+ const azel = ecefToAzEl(x2, y2, z2, sx, sy, sz);
4806
+ elev = azel.el;
4807
+ if (iter >= 2 && elev < elevationMaskDeg * Math.PI / 180) continue;
4808
+ const sinEl = Math.max(Math.sin(elev), 0.1);
4809
+ weight = sinEl * sinEl;
4810
+ }
4811
+ const tropo = troposphere && positionSane ? tropoDelay(elev) : 0;
4812
+ const predicted = rho + clk - C_LIGHT * dts + tropo;
4813
+ const v = psr - predicted;
4814
+ const h = new Array(dim).fill(0);
4815
+ h[0] = ux;
4816
+ h[1] = uy;
4817
+ h[2] = uz;
4818
+ h[sysIndex.get(sys)] = 1;
4819
+ for (let i = 0; i < dim; i++) {
4820
+ for (let j = 0; j < dim; j++) HtH[i][j] += weight * h[i] * h[j];
4821
+ Htv[i] += weight * h[i] * v;
4822
+ }
4823
+ residuals2[prn] = v;
4824
+ rows++;
4825
+ }
4826
+ if (rows < dim) return null;
4827
+ const dx = solveLinear(HtH, Htv);
4828
+ if (!dx) return null;
4829
+ x2 += dx[0];
4830
+ y2 += dx[1];
4831
+ z2 += dx[2];
4832
+ for (const s of systems)
4833
+ clock2.set(s, clock2.get(s) + dx[sysIndex.get(s)]);
4834
+ if (Math.hypot(dx[0], dx[1], dx[2]) < convergenceM) {
4835
+ converged2 = true;
4836
+ break;
4837
+ }
4838
+ }
4839
+ return { x: x2, y: y2, z: z2, clock: clock2, residuals: residuals2, iterations: iterations2, converged: converged2 };
4840
+ };
4841
+ const REJECT_THRESHOLD_M = 50;
4842
+ let candidates = all;
4843
+ const rejected = [];
4844
+ let inner = null;
4845
+ for (let round = 0; round <= all.length; round++) {
4846
+ inner = gaussNewton(candidates);
4847
+ if (!inner) return null;
4848
+ const vals = Object.entries(inner.residuals);
4849
+ let worst = null;
4850
+ let worstAbs = REJECT_THRESHOLD_M;
4851
+ for (const [prn, v] of vals) {
4852
+ if (Math.abs(v) > worstAbs) {
4853
+ worstAbs = Math.abs(v);
4854
+ worst = prn;
4855
+ }
4856
+ }
4857
+ if (!worst) break;
4858
+ const remaining = candidates.filter((p) => p !== worst);
4859
+ if (remaining.length < minSats(remaining)) break;
4860
+ rejected.push(worst);
4861
+ candidates = remaining;
4862
+ }
4863
+ if (!inner) return null;
4864
+ const { x, y, z, clock, residuals, iterations, converged } = inner;
4865
+ const used = [];
4866
+ const azels = [];
4867
+ for (const prn of candidates) {
4868
+ if (!(prn in residuals)) continue;
4869
+ const eph = ephemerides.get(prn);
4870
+ const sat = computeSatPosition(eph, timeMs);
4871
+ if (!Number.isFinite(sat.x)) continue;
4872
+ const azel = ecefToAzEl(x, y, z, sat.x, sat.y, sat.z);
4873
+ if (azel.el >= elevationMaskDeg * Math.PI / 180) {
4874
+ used.push(prn);
4875
+ azels.push(azel);
4876
+ }
4877
+ }
4878
+ return {
4879
+ position: [x, y, z],
4880
+ clockBias: Object.fromEntries(clock),
4881
+ usedSatellites: used,
4882
+ rejectedSatellites: rejected,
4883
+ residuals,
4884
+ dop: computeDop(azels),
4885
+ iterations,
4886
+ converged
4887
+ };
4888
+ }
4411
4889
 
4412
4890
  // src/ntrip/index.ts
4413
4891
  function parseStreamEntry(fields) {
@@ -6285,6 +6763,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
6285
6763
  MILLISECONDS_IN_WEEK,
6286
6764
  MILLISECONDS_TT_TAI,
6287
6765
  MultipathAccumulator,
6766
+ REFERENCE_FRAMES,
6288
6767
  RINEX_CODES,
6289
6768
  RTCM3_MESSAGE_NAMES,
6290
6769
  Rtcm3Decoder,
@@ -6311,6 +6790,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
6311
6790
  WGS84_SEMI_MINOR_AXIS,
6312
6791
  WarningAccumulator,
6313
6792
  analyzeQuality,
6793
+ applyHelmert,
6314
6794
  buildGloChannelMap,
6315
6795
  buildObsIndices,
6316
6796
  clampUnit,
@@ -6320,17 +6800,20 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
6320
6800
  computePsdDb,
6321
6801
  computeSatPosition,
6322
6802
  computeStats,
6803
+ computeVisibility,
6323
6804
  connectToMountpoint,
6324
6805
  createStationMeta,
6325
6806
  createStreamStats,
6326
6807
  crxDecompress,
6327
6808
  crxRepair,
6809
+ dateToEpoch,
6328
6810
  decodeEphemeris,
6329
6811
  decodeMsmFull,
6330
6812
  deg2dms,
6331
6813
  deg2rad,
6332
6814
  ecefToAzEl,
6333
6815
  ecefToGeodetic,
6816
+ ephInfoToEphemeris,
6334
6817
  euclidean3D,
6335
6818
  fetchSourcetable,
6336
6819
  fmtF,
@@ -6398,6 +6881,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
6398
6881
  greatCircleMidpoint,
6399
6882
  hdrLine,
6400
6883
  horizonDistance,
6884
+ ionoFree,
6401
6885
  keplerPosition,
6402
6886
  msmEpochToDate,
6403
6887
  navTimesFromEph,
@@ -6420,11 +6904,14 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
6420
6904
  resetGloFreqCache,
6421
6905
  rhumbLine,
6422
6906
  rtcm3Constellation,
6907
+ satClockCorrection,
6423
6908
  selectEphemeris,
6424
6909
  setGloFreqNumber,
6425
6910
  setRtcm3DebugHandler,
6911
+ solveSpp,
6426
6912
  systemCmp,
6427
6913
  systemName,
6914
+ transformFrame,
6428
6915
  updateStationMeta,
6429
6916
  updateStreamStats,
6430
6917
  verifyChecksum,
package/dist/index.d.cts CHANGED
@@ -9,7 +9,9 @@ export { E as EpochSummary, P as ParseOptions, R as RinexHeader, a as RinexResul
9
9
  export { E as Ephemeris, G as GlonassEphemeris, K as KeplerEphemeris, N as NavHeader, a as NavResult, p as parseNavFile } from './nav-BAI1a9vK.cjs';
10
10
  export { B as BitReader, E as EphemerisInfo, R as Rtcm3DebugHandler, a as Rtcm3Decoder, b as Rtcm3Frame, d as decodeEphemeris, r as readString, c as reportDecodeError, s as setRtcm3DebugHandler } from './ephemeris-Ohl6NAfw.cjs';
11
11
  export { MessageTypeStats, MsmEpoch, MsmSatObs, MsmSignal, RTCM3_MESSAGE_NAMES, SatCn0, SignalCn0, StationMeta, StreamStats, createStationMeta, createStreamStats, decodeMsmFull, msmEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats } from './rtcm3.cjs';
12
- export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, ecefToAzEl, glonassPosition, keplerPosition, navTimesFromEph, selectEphemeris } from './orbit.cjs';
12
+ export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, navTimesFromEph, selectEphemeris } from './orbit.cjs';
13
+ export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.cjs';
14
+ export { SppOptions, SppSolution, ionoFree, satClockCorrection, solveSpp } from './positioning.cjs';
13
15
  export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.cjs';
14
16
  export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, analyzeQuality } from './analysis.cjs';
15
17
  export { AntennaEntry, AntexFile, FrequencyData, frequencyLabel, parseAntex } from './antex.cjs';
package/dist/index.d.ts CHANGED
@@ -9,7 +9,9 @@ export { E as EpochSummary, P as ParseOptions, R as RinexHeader, a as RinexResul
9
9
  export { E as Ephemeris, G as GlonassEphemeris, K as KeplerEphemeris, N as NavHeader, a as NavResult, p as parseNavFile } from './nav-BAI1a9vK.js';
10
10
  export { B as BitReader, E as EphemerisInfo, R as Rtcm3DebugHandler, a as Rtcm3Decoder, b as Rtcm3Frame, d as decodeEphemeris, r as readString, c as reportDecodeError, s as setRtcm3DebugHandler } from './ephemeris-Ohl6NAfw.js';
11
11
  export { MessageTypeStats, MsmEpoch, MsmSatObs, MsmSignal, RTCM3_MESSAGE_NAMES, SatCn0, SignalCn0, StationMeta, StreamStats, createStationMeta, createStreamStats, decodeMsmFull, msmEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats } from './rtcm3.js';
12
- export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, ecefToAzEl, glonassPosition, keplerPosition, navTimesFromEph, selectEphemeris } from './orbit.js';
12
+ export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, navTimesFromEph, selectEphemeris } from './orbit.js';
13
+ export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.js';
14
+ export { SppOptions, SppSolution, ionoFree, satClockCorrection, solveSpp } from './positioning.js';
13
15
  export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.js';
14
16
  export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, analyzeQuality } from './analysis.js';
15
17
  export { AntennaEntry, AntexFile, FrequencyData, frequencyLabel, parseAntex } from './antex.js';