gnss-js 1.48.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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  decodeSbasGeoNav
3
- } from "./chunk-ZIGMN3E5.js";
3
+ } from "./chunk-WQX354KD.js";
4
4
  import {
5
5
  CnavAssembler,
6
6
  cnavCrcOk
@@ -9,7 +9,7 @@ import {
9
9
  import {
10
10
  decodeSbasGeoNav,
11
11
  sbasCrcOk
12
- } from "./chunk-ZIGMN3E5.js";
12
+ } from "./chunk-WQX354KD.js";
13
13
  import {
14
14
  CnavAssembler,
15
15
  cnavCrcOk
@@ -292,6 +292,7 @@ function parseUbxRawNav(data, opts = {}) {
292
292
  continue;
293
293
  }
294
294
  if (refWeek === 0) continue;
295
+ opts.onSbasMessage?.(msg, svId, refWeek, refTow);
295
296
  const eph = decodeSbasGeoNav(msg, svId, refWeek, refTow);
296
297
  if (eph) {
297
298
  const key = `${eph.prn}@${eph.tocDate.getTime()}`;
@@ -1,3 +1,6 @@
1
+ import {
2
+ sbasMessageType
3
+ } from "./chunk-WQX354KD.js";
1
4
  import {
2
5
  computeDop,
3
6
  computeSatPosition,
@@ -13,6 +16,10 @@ import {
13
16
  clkBias,
14
17
  sp3Position
15
18
  } from "./chunk-GPEP67TG.js";
19
+ import {
20
+ getBitS,
21
+ getBitU
22
+ } from "./chunk-V2D5KUKH.js";
16
23
  import {
17
24
  C_LIGHT,
18
25
  OMEGA_E
@@ -45,8 +52,8 @@ function klobucharDelay(coeffs, latRad, lonRad, azRad, elRad, gpsTowSec) {
45
52
  const F = 1 + 16 * Math.pow(0.53 - E, 3);
46
53
  const x = 2 * Math.PI * (t - 50400) / per;
47
54
  if (Math.abs(x) >= 1.57) return F * 5e-9;
48
- const x2 = x * x;
49
- return F * (5e-9 + amp * (1 - x2 / 2 + x2 * x2 / 24));
55
+ const x22 = x * x;
56
+ return F * (5e-9 + amp * (1 - x22 / 2 + x22 * x22 / 24));
50
57
  }
51
58
 
52
59
  // src/positioning/gim.ts
@@ -1198,6 +1205,492 @@ function postProcessRtk(base, rover, ephemerides, baseEcef, opts = {}) {
1198
1205
  return { track, solved, unmatched, fixRate: solved ? fixed / solved : 0 };
1199
1206
  }
1200
1207
 
1208
+ // src/positioning/sbas.ts
1209
+ var P2_11 = 2 ** -11;
1210
+ var P2_31 = 2 ** -31;
1211
+ var P2_39 = 2 ** -39;
1212
+ var D2R = Math.PI / 180;
1213
+ var R2D = 180 / Math.PI;
1214
+ var SEC_PER_WEEK = 604800;
1215
+ var MAXSBSAGEF = 30;
1216
+ var MAXSBSAGEL = 1800;
1217
+ var MAXBAND = 10;
1218
+ var two = (n) => String(n).padStart(2, "0");
1219
+ var VAR_FCORR = [
1220
+ 0.052,
1221
+ 0.0924,
1222
+ 0.1444,
1223
+ 0.283,
1224
+ 0.4678,
1225
+ 0.8315,
1226
+ 1.2992,
1227
+ 1.8709,
1228
+ 2.5465,
1229
+ 3.326,
1230
+ 5.1968,
1231
+ 20.787,
1232
+ 230.9661,
1233
+ 2078.695
1234
+ ];
1235
+ var varfcorr = (udre) => udre > 0 && udre <= 14 ? VAR_FCORR[udre - 1] : 0;
1236
+ var VAR_ICORR = [
1237
+ 84e-4,
1238
+ 0.0333,
1239
+ 0.0749,
1240
+ 0.1331,
1241
+ 0.2079,
1242
+ 0.2994,
1243
+ 0.4075,
1244
+ 0.5322,
1245
+ 0.6735,
1246
+ 0.8315,
1247
+ 1.1974,
1248
+ 1.8709,
1249
+ 3.326,
1250
+ 20.787,
1251
+ 187.0826
1252
+ ];
1253
+ var varicorr = (give) => give > 0 && give <= 15 ? VAR_ICORR[give - 1] : 0;
1254
+ var DEGF = [
1255
+ 0,
1256
+ 5e-5,
1257
+ 9e-5,
1258
+ 12e-5,
1259
+ 15e-5,
1260
+ 2e-4,
1261
+ 3e-4,
1262
+ 45e-5,
1263
+ 6e-4,
1264
+ 9e-4,
1265
+ 15e-4,
1266
+ 21e-4,
1267
+ 27e-4,
1268
+ 33e-4,
1269
+ 46e-4,
1270
+ 58e-4
1271
+ ];
1272
+ var degfcorr = (ai) => ai > 0 && ai <= 15 ? DEGF[ai] : 58e-4;
1273
+ 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];
1274
+ 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];
1275
+ 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];
1276
+ 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];
1277
+ 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];
1278
+ 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];
1279
+ var x7 = [-180, -150, -120, -90, -60, -30, 0, 30, 60, 90, 120, 150];
1280
+ var x8 = [-170, -140, -110, -80, -50, -20, 10, 40, 70, 100, 130, 160];
1281
+ var igpband1 = [
1282
+ [{ 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 }],
1283
+ [{ 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 }],
1284
+ [{ 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 }],
1285
+ [{ 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 }],
1286
+ [{ 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 }],
1287
+ [{ 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 }],
1288
+ [{ 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 }],
1289
+ [{ 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 }],
1290
+ [{ 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 }]
1291
+ ];
1292
+ var igpband2 = [
1293
+ [{ 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 }],
1294
+ [{ 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 }]
1295
+ ];
1296
+ var gpsSeconds = (week, tow) => week * SEC_PER_WEEK + tow;
1297
+ function maskPrn(i) {
1298
+ if (i >= 1 && i <= 37) return `G${two(i)}`;
1299
+ if (i >= 38 && i <= 61) return `R${two(i - 37)}`;
1300
+ if (i >= 120 && i <= 138) return `S${two(i - 100)}`;
1301
+ if (i >= 193 && i <= 202) return `J${two(i - 192)}`;
1302
+ return null;
1303
+ }
1304
+ function ionppp(latRad, lonRad, azRad, elRad, re, hion) {
1305
+ const rp = re / (re + hion) * Math.cos(elRad);
1306
+ const ap = Math.PI / 2 - elRad - Math.asin(rp);
1307
+ const sinap = Math.sin(ap);
1308
+ const tanap = Math.tan(ap);
1309
+ const cosaz = Math.cos(azRad);
1310
+ const latP = Math.asin(
1311
+ Math.sin(latRad) * Math.cos(ap) + Math.cos(latRad) * sinap * cosaz
1312
+ );
1313
+ let lonP;
1314
+ if (latRad > 70 * D2R && tanap * cosaz > Math.tan(Math.PI / 2 - latRad) || latRad < -70 * D2R && -tanap * cosaz > Math.tan(Math.PI / 2 + latRad)) {
1315
+ lonP = lonRad + Math.PI - Math.asin(sinap * Math.sin(azRad) / Math.cos(latP));
1316
+ } else {
1317
+ lonP = lonRad + Math.asin(sinap * Math.sin(azRad) / Math.cos(latP));
1318
+ }
1319
+ return { fp: 1 / Math.sqrt(1 - rp * rp), latP, lonP };
1320
+ }
1321
+ var SbasProcessor = class {
1322
+ sats = [];
1323
+ satIdx = /* @__PURE__ */ new Map();
1324
+ iodp = -1;
1325
+ tlat = 0;
1326
+ ion = [];
1327
+ /** GEO PRN(s) whose messages have been ingested. */
1328
+ geoPrns = /* @__PURE__ */ new Set();
1329
+ /**
1330
+ * Ingest one decoded SBAS L1 message (250-bit, MSB-first) received at the
1331
+ * given GPS week / time-of-week. Returns the message type, or -1 if it was
1332
+ * not a recognised/applicable correction message.
1333
+ */
1334
+ update(msg, week, tow, geoPrn) {
1335
+ if (week === 0) return -1;
1336
+ const type = sbasMessageType(msg);
1337
+ if (geoPrn != null && geoPrn >= 120 && geoPrn <= 158)
1338
+ this.geoPrns.add(`S${two(geoPrn - 100)}`);
1339
+ switch (type) {
1340
+ case 1:
1341
+ return this.decodeMask(msg) ? type : -1;
1342
+ case 0:
1343
+ case 2:
1344
+ case 3:
1345
+ case 4:
1346
+ case 5:
1347
+ return this.decodeFast(msg, type, week, tow) ? type : -1;
1348
+ case 6:
1349
+ return this.decodeIntegrity(msg) ? type : -1;
1350
+ case 7:
1351
+ return this.decodeDegradation(msg) ? type : -1;
1352
+ case 18:
1353
+ return this.decodeIgpMask(msg) ? type : -1;
1354
+ case 24:
1355
+ return this.decodeMixed(msg, week, tow) ? type : -1;
1356
+ case 25:
1357
+ return this.decodeLongCorrHalf(msg, 14, week, tow) && this.decodeLongCorrHalf(msg, 120, week, tow) ? type : -1;
1358
+ case 26:
1359
+ return this.decodeIgpDelay(msg, week, tow) ? type : -1;
1360
+ case 9:
1361
+ // GEO nav — handled by decodeSbasGeoNav elsewhere
1362
+ case 63:
1363
+ return type;
1364
+ default:
1365
+ return -1;
1366
+ }
1367
+ }
1368
+ decodeMask(msg) {
1369
+ const sats = [];
1370
+ for (let i = 1; i <= 210; i++) {
1371
+ if (getBitU(msg, 13 + i, 1)) sats.push({ prn: maskPrn(i) });
1372
+ }
1373
+ this.iodp = getBitU(msg, 224, 2);
1374
+ this.sats = sats;
1375
+ this.satIdx.clear();
1376
+ sats.forEach((s, k) => {
1377
+ if (s.prn) this.satIdx.set(s.prn, k);
1378
+ });
1379
+ return true;
1380
+ }
1381
+ decodeFast(msg, type, week, tow) {
1382
+ if (this.iodp !== getBitU(msg, 16, 2)) return false;
1383
+ const iodf = getBitU(msg, 14, 2);
1384
+ const t0s = gpsSeconds(week, tow);
1385
+ for (let i = 0; i < 13; i++) {
1386
+ const j = 13 * ((type === 0 ? 2 : type) - 2) + i;
1387
+ if (j >= this.sats.length) break;
1388
+ const udre = getBitU(msg, 174 + 4 * i, 4);
1389
+ const prc = getBitS(msg, 18 + i * 12, 12) * 0.125;
1390
+ const prev = this.sats[j].fcorr;
1391
+ this.sats[j].fcorr = {
1392
+ prc,
1393
+ udre: udre + 1,
1394
+ ai: prev?.ai ?? 0,
1395
+ iodf,
1396
+ t0s
1397
+ };
1398
+ }
1399
+ return true;
1400
+ }
1401
+ decodeIntegrity(msg) {
1402
+ const iodf = [0, 1, 2, 3].map((i) => getBitU(msg, 14 + i * 2, 2));
1403
+ for (let i = 0; i < this.sats.length; i++) {
1404
+ const f = this.sats[i].fcorr;
1405
+ if (!f || f.iodf !== iodf[Math.floor(i / 13)]) continue;
1406
+ f.udre = getBitU(msg, 22 + i * 4, 4) + 1;
1407
+ }
1408
+ return true;
1409
+ }
1410
+ decodeDegradation(msg) {
1411
+ if (this.iodp !== getBitU(msg, 18, 2)) return false;
1412
+ this.tlat = getBitU(msg, 14, 4);
1413
+ for (let i = 0; i < this.sats.length; i++) {
1414
+ const ai = getBitU(msg, 22 + i * 4, 4);
1415
+ if (this.sats[i].fcorr) this.sats[i].fcorr.ai = ai;
1416
+ else this.sats[i].fcorr = { prc: 0, udre: 0, ai, iodf: -1, t0s: 0 };
1417
+ }
1418
+ return true;
1419
+ }
1420
+ decodeMixed(msg, week, tow) {
1421
+ if (this.iodp !== getBitU(msg, 110, 2)) return false;
1422
+ const blk = getBitU(msg, 112, 2);
1423
+ const iodf = getBitU(msg, 114, 2);
1424
+ const t0s = gpsSeconds(week, tow);
1425
+ for (let i = 0; i < 6; i++) {
1426
+ const j = 13 * blk + i;
1427
+ if (j >= this.sats.length) break;
1428
+ const udre = getBitU(msg, 86 + 4 * i, 4);
1429
+ const prc = getBitS(msg, 14 + i * 12, 12) * 0.125;
1430
+ const prev = this.sats[j].fcorr;
1431
+ this.sats[j].fcorr = {
1432
+ prc,
1433
+ udre: udre + 1,
1434
+ ai: prev?.ai ?? 0,
1435
+ iodf,
1436
+ t0s
1437
+ };
1438
+ }
1439
+ return this.decodeLongCorrHalf(msg, 120, week, tow);
1440
+ }
1441
+ decodeLongCorrHalf(msg, p, week, tow) {
1442
+ if (getBitU(msg, p, 1) === 0) {
1443
+ if (this.iodp === getBitU(msg, p + 103, 2)) {
1444
+ const a = this.decodeLong0(msg, p + 1, week, tow);
1445
+ const b = this.decodeLong0(msg, p + 52, week, tow);
1446
+ return a || b;
1447
+ }
1448
+ } else if (this.iodp === getBitU(msg, p + 104, 2)) {
1449
+ return this.decodeLong1(msg, p + 1, week, tow);
1450
+ }
1451
+ return false;
1452
+ }
1453
+ decodeLong0(msg, p, week, tow) {
1454
+ const n = getBitU(msg, p, 6);
1455
+ if (n === 0 || n > this.sats.length) return false;
1456
+ this.sats[n - 1].lcorr = {
1457
+ iode: getBitU(msg, p + 6, 8),
1458
+ dpos: [
1459
+ getBitS(msg, p + 14, 9) * 0.125,
1460
+ getBitS(msg, p + 23, 9) * 0.125,
1461
+ getBitS(msg, p + 32, 9) * 0.125
1462
+ ],
1463
+ dvel: [0, 0, 0],
1464
+ daf0: getBitS(msg, p + 41, 10) * P2_31,
1465
+ daf1: 0,
1466
+ t0s: gpsSeconds(week, tow)
1467
+ };
1468
+ return true;
1469
+ }
1470
+ decodeLong1(msg, p, week, tow) {
1471
+ const n = getBitU(msg, p, 6);
1472
+ if (n === 0 || n > this.sats.length) return false;
1473
+ let t = getBitU(msg, p + 90, 13) * 16 - tow % 86400;
1474
+ if (t <= -43200) t += 86400;
1475
+ else if (t > 43200) t -= 86400;
1476
+ this.sats[n - 1].lcorr = {
1477
+ iode: getBitU(msg, p + 6, 8),
1478
+ dpos: [
1479
+ getBitS(msg, p + 14, 11) * 0.125,
1480
+ getBitS(msg, p + 25, 11) * 0.125,
1481
+ getBitS(msg, p + 36, 11) * 0.125
1482
+ ],
1483
+ dvel: [
1484
+ getBitS(msg, p + 58, 8) * P2_11,
1485
+ getBitS(msg, p + 66, 8) * P2_11,
1486
+ getBitS(msg, p + 74, 8) * P2_11
1487
+ ],
1488
+ daf0: getBitS(msg, p + 47, 11) * P2_31,
1489
+ daf1: getBitS(msg, p + 82, 8) * P2_39,
1490
+ t0s: gpsSeconds(week, tow + t)
1491
+ };
1492
+ return true;
1493
+ }
1494
+ decodeIgpMask(msg) {
1495
+ const band = getBitU(msg, 18, 4);
1496
+ let p;
1497
+ let m;
1498
+ if (band <= 8) {
1499
+ p = igpband1[band];
1500
+ m = 8;
1501
+ } else if (band <= 10) {
1502
+ p = igpband2[band - 9];
1503
+ m = 5;
1504
+ } else return false;
1505
+ const igp = [];
1506
+ for (let i = 1; i <= 201; i++) {
1507
+ if (!getBitU(msg, 23 + i, 1)) continue;
1508
+ for (let j = 0; j < m; j++) {
1509
+ const b = p[j];
1510
+ if (i < b.bits || b.bite < i) continue;
1511
+ const varying = b.arr[i - b.bits];
1512
+ igp.push({
1513
+ lat: band <= 8 ? varying : b.coord,
1514
+ lon: band <= 8 ? b.coord : varying,
1515
+ delay: 0,
1516
+ give: 0,
1517
+ t0s: 0
1518
+ });
1519
+ break;
1520
+ }
1521
+ }
1522
+ this.ion[band] = { iodi: getBitU(msg, 22, 2), igp };
1523
+ return true;
1524
+ }
1525
+ decodeIgpDelay(msg, week, tow) {
1526
+ const band = getBitU(msg, 14, 4);
1527
+ const b = this.ion[band];
1528
+ if (band > MAXBAND || !b || b.iodi !== getBitU(msg, 217, 2)) return false;
1529
+ const block = getBitU(msg, 18, 4);
1530
+ const t0s = gpsSeconds(week, tow);
1531
+ for (let i = 0; i < 15; i++) {
1532
+ const j = block * 15 + i;
1533
+ if (j >= b.igp.length) continue;
1534
+ const give = getBitU(msg, 22 + i * 13 + 9, 4);
1535
+ const delay = getBitU(msg, 22 + i * 13, 9);
1536
+ b.igp[j].delay = delay === 511 ? 0 : delay * 0.125;
1537
+ b.igp[j].give = give + 1 >= 16 ? 0 : give + 1;
1538
+ b.igp[j].t0s = t0s;
1539
+ }
1540
+ return true;
1541
+ }
1542
+ /** PRNs currently in the correction mask (in mask order). */
1543
+ activeSats() {
1544
+ return this.sats.map((s) => s.prn).filter((p) => !!p);
1545
+ }
1546
+ /** Number of ionospheric grid points with a valid delay across all bands. */
1547
+ ionoGridPoints() {
1548
+ let n = 0;
1549
+ for (const b of this.ion) if (b) {
1550
+ for (const g of b.igp) if (g.give > 0) n++;
1551
+ }
1552
+ return n;
1553
+ }
1554
+ /**
1555
+ * Satellite range/clock correction for a PRN at a GPS week/tow, or null if
1556
+ * no valid (unexpired) long-term + fast correction is available. Add `dPos`
1557
+ * to the satellite ECEF position and `dClkS` to its clock bias.
1558
+ */
1559
+ satCorrection(prn, week, tow) {
1560
+ const idx = this.satIdx.get(prn);
1561
+ if (idx === void 0) return null;
1562
+ const s = this.sats[idx];
1563
+ const now = gpsSeconds(week, tow);
1564
+ const drs = [0, 0, 0];
1565
+ let dclk = 0;
1566
+ let iode = -1;
1567
+ if (s.lcorr && s.lcorr.t0s !== 0) {
1568
+ const t = now - s.lcorr.t0s;
1569
+ if (Math.abs(t) > MAXSBSAGEL) return null;
1570
+ for (let i = 0; i < 3; i++)
1571
+ drs[i] = s.lcorr.dpos[i] + s.lcorr.dvel[i] * t;
1572
+ dclk = s.lcorr.daf0 + s.lcorr.daf1 * t;
1573
+ iode = s.lcorr.iode;
1574
+ } else if (prn[0] !== "S") {
1575
+ return null;
1576
+ }
1577
+ if (!s.fcorr || s.fcorr.t0s === 0) return null;
1578
+ const tf = now - s.fcorr.t0s + this.tlat;
1579
+ if (Math.abs(tf) > MAXSBSAGEF || s.fcorr.udre >= 15) return null;
1580
+ const prc = s.fcorr.prc;
1581
+ const varM2 = varfcorr(s.fcorr.udre) + degfcorr(s.fcorr.ai) * tf * tf / 2;
1582
+ return { dPos: drs, dClkS: dclk + prc / C_LIGHT, prcM: prc, varM2, iode };
1583
+ }
1584
+ /**
1585
+ * Slant L1 ionospheric delay (m) at the pierce point for a receiver geodetic
1586
+ * position (lat/lon rad, height m) and a satellite az/el (rad), interpolated
1587
+ * from the SBAS grid — or null if the pierce point isn't covered. RTKLIB
1588
+ * `sbsioncorr`.
1589
+ */
1590
+ ionoDelay(week, tow, latRad, lonRad, heightM, azRad, elRad) {
1591
+ if (heightM < -100 || elRad <= 0) return { delayM: 0, varM2: 0 };
1592
+ const { fp, latP, lonP } = ionppp(
1593
+ latRad,
1594
+ lonRad,
1595
+ azRad,
1596
+ elRad,
1597
+ 6378.1363,
1598
+ 350
1599
+ );
1600
+ const igp = this.searchIgp(latP, lonP);
1601
+ if (!igp) return null;
1602
+ const { pts, x, y } = igp;
1603
+ const w = [0, 0, 0, 0];
1604
+ if (pts[0] && pts[1] && pts[2] && pts[3]) {
1605
+ w[0] = (1 - x) * (1 - y);
1606
+ w[1] = (1 - x) * y;
1607
+ w[2] = x * (1 - y);
1608
+ w[3] = x * y;
1609
+ } else if (pts[0] && pts[1] && pts[2]) {
1610
+ w[1] = y;
1611
+ w[2] = x;
1612
+ w[0] = 1 - w[1] - w[2];
1613
+ if (w[0] < 0) return null;
1614
+ } else if (pts[0] && pts[2] && pts[3]) {
1615
+ w[0] = 1 - x;
1616
+ w[3] = y;
1617
+ w[2] = 1 - w[0] - w[3];
1618
+ if (w[2] < 0) return null;
1619
+ } else if (pts[0] && pts[1] && pts[3]) {
1620
+ w[0] = 1 - y;
1621
+ w[3] = x;
1622
+ w[1] = 1 - w[0] - w[3];
1623
+ if (w[1] < 0) return null;
1624
+ } else if (pts[1] && pts[2] && pts[3]) {
1625
+ w[1] = 1 - x;
1626
+ w[2] = 1 - y;
1627
+ w[3] = 1 - w[1] - w[2];
1628
+ if (w[3] < 0) return null;
1629
+ } else return null;
1630
+ const now = gpsSeconds(week, tow);
1631
+ let delay = 0;
1632
+ let varM2 = 0;
1633
+ for (let i = 0; i < 4; i++) {
1634
+ const g = pts[i];
1635
+ if (!g) continue;
1636
+ delay += w[i] * g.delay;
1637
+ varM2 += w[i] * varicorr(g.give) * 9e-8 * Math.abs(now - g.t0s);
1638
+ }
1639
+ return { delayM: delay * fp, varM2: varM2 * fp * fp };
1640
+ }
1641
+ /** Find the four IGPs bracketing a pierce point (RTKLIB `searchigp`). */
1642
+ searchIgp(latP, lonP) {
1643
+ const lat = latP * R2D;
1644
+ let lon = lonP * R2D;
1645
+ if (lon >= 180) lon -= 360;
1646
+ const latp = [0, 0];
1647
+ const lonp = [0, 0, 0, 0];
1648
+ let x;
1649
+ let y;
1650
+ if (lat >= -55 && lat < 55) {
1651
+ latp[0] = Math.floor(lat / 5) * 5;
1652
+ latp[1] = latp[0] + 5;
1653
+ lonp[0] = lonp[1] = Math.floor(lon / 5) * 5;
1654
+ lonp[2] = lonp[3] = lonp[0] + 5;
1655
+ x = (lon - lonp[0]) / 5;
1656
+ y = (lat - latp[0]) / 5;
1657
+ } else {
1658
+ latp[0] = Math.floor((lat - 5) / 10) * 10 + 5;
1659
+ latp[1] = latp[0] + 10;
1660
+ lonp[0] = lonp[1] = Math.floor(lon / 10) * 10;
1661
+ lonp[2] = lonp[3] = lonp[0] + 10;
1662
+ x = (lon - lonp[0]) / 10;
1663
+ y = (lat - latp[0]) / 10;
1664
+ if (lat >= 75 && lat < 85) {
1665
+ lonp[1] = Math.floor(lon / 90) * 90;
1666
+ lonp[3] = lonp[1] + 90;
1667
+ } else if (lat >= -85 && lat < -75) {
1668
+ lonp[0] = Math.floor((lon - 50) / 90) * 90 + 40;
1669
+ lonp[2] = lonp[0] + 90;
1670
+ } else if (lat >= 85) {
1671
+ for (let i = 0; i < 4; i++) lonp[i] = Math.floor(lon / 90) * 90;
1672
+ } else if (lat < -85) {
1673
+ for (let i = 0; i < 4; i++)
1674
+ lonp[i] = Math.floor((lon - 50) / 90) * 90 + 40;
1675
+ }
1676
+ }
1677
+ for (let i = 0; i < 4; i++) if (lonp[i] === 180) lonp[i] = -180;
1678
+ const pts = [null, null, null, null];
1679
+ for (const band of this.ion) {
1680
+ if (!band) continue;
1681
+ for (const g of band.igp) {
1682
+ if (g.t0s === 0 || g.give <= 0) continue;
1683
+ if (g.lat === latp[0] && g.lon === lonp[0]) pts[0] = g;
1684
+ else if (g.lat === latp[1] && g.lon === lonp[1]) pts[1] = g;
1685
+ else if (g.lat === latp[0] && g.lon === lonp[2]) pts[2] = g;
1686
+ else if (g.lat === latp[1] && g.lon === lonp[3]) pts[3] = g;
1687
+ }
1688
+ }
1689
+ if (!pts[0] && !pts[1] && !pts[2] && !pts[3]) return null;
1690
+ return { pts, x, y };
1691
+ }
1692
+ };
1693
+
1201
1694
  // src/positioning/ppp-tropo.ts
1202
1695
  var HYD_AVG_A = [
1203
1696
  0.0012769934,
@@ -2591,7 +3084,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2591
3084
  const systems = [...new Set(prns.map((p) => p[0]))].sort();
2592
3085
  const dim = 3 + systems.length;
2593
3086
  const sysIndex = new Map(systems.map((s, i) => [s, 3 + i]));
2594
- let x2 = 0;
3087
+ let x9 = 0;
2595
3088
  let y2 = 0;
2596
3089
  let z2 = 0;
2597
3090
  const clock2 = new Map(systems.map((s) => [s, 0]));
@@ -2607,8 +3100,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2607
3100
  const Htv = new Array(dim).fill(0);
2608
3101
  let rows = 0;
2609
3102
  for (const k of Object.keys(residuals2)) delete residuals2[k];
2610
- const positionSaneIter = x2 * x2 + y2 * y2 + z2 * z2 > 1e12;
2611
- const [rxLat, rxLon, rxHgt] = positionSaneIter ? ecefToGeodetic(x2, y2, z2) : [0, 0, 0];
3103
+ const positionSaneIter = x9 * x9 + y2 * y2 + z2 * z2 > 1e12;
3104
+ const [rxLat, rxLon, rxHgt] = positionSaneIter ? ecefToGeodetic(x9, y2, z2) : [0, 0, 0];
2612
3105
  for (const prn of prns) {
2613
3106
  const psr = pseudoranges.get(prn);
2614
3107
  const eph = ephemerides.get(prn);
@@ -2620,10 +3113,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2620
3113
  if (!Number.isFinite(sat.x)) continue;
2621
3114
  const isKepler = eph.system !== "R" && eph.system !== "S";
2622
3115
  const dts = dtsClock - (tgd && isKepler ? eph.tgd : 0);
2623
- const travel = Math.hypot(sat.x - x2, sat.y - y2, sat.z - z2) / C_LIGHT;
3116
+ const travel = Math.hypot(sat.x - x9, sat.y - y2, sat.z - z2) / C_LIGHT;
2624
3117
  const [sx, sy, sz] = sagnac2(sat, travel);
2625
- const rho = Math.hypot(sx - x2, sy - y2, sz - z2);
2626
- const ux = (x2 - sx) / rho;
3118
+ const rho = Math.hypot(sx - x9, sy - y2, sz - z2);
3119
+ const ux = (x9 - sx) / rho;
2627
3120
  const uy = (y2 - sy) / rho;
2628
3121
  const uz = (z2 - sz) / rho;
2629
3122
  let elev = Math.PI / 2;
@@ -2631,7 +3124,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2631
3124
  let weight = 1;
2632
3125
  const positionSane = positionSaneIter;
2633
3126
  if (positionSane) {
2634
- const azel = ecefToAzEl(x2, y2, z2, sx, sy, sz);
3127
+ const azel = ecefToAzEl(x9, y2, z2, sx, sy, sz);
2635
3128
  elev = azel.el;
2636
3129
  azim = azel.az;
2637
3130
  if (iter >= 2 && elev < elevationMaskDeg * Math.PI / 180) continue;
@@ -2669,7 +3162,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2669
3162
  if (rows < dim) return null;
2670
3163
  const dx = solveLinear(HtH, Htv);
2671
3164
  if (!dx) return null;
2672
- x2 += dx[0];
3165
+ x9 += dx[0];
2673
3166
  y2 += dx[1];
2674
3167
  z2 += dx[2];
2675
3168
  for (const s of systems)
@@ -2679,7 +3172,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2679
3172
  break;
2680
3173
  }
2681
3174
  }
2682
- return { x: x2, y: y2, z: z2, clock: clock2, residuals: residuals2, iterations: iterations2, converged: converged2 };
3175
+ return { x: x9, y: y2, z: z2, clock: clock2, residuals: residuals2, iterations: iterations2, converged: converged2 };
2683
3176
  };
2684
3177
  const REJECT_THRESHOLD_M = 50;
2685
3178
  let candidates = all;
@@ -2741,6 +3234,7 @@ export {
2741
3234
  solveDgnss,
2742
3235
  RtkFloatEngine,
2743
3236
  postProcessRtk,
3237
+ SbasProcessor,
2744
3238
  niellMapping,
2745
3239
  gmst,
2746
3240
  sunEcef,
@@ -50,6 +50,7 @@ function decodeSbasGeoNav(msg, prn, week, tow) {
50
50
  }
51
51
 
52
52
  export {
53
+ sbasMessageType,
53
54
  sbasCrcOk,
54
55
  decodeSbasGeoNav
55
56
  };
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  decodeSbasGeoNav,
13
13
  sbasCrcOk
14
- } from "./chunk-ZIGMN3E5.js";
14
+ } from "./chunk-WQX354KD.js";
15
15
  import {
16
16
  geodeticToEcef
17
17
  } from "./chunk-37QNKGTC.js";