gnss-js 1.26.0 → 1.28.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.
package/dist/index.cjs CHANGED
@@ -117,6 +117,7 @@ __export(src_exports, {
117
117
  binexRecords: () => binexRecords,
118
118
  buildGloChannelMap: () => buildGloChannelMap,
119
119
  buildObsIndices: () => buildObsIndices,
120
+ buildPppAntenna: () => buildPppAntenna,
120
121
  clampUnit: () => clampUnit,
121
122
  computeAllPositions: () => computeAllPositions,
122
123
  computeDop: () => computeDop,
@@ -130,6 +131,7 @@ __export(src_exports, {
130
131
  connectToMountpoint: () => connectToMountpoint,
131
132
  crc32: () => crc32,
132
133
  createGzipLineSink: () => createGzipLineSink,
134
+ createPppCorrections: () => createPppCorrections,
133
135
  createStationMeta: () => createStationMeta,
134
136
  createStreamStats: () => createStreamStats,
135
137
  crxDecompress: () => crxDecompress,
@@ -215,6 +217,7 @@ __export(src_exports, {
215
217
  glonassAlmanacEpochMs: () => glonassAlmanacEpochMs,
216
218
  glonassAlmanacPosition: () => glonassAlmanacPosition,
217
219
  glonassPosition: () => glonassPosition,
220
+ gmst: () => gmst,
218
221
  greatCircleMidpoint: () => greatCircleMidpoint,
219
222
  hdrLine: () => hdrLine,
220
223
  horizonDistance: () => horizonDistance,
@@ -224,8 +227,10 @@ __export(src_exports, {
224
227
  lambdaReduction: () => lambdaReduction,
225
228
  lambdaSearch: () => lambdaSearch,
226
229
  maskRadForAzimuth: () => maskRadForAzimuth,
230
+ moonEcef: () => moonEcef,
227
231
  msmEpochToDate: () => msmEpochToDate,
228
232
  navTimesFromEph: () => navTimesFromEph,
233
+ niellMapping: () => niellMapping,
229
234
  nmeaCoordToDecimal: () => nmeaCoordToDecimal,
230
235
  oem4Frames: () => oem4Frames,
231
236
  padL: () => padL,
@@ -275,10 +280,13 @@ __export(src_exports, {
275
280
  selectEphemeris: () => selectEphemeris,
276
281
  setGloFreqNumber: () => setGloFreqNumber,
277
282
  setRtcm3DebugHandler: () => setRtcm3DebugHandler,
283
+ solidEarthTide: () => solidEarthTide,
278
284
  solveDgnss: () => solveDgnss,
285
+ solvePpp: () => solvePpp,
279
286
  solveSpp: () => solveSpp,
280
287
  sp3Position: () => sp3Position,
281
288
  stationHeaderLines: () => stationHeaderLines,
289
+ sunEcef: () => sunEcef,
282
290
  systemCmp: () => systemCmp,
283
291
  systemName: () => systemName,
284
292
  toRtkEpoch: () => toRtkEpoch,
@@ -289,6 +297,7 @@ __export(src_exports, {
289
297
  updateStreamStats: () => updateStreamStats,
290
298
  verifyChecksum: () => verifyChecksum,
291
299
  vincenty: () => vincenty,
300
+ writeCrx: () => writeCrx,
292
301
  writeModernObsBlob: () => writeModernObsBlob,
293
302
  writeRinex2ObsBlob: () => writeRinex2ObsBlob,
294
303
  writeRinex4ObsBlob: () => writeRinex4ObsBlob,
@@ -639,7 +648,7 @@ function getDayOfYear(date) {
639
648
  (date.getTime() - Date.UTC(date.getUTCFullYear())) / MILLISECONDS_IN_DAY
640
649
  ) + 1;
641
650
  }
642
- function getDateFromDayOfYear(dayOfYear, date) {
651
+ function getDateFromDayOfYear(dayOfYear2, date) {
643
652
  return new Date(
644
653
  Date.UTC(
645
654
  date.getUTCFullYear(),
@@ -649,7 +658,7 @@ function getDateFromDayOfYear(dayOfYear, date) {
649
658
  date.getUTCMinutes(),
650
659
  date.getUTCSeconds(),
651
660
  date.getUTCMilliseconds()
652
- ) + (dayOfYear - 1) * MILLISECONDS_IN_DAY
661
+ ) + (dayOfYear2 - 1) * MILLISECONDS_IN_DAY
653
662
  );
654
663
  }
655
664
  function getDayOfWeek(date) {
@@ -1319,6 +1328,442 @@ function crxDecompress(prev, field) {
1319
1328
  };
1320
1329
  }
1321
1330
 
1331
+ // src/rinex/crx-writer.ts
1332
+ var ARC_ORDER = 3;
1333
+ function emptyData() {
1334
+ return { u: [0, 0, 0, 0], l: [0, 0, 0, 0], order: -1 };
1335
+ }
1336
+ function cAtol(s) {
1337
+ let i = 0;
1338
+ while (i < s.length && (s[i] === " " || s[i] === " ")) i++;
1339
+ let sign = 1;
1340
+ if (s[i] === "+" || s[i] === "-") {
1341
+ if (s[i] === "-") sign = -1;
1342
+ i++;
1343
+ }
1344
+ let n = 0;
1345
+ let any = false;
1346
+ while (i < s.length && s[i] >= "0" && s[i] <= "9") {
1347
+ n = n * 10 + (s.charCodeAt(i) - 48);
1348
+ i++;
1349
+ any = true;
1350
+ }
1351
+ return any ? sign * n : 0;
1352
+ }
1353
+ function strdiff(s1, s2) {
1354
+ let ds = "";
1355
+ const n = Math.min(s1.length, s2.length);
1356
+ let i = 0;
1357
+ for (; i < n; i++) {
1358
+ if (s2[i] === s1[i]) ds += " ";
1359
+ else if (s2[i] === " ") ds += "&";
1360
+ else ds += s2[i];
1361
+ }
1362
+ if (s1.length > s2.length) {
1363
+ for (let k = i; k < s1.length; k++) ds += s1[k] === " " ? " " : "&";
1364
+ } else if (s2.length > s1.length) {
1365
+ ds += s2.slice(i);
1366
+ }
1367
+ let end = ds.length;
1368
+ while (end > 0 && ds[end - 1] === " ") end--;
1369
+ return ds.slice(0, end);
1370
+ }
1371
+ function readValue(field14) {
1372
+ const c = field14.padEnd(14, " ").split("");
1373
+ const p7 = c[7];
1374
+ c[10] = c[9];
1375
+ c[9] = c[8];
1376
+ let l = cAtol(c.slice(9, 14).join(""));
1377
+ let u;
1378
+ if (p7 === " ") {
1379
+ u = 0;
1380
+ } else if (p7 === "-") {
1381
+ u = 0;
1382
+ l = -l;
1383
+ } else {
1384
+ c[8] = ".";
1385
+ u = cAtol(c.slice(0, 14).join(""));
1386
+ if (u < 0) l = -l;
1387
+ }
1388
+ return { u, l };
1389
+ }
1390
+ function putdiff(dddu, dddl) {
1391
+ dddu += Math.trunc(dddl / 1e5);
1392
+ dddl %= 1e5;
1393
+ if (dddu < 0 && dddl > 0) {
1394
+ dddu++;
1395
+ dddl -= 1e5;
1396
+ } else if (dddu > 0 && dddl < 0) {
1397
+ dddu--;
1398
+ dddl += 1e5;
1399
+ }
1400
+ if (dddu === 0) return String(dddl);
1401
+ return `${dddu}${String(Math.abs(dddl)).padStart(5, "0")}`;
1402
+ }
1403
+ function putClock(du, dl, cOrder) {
1404
+ du += Math.trunc(dl / 1e8);
1405
+ dl %= 1e8;
1406
+ if (du < 0 && dl > 0) {
1407
+ du++;
1408
+ dl -= 1e8;
1409
+ } else if (du > 0 && dl < 0) {
1410
+ du--;
1411
+ dl += 1e8;
1412
+ }
1413
+ let out = "";
1414
+ if (cOrder === 0) out += `${ARC_ORDER}&`;
1415
+ if (du === 0) out += String(dl);
1416
+ else out += `${du}${String(Math.abs(dl)).padStart(8, "0")}`;
1417
+ return out;
1418
+ }
1419
+ function chop(line) {
1420
+ let end = line.length;
1421
+ if (end > 0 && line[end - 1] === "\r") end--;
1422
+ while (end > 0 && line[end - 1] === " ") end--;
1423
+ return line.slice(0, end);
1424
+ }
1425
+ function readSat(lines, cursor, rinexVersion, ntype, ntypeGnss) {
1426
+ let line = chop(lines[cursor.li++] ?? "");
1427
+ let maxField;
1428
+ let ntypeRec;
1429
+ let firstRec;
1430
+ let satId = "";
1431
+ if (rinexVersion === 2) {
1432
+ maxField = 5;
1433
+ ntypeRec = ntype;
1434
+ firstRec = 0;
1435
+ } else {
1436
+ satId = line.slice(0, 3);
1437
+ const sys = line[0];
1438
+ maxField = ntypeRec = ntypeGnss[sys] ?? -1;
1439
+ if (maxField < 0) return null;
1440
+ firstRec = 3;
1441
+ }
1442
+ const data = [];
1443
+ let flag = "";
1444
+ for (let i = 0; i < ntypeRec; i += maxField) {
1445
+ const nfield = Math.min(ntypeRec - i, maxField);
1446
+ const need = firstRec + 16 * nfield;
1447
+ let body = line;
1448
+ if (body.length < need) body = body.padEnd(need, " ");
1449
+ for (let j = 0; j < nfield; j++) {
1450
+ const p = firstRec + j * 16;
1451
+ const rec = body.slice(p, p + 16);
1452
+ const d = emptyData();
1453
+ if (rec[10] === ".") {
1454
+ flag += rec[14] ?? " ";
1455
+ flag += rec[15] ?? " ";
1456
+ const v = readValue(rec.slice(0, 14));
1457
+ d.u[0] = v.u;
1458
+ d.l[0] = v.l;
1459
+ d.order = 0;
1460
+ } else if (rec.slice(0, 14).trim() === "") {
1461
+ flag += rec[14] ?? " ";
1462
+ flag += rec[15] ?? " ";
1463
+ d.order = -1;
1464
+ } else {
1465
+ return null;
1466
+ }
1467
+ data.push(d);
1468
+ }
1469
+ if (i + maxField < ntypeRec) {
1470
+ line = chop(lines[cursor.li++] ?? "");
1471
+ }
1472
+ }
1473
+ return { sat: { data, flag, ntypeRec }, satId };
1474
+ }
1475
+ var MONTHS = [
1476
+ "Jan",
1477
+ "Feb",
1478
+ "Mar",
1479
+ "Apr",
1480
+ "May",
1481
+ "Jun",
1482
+ "Jul",
1483
+ "Aug",
1484
+ "Sep",
1485
+ "Oct",
1486
+ "Nov",
1487
+ "Dec"
1488
+ ];
1489
+ function crinexDate() {
1490
+ const d = /* @__PURE__ */ new Date();
1491
+ const dd = String(d.getUTCDate()).padStart(2, "0");
1492
+ const mon = MONTHS[d.getUTCMonth()];
1493
+ const yy = String(d.getUTCFullYear() % 100).padStart(2, "0");
1494
+ const hh = String(d.getUTCHours()).padStart(2, "0");
1495
+ const mm = String(d.getUTCMinutes()).padStart(2, "0");
1496
+ return `${dd}-${mon}-${yy} ${hh}:${mm}`;
1497
+ }
1498
+ function writeCrx(rinexText, opts = {}) {
1499
+ const rawLines = rinexText.split("\n");
1500
+ const cursor = { li: 0 };
1501
+ const out = [];
1502
+ const firstLine = chop(rawLines[cursor.li++] ?? "");
1503
+ if (firstLine.slice(60, 80) !== "RINEX VERSION / TYPE" || firstLine[20] !== "O") {
1504
+ throw new Error("writeCrx: not a RINEX observation file");
1505
+ }
1506
+ const rinexVersion = parseInt(firstLine, 10);
1507
+ const rinexVersionFull = readVersionFull(firstLine);
1508
+ if (rinexVersionFull < 0) throw new Error("writeCrx: invalid RINEX version");
1509
+ const crxVersion = rinexVersionFull >= 402 ? "3.1" : rinexVersion === 2 ? "1.0" : "3.0";
1510
+ out.push(
1511
+ `${crxVersion.padEnd(20)}${"COMPACT RINEX FORMAT".padEnd(40)}${"CRINEX VERS / TYPE".padEnd(20)}`
1512
+ );
1513
+ const prog = (opts.prog ?? "gnss-js writeCrx").slice(0, 40);
1514
+ const date = opts.date ?? crinexDate();
1515
+ out.push(`${prog.padEnd(40)}${date.padEnd(20)}CRINEX PROG / DATE`);
1516
+ out.push(firstLine);
1517
+ let ntype = 0;
1518
+ const ntypeGnss = {};
1519
+ let line;
1520
+ do {
1521
+ line = chop(rawLines[cursor.li++] ?? "");
1522
+ out.push(line);
1523
+ const label2 = line.slice(60).trimStart();
1524
+ if (label2.startsWith("# / TYPES OF OBSERV") && line[5] !== " ") {
1525
+ ntype = parseInt(line, 10);
1526
+ } else if (label2.startsWith("SYS / # / OBS TYPES") && line[0] !== " ") {
1527
+ ntypeGnss[line[0]] = parseInt(line.slice(3), 10);
1528
+ }
1529
+ if (cursor.li >= rawLines.length) break;
1530
+ } while (!line.slice(60).trimStart().startsWith("END OF HEADER"));
1531
+ const v2 = rinexVersion === 2;
1532
+ const off = v2 ? { event: 28, nsat: 29, satlst: 32, clock: 68, satOld: 32, shiftClk: 1 } : {
1533
+ event: 31,
1534
+ nsat: 32,
1535
+ satlst: 41,
1536
+ clock: 41,
1537
+ satOld: 41,
1538
+ shiftClk: 4,
1539
+ psec: 57
1540
+ };
1541
+ let oldline = "&";
1542
+ let nsatOld = 0;
1543
+ let clk0 = { u: [0, 0, 0, 0], l: [0, 0, 0, 0] };
1544
+ let dy0 = [];
1545
+ let flag0 = [];
1546
+ let oldpsec = "";
1547
+ let clkOrderPrev = -1;
1548
+ const resetArcs = () => {
1549
+ oldline = "&";
1550
+ nsatOld = 0;
1551
+ clkOrderPrev = -1;
1552
+ };
1553
+ for (; ; ) {
1554
+ if (cursor.li >= rawLines.length) break;
1555
+ let epoch = rawLines[cursor.li];
1556
+ if (epoch === void 0) break;
1557
+ if (epoch === "" && cursor.li === rawLines.length - 1) break;
1558
+ cursor.li++;
1559
+ epoch = chop(epoch);
1560
+ if (epoch === "") continue;
1561
+ let newline;
1562
+ if (v2) {
1563
+ newline = epoch;
1564
+ } else {
1565
+ newline = epoch.length < 41 ? epoch.padEnd(41, " ") : epoch;
1566
+ }
1567
+ const eventFlag = parseInt(newline[off.event] ?? "0", 10) || 0;
1568
+ if (eventFlag > 1) {
1569
+ putEventData(rawLines, cursor, v2, newline, out, ntypeGnss, (n) => {
1570
+ ntype = n;
1571
+ });
1572
+ resetArcs();
1573
+ clk0 = { u: [0, 0, 0, 0], l: [0, 0, 0, 0] };
1574
+ dy0 = [];
1575
+ flag0 = [];
1576
+ continue;
1577
+ }
1578
+ let newpsec = "";
1579
+ if (rinexVersionFull >= 402 && newline.length === 62) {
1580
+ const psec = newline.slice(off.psec, off.psec + 5);
1581
+ if (/^\d{5}$/.test(psec)) newpsec = psec;
1582
+ newline = newline.slice(0, off.psec).replace(/\s+$/, "");
1583
+ }
1584
+ let clkOrder = -1;
1585
+ const clk1 = { u: [0, 0, 0, 0], l: [0, 0, 0, 0] };
1586
+ if (newline.length > off.clock) {
1587
+ clkOrder = readClock(
1588
+ newline.slice(off.clock),
1589
+ off.shiftClk,
1590
+ clk1,
1591
+ clkOrderPrev
1592
+ );
1593
+ if (clkOrder > -1) {
1594
+ newline = newline.slice(0, off.clock).replace(/\s+$/, "");
1595
+ if (!v2 && newline.length < 41) newline = newline.padEnd(41, " ");
1596
+ }
1597
+ }
1598
+ clkOrderPrev = clkOrder;
1599
+ const nsat = parseInt((newline.slice(off.nsat) || "0").trim(), 10) || 0;
1600
+ if (v2 && nsat > 12) {
1601
+ newline = readMoreSat(rawLines, cursor, nsat, newline);
1602
+ }
1603
+ const sats = [];
1604
+ const satIds = [];
1605
+ let bad = false;
1606
+ let satlist = v2 ? newline.slice(off.satlst, off.satlst + nsat * 3) : "";
1607
+ for (let i = 0; i < nsat; i++) {
1608
+ const r = readSat(rawLines, cursor, rinexVersion, ntype, ntypeGnss);
1609
+ if (!r) {
1610
+ bad = true;
1611
+ break;
1612
+ }
1613
+ sats.push(r.sat);
1614
+ satIds.push(v2 ? satlist.slice(i * 3, i * 3 + 3) : r.satId);
1615
+ }
1616
+ if (bad) continue;
1617
+ if (!v2) satlist = satIds.join("");
1618
+ const fullNew = v2 ? newline : newline.slice(0, 41) + satlist;
1619
+ const oldSatList = v2 ? oldline.slice(off.satOld, off.satOld + nsatOld * 3) : oldline.slice(off.satOld);
1620
+ const sattbl = [];
1621
+ const dup = /* @__PURE__ */ new Set();
1622
+ for (let i = 0; i < nsat; i++) {
1623
+ const id = satIds[i];
1624
+ if (dup.has(id)) {
1625
+ bad = true;
1626
+ break;
1627
+ }
1628
+ dup.add(id);
1629
+ let found = -1;
1630
+ for (let j = 0; j < nsatOld; j++) {
1631
+ if (oldSatList.slice(j * 3, j * 3 + 3) === id) {
1632
+ found = j;
1633
+ break;
1634
+ }
1635
+ }
1636
+ sattbl.push(found);
1637
+ }
1638
+ if (bad) continue;
1639
+ out.push(strdiff(oldline, fullNew));
1640
+ let clockLine = "";
1641
+ if (clkOrder > -1) {
1642
+ if (clkOrder > 0) processClock(clk1, clk0, clkOrder);
1643
+ clockLine += putClock(clk1.u[clkOrder], clk1.l[clkOrder], clkOrder);
1644
+ }
1645
+ if (newpsec.length === 5) {
1646
+ clockLine += " " + strdiff(oldpsec, newpsec);
1647
+ }
1648
+ out.push(clockLine);
1649
+ const dataLines = buildData(sats, sattbl, dy0, flag0, rinexVersion);
1650
+ for (const dl of dataLines) out.push(dl);
1651
+ oldline = fullNew;
1652
+ nsatOld = nsat;
1653
+ clk0 = clk1;
1654
+ oldpsec = newpsec;
1655
+ dy0 = sats.map((s) => s.data);
1656
+ flag0 = sats.map((s) => s.flag);
1657
+ }
1658
+ return out.join("\n") + "\n";
1659
+ }
1660
+ function readClock(field, shiftClk, clk1, prevOrder) {
1661
+ const c = field.split("");
1662
+ if (c[2] !== ".") return -1;
1663
+ for (let k = 0; k < shiftClk; k++) c[2 + k] = c[3 + k];
1664
+ c[2 + shiftClk] = ".";
1665
+ const s = c.join("");
1666
+ const m = /(-?\d+)\.(\d+)/.exec(s);
1667
+ if (!m) return -1;
1668
+ clk1.u[0] = parseInt(m[1], 10);
1669
+ clk1.l[0] = parseInt(m[2], 10);
1670
+ if (c[0] === "-" || c[1] === "-") clk1.l[0] = -clk1.l[0];
1671
+ return Math.min(prevOrder + 1, ARC_ORDER);
1672
+ }
1673
+ function processClock(clk1, clk0, clkOrder) {
1674
+ for (let i = 0; i < clkOrder; i++) {
1675
+ clk1.u[i + 1] = clk1.u[i] - clk0.u[i];
1676
+ clk1.l[i + 1] = clk1.l[i] - clk0.l[i];
1677
+ }
1678
+ }
1679
+ function takeDiff(py1, py0) {
1680
+ py1.order = py0.order;
1681
+ if (py1.order < ARC_ORDER) py1.order++;
1682
+ for (let k = 0; k < py1.order; k++) {
1683
+ py1.u[k + 1] = py1.u[k] - py0.u[k];
1684
+ py1.l[k + 1] = py1.l[k] - py0.l[k];
1685
+ }
1686
+ }
1687
+ function buildData(sats, sattbl, dy0, flag0, rinexVersion) {
1688
+ const lines = [];
1689
+ for (let i = 0; i < sats.length; i++) {
1690
+ const sat = sats[i];
1691
+ const i0 = sattbl[i];
1692
+ const prevFlag = (i0 >= 0 ? flag0[i0] ?? "" : "").split("");
1693
+ let line = "";
1694
+ for (let j = 0; j < sat.ntypeRec; j++) {
1695
+ const py1 = sat.data[j];
1696
+ if (py1.order >= 0) {
1697
+ if (i0 < 0 || dy0[i0]?.[j]?.order === -1) {
1698
+ py1.order = 0;
1699
+ line += `${ARC_ORDER}&`;
1700
+ } else {
1701
+ takeDiff(py1, dy0[i0][j]);
1702
+ if (Math.abs(py1.u[py1.order]) > 1e5) {
1703
+ py1.order = 0;
1704
+ line += `${ARC_ORDER}&`;
1705
+ }
1706
+ }
1707
+ line += putdiff(py1.u[py1.order], py1.l[py1.order]);
1708
+ } else if (i0 >= 0 && rinexVersion === 2) {
1709
+ prevFlag[j * 2] = " ";
1710
+ prevFlag[j * 2 + 1] = " ";
1711
+ }
1712
+ line += " ";
1713
+ }
1714
+ if (i0 < 0 && rinexVersion !== 2) {
1715
+ for (const ch of sat.flag) line += ch === " " ? "&" : ch;
1716
+ } else {
1717
+ line += i0 < 0 ? strdiff("", sat.flag) : strdiff(prevFlag.join(""), sat.flag);
1718
+ line = line.replace(/ +$/, "");
1719
+ }
1720
+ lines.push(line);
1721
+ }
1722
+ return lines;
1723
+ }
1724
+ function putEventData(lines, cursor, v2, epochLine, out, ntypeGnss, setNtype) {
1725
+ if (v2) {
1726
+ out.push("&" + epochLine.slice(1));
1727
+ if (epochLine.length > 29) {
1728
+ const n = parseInt(epochLine.slice(29), 10) || 0;
1729
+ for (let i = 0; i < n; i++) {
1730
+ const l = chop(lines[cursor.li++] ?? "");
1731
+ out.push(l);
1732
+ if (l.slice(60).trimStart().startsWith("# / TYPES OF OBSERV") && l[5] !== " ") {
1733
+ setNtype(parseInt(l, 10));
1734
+ }
1735
+ }
1736
+ }
1737
+ } else {
1738
+ out.push(epochLine.replace(/\s+$/, ""));
1739
+ const n = parseInt(epochLine.slice(32), 10) || 0;
1740
+ for (let i = 0; i < n; i++) {
1741
+ const l = chop(lines[cursor.li++] ?? "");
1742
+ out.push(l);
1743
+ if (l.slice(60).trimStart().startsWith("SYS / # / OBS TYPES") && l[0] !== " ") {
1744
+ ntypeGnss[l[0]] = parseInt(l.slice(3), 10);
1745
+ }
1746
+ }
1747
+ }
1748
+ }
1749
+ function readMoreSat(lines, cursor, n, epochLine) {
1750
+ let result = epochLine;
1751
+ let remaining = n;
1752
+ while (remaining > 12) {
1753
+ const l = lines[cursor.li++] ?? "";
1754
+ const chopped = chop(l);
1755
+ result += chopped[2] === " " ? chopped.slice(32) : chopped;
1756
+ remaining -= 12;
1757
+ }
1758
+ return result;
1759
+ }
1760
+ function readVersionFull(line) {
1761
+ if (line[5] === "2" && line[6] === " ") return 200;
1762
+ const d = (c) => c !== void 0 && c >= "0" && c <= "9";
1763
+ if (!d(line[5]) || line[6] !== "." || !d(line[7]) || !d(line[8])) return -1;
1764
+ return (line.charCodeAt(5) - 48) * 100 + (line.charCodeAt(7) - 48) * 10 + (line.charCodeAt(8) - 48);
1765
+ }
1766
+
1322
1767
  // src/rinex/format.ts
1323
1768
  function padL(s, w) {
1324
1769
  return s.length >= w ? s.slice(0, w) : s + " ".repeat(w - s.length);
@@ -7186,14 +7631,14 @@ function parseOrbitBlock(c, masks) {
7186
7631
  const gnssIod = c.u(IOD_BITS[sys.system]);
7187
7632
  const rad = c.s(13);
7188
7633
  const inT = c.s(12);
7189
- const cross = c.s(12);
7634
+ const cross2 = c.s(12);
7190
7635
  corrections.push({
7191
7636
  system: sys.system,
7192
7637
  prn,
7193
7638
  gnssIod,
7194
7639
  deltaRadial: rad === -4096 ? null : rad * 25e-4,
7195
7640
  deltaInTrack: inT === -2048 ? null : inT * 8e-3,
7196
- deltaCrossTrack: cross === -2048 ? null : cross * 8e-3
7641
+ deltaCrossTrack: cross2 === -2048 ? null : cross2 * 8e-3
7197
7642
  });
7198
7643
  }
7199
7644
  }
@@ -10220,17 +10665,17 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
10220
10665
  function computeDop(satAzEls) {
10221
10666
  const n = satAzEls.length;
10222
10667
  if (n < 4) return null;
10223
- const H2 = [];
10668
+ const H3 = [];
10224
10669
  for (const { az, el } of satAzEls) {
10225
10670
  const cosEl = Math.cos(el);
10226
- H2.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
10671
+ H3.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
10227
10672
  }
10228
10673
  const HtH = Array.from({ length: 4 }, () => new Array(4).fill(0));
10229
10674
  for (let i = 0; i < 4; i++) {
10230
10675
  for (let j = 0; j < 4; j++) {
10231
10676
  let sum = 0;
10232
10677
  for (let k = 0; k < n; k++) {
10233
- sum += H2[k][i] * H2[k][j];
10678
+ sum += H3[k][i] * H3[k][j];
10234
10679
  }
10235
10680
  HtH[i][j] = sum;
10236
10681
  }
@@ -11357,7 +11802,7 @@ function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
11357
11802
  let cov = null;
11358
11803
  for (let iter = 0; iter < maxIterations; iter++) {
11359
11804
  iterations = iter + 1;
11360
- const H2 = [];
11805
+ const H3 = [];
11361
11806
  const v = [];
11362
11807
  const terms = /* @__PURE__ */ new Map();
11363
11808
  const termOf = (g) => {
@@ -11373,11 +11818,11 @@ function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
11373
11818
  const tr = termOf(row.ref);
11374
11819
  const pred = ts.rho - row.g.rhoB + ts.dTropo - (tr.rho - row.ref.rhoB + tr.dTropo);
11375
11820
  v.push(row.z - pred);
11376
- H2.push([ts.u[0] - tr.u[0], ts.u[1] - tr.u[1], ts.u[2] - tr.u[2]]);
11821
+ H3.push([ts.u[0] - tr.u[0], ts.u[1] - tr.u[1], ts.u[2] - tr.u[2]]);
11377
11822
  }
11378
- const Ht = transpose(H2);
11823
+ const Ht = transpose(H3);
11379
11824
  const HtW = matMul(Ht, W);
11380
- const N = matMul(HtW, H2);
11825
+ const N = matMul(HtW, H3);
11381
11826
  const b = matMul(
11382
11827
  HtW,
11383
11828
  v.map((s) => [s])
@@ -11393,7 +11838,7 @@ function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
11393
11838
  z += dx[2][0];
11394
11839
  cov = Ninv;
11395
11840
  rows.forEach((row, i) => {
11396
- residuals[row.g.prn] = v[i] - (H2[i][0] * dx[0][0] + H2[i][1] * dx[1][0] + H2[i][2] * dx[2][0]);
11841
+ residuals[row.g.prn] = v[i] - (H3[i][0] * dx[0][0] + H3[i][1] * dx[1][0] + H3[i][2] * dx[2][0]);
11397
11842
  });
11398
11843
  if (Math.hypot(dx[0][0], dx[1][0], dx[2][0]) < convergenceM) {
11399
11844
  converged = true;
@@ -11785,19 +12230,19 @@ var RtkFloatEngine = class {
11785
12230
  const PPrior = this.P;
11786
12231
  let xi = [...this.x];
11787
12232
  let K = [];
11788
- let H2 = [];
12233
+ let H3 = [];
11789
12234
  for (let it = 0; it < Math.max(o.updateIterations, 1); it++) {
11790
- H2 = [];
12235
+ H3 = [];
11791
12236
  const v = [];
11792
12237
  for (const row of rows) {
11793
12238
  const { pred, h } = predict(row, xi);
11794
12239
  let corr = 0;
11795
12240
  for (let j = 0; j < n; j++) corr += h[j] * (xPrior[j] - xi[j]);
11796
12241
  v.push(row.z - pred - corr);
11797
- H2.push(h);
12242
+ H3.push(h);
11798
12243
  }
11799
- const Ht = transpose(H2);
11800
- const S = matMul(matMul(H2, PPrior), Ht);
12244
+ const Ht = transpose(H3);
12245
+ const S = matMul(matMul(H3, PPrior), Ht);
11801
12246
  for (let i = 0; i < rows.length; i++)
11802
12247
  for (let j = 0; j < rows.length; j++) S[i][j] += R[i][j];
11803
12248
  const Sinv = matInv(S);
@@ -11813,7 +12258,7 @@ var RtkFloatEngine = class {
11813
12258
  xi = xPrior.map((s, i) => s + dx[i][0]);
11814
12259
  }
11815
12260
  this.x = xi;
11816
- const KH = matMul(K, H2);
12261
+ const KH = matMul(K, H3);
11817
12262
  const IKH = zeros(n, n);
11818
12263
  for (let i = 0; i < n; i++)
11819
12264
  for (let j = 0; j < n; j++) IKH[i][j] = (i === j ? 1 : 0) - KH[i][j];
@@ -11906,6 +12351,800 @@ var RtkFloatEngine = class {
11906
12351
  }
11907
12352
  };
11908
12353
 
12354
+ // src/positioning/ppp-tropo.ts
12355
+ var HYD_AVG_A = [
12356
+ 0.0012769934,
12357
+ 1268323e-9,
12358
+ 0.0012465397,
12359
+ 0.0012196049,
12360
+ 0.0012045996
12361
+ ];
12362
+ var HYD_AVG_B = [
12363
+ 0.0029153695,
12364
+ 0.0029152299,
12365
+ 0.0029288445,
12366
+ 0.0029022565,
12367
+ 0.0029024912
12368
+ ];
12369
+ var HYD_AVG_C = [
12370
+ 0.062610505,
12371
+ 0.062837393,
12372
+ 0.063721774,
12373
+ 0.063824265,
12374
+ 0.064258455
12375
+ ];
12376
+ var HYD_AMP_A = [0, 12709626e-12, 26523662e-12, 34000452e-12, 41202191e-12];
12377
+ var HYD_AMP_B = [0, 21414979e-12, 30160779e-12, 72562722e-12, 11723375e-11];
12378
+ var HYD_AMP_C = [0, 901284e-10, 43497037e-12, 84795348e-11, 0.0017037206];
12379
+ var HT_A = 253e-7;
12380
+ var HT_B = 549e-5;
12381
+ var HT_C = 114e-5;
12382
+ var WET_A = [
12383
+ 58021897e-11,
12384
+ 56794847e-11,
12385
+ 58118019e-11,
12386
+ 59727542e-11,
12387
+ 61641693e-11
12388
+ ];
12389
+ var WET_B = [
12390
+ 0.0014275268,
12391
+ 0.0015138625,
12392
+ 0.0014572752,
12393
+ 0.0015007428,
12394
+ 0.0017599082
12395
+ ];
12396
+ var WET_C = [
12397
+ 0.043472961,
12398
+ 0.04672951,
12399
+ 0.043908931,
12400
+ 0.044626982,
12401
+ 0.054736038
12402
+ ];
12403
+ var LATS = [15, 30, 45, 60, 75];
12404
+ function interpLat(table, absLatDeg) {
12405
+ if (absLatDeg <= 15) return table[0];
12406
+ if (absLatDeg >= 75) return table[4];
12407
+ let i = 0;
12408
+ while (i < 4 && absLatDeg > LATS[i + 1]) i++;
12409
+ const t = (absLatDeg - LATS[i]) / (LATS[i + 1] - LATS[i]);
12410
+ return table[i] + t * (table[i + 1] - table[i]);
12411
+ }
12412
+ function marini(sinEl, a, b, c) {
12413
+ const num = 1 + a / (1 + b / (1 + c));
12414
+ const den = sinEl + a / (sinEl + b / (sinEl + c));
12415
+ return num / den;
12416
+ }
12417
+ function dayOfYear(timeMs) {
12418
+ const d = new Date(timeMs);
12419
+ const start = Date.UTC(d.getUTCFullYear(), 0, 1);
12420
+ return Math.floor((timeMs - start) / 864e5) + 1;
12421
+ }
12422
+ function niellMapping(elRad, latRad, heightM, timeMs) {
12423
+ const sinEl = Math.sin(Math.max(elRad, 1e-3));
12424
+ const absLat = Math.abs(latRad * 180 / Math.PI);
12425
+ const hKm = heightM / 1e3;
12426
+ let doy = dayOfYear(timeMs);
12427
+ if (latRad < 0) doy += 365.25 / 2;
12428
+ const yfrac = Math.cos(2 * Math.PI * (doy - 28) / 365.25);
12429
+ const ah = interpLat(HYD_AVG_A, absLat) - interpLat(HYD_AMP_A, absLat) * yfrac;
12430
+ const bh = interpLat(HYD_AVG_B, absLat) - interpLat(HYD_AMP_B, absLat) * yfrac;
12431
+ const ch = interpLat(HYD_AVG_C, absLat) - interpLat(HYD_AMP_C, absLat) * yfrac;
12432
+ const mHt = marini(sinEl, HT_A, HT_B, HT_C);
12433
+ const dmdh = 1 / sinEl - mHt;
12434
+ const mh = marini(sinEl, ah, bh, ch) + dmdh * hKm;
12435
+ const aw = interpLat(WET_A, absLat);
12436
+ const bw = interpLat(WET_B, absLat);
12437
+ const cw = interpLat(WET_C, absLat);
12438
+ const mw = marini(sinEl, aw, bw, cw);
12439
+ const h = heightM < 0 ? 0 : heightM > 1e4 ? 1e4 : heightM;
12440
+ const pres = 1013.25 * Math.pow(1 - 22557e-9 * h, 5.2568);
12441
+ const zhd = 22768e-7 * pres / (1 - 266e-5 * Math.cos(2 * latRad) - 28e-5 * (h / 1e3));
12442
+ return { zhd, mh, mw };
12443
+ }
12444
+
12445
+ // src/positioning/ppp-astro.ts
12446
+ var AU = 149597870700;
12447
+ var GPS_UTC_LEAP = 18;
12448
+ var J2000_MS = Date.UTC(2e3, 0, 1, 12, 0, 0);
12449
+ var DEG = Math.PI / 180;
12450
+ function daysSinceJ2000(timeMs) {
12451
+ return (timeMs - GPS_UTC_LEAP * 1e3 - J2000_MS) / 864e5;
12452
+ }
12453
+ function gmst(timeMs) {
12454
+ const d = daysSinceJ2000(timeMs);
12455
+ let deg = 280.46061837 + 360.98564736629 * d;
12456
+ deg %= 360;
12457
+ if (deg < 0) deg += 360;
12458
+ return deg * DEG;
12459
+ }
12460
+ function eciToEcef(v, theta) {
12461
+ const c = Math.cos(theta);
12462
+ const s = Math.sin(theta);
12463
+ return [v[0] * c + v[1] * s, -v[0] * s + v[1] * c, v[2]];
12464
+ }
12465
+ function sunEcef(timeMs) {
12466
+ const n = daysSinceJ2000(timeMs);
12467
+ const L = (280.46 + 0.9856474 * n) * DEG;
12468
+ const g = (357.528 + 0.9856003 * n) * DEG;
12469
+ const lambda = L + (1.915 * Math.sin(g) + 0.02 * Math.sin(2 * g)) * DEG;
12470
+ const eps = (23.439 - 4e-7 * n) * DEG;
12471
+ const R = (1.00014 - 0.01671 * Math.cos(g) - 14e-5 * Math.cos(2 * g)) * AU;
12472
+ const eci = [
12473
+ R * Math.cos(lambda),
12474
+ R * Math.cos(eps) * Math.sin(lambda),
12475
+ R * Math.sin(eps) * Math.sin(lambda)
12476
+ ];
12477
+ return eciToEcef(eci, gmst(timeMs));
12478
+ }
12479
+ function moonEcef(timeMs) {
12480
+ const T = daysSinceJ2000(timeMs) / 36525;
12481
+ const L0 = 218.31617 + 481267.88088 * T - 1.3972 * T;
12482
+ const l = 134.96292 + 477198.86753 * T;
12483
+ const lp = 357.52543 + 35999.04944 * T;
12484
+ const F = 93.27283 + 483202.01873 * T;
12485
+ const D = 297.85027 + 445267.11135 * T;
12486
+ const s = (x) => Math.sin(x * DEG);
12487
+ const c = (x) => Math.cos(x * DEG);
12488
+ const lon = L0 + 6.28875 * s(l) + 1.274018 * s(2 * D - l) + 0.658309 * s(2 * D) + 0.213616 * s(2 * l) - 0.185596 * s(lp) - 0.114336 * s(2 * F) + 0.058793 * s(2 * D - 2 * l) + 0.057212 * s(2 * D - lp - l) + 0.05332 * s(2 * D + l);
12489
+ const lat = 5.128189 * s(F) + 0.280606 * s(l + F) + 0.277693 * s(l - F) + 0.173238 * s(2 * D - F) + 0.055413 * s(2 * D + F - l) + 0.046272 * s(2 * D - F - l);
12490
+ const r = (385e3 - 20905 * c(l) - 3699 * c(2 * D - l) - 2956 * c(2 * D) - 570 * c(2 * l)) * 1e3;
12491
+ const eps = (23.4393 - 4e-7 * daysSinceJ2000(timeMs)) * DEG;
12492
+ const lonR = lon * DEG;
12493
+ const latR = lat * DEG;
12494
+ const xe = r * Math.cos(latR) * Math.cos(lonR);
12495
+ const ye = r * Math.cos(latR) * Math.sin(lonR);
12496
+ const ze = r * Math.sin(latR);
12497
+ const eci = [
12498
+ xe,
12499
+ ye * Math.cos(eps) - ze * Math.sin(eps),
12500
+ ye * Math.sin(eps) + ze * Math.cos(eps)
12501
+ ];
12502
+ return eciToEcef(eci, gmst(timeMs));
12503
+ }
12504
+ var H2 = 0.6078;
12505
+ var L2 = 0.0847;
12506
+ var RE = 6378137;
12507
+ var GM_SUN_RATIO = 332946.0482;
12508
+ var GM_MOON_RATIO = 0.0123000371;
12509
+ function solidEarthTide(rcv, sun, moon) {
12510
+ const rNorm = Math.hypot(rcv[0], rcv[1], rcv[2]);
12511
+ const rHat = [
12512
+ rcv[0] / rNorm,
12513
+ rcv[1] / rNorm,
12514
+ rcv[2] / rNorm
12515
+ ];
12516
+ const disp = [0, 0, 0];
12517
+ for (const [body, ratio] of [
12518
+ [sun, GM_SUN_RATIO],
12519
+ [moon, GM_MOON_RATIO]
12520
+ ]) {
12521
+ const rj = Math.hypot(body[0], body[1], body[2]);
12522
+ const jHat = [
12523
+ body[0] / rj,
12524
+ body[1] / rj,
12525
+ body[2] / rj
12526
+ ];
12527
+ const dot2 = rHat[0] * jHat[0] + rHat[1] * jHat[1] + rHat[2] * jHat[2];
12528
+ const factor = ratio * RE * RE * RE * RE / (rj * rj * rj);
12529
+ const radialScale = H2 * (1.5 * dot2 * dot2 - 0.5);
12530
+ const tanScale = 3 * L2 * dot2;
12531
+ for (let k = 0; k < 3; k++) {
12532
+ disp[k] += factor * (radialScale * rHat[k] + tanScale * (jHat[k] - dot2 * rHat[k]));
12533
+ }
12534
+ }
12535
+ return disp;
12536
+ }
12537
+
12538
+ // src/positioning/ppp-antenna.ts
12539
+ function parseAntexTime(s) {
12540
+ const p = s.trim().split(/\s+/).map(Number);
12541
+ if (p.length < 3 || p.some((v) => Number.isNaN(v))) return null;
12542
+ return Date.UTC(
12543
+ p[0],
12544
+ (p[1] || 1) - 1,
12545
+ p[2] || 1,
12546
+ p[3] ?? 0,
12547
+ p[4] ?? 0,
12548
+ Math.floor(p[5] ?? 0)
12549
+ );
12550
+ }
12551
+ function freqOf(entry, code) {
12552
+ return entry.frequencies.find((f) => f.frequency === code);
12553
+ }
12554
+ function combineIf(fA, fB, f1, f2) {
12555
+ const g = f1 * f1 / (f1 * f1 - f2 * f2);
12556
+ const mm = 1e-3;
12557
+ const pco = [
12558
+ (g * fA.pcoN - (g - 1) * fB.pcoN) * mm,
12559
+ (g * fA.pcoE - (g - 1) * fB.pcoE) * mm,
12560
+ (g * fA.pcoU - (g - 1) * fB.pcoU) * mm
12561
+ ];
12562
+ const nz = Math.min(fA.pcvNoazi.length, fB.pcvNoazi.length);
12563
+ const pcvNoazi = new Array(nz);
12564
+ for (let i = 0; i < nz; i++) {
12565
+ pcvNoazi[i] = (g * fA.pcvNoazi[i] - (g - 1) * fB.pcvNoazi[i]) * mm;
12566
+ }
12567
+ return { pco, pcvZen1Deg: 0, pcvDzenDeg: 0, pcvNoazi };
12568
+ }
12569
+ function buildPppAntenna(antex) {
12570
+ const sats = /* @__PURE__ */ new Map();
12571
+ const rcvs = /* @__PURE__ */ new Map();
12572
+ for (const a of antex.antennas) {
12573
+ if (a.isSatellite) {
12574
+ const list = sats.get(a.serialNo) ?? [];
12575
+ list.push(a);
12576
+ sats.set(a.serialNo, list);
12577
+ } else if (!rcvs.has(a.type)) {
12578
+ rcvs.set(a.type, a);
12579
+ }
12580
+ }
12581
+ const withZen = (entry, off) => ({
12582
+ ...off,
12583
+ pcvZen1Deg: entry.zen1,
12584
+ pcvDzenDeg: entry.dzen
12585
+ });
12586
+ return {
12587
+ satOffset(prn, dateMs, band1, band2, f1, f2) {
12588
+ const list = sats.get(prn);
12589
+ if (!list) return null;
12590
+ const entry = list.find((e) => {
12591
+ const from = parseAntexTime(e.validFrom);
12592
+ const until = parseAntexTime(e.validUntil);
12593
+ return (from == null || dateMs >= from) && (until == null || dateMs <= until);
12594
+ });
12595
+ if (!entry) return null;
12596
+ const a = freqOf(entry, band1);
12597
+ const b = freqOf(entry, band2);
12598
+ if (!a || !b) return null;
12599
+ return withZen(entry, combineIf(a, b, f1, f2));
12600
+ },
12601
+ rcvOffset(antType, band1, band2, f1, f2) {
12602
+ const entry = rcvs.get(antType);
12603
+ if (!entry) return null;
12604
+ const a = freqOf(entry, band1);
12605
+ const b = freqOf(entry, band2);
12606
+ if (!a || !b) return null;
12607
+ return withZen(entry, combineIf(a, b, f1, f2));
12608
+ }
12609
+ };
12610
+ }
12611
+ function cross(a, b) {
12612
+ return [
12613
+ a[1] * b[2] - a[2] * b[1],
12614
+ a[2] * b[0] - a[0] * b[2],
12615
+ a[0] * b[1] - a[1] * b[0]
12616
+ ];
12617
+ }
12618
+ function norm(a) {
12619
+ const n = Math.hypot(a[0], a[1], a[2]) || 1;
12620
+ return [a[0] / n, a[1] / n, a[2] / n];
12621
+ }
12622
+ function dot(a, b) {
12623
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
12624
+ }
12625
+ function satBodyFrame(satEcef, sunEcef2) {
12626
+ const z = norm([-satEcef[0], -satEcef[1], -satEcef[2]]);
12627
+ const eSun = norm([
12628
+ sunEcef2[0] - satEcef[0],
12629
+ sunEcef2[1] - satEcef[1],
12630
+ sunEcef2[2] - satEcef[2]
12631
+ ]);
12632
+ const y = norm(cross(z, eSun));
12633
+ const x = norm(cross(y, z));
12634
+ return { x, y, z };
12635
+ }
12636
+ function satPcoToEcef(pcoBody, frame) {
12637
+ return [
12638
+ pcoBody[0] * frame.x[0] + pcoBody[1] * frame.y[0] + pcoBody[2] * frame.z[0],
12639
+ pcoBody[0] * frame.x[1] + pcoBody[1] * frame.y[1] + pcoBody[2] * frame.z[1],
12640
+ pcoBody[0] * frame.x[2] + pcoBody[1] * frame.y[2] + pcoBody[2] * frame.z[2]
12641
+ ];
12642
+ }
12643
+ function enuToEcef(enu, latRad, lonRad) {
12644
+ const sl = Math.sin(latRad);
12645
+ const cl = Math.cos(latRad);
12646
+ const so = Math.sin(lonRad);
12647
+ const co = Math.cos(lonRad);
12648
+ const [e, n, u] = enu;
12649
+ return [
12650
+ -so * e - sl * co * n + cl * co * u,
12651
+ co * e - sl * so * n + cl * so * u,
12652
+ cl * n + sl * u
12653
+ ];
12654
+ }
12655
+ function interpPcv(off, angleDeg) {
12656
+ const { pcvZen1Deg, pcvDzenDeg, pcvNoazi } = off;
12657
+ if (pcvDzenDeg <= 0 || pcvNoazi.length < 2) return 0;
12658
+ const x = (angleDeg - pcvZen1Deg) / pcvDzenDeg;
12659
+ if (x <= 0) return pcvNoazi[0];
12660
+ if (x >= pcvNoazi.length - 1) return pcvNoazi[pcvNoazi.length - 1];
12661
+ const i = Math.floor(x);
12662
+ const t = x - i;
12663
+ return pcvNoazi[i] * (1 - t) + pcvNoazi[i + 1] * t;
12664
+ }
12665
+ function phaseWindup(prn, losRcvToSat, satFrame, latRad, lonRad, state) {
12666
+ const k = [
12667
+ -losRcvToSat[0],
12668
+ -losRcvToSat[1],
12669
+ -losRcvToSat[2]
12670
+ ];
12671
+ const kx = satFrame.x;
12672
+ const ky = satFrame.y;
12673
+ const kdotx = dot(k, kx);
12674
+ const kcrossY = cross(k, ky);
12675
+ const dS = [
12676
+ kx[0] - k[0] * kdotx - kcrossY[0],
12677
+ kx[1] - k[1] * kdotx - kcrossY[1],
12678
+ kx[2] - k[2] * kdotx - kcrossY[2]
12679
+ ];
12680
+ const north = enuToEcef([0, 1, 0], latRad, lonRad);
12681
+ const east = enuToEcef([1, 0, 0], latRad, lonRad);
12682
+ const kdotn = dot(k, north);
12683
+ const kcrossE = cross(k, east);
12684
+ const dR = [
12685
+ north[0] - k[0] * kdotn + kcrossE[0],
12686
+ north[1] - k[1] * kdotn + kcrossE[1],
12687
+ north[2] - k[2] * kdotn + kcrossE[2]
12688
+ ];
12689
+ const nS = Math.hypot(dS[0], dS[1], dS[2]) || 1;
12690
+ const nR = Math.hypot(dR[0], dR[1], dR[2]) || 1;
12691
+ let cosPhi = dot(dS, dR) / (nS * nR);
12692
+ cosPhi = Math.max(-1, Math.min(1, cosPhi));
12693
+ const zeta = cross(dS, dR);
12694
+ const sign = dot(k, zeta) >= 0 ? 1 : -1;
12695
+ const dphi = sign * Math.acos(cosPhi) / (2 * Math.PI);
12696
+ const prev = state.get(prn);
12697
+ const wu = prev == null ? dphi : dphi + Math.round(prev - dphi);
12698
+ state.set(prn, wu);
12699
+ return wu;
12700
+ }
12701
+
12702
+ // src/positioning/ppp-corrections.ts
12703
+ function applyCorrections(corrections, geom, ctx) {
12704
+ if (corrections.evaluate) return corrections.evaluate(geom, ctx);
12705
+ return { rangeM: 0, phaseWindupM: 0 };
12706
+ }
12707
+ function createPppCorrections(cfg) {
12708
+ const useSat = cfg.satPco ?? false;
12709
+ const useRcv = cfg.rcvPco ?? true;
12710
+ const useTides = cfg.tides ?? true;
12711
+ const useWindup = cfg.windup ?? true;
12712
+ const antDelta = cfg.antDeltaEnu ?? [0, 0, 0];
12713
+ const windupState = /* @__PURE__ */ new Map();
12714
+ return {
12715
+ epochContext(timeMs, rcvEcef) {
12716
+ const sun = sunEcef(timeMs);
12717
+ const moon = moonEcef(timeMs);
12718
+ const tideDisp = useTides ? solidEarthTide(rcvEcef, sun, moon) : [0, 0, 0];
12719
+ return { timeMs, sun, moon, tideDisp };
12720
+ },
12721
+ evaluate(geom, ctx) {
12722
+ let rangeM = 0;
12723
+ let phaseWindupM = 0;
12724
+ const e = geom.los;
12725
+ const [latDeg, lonDeg] = ecefToGeodetic(
12726
+ geom.rcvEcef[0],
12727
+ geom.rcvEcef[1],
12728
+ geom.rcvEcef[2]
12729
+ );
12730
+ const latRad = latDeg * Math.PI / 180;
12731
+ const lonRad = lonDeg * Math.PI / 180;
12732
+ const frame = ctx ? satBodyFrame(geom.satEcef, ctx.sun) : null;
12733
+ if (useSat && frame && geom.band1 && geom.band2) {
12734
+ const off = cfg.antenna.satOffset(
12735
+ geom.prn,
12736
+ ctx.timeMs,
12737
+ geom.band1,
12738
+ geom.band2,
12739
+ geom.f1,
12740
+ geom.f2
12741
+ );
12742
+ if (off) {
12743
+ const pcoEcef = satPcoToEcef(off.pco, frame);
12744
+ rangeM += dot(e, pcoEcef);
12745
+ const satR = Math.hypot(
12746
+ geom.satEcef[0],
12747
+ geom.satEcef[1],
12748
+ geom.satEcef[2]
12749
+ );
12750
+ const cosNadir = (e[0] * geom.satEcef[0] + e[1] * geom.satEcef[1] + e[2] * geom.satEcef[2]) / satR;
12751
+ const nadirDeg = Math.acos(Math.max(-1, Math.min(1, cosNadir))) * 180 / Math.PI;
12752
+ rangeM += interpPcv(off, nadirDeg);
12753
+ }
12754
+ }
12755
+ if (useRcv && geom.band1 && geom.band2) {
12756
+ const off = cfg.antenna.rcvOffset(
12757
+ cfg.rcvAntType,
12758
+ geom.band1,
12759
+ geom.band2,
12760
+ geom.f1,
12761
+ geom.f2
12762
+ );
12763
+ if (off) {
12764
+ const total = [
12765
+ off.pco[0] + antDelta[0],
12766
+ off.pco[1] + antDelta[1],
12767
+ off.pco[2] + antDelta[2]
12768
+ ];
12769
+ const ecef = enuToEcef(total, latRad, lonRad);
12770
+ rangeM -= dot(e, ecef);
12771
+ const zenithDeg = 90 - geom.elRad * 180 / Math.PI;
12772
+ rangeM += interpPcv(off, zenithDeg);
12773
+ }
12774
+ }
12775
+ if (useTides && ctx) {
12776
+ rangeM -= dot(e, ctx.tideDisp);
12777
+ }
12778
+ if (useWindup && frame) {
12779
+ const wu = phaseWindup(geom.prn, e, frame, latRad, lonRad, windupState);
12780
+ const lambdaIf = 299792458 / (geom.f1 + geom.f2);
12781
+ phaseWindupM = wu * lambdaIf;
12782
+ }
12783
+ return { rangeM, phaseWindupM };
12784
+ }
12785
+ };
12786
+ }
12787
+
12788
+ // src/positioning/ppp.ts
12789
+ function matVec(P, h) {
12790
+ const n = P.length;
12791
+ const out = new Array(n).fill(0);
12792
+ for (let i = 0; i < n; i++) {
12793
+ let s = 0;
12794
+ const Pi = P[i];
12795
+ for (let j = 0; j < n; j++) s += Pi[j] * h[j];
12796
+ out[i] = s;
12797
+ }
12798
+ return out;
12799
+ }
12800
+ function ekfUpdateScalar(x, P, h, innovation, r, rejectK = 0) {
12801
+ const n = x.length;
12802
+ const Ph = matVec(P, h);
12803
+ let hPh = 0;
12804
+ for (let i = 0; i < n; i++) hPh += h[i] * Ph[i];
12805
+ const s = hPh + r;
12806
+ if (s <= 0) return false;
12807
+ if (rejectK > 0 && innovation * innovation > rejectK * rejectK * s) {
12808
+ return false;
12809
+ }
12810
+ const K = Ph.map((v) => v / s);
12811
+ for (let i = 0; i < n; i++) x[i] += K[i] * innovation;
12812
+ for (let i = 0; i < n; i++) {
12813
+ for (let j = 0; j < n; j++) {
12814
+ P[i][j] -= K[i] * Ph[j];
12815
+ }
12816
+ }
12817
+ for (let i = 0; i < n; i++) {
12818
+ for (let j = i + 1; j < n; j++) {
12819
+ const m = 0.5 * (P[i][j] + P[j][i]);
12820
+ P[i][j] = m;
12821
+ P[j][i] = m;
12822
+ }
12823
+ }
12824
+ return true;
12825
+ }
12826
+ function ifCoeffs(f1, f2) {
12827
+ const g = f1 * f1 / (f1 * f1 - f2 * f2);
12828
+ const lambdaIf = C_LIGHT / (f1 + f2);
12829
+ return { g, lambdaIf };
12830
+ }
12831
+ function sagnacRotate(x, y, z, travelS) {
12832
+ const theta = OMEGA_E * travelS;
12833
+ const c = Math.cos(theta);
12834
+ const s = Math.sin(theta);
12835
+ return [x * c + y * s, -x * s + y * c, z];
12836
+ }
12837
+ function satStateAtEmission(sp3, prn, recvTimeMs, rcv) {
12838
+ let travelS = 0.075;
12839
+ let sx = 0;
12840
+ let sy = 0;
12841
+ let sz = 0;
12842
+ let clkS = null;
12843
+ let velDotPos = 0;
12844
+ for (let iter = 0; iter < 3; iter++) {
12845
+ const tEmit = recvTimeMs - travelS * 1e3;
12846
+ const p = sp3Position(sp3, prn, tEmit);
12847
+ if (!p || p.clk == null) return null;
12848
+ const pPlus = sp3Position(sp3, prn, tEmit + 500);
12849
+ const pMinus = sp3Position(sp3, prn, tEmit - 500);
12850
+ if (pPlus && pMinus) {
12851
+ const vx = (pPlus.x - pMinus.x) / 1;
12852
+ const vy = (pPlus.y - pMinus.y) / 1;
12853
+ const vz = (pPlus.z - pMinus.z) / 1;
12854
+ velDotPos = p.x * vx + p.y * vy + p.z * vz;
12855
+ }
12856
+ clkS = p.clk;
12857
+ const [rx, ry, rz] = sagnacRotate(p.x, p.y, p.z, travelS);
12858
+ sx = rx;
12859
+ sy = ry;
12860
+ sz = rz;
12861
+ const dx = sx - rcv[0];
12862
+ const dy = sy - rcv[1];
12863
+ const dz = sz - rcv[2];
12864
+ const range = Math.hypot(dx, dy, dz);
12865
+ travelS = range / C_LIGHT;
12866
+ }
12867
+ if (clkS == null) return null;
12868
+ const relS = -2 * velDotPos / (C_LIGHT * C_LIGHT);
12869
+ const clkM = C_LIGHT * (clkS + relS);
12870
+ return { state: { x: sx, y: sy, z: sz, clkM }, travelS };
12871
+ }
12872
+ var IDX_X = 0;
12873
+ var IDX_Y = 1;
12874
+ var IDX_Z = 2;
12875
+ var IDX_CLK = 3;
12876
+ var IDX_ZWD = 4;
12877
+ var NBASE = 5;
12878
+ function solvePpp(epochs, sp3, opts) {
12879
+ const elevMask = (opts.elevationMaskDeg ?? 10) * Math.PI / 180;
12880
+ const codeSigma = opts.codeSigma ?? 3;
12881
+ const phaseSigma = opts.phaseSigma ?? 0.01;
12882
+ const ztdQ = opts.ztdProcessNoise ?? 1e-8;
12883
+ const corrections = opts.corrections;
12884
+ const x = [
12885
+ opts.aprioriPos[0],
12886
+ opts.aprioriPos[1],
12887
+ opts.aprioriPos[2],
12888
+ 0,
12889
+ // receiver clock (m)
12890
+ 0.1
12891
+ // zenith wet delay (m) a priori
12892
+ ];
12893
+ const P = [];
12894
+ for (let i = 0; i < NBASE; i++) {
12895
+ P.push(new Array(NBASE).fill(0));
12896
+ }
12897
+ P[IDX_X][IDX_X] = 100 * 100;
12898
+ P[IDX_Y][IDX_Y] = 100 * 100;
12899
+ P[IDX_Z][IDX_Z] = 100 * 100;
12900
+ P[IDX_CLK][IDX_CLK] = 1e10;
12901
+ P[IDX_ZWD][IDX_ZWD] = 0.5 * 0.5;
12902
+ const ambIdx = /* @__PURE__ */ new Map();
12903
+ const lastSeen = /* @__PURE__ */ new Map();
12904
+ const mwState = /* @__PURE__ */ new Map();
12905
+ const detectSlip = (o, ei) => {
12906
+ const lamW = C_LIGHT / (o.f1 - o.f2);
12907
+ const lw = o.l1 - o.l2;
12908
+ const pn = (o.f1 * o.c1 + o.f2 * o.c2) / (o.f1 + o.f2);
12909
+ const mw = lw - pn / lamW;
12910
+ const st = mwState.get(o.prn);
12911
+ let slip = o.slip;
12912
+ if (!st || ei - st.lastEi > 2)
12913
+ slip = true;
12914
+ else if (Math.abs(mw - st.mean) > 4) slip = true;
12915
+ if (slip) mwState.set(o.prn, { mean: mw, n: 1, lastEi: ei });
12916
+ else {
12917
+ const n = st.n + 1;
12918
+ mwState.set(o.prn, {
12919
+ mean: st.mean + (mw - st.mean) / Math.min(n, 100),
12920
+ n,
12921
+ lastEi: ei
12922
+ });
12923
+ }
12924
+ return slip;
12925
+ };
12926
+ const growState = (initVal, initVar) => {
12927
+ const idx = x.length;
12928
+ x.push(initVal);
12929
+ for (const row of P) row.push(0);
12930
+ P.push(new Array(x.length).fill(0));
12931
+ P[idx][idx] = initVar;
12932
+ return idx;
12933
+ };
12934
+ const dropState = (idx) => {
12935
+ x.splice(idx, 1);
12936
+ P.splice(idx, 1);
12937
+ for (const row of P) row.splice(idx, 1);
12938
+ for (const [prn, i] of ambIdx) {
12939
+ if (i === idx) ambIdx.delete(prn);
12940
+ else if (i > idx) ambIdx.set(prn, i - 1);
12941
+ }
12942
+ };
12943
+ let clockSeeded = false;
12944
+ const series = [];
12945
+ const truth = opts.groundTruth;
12946
+ const t0 = epochs[0]?.timeMs ?? 0;
12947
+ let convergenceSec = null;
12948
+ let convergedStreak = 0;
12949
+ for (let ei = 0; ei < epochs.length; ei++) {
12950
+ const epoch = epochs[ei];
12951
+ P[IDX_CLK][IDX_CLK] = 1e10;
12952
+ P[IDX_ZWD][IDX_ZWD] += ztdQ * (ei > 0 ? 1 : 0);
12953
+ const rcv = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
12954
+ const [latDeg, , heightM] = ecefToGeodetic(rcv[0], rcv[1], rcv[2]);
12955
+ const latRad = latDeg * Math.PI / 180;
12956
+ const epochCtx = corrections?.epochContext?.(epoch.timeMs, rcv) ?? null;
12957
+ const vis = [];
12958
+ for (const o of epoch.obs) {
12959
+ if (o.c1 === 0 || o.c2 === 0 || o.l1 === 0 || o.l2 === 0) continue;
12960
+ const slip = detectSlip(o, ei);
12961
+ const sat = satStateAtEmission(sp3, o.prn, epoch.timeMs, rcv);
12962
+ if (!sat) continue;
12963
+ const [elRad, azRad] = getAer(
12964
+ sat.state.x,
12965
+ sat.state.y,
12966
+ sat.state.z,
12967
+ rcv[0],
12968
+ rcv[1],
12969
+ rcv[2]
12970
+ );
12971
+ if (elRad < elevMask) continue;
12972
+ const dx = sat.state.x - rcv[0];
12973
+ const dy = sat.state.y - rcv[1];
12974
+ const dz = sat.state.z - rcv[2];
12975
+ const range = Math.hypot(dx, dy, dz);
12976
+ const e = [dx / range, dy / range, dz / range];
12977
+ const { g } = ifCoeffs(o.f1, o.f2);
12978
+ const lam1 = C_LIGHT / o.f1;
12979
+ const lam2 = C_LIGHT / o.f2;
12980
+ if (o.c1 === 0 || o.c2 === 0 || o.l1 === 0 || o.l2 === 0) continue;
12981
+ const pIf = g * o.c1 - (g - 1) * o.c2;
12982
+ const lIf = g * (o.l1 * lam1) - (g - 1) * (o.l2 * lam2);
12983
+ if (!Number.isFinite(pIf) || !Number.isFinite(lIf)) continue;
12984
+ const trop = opts.troposphere ?? true ? niellMapping(elRad, latRad, heightM, epoch.timeMs) : { zhd: 0, mh: 0, mw: 0 };
12985
+ const { zhd, mh, mw } = trop;
12986
+ const geom = {
12987
+ prn: o.prn,
12988
+ satEcef: [sat.state.x, sat.state.y, sat.state.z],
12989
+ rcvEcef: rcv,
12990
+ los: e,
12991
+ elRad,
12992
+ azRad,
12993
+ f1: o.f1,
12994
+ f2: o.f2,
12995
+ band1: o.band1,
12996
+ band2: o.band2,
12997
+ g
12998
+ };
12999
+ const corr = corrections ? applyCorrections(corrections, geom, epochCtx) : { rangeM: 0, phaseWindupM: 0 };
13000
+ vis.push({
13001
+ prn: o.prn,
13002
+ e,
13003
+ elRad,
13004
+ pIf,
13005
+ lIf,
13006
+ satClkM: sat.state.clkM,
13007
+ range,
13008
+ tropoFixed: zhd * mh + corr.rangeM,
13009
+ mw,
13010
+ windupM: corr.phaseWindupM,
13011
+ slip
13012
+ });
13013
+ }
13014
+ const zwd0 = x[IDX_ZWD];
13015
+ if (vis.length > 0) {
13016
+ if (!clockSeeded) {
13017
+ const s = vis.map(
13018
+ (v) => v.pIf - (v.range - v.satClkM + v.tropoFixed + zwd0 * v.mw)
13019
+ ).sort((a, b) => a - b);
13020
+ x[IDX_CLK] = s[Math.floor(s.length / 2)];
13021
+ clockSeeded = true;
13022
+ }
13023
+ P[IDX_CLK][IDX_CLK] = 1e10;
13024
+ }
13025
+ let nSats = 0;
13026
+ let sumPhaseResSq = 0;
13027
+ let nPhaseRes = 0;
13028
+ for (const v of vis) {
13029
+ const common = v.range + x[IDX_CLK] - v.satClkM + v.tropoFixed + zwd0 * v.mw;
13030
+ let ai = ambIdx.get(v.prn);
13031
+ if (ai === void 0 || v.slip) {
13032
+ const bInit = v.lIf - (common + v.windupM);
13033
+ if (ai === void 0) {
13034
+ ai = growState(bInit, 100 * 100);
13035
+ } else {
13036
+ x[ai] = bInit;
13037
+ for (let j = 0; j < P.length; j++) {
13038
+ P[ai][j] = 0;
13039
+ P[j][ai] = 0;
13040
+ }
13041
+ P[ai][ai] = 100 * 100;
13042
+ }
13043
+ ambIdx.set(v.prn, ai);
13044
+ }
13045
+ const n = x.length;
13046
+ const sinEl = Math.max(Math.sin(v.elRad), 0.1);
13047
+ const rCode = (codeSigma / sinEl) ** 2;
13048
+ const rPhase = (phaseSigma / sinEl) ** 2;
13049
+ {
13050
+ const h = new Array(n).fill(0);
13051
+ h[IDX_X] = -v.e[0];
13052
+ h[IDX_Y] = -v.e[1];
13053
+ h[IDX_Z] = -v.e[2];
13054
+ h[IDX_CLK] = 1;
13055
+ h[IDX_ZWD] = v.mw;
13056
+ const innov = v.pIf - common;
13057
+ if (Math.abs(innov) < 1e6) ekfUpdateScalar(x, P, h, innov, rCode, 4);
13058
+ }
13059
+ {
13060
+ const commonPh = v.range + x[IDX_CLK] - v.satClkM + v.tropoFixed + x[IDX_ZWD] * v.mw;
13061
+ const innov = v.lIf - (commonPh + v.windupM + x[ai]);
13062
+ if (Math.abs(innov) > 0.15) {
13063
+ x[ai] = v.lIf - (commonPh + v.windupM);
13064
+ for (let j = 0; j < P.length; j++) {
13065
+ P[ai][j] = 0;
13066
+ P[j][ai] = 0;
13067
+ }
13068
+ P[ai][ai] = 100 * 100;
13069
+ } else {
13070
+ const h = new Array(n).fill(0);
13071
+ h[IDX_X] = -v.e[0];
13072
+ h[IDX_Y] = -v.e[1];
13073
+ h[IDX_Z] = -v.e[2];
13074
+ h[IDX_CLK] = 1;
13075
+ h[IDX_ZWD] = v.mw;
13076
+ h[ai] = 1;
13077
+ if (ekfUpdateScalar(x, P, h, innov, rPhase, 4)) {
13078
+ sumPhaseResSq += innov * innov;
13079
+ nPhaseRes++;
13080
+ }
13081
+ }
13082
+ }
13083
+ lastSeen.set(v.prn, ei);
13084
+ nSats++;
13085
+ }
13086
+ for (const [prn, seen] of [...lastSeen]) {
13087
+ if (ei - seen > 20) {
13088
+ const idx = ambIdx.get(prn);
13089
+ if (idx !== void 0) dropState(idx);
13090
+ lastSeen.delete(prn);
13091
+ }
13092
+ }
13093
+ const pos = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
13094
+ let enu = null;
13095
+ let error3d = null;
13096
+ if (truth) {
13097
+ enu = getEnuDifference(
13098
+ pos[0],
13099
+ pos[1],
13100
+ pos[2],
13101
+ truth[0],
13102
+ truth[1],
13103
+ truth[2]
13104
+ );
13105
+ error3d = Math.hypot(
13106
+ pos[0] - truth[0],
13107
+ pos[1] - truth[1],
13108
+ pos[2] - truth[2]
13109
+ );
13110
+ if (error3d < 0.1) {
13111
+ convergedStreak++;
13112
+ if (convergedStreak >= 10 && convergenceSec == null) {
13113
+ convergenceSec = (epoch.timeMs - t0) / 1e3;
13114
+ }
13115
+ } else {
13116
+ convergedStreak = 0;
13117
+ convergenceSec = null;
13118
+ }
13119
+ }
13120
+ series.push({
13121
+ timeMs: epoch.timeMs,
13122
+ enu,
13123
+ error3d,
13124
+ position: pos,
13125
+ nSats,
13126
+ ztdWet: x[IDX_ZWD],
13127
+ phaseResRms: nPhaseRes > 0 ? Math.sqrt(sumPhaseResSq / nPhaseRes) : 0
13128
+ });
13129
+ }
13130
+ const finalPos = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
13131
+ const llh = ecefToGeodetic(finalPos[0], finalPos[1], finalPos[2]);
13132
+ const finalError3d = truth ? Math.hypot(
13133
+ finalPos[0] - truth[0],
13134
+ finalPos[1] - truth[1],
13135
+ finalPos[2] - truth[2]
13136
+ ) : null;
13137
+ return {
13138
+ position: finalPos,
13139
+ llh,
13140
+ series,
13141
+ ztdWet: x[IDX_ZWD],
13142
+ epochsUsed: epochs.length,
13143
+ convergenceSec,
13144
+ finalError3d
13145
+ };
13146
+ }
13147
+
11909
13148
  // src/positioning/index.ts
11910
13149
  var GM_GPS3 = 3986005e8;
11911
13150
  var F_REL = -4442807633e-19;
@@ -12046,7 +13285,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
12046
13285
  let ionoM = 0;
12047
13286
  if ((gim || iono) && positionSane) {
12048
13287
  let l1M = null;
12049
- if (gim) l1M = gimSlantIonoDelayL1(gim, rxLat, rxLon, azim, elev, timeMs);
13288
+ if (gim)
13289
+ l1M = gimSlantIonoDelayL1(gim, rxLat, rxLon, azim, elev, timeMs);
12050
13290
  if (l1M === null && iono) {
12051
13291
  l1M = C_LIGHT * klobucharDelay(iono, rxLat, rxLon, azim, elev, gpsTow);
12052
13292
  }
@@ -14490,6 +15730,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
14490
15730
  binexRecords,
14491
15731
  buildGloChannelMap,
14492
15732
  buildObsIndices,
15733
+ buildPppAntenna,
14493
15734
  clampUnit,
14494
15735
  computeAllPositions,
14495
15736
  computeDop,
@@ -14503,6 +15744,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
14503
15744
  connectToMountpoint,
14504
15745
  crc32,
14505
15746
  createGzipLineSink,
15747
+ createPppCorrections,
14506
15748
  createStationMeta,
14507
15749
  createStreamStats,
14508
15750
  crxDecompress,
@@ -14588,6 +15830,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
14588
15830
  glonassAlmanacEpochMs,
14589
15831
  glonassAlmanacPosition,
14590
15832
  glonassPosition,
15833
+ gmst,
14591
15834
  greatCircleMidpoint,
14592
15835
  hdrLine,
14593
15836
  horizonDistance,
@@ -14597,8 +15840,10 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
14597
15840
  lambdaReduction,
14598
15841
  lambdaSearch,
14599
15842
  maskRadForAzimuth,
15843
+ moonEcef,
14600
15844
  msmEpochToDate,
14601
15845
  navTimesFromEph,
15846
+ niellMapping,
14602
15847
  nmeaCoordToDecimal,
14603
15848
  oem4Frames,
14604
15849
  padL,
@@ -14648,10 +15893,13 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
14648
15893
  selectEphemeris,
14649
15894
  setGloFreqNumber,
14650
15895
  setRtcm3DebugHandler,
15896
+ solidEarthTide,
14651
15897
  solveDgnss,
15898
+ solvePpp,
14652
15899
  solveSpp,
14653
15900
  sp3Position,
14654
15901
  stationHeaderLines,
15902
+ sunEcef,
14655
15903
  systemCmp,
14656
15904
  systemName,
14657
15905
  toRtkEpoch,
@@ -14662,6 +15910,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
14662
15910
  updateStreamStats,
14663
15911
  verifyChecksum,
14664
15912
  vincenty,
15913
+ writeCrx,
14665
15914
  writeModernObsBlob,
14666
15915
  writeRinex2ObsBlob,
14667
15916
  writeRinex4ObsBlob,