gnss-js 1.47.0 → 1.49.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.
@@ -22,6 +22,7 @@ var positioning_exports = {};
22
22
  __export(positioning_exports, {
23
23
  IONO_L1_M_PER_TECU: () => IONO_L1_M_PER_TECU,
24
24
  RtkFloatEngine: () => RtkFloatEngine,
25
+ SbasProcessor: () => SbasProcessor,
25
26
  buildPppAntenna: () => buildPppAntenna,
26
27
  createPppCorrections: () => createPppCorrections,
27
28
  estimateNarrowlaneFcb: () => estimateNarrowlaneFcb,
@@ -450,8 +451,8 @@ function computeSatPosition(eph, timeMs) {
450
451
  return glonassPosition(eph, (timeMs - leapMs) / 1e3);
451
452
  }
452
453
  const GPS_EPOCH = START_GPS_TIME.getTime();
453
- const gpsSeconds = (timeMs - GPS_EPOCH) / 1e3;
454
- let tow = gpsSeconds % (7 * 86400);
454
+ const gpsSeconds2 = (timeMs - GPS_EPOCH) / 1e3;
455
+ let tow = gpsSeconds2 % (7 * 86400);
455
456
  if (eph.system === "C") tow = (tow - 14 + 604800) % 604800;
456
457
  return keplerPosition(eph, tow);
457
458
  }
@@ -501,8 +502,8 @@ function klobucharDelay(coeffs, latRad, lonRad, azRad, elRad, gpsTowSec) {
501
502
  const F = 1 + 16 * Math.pow(0.53 - E, 3);
502
503
  const x = 2 * Math.PI * (t - 50400) / per;
503
504
  if (Math.abs(x) >= 1.57) return F * 5e-9;
504
- const x2 = x * x;
505
- return F * (5e-9 + amp * (1 - x2 / 2 + x2 * x2 / 24));
505
+ const x22 = x * x;
506
+ return F * (5e-9 + amp * (1 - x22 / 2 + x22 * x22 / 24));
506
507
  }
507
508
 
508
509
  // src/positioning/gim.ts
@@ -1654,6 +1655,515 @@ function postProcessRtk(base, rover, ephemerides, baseEcef, opts = {}) {
1654
1655
  return { track, solved, unmatched, fixRate: solved ? fixed / solved : 0 };
1655
1656
  }
1656
1657
 
1658
+ // src/navbits/index.ts
1659
+ var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
1660
+ var SEC_PER_WEEK = 7 * 86400;
1661
+ function getBitU(buff, pos, len) {
1662
+ let bits = 0;
1663
+ for (let i = pos; i < pos + len; i++) {
1664
+ bits = bits * 2 + (buff[i >> 3] >> 7 - (i & 7) & 1);
1665
+ }
1666
+ return bits;
1667
+ }
1668
+ function getBitS(buff, pos, len) {
1669
+ const bits = getBitU(buff, pos, len);
1670
+ if (len <= 0 || bits < 2 ** (len - 1)) return bits;
1671
+ return bits - 2 ** len;
1672
+ }
1673
+
1674
+ // src/navbits/sbas.ts
1675
+ var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
1676
+ var MS_PER_WEEK2 = 7 * 86400 * 1e3;
1677
+ function sbasMessageType(msg) {
1678
+ return getBitU(msg, 8, 6);
1679
+ }
1680
+
1681
+ // src/positioning/sbas.ts
1682
+ var P2_11 = 2 ** -11;
1683
+ var P2_31 = 2 ** -31;
1684
+ var P2_39 = 2 ** -39;
1685
+ var D2R = Math.PI / 180;
1686
+ var R2D = 180 / Math.PI;
1687
+ var SEC_PER_WEEK2 = 604800;
1688
+ var MAXSBSAGEF = 30;
1689
+ var MAXSBSAGEL = 1800;
1690
+ var MAXBAND = 10;
1691
+ var two = (n) => String(n).padStart(2, "0");
1692
+ var VAR_FCORR = [
1693
+ 0.052,
1694
+ 0.0924,
1695
+ 0.1444,
1696
+ 0.283,
1697
+ 0.4678,
1698
+ 0.8315,
1699
+ 1.2992,
1700
+ 1.8709,
1701
+ 2.5465,
1702
+ 3.326,
1703
+ 5.1968,
1704
+ 20.787,
1705
+ 230.9661,
1706
+ 2078.695
1707
+ ];
1708
+ var varfcorr = (udre) => udre > 0 && udre <= 14 ? VAR_FCORR[udre - 1] : 0;
1709
+ var VAR_ICORR = [
1710
+ 84e-4,
1711
+ 0.0333,
1712
+ 0.0749,
1713
+ 0.1331,
1714
+ 0.2079,
1715
+ 0.2994,
1716
+ 0.4075,
1717
+ 0.5322,
1718
+ 0.6735,
1719
+ 0.8315,
1720
+ 1.1974,
1721
+ 1.8709,
1722
+ 3.326,
1723
+ 20.787,
1724
+ 187.0826
1725
+ ];
1726
+ var varicorr = (give) => give > 0 && give <= 15 ? VAR_ICORR[give - 1] : 0;
1727
+ var DEGF = [
1728
+ 0,
1729
+ 5e-5,
1730
+ 9e-5,
1731
+ 12e-5,
1732
+ 15e-5,
1733
+ 2e-4,
1734
+ 3e-4,
1735
+ 45e-5,
1736
+ 6e-4,
1737
+ 9e-4,
1738
+ 15e-4,
1739
+ 21e-4,
1740
+ 27e-4,
1741
+ 33e-4,
1742
+ 46e-4,
1743
+ 58e-4
1744
+ ];
1745
+ var degfcorr = (ai) => ai > 0 && ai <= 15 ? DEGF[ai] : 58e-4;
1746
+ var x1 = [-75, -65, -55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 65, 75, 85];
1747
+ var x2 = [-55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55];
1748
+ var x3 = [-75, -65, -55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 65, 75];
1749
+ var x4 = [-85, -75, -65, -55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 65, 75];
1750
+ var x5 = [-180, -175, -170, -165, -160, -155, -150, -145, -140, -135, -130, -125, -120, -115, -110, -105, -100, -95, -90, -85, -80, -75, -70, -65, -60, -55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175];
1751
+ var x6 = [-180, -170, -160, -150, -140, -130, -120, -110, -100, -90, -80, -70, -60, -50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170];
1752
+ var x7 = [-180, -150, -120, -90, -60, -30, 0, 30, 60, 90, 120, 150];
1753
+ var x8 = [-170, -140, -110, -80, -50, -20, 10, 40, 70, 100, 130, 160];
1754
+ var igpband1 = [
1755
+ [{ coord: -180, arr: x1, bits: 1, bite: 28 }, { coord: -175, arr: x2, bits: 29, bite: 51 }, { coord: -170, arr: x3, bits: 52, bite: 78 }, { coord: -165, arr: x2, bits: 79, bite: 101 }, { coord: -160, arr: x3, bits: 102, bite: 128 }, { coord: -155, arr: x2, bits: 129, bite: 151 }, { coord: -150, arr: x3, bits: 152, bite: 178 }, { coord: -145, arr: x2, bits: 179, bite: 201 }],
1756
+ [{ coord: -140, arr: x4, bits: 1, bite: 28 }, { coord: -135, arr: x2, bits: 29, bite: 51 }, { coord: -130, arr: x3, bits: 52, bite: 78 }, { coord: -125, arr: x2, bits: 79, bite: 101 }, { coord: -120, arr: x3, bits: 102, bite: 128 }, { coord: -115, arr: x2, bits: 129, bite: 151 }, { coord: -110, arr: x3, bits: 152, bite: 178 }, { coord: -105, arr: x2, bits: 179, bite: 201 }],
1757
+ [{ coord: -100, arr: x3, bits: 1, bite: 27 }, { coord: -95, arr: x2, bits: 28, bite: 50 }, { coord: -90, arr: x1, bits: 51, bite: 78 }, { coord: -85, arr: x2, bits: 79, bite: 101 }, { coord: -80, arr: x3, bits: 102, bite: 128 }, { coord: -75, arr: x2, bits: 129, bite: 151 }, { coord: -70, arr: x3, bits: 152, bite: 178 }, { coord: -65, arr: x2, bits: 179, bite: 201 }],
1758
+ [{ coord: -60, arr: x3, bits: 1, bite: 27 }, { coord: -55, arr: x2, bits: 28, bite: 50 }, { coord: -50, arr: x4, bits: 51, bite: 78 }, { coord: -45, arr: x2, bits: 79, bite: 101 }, { coord: -40, arr: x3, bits: 102, bite: 128 }, { coord: -35, arr: x2, bits: 129, bite: 151 }, { coord: -30, arr: x3, bits: 152, bite: 178 }, { coord: -25, arr: x2, bits: 179, bite: 201 }],
1759
+ [{ coord: -20, arr: x3, bits: 1, bite: 27 }, { coord: -15, arr: x2, bits: 28, bite: 50 }, { coord: -10, arr: x3, bits: 51, bite: 77 }, { coord: -5, arr: x2, bits: 78, bite: 100 }, { coord: 0, arr: x1, bits: 101, bite: 128 }, { coord: 5, arr: x2, bits: 129, bite: 151 }, { coord: 10, arr: x3, bits: 152, bite: 178 }, { coord: 15, arr: x2, bits: 179, bite: 201 }],
1760
+ [{ coord: 20, arr: x3, bits: 1, bite: 27 }, { coord: 25, arr: x2, bits: 28, bite: 50 }, { coord: 30, arr: x3, bits: 51, bite: 77 }, { coord: 35, arr: x2, bits: 78, bite: 100 }, { coord: 40, arr: x4, bits: 101, bite: 128 }, { coord: 45, arr: x2, bits: 129, bite: 151 }, { coord: 50, arr: x3, bits: 152, bite: 178 }, { coord: 55, arr: x2, bits: 179, bite: 201 }],
1761
+ [{ coord: 60, arr: x3, bits: 1, bite: 27 }, { coord: 65, arr: x2, bits: 28, bite: 50 }, { coord: 70, arr: x3, bits: 51, bite: 77 }, { coord: 75, arr: x2, bits: 78, bite: 100 }, { coord: 80, arr: x3, bits: 101, bite: 127 }, { coord: 85, arr: x2, bits: 128, bite: 150 }, { coord: 90, arr: x1, bits: 151, bite: 178 }, { coord: 95, arr: x2, bits: 179, bite: 201 }],
1762
+ [{ coord: 100, arr: x3, bits: 1, bite: 27 }, { coord: 105, arr: x2, bits: 28, bite: 50 }, { coord: 110, arr: x3, bits: 51, bite: 77 }, { coord: 115, arr: x2, bits: 78, bite: 100 }, { coord: 120, arr: x3, bits: 101, bite: 127 }, { coord: 125, arr: x2, bits: 128, bite: 150 }, { coord: 130, arr: x4, bits: 151, bite: 178 }, { coord: 135, arr: x2, bits: 179, bite: 201 }],
1763
+ [{ coord: 140, arr: x3, bits: 1, bite: 27 }, { coord: 145, arr: x2, bits: 28, bite: 50 }, { coord: 150, arr: x3, bits: 51, bite: 77 }, { coord: 155, arr: x2, bits: 78, bite: 100 }, { coord: 160, arr: x3, bits: 101, bite: 127 }, { coord: 165, arr: x2, bits: 128, bite: 150 }, { coord: 170, arr: x3, bits: 151, bite: 177 }, { coord: 175, arr: x2, bits: 178, bite: 200 }]
1764
+ ];
1765
+ var igpband2 = [
1766
+ [{ coord: 60, arr: x5, bits: 1, bite: 72 }, { coord: 65, arr: x6, bits: 73, bite: 108 }, { coord: 70, arr: x6, bits: 109, bite: 144 }, { coord: 75, arr: x6, bits: 145, bite: 180 }, { coord: 85, arr: x7, bits: 181, bite: 192 }],
1767
+ [{ coord: -60, arr: x5, bits: 1, bite: 72 }, { coord: -65, arr: x6, bits: 73, bite: 108 }, { coord: -70, arr: x6, bits: 109, bite: 144 }, { coord: -75, arr: x6, bits: 145, bite: 180 }, { coord: -85, arr: x8, bits: 181, bite: 192 }]
1768
+ ];
1769
+ var gpsSeconds = (week, tow) => week * SEC_PER_WEEK2 + tow;
1770
+ function maskPrn(i) {
1771
+ if (i >= 1 && i <= 37) return `G${two(i)}`;
1772
+ if (i >= 38 && i <= 61) return `R${two(i - 37)}`;
1773
+ if (i >= 120 && i <= 138) return `S${two(i - 100)}`;
1774
+ if (i >= 193 && i <= 202) return `J${two(i - 192)}`;
1775
+ return null;
1776
+ }
1777
+ function ionppp(latRad, lonRad, azRad, elRad, re, hion) {
1778
+ const rp = re / (re + hion) * Math.cos(elRad);
1779
+ const ap = Math.PI / 2 - elRad - Math.asin(rp);
1780
+ const sinap = Math.sin(ap);
1781
+ const tanap = Math.tan(ap);
1782
+ const cosaz = Math.cos(azRad);
1783
+ const latP = Math.asin(
1784
+ Math.sin(latRad) * Math.cos(ap) + Math.cos(latRad) * sinap * cosaz
1785
+ );
1786
+ let lonP;
1787
+ if (latRad > 70 * D2R && tanap * cosaz > Math.tan(Math.PI / 2 - latRad) || latRad < -70 * D2R && -tanap * cosaz > Math.tan(Math.PI / 2 + latRad)) {
1788
+ lonP = lonRad + Math.PI - Math.asin(sinap * Math.sin(azRad) / Math.cos(latP));
1789
+ } else {
1790
+ lonP = lonRad + Math.asin(sinap * Math.sin(azRad) / Math.cos(latP));
1791
+ }
1792
+ return { fp: 1 / Math.sqrt(1 - rp * rp), latP, lonP };
1793
+ }
1794
+ var SbasProcessor = class {
1795
+ sats = [];
1796
+ satIdx = /* @__PURE__ */ new Map();
1797
+ iodp = -1;
1798
+ tlat = 0;
1799
+ ion = [];
1800
+ /** GEO PRN(s) whose messages have been ingested. */
1801
+ geoPrns = /* @__PURE__ */ new Set();
1802
+ /**
1803
+ * Ingest one decoded SBAS L1 message (250-bit, MSB-first) received at the
1804
+ * given GPS week / time-of-week. Returns the message type, or -1 if it was
1805
+ * not a recognised/applicable correction message.
1806
+ */
1807
+ update(msg, week, tow, geoPrn) {
1808
+ if (week === 0) return -1;
1809
+ const type = sbasMessageType(msg);
1810
+ if (geoPrn != null && geoPrn >= 120 && geoPrn <= 158)
1811
+ this.geoPrns.add(`S${two(geoPrn - 100)}`);
1812
+ switch (type) {
1813
+ case 1:
1814
+ return this.decodeMask(msg) ? type : -1;
1815
+ case 0:
1816
+ case 2:
1817
+ case 3:
1818
+ case 4:
1819
+ case 5:
1820
+ return this.decodeFast(msg, type, week, tow) ? type : -1;
1821
+ case 6:
1822
+ return this.decodeIntegrity(msg) ? type : -1;
1823
+ case 7:
1824
+ return this.decodeDegradation(msg) ? type : -1;
1825
+ case 18:
1826
+ return this.decodeIgpMask(msg) ? type : -1;
1827
+ case 24:
1828
+ return this.decodeMixed(msg, week, tow) ? type : -1;
1829
+ case 25:
1830
+ return this.decodeLongCorrHalf(msg, 14, week, tow) && this.decodeLongCorrHalf(msg, 120, week, tow) ? type : -1;
1831
+ case 26:
1832
+ return this.decodeIgpDelay(msg, week, tow) ? type : -1;
1833
+ case 9:
1834
+ // GEO nav — handled by decodeSbasGeoNav elsewhere
1835
+ case 63:
1836
+ return type;
1837
+ default:
1838
+ return -1;
1839
+ }
1840
+ }
1841
+ decodeMask(msg) {
1842
+ const sats = [];
1843
+ for (let i = 1; i <= 210; i++) {
1844
+ if (getBitU(msg, 13 + i, 1)) sats.push({ prn: maskPrn(i) });
1845
+ }
1846
+ this.iodp = getBitU(msg, 224, 2);
1847
+ this.sats = sats;
1848
+ this.satIdx.clear();
1849
+ sats.forEach((s, k) => {
1850
+ if (s.prn) this.satIdx.set(s.prn, k);
1851
+ });
1852
+ return true;
1853
+ }
1854
+ decodeFast(msg, type, week, tow) {
1855
+ if (this.iodp !== getBitU(msg, 16, 2)) return false;
1856
+ const iodf = getBitU(msg, 14, 2);
1857
+ const t0s = gpsSeconds(week, tow);
1858
+ for (let i = 0; i < 13; i++) {
1859
+ const j = 13 * ((type === 0 ? 2 : type) - 2) + i;
1860
+ if (j >= this.sats.length) break;
1861
+ const udre = getBitU(msg, 174 + 4 * i, 4);
1862
+ const prc = getBitS(msg, 18 + i * 12, 12) * 0.125;
1863
+ const prev = this.sats[j].fcorr;
1864
+ this.sats[j].fcorr = {
1865
+ prc,
1866
+ udre: udre + 1,
1867
+ ai: prev?.ai ?? 0,
1868
+ iodf,
1869
+ t0s
1870
+ };
1871
+ }
1872
+ return true;
1873
+ }
1874
+ decodeIntegrity(msg) {
1875
+ const iodf = [0, 1, 2, 3].map((i) => getBitU(msg, 14 + i * 2, 2));
1876
+ for (let i = 0; i < this.sats.length; i++) {
1877
+ const f = this.sats[i].fcorr;
1878
+ if (!f || f.iodf !== iodf[Math.floor(i / 13)]) continue;
1879
+ f.udre = getBitU(msg, 22 + i * 4, 4) + 1;
1880
+ }
1881
+ return true;
1882
+ }
1883
+ decodeDegradation(msg) {
1884
+ if (this.iodp !== getBitU(msg, 18, 2)) return false;
1885
+ this.tlat = getBitU(msg, 14, 4);
1886
+ for (let i = 0; i < this.sats.length; i++) {
1887
+ const ai = getBitU(msg, 22 + i * 4, 4);
1888
+ if (this.sats[i].fcorr) this.sats[i].fcorr.ai = ai;
1889
+ else this.sats[i].fcorr = { prc: 0, udre: 0, ai, iodf: -1, t0s: 0 };
1890
+ }
1891
+ return true;
1892
+ }
1893
+ decodeMixed(msg, week, tow) {
1894
+ if (this.iodp !== getBitU(msg, 110, 2)) return false;
1895
+ const blk = getBitU(msg, 112, 2);
1896
+ const iodf = getBitU(msg, 114, 2);
1897
+ const t0s = gpsSeconds(week, tow);
1898
+ for (let i = 0; i < 6; i++) {
1899
+ const j = 13 * blk + i;
1900
+ if (j >= this.sats.length) break;
1901
+ const udre = getBitU(msg, 86 + 4 * i, 4);
1902
+ const prc = getBitS(msg, 14 + i * 12, 12) * 0.125;
1903
+ const prev = this.sats[j].fcorr;
1904
+ this.sats[j].fcorr = {
1905
+ prc,
1906
+ udre: udre + 1,
1907
+ ai: prev?.ai ?? 0,
1908
+ iodf,
1909
+ t0s
1910
+ };
1911
+ }
1912
+ return this.decodeLongCorrHalf(msg, 120, week, tow);
1913
+ }
1914
+ decodeLongCorrHalf(msg, p, week, tow) {
1915
+ if (getBitU(msg, p, 1) === 0) {
1916
+ if (this.iodp === getBitU(msg, p + 103, 2)) {
1917
+ const a = this.decodeLong0(msg, p + 1, week, tow);
1918
+ const b = this.decodeLong0(msg, p + 52, week, tow);
1919
+ return a || b;
1920
+ }
1921
+ } else if (this.iodp === getBitU(msg, p + 104, 2)) {
1922
+ return this.decodeLong1(msg, p + 1, week, tow);
1923
+ }
1924
+ return false;
1925
+ }
1926
+ decodeLong0(msg, p, week, tow) {
1927
+ const n = getBitU(msg, p, 6);
1928
+ if (n === 0 || n > this.sats.length) return false;
1929
+ this.sats[n - 1].lcorr = {
1930
+ iode: getBitU(msg, p + 6, 8),
1931
+ dpos: [
1932
+ getBitS(msg, p + 14, 9) * 0.125,
1933
+ getBitS(msg, p + 23, 9) * 0.125,
1934
+ getBitS(msg, p + 32, 9) * 0.125
1935
+ ],
1936
+ dvel: [0, 0, 0],
1937
+ daf0: getBitS(msg, p + 41, 10) * P2_31,
1938
+ daf1: 0,
1939
+ t0s: gpsSeconds(week, tow)
1940
+ };
1941
+ return true;
1942
+ }
1943
+ decodeLong1(msg, p, week, tow) {
1944
+ const n = getBitU(msg, p, 6);
1945
+ if (n === 0 || n > this.sats.length) return false;
1946
+ let t = getBitU(msg, p + 90, 13) * 16 - tow % 86400;
1947
+ if (t <= -43200) t += 86400;
1948
+ else if (t > 43200) t -= 86400;
1949
+ this.sats[n - 1].lcorr = {
1950
+ iode: getBitU(msg, p + 6, 8),
1951
+ dpos: [
1952
+ getBitS(msg, p + 14, 11) * 0.125,
1953
+ getBitS(msg, p + 25, 11) * 0.125,
1954
+ getBitS(msg, p + 36, 11) * 0.125
1955
+ ],
1956
+ dvel: [
1957
+ getBitS(msg, p + 58, 8) * P2_11,
1958
+ getBitS(msg, p + 66, 8) * P2_11,
1959
+ getBitS(msg, p + 74, 8) * P2_11
1960
+ ],
1961
+ daf0: getBitS(msg, p + 47, 11) * P2_31,
1962
+ daf1: getBitS(msg, p + 82, 8) * P2_39,
1963
+ t0s: gpsSeconds(week, tow + t)
1964
+ };
1965
+ return true;
1966
+ }
1967
+ decodeIgpMask(msg) {
1968
+ const band = getBitU(msg, 18, 4);
1969
+ let p;
1970
+ let m;
1971
+ if (band <= 8) {
1972
+ p = igpband1[band];
1973
+ m = 8;
1974
+ } else if (band <= 10) {
1975
+ p = igpband2[band - 9];
1976
+ m = 5;
1977
+ } else return false;
1978
+ const igp = [];
1979
+ for (let i = 1; i <= 201; i++) {
1980
+ if (!getBitU(msg, 23 + i, 1)) continue;
1981
+ for (let j = 0; j < m; j++) {
1982
+ const b = p[j];
1983
+ if (i < b.bits || b.bite < i) continue;
1984
+ const varying = b.arr[i - b.bits];
1985
+ igp.push({
1986
+ lat: band <= 8 ? varying : b.coord,
1987
+ lon: band <= 8 ? b.coord : varying,
1988
+ delay: 0,
1989
+ give: 0,
1990
+ t0s: 0
1991
+ });
1992
+ break;
1993
+ }
1994
+ }
1995
+ this.ion[band] = { iodi: getBitU(msg, 22, 2), igp };
1996
+ return true;
1997
+ }
1998
+ decodeIgpDelay(msg, week, tow) {
1999
+ const band = getBitU(msg, 14, 4);
2000
+ const b = this.ion[band];
2001
+ if (band > MAXBAND || !b || b.iodi !== getBitU(msg, 217, 2)) return false;
2002
+ const block = getBitU(msg, 18, 4);
2003
+ const t0s = gpsSeconds(week, tow);
2004
+ for (let i = 0; i < 15; i++) {
2005
+ const j = block * 15 + i;
2006
+ if (j >= b.igp.length) continue;
2007
+ const give = getBitU(msg, 22 + i * 13 + 9, 4);
2008
+ const delay = getBitU(msg, 22 + i * 13, 9);
2009
+ b.igp[j].delay = delay === 511 ? 0 : delay * 0.125;
2010
+ b.igp[j].give = give + 1 >= 16 ? 0 : give + 1;
2011
+ b.igp[j].t0s = t0s;
2012
+ }
2013
+ return true;
2014
+ }
2015
+ /** PRNs currently in the correction mask (in mask order). */
2016
+ activeSats() {
2017
+ return this.sats.map((s) => s.prn).filter((p) => !!p);
2018
+ }
2019
+ /** Number of ionospheric grid points with a valid delay across all bands. */
2020
+ ionoGridPoints() {
2021
+ let n = 0;
2022
+ for (const b of this.ion) if (b) {
2023
+ for (const g of b.igp) if (g.give > 0) n++;
2024
+ }
2025
+ return n;
2026
+ }
2027
+ /**
2028
+ * Satellite range/clock correction for a PRN at a GPS week/tow, or null if
2029
+ * no valid (unexpired) long-term + fast correction is available. Add `dPos`
2030
+ * to the satellite ECEF position and `dClkS` to its clock bias.
2031
+ */
2032
+ satCorrection(prn, week, tow) {
2033
+ const idx = this.satIdx.get(prn);
2034
+ if (idx === void 0) return null;
2035
+ const s = this.sats[idx];
2036
+ const now = gpsSeconds(week, tow);
2037
+ const drs = [0, 0, 0];
2038
+ let dclk = 0;
2039
+ let iode = -1;
2040
+ if (s.lcorr && s.lcorr.t0s !== 0) {
2041
+ const t = now - s.lcorr.t0s;
2042
+ if (Math.abs(t) > MAXSBSAGEL) return null;
2043
+ for (let i = 0; i < 3; i++)
2044
+ drs[i] = s.lcorr.dpos[i] + s.lcorr.dvel[i] * t;
2045
+ dclk = s.lcorr.daf0 + s.lcorr.daf1 * t;
2046
+ iode = s.lcorr.iode;
2047
+ } else if (prn[0] !== "S") {
2048
+ return null;
2049
+ }
2050
+ if (!s.fcorr || s.fcorr.t0s === 0) return null;
2051
+ const tf = now - s.fcorr.t0s + this.tlat;
2052
+ if (Math.abs(tf) > MAXSBSAGEF || s.fcorr.udre >= 15) return null;
2053
+ const prc = s.fcorr.prc;
2054
+ const varM2 = varfcorr(s.fcorr.udre) + degfcorr(s.fcorr.ai) * tf * tf / 2;
2055
+ return { dPos: drs, dClkS: dclk + prc / C_LIGHT, prcM: prc, varM2, iode };
2056
+ }
2057
+ /**
2058
+ * Slant L1 ionospheric delay (m) at the pierce point for a receiver geodetic
2059
+ * position (lat/lon rad, height m) and a satellite az/el (rad), interpolated
2060
+ * from the SBAS grid — or null if the pierce point isn't covered. RTKLIB
2061
+ * `sbsioncorr`.
2062
+ */
2063
+ ionoDelay(week, tow, latRad, lonRad, heightM, azRad, elRad) {
2064
+ if (heightM < -100 || elRad <= 0) return { delayM: 0, varM2: 0 };
2065
+ const { fp, latP, lonP } = ionppp(
2066
+ latRad,
2067
+ lonRad,
2068
+ azRad,
2069
+ elRad,
2070
+ 6378.1363,
2071
+ 350
2072
+ );
2073
+ const igp = this.searchIgp(latP, lonP);
2074
+ if (!igp) return null;
2075
+ const { pts, x, y } = igp;
2076
+ const w = [0, 0, 0, 0];
2077
+ if (pts[0] && pts[1] && pts[2] && pts[3]) {
2078
+ w[0] = (1 - x) * (1 - y);
2079
+ w[1] = (1 - x) * y;
2080
+ w[2] = x * (1 - y);
2081
+ w[3] = x * y;
2082
+ } else if (pts[0] && pts[1] && pts[2]) {
2083
+ w[1] = y;
2084
+ w[2] = x;
2085
+ w[0] = 1 - w[1] - w[2];
2086
+ if (w[0] < 0) return null;
2087
+ } else if (pts[0] && pts[2] && pts[3]) {
2088
+ w[0] = 1 - x;
2089
+ w[3] = y;
2090
+ w[2] = 1 - w[0] - w[3];
2091
+ if (w[2] < 0) return null;
2092
+ } else if (pts[0] && pts[1] && pts[3]) {
2093
+ w[0] = 1 - y;
2094
+ w[3] = x;
2095
+ w[1] = 1 - w[0] - w[3];
2096
+ if (w[1] < 0) return null;
2097
+ } else if (pts[1] && pts[2] && pts[3]) {
2098
+ w[1] = 1 - x;
2099
+ w[2] = 1 - y;
2100
+ w[3] = 1 - w[1] - w[2];
2101
+ if (w[3] < 0) return null;
2102
+ } else return null;
2103
+ const now = gpsSeconds(week, tow);
2104
+ let delay = 0;
2105
+ let varM2 = 0;
2106
+ for (let i = 0; i < 4; i++) {
2107
+ const g = pts[i];
2108
+ if (!g) continue;
2109
+ delay += w[i] * g.delay;
2110
+ varM2 += w[i] * varicorr(g.give) * 9e-8 * Math.abs(now - g.t0s);
2111
+ }
2112
+ return { delayM: delay * fp, varM2: varM2 * fp * fp };
2113
+ }
2114
+ /** Find the four IGPs bracketing a pierce point (RTKLIB `searchigp`). */
2115
+ searchIgp(latP, lonP) {
2116
+ const lat = latP * R2D;
2117
+ let lon = lonP * R2D;
2118
+ if (lon >= 180) lon -= 360;
2119
+ const latp = [0, 0];
2120
+ const lonp = [0, 0, 0, 0];
2121
+ let x;
2122
+ let y;
2123
+ if (lat >= -55 && lat < 55) {
2124
+ latp[0] = Math.floor(lat / 5) * 5;
2125
+ latp[1] = latp[0] + 5;
2126
+ lonp[0] = lonp[1] = Math.floor(lon / 5) * 5;
2127
+ lonp[2] = lonp[3] = lonp[0] + 5;
2128
+ x = (lon - lonp[0]) / 5;
2129
+ y = (lat - latp[0]) / 5;
2130
+ } else {
2131
+ latp[0] = Math.floor((lat - 5) / 10) * 10 + 5;
2132
+ latp[1] = latp[0] + 10;
2133
+ lonp[0] = lonp[1] = Math.floor(lon / 10) * 10;
2134
+ lonp[2] = lonp[3] = lonp[0] + 10;
2135
+ x = (lon - lonp[0]) / 10;
2136
+ y = (lat - latp[0]) / 10;
2137
+ if (lat >= 75 && lat < 85) {
2138
+ lonp[1] = Math.floor(lon / 90) * 90;
2139
+ lonp[3] = lonp[1] + 90;
2140
+ } else if (lat >= -85 && lat < -75) {
2141
+ lonp[0] = Math.floor((lon - 50) / 90) * 90 + 40;
2142
+ lonp[2] = lonp[0] + 90;
2143
+ } else if (lat >= 85) {
2144
+ for (let i = 0; i < 4; i++) lonp[i] = Math.floor(lon / 90) * 90;
2145
+ } else if (lat < -85) {
2146
+ for (let i = 0; i < 4; i++)
2147
+ lonp[i] = Math.floor((lon - 50) / 90) * 90 + 40;
2148
+ }
2149
+ }
2150
+ for (let i = 0; i < 4; i++) if (lonp[i] === 180) lonp[i] = -180;
2151
+ const pts = [null, null, null, null];
2152
+ for (const band of this.ion) {
2153
+ if (!band) continue;
2154
+ for (const g of band.igp) {
2155
+ if (g.t0s === 0 || g.give <= 0) continue;
2156
+ if (g.lat === latp[0] && g.lon === lonp[0]) pts[0] = g;
2157
+ else if (g.lat === latp[1] && g.lon === lonp[1]) pts[1] = g;
2158
+ else if (g.lat === latp[0] && g.lon === lonp[2]) pts[2] = g;
2159
+ else if (g.lat === latp[1] && g.lon === lonp[3]) pts[3] = g;
2160
+ }
2161
+ }
2162
+ if (!pts[0] && !pts[1] && !pts[2] && !pts[3]) return null;
2163
+ return { pts, x, y };
2164
+ }
2165
+ };
2166
+
1657
2167
  // src/rinex/sp3.ts
1658
2168
  function sp3Position(sp3, prn, tMs, order = 9) {
1659
2169
  const samples = sp3.satellites[prn];
@@ -1705,7 +2215,7 @@ function sp3Position(sp3, prn, tMs, order = 9) {
1705
2215
  }
1706
2216
 
1707
2217
  // src/rinex/clk.ts
1708
- var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
2218
+ var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
1709
2219
  function clkBias(clk, prn, tMs) {
1710
2220
  const e = clk.sats[prn];
1711
2221
  if (!e || e.t.length === 0) return null;
@@ -2250,11 +2760,13 @@ var IDX_Z = 2;
2250
2760
  var IDX_ZWD = 3;
2251
2761
  var NBASE = 4;
2252
2762
  var CLK_VAR = 1e10;
2763
+ var POS_VAR = 60 * 60;
2253
2764
  function solvePpp(epochs, sp3, opts) {
2254
2765
  const elevMask = (opts.elevationMaskDeg ?? 10) * Math.PI / 180;
2255
2766
  const codeSigma = opts.codeSigma ?? 3;
2256
2767
  const phaseSigma = opts.phaseSigma ?? 0.01;
2257
2768
  const ztdQ = opts.ztdProcessNoise ?? 1e-8;
2769
+ const kinematic = opts.mode === "kinematic";
2258
2770
  const corrections = opts.corrections;
2259
2771
  const x = [
2260
2772
  opts.aprioriPos[0],
@@ -2350,6 +2862,15 @@ function solvePpp(epochs, sp3, opts) {
2350
2862
  let convergedStreak = 0;
2351
2863
  for (let ei = 0; ei < epochs.length; ei++) {
2352
2864
  const epoch = epochs[ei];
2865
+ if (kinematic && ei > 0) {
2866
+ for (const p of [IDX_X, IDX_Y, IDX_Z]) {
2867
+ for (let j = 0; j < x.length; j++) {
2868
+ P[p][j] = 0;
2869
+ P[j][p] = 0;
2870
+ }
2871
+ P[p][p] = POS_VAR;
2872
+ }
2873
+ }
2353
2874
  for (const ci of clkIdx.values()) P[ci][ci] = CLK_VAR;
2354
2875
  P[IDX_ZWD][IDX_ZWD] += ztdQ * (ei > 0 ? 1 : 0);
2355
2876
  const rcv = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
@@ -3108,7 +3629,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
3108
3629
  const systems = [...new Set(prns.map((p) => p[0]))].sort();
3109
3630
  const dim = 3 + systems.length;
3110
3631
  const sysIndex = new Map(systems.map((s, i) => [s, 3 + i]));
3111
- let x2 = 0;
3632
+ let x9 = 0;
3112
3633
  let y2 = 0;
3113
3634
  let z2 = 0;
3114
3635
  const clock2 = new Map(systems.map((s) => [s, 0]));
@@ -3124,8 +3645,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
3124
3645
  const Htv = new Array(dim).fill(0);
3125
3646
  let rows = 0;
3126
3647
  for (const k of Object.keys(residuals2)) delete residuals2[k];
3127
- const positionSaneIter = x2 * x2 + y2 * y2 + z2 * z2 > 1e12;
3128
- const [rxLat, rxLon, rxHgt] = positionSaneIter ? ecefToGeodetic(x2, y2, z2) : [0, 0, 0];
3648
+ const positionSaneIter = x9 * x9 + y2 * y2 + z2 * z2 > 1e12;
3649
+ const [rxLat, rxLon, rxHgt] = positionSaneIter ? ecefToGeodetic(x9, y2, z2) : [0, 0, 0];
3129
3650
  for (const prn of prns) {
3130
3651
  const psr = pseudoranges.get(prn);
3131
3652
  const eph = ephemerides.get(prn);
@@ -3137,10 +3658,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
3137
3658
  if (!Number.isFinite(sat.x)) continue;
3138
3659
  const isKepler = eph.system !== "R" && eph.system !== "S";
3139
3660
  const dts = dtsClock - (tgd && isKepler ? eph.tgd : 0);
3140
- const travel = Math.hypot(sat.x - x2, sat.y - y2, sat.z - z2) / C_LIGHT;
3661
+ const travel = Math.hypot(sat.x - x9, sat.y - y2, sat.z - z2) / C_LIGHT;
3141
3662
  const [sx, sy, sz] = sagnac2(sat, travel);
3142
- const rho = Math.hypot(sx - x2, sy - y2, sz - z2);
3143
- const ux = (x2 - sx) / rho;
3663
+ const rho = Math.hypot(sx - x9, sy - y2, sz - z2);
3664
+ const ux = (x9 - sx) / rho;
3144
3665
  const uy = (y2 - sy) / rho;
3145
3666
  const uz = (z2 - sz) / rho;
3146
3667
  let elev = Math.PI / 2;
@@ -3148,7 +3669,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
3148
3669
  let weight = 1;
3149
3670
  const positionSane = positionSaneIter;
3150
3671
  if (positionSane) {
3151
- const azel = ecefToAzEl(x2, y2, z2, sx, sy, sz);
3672
+ const azel = ecefToAzEl(x9, y2, z2, sx, sy, sz);
3152
3673
  elev = azel.el;
3153
3674
  azim = azel.az;
3154
3675
  if (iter >= 2 && elev < elevationMaskDeg * Math.PI / 180) continue;
@@ -3186,7 +3707,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
3186
3707
  if (rows < dim) return null;
3187
3708
  const dx = solveLinear(HtH, Htv);
3188
3709
  if (!dx) return null;
3189
- x2 += dx[0];
3710
+ x9 += dx[0];
3190
3711
  y2 += dx[1];
3191
3712
  z2 += dx[2];
3192
3713
  for (const s of systems)
@@ -3196,7 +3717,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
3196
3717
  break;
3197
3718
  }
3198
3719
  }
3199
- return { x: x2, y: y2, z: z2, clock: clock2, residuals: residuals2, iterations: iterations2, converged: converged2 };
3720
+ return { x: x9, y: y2, z: z2, clock: clock2, residuals: residuals2, iterations: iterations2, converged: converged2 };
3200
3721
  };
3201
3722
  const REJECT_THRESHOLD_M = 50;
3202
3723
  let candidates = all;
@@ -3250,6 +3771,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
3250
3771
  0 && (module.exports = {
3251
3772
  IONO_L1_M_PER_TECU,
3252
3773
  RtkFloatEngine,
3774
+ SbasProcessor,
3253
3775
  buildPppAntenna,
3254
3776
  createPppCorrections,
3255
3777
  estimateNarrowlaneFcb,