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.
@@ -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,
@@ -1722,11 +2215,13 @@ var IDX_Z = 2;
1722
2215
  var IDX_ZWD = 3;
1723
2216
  var NBASE = 4;
1724
2217
  var CLK_VAR = 1e10;
2218
+ var POS_VAR = 60 * 60;
1725
2219
  function solvePpp(epochs, sp3, opts) {
1726
2220
  const elevMask = (opts.elevationMaskDeg ?? 10) * Math.PI / 180;
1727
2221
  const codeSigma = opts.codeSigma ?? 3;
1728
2222
  const phaseSigma = opts.phaseSigma ?? 0.01;
1729
2223
  const ztdQ = opts.ztdProcessNoise ?? 1e-8;
2224
+ const kinematic = opts.mode === "kinematic";
1730
2225
  const corrections = opts.corrections;
1731
2226
  const x = [
1732
2227
  opts.aprioriPos[0],
@@ -1822,6 +2317,15 @@ function solvePpp(epochs, sp3, opts) {
1822
2317
  let convergedStreak = 0;
1823
2318
  for (let ei = 0; ei < epochs.length; ei++) {
1824
2319
  const epoch = epochs[ei];
2320
+ if (kinematic && ei > 0) {
2321
+ for (const p of [IDX_X, IDX_Y, IDX_Z]) {
2322
+ for (let j = 0; j < x.length; j++) {
2323
+ P[p][j] = 0;
2324
+ P[j][p] = 0;
2325
+ }
2326
+ P[p][p] = POS_VAR;
2327
+ }
2328
+ }
1825
2329
  for (const ci of clkIdx.values()) P[ci][ci] = CLK_VAR;
1826
2330
  P[IDX_ZWD][IDX_ZWD] += ztdQ * (ei > 0 ? 1 : 0);
1827
2331
  const rcv = [x[IDX_X], x[IDX_Y], x[IDX_Z]];
@@ -2580,7 +3084,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2580
3084
  const systems = [...new Set(prns.map((p) => p[0]))].sort();
2581
3085
  const dim = 3 + systems.length;
2582
3086
  const sysIndex = new Map(systems.map((s, i) => [s, 3 + i]));
2583
- let x2 = 0;
3087
+ let x9 = 0;
2584
3088
  let y2 = 0;
2585
3089
  let z2 = 0;
2586
3090
  const clock2 = new Map(systems.map((s) => [s, 0]));
@@ -2596,8 +3100,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2596
3100
  const Htv = new Array(dim).fill(0);
2597
3101
  let rows = 0;
2598
3102
  for (const k of Object.keys(residuals2)) delete residuals2[k];
2599
- const positionSaneIter = x2 * x2 + y2 * y2 + z2 * z2 > 1e12;
2600
- 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];
2601
3105
  for (const prn of prns) {
2602
3106
  const psr = pseudoranges.get(prn);
2603
3107
  const eph = ephemerides.get(prn);
@@ -2609,10 +3113,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2609
3113
  if (!Number.isFinite(sat.x)) continue;
2610
3114
  const isKepler = eph.system !== "R" && eph.system !== "S";
2611
3115
  const dts = dtsClock - (tgd && isKepler ? eph.tgd : 0);
2612
- 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;
2613
3117
  const [sx, sy, sz] = sagnac2(sat, travel);
2614
- const rho = Math.hypot(sx - x2, sy - y2, sz - z2);
2615
- const ux = (x2 - sx) / rho;
3118
+ const rho = Math.hypot(sx - x9, sy - y2, sz - z2);
3119
+ const ux = (x9 - sx) / rho;
2616
3120
  const uy = (y2 - sy) / rho;
2617
3121
  const uz = (z2 - sz) / rho;
2618
3122
  let elev = Math.PI / 2;
@@ -2620,7 +3124,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2620
3124
  let weight = 1;
2621
3125
  const positionSane = positionSaneIter;
2622
3126
  if (positionSane) {
2623
- const azel = ecefToAzEl(x2, y2, z2, sx, sy, sz);
3127
+ const azel = ecefToAzEl(x9, y2, z2, sx, sy, sz);
2624
3128
  elev = azel.el;
2625
3129
  azim = azel.az;
2626
3130
  if (iter >= 2 && elev < elevationMaskDeg * Math.PI / 180) continue;
@@ -2658,7 +3162,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2658
3162
  if (rows < dim) return null;
2659
3163
  const dx = solveLinear(HtH, Htv);
2660
3164
  if (!dx) return null;
2661
- x2 += dx[0];
3165
+ x9 += dx[0];
2662
3166
  y2 += dx[1];
2663
3167
  z2 += dx[2];
2664
3168
  for (const s of systems)
@@ -2668,7 +3172,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
2668
3172
  break;
2669
3173
  }
2670
3174
  }
2671
- 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 };
2672
3176
  };
2673
3177
  const REJECT_THRESHOLD_M = 50;
2674
3178
  let candidates = all;
@@ -2730,6 +3234,7 @@ export {
2730
3234
  solveDgnss,
2731
3235
  RtkFloatEngine,
2732
3236
  postProcessRtk,
3237
+ SbasProcessor,
2733
3238
  niellMapping,
2734
3239
  gmst,
2735
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";