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.
- package/dist/{chunk-ROVSSEGZ.js → chunk-2IM5JYKP.js} +1 -1
- package/dist/{chunk-EQNC24OB.js → chunk-DEVUKH6A.js} +2 -1
- package/dist/{chunk-KRXX4UGR.js → chunk-VYC2FRDV.js} +518 -12
- package/dist/{chunk-ZIGMN3E5.js → chunk-WQX354KD.js} +1 -0
- package/dist/{chunk-MEDAWNWM.js → chunk-XFPDTGSV.js} +1 -1
- package/dist/index.cjs +524 -23
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -7
- package/dist/novatel.js +2 -2
- package/dist/positioning.cjs +538 -15
- package/dist/positioning.d.cts +66 -1
- package/dist/positioning.d.ts +66 -1
- package/dist/positioning.js +6 -1
- package/dist/rinex.js +2 -2
- package/dist/sbf.js +2 -2
- package/dist/ubx.cjs +1 -0
- package/dist/ubx.d.cts +7 -0
- package/dist/ubx.d.ts +7 -0
- package/dist/ubx.js +2 -2
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
decodeSbasGeoNav,
|
|
11
11
|
sbasCrcOk
|
|
12
|
-
} from "./chunk-
|
|
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
|
|
49
|
-
return F * (5e-9 + amp * (1 -
|
|
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
|
|
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 =
|
|
2611
|
-
const [rxLat, rxLon, rxHgt] = positionSaneIter ? ecefToGeodetic(
|
|
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 -
|
|
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 -
|
|
2626
|
-
const ux = (
|
|
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(
|
|
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 <
|
|
3174
|
+
if (rows < 3 + activeSystems) return null;
|
|
2670
3175
|
const dx = solveLinear(HtH, Htv);
|
|
2671
3176
|
if (!dx) return null;
|
|
2672
|
-
|
|
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:
|
|
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,
|