gnss-js 1.59.0 → 1.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-7NZ4SJTJ.js → chunk-GWN2F3PQ.js} +109 -9
- package/dist/index.cjs +109 -9
- package/dist/index.js +1 -1
- package/dist/positioning.cjs +109 -9
- package/dist/positioning.d.cts +13 -2
- package/dist/positioning.d.ts +13 -2
- package/dist/positioning.js +1 -1
- package/package.json +1 -1
|
@@ -1270,6 +1270,44 @@ var DEGF = [
|
|
|
1270
1270
|
58e-4
|
|
1271
1271
|
];
|
|
1272
1272
|
var degfcorr = (ai) => ai > 0 && ai <= 15 ? DEGF[ai] : 58e-4;
|
|
1273
|
+
var DELTA_UDRE = [
|
|
1274
|
+
1,
|
|
1275
|
+
1.1,
|
|
1276
|
+
1.25,
|
|
1277
|
+
1.5,
|
|
1278
|
+
2,
|
|
1279
|
+
3,
|
|
1280
|
+
4,
|
|
1281
|
+
5,
|
|
1282
|
+
6,
|
|
1283
|
+
8,
|
|
1284
|
+
10,
|
|
1285
|
+
20,
|
|
1286
|
+
30,
|
|
1287
|
+
40,
|
|
1288
|
+
50,
|
|
1289
|
+
100
|
|
1290
|
+
];
|
|
1291
|
+
function pointInRegion(lat, lon, rg) {
|
|
1292
|
+
const poly = rg.square ? [
|
|
1293
|
+
[rg.lat1, rg.lon1],
|
|
1294
|
+
[rg.lat1, rg.lon2],
|
|
1295
|
+
[rg.lat2, rg.lon2],
|
|
1296
|
+
[rg.lat2, rg.lon1]
|
|
1297
|
+
] : [
|
|
1298
|
+
[rg.lat1, rg.lon1],
|
|
1299
|
+
[rg.lat2, rg.lon2],
|
|
1300
|
+
[rg.lat1, rg.lon2]
|
|
1301
|
+
];
|
|
1302
|
+
let inside = false;
|
|
1303
|
+
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
1304
|
+
const [yi, xi] = poly[i];
|
|
1305
|
+
const [yj, xj] = poly[j];
|
|
1306
|
+
if (yi > lat !== yj > lat && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi)
|
|
1307
|
+
inside = !inside;
|
|
1308
|
+
}
|
|
1309
|
+
return inside;
|
|
1310
|
+
}
|
|
1273
1311
|
function sbasLongTermDeg(d, vel, ageSec) {
|
|
1274
1312
|
if (vel) {
|
|
1275
1313
|
if (ageSec > 0 && ageSec < d.iltcV1) return 0;
|
|
@@ -1335,6 +1373,7 @@ var SbasProcessor = class {
|
|
|
1335
1373
|
iodp = -1;
|
|
1336
1374
|
tlat = 0;
|
|
1337
1375
|
degr = null;
|
|
1376
|
+
mt27 = null;
|
|
1338
1377
|
ion = [];
|
|
1339
1378
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
1340
1379
|
geoPrns = /* @__PURE__ */ new Set();
|
|
@@ -1363,6 +1402,8 @@ var SbasProcessor = class {
|
|
|
1363
1402
|
return this.decodeDegradation(msg) ? type : -1;
|
|
1364
1403
|
case 10:
|
|
1365
1404
|
return this.decodeMt10(msg) ? type : -1;
|
|
1405
|
+
case 27:
|
|
1406
|
+
return this.decodeMt27(msg) ? type : -1;
|
|
1366
1407
|
case 18:
|
|
1367
1408
|
return this.decodeIgpMask(msg) ? type : -1;
|
|
1368
1409
|
case 24:
|
|
@@ -1380,14 +1421,17 @@ var SbasProcessor = class {
|
|
|
1380
1421
|
}
|
|
1381
1422
|
}
|
|
1382
1423
|
decodeMask(msg) {
|
|
1383
|
-
const
|
|
1424
|
+
const iodp = getBitU(msg, 224, 2);
|
|
1425
|
+
const prns = [];
|
|
1384
1426
|
for (let i = 1; i <= 210; i++) {
|
|
1385
|
-
if (getBitU(msg, 13 + i, 1))
|
|
1427
|
+
if (getBitU(msg, 13 + i, 1)) prns.push(maskPrn(i));
|
|
1386
1428
|
}
|
|
1387
|
-
this.iodp
|
|
1388
|
-
|
|
1429
|
+
if (this.iodp === iodp && this.sats.length === prns.length && this.sats.every((s, k) => s.prn === prns[k]))
|
|
1430
|
+
return true;
|
|
1431
|
+
this.iodp = iodp;
|
|
1432
|
+
this.sats = prns.map((prn) => ({ prn }));
|
|
1389
1433
|
this.satIdx.clear();
|
|
1390
|
-
sats.forEach((s, k) => {
|
|
1434
|
+
this.sats.forEach((s, k) => {
|
|
1391
1435
|
if (s.prn) this.satIdx.set(s.prn, k);
|
|
1392
1436
|
});
|
|
1393
1437
|
return true;
|
|
@@ -1455,6 +1499,54 @@ var SbasProcessor = class {
|
|
|
1455
1499
|
};
|
|
1456
1500
|
return true;
|
|
1457
1501
|
}
|
|
1502
|
+
/** MT27 SBAS service message (DO-229D §A.4.4.13, Table A-20): regions with a
|
|
1503
|
+
* δUDRE that inflates σ_UDRE for users inside/outside them. */
|
|
1504
|
+
decodeMt27(msg) {
|
|
1505
|
+
const iods = getBitU(msg, 14, 3);
|
|
1506
|
+
const svcNum = getBitU(msg, 20, 3) + 1;
|
|
1507
|
+
const nRegions = getBitU(msg, 23, 3);
|
|
1508
|
+
const priority = getBitU(msg, 26, 2);
|
|
1509
|
+
const udreInside = getBitU(msg, 28, 4);
|
|
1510
|
+
const udreOutside = getBitU(msg, 32, 4);
|
|
1511
|
+
if (!this.mt27 || this.mt27.iods !== iods)
|
|
1512
|
+
this.mt27 = { iods, udreOutside, msgs: /* @__PURE__ */ new Map() };
|
|
1513
|
+
this.mt27.udreOutside = udreOutside;
|
|
1514
|
+
const regions = [];
|
|
1515
|
+
for (let r = 0; r < nRegions && r < 5; r++) {
|
|
1516
|
+
const p = 36 + r * 35;
|
|
1517
|
+
regions.push({
|
|
1518
|
+
lat1: getBitS(msg, p, 8),
|
|
1519
|
+
lon1: getBitS(msg, p + 8, 9),
|
|
1520
|
+
lat2: getBitS(msg, p + 17, 8),
|
|
1521
|
+
lon2: getBitS(msg, p + 25, 9),
|
|
1522
|
+
square: getBitU(msg, p + 34, 1) === 1
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
this.mt27.msgs.set(svcNum, { priority, udreInside, regions });
|
|
1526
|
+
return true;
|
|
1527
|
+
}
|
|
1528
|
+
/** δUDRE multiplier (DO-229D Table A-21) for a user location from the MT27
|
|
1529
|
+
* set: the highest-priority containing region wins (ties → lower δUDRE),
|
|
1530
|
+
* else the outside value. 1 when no MT27 has been received. */
|
|
1531
|
+
deltaUdre(latDeg, lonDeg) {
|
|
1532
|
+
const s = this.mt27;
|
|
1533
|
+
if (!s) return 1;
|
|
1534
|
+
let best = null;
|
|
1535
|
+
let bestPri = -1;
|
|
1536
|
+
for (const m of s.msgs.values()) {
|
|
1537
|
+
for (const rg of m.regions) {
|
|
1538
|
+
if (!pointInRegion(latDeg, lonDeg, rg)) continue;
|
|
1539
|
+
const val = DELTA_UDRE[m.udreInside] ?? 1;
|
|
1540
|
+
if (m.priority > bestPri) {
|
|
1541
|
+
bestPri = m.priority;
|
|
1542
|
+
best = val;
|
|
1543
|
+
} else if (m.priority === bestPri && best !== null) {
|
|
1544
|
+
best = Math.min(best, val);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
return best ?? DELTA_UDRE[s.udreOutside] ?? 1;
|
|
1549
|
+
}
|
|
1458
1550
|
decodeMixed(msg, week, tow) {
|
|
1459
1551
|
if (this.iodp !== getBitU(msg, 110, 2)) return false;
|
|
1460
1552
|
const blk = getBitU(msg, 112, 2);
|
|
@@ -1559,7 +1651,11 @@ var SbasProcessor = class {
|
|
|
1559
1651
|
break;
|
|
1560
1652
|
}
|
|
1561
1653
|
}
|
|
1562
|
-
|
|
1654
|
+
const iodi = getBitU(msg, 22, 2);
|
|
1655
|
+
const cur = this.ion[band];
|
|
1656
|
+
if (cur && cur.iodi === iodi && cur.igp.length === igp.length && cur.igp.every((g, k) => g.lat === igp[k].lat && g.lon === igp[k].lon))
|
|
1657
|
+
return true;
|
|
1658
|
+
this.ion[band] = { iodi, igp };
|
|
1563
1659
|
return true;
|
|
1564
1660
|
}
|
|
1565
1661
|
decodeIgpDelay(msg, week, tow) {
|
|
@@ -1626,7 +1722,7 @@ var SbasProcessor = class {
|
|
|
1626
1722
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
1627
1723
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
1628
1724
|
*/
|
|
1629
|
-
satCorrection(prn, week, tow) {
|
|
1725
|
+
satCorrection(prn, week, tow, userLatRad, userLonRad) {
|
|
1630
1726
|
const idx = this.satIdx.get(prn);
|
|
1631
1727
|
if (idx === void 0) return null;
|
|
1632
1728
|
const s = this.sats[idx];
|
|
@@ -1648,7 +1744,11 @@ var SbasProcessor = class {
|
|
|
1648
1744
|
const tf = now - s.fcorr.t0s + this.tlat;
|
|
1649
1745
|
if (Math.abs(tf) > MAXSBSAGEF || s.fcorr.udre >= 15) return null;
|
|
1650
1746
|
const prc = s.fcorr.prc;
|
|
1651
|
-
const
|
|
1747
|
+
const dudre = userLatRad != null && userLonRad != null ? this.deltaUdre(
|
|
1748
|
+
userLatRad * 180 / Math.PI,
|
|
1749
|
+
userLonRad * 180 / Math.PI
|
|
1750
|
+
) : 1;
|
|
1751
|
+
const sigUdre = Math.sqrt(varfcorr(s.fcorr.udre)) * dudre;
|
|
1652
1752
|
const efc = degfcorr(s.fcorr.ai) * tf * tf / 2;
|
|
1653
1753
|
const eltc = this.degr && s.lcorr && s.lcorr.t0s !== 0 ? sbasLongTermDeg(this.degr, s.lcorr.vel, now - s.lcorr.t0s) : 0;
|
|
1654
1754
|
const varM2 = this.degr?.rssUdre ? sigUdre * sigUdre + efc * efc + eltc * eltc : (sigUdre + efc + eltc) ** 2;
|
|
@@ -3387,7 +3487,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3387
3487
|
used.push(prn);
|
|
3388
3488
|
azels.push(azel);
|
|
3389
3489
|
if (sbas) {
|
|
3390
|
-
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow);
|
|
3490
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow, rxLat, rxLon);
|
|
3391
3491
|
const si = sbas.ionoDelay(
|
|
3392
3492
|
gpsWeek,
|
|
3393
3493
|
gpsTow,
|
package/dist/index.cjs
CHANGED
|
@@ -13480,6 +13480,44 @@ var DEGF = [
|
|
|
13480
13480
|
58e-4
|
|
13481
13481
|
];
|
|
13482
13482
|
var degfcorr = (ai) => ai > 0 && ai <= 15 ? DEGF[ai] : 58e-4;
|
|
13483
|
+
var DELTA_UDRE = [
|
|
13484
|
+
1,
|
|
13485
|
+
1.1,
|
|
13486
|
+
1.25,
|
|
13487
|
+
1.5,
|
|
13488
|
+
2,
|
|
13489
|
+
3,
|
|
13490
|
+
4,
|
|
13491
|
+
5,
|
|
13492
|
+
6,
|
|
13493
|
+
8,
|
|
13494
|
+
10,
|
|
13495
|
+
20,
|
|
13496
|
+
30,
|
|
13497
|
+
40,
|
|
13498
|
+
50,
|
|
13499
|
+
100
|
|
13500
|
+
];
|
|
13501
|
+
function pointInRegion(lat, lon, rg) {
|
|
13502
|
+
const poly = rg.square ? [
|
|
13503
|
+
[rg.lat1, rg.lon1],
|
|
13504
|
+
[rg.lat1, rg.lon2],
|
|
13505
|
+
[rg.lat2, rg.lon2],
|
|
13506
|
+
[rg.lat2, rg.lon1]
|
|
13507
|
+
] : [
|
|
13508
|
+
[rg.lat1, rg.lon1],
|
|
13509
|
+
[rg.lat2, rg.lon2],
|
|
13510
|
+
[rg.lat1, rg.lon2]
|
|
13511
|
+
];
|
|
13512
|
+
let inside = false;
|
|
13513
|
+
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
13514
|
+
const [yi, xi] = poly[i];
|
|
13515
|
+
const [yj, xj] = poly[j];
|
|
13516
|
+
if (yi > lat !== yj > lat && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi)
|
|
13517
|
+
inside = !inside;
|
|
13518
|
+
}
|
|
13519
|
+
return inside;
|
|
13520
|
+
}
|
|
13483
13521
|
function sbasLongTermDeg(d, vel, ageSec) {
|
|
13484
13522
|
if (vel) {
|
|
13485
13523
|
if (ageSec > 0 && ageSec < d.iltcV1) return 0;
|
|
@@ -13545,6 +13583,7 @@ var SbasProcessor = class {
|
|
|
13545
13583
|
iodp = -1;
|
|
13546
13584
|
tlat = 0;
|
|
13547
13585
|
degr = null;
|
|
13586
|
+
mt27 = null;
|
|
13548
13587
|
ion = [];
|
|
13549
13588
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
13550
13589
|
geoPrns = /* @__PURE__ */ new Set();
|
|
@@ -13573,6 +13612,8 @@ var SbasProcessor = class {
|
|
|
13573
13612
|
return this.decodeDegradation(msg) ? type : -1;
|
|
13574
13613
|
case 10:
|
|
13575
13614
|
return this.decodeMt10(msg) ? type : -1;
|
|
13615
|
+
case 27:
|
|
13616
|
+
return this.decodeMt27(msg) ? type : -1;
|
|
13576
13617
|
case 18:
|
|
13577
13618
|
return this.decodeIgpMask(msg) ? type : -1;
|
|
13578
13619
|
case 24:
|
|
@@ -13590,14 +13631,17 @@ var SbasProcessor = class {
|
|
|
13590
13631
|
}
|
|
13591
13632
|
}
|
|
13592
13633
|
decodeMask(msg) {
|
|
13593
|
-
const
|
|
13634
|
+
const iodp = getBitU(msg, 224, 2);
|
|
13635
|
+
const prns = [];
|
|
13594
13636
|
for (let i = 1; i <= 210; i++) {
|
|
13595
|
-
if (getBitU(msg, 13 + i, 1))
|
|
13637
|
+
if (getBitU(msg, 13 + i, 1)) prns.push(maskPrn(i));
|
|
13596
13638
|
}
|
|
13597
|
-
this.iodp
|
|
13598
|
-
|
|
13639
|
+
if (this.iodp === iodp && this.sats.length === prns.length && this.sats.every((s, k) => s.prn === prns[k]))
|
|
13640
|
+
return true;
|
|
13641
|
+
this.iodp = iodp;
|
|
13642
|
+
this.sats = prns.map((prn) => ({ prn }));
|
|
13599
13643
|
this.satIdx.clear();
|
|
13600
|
-
sats.forEach((s, k) => {
|
|
13644
|
+
this.sats.forEach((s, k) => {
|
|
13601
13645
|
if (s.prn) this.satIdx.set(s.prn, k);
|
|
13602
13646
|
});
|
|
13603
13647
|
return true;
|
|
@@ -13665,6 +13709,54 @@ var SbasProcessor = class {
|
|
|
13665
13709
|
};
|
|
13666
13710
|
return true;
|
|
13667
13711
|
}
|
|
13712
|
+
/** MT27 SBAS service message (DO-229D §A.4.4.13, Table A-20): regions with a
|
|
13713
|
+
* δUDRE that inflates σ_UDRE for users inside/outside them. */
|
|
13714
|
+
decodeMt27(msg) {
|
|
13715
|
+
const iods = getBitU(msg, 14, 3);
|
|
13716
|
+
const svcNum = getBitU(msg, 20, 3) + 1;
|
|
13717
|
+
const nRegions = getBitU(msg, 23, 3);
|
|
13718
|
+
const priority = getBitU(msg, 26, 2);
|
|
13719
|
+
const udreInside = getBitU(msg, 28, 4);
|
|
13720
|
+
const udreOutside = getBitU(msg, 32, 4);
|
|
13721
|
+
if (!this.mt27 || this.mt27.iods !== iods)
|
|
13722
|
+
this.mt27 = { iods, udreOutside, msgs: /* @__PURE__ */ new Map() };
|
|
13723
|
+
this.mt27.udreOutside = udreOutside;
|
|
13724
|
+
const regions = [];
|
|
13725
|
+
for (let r = 0; r < nRegions && r < 5; r++) {
|
|
13726
|
+
const p = 36 + r * 35;
|
|
13727
|
+
regions.push({
|
|
13728
|
+
lat1: getBitS(msg, p, 8),
|
|
13729
|
+
lon1: getBitS(msg, p + 8, 9),
|
|
13730
|
+
lat2: getBitS(msg, p + 17, 8),
|
|
13731
|
+
lon2: getBitS(msg, p + 25, 9),
|
|
13732
|
+
square: getBitU(msg, p + 34, 1) === 1
|
|
13733
|
+
});
|
|
13734
|
+
}
|
|
13735
|
+
this.mt27.msgs.set(svcNum, { priority, udreInside, regions });
|
|
13736
|
+
return true;
|
|
13737
|
+
}
|
|
13738
|
+
/** δUDRE multiplier (DO-229D Table A-21) for a user location from the MT27
|
|
13739
|
+
* set: the highest-priority containing region wins (ties → lower δUDRE),
|
|
13740
|
+
* else the outside value. 1 when no MT27 has been received. */
|
|
13741
|
+
deltaUdre(latDeg, lonDeg) {
|
|
13742
|
+
const s = this.mt27;
|
|
13743
|
+
if (!s) return 1;
|
|
13744
|
+
let best = null;
|
|
13745
|
+
let bestPri = -1;
|
|
13746
|
+
for (const m of s.msgs.values()) {
|
|
13747
|
+
for (const rg of m.regions) {
|
|
13748
|
+
if (!pointInRegion(latDeg, lonDeg, rg)) continue;
|
|
13749
|
+
const val = DELTA_UDRE[m.udreInside] ?? 1;
|
|
13750
|
+
if (m.priority > bestPri) {
|
|
13751
|
+
bestPri = m.priority;
|
|
13752
|
+
best = val;
|
|
13753
|
+
} else if (m.priority === bestPri && best !== null) {
|
|
13754
|
+
best = Math.min(best, val);
|
|
13755
|
+
}
|
|
13756
|
+
}
|
|
13757
|
+
}
|
|
13758
|
+
return best ?? DELTA_UDRE[s.udreOutside] ?? 1;
|
|
13759
|
+
}
|
|
13668
13760
|
decodeMixed(msg, week, tow) {
|
|
13669
13761
|
if (this.iodp !== getBitU(msg, 110, 2)) return false;
|
|
13670
13762
|
const blk = getBitU(msg, 112, 2);
|
|
@@ -13769,7 +13861,11 @@ var SbasProcessor = class {
|
|
|
13769
13861
|
break;
|
|
13770
13862
|
}
|
|
13771
13863
|
}
|
|
13772
|
-
|
|
13864
|
+
const iodi = getBitU(msg, 22, 2);
|
|
13865
|
+
const cur = this.ion[band];
|
|
13866
|
+
if (cur && cur.iodi === iodi && cur.igp.length === igp.length && cur.igp.every((g, k) => g.lat === igp[k].lat && g.lon === igp[k].lon))
|
|
13867
|
+
return true;
|
|
13868
|
+
this.ion[band] = { iodi, igp };
|
|
13773
13869
|
return true;
|
|
13774
13870
|
}
|
|
13775
13871
|
decodeIgpDelay(msg, week, tow) {
|
|
@@ -13836,7 +13932,7 @@ var SbasProcessor = class {
|
|
|
13836
13932
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
13837
13933
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
13838
13934
|
*/
|
|
13839
|
-
satCorrection(prn, week, tow) {
|
|
13935
|
+
satCorrection(prn, week, tow, userLatRad, userLonRad) {
|
|
13840
13936
|
const idx = this.satIdx.get(prn);
|
|
13841
13937
|
if (idx === void 0) return null;
|
|
13842
13938
|
const s = this.sats[idx];
|
|
@@ -13858,7 +13954,11 @@ var SbasProcessor = class {
|
|
|
13858
13954
|
const tf = now - s.fcorr.t0s + this.tlat;
|
|
13859
13955
|
if (Math.abs(tf) > MAXSBSAGEF || s.fcorr.udre >= 15) return null;
|
|
13860
13956
|
const prc = s.fcorr.prc;
|
|
13861
|
-
const
|
|
13957
|
+
const dudre = userLatRad != null && userLonRad != null ? this.deltaUdre(
|
|
13958
|
+
userLatRad * 180 / Math.PI,
|
|
13959
|
+
userLonRad * 180 / Math.PI
|
|
13960
|
+
) : 1;
|
|
13961
|
+
const sigUdre = Math.sqrt(varfcorr(s.fcorr.udre)) * dudre;
|
|
13862
13962
|
const efc = degfcorr(s.fcorr.ai) * tf * tf / 2;
|
|
13863
13963
|
const eltc = this.degr && s.lcorr && s.lcorr.t0s !== 0 ? sbasLongTermDeg(this.degr, s.lcorr.vel, now - s.lcorr.t0s) : 0;
|
|
13864
13964
|
const varM2 = this.degr?.rssUdre ? sigUdre * sigUdre + efc * efc + eltc * eltc : (sigUdre + efc + eltc) ** 2;
|
|
@@ -15597,7 +15697,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
15597
15697
|
used.push(prn);
|
|
15598
15698
|
azels.push(azel);
|
|
15599
15699
|
if (sbas) {
|
|
15600
|
-
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow);
|
|
15700
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow, rxLat, rxLon);
|
|
15601
15701
|
const si = sbas.ionoDelay(
|
|
15602
15702
|
gpsWeek,
|
|
15603
15703
|
gpsTow,
|
package/dist/index.js
CHANGED
package/dist/positioning.cjs
CHANGED
|
@@ -1746,6 +1746,44 @@ var DEGF = [
|
|
|
1746
1746
|
58e-4
|
|
1747
1747
|
];
|
|
1748
1748
|
var degfcorr = (ai) => ai > 0 && ai <= 15 ? DEGF[ai] : 58e-4;
|
|
1749
|
+
var DELTA_UDRE = [
|
|
1750
|
+
1,
|
|
1751
|
+
1.1,
|
|
1752
|
+
1.25,
|
|
1753
|
+
1.5,
|
|
1754
|
+
2,
|
|
1755
|
+
3,
|
|
1756
|
+
4,
|
|
1757
|
+
5,
|
|
1758
|
+
6,
|
|
1759
|
+
8,
|
|
1760
|
+
10,
|
|
1761
|
+
20,
|
|
1762
|
+
30,
|
|
1763
|
+
40,
|
|
1764
|
+
50,
|
|
1765
|
+
100
|
|
1766
|
+
];
|
|
1767
|
+
function pointInRegion(lat, lon, rg) {
|
|
1768
|
+
const poly = rg.square ? [
|
|
1769
|
+
[rg.lat1, rg.lon1],
|
|
1770
|
+
[rg.lat1, rg.lon2],
|
|
1771
|
+
[rg.lat2, rg.lon2],
|
|
1772
|
+
[rg.lat2, rg.lon1]
|
|
1773
|
+
] : [
|
|
1774
|
+
[rg.lat1, rg.lon1],
|
|
1775
|
+
[rg.lat2, rg.lon2],
|
|
1776
|
+
[rg.lat1, rg.lon2]
|
|
1777
|
+
];
|
|
1778
|
+
let inside = false;
|
|
1779
|
+
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
1780
|
+
const [yi, xi] = poly[i];
|
|
1781
|
+
const [yj, xj] = poly[j];
|
|
1782
|
+
if (yi > lat !== yj > lat && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi)
|
|
1783
|
+
inside = !inside;
|
|
1784
|
+
}
|
|
1785
|
+
return inside;
|
|
1786
|
+
}
|
|
1749
1787
|
function sbasLongTermDeg(d, vel, ageSec) {
|
|
1750
1788
|
if (vel) {
|
|
1751
1789
|
if (ageSec > 0 && ageSec < d.iltcV1) return 0;
|
|
@@ -1811,6 +1849,7 @@ var SbasProcessor = class {
|
|
|
1811
1849
|
iodp = -1;
|
|
1812
1850
|
tlat = 0;
|
|
1813
1851
|
degr = null;
|
|
1852
|
+
mt27 = null;
|
|
1814
1853
|
ion = [];
|
|
1815
1854
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
1816
1855
|
geoPrns = /* @__PURE__ */ new Set();
|
|
@@ -1839,6 +1878,8 @@ var SbasProcessor = class {
|
|
|
1839
1878
|
return this.decodeDegradation(msg) ? type : -1;
|
|
1840
1879
|
case 10:
|
|
1841
1880
|
return this.decodeMt10(msg) ? type : -1;
|
|
1881
|
+
case 27:
|
|
1882
|
+
return this.decodeMt27(msg) ? type : -1;
|
|
1842
1883
|
case 18:
|
|
1843
1884
|
return this.decodeIgpMask(msg) ? type : -1;
|
|
1844
1885
|
case 24:
|
|
@@ -1856,14 +1897,17 @@ var SbasProcessor = class {
|
|
|
1856
1897
|
}
|
|
1857
1898
|
}
|
|
1858
1899
|
decodeMask(msg) {
|
|
1859
|
-
const
|
|
1900
|
+
const iodp = getBitU(msg, 224, 2);
|
|
1901
|
+
const prns = [];
|
|
1860
1902
|
for (let i = 1; i <= 210; i++) {
|
|
1861
|
-
if (getBitU(msg, 13 + i, 1))
|
|
1903
|
+
if (getBitU(msg, 13 + i, 1)) prns.push(maskPrn(i));
|
|
1862
1904
|
}
|
|
1863
|
-
this.iodp
|
|
1864
|
-
|
|
1905
|
+
if (this.iodp === iodp && this.sats.length === prns.length && this.sats.every((s, k) => s.prn === prns[k]))
|
|
1906
|
+
return true;
|
|
1907
|
+
this.iodp = iodp;
|
|
1908
|
+
this.sats = prns.map((prn) => ({ prn }));
|
|
1865
1909
|
this.satIdx.clear();
|
|
1866
|
-
sats.forEach((s, k) => {
|
|
1910
|
+
this.sats.forEach((s, k) => {
|
|
1867
1911
|
if (s.prn) this.satIdx.set(s.prn, k);
|
|
1868
1912
|
});
|
|
1869
1913
|
return true;
|
|
@@ -1931,6 +1975,54 @@ var SbasProcessor = class {
|
|
|
1931
1975
|
};
|
|
1932
1976
|
return true;
|
|
1933
1977
|
}
|
|
1978
|
+
/** MT27 SBAS service message (DO-229D §A.4.4.13, Table A-20): regions with a
|
|
1979
|
+
* δUDRE that inflates σ_UDRE for users inside/outside them. */
|
|
1980
|
+
decodeMt27(msg) {
|
|
1981
|
+
const iods = getBitU(msg, 14, 3);
|
|
1982
|
+
const svcNum = getBitU(msg, 20, 3) + 1;
|
|
1983
|
+
const nRegions = getBitU(msg, 23, 3);
|
|
1984
|
+
const priority = getBitU(msg, 26, 2);
|
|
1985
|
+
const udreInside = getBitU(msg, 28, 4);
|
|
1986
|
+
const udreOutside = getBitU(msg, 32, 4);
|
|
1987
|
+
if (!this.mt27 || this.mt27.iods !== iods)
|
|
1988
|
+
this.mt27 = { iods, udreOutside, msgs: /* @__PURE__ */ new Map() };
|
|
1989
|
+
this.mt27.udreOutside = udreOutside;
|
|
1990
|
+
const regions = [];
|
|
1991
|
+
for (let r = 0; r < nRegions && r < 5; r++) {
|
|
1992
|
+
const p = 36 + r * 35;
|
|
1993
|
+
regions.push({
|
|
1994
|
+
lat1: getBitS(msg, p, 8),
|
|
1995
|
+
lon1: getBitS(msg, p + 8, 9),
|
|
1996
|
+
lat2: getBitS(msg, p + 17, 8),
|
|
1997
|
+
lon2: getBitS(msg, p + 25, 9),
|
|
1998
|
+
square: getBitU(msg, p + 34, 1) === 1
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
this.mt27.msgs.set(svcNum, { priority, udreInside, regions });
|
|
2002
|
+
return true;
|
|
2003
|
+
}
|
|
2004
|
+
/** δUDRE multiplier (DO-229D Table A-21) for a user location from the MT27
|
|
2005
|
+
* set: the highest-priority containing region wins (ties → lower δUDRE),
|
|
2006
|
+
* else the outside value. 1 when no MT27 has been received. */
|
|
2007
|
+
deltaUdre(latDeg, lonDeg) {
|
|
2008
|
+
const s = this.mt27;
|
|
2009
|
+
if (!s) return 1;
|
|
2010
|
+
let best = null;
|
|
2011
|
+
let bestPri = -1;
|
|
2012
|
+
for (const m of s.msgs.values()) {
|
|
2013
|
+
for (const rg of m.regions) {
|
|
2014
|
+
if (!pointInRegion(latDeg, lonDeg, rg)) continue;
|
|
2015
|
+
const val = DELTA_UDRE[m.udreInside] ?? 1;
|
|
2016
|
+
if (m.priority > bestPri) {
|
|
2017
|
+
bestPri = m.priority;
|
|
2018
|
+
best = val;
|
|
2019
|
+
} else if (m.priority === bestPri && best !== null) {
|
|
2020
|
+
best = Math.min(best, val);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
return best ?? DELTA_UDRE[s.udreOutside] ?? 1;
|
|
2025
|
+
}
|
|
1934
2026
|
decodeMixed(msg, week, tow) {
|
|
1935
2027
|
if (this.iodp !== getBitU(msg, 110, 2)) return false;
|
|
1936
2028
|
const blk = getBitU(msg, 112, 2);
|
|
@@ -2035,7 +2127,11 @@ var SbasProcessor = class {
|
|
|
2035
2127
|
break;
|
|
2036
2128
|
}
|
|
2037
2129
|
}
|
|
2038
|
-
|
|
2130
|
+
const iodi = getBitU(msg, 22, 2);
|
|
2131
|
+
const cur = this.ion[band];
|
|
2132
|
+
if (cur && cur.iodi === iodi && cur.igp.length === igp.length && cur.igp.every((g, k) => g.lat === igp[k].lat && g.lon === igp[k].lon))
|
|
2133
|
+
return true;
|
|
2134
|
+
this.ion[band] = { iodi, igp };
|
|
2039
2135
|
return true;
|
|
2040
2136
|
}
|
|
2041
2137
|
decodeIgpDelay(msg, week, tow) {
|
|
@@ -2102,7 +2198,7 @@ var SbasProcessor = class {
|
|
|
2102
2198
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
2103
2199
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
2104
2200
|
*/
|
|
2105
|
-
satCorrection(prn, week, tow) {
|
|
2201
|
+
satCorrection(prn, week, tow, userLatRad, userLonRad) {
|
|
2106
2202
|
const idx = this.satIdx.get(prn);
|
|
2107
2203
|
if (idx === void 0) return null;
|
|
2108
2204
|
const s = this.sats[idx];
|
|
@@ -2124,7 +2220,11 @@ var SbasProcessor = class {
|
|
|
2124
2220
|
const tf = now - s.fcorr.t0s + this.tlat;
|
|
2125
2221
|
if (Math.abs(tf) > MAXSBSAGEF || s.fcorr.udre >= 15) return null;
|
|
2126
2222
|
const prc = s.fcorr.prc;
|
|
2127
|
-
const
|
|
2223
|
+
const dudre = userLatRad != null && userLonRad != null ? this.deltaUdre(
|
|
2224
|
+
userLatRad * 180 / Math.PI,
|
|
2225
|
+
userLonRad * 180 / Math.PI
|
|
2226
|
+
) : 1;
|
|
2227
|
+
const sigUdre = Math.sqrt(varfcorr(s.fcorr.udre)) * dudre;
|
|
2128
2228
|
const efc = degfcorr(s.fcorr.ai) * tf * tf / 2;
|
|
2129
2229
|
const eltc = this.degr && s.lcorr && s.lcorr.t0s !== 0 ? sbasLongTermDeg(this.degr, s.lcorr.vel, now - s.lcorr.t0s) : 0;
|
|
2130
2230
|
const varM2 = this.degr?.rssUdre ? sigUdre * sigUdre + efc * efc + eltc * eltc : (sigUdre + efc + eltc) ** 2;
|
|
@@ -3935,7 +4035,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3935
4035
|
used.push(prn);
|
|
3936
4036
|
azels.push(azel);
|
|
3937
4037
|
if (sbas) {
|
|
3938
|
-
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow);
|
|
4038
|
+
const sc = sbas.satCorrection(prn, gpsWeek, gpsTow, rxLat, rxLon);
|
|
3939
4039
|
const si = sbas.ionoDelay(
|
|
3940
4040
|
gpsWeek,
|
|
3941
4041
|
gpsTow,
|
package/dist/positioning.d.cts
CHANGED
|
@@ -605,6 +605,7 @@ declare class SbasProcessor {
|
|
|
605
605
|
private iodp;
|
|
606
606
|
private tlat;
|
|
607
607
|
private degr;
|
|
608
|
+
private mt27;
|
|
608
609
|
private ion;
|
|
609
610
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
610
611
|
readonly geoPrns: Set<string>;
|
|
@@ -622,6 +623,13 @@ declare class SbasProcessor {
|
|
|
622
623
|
* preamble + 6-bit type (start bit 14), MSB-first, with the listed LSBs.
|
|
623
624
|
* Iiono / Iltc_v0 of 0 must be read as 1 (Table A-9, Note 3). */
|
|
624
625
|
private decodeMt10;
|
|
626
|
+
/** MT27 SBAS service message (DO-229D §A.4.4.13, Table A-20): regions with a
|
|
627
|
+
* δUDRE that inflates σ_UDRE for users inside/outside them. */
|
|
628
|
+
private decodeMt27;
|
|
629
|
+
/** δUDRE multiplier (DO-229D Table A-21) for a user location from the MT27
|
|
630
|
+
* set: the highest-priority containing region wins (ties → lower δUDRE),
|
|
631
|
+
* else the outside value. 1 when no MT27 has been received. */
|
|
632
|
+
deltaUdre(latDeg: number, lonDeg: number): number;
|
|
625
633
|
private decodeMixed;
|
|
626
634
|
private decodeLongCorrHalf;
|
|
627
635
|
private decodeLong0;
|
|
@@ -657,7 +665,7 @@ declare class SbasProcessor {
|
|
|
657
665
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
658
666
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
659
667
|
*/
|
|
660
|
-
satCorrection(prn: string, week: number, tow: number): SbasSatCorrection | null;
|
|
668
|
+
satCorrection(prn: string, week: number, tow: number, userLatRad?: number, userLonRad?: number): SbasSatCorrection | null;
|
|
661
669
|
/** The last-decoded MT10 degradation factors, or null if none seen. */
|
|
662
670
|
get degradation(): Readonly<Degradation> | null;
|
|
663
671
|
/**
|
|
@@ -1292,7 +1300,10 @@ interface SppOptions {
|
|
|
1292
1300
|
/** The two SBAS corrections `solveSpp` consumes — {@link ./sbas.SbasProcessor}
|
|
1293
1301
|
* satisfies this structurally. */
|
|
1294
1302
|
interface SbasCorrectionSource {
|
|
1295
|
-
satCorrection(prn: string, week: number, tow: number
|
|
1303
|
+
satCorrection(prn: string, week: number, tow: number,
|
|
1304
|
+
/** User geodetic lat/lon (rad) for the MT27 service-region δUDRE, when
|
|
1305
|
+
* known (the final protection-level pass). Omitted during iteration. */
|
|
1306
|
+
userLatRad?: number, userLonRad?: number): {
|
|
1296
1307
|
dPos: [number, number, number];
|
|
1297
1308
|
dClkS: number;
|
|
1298
1309
|
varM2: number;
|
package/dist/positioning.d.ts
CHANGED
|
@@ -605,6 +605,7 @@ declare class SbasProcessor {
|
|
|
605
605
|
private iodp;
|
|
606
606
|
private tlat;
|
|
607
607
|
private degr;
|
|
608
|
+
private mt27;
|
|
608
609
|
private ion;
|
|
609
610
|
/** GEO PRN(s) whose messages have been ingested. */
|
|
610
611
|
readonly geoPrns: Set<string>;
|
|
@@ -622,6 +623,13 @@ declare class SbasProcessor {
|
|
|
622
623
|
* preamble + 6-bit type (start bit 14), MSB-first, with the listed LSBs.
|
|
623
624
|
* Iiono / Iltc_v0 of 0 must be read as 1 (Table A-9, Note 3). */
|
|
624
625
|
private decodeMt10;
|
|
626
|
+
/** MT27 SBAS service message (DO-229D §A.4.4.13, Table A-20): regions with a
|
|
627
|
+
* δUDRE that inflates σ_UDRE for users inside/outside them. */
|
|
628
|
+
private decodeMt27;
|
|
629
|
+
/** δUDRE multiplier (DO-229D Table A-21) for a user location from the MT27
|
|
630
|
+
* set: the highest-priority containing region wins (ties → lower δUDRE),
|
|
631
|
+
* else the outside value. 1 when no MT27 has been received. */
|
|
632
|
+
deltaUdre(latDeg: number, lonDeg: number): number;
|
|
625
633
|
private decodeMixed;
|
|
626
634
|
private decodeLongCorrHalf;
|
|
627
635
|
private decodeLong0;
|
|
@@ -657,7 +665,7 @@ declare class SbasProcessor {
|
|
|
657
665
|
* no valid (unexpired) long-term + fast correction is available. Add `dPos`
|
|
658
666
|
* to the satellite ECEF position and `dClkS` to its clock bias.
|
|
659
667
|
*/
|
|
660
|
-
satCorrection(prn: string, week: number, tow: number): SbasSatCorrection | null;
|
|
668
|
+
satCorrection(prn: string, week: number, tow: number, userLatRad?: number, userLonRad?: number): SbasSatCorrection | null;
|
|
661
669
|
/** The last-decoded MT10 degradation factors, or null if none seen. */
|
|
662
670
|
get degradation(): Readonly<Degradation> | null;
|
|
663
671
|
/**
|
|
@@ -1292,7 +1300,10 @@ interface SppOptions {
|
|
|
1292
1300
|
/** The two SBAS corrections `solveSpp` consumes — {@link ./sbas.SbasProcessor}
|
|
1293
1301
|
* satisfies this structurally. */
|
|
1294
1302
|
interface SbasCorrectionSource {
|
|
1295
|
-
satCorrection(prn: string, week: number, tow: number
|
|
1303
|
+
satCorrection(prn: string, week: number, tow: number,
|
|
1304
|
+
/** User geodetic lat/lon (rad) for the MT27 service-region δUDRE, when
|
|
1305
|
+
* known (the final protection-level pass). Omitted during iteration. */
|
|
1306
|
+
userLatRad?: number, userLonRad?: number): {
|
|
1296
1307
|
dPos: [number, number, number];
|
|
1297
1308
|
dClkS: number;
|
|
1298
1309
|
varM2: number;
|
package/dist/positioning.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gnss-js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.61.0",
|
|
4
4
|
"description": "Comprehensive GNSS library for JavaScript — time scales, coordinates, RINEX parsing, RTCM3 decoding, orbit computation, and signal analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|