gnss-js 1.48.0 → 1.50.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]));
@@ -2606,9 +3099,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2606
3099
  );
2607
3100
  const Htv = new Array(dim).fill(0);
2608
3101
  let rows = 0;
3102
+ const sysRows = new Map(systems.map((s) => [s, 0]));
2609
3103
  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];
3104
+ const positionSaneIter = x9 * x9 + y2 * y2 + z2 * z2 > 1e12;
3105
+ const [rxLat, rxLon, rxHgt] = positionSaneIter ? ecefToGeodetic(x9, y2, z2) : [0, 0, 0];
2612
3106
  for (const prn of prns) {
2613
3107
  const psr = pseudoranges.get(prn);
2614
3108
  const eph = ephemerides.get(prn);
@@ -2620,10 +3114,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2620
3114
  if (!Number.isFinite(sat.x)) continue;
2621
3115
  const isKepler = eph.system !== "R" && eph.system !== "S";
2622
3116
  const dts = dtsClock - (tgd && isKepler ? eph.tgd : 0);
2623
- const travel = Math.hypot(sat.x - x2, sat.y - y2, sat.z - z2) / C_LIGHT;
3117
+ const travel = Math.hypot(sat.x - x9, sat.y - y2, sat.z - z2) / C_LIGHT;
2624
3118
  const [sx, sy, sz] = sagnac2(sat, travel);
2625
- const rho = Math.hypot(sx - x2, sy - y2, sz - z2);
2626
- const ux = (x2 - sx) / rho;
3119
+ const rho = Math.hypot(sx - x9, sy - y2, sz - z2);
3120
+ const ux = (x9 - sx) / rho;
2627
3121
  const uy = (y2 - sy) / rho;
2628
3122
  const uz = (z2 - sz) / rho;
2629
3123
  let elev = Math.PI / 2;
@@ -2631,7 +3125,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2631
3125
  let weight = 1;
2632
3126
  const positionSane = positionSaneIter;
2633
3127
  if (positionSane) {
2634
- const azel = ecefToAzEl(x2, y2, z2, sx, sy, sz);
3128
+ const azel = ecefToAzEl(x9, y2, z2, sx, sy, sz);
2635
3129
  elev = azel.el;
2636
3130
  azim = azel.az;
2637
3131
  if (iter >= 2 && elev < elevationMaskDeg * Math.PI / 180) continue;
@@ -2665,11 +3159,22 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2665
3159
  }
2666
3160
  residuals2[prn] = v;
2667
3161
  rows++;
3162
+ sysRows.set(sys, sysRows.get(sys) + 1);
3163
+ }
3164
+ let activeSystems = 0;
3165
+ for (const s of systems) {
3166
+ if (sysRows.get(s) === 0) {
3167
+ const idx = sysIndex.get(s);
3168
+ HtH[idx][idx] = 1;
3169
+ Htv[idx] = 0;
3170
+ } else {
3171
+ activeSystems++;
3172
+ }
2668
3173
  }
2669
- if (rows < dim) return null;
3174
+ if (rows < 3 + activeSystems) return null;
2670
3175
  const dx = solveLinear(HtH, Htv);
2671
3176
  if (!dx) return null;
2672
- x2 += dx[0];
3177
+ x9 += dx[0];
2673
3178
  y2 += dx[1];
2674
3179
  z2 += dx[2];
2675
3180
  for (const s of systems)
@@ -2679,7 +3184,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2679
3184
  break;
2680
3185
  }
2681
3186
  }
2682
- return { x: x2, y: y2, z: z2, clock: clock2, residuals: residuals2, iterations: iterations2, converged: converged2 };
3187
+ return { x: x9, y: y2, z: z2, clock: clock2, residuals: residuals2, iterations: iterations2, converged: converged2 };
2683
3188
  };
2684
3189
  const REJECT_THRESHOLD_M = 50;
2685
3190
  let candidates = all;
@@ -2741,6 +3246,7 @@ export {
2741
3246
  solveDgnss,
2742
3247
  RtkFloatEngine,
2743
3248
  postProcessRtk,
3249
+ SbasProcessor,
2744
3250
  niellMapping,
2745
3251
  gmst,
2746
3252
  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";