gnss-js 1.21.0 → 1.23.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-CUWF56ST.js +966 -0
- package/dist/{chunk-DLXTTHRK.js → chunk-MCAVU3XL.js} +118 -9
- package/dist/{chunk-JNFV5BYB.js → chunk-MHGNKSSH.js} +2 -3
- package/dist/{chunk-K3V3NHWL.js → chunk-PZNVFCKJ.js} +25 -766
- package/dist/chunk-VTX3VCL4.js +769 -0
- package/dist/index.cjs +1906 -1099
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -5
- package/dist/orbit.cjs +2 -3
- package/dist/orbit.js +1 -1
- package/dist/positioning.cjs +734 -9
- package/dist/positioning.d.cts +238 -1
- package/dist/positioning.d.ts +238 -1
- package/dist/positioning.js +10 -4
- package/dist/sbf.js +2 -1
- package/dist/ubx.cjs +809 -10
- package/dist/ubx.d.cts +111 -1
- package/dist/ubx.d.ts +111 -1
- package/dist/ubx.js +6 -1
- package/package.json +1 -1
- package/dist/chunk-B5LU4746.js +0 -262
package/dist/index.cjs
CHANGED
|
@@ -82,6 +82,7 @@ __export(src_exports, {
|
|
|
82
82
|
RINEX_CODES: () => RINEX_CODES,
|
|
83
83
|
RTCM3_MESSAGE_NAMES: () => RTCM3_MESSAGE_NAMES,
|
|
84
84
|
Rtcm3Decoder: () => Rtcm3Decoder,
|
|
85
|
+
RtkFloatEngine: () => RtkFloatEngine,
|
|
85
86
|
SECONDS_IN_DAY: () => SECONDS_IN_DAY,
|
|
86
87
|
SECONDS_IN_HOUR: () => SECONDS_IN_HOUR,
|
|
87
88
|
SECONDS_IN_MINUTE: () => SECONDS_IN_MINUTE,
|
|
@@ -241,6 +242,7 @@ __export(src_exports, {
|
|
|
241
242
|
parseUbxCnav: () => parseUbxCnav,
|
|
242
243
|
parseUbxIonoUtc: () => parseUbxIonoUtc,
|
|
243
244
|
parseUbxNav: () => parseUbxNav,
|
|
245
|
+
parseUbxRawNav: () => parseUbxRawNav,
|
|
244
246
|
parseUbxRawx: () => parseUbxRawx,
|
|
245
247
|
phiAltBOC: () => phiAltBOC,
|
|
246
248
|
phiBOCc: () => phiBOCc,
|
|
@@ -259,11 +261,13 @@ __export(src_exports, {
|
|
|
259
261
|
selectEphemeris: () => selectEphemeris,
|
|
260
262
|
setGloFreqNumber: () => setGloFreqNumber,
|
|
261
263
|
setRtcm3DebugHandler: () => setRtcm3DebugHandler,
|
|
264
|
+
solveDgnss: () => solveDgnss,
|
|
262
265
|
solveSpp: () => solveSpp,
|
|
263
266
|
sp3Position: () => sp3Position,
|
|
264
267
|
stationHeaderLines: () => stationHeaderLines,
|
|
265
268
|
systemCmp: () => systemCmp,
|
|
266
269
|
systemName: () => systemName,
|
|
270
|
+
toRtkEpoch: () => toRtkEpoch,
|
|
267
271
|
transformFrame: () => transformFrame,
|
|
268
272
|
ubxFrames: () => ubxFrames,
|
|
269
273
|
updateStationMeta: () => updateStationMeta,
|
|
@@ -5404,541 +5408,288 @@ function parseUbxCnav(data) {
|
|
|
5404
5408
|
return { ephemerides, badCrc, messages };
|
|
5405
5409
|
}
|
|
5406
5410
|
|
|
5407
|
-
// src/
|
|
5408
|
-
var
|
|
5409
|
-
var
|
|
5410
|
-
var
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
// E1C/B, E5bI/Q
|
|
5417
|
-
3: { 0: ["C", "2I"], 1: ["C", "2I"], 2: ["C", "7I"], 3: ["C", "7I"] },
|
|
5418
|
-
// B1I D1/D2, B2I D1/D2
|
|
5419
|
-
5: { 0: ["J", "1C"], 4: ["J", "2X"], 5: ["J", "2X"] },
|
|
5420
|
-
// QZSS L1C/A, L2CM/CL
|
|
5421
|
-
6: { 0: ["R", "1C"], 2: ["R", "2C"] }
|
|
5422
|
-
// GLONASS L1OF, L2OF
|
|
5423
|
-
};
|
|
5424
|
-
function prnFor(gnssId, svId) {
|
|
5425
|
-
const two4 = (n) => String(n).padStart(2, "0");
|
|
5426
|
-
switch (gnssId) {
|
|
5427
|
-
case 0:
|
|
5428
|
-
return svId >= 1 && svId <= 32 ? `G${two4(svId)}` : null;
|
|
5429
|
-
case 1:
|
|
5430
|
-
return svId >= 120 && svId <= 158 ? `S${two4(svId - 100)}` : null;
|
|
5431
|
-
case 2:
|
|
5432
|
-
return svId >= 1 && svId <= 36 ? `E${two4(svId)}` : null;
|
|
5433
|
-
case 3:
|
|
5434
|
-
return svId >= 1 && svId <= 63 ? `C${two4(svId)}` : null;
|
|
5435
|
-
case 5:
|
|
5436
|
-
return svId >= 1 && svId <= 10 ? `J${two4(svId)}` : null;
|
|
5437
|
-
case 6:
|
|
5438
|
-
return svId >= 1 && svId <= 32 ? `R${two4(svId)}` : null;
|
|
5439
|
-
default:
|
|
5440
|
-
return null;
|
|
5441
|
-
}
|
|
5411
|
+
// src/navbits/bds.ts
|
|
5412
|
+
var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
|
|
5413
|
+
var SEC_PER_WEEK4 = 7 * 86400;
|
|
5414
|
+
var MS_PER_WEEK2 = SEC_PER_WEEK4 * 1e3;
|
|
5415
|
+
var HALF_WEEK2 = 302400;
|
|
5416
|
+
var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK4;
|
|
5417
|
+
var BDS_SUBFRAME_BYTES = 38;
|
|
5418
|
+
function getBitU2(b, p1, l1, p2, l2) {
|
|
5419
|
+
return getBitU(b, p1, l1) * 2 ** l2 + getBitU(b, p2, l2);
|
|
5442
5420
|
}
|
|
5443
|
-
function
|
|
5444
|
-
|
|
5445
|
-
const epochs = [];
|
|
5446
|
-
const messageCounts = {};
|
|
5447
|
-
const obsCodes = {};
|
|
5448
|
-
const stats = { badChecksums: 0 };
|
|
5449
|
-
for (const frame of ubxFrames(data, stats)) {
|
|
5450
|
-
const { msgClass: cls, msgId: id } = frame;
|
|
5451
|
-
const len = frame.payload.length;
|
|
5452
|
-
const key = `${cls.toString(16).padStart(2, "0")}-${id.toString(16).padStart(2, "0")}`;
|
|
5453
|
-
messageCounts[key] = (messageCounts[key] ?? 0) + 1;
|
|
5454
|
-
if (cls === 2 && id === 21 && len >= 16) {
|
|
5455
|
-
const p = frame.payloadStart;
|
|
5456
|
-
const rcvTow = view.getFloat64(p, true);
|
|
5457
|
-
const week = view.getUint16(p + 8, true);
|
|
5458
|
-
const leapS = view.getInt8(p + 10);
|
|
5459
|
-
const numMeas = data[p + 11];
|
|
5460
|
-
const recStat = data[p + 12];
|
|
5461
|
-
const leapValid = (recStat & 1) !== 0;
|
|
5462
|
-
const meas = [];
|
|
5463
|
-
for (let k = 0; k < numMeas; k++) {
|
|
5464
|
-
const off = p + 16 + 32 * k;
|
|
5465
|
-
if (off + 32 > p + len) break;
|
|
5466
|
-
const gnssId = data[off + 20];
|
|
5467
|
-
const svId = data[off + 21];
|
|
5468
|
-
const sigId = data[off + 22];
|
|
5469
|
-
const trkStat = data[off + 30];
|
|
5470
|
-
const prn = prnFor(gnssId, svId);
|
|
5471
|
-
const sig = SIGNALS[gnssId]?.[sigId];
|
|
5472
|
-
if (!prn || !sig) continue;
|
|
5473
|
-
const prValid = (trkStat & 1) !== 0;
|
|
5474
|
-
const cpValid = (trkStat & 2) !== 0;
|
|
5475
|
-
const cp = view.getFloat64(off + 8, true);
|
|
5476
|
-
meas.push({
|
|
5477
|
-
prn,
|
|
5478
|
-
code: sig[1],
|
|
5479
|
-
pr: prValid ? view.getFloat64(off, true) : null,
|
|
5480
|
-
cp: cpValid && cp !== 0 ? cp : null,
|
|
5481
|
-
doppler: view.getFloat32(off + 16, true),
|
|
5482
|
-
cn0: data[off + 26],
|
|
5483
|
-
halfCycleAmbiguous: (trkStat & 4) !== 0,
|
|
5484
|
-
lockTimeMs: view.getUint16(off + 24, true)
|
|
5485
|
-
});
|
|
5486
|
-
const sys = sig[0];
|
|
5487
|
-
const codes = obsCodes[sys] ??= [];
|
|
5488
|
-
if (!codes.includes(sig[1])) codes.push(sig[1]);
|
|
5489
|
-
}
|
|
5490
|
-
epochs.push({
|
|
5491
|
-
timeMs: GPS_EPOCH_MS4 + week * MS_PER_WEEK2 + Math.round(rcvTow * 1e3),
|
|
5492
|
-
leapS: leapValid ? leapS : null,
|
|
5493
|
-
meas
|
|
5494
|
-
});
|
|
5495
|
-
}
|
|
5496
|
-
}
|
|
5497
|
-
return { epochs, messageCounts, obsCodes, badChecksums: stats.badChecksums };
|
|
5421
|
+
function getBitS2(b, p1, l1, p2, l2) {
|
|
5422
|
+
return getBitS(b, p1, l1) * 2 ** l2 + getBitU(b, p2, l2);
|
|
5498
5423
|
}
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
var CRC_TABLE = (() => {
|
|
5502
|
-
const t = new Uint16Array(256);
|
|
5503
|
-
for (let n = 0; n < 256; n++) {
|
|
5504
|
-
let c = n << 8;
|
|
5505
|
-
for (let k = 0; k < 8; k++)
|
|
5506
|
-
c = c & 32768 ? (c << 1 ^ 4129) & 65535 : c << 1 & 65535;
|
|
5507
|
-
t[n] = c;
|
|
5508
|
-
}
|
|
5509
|
-
return t;
|
|
5510
|
-
})();
|
|
5511
|
-
function crc16(data, start, end) {
|
|
5512
|
-
let crc = 0;
|
|
5513
|
-
for (let i = start; i < end; i++)
|
|
5514
|
-
crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
|
|
5515
|
-
return crc;
|
|
5424
|
+
function getBitU3(b, p1, l1, p2, l2, p3, l3) {
|
|
5425
|
+
return getBitU(b, p1, l1) * 2 ** (l2 + l3) + getBitU(b, p2, l2) * 2 ** l3 + getBitU(b, p3, l3);
|
|
5516
5426
|
}
|
|
5517
|
-
function
|
|
5518
|
-
|
|
5519
|
-
let i = 0;
|
|
5520
|
-
while (i + 8 <= data.length) {
|
|
5521
|
-
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
5522
|
-
i++;
|
|
5523
|
-
continue;
|
|
5524
|
-
}
|
|
5525
|
-
const len = view.getUint16(i + 6, true);
|
|
5526
|
-
if (len < 8 || len % 4 !== 0) {
|
|
5527
|
-
i++;
|
|
5528
|
-
continue;
|
|
5529
|
-
}
|
|
5530
|
-
if (i + len > data.length) break;
|
|
5531
|
-
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
5532
|
-
badCrc++;
|
|
5533
|
-
i++;
|
|
5534
|
-
continue;
|
|
5535
|
-
}
|
|
5536
|
-
onBlock(view.getUint16(i + 4, true) & 8191, i, len);
|
|
5537
|
-
i += len;
|
|
5538
|
-
}
|
|
5539
|
-
return badCrc;
|
|
5427
|
+
function getBitS3(b, p1, l1, p2, l2, p3, l3) {
|
|
5428
|
+
return getBitS(b, p1, l1) * 2 ** (l2 + l3) + getBitU(b, p2, l2) * 2 ** l3 + getBitU(b, p3, l3);
|
|
5540
5429
|
}
|
|
5541
|
-
var
|
|
5542
|
-
function
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
if (svid <= 70) return null;
|
|
5548
|
-
if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
|
|
5549
|
-
if (svid <= 119) return null;
|
|
5550
|
-
if (svid <= 140) return `S${two(svid - 100)}`;
|
|
5551
|
-
if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
|
|
5552
|
-
if (svid <= 190) return `J${two(svid - 180)}`;
|
|
5553
|
-
if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
|
|
5554
|
-
if (svid <= 215) return `S${two(svid - 157)}`;
|
|
5555
|
-
if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
|
|
5556
|
-
if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
|
|
5557
|
-
return null;
|
|
5430
|
+
var mergeS = (hi, lo, n) => hi * 2 ** n + lo;
|
|
5431
|
+
function bchOk(cw) {
|
|
5432
|
+
for (let i = 14; i >= 4; i--) {
|
|
5433
|
+
if (cw & 1 << i) cw ^= 19 << i - 4;
|
|
5434
|
+
}
|
|
5435
|
+
return cw === 0;
|
|
5558
5436
|
}
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
let offset = refWeek % mod - wn;
|
|
5570
|
-
if (offset > mod / 2) offset -= mod;
|
|
5571
|
-
if (offset < -(mod / 2 - 1)) offset += mod;
|
|
5572
|
-
return refWeek - offset;
|
|
5437
|
+
function bdsSubframeParityOk(subframe) {
|
|
5438
|
+
if (subframe.length < BDS_SUBFRAME_BYTES) return false;
|
|
5439
|
+
if (!bchOk(getBitU(subframe, 15, 15))) return false;
|
|
5440
|
+
for (let w = 1; w < 10; w++) {
|
|
5441
|
+
const base = 30 * w;
|
|
5442
|
+
const cw1 = getBitU(subframe, base, 11) * 16 + getBitU(subframe, base + 22, 4);
|
|
5443
|
+
const cw2 = getBitU(subframe, base + 11, 11) * 16 + getBitU(subframe, base + 26, 4);
|
|
5444
|
+
if (!bchOk(cw1) || !bchOk(cw2)) return false;
|
|
5445
|
+
}
|
|
5446
|
+
return true;
|
|
5573
5447
|
}
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
const
|
|
5579
|
-
const max = sys === "G" ? 32 : 10;
|
|
5580
|
-
if (prn < 1 || prn > max) return null;
|
|
5581
|
-
const wnc = view.getUint16(b + 12, true);
|
|
5582
|
-
const tgdRaw = view.getFloat32(b + 28, true);
|
|
5583
|
-
const tocs = view.getUint32(b + 32, true);
|
|
5584
|
-
const toes = view.getUint32(b + 88, true);
|
|
5585
|
-
const wnToc = adjustWeek(wnc, view.getUint16(b + 136, true), 1024);
|
|
5586
|
-
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
5448
|
+
function buildBdsEphemeris(prn, f) {
|
|
5449
|
+
let week = f.week;
|
|
5450
|
+
if (f.toes < f.sow - HALF_WEEK2) week++;
|
|
5451
|
+
else if (f.toes > f.sow + HALF_WEEK2) week--;
|
|
5452
|
+
const tocDate = new Date(BDT_EPOCH_MS + week * MS_PER_WEEK2 + f.tocSec * 1e3);
|
|
5587
5453
|
return {
|
|
5588
|
-
system:
|
|
5589
|
-
prn
|
|
5454
|
+
system: "C",
|
|
5455
|
+
prn,
|
|
5590
5456
|
toc: sowOf(tocDate.getTime()),
|
|
5591
5457
|
tocDate,
|
|
5592
|
-
af0:
|
|
5593
|
-
af1:
|
|
5594
|
-
af2:
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
week
|
|
5616
|
-
svHealth:
|
|
5617
|
-
|
|
5458
|
+
af0: f.af0,
|
|
5459
|
+
af1: f.af1,
|
|
5460
|
+
af2: f.af2,
|
|
5461
|
+
// AODE is not in the ephemeris subframes; RTKLIB derives the RINEX
|
|
5462
|
+
// IODE/AODE slot from toc per the BDS ICD update schedule.
|
|
5463
|
+
iode: Math.floor(f.tocSec / 720) % 240,
|
|
5464
|
+
crs: f.crs,
|
|
5465
|
+
deltaN: f.deltaN,
|
|
5466
|
+
m0: f.m0,
|
|
5467
|
+
cuc: f.cuc,
|
|
5468
|
+
e: f.e,
|
|
5469
|
+
cus: f.cus,
|
|
5470
|
+
sqrtA: f.sqrtA,
|
|
5471
|
+
toe: f.toes,
|
|
5472
|
+
cic: f.cic,
|
|
5473
|
+
omega0: f.omega0,
|
|
5474
|
+
cis: f.cis,
|
|
5475
|
+
i0: f.i0,
|
|
5476
|
+
crc: f.crc,
|
|
5477
|
+
omega: f.omega,
|
|
5478
|
+
omegaDot: f.omegaDot,
|
|
5479
|
+
idot: f.idot,
|
|
5480
|
+
week,
|
|
5481
|
+
// RINEX BDS week field is the BDT week of toe
|
|
5482
|
+
svHealth: f.svh,
|
|
5483
|
+
// SatH1
|
|
5484
|
+
tgd: f.tgd1
|
|
5485
|
+
// TGD1 (B1) — RINEX slot
|
|
5618
5486
|
};
|
|
5619
5487
|
}
|
|
5620
|
-
function
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
const
|
|
5626
|
-
const
|
|
5627
|
-
const
|
|
5628
|
-
const
|
|
5629
|
-
const
|
|
5630
|
-
const
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
const
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
af1: view.getFloat32(b + 40, true),
|
|
5686
|
-
af2: view.getFloat32(b + 36, true),
|
|
5687
|
-
iode: view.getUint8(b + 21),
|
|
5688
|
-
// AODE
|
|
5689
|
-
crs: view.getFloat32(b + 48, true),
|
|
5690
|
-
deltaN: view.getFloat32(b + 52, true) * PI2,
|
|
5691
|
-
m0: view.getFloat64(b + 56, true) * PI2,
|
|
5692
|
-
cuc: view.getFloat32(b + 64, true),
|
|
5693
|
-
e: view.getFloat64(b + 68, true),
|
|
5694
|
-
cus: view.getFloat32(b + 76, true),
|
|
5695
|
-
sqrtA: view.getFloat64(b + 80, true),
|
|
5696
|
-
toe: toes,
|
|
5697
|
-
cic: view.getFloat32(b + 92, true),
|
|
5698
|
-
omega0: view.getFloat64(b + 96, true) * PI2,
|
|
5699
|
-
cis: view.getFloat32(b + 104, true),
|
|
5700
|
-
i0: view.getFloat64(b + 108, true) * PI2,
|
|
5701
|
-
crc: view.getFloat32(b + 116, true),
|
|
5702
|
-
omega: view.getFloat64(b + 120, true) * PI2,
|
|
5703
|
-
omegaDot: view.getFloat32(b + 128, true) * PI2,
|
|
5704
|
-
idot: view.getFloat32(b + 132, true) * PI2,
|
|
5705
|
-
week: wnToe,
|
|
5706
|
-
// RINEX BDS week field is the BDT week
|
|
5707
|
-
svHealth: view.getUint8(b + 19),
|
|
5708
|
-
// SatH1
|
|
5709
|
-
tgd: tgd1 !== F4_DNU ? tgd1 : 0
|
|
5710
|
-
// TGD1 B1/B3 (RINEX slot)
|
|
5711
|
-
};
|
|
5712
|
-
}
|
|
5713
|
-
function decodeGloNav(view, b) {
|
|
5714
|
-
const prn = svidToPrn(view.getUint8(b + 14));
|
|
5715
|
-
if (!prn || prn[0] !== "R") return null;
|
|
5716
|
-
const wnc = view.getUint16(b + 12, true);
|
|
5717
|
-
const toes = view.getUint32(b + 76, true);
|
|
5718
|
-
const wnToe = adjustWeek(wnc, view.getUint16(b + 80, true), 1024);
|
|
5719
|
-
const toeGpsMs = gpsMs(wnToe, toes);
|
|
5720
|
-
const leapMs = getGpsLeap(new Date(toeGpsMs)) * 1e3;
|
|
5721
|
-
const tocDate = new Date(toeGpsMs - leapMs);
|
|
5722
|
-
const tofGpsMs = gpsMs(wnc, 0) + view.getUint32(b + 8, true);
|
|
5723
|
-
const tofLeapMs = getGpsLeap(new Date(tofGpsMs)) * 1e3;
|
|
5724
|
-
const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS5) / 1e3 % SEC_PER_WEEK4;
|
|
5725
|
-
return {
|
|
5726
|
-
system: "R",
|
|
5727
|
-
prn,
|
|
5728
|
-
tocDate,
|
|
5729
|
-
// RINEX stores −τn as the clock bias; SBF carries τn (ICD sign)
|
|
5730
|
-
tauN: -view.getFloat32(b + 68, true),
|
|
5731
|
-
gammaN: view.getFloat32(b + 64, true),
|
|
5732
|
-
messageFrameTime,
|
|
5733
|
-
x: view.getFloat64(b + 16, true),
|
|
5734
|
-
// SBF unit is km, like RINEX
|
|
5735
|
-
xDot: view.getFloat32(b + 40, true),
|
|
5736
|
-
xAcc: view.getFloat32(b + 52, true),
|
|
5737
|
-
y: view.getFloat64(b + 24, true),
|
|
5738
|
-
yDot: view.getFloat32(b + 44, true),
|
|
5739
|
-
yAcc: view.getFloat32(b + 56, true),
|
|
5740
|
-
z: view.getFloat64(b + 32, true),
|
|
5741
|
-
zDot: view.getFloat32(b + 48, true),
|
|
5742
|
-
zAcc: view.getFloat32(b + 60, true),
|
|
5743
|
-
// MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
|
|
5744
|
-
health: view.getUint8(b + 85) >> 2,
|
|
5745
|
-
freqNum: view.getUint8(b + 15) - 8
|
|
5746
|
-
};
|
|
5747
|
-
}
|
|
5748
|
-
function parseSbfNav(data) {
|
|
5749
|
-
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
5750
|
-
const ephemerides = [];
|
|
5751
|
-
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
5752
|
-
let eph = null;
|
|
5753
|
-
if (id === 5891 && len >= 140) eph = decodeGpsQzsNav(view, b, "G");
|
|
5754
|
-
else if (id === 4095 && len >= 140) eph = decodeGpsQzsNav(view, b, "J");
|
|
5755
|
-
else if (id === 4002 && len >= 149) eph = decodeGalNav(view, b);
|
|
5756
|
-
else if (id === 4081 && len >= 140) eph = decodeBdsNav(view, b);
|
|
5757
|
-
else if (id === 4004 && len >= 96) eph = decodeGloNav(view, b);
|
|
5758
|
-
if (eph) ephemerides.push(eph);
|
|
5488
|
+
function decodeBdsD1Frame(subframes, opts = {}) {
|
|
5489
|
+
if (subframes.length < 3 * BDS_SUBFRAME_BYTES) return null;
|
|
5490
|
+
const b = subframes;
|
|
5491
|
+
let i = 8 * 38 * 0;
|
|
5492
|
+
const frn1 = getBitU(b, i + 15, 3);
|
|
5493
|
+
const sow1 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5494
|
+
const svh = getBitU(b, i + 42, 1);
|
|
5495
|
+
const week = getBitU(b, i + 60, 13);
|
|
5496
|
+
const tocSec = getBitU2(b, i + 73, 9, i + 90, 8) * 8;
|
|
5497
|
+
const tgd1 = getBitS(b, i + 98, 10) * 0.1 * 1e-9;
|
|
5498
|
+
const af2 = getBitS(b, i + 214, 11) * 2 ** -66;
|
|
5499
|
+
const af0 = getBitS2(b, i + 225, 7, i + 240, 17) * 2 ** -33;
|
|
5500
|
+
const af1 = getBitS2(b, i + 257, 5, i + 270, 17) * 2 ** -50;
|
|
5501
|
+
i = 8 * 38 * 1;
|
|
5502
|
+
const frn2 = getBitU(b, i + 15, 3);
|
|
5503
|
+
const sow2 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5504
|
+
const deltaN = getBitS2(b, i + 42, 10, i + 60, 6) * 2 ** -43 * GPS_PI;
|
|
5505
|
+
const cuc = getBitS2(b, i + 66, 16, i + 90, 2) * 2 ** -31;
|
|
5506
|
+
const m0 = getBitS2(b, i + 92, 20, i + 120, 12) * 2 ** -31 * GPS_PI;
|
|
5507
|
+
const e = getBitU2(b, i + 132, 10, i + 150, 22) * 2 ** -33;
|
|
5508
|
+
const cus = getBitS(b, i + 180, 18) * 2 ** -31;
|
|
5509
|
+
const crc = getBitS2(b, i + 198, 4, i + 210, 14) * 2 ** -6;
|
|
5510
|
+
const crs = getBitS2(b, i + 224, 8, i + 240, 10) * 2 ** -6;
|
|
5511
|
+
const sqrtA = getBitU2(b, i + 250, 12, i + 270, 20) * 2 ** -19;
|
|
5512
|
+
const toe1 = getBitU(b, i + 290, 2);
|
|
5513
|
+
i = 8 * 38 * 2;
|
|
5514
|
+
const frn3 = getBitU(b, i + 15, 3);
|
|
5515
|
+
const sow3 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5516
|
+
const toe2 = getBitU2(b, i + 42, 10, i + 60, 5);
|
|
5517
|
+
const i0 = getBitS2(b, i + 65, 17, i + 90, 15) * 2 ** -31 * GPS_PI;
|
|
5518
|
+
const cic = getBitS2(b, i + 105, 7, i + 120, 11) * 2 ** -31;
|
|
5519
|
+
const omegaDot = getBitS2(b, i + 131, 11, i + 150, 13) * 2 ** -43 * GPS_PI;
|
|
5520
|
+
const cis = getBitS2(b, i + 163, 9, i + 180, 9) * 2 ** -31;
|
|
5521
|
+
const idot = getBitS2(b, i + 189, 13, i + 210, 1) * 2 ** -43 * GPS_PI;
|
|
5522
|
+
const omega0 = getBitS2(b, i + 211, 21, i + 240, 11) * 2 ** -31 * GPS_PI;
|
|
5523
|
+
const omega = getBitS2(b, i + 251, 11, i + 270, 21) * 2 ** -31 * GPS_PI;
|
|
5524
|
+
const toes = (toe1 * 2 ** 15 + toe2) * 8;
|
|
5525
|
+
if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3) return null;
|
|
5526
|
+
if (sow2 !== sow1 + 6 || sow3 !== sow2 + 6) return null;
|
|
5527
|
+
if (tocSec !== toes) return null;
|
|
5528
|
+
return buildBdsEphemeris(opts.prn ?? "C00", {
|
|
5529
|
+
week,
|
|
5530
|
+
sow: sow1,
|
|
5531
|
+
toes,
|
|
5532
|
+
tocSec,
|
|
5533
|
+
svh,
|
|
5534
|
+
tgd1,
|
|
5535
|
+
af0,
|
|
5536
|
+
af1,
|
|
5537
|
+
af2,
|
|
5538
|
+
crs,
|
|
5539
|
+
deltaN,
|
|
5540
|
+
m0,
|
|
5541
|
+
cuc,
|
|
5542
|
+
e,
|
|
5543
|
+
cus,
|
|
5544
|
+
sqrtA,
|
|
5545
|
+
cic,
|
|
5546
|
+
omega0,
|
|
5547
|
+
cis,
|
|
5548
|
+
i0,
|
|
5549
|
+
crc,
|
|
5550
|
+
omega,
|
|
5551
|
+
omegaDot,
|
|
5552
|
+
idot
|
|
5759
5553
|
});
|
|
5760
|
-
return { ephemerides, badCrc };
|
|
5761
5554
|
}
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
const
|
|
5767
|
-
const
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5555
|
+
function decodeBdsD2Frame(pages, opts = {}) {
|
|
5556
|
+
if (pages.length < 10 * BDS_SUBFRAME_BYTES) return null;
|
|
5557
|
+
const b = pages;
|
|
5558
|
+
let i = 8 * 38 * 0;
|
|
5559
|
+
const pgn1 = getBitU(b, i + 42, 4);
|
|
5560
|
+
const sow1 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5561
|
+
const svh = getBitU(b, i + 46, 1);
|
|
5562
|
+
const week = getBitU(b, i + 64, 13);
|
|
5563
|
+
const tocSec = getBitU2(b, i + 77, 5, i + 90, 12) * 8;
|
|
5564
|
+
const tgd1 = getBitS(b, i + 102, 10) * 0.1 * 1e-9;
|
|
5565
|
+
i = 8 * 38 * 2;
|
|
5566
|
+
const pgn3 = getBitU(b, i + 42, 4);
|
|
5567
|
+
const sow3 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5568
|
+
const af0 = getBitS2(b, i + 100, 12, i + 120, 12) * 2 ** -33;
|
|
5569
|
+
const af1p3 = getBitS(b, i + 132, 4);
|
|
5570
|
+
i = 8 * 38 * 3;
|
|
5571
|
+
const pgn4 = getBitU(b, i + 42, 4);
|
|
5572
|
+
const sow4 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5573
|
+
const af1p4 = getBitU2(b, i + 46, 6, i + 60, 12);
|
|
5574
|
+
const af2 = getBitS2(b, i + 72, 10, i + 90, 1) * 2 ** -66;
|
|
5575
|
+
const deltaN = getBitS(b, i + 96, 16) * 2 ** -43 * GPS_PI;
|
|
5576
|
+
const cucp4 = getBitS(b, i + 120, 14);
|
|
5577
|
+
i = 8 * 38 * 4;
|
|
5578
|
+
const pgn5 = getBitU(b, i + 42, 4);
|
|
5579
|
+
const sow5 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5580
|
+
const cucp5 = getBitU(b, i + 46, 4);
|
|
5581
|
+
const m0 = getBitS3(b, i + 50, 2, i + 60, 22, i + 90, 8) * 2 ** -31 * GPS_PI;
|
|
5582
|
+
const cus = getBitS2(b, i + 98, 14, i + 120, 4) * 2 ** -31;
|
|
5583
|
+
const ep5 = getBitS(b, i + 124, 10);
|
|
5584
|
+
i = 8 * 38 * 5;
|
|
5585
|
+
const pgn6 = getBitU(b, i + 42, 4);
|
|
5586
|
+
const sow6 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5587
|
+
const ep6 = getBitU2(b, i + 46, 6, i + 60, 16);
|
|
5588
|
+
const sqrtA = getBitU3(b, i + 76, 6, i + 90, 22, i + 120, 4) * 2 ** -19;
|
|
5589
|
+
const cicp6 = getBitS(b, i + 124, 10);
|
|
5590
|
+
i = 8 * 38 * 6;
|
|
5591
|
+
const pgn7 = getBitU(b, i + 42, 4);
|
|
5592
|
+
const sow7 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5593
|
+
const cicp7 = getBitU2(b, i + 46, 6, i + 60, 2);
|
|
5594
|
+
const cis = getBitS(b, i + 62, 18) * 2 ** -31;
|
|
5595
|
+
const toes = getBitU2(b, i + 80, 2, i + 90, 15) * 8;
|
|
5596
|
+
const i0p7 = getBitS2(b, i + 105, 7, i + 120, 14);
|
|
5597
|
+
i = 8 * 38 * 7;
|
|
5598
|
+
const pgn8 = getBitU(b, i + 42, 4);
|
|
5599
|
+
const sow8 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5600
|
+
const i0p8 = getBitU2(b, i + 46, 6, i + 60, 5);
|
|
5601
|
+
const crc = getBitS2(b, i + 65, 17, i + 90, 1) * 2 ** -6;
|
|
5602
|
+
const crs = getBitS(b, i + 91, 18) * 2 ** -6;
|
|
5603
|
+
const omegaDotP8 = getBitS2(b, i + 109, 3, i + 120, 16);
|
|
5604
|
+
i = 8 * 38 * 8;
|
|
5605
|
+
const pgn9 = getBitU(b, i + 42, 4);
|
|
5606
|
+
const sow9 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5607
|
+
const omegaDotP9 = getBitU(b, i + 46, 5);
|
|
5608
|
+
const omega0 = getBitS3(b, i + 51, 1, i + 60, 22, i + 90, 9) * 2 ** -31 * GPS_PI;
|
|
5609
|
+
const omegaP9 = getBitS2(b, i + 99, 13, i + 120, 14);
|
|
5610
|
+
i = 8 * 38 * 9;
|
|
5611
|
+
const pgn10 = getBitU(b, i + 42, 4);
|
|
5612
|
+
const sow10 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5613
|
+
const omegaP10 = getBitU(b, i + 46, 5);
|
|
5614
|
+
const idot = getBitS2(b, i + 51, 1, i + 60, 13) * 2 ** -43 * GPS_PI;
|
|
5615
|
+
if (pgn1 !== 1 || pgn3 !== 3 || pgn4 !== 4 || pgn5 !== 5 || pgn6 !== 6 || pgn7 !== 7 || pgn8 !== 8 || pgn9 !== 9 || pgn10 !== 10) {
|
|
5616
|
+
return null;
|
|
5787
5617
|
}
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5618
|
+
if (sow3 !== sow1 + 6 || sow4 !== sow3 + 3 || sow5 !== sow4 + 3 || sow6 !== sow5 + 3 || sow7 !== sow6 + 3 || sow8 !== sow7 + 3 || sow9 !== sow8 + 3 || sow10 !== sow9 + 3) {
|
|
5619
|
+
return null;
|
|
5620
|
+
}
|
|
5621
|
+
if (tocSec !== toes) return null;
|
|
5622
|
+
return buildBdsEphemeris(opts.prn ?? "C00", {
|
|
5623
|
+
week,
|
|
5624
|
+
sow: sow1,
|
|
5625
|
+
toes,
|
|
5626
|
+
tocSec,
|
|
5627
|
+
svh,
|
|
5628
|
+
tgd1,
|
|
5629
|
+
af0,
|
|
5630
|
+
af1: mergeS(af1p3, af1p4, 18) * 2 ** -50,
|
|
5631
|
+
af2,
|
|
5632
|
+
crs,
|
|
5633
|
+
deltaN,
|
|
5634
|
+
m0,
|
|
5635
|
+
cuc: mergeS(cucp4, cucp5, 4) * 2 ** -31,
|
|
5636
|
+
e: mergeS(ep5, ep6, 22) * 2 ** -33,
|
|
5637
|
+
cus,
|
|
5793
5638
|
sqrtA,
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
health
|
|
5803
|
-
};
|
|
5804
|
-
}
|
|
5805
|
-
function decodeBdsAlm(view, b) {
|
|
5806
|
-
const prnStr = svidToPrn(view.getUint8(b + 14));
|
|
5807
|
-
if (!prnStr || prnStr[0] !== "C") return null;
|
|
5808
|
-
const prn = parseInt(prnStr.slice(1), 10);
|
|
5809
|
-
const toa = view.getUint32(b + 16, true);
|
|
5810
|
-
if (toa === U4_DNU) return null;
|
|
5811
|
-
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
5812
|
-
const geo = prn <= 5 || prn >= 59;
|
|
5813
|
-
return {
|
|
5814
|
-
system: "C",
|
|
5815
|
-
prn: prnStr,
|
|
5816
|
-
weekAlm: adjustWeek(bdsWeekRef, view.getUint8(b + 15), 256),
|
|
5817
|
-
toaSec: toa,
|
|
5818
|
-
sqrtA: view.getFloat32(b + 20, true),
|
|
5819
|
-
e: view.getFloat32(b + 24, true),
|
|
5820
|
-
i0OrDeltaI: (geo ? 0 : I_REF_03) + view.getFloat32(b + 44, true) * PI2,
|
|
5821
|
-
omega0: view.getFloat32(b + 36, true) * PI2,
|
|
5822
|
-
omega: view.getFloat32(b + 28, true) * PI2,
|
|
5823
|
-
m0: view.getFloat32(b + 32, true) * PI2,
|
|
5824
|
-
omegaDot: view.getFloat32(b + 40, true) * PI2,
|
|
5825
|
-
af0: view.getFloat32(b + 48, true),
|
|
5826
|
-
af1: view.getFloat32(b + 52, true),
|
|
5827
|
-
health: view.getUint16(b + 56, true)
|
|
5828
|
-
};
|
|
5829
|
-
}
|
|
5830
|
-
function decodeGloAlm(view, b) {
|
|
5831
|
-
const prn = svidToPrn(view.getUint8(b + 14));
|
|
5832
|
-
if (!prn || prn[0] !== "R") return null;
|
|
5833
|
-
const wnc = view.getUint16(b + 12, true);
|
|
5834
|
-
const toa = view.getUint32(b + 20, true);
|
|
5835
|
-
if (toa === U4_DNU) return null;
|
|
5836
|
-
return {
|
|
5837
|
-
system: "R",
|
|
5838
|
-
prn,
|
|
5839
|
-
freqNr: view.getUint8(b + 15) - 8,
|
|
5840
|
-
weekAlm: adjustWeek(wnc, view.getUint8(b + 52), 256),
|
|
5841
|
-
toaSec: toa,
|
|
5842
|
-
epsilon: view.getFloat32(b + 16, true),
|
|
5843
|
-
deltaI: view.getFloat32(b + 24, true) * PI2,
|
|
5844
|
-
lambda: view.getFloat32(b + 28, true) * PI2,
|
|
5845
|
-
tLambda: view.getFloat32(b + 32, true),
|
|
5846
|
-
omega: view.getFloat32(b + 36, true) * PI2,
|
|
5847
|
-
deltaT: view.getFloat32(b + 40, true),
|
|
5848
|
-
deltaTDot: view.getFloat32(b + 44, true),
|
|
5849
|
-
tau: view.getFloat32(b + 48, true),
|
|
5850
|
-
health: view.getUint8(b + 53),
|
|
5851
|
-
nDay: view.getUint16(b + 54, true),
|
|
5852
|
-
n4: view.getUint8(b + 57)
|
|
5853
|
-
};
|
|
5854
|
-
}
|
|
5855
|
-
function parseSbfAlmanac(data) {
|
|
5856
|
-
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
5857
|
-
const almanacs = [];
|
|
5858
|
-
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
5859
|
-
let alm = null;
|
|
5860
|
-
if (id === 5892 && len >= 60) alm = decodeGpsGalAlm(view, b, "G");
|
|
5861
|
-
else if (id === 4003 && len >= 61) alm = decodeGpsGalAlm(view, b, "E");
|
|
5862
|
-
else if (id === 4119 && len >= 60) alm = decodeBdsAlm(view, b);
|
|
5863
|
-
else if (id === 4005 && len >= 60) alm = decodeGloAlm(view, b);
|
|
5864
|
-
if (alm) almanacs.push(alm);
|
|
5865
|
-
});
|
|
5866
|
-
return { almanacs, badCrc };
|
|
5867
|
-
}
|
|
5868
|
-
|
|
5869
|
-
// src/sbf/iono.ts
|
|
5870
|
-
var F4_DNU2 = -2e10;
|
|
5871
|
-
function parseSbfIonoUtc(data) {
|
|
5872
|
-
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
5873
|
-
const ionoCorrections = {};
|
|
5874
|
-
let leapSeconds = null;
|
|
5875
|
-
const f4s = (b, n) => {
|
|
5876
|
-
const out = [];
|
|
5877
|
-
for (let k = 0; k < n; k++) {
|
|
5878
|
-
const v = view.getFloat32(b + 4 * k, true);
|
|
5879
|
-
if (v === F4_DNU2) return null;
|
|
5880
|
-
out.push(v);
|
|
5881
|
-
}
|
|
5882
|
-
return out;
|
|
5883
|
-
};
|
|
5884
|
-
scanSbfFrames(data, view, (id, b, len) => {
|
|
5885
|
-
if ((id === 5893 || id === 4120) && len >= 48) {
|
|
5886
|
-
const alpha = f4s(b + 16, 4);
|
|
5887
|
-
const beta = f4s(b + 32, 4);
|
|
5888
|
-
if (!alpha || !beta) return;
|
|
5889
|
-
const sys = id === 5893 ? "GPS" : "BDS";
|
|
5890
|
-
ionoCorrections[`${sys}A`] = alpha;
|
|
5891
|
-
ionoCorrections[`${sys}B`] = beta;
|
|
5892
|
-
} else if (id === 4030 && len >= 28) {
|
|
5893
|
-
const ai = f4s(b + 16, 3);
|
|
5894
|
-
if (ai) ionoCorrections["GAL"] = ai;
|
|
5895
|
-
} else if (id === 5894 && len >= 37) {
|
|
5896
|
-
leapSeconds = view.getInt8(b + 33);
|
|
5897
|
-
}
|
|
5639
|
+
cic: mergeS(cicp6, cicp7, 8) * 2 ** -31,
|
|
5640
|
+
omega0,
|
|
5641
|
+
cis,
|
|
5642
|
+
i0: mergeS(i0p7, i0p8, 11) * 2 ** -31 * GPS_PI,
|
|
5643
|
+
crc,
|
|
5644
|
+
omega: mergeS(omegaP9, omegaP10, 5) * 2 ** -31 * GPS_PI,
|
|
5645
|
+
omegaDot: mergeS(omegaDotP8, omegaDotP9, 5) * 2 ** -43 * GPS_PI,
|
|
5646
|
+
idot
|
|
5898
5647
|
});
|
|
5899
|
-
return { ionoCorrections, leapSeconds };
|
|
5900
5648
|
}
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
if (!
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
for (let k = 0; k < 10; k++) {
|
|
5922
|
-
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
5923
|
-
msg[4 * k] = w >>> 24;
|
|
5924
|
-
msg[4 * k + 1] = w >>> 16 & 255;
|
|
5925
|
-
msg[4 * k + 2] = w >>> 8 & 255;
|
|
5926
|
-
msg[4 * k + 3] = w & 255;
|
|
5649
|
+
function isBdsGeoPrn(prn) {
|
|
5650
|
+
return prn < 6 || prn > 58;
|
|
5651
|
+
}
|
|
5652
|
+
var BdsAssembler = class {
|
|
5653
|
+
sats = /* @__PURE__ */ new Map();
|
|
5654
|
+
/**
|
|
5655
|
+
* Push one 300-bit subframe (38+ bytes, bit 0 = first bit of the
|
|
5656
|
+
* preamble) for the satellite `prn` ("C06"). Returns the newly
|
|
5657
|
+
* completed ephemeris, or null.
|
|
5658
|
+
*/
|
|
5659
|
+
push(prn, subframe) {
|
|
5660
|
+
if (subframe.length < BDS_SUBFRAME_BYTES) return null;
|
|
5661
|
+
const num = parseInt(prn.slice(1), 10);
|
|
5662
|
+
if (!Number.isFinite(num) || num < 1 || num > 63) return null;
|
|
5663
|
+
const id = getBitU(subframe, 15, 3);
|
|
5664
|
+
if (id < 1 || id > 5) return null;
|
|
5665
|
+
let sat = this.sats.get(prn);
|
|
5666
|
+
if (!sat) {
|
|
5667
|
+
sat = { buf: new Uint8Array(10 * BDS_SUBFRAME_BYTES) };
|
|
5668
|
+
this.sats.set(prn, sat);
|
|
5927
5669
|
}
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5670
|
+
let eph = null;
|
|
5671
|
+
if (!isBdsGeoPrn(num)) {
|
|
5672
|
+
sat.buf.set(subframe.subarray(0, BDS_SUBFRAME_BYTES), (id - 1) * 38);
|
|
5673
|
+
if (id === 3) eph = decodeBdsD1Frame(sat.buf, { prn });
|
|
5674
|
+
} else {
|
|
5675
|
+
if (id !== 1) return null;
|
|
5676
|
+
const pgn = getBitU(subframe, 42, 4);
|
|
5677
|
+
if (pgn < 1 || pgn > 10) return null;
|
|
5678
|
+
sat.buf.set(subframe.subarray(0, BDS_SUBFRAME_BYTES), (pgn - 1) * 38);
|
|
5679
|
+
if (pgn === 10) eph = decodeBdsD2Frame(sat.buf, { prn });
|
|
5931
5680
|
}
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5681
|
+
if (!eph) return null;
|
|
5682
|
+
const key = `${eph.week}:${eph.toe}`;
|
|
5683
|
+
if (key === sat.lastKey) return null;
|
|
5684
|
+
sat.lastKey = key;
|
|
5685
|
+
return eph;
|
|
5686
|
+
}
|
|
5687
|
+
};
|
|
5937
5688
|
|
|
5938
5689
|
// src/navbits/gal.ts
|
|
5939
|
-
var
|
|
5690
|
+
var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
|
|
5940
5691
|
var SEC_PER_WEEK5 = 7 * 86400;
|
|
5941
|
-
var
|
|
5692
|
+
var HALF_WEEK3 = 302400;
|
|
5942
5693
|
var GST_GPS_WEEK_OFFSET = 1024;
|
|
5943
5694
|
function galInavPageCrcOk(page) {
|
|
5944
5695
|
if (page.length < 28) return false;
|
|
@@ -5949,14 +5700,14 @@ function galFnavPageCrcOk(page) {
|
|
|
5949
5700
|
return crc24q(page, 214) === getBitU(page, 214, 24);
|
|
5950
5701
|
}
|
|
5951
5702
|
function gpsDate2(week, sec) {
|
|
5952
|
-
return new Date(
|
|
5703
|
+
return new Date(GPS_EPOCH_MS4 + (week * SEC_PER_WEEK5 + sec) * 1e3);
|
|
5953
5704
|
}
|
|
5954
5705
|
function weekOfToe(week, tow, toe) {
|
|
5955
|
-
if (toe - tow >
|
|
5956
|
-
if (toe - tow < -
|
|
5706
|
+
if (toe - tow > HALF_WEEK3) return week - 1;
|
|
5707
|
+
if (toe - tow < -HALF_WEEK3) return week + 1;
|
|
5957
5708
|
return week;
|
|
5958
5709
|
}
|
|
5959
|
-
var
|
|
5710
|
+
var two = (n) => String(n).padStart(2, "0");
|
|
5960
5711
|
function decodeGalInavWords(words) {
|
|
5961
5712
|
if (words.length < 96) return null;
|
|
5962
5713
|
const b = words;
|
|
@@ -6044,7 +5795,7 @@ function decodeGalInavWords(words) {
|
|
|
6044
5795
|
const tocDate = gpsDate2(week, tocs);
|
|
6045
5796
|
return {
|
|
6046
5797
|
system: "E",
|
|
6047
|
-
prn: `E${
|
|
5798
|
+
prn: `E${two(svid)}`,
|
|
6048
5799
|
toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK5,
|
|
6049
5800
|
tocDate,
|
|
6050
5801
|
af0,
|
|
@@ -6151,598 +5902,951 @@ function decodeGalFnavPages(pages) {
|
|
|
6151
5902
|
const week = weekOfToe(gstWeek, tow, toes) + GST_GPS_WEEK_OFFSET;
|
|
6152
5903
|
const tocDate = gpsDate2(week, tocs);
|
|
6153
5904
|
return {
|
|
6154
|
-
system: "E",
|
|
6155
|
-
prn: `E${
|
|
6156
|
-
toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK5,
|
|
6157
|
-
tocDate,
|
|
6158
|
-
af0,
|
|
6159
|
-
af1,
|
|
6160
|
-
af2,
|
|
6161
|
-
iode: iodNav1,
|
|
6162
|
-
crs,
|
|
6163
|
-
deltaN,
|
|
6164
|
-
m0,
|
|
6165
|
-
cuc,
|
|
6166
|
-
e,
|
|
6167
|
-
cus,
|
|
6168
|
-
sqrtA,
|
|
6169
|
-
toe: toes,
|
|
6170
|
-
cic,
|
|
6171
|
-
omega0,
|
|
6172
|
-
cis,
|
|
6173
|
-
i0,
|
|
6174
|
-
crc,
|
|
6175
|
-
omega,
|
|
6176
|
-
omegaDot,
|
|
6177
|
-
idot,
|
|
6178
|
-
week,
|
|
6179
|
-
svHealth: e5aHs << 4 | e5aDvs << 3,
|
|
6180
|
-
tgd: bgdE5a
|
|
5905
|
+
system: "E",
|
|
5906
|
+
prn: `E${two(svid)}`,
|
|
5907
|
+
toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK5,
|
|
5908
|
+
tocDate,
|
|
5909
|
+
af0,
|
|
5910
|
+
af1,
|
|
5911
|
+
af2,
|
|
5912
|
+
iode: iodNav1,
|
|
5913
|
+
crs,
|
|
5914
|
+
deltaN,
|
|
5915
|
+
m0,
|
|
5916
|
+
cuc,
|
|
5917
|
+
e,
|
|
5918
|
+
cus,
|
|
5919
|
+
sqrtA,
|
|
5920
|
+
toe: toes,
|
|
5921
|
+
cic,
|
|
5922
|
+
omega0,
|
|
5923
|
+
cis,
|
|
5924
|
+
i0,
|
|
5925
|
+
crc,
|
|
5926
|
+
omega,
|
|
5927
|
+
omegaDot,
|
|
5928
|
+
idot,
|
|
5929
|
+
week,
|
|
5930
|
+
svHealth: e5aHs << 4 | e5aDvs << 3,
|
|
5931
|
+
tgd: bgdE5a
|
|
5932
|
+
};
|
|
5933
|
+
}
|
|
5934
|
+
var dedupKey = (eph) => `${eph.iode}:${eph.toe}:${eph.tocDate.getTime()}`;
|
|
5935
|
+
var GalInavAssembler = class {
|
|
5936
|
+
sats = /* @__PURE__ */ new Map();
|
|
5937
|
+
/**
|
|
5938
|
+
* Push one I/NAV page pair for satellite `prn` (1-36): the 234-bit
|
|
5939
|
+
* even+odd concatenation, even part first with its 6 tail bits
|
|
5940
|
+
* removed, bit 0 = the even part's Even/Odd bit (≥ 17 bytes).
|
|
5941
|
+
* Returns the newly completed ephemeris, or null. Pages that are
|
|
5942
|
+
* not an even/odd nominal pair, alert pages and word types outside
|
|
5943
|
+
* 1-5 are ignored.
|
|
5944
|
+
*/
|
|
5945
|
+
push(prn, page) {
|
|
5946
|
+
if (prn < 1 || prn > 36 || page.length < 17) return null;
|
|
5947
|
+
if (getBitU(page, 0, 1) !== 0 || getBitU(page, 114, 1) !== 1) return null;
|
|
5948
|
+
if (getBitU(page, 1, 1) === 1 || getBitU(page, 115, 1) === 1) return null;
|
|
5949
|
+
const type = getBitU(page, 2, 6);
|
|
5950
|
+
if (type < 1 || type > 5) return null;
|
|
5951
|
+
let sat = this.sats.get(prn);
|
|
5952
|
+
if (!sat) {
|
|
5953
|
+
sat = { words: new Uint8Array(96) };
|
|
5954
|
+
this.sats.set(prn, sat);
|
|
5955
|
+
}
|
|
5956
|
+
for (let k = 0; k < 14; k++)
|
|
5957
|
+
sat.words[type * 16 + k] = getBitU(page, 2 + 8 * k, 8);
|
|
5958
|
+
sat.words[type * 16 + 14] = getBitU(page, 116, 8);
|
|
5959
|
+
sat.words[type * 16 + 15] = getBitU(page, 124, 8);
|
|
5960
|
+
if (type !== 5) return null;
|
|
5961
|
+
const eph = decodeGalInavWords(sat.words);
|
|
5962
|
+
if (!eph || eph.prn !== `E${two(prn)}`) return null;
|
|
5963
|
+
const key = dedupKey(eph);
|
|
5964
|
+
if (key === sat.lastKey) return null;
|
|
5965
|
+
sat.lastKey = key;
|
|
5966
|
+
return eph;
|
|
5967
|
+
}
|
|
5968
|
+
};
|
|
5969
|
+
var GalFnavAssembler = class {
|
|
5970
|
+
sats = /* @__PURE__ */ new Map();
|
|
5971
|
+
/**
|
|
5972
|
+
* Push one F/NAV page for satellite `prn` (1-36): 244 bits with the
|
|
5973
|
+
* sync field stripped, bit 0 = first page-type bit (≥ 31 bytes).
|
|
5974
|
+
* Returns the newly completed ephemeris, or null. Dummy pages
|
|
5975
|
+
* (type 63) and page types outside 1-4 are ignored.
|
|
5976
|
+
*/
|
|
5977
|
+
push(prn, page) {
|
|
5978
|
+
if (prn < 1 || prn > 36 || page.length < 31) return null;
|
|
5979
|
+
const type = getBitU(page, 0, 6);
|
|
5980
|
+
if (type < 1 || type > 4) return null;
|
|
5981
|
+
let sat = this.sats.get(prn);
|
|
5982
|
+
if (!sat) {
|
|
5983
|
+
sat = { words: new Uint8Array(124) };
|
|
5984
|
+
this.sats.set(prn, sat);
|
|
5985
|
+
}
|
|
5986
|
+
sat.words.set(page.subarray(0, 31), (type - 1) * 31);
|
|
5987
|
+
if (type !== 4) return null;
|
|
5988
|
+
const eph = decodeGalFnavPages(sat.words);
|
|
5989
|
+
if (!eph || eph.prn !== `E${two(prn)}`) return null;
|
|
5990
|
+
const key = dedupKey(eph);
|
|
5991
|
+
if (key === sat.lastKey) return null;
|
|
5992
|
+
sat.lastKey = key;
|
|
5993
|
+
return eph;
|
|
5994
|
+
}
|
|
5995
|
+
};
|
|
5996
|
+
|
|
5997
|
+
// src/navbits/glo.ts
|
|
5998
|
+
var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
|
|
5999
|
+
var SEC_PER_WEEK6 = 7 * 86400;
|
|
6000
|
+
var SEC_PER_DAY = 86400;
|
|
6001
|
+
var GLO_STRING_BYTES = 10;
|
|
6002
|
+
function getBitG(b, pos, len) {
|
|
6003
|
+
const value = getBitU(b, pos + 1, len - 1);
|
|
6004
|
+
return getBitU(b, pos, 1) && value !== 0 ? -value : value;
|
|
6005
|
+
}
|
|
6006
|
+
var HAMMING_MASKS = [
|
|
6007
|
+
[85, 85, 90, 170, 170, 170, 181, 85, 106, 216, 8],
|
|
6008
|
+
[102, 102, 108, 204, 204, 204, 217, 153, 179, 104, 16],
|
|
6009
|
+
[135, 135, 143, 15, 15, 15, 30, 30, 60, 112, 32],
|
|
6010
|
+
[7, 248, 15, 240, 15, 240, 31, 224, 63, 128, 64],
|
|
6011
|
+
[248, 0, 15, 255, 240, 0, 31, 255, 192, 0, 128],
|
|
6012
|
+
[0, 0, 15, 255, 255, 255, 224, 0, 0, 1, 0],
|
|
6013
|
+
[255, 255, 240, 0, 0, 0, 0, 0, 0, 2, 0],
|
|
6014
|
+
[255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248]
|
|
6015
|
+
];
|
|
6016
|
+
function testGloString(buff) {
|
|
6017
|
+
if (buff.length < 11) return false;
|
|
6018
|
+
let n = 0;
|
|
6019
|
+
let cs = 0;
|
|
6020
|
+
for (const mask of HAMMING_MASKS) {
|
|
6021
|
+
cs = 0;
|
|
6022
|
+
for (let j = 0; j < 11; j++) {
|
|
6023
|
+
let x = buff[j] & mask[j];
|
|
6024
|
+
x ^= x >> 4;
|
|
6025
|
+
x ^= x >> 2;
|
|
6026
|
+
x ^= x >> 1;
|
|
6027
|
+
cs ^= x & 1;
|
|
6028
|
+
}
|
|
6029
|
+
if (cs) n++;
|
|
6030
|
+
}
|
|
6031
|
+
return n === 0 || n === 2 && cs === 1;
|
|
6032
|
+
}
|
|
6033
|
+
function decodeGloStrings(strings, refDate, opts = {}) {
|
|
6034
|
+
if (strings.length < 4 * GLO_STRING_BYTES) return null;
|
|
6035
|
+
const b = strings;
|
|
6036
|
+
let i = 1;
|
|
6037
|
+
const frn1 = getBitU(b, i, 4);
|
|
6038
|
+
i += 4 + 2;
|
|
6039
|
+
i += 2;
|
|
6040
|
+
const tkH = getBitU(b, i, 5);
|
|
6041
|
+
i += 5;
|
|
6042
|
+
const tkM = getBitU(b, i, 6);
|
|
6043
|
+
i += 6;
|
|
6044
|
+
const tkS = getBitU(b, i, 1) * 30;
|
|
6045
|
+
i += 1;
|
|
6046
|
+
const xDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6047
|
+
i += 24;
|
|
6048
|
+
const xAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6049
|
+
i += 5;
|
|
6050
|
+
const x = getBitG(b, i, 27) * 2 ** -11;
|
|
6051
|
+
i = 80 + 1;
|
|
6052
|
+
const frn2 = getBitU(b, i, 4);
|
|
6053
|
+
i += 4;
|
|
6054
|
+
const bn = getBitU(b, i, 1);
|
|
6055
|
+
i += 1 + 2;
|
|
6056
|
+
i += 1;
|
|
6057
|
+
const tb = getBitU(b, i, 7);
|
|
6058
|
+
i += 7 + 5;
|
|
6059
|
+
const yDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6060
|
+
i += 24;
|
|
6061
|
+
const yAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6062
|
+
i += 5;
|
|
6063
|
+
const y = getBitG(b, i, 27) * 2 ** -11;
|
|
6064
|
+
i = 160 + 1;
|
|
6065
|
+
const frn3 = getBitU(b, i, 4);
|
|
6066
|
+
i += 4;
|
|
6067
|
+
i += 1;
|
|
6068
|
+
const gammaN = getBitG(b, i, 11) * 2 ** -40;
|
|
6069
|
+
i += 11 + 1;
|
|
6070
|
+
i += 2;
|
|
6071
|
+
i += 1;
|
|
6072
|
+
const zDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6073
|
+
i += 24;
|
|
6074
|
+
const zAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6075
|
+
i += 5;
|
|
6076
|
+
const z = getBitG(b, i, 27) * 2 ** -11;
|
|
6077
|
+
i = 240 + 1;
|
|
6078
|
+
const frn4 = getBitU(b, i, 4);
|
|
6079
|
+
i += 4;
|
|
6080
|
+
const tauN = getBitG(b, i, 22) * 2 ** -30;
|
|
6081
|
+
i += 22;
|
|
6082
|
+
i += 5;
|
|
6083
|
+
i += 5;
|
|
6084
|
+
i += 14;
|
|
6085
|
+
i += 1;
|
|
6086
|
+
i += 4;
|
|
6087
|
+
i += 3;
|
|
6088
|
+
i += 11;
|
|
6089
|
+
const slot = getBitU(b, i, 5);
|
|
6090
|
+
if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3 || frn4 !== 4) return null;
|
|
6091
|
+
if (slot < 1 || slot > 27) return null;
|
|
6092
|
+
const utcSec = (getUtcDate(refDate).getTime() - GPS_EPOCH_MS5) / 1e3;
|
|
6093
|
+
const week = Math.floor(utcSec / SEC_PER_WEEK6);
|
|
6094
|
+
let tow = utcSec - week * SEC_PER_WEEK6;
|
|
6095
|
+
const tod = tow % SEC_PER_DAY;
|
|
6096
|
+
tow -= tod;
|
|
6097
|
+
let tof = tkH * 3600 + tkM * 60 + tkS - 10800;
|
|
6098
|
+
if (tof < tod - 43200) tof += SEC_PER_DAY;
|
|
6099
|
+
else if (tof > tod + 43200) tof -= SEC_PER_DAY;
|
|
6100
|
+
let toe = tb * 900 - 10800;
|
|
6101
|
+
if (toe < tod - 43200) toe += SEC_PER_DAY;
|
|
6102
|
+
else if (toe > tod + 43200) toe -= SEC_PER_DAY;
|
|
6103
|
+
return {
|
|
6104
|
+
system: "R",
|
|
6105
|
+
prn: `R${String(slot).padStart(2, "0")}`,
|
|
6106
|
+
// RINEX GLONASS epochs are UTC: build the UTC toe Date directly.
|
|
6107
|
+
tocDate: new Date(GPS_EPOCH_MS5 + (week * SEC_PER_WEEK6 + tow + toe) * 1e3),
|
|
6108
|
+
tauN: -tauN,
|
|
6109
|
+
// RINEX stores −τn; the SIS carries τn (ICD sign)
|
|
6110
|
+
gammaN,
|
|
6111
|
+
// v3 message frame time: seconds of the UTC week (RTKLIB tof).
|
|
6112
|
+
messageFrameTime: ((tow + tof) % SEC_PER_WEEK6 + SEC_PER_WEEK6) % SEC_PER_WEEK6,
|
|
6113
|
+
x,
|
|
6114
|
+
xDot,
|
|
6115
|
+
xAcc,
|
|
6116
|
+
y,
|
|
6117
|
+
yDot,
|
|
6118
|
+
yAcc,
|
|
6119
|
+
z,
|
|
6120
|
+
zDot,
|
|
6121
|
+
zAcc,
|
|
6122
|
+
// MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
|
|
6123
|
+
health: bn,
|
|
6124
|
+
freqNum: opts.freqNum ?? 0
|
|
6181
6125
|
};
|
|
6182
6126
|
}
|
|
6183
|
-
var
|
|
6184
|
-
var GalInavAssembler = class {
|
|
6185
|
-
sats = /* @__PURE__ */ new Map();
|
|
6186
|
-
/**
|
|
6187
|
-
* Push one I/NAV page pair for satellite `prn` (1-36): the 234-bit
|
|
6188
|
-
* even+odd concatenation, even part first with its 6 tail bits
|
|
6189
|
-
* removed, bit 0 = the even part's Even/Odd bit (≥ 17 bytes).
|
|
6190
|
-
* Returns the newly completed ephemeris, or null. Pages that are
|
|
6191
|
-
* not an even/odd nominal pair, alert pages and word types outside
|
|
6192
|
-
* 1-5 are ignored.
|
|
6193
|
-
*/
|
|
6194
|
-
push(prn, page) {
|
|
6195
|
-
if (prn < 1 || prn > 36 || page.length < 17) return null;
|
|
6196
|
-
if (getBitU(page, 0, 1) !== 0 || getBitU(page, 114, 1) !== 1) return null;
|
|
6197
|
-
if (getBitU(page, 1, 1) === 1 || getBitU(page, 115, 1) === 1) return null;
|
|
6198
|
-
const type = getBitU(page, 2, 6);
|
|
6199
|
-
if (type < 1 || type > 5) return null;
|
|
6200
|
-
let sat = this.sats.get(prn);
|
|
6201
|
-
if (!sat) {
|
|
6202
|
-
sat = { words: new Uint8Array(96) };
|
|
6203
|
-
this.sats.set(prn, sat);
|
|
6204
|
-
}
|
|
6205
|
-
for (let k = 0; k < 14; k++)
|
|
6206
|
-
sat.words[type * 16 + k] = getBitU(page, 2 + 8 * k, 8);
|
|
6207
|
-
sat.words[type * 16 + 14] = getBitU(page, 116, 8);
|
|
6208
|
-
sat.words[type * 16 + 15] = getBitU(page, 124, 8);
|
|
6209
|
-
if (type !== 5) return null;
|
|
6210
|
-
const eph = decodeGalInavWords(sat.words);
|
|
6211
|
-
if (!eph || eph.prn !== `E${two2(prn)}`) return null;
|
|
6212
|
-
const key = dedupKey(eph);
|
|
6213
|
-
if (key === sat.lastKey) return null;
|
|
6214
|
-
sat.lastKey = key;
|
|
6215
|
-
return eph;
|
|
6216
|
-
}
|
|
6217
|
-
};
|
|
6218
|
-
var GalFnavAssembler = class {
|
|
6127
|
+
var GloStringAssembler = class {
|
|
6219
6128
|
sats = /* @__PURE__ */ new Map();
|
|
6220
6129
|
/**
|
|
6221
|
-
* Push one
|
|
6222
|
-
*
|
|
6223
|
-
* Returns the newly completed ephemeris, or null.
|
|
6224
|
-
*
|
|
6130
|
+
* Push one navigation string (10+ bytes, bit 0 = string bit 85) for
|
|
6131
|
+
* the satellite `prn` ("R09"), received at the GPS-scale `time`.
|
|
6132
|
+
* Returns the newly completed ephemeris, or null. The decoded
|
|
6133
|
+
* string-4 slot number must match `prn`, or nothing is emitted.
|
|
6225
6134
|
*/
|
|
6226
|
-
push(prn,
|
|
6227
|
-
if (
|
|
6228
|
-
const
|
|
6229
|
-
if (
|
|
6135
|
+
push(prn, str, time, freqNum = 0) {
|
|
6136
|
+
if (str.length < GLO_STRING_BYTES) return null;
|
|
6137
|
+
const m = getBitU(str, 1, 4);
|
|
6138
|
+
if (m < 1) return null;
|
|
6139
|
+
const sec = Math.floor(time.getTime() / 1e3);
|
|
6230
6140
|
let sat = this.sats.get(prn);
|
|
6231
6141
|
if (!sat) {
|
|
6232
|
-
sat = {
|
|
6142
|
+
sat = { buf: new Uint8Array(4 * GLO_STRING_BYTES), batchSec: sec };
|
|
6233
6143
|
this.sats.set(prn, sat);
|
|
6144
|
+
} else if (Math.abs(sec - sat.batchSec) > 30) {
|
|
6145
|
+
sat.buf.fill(0);
|
|
6146
|
+
sat.batchSec = sec;
|
|
6234
6147
|
}
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6148
|
+
if (m > 4) return null;
|
|
6149
|
+
sat.buf.set(str.subarray(0, GLO_STRING_BYTES), (m - 1) * 10);
|
|
6150
|
+
if (m !== 4) return null;
|
|
6151
|
+
const eph = decodeGloStrings(sat.buf, time, { freqNum });
|
|
6152
|
+
if (!eph || eph.prn !== prn) return null;
|
|
6153
|
+
const key = `${eph.tocDate.getTime()}:${eph.health}`;
|
|
6240
6154
|
if (key === sat.lastKey) return null;
|
|
6241
6155
|
sat.lastKey = key;
|
|
6242
6156
|
return eph;
|
|
6243
6157
|
}
|
|
6244
6158
|
};
|
|
6245
6159
|
|
|
6246
|
-
// src/
|
|
6247
|
-
var
|
|
6248
|
-
var
|
|
6249
|
-
|
|
6160
|
+
// src/ubx/rawnav.ts
|
|
6161
|
+
var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
|
|
6162
|
+
var SEC_PER_WEEK7 = 7 * 86400;
|
|
6163
|
+
var two2 = (n) => String(n).padStart(2, "0");
|
|
6164
|
+
function parseUbxRawNav(data, opts = {}) {
|
|
6250
6165
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6251
6166
|
const ephemerides = [];
|
|
6167
|
+
const counts = { gal: 0, bds: 0, glo: 0 };
|
|
6168
|
+
let badParity = 0;
|
|
6252
6169
|
const inav = new GalInavAssembler();
|
|
6253
|
-
const
|
|
6254
|
-
|
|
6255
|
-
let
|
|
6256
|
-
|
|
6257
|
-
if (
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
page[4 * k + 2] = w >>> 8 & 255;
|
|
6269
|
-
page[4 * k + 3] = w & 255;
|
|
6170
|
+
const bds = new BdsAssembler();
|
|
6171
|
+
const glo = new GloStringAssembler();
|
|
6172
|
+
let refDate = null;
|
|
6173
|
+
for (const f of ubxFrames(data)) {
|
|
6174
|
+
if (f.msgClass !== 2) continue;
|
|
6175
|
+
if (f.msgId === 21 && f.payload.length >= 16) {
|
|
6176
|
+
const rcvTow = view.getFloat64(f.payloadStart, true);
|
|
6177
|
+
let week = view.getUint16(f.payloadStart + 8, true);
|
|
6178
|
+
if (week === 0 && opts.refWeek !== void 0) week = opts.refWeek;
|
|
6179
|
+
if (week > 0) {
|
|
6180
|
+
refDate = new Date(
|
|
6181
|
+
GPS_EPOCH_MS6 + (week * SEC_PER_WEEK7 + rcvTow) * 1e3
|
|
6182
|
+
);
|
|
6183
|
+
}
|
|
6184
|
+
continue;
|
|
6270
6185
|
}
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
if (
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6186
|
+
if (f.msgId !== 19) continue;
|
|
6187
|
+
const p = f.payload;
|
|
6188
|
+
if (p.length < 8) continue;
|
|
6189
|
+
const gnssId = p[0];
|
|
6190
|
+
const svId = p[1];
|
|
6191
|
+
const sigId = p[2];
|
|
6192
|
+
const base = f.payloadStart + 8;
|
|
6193
|
+
if (gnssId === 2) {
|
|
6194
|
+
if (sigId === 3 || sigId === 8) continue;
|
|
6195
|
+
if (p.length < 8 + 32) continue;
|
|
6196
|
+
counts.gal++;
|
|
6197
|
+
if (svId < 1 || svId > 36) continue;
|
|
6198
|
+
const buff = new Uint8Array(32);
|
|
6199
|
+
for (let k = 0; k < 8; k++) {
|
|
6200
|
+
const w = view.getUint32(base + 4 * k, true);
|
|
6201
|
+
buff[4 * k] = w >>> 24;
|
|
6202
|
+
buff[4 * k + 1] = w >>> 16 & 255;
|
|
6203
|
+
buff[4 * k + 2] = w >>> 8 & 255;
|
|
6204
|
+
buff[4 * k + 3] = w & 255;
|
|
6205
|
+
}
|
|
6206
|
+
const page = new Uint8Array(30);
|
|
6207
|
+
for (let k = 0; k < 14; k++) page[k] = buff[k];
|
|
6208
|
+
setBitU(page, 112, 2, getBitU(buff, 112, 2));
|
|
6209
|
+
for (let k = 0; k < 15; k++) {
|
|
6210
|
+
setBitU(page, 114 + 8 * k, 8, getBitU(buff, 128 + 8 * k, 8));
|
|
6211
|
+
}
|
|
6212
|
+
if (getBitU(page, 0, 1) !== 0 || getBitU(page, 114, 1) !== 1) continue;
|
|
6213
|
+
if (getBitU(page, 1, 1) === 1 || getBitU(page, 115, 1) === 1) continue;
|
|
6277
6214
|
if (!galInavPageCrcOk(page)) {
|
|
6278
|
-
|
|
6279
|
-
|
|
6215
|
+
badParity++;
|
|
6216
|
+
continue;
|
|
6280
6217
|
}
|
|
6281
|
-
eph = inav.push(
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
if (
|
|
6285
|
-
if (
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6218
|
+
const eph = inav.push(svId, page);
|
|
6219
|
+
if (eph) ephemerides.push(eph);
|
|
6220
|
+
} else if (gnssId === 3) {
|
|
6221
|
+
if (sigId === 6 || sigId === 8) continue;
|
|
6222
|
+
if (p.length < 8 + 40) continue;
|
|
6223
|
+
counts.bds++;
|
|
6224
|
+
if (svId < 1 || svId > 63) continue;
|
|
6225
|
+
const sf = new Uint8Array(38);
|
|
6226
|
+
for (let k = 0; k < 10; k++) {
|
|
6227
|
+
const w = view.getUint32(base + 4 * k, true);
|
|
6228
|
+
setBitU(sf, 30 * k, 30, w & 1073741823);
|
|
6229
|
+
}
|
|
6230
|
+
if (!bdsSubframeParityOk(sf)) {
|
|
6231
|
+
badParity++;
|
|
6232
|
+
continue;
|
|
6233
|
+
}
|
|
6234
|
+
const eph = bds.push(`C${two2(svId)}`, sf);
|
|
6235
|
+
if (eph) ephemerides.push(eph);
|
|
6236
|
+
} else if (gnssId === 6) {
|
|
6237
|
+
if (p.length < 8 + 16) continue;
|
|
6238
|
+
counts.glo++;
|
|
6239
|
+
if (svId < 1 || svId > 32) continue;
|
|
6240
|
+
const str = new Uint8Array(16);
|
|
6241
|
+
for (let k = 0; k < 4; k++) {
|
|
6242
|
+
const w = view.getUint32(base + 4 * k, true);
|
|
6243
|
+
str[4 * k] = w >>> 24;
|
|
6244
|
+
str[4 * k + 1] = w >>> 16 & 255;
|
|
6245
|
+
str[4 * k + 2] = w >>> 8 & 255;
|
|
6246
|
+
str[4 * k + 3] = w & 255;
|
|
6247
|
+
}
|
|
6248
|
+
if (!testGloString(str)) {
|
|
6249
|
+
badParity++;
|
|
6250
|
+
continue;
|
|
6251
|
+
}
|
|
6252
|
+
if (!refDate) continue;
|
|
6253
|
+
const eph = glo.push(`R${two2(svId)}`, str, refDate, p[3] - 7);
|
|
6254
|
+
if (eph) ephemerides.push(eph);
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
return { ephemerides, counts, badParity };
|
|
6258
|
+
}
|
|
6259
|
+
|
|
6260
|
+
// src/ubx/index.ts
|
|
6261
|
+
var GPS_EPOCH_MS7 = Date.UTC(1980, 0, 6);
|
|
6262
|
+
var MS_PER_WEEK3 = 7 * 864e5;
|
|
6263
|
+
var SIGNALS = {
|
|
6264
|
+
0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
|
|
6265
|
+
// GPS L1C/A, L2CL, L2CM
|
|
6266
|
+
1: { 0: ["S", "1C"] },
|
|
6267
|
+
// SBAS L1C/A
|
|
6268
|
+
2: { 0: ["E", "1X"], 1: ["E", "1X"], 5: ["E", "7X"], 6: ["E", "7X"] },
|
|
6269
|
+
// E1C/B, E5bI/Q
|
|
6270
|
+
3: { 0: ["C", "2I"], 1: ["C", "2I"], 2: ["C", "7I"], 3: ["C", "7I"] },
|
|
6271
|
+
// B1I D1/D2, B2I D1/D2
|
|
6272
|
+
5: { 0: ["J", "1C"], 4: ["J", "2X"], 5: ["J", "2X"] },
|
|
6273
|
+
// QZSS L1C/A, L2CM/CL
|
|
6274
|
+
6: { 0: ["R", "1C"], 2: ["R", "2C"] }
|
|
6275
|
+
// GLONASS L1OF, L2OF
|
|
6276
|
+
};
|
|
6277
|
+
function prnFor(gnssId, svId) {
|
|
6278
|
+
const two5 = (n) => String(n).padStart(2, "0");
|
|
6279
|
+
switch (gnssId) {
|
|
6280
|
+
case 0:
|
|
6281
|
+
return svId >= 1 && svId <= 32 ? `G${two5(svId)}` : null;
|
|
6282
|
+
case 1:
|
|
6283
|
+
return svId >= 120 && svId <= 158 ? `S${two5(svId - 100)}` : null;
|
|
6284
|
+
case 2:
|
|
6285
|
+
return svId >= 1 && svId <= 36 ? `E${two5(svId)}` : null;
|
|
6286
|
+
case 3:
|
|
6287
|
+
return svId >= 1 && svId <= 63 ? `C${two5(svId)}` : null;
|
|
6288
|
+
case 5:
|
|
6289
|
+
return svId >= 1 && svId <= 10 ? `J${two5(svId)}` : null;
|
|
6290
|
+
case 6:
|
|
6291
|
+
return svId >= 1 && svId <= 32 ? `R${two5(svId)}` : null;
|
|
6292
|
+
default:
|
|
6293
|
+
return null;
|
|
6294
|
+
}
|
|
6295
|
+
}
|
|
6296
|
+
function parseUbxRawx(data) {
|
|
6297
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6298
|
+
const epochs = [];
|
|
6299
|
+
const messageCounts = {};
|
|
6300
|
+
const obsCodes = {};
|
|
6301
|
+
const stats = { badChecksums: 0 };
|
|
6302
|
+
for (const frame of ubxFrames(data, stats)) {
|
|
6303
|
+
const { msgClass: cls, msgId: id } = frame;
|
|
6304
|
+
const len = frame.payload.length;
|
|
6305
|
+
const key = `${cls.toString(16).padStart(2, "0")}-${id.toString(16).padStart(2, "0")}`;
|
|
6306
|
+
messageCounts[key] = (messageCounts[key] ?? 0) + 1;
|
|
6307
|
+
if (cls === 2 && id === 21 && len >= 16) {
|
|
6308
|
+
const p = frame.payloadStart;
|
|
6309
|
+
const rcvTow = view.getFloat64(p, true);
|
|
6310
|
+
const week = view.getUint16(p + 8, true);
|
|
6311
|
+
const leapS = view.getInt8(p + 10);
|
|
6312
|
+
const numMeas = data[p + 11];
|
|
6313
|
+
const recStat = data[p + 12];
|
|
6314
|
+
const leapValid = (recStat & 1) !== 0;
|
|
6315
|
+
const meas = [];
|
|
6316
|
+
for (let k = 0; k < numMeas; k++) {
|
|
6317
|
+
const off = p + 16 + 32 * k;
|
|
6318
|
+
if (off + 32 > p + len) break;
|
|
6319
|
+
const gnssId = data[off + 20];
|
|
6320
|
+
const svId = data[off + 21];
|
|
6321
|
+
const sigId = data[off + 22];
|
|
6322
|
+
const trkStat = data[off + 30];
|
|
6323
|
+
const prn = prnFor(gnssId, svId);
|
|
6324
|
+
const sig = SIGNALS[gnssId]?.[sigId];
|
|
6325
|
+
if (!prn || !sig) continue;
|
|
6326
|
+
const prValid = (trkStat & 1) !== 0;
|
|
6327
|
+
const cpValid = (trkStat & 2) !== 0;
|
|
6328
|
+
const cp = view.getFloat64(off + 8, true);
|
|
6329
|
+
meas.push({
|
|
6330
|
+
prn,
|
|
6331
|
+
code: sig[1],
|
|
6332
|
+
pr: prValid ? view.getFloat64(off, true) : null,
|
|
6333
|
+
cp: cpValid && cp !== 0 ? cp : null,
|
|
6334
|
+
doppler: view.getFloat32(off + 16, true),
|
|
6335
|
+
cn0: data[off + 26],
|
|
6336
|
+
halfCycleAmbiguous: (trkStat & 4) !== 0,
|
|
6337
|
+
lockTimeMs: view.getUint16(off + 24, true)
|
|
6338
|
+
});
|
|
6339
|
+
const sys = sig[0];
|
|
6340
|
+
const codes = obsCodes[sys] ??= [];
|
|
6341
|
+
if (!codes.includes(sig[1])) codes.push(sig[1]);
|
|
6289
6342
|
}
|
|
6290
|
-
|
|
6343
|
+
epochs.push({
|
|
6344
|
+
timeMs: GPS_EPOCH_MS7 + week * MS_PER_WEEK3 + Math.round(rcvTow * 1e3),
|
|
6345
|
+
leapS: leapValid ? leapS : null,
|
|
6346
|
+
meas
|
|
6347
|
+
});
|
|
6291
6348
|
}
|
|
6292
|
-
|
|
6293
|
-
}
|
|
6294
|
-
return { ephemerides, badCrc, messages };
|
|
6349
|
+
}
|
|
6350
|
+
return { epochs, messageCounts, obsCodes, badChecksums: stats.badChecksums };
|
|
6295
6351
|
}
|
|
6296
|
-
|
|
6297
|
-
|
|
6352
|
+
|
|
6353
|
+
// src/sbf/frame.ts
|
|
6354
|
+
var CRC_TABLE = (() => {
|
|
6355
|
+
const t = new Uint16Array(256);
|
|
6356
|
+
for (let n = 0; n < 256; n++) {
|
|
6357
|
+
let c = n << 8;
|
|
6358
|
+
for (let k = 0; k < 8; k++)
|
|
6359
|
+
c = c & 32768 ? (c << 1 ^ 4129) & 65535 : c << 1 & 65535;
|
|
6360
|
+
t[n] = c;
|
|
6361
|
+
}
|
|
6362
|
+
return t;
|
|
6363
|
+
})();
|
|
6364
|
+
function crc16(data, start, end) {
|
|
6365
|
+
let crc = 0;
|
|
6366
|
+
for (let i = start; i < end; i++)
|
|
6367
|
+
crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
|
|
6368
|
+
return crc;
|
|
6298
6369
|
}
|
|
6299
|
-
function
|
|
6300
|
-
|
|
6370
|
+
function scanSbfFrames(data, view, onBlock) {
|
|
6371
|
+
let badCrc = 0;
|
|
6372
|
+
let i = 0;
|
|
6373
|
+
while (i + 8 <= data.length) {
|
|
6374
|
+
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
6375
|
+
i++;
|
|
6376
|
+
continue;
|
|
6377
|
+
}
|
|
6378
|
+
const len = view.getUint16(i + 6, true);
|
|
6379
|
+
if (len < 8 || len % 4 !== 0) {
|
|
6380
|
+
i++;
|
|
6381
|
+
continue;
|
|
6382
|
+
}
|
|
6383
|
+
if (i + len > data.length) break;
|
|
6384
|
+
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
6385
|
+
badCrc++;
|
|
6386
|
+
i++;
|
|
6387
|
+
continue;
|
|
6388
|
+
}
|
|
6389
|
+
onBlock(view.getUint16(i + 4, true) & 8191, i, len);
|
|
6390
|
+
i += len;
|
|
6391
|
+
}
|
|
6392
|
+
return badCrc;
|
|
6393
|
+
}
|
|
6394
|
+
var two3 = (n) => String(n).padStart(2, "0");
|
|
6395
|
+
function svidToPrn(svid) {
|
|
6396
|
+
if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two3(svid)}` : null;
|
|
6397
|
+
if (svid <= 61) return svid - 37 <= 27 ? `R${two3(svid - 37)}` : null;
|
|
6398
|
+
if (svid <= 62) return null;
|
|
6399
|
+
if (svid <= 68) return svid - 38 <= 27 ? `R${two3(svid - 38)}` : null;
|
|
6400
|
+
if (svid <= 70) return null;
|
|
6401
|
+
if (svid <= 106) return svid - 70 <= 36 ? `E${two3(svid - 70)}` : null;
|
|
6402
|
+
if (svid <= 119) return null;
|
|
6403
|
+
if (svid <= 140) return `S${two3(svid - 100)}`;
|
|
6404
|
+
if (svid <= 180) return svid - 140 <= 50 ? `C${two3(svid - 140)}` : null;
|
|
6405
|
+
if (svid <= 190) return `J${two3(svid - 180)}`;
|
|
6406
|
+
if (svid <= 197) return svid - 190 <= 14 ? `I${two3(svid - 190)}` : null;
|
|
6407
|
+
if (svid <= 215) return `S${two3(svid - 157)}`;
|
|
6408
|
+
if (svid <= 222) return svid - 208 <= 14 ? `I${two3(svid - 208)}` : null;
|
|
6409
|
+
if (svid <= 245) return svid - 182 <= 50 ? `C${two3(svid - 182)}` : null;
|
|
6410
|
+
return null;
|
|
6301
6411
|
}
|
|
6302
6412
|
|
|
6303
|
-
// src/
|
|
6413
|
+
// src/sbf/nav.ts
|
|
6414
|
+
var PI2 = Math.PI;
|
|
6415
|
+
var GPS_EPOCH_MS8 = Date.UTC(1980, 0, 6);
|
|
6304
6416
|
var BDT_EPOCH_MS2 = Date.UTC(2006, 0, 1);
|
|
6305
|
-
var
|
|
6306
|
-
var MS_PER_WEEK4 =
|
|
6307
|
-
var
|
|
6308
|
-
var
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
return getBitS(b, p1, l1) * 2 ** l2 + getBitU(b, p2, l2);
|
|
6315
|
-
}
|
|
6316
|
-
function getBitU3(b, p1, l1, p2, l2, p3, l3) {
|
|
6317
|
-
return getBitU(b, p1, l1) * 2 ** (l2 + l3) + getBitU(b, p2, l2) * 2 ** l3 + getBitU(b, p3, l3);
|
|
6417
|
+
var SEC_PER_WEEK8 = 7 * 86400;
|
|
6418
|
+
var MS_PER_WEEK4 = SEC_PER_WEEK8 * 1e3;
|
|
6419
|
+
var F4_DNU = -2e10;
|
|
6420
|
+
var U4_DNU = 4294967295;
|
|
6421
|
+
function adjustWeek(refWeek, wn, mod) {
|
|
6422
|
+
let offset = refWeek % mod - wn;
|
|
6423
|
+
if (offset > mod / 2) offset -= mod;
|
|
6424
|
+
if (offset < -(mod / 2 - 1)) offset += mod;
|
|
6425
|
+
return refWeek - offset;
|
|
6318
6426
|
}
|
|
6319
|
-
|
|
6320
|
-
|
|
6427
|
+
var gpsMs = (week, sec) => GPS_EPOCH_MS8 + week * MS_PER_WEEK4 + sec * 1e3;
|
|
6428
|
+
var sowOf2 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK8;
|
|
6429
|
+
function decodeGpsQzsNav(view, b, sys) {
|
|
6430
|
+
const svid = view.getUint8(b + 14);
|
|
6431
|
+
const prn = sys === "G" ? svid : svid - 180;
|
|
6432
|
+
const max = sys === "G" ? 32 : 10;
|
|
6433
|
+
if (prn < 1 || prn > max) return null;
|
|
6434
|
+
const wnc = view.getUint16(b + 12, true);
|
|
6435
|
+
const tgdRaw = view.getFloat32(b + 28, true);
|
|
6436
|
+
const tocs = view.getUint32(b + 32, true);
|
|
6437
|
+
const toes = view.getUint32(b + 88, true);
|
|
6438
|
+
const wnToc = adjustWeek(wnc, view.getUint16(b + 136, true), 1024);
|
|
6439
|
+
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
6440
|
+
return {
|
|
6441
|
+
system: sys,
|
|
6442
|
+
prn: `${sys}${String(prn).padStart(2, "0")}`,
|
|
6443
|
+
toc: sowOf2(tocDate.getTime()),
|
|
6444
|
+
tocDate,
|
|
6445
|
+
af0: view.getFloat32(b + 44, true),
|
|
6446
|
+
af1: view.getFloat32(b + 40, true),
|
|
6447
|
+
af2: view.getFloat32(b + 36, true),
|
|
6448
|
+
iode: view.getUint8(b + 24),
|
|
6449
|
+
crs: view.getFloat32(b + 48, true),
|
|
6450
|
+
deltaN: view.getFloat32(b + 52, true) * PI2,
|
|
6451
|
+
m0: view.getFloat64(b + 56, true) * PI2,
|
|
6452
|
+
cuc: view.getFloat32(b + 64, true),
|
|
6453
|
+
e: view.getFloat64(b + 68, true),
|
|
6454
|
+
cus: view.getFloat32(b + 76, true),
|
|
6455
|
+
sqrtA: view.getFloat64(b + 80, true),
|
|
6456
|
+
toe: toes,
|
|
6457
|
+
cic: view.getFloat32(b + 92, true),
|
|
6458
|
+
omega0: view.getFloat64(b + 96, true) * PI2,
|
|
6459
|
+
cis: view.getFloat32(b + 104, true),
|
|
6460
|
+
i0: view.getFloat64(b + 108, true) * PI2,
|
|
6461
|
+
crc: view.getFloat32(b + 116, true),
|
|
6462
|
+
omega: view.getFloat64(b + 120, true) * PI2,
|
|
6463
|
+
omegaDot: view.getFloat32(b + 128, true) * PI2,
|
|
6464
|
+
idot: view.getFloat32(b + 132, true) * PI2,
|
|
6465
|
+
// RINEX "GPS week to go with toe": RTKLIB writes WNc for GPS and
|
|
6466
|
+
// the week of toc for QZSS — mirrored here so records compare
|
|
6467
|
+
// 1:1 against convbin output. wnToe is still used for `toe` above.
|
|
6468
|
+
week: sys === "G" ? wnc : wnToc,
|
|
6469
|
+
svHealth: view.getUint8(b + 20),
|
|
6470
|
+
tgd: tgdRaw !== F4_DNU ? tgdRaw : 0
|
|
6471
|
+
};
|
|
6321
6472
|
}
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6473
|
+
function decodeGalNav(view, b) {
|
|
6474
|
+
const prn = view.getUint8(b + 14) - 70;
|
|
6475
|
+
if (prn < 1 || prn > 36) return null;
|
|
6476
|
+
const source = view.getUint8(b + 15);
|
|
6477
|
+
if (source !== 2 && source !== 16) return null;
|
|
6478
|
+
const wnc = view.getUint16(b + 12, true);
|
|
6479
|
+
const toes = view.getUint32(b + 100, true);
|
|
6480
|
+
const tocs = view.getUint32(b + 104, true);
|
|
6481
|
+
const wnToc = adjustWeek(wnc, view.getUint16(b + 126, true), 4096);
|
|
6482
|
+
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
6483
|
+
const health = view.getUint16(b + 130, true);
|
|
6484
|
+
let svh = 0;
|
|
6485
|
+
if (health & 1) svh |= health >> 1 & 7;
|
|
6486
|
+
if (health & 16) svh |= (health >> 5 & 7) << 6;
|
|
6487
|
+
if (health & 256) svh |= (health >> 9 & 7) << 3;
|
|
6488
|
+
const bgdE5a = view.getFloat32(b + 136, true);
|
|
6489
|
+
return {
|
|
6490
|
+
system: "E",
|
|
6491
|
+
prn: `E${String(prn).padStart(2, "0")}`,
|
|
6492
|
+
toc: sowOf2(tocDate.getTime()),
|
|
6493
|
+
tocDate,
|
|
6494
|
+
af0: view.getFloat64(b + 116, true),
|
|
6495
|
+
af1: view.getFloat32(b + 112, true),
|
|
6496
|
+
af2: view.getFloat32(b + 108, true),
|
|
6497
|
+
iode: view.getUint16(b + 128, true),
|
|
6498
|
+
// IODnav
|
|
6499
|
+
crs: view.getFloat32(b + 88, true),
|
|
6500
|
+
deltaN: view.getFloat32(b + 72, true) * PI2,
|
|
6501
|
+
m0: view.getFloat64(b + 24, true) * PI2,
|
|
6502
|
+
cuc: view.getFloat32(b + 76, true),
|
|
6503
|
+
e: view.getFloat64(b + 32, true),
|
|
6504
|
+
cus: view.getFloat32(b + 80, true),
|
|
6505
|
+
sqrtA: view.getFloat64(b + 16, true),
|
|
6506
|
+
toe: toes,
|
|
6507
|
+
cic: view.getFloat32(b + 92, true),
|
|
6508
|
+
omega0: view.getFloat64(b + 56, true) * PI2,
|
|
6509
|
+
cis: view.getFloat32(b + 96, true),
|
|
6510
|
+
i0: view.getFloat64(b + 40, true) * PI2,
|
|
6511
|
+
crc: view.getFloat32(b + 84, true),
|
|
6512
|
+
omega: view.getFloat64(b + 48, true) * PI2,
|
|
6513
|
+
omegaDot: view.getFloat32(b + 64, true) * PI2,
|
|
6514
|
+
idot: view.getFloat32(b + 68, true) * PI2,
|
|
6515
|
+
week: wnc,
|
|
6516
|
+
// RINEX GAL week is GPS-aligned and continuous
|
|
6517
|
+
svHealth: svh,
|
|
6518
|
+
tgd: bgdE5a !== F4_DNU ? bgdE5a : 0
|
|
6519
|
+
// BGD E5a/E1 (RINEX slot)
|
|
6520
|
+
};
|
|
6328
6521
|
}
|
|
6329
|
-
function
|
|
6330
|
-
|
|
6331
|
-
if (!
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6522
|
+
function decodeBdsNav(view, b) {
|
|
6523
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
6524
|
+
if (!prn || prn[0] !== "C") return null;
|
|
6525
|
+
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
6526
|
+
const tgd1 = view.getFloat32(b + 24, true);
|
|
6527
|
+
const tocs = view.getUint32(b + 32, true);
|
|
6528
|
+
const toes = view.getUint32(b + 88, true);
|
|
6529
|
+
const wnToc = adjustWeek(bdsWeekRef, view.getUint16(b + 136, true), 8192);
|
|
6530
|
+
const wnToe = adjustWeek(bdsWeekRef, view.getUint16(b + 138, true), 8192);
|
|
6531
|
+
const tocDate = new Date(BDT_EPOCH_MS2 + wnToc * MS_PER_WEEK4 + tocs * 1e3);
|
|
6532
|
+
return {
|
|
6533
|
+
system: "C",
|
|
6534
|
+
prn,
|
|
6535
|
+
toc: sowOf2(tocDate.getTime()),
|
|
6536
|
+
tocDate,
|
|
6537
|
+
af0: view.getFloat32(b + 44, true),
|
|
6538
|
+
af1: view.getFloat32(b + 40, true),
|
|
6539
|
+
af2: view.getFloat32(b + 36, true),
|
|
6540
|
+
iode: view.getUint8(b + 21),
|
|
6541
|
+
// AODE
|
|
6542
|
+
crs: view.getFloat32(b + 48, true),
|
|
6543
|
+
deltaN: view.getFloat32(b + 52, true) * PI2,
|
|
6544
|
+
m0: view.getFloat64(b + 56, true) * PI2,
|
|
6545
|
+
cuc: view.getFloat32(b + 64, true),
|
|
6546
|
+
e: view.getFloat64(b + 68, true),
|
|
6547
|
+
cus: view.getFloat32(b + 76, true),
|
|
6548
|
+
sqrtA: view.getFloat64(b + 80, true),
|
|
6549
|
+
toe: toes,
|
|
6550
|
+
cic: view.getFloat32(b + 92, true),
|
|
6551
|
+
omega0: view.getFloat64(b + 96, true) * PI2,
|
|
6552
|
+
cis: view.getFloat32(b + 104, true),
|
|
6553
|
+
i0: view.getFloat64(b + 108, true) * PI2,
|
|
6554
|
+
crc: view.getFloat32(b + 116, true),
|
|
6555
|
+
omega: view.getFloat64(b + 120, true) * PI2,
|
|
6556
|
+
omegaDot: view.getFloat32(b + 128, true) * PI2,
|
|
6557
|
+
idot: view.getFloat32(b + 132, true) * PI2,
|
|
6558
|
+
week: wnToe,
|
|
6559
|
+
// RINEX BDS week field is the BDT week
|
|
6560
|
+
svHealth: view.getUint8(b + 19),
|
|
6561
|
+
// SatH1
|
|
6562
|
+
tgd: tgd1 !== F4_DNU ? tgd1 : 0
|
|
6563
|
+
// TGD1 B1/B3 (RINEX slot)
|
|
6564
|
+
};
|
|
6339
6565
|
}
|
|
6340
|
-
function
|
|
6341
|
-
|
|
6342
|
-
if (
|
|
6343
|
-
|
|
6344
|
-
const
|
|
6566
|
+
function decodeGloNav(view, b) {
|
|
6567
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
6568
|
+
if (!prn || prn[0] !== "R") return null;
|
|
6569
|
+
const wnc = view.getUint16(b + 12, true);
|
|
6570
|
+
const toes = view.getUint32(b + 76, true);
|
|
6571
|
+
const wnToe = adjustWeek(wnc, view.getUint16(b + 80, true), 1024);
|
|
6572
|
+
const toeGpsMs = gpsMs(wnToe, toes);
|
|
6573
|
+
const leapMs = getGpsLeap(new Date(toeGpsMs)) * 1e3;
|
|
6574
|
+
const tocDate = new Date(toeGpsMs - leapMs);
|
|
6575
|
+
const tofGpsMs = gpsMs(wnc, 0) + view.getUint32(b + 8, true);
|
|
6576
|
+
const tofLeapMs = getGpsLeap(new Date(tofGpsMs)) * 1e3;
|
|
6577
|
+
const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS8) / 1e3 % SEC_PER_WEEK8;
|
|
6345
6578
|
return {
|
|
6346
|
-
system: "
|
|
6579
|
+
system: "R",
|
|
6347
6580
|
prn,
|
|
6348
|
-
toc: sowOf2(tocDate.getTime()),
|
|
6349
6581
|
tocDate,
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
i0: f.i0,
|
|
6368
|
-
crc: f.crc,
|
|
6369
|
-
omega: f.omega,
|
|
6370
|
-
omegaDot: f.omegaDot,
|
|
6371
|
-
idot: f.idot,
|
|
6372
|
-
week,
|
|
6373
|
-
// RINEX BDS week field is the BDT week of toe
|
|
6374
|
-
svHealth: f.svh,
|
|
6375
|
-
// SatH1
|
|
6376
|
-
tgd: f.tgd1
|
|
6377
|
-
// TGD1 (B1) — RINEX slot
|
|
6582
|
+
// RINEX stores −τn as the clock bias; SBF carries τn (ICD sign)
|
|
6583
|
+
tauN: -view.getFloat32(b + 68, true),
|
|
6584
|
+
gammaN: view.getFloat32(b + 64, true),
|
|
6585
|
+
messageFrameTime,
|
|
6586
|
+
x: view.getFloat64(b + 16, true),
|
|
6587
|
+
// SBF unit is km, like RINEX
|
|
6588
|
+
xDot: view.getFloat32(b + 40, true),
|
|
6589
|
+
xAcc: view.getFloat32(b + 52, true),
|
|
6590
|
+
y: view.getFloat64(b + 24, true),
|
|
6591
|
+
yDot: view.getFloat32(b + 44, true),
|
|
6592
|
+
yAcc: view.getFloat32(b + 56, true),
|
|
6593
|
+
z: view.getFloat64(b + 32, true),
|
|
6594
|
+
zDot: view.getFloat32(b + 48, true),
|
|
6595
|
+
zAcc: view.getFloat32(b + 60, true),
|
|
6596
|
+
// MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
|
|
6597
|
+
health: view.getUint8(b + 85) >> 2,
|
|
6598
|
+
freqNum: view.getUint8(b + 15) - 8
|
|
6378
6599
|
};
|
|
6379
6600
|
}
|
|
6380
|
-
function
|
|
6381
|
-
|
|
6382
|
-
const
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
const af0 = getBitS2(b, i + 225, 7, i + 240, 17) * 2 ** -33;
|
|
6392
|
-
const af1 = getBitS2(b, i + 257, 5, i + 270, 17) * 2 ** -50;
|
|
6393
|
-
i = 8 * 38 * 1;
|
|
6394
|
-
const frn2 = getBitU(b, i + 15, 3);
|
|
6395
|
-
const sow2 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6396
|
-
const deltaN = getBitS2(b, i + 42, 10, i + 60, 6) * 2 ** -43 * GPS_PI;
|
|
6397
|
-
const cuc = getBitS2(b, i + 66, 16, i + 90, 2) * 2 ** -31;
|
|
6398
|
-
const m0 = getBitS2(b, i + 92, 20, i + 120, 12) * 2 ** -31 * GPS_PI;
|
|
6399
|
-
const e = getBitU22(b, i + 132, 10, i + 150, 22) * 2 ** -33;
|
|
6400
|
-
const cus = getBitS(b, i + 180, 18) * 2 ** -31;
|
|
6401
|
-
const crc = getBitS2(b, i + 198, 4, i + 210, 14) * 2 ** -6;
|
|
6402
|
-
const crs = getBitS2(b, i + 224, 8, i + 240, 10) * 2 ** -6;
|
|
6403
|
-
const sqrtA = getBitU22(b, i + 250, 12, i + 270, 20) * 2 ** -19;
|
|
6404
|
-
const toe1 = getBitU(b, i + 290, 2);
|
|
6405
|
-
i = 8 * 38 * 2;
|
|
6406
|
-
const frn3 = getBitU(b, i + 15, 3);
|
|
6407
|
-
const sow3 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6408
|
-
const toe2 = getBitU22(b, i + 42, 10, i + 60, 5);
|
|
6409
|
-
const i0 = getBitS2(b, i + 65, 17, i + 90, 15) * 2 ** -31 * GPS_PI;
|
|
6410
|
-
const cic = getBitS2(b, i + 105, 7, i + 120, 11) * 2 ** -31;
|
|
6411
|
-
const omegaDot = getBitS2(b, i + 131, 11, i + 150, 13) * 2 ** -43 * GPS_PI;
|
|
6412
|
-
const cis = getBitS2(b, i + 163, 9, i + 180, 9) * 2 ** -31;
|
|
6413
|
-
const idot = getBitS2(b, i + 189, 13, i + 210, 1) * 2 ** -43 * GPS_PI;
|
|
6414
|
-
const omega0 = getBitS2(b, i + 211, 21, i + 240, 11) * 2 ** -31 * GPS_PI;
|
|
6415
|
-
const omega = getBitS2(b, i + 251, 11, i + 270, 21) * 2 ** -31 * GPS_PI;
|
|
6416
|
-
const toes = (toe1 * 2 ** 15 + toe2) * 8;
|
|
6417
|
-
if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3) return null;
|
|
6418
|
-
if (sow2 !== sow1 + 6 || sow3 !== sow2 + 6) return null;
|
|
6419
|
-
if (tocSec !== toes) return null;
|
|
6420
|
-
return buildBdsEphemeris(opts.prn ?? "C00", {
|
|
6421
|
-
week,
|
|
6422
|
-
sow: sow1,
|
|
6423
|
-
toes,
|
|
6424
|
-
tocSec,
|
|
6425
|
-
svh,
|
|
6426
|
-
tgd1,
|
|
6427
|
-
af0,
|
|
6428
|
-
af1,
|
|
6429
|
-
af2,
|
|
6430
|
-
crs,
|
|
6431
|
-
deltaN,
|
|
6432
|
-
m0,
|
|
6433
|
-
cuc,
|
|
6434
|
-
e,
|
|
6435
|
-
cus,
|
|
6436
|
-
sqrtA,
|
|
6437
|
-
cic,
|
|
6438
|
-
omega0,
|
|
6439
|
-
cis,
|
|
6440
|
-
i0,
|
|
6441
|
-
crc,
|
|
6442
|
-
omega,
|
|
6443
|
-
omegaDot,
|
|
6444
|
-
idot
|
|
6445
|
-
});
|
|
6446
|
-
}
|
|
6447
|
-
function decodeBdsD2Frame(pages, opts = {}) {
|
|
6448
|
-
if (pages.length < 10 * BDS_SUBFRAME_BYTES) return null;
|
|
6449
|
-
const b = pages;
|
|
6450
|
-
let i = 8 * 38 * 0;
|
|
6451
|
-
const pgn1 = getBitU(b, i + 42, 4);
|
|
6452
|
-
const sow1 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6453
|
-
const svh = getBitU(b, i + 46, 1);
|
|
6454
|
-
const week = getBitU(b, i + 64, 13);
|
|
6455
|
-
const tocSec = getBitU22(b, i + 77, 5, i + 90, 12) * 8;
|
|
6456
|
-
const tgd1 = getBitS(b, i + 102, 10) * 0.1 * 1e-9;
|
|
6457
|
-
i = 8 * 38 * 2;
|
|
6458
|
-
const pgn3 = getBitU(b, i + 42, 4);
|
|
6459
|
-
const sow3 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6460
|
-
const af0 = getBitS2(b, i + 100, 12, i + 120, 12) * 2 ** -33;
|
|
6461
|
-
const af1p3 = getBitS(b, i + 132, 4);
|
|
6462
|
-
i = 8 * 38 * 3;
|
|
6463
|
-
const pgn4 = getBitU(b, i + 42, 4);
|
|
6464
|
-
const sow4 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6465
|
-
const af1p4 = getBitU22(b, i + 46, 6, i + 60, 12);
|
|
6466
|
-
const af2 = getBitS2(b, i + 72, 10, i + 90, 1) * 2 ** -66;
|
|
6467
|
-
const deltaN = getBitS(b, i + 96, 16) * 2 ** -43 * GPS_PI;
|
|
6468
|
-
const cucp4 = getBitS(b, i + 120, 14);
|
|
6469
|
-
i = 8 * 38 * 4;
|
|
6470
|
-
const pgn5 = getBitU(b, i + 42, 4);
|
|
6471
|
-
const sow5 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6472
|
-
const cucp5 = getBitU(b, i + 46, 4);
|
|
6473
|
-
const m0 = getBitS3(b, i + 50, 2, i + 60, 22, i + 90, 8) * 2 ** -31 * GPS_PI;
|
|
6474
|
-
const cus = getBitS2(b, i + 98, 14, i + 120, 4) * 2 ** -31;
|
|
6475
|
-
const ep5 = getBitS(b, i + 124, 10);
|
|
6476
|
-
i = 8 * 38 * 5;
|
|
6477
|
-
const pgn6 = getBitU(b, i + 42, 4);
|
|
6478
|
-
const sow6 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6479
|
-
const ep6 = getBitU22(b, i + 46, 6, i + 60, 16);
|
|
6480
|
-
const sqrtA = getBitU3(b, i + 76, 6, i + 90, 22, i + 120, 4) * 2 ** -19;
|
|
6481
|
-
const cicp6 = getBitS(b, i + 124, 10);
|
|
6482
|
-
i = 8 * 38 * 6;
|
|
6483
|
-
const pgn7 = getBitU(b, i + 42, 4);
|
|
6484
|
-
const sow7 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6485
|
-
const cicp7 = getBitU22(b, i + 46, 6, i + 60, 2);
|
|
6486
|
-
const cis = getBitS(b, i + 62, 18) * 2 ** -31;
|
|
6487
|
-
const toes = getBitU22(b, i + 80, 2, i + 90, 15) * 8;
|
|
6488
|
-
const i0p7 = getBitS2(b, i + 105, 7, i + 120, 14);
|
|
6489
|
-
i = 8 * 38 * 7;
|
|
6490
|
-
const pgn8 = getBitU(b, i + 42, 4);
|
|
6491
|
-
const sow8 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6492
|
-
const i0p8 = getBitU22(b, i + 46, 6, i + 60, 5);
|
|
6493
|
-
const crc = getBitS2(b, i + 65, 17, i + 90, 1) * 2 ** -6;
|
|
6494
|
-
const crs = getBitS(b, i + 91, 18) * 2 ** -6;
|
|
6495
|
-
const omegaDotP8 = getBitS2(b, i + 109, 3, i + 120, 16);
|
|
6496
|
-
i = 8 * 38 * 8;
|
|
6497
|
-
const pgn9 = getBitU(b, i + 42, 4);
|
|
6498
|
-
const sow9 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6499
|
-
const omegaDotP9 = getBitU(b, i + 46, 5);
|
|
6500
|
-
const omega0 = getBitS3(b, i + 51, 1, i + 60, 22, i + 90, 9) * 2 ** -31 * GPS_PI;
|
|
6501
|
-
const omegaP9 = getBitS2(b, i + 99, 13, i + 120, 14);
|
|
6502
|
-
i = 8 * 38 * 9;
|
|
6503
|
-
const pgn10 = getBitU(b, i + 42, 4);
|
|
6504
|
-
const sow10 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6505
|
-
const omegaP10 = getBitU(b, i + 46, 5);
|
|
6506
|
-
const idot = getBitS2(b, i + 51, 1, i + 60, 13) * 2 ** -43 * GPS_PI;
|
|
6507
|
-
if (pgn1 !== 1 || pgn3 !== 3 || pgn4 !== 4 || pgn5 !== 5 || pgn6 !== 6 || pgn7 !== 7 || pgn8 !== 8 || pgn9 !== 9 || pgn10 !== 10) {
|
|
6508
|
-
return null;
|
|
6509
|
-
}
|
|
6510
|
-
if (sow3 !== sow1 + 6 || sow4 !== sow3 + 3 || sow5 !== sow4 + 3 || sow6 !== sow5 + 3 || sow7 !== sow6 + 3 || sow8 !== sow7 + 3 || sow9 !== sow8 + 3 || sow10 !== sow9 + 3) {
|
|
6511
|
-
return null;
|
|
6512
|
-
}
|
|
6513
|
-
if (tocSec !== toes) return null;
|
|
6514
|
-
return buildBdsEphemeris(opts.prn ?? "C00", {
|
|
6515
|
-
week,
|
|
6516
|
-
sow: sow1,
|
|
6517
|
-
toes,
|
|
6518
|
-
tocSec,
|
|
6519
|
-
svh,
|
|
6520
|
-
tgd1,
|
|
6521
|
-
af0,
|
|
6522
|
-
af1: mergeS(af1p3, af1p4, 18) * 2 ** -50,
|
|
6523
|
-
af2,
|
|
6524
|
-
crs,
|
|
6525
|
-
deltaN,
|
|
6526
|
-
m0,
|
|
6527
|
-
cuc: mergeS(cucp4, cucp5, 4) * 2 ** -31,
|
|
6528
|
-
e: mergeS(ep5, ep6, 22) * 2 ** -33,
|
|
6529
|
-
cus,
|
|
6530
|
-
sqrtA,
|
|
6531
|
-
cic: mergeS(cicp6, cicp7, 8) * 2 ** -31,
|
|
6532
|
-
omega0,
|
|
6533
|
-
cis,
|
|
6534
|
-
i0: mergeS(i0p7, i0p8, 11) * 2 ** -31 * GPS_PI,
|
|
6535
|
-
crc,
|
|
6536
|
-
omega: mergeS(omegaP9, omegaP10, 5) * 2 ** -31 * GPS_PI,
|
|
6537
|
-
omegaDot: mergeS(omegaDotP8, omegaDotP9, 5) * 2 ** -43 * GPS_PI,
|
|
6538
|
-
idot
|
|
6601
|
+
function parseSbfNav(data) {
|
|
6602
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6603
|
+
const ephemerides = [];
|
|
6604
|
+
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
6605
|
+
let eph = null;
|
|
6606
|
+
if (id === 5891 && len >= 140) eph = decodeGpsQzsNav(view, b, "G");
|
|
6607
|
+
else if (id === 4095 && len >= 140) eph = decodeGpsQzsNav(view, b, "J");
|
|
6608
|
+
else if (id === 4002 && len >= 149) eph = decodeGalNav(view, b);
|
|
6609
|
+
else if (id === 4081 && len >= 140) eph = decodeBdsNav(view, b);
|
|
6610
|
+
else if (id === 4004 && len >= 96) eph = decodeGloNav(view, b);
|
|
6611
|
+
if (eph) ephemerides.push(eph);
|
|
6539
6612
|
});
|
|
6613
|
+
return { ephemerides, badCrc };
|
|
6540
6614
|
}
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
} else {
|
|
6567
|
-
if (id !== 1) return null;
|
|
6568
|
-
const pgn = getBitU(subframe, 42, 4);
|
|
6569
|
-
if (pgn < 1 || pgn > 10) return null;
|
|
6570
|
-
sat.buf.set(subframe.subarray(0, BDS_SUBFRAME_BYTES), (pgn - 1) * 38);
|
|
6571
|
-
if (pgn === 10) eph = decodeBdsD2Frame(sat.buf, { prn });
|
|
6572
|
-
}
|
|
6573
|
-
if (!eph) return null;
|
|
6574
|
-
const key = `${eph.week}:${eph.toe}`;
|
|
6575
|
-
if (key === sat.lastKey) return null;
|
|
6576
|
-
sat.lastKey = key;
|
|
6577
|
-
return eph;
|
|
6615
|
+
var GAL_SQRT_A_NOMINAL = Math.sqrt(296e5);
|
|
6616
|
+
var GAL_I_NOMINAL = 56 / 180 * PI2;
|
|
6617
|
+
var I_REF_03 = 0.3 * PI2;
|
|
6618
|
+
function decodeGpsGalAlm(view, b, sys) {
|
|
6619
|
+
const wnc = view.getUint16(b + 12, true);
|
|
6620
|
+
const toa = view.getUint32(b + 20, true);
|
|
6621
|
+
if (toa === U4_DNU) return null;
|
|
6622
|
+
let prn;
|
|
6623
|
+
let weekAlm;
|
|
6624
|
+
let i0;
|
|
6625
|
+
let sqrtA = view.getFloat32(b + 32, true);
|
|
6626
|
+
let health;
|
|
6627
|
+
if (sys === "G") {
|
|
6628
|
+
prn = view.getUint8(b + 14);
|
|
6629
|
+
if (prn < 1 || prn > 32) return null;
|
|
6630
|
+
weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 256);
|
|
6631
|
+
i0 = I_REF_03 + view.getFloat32(b + 24, true) * PI2;
|
|
6632
|
+
health = view.getUint8(b + 58);
|
|
6633
|
+
} else {
|
|
6634
|
+
prn = view.getUint8(b + 57) - 70;
|
|
6635
|
+
if (prn < 1 || prn > 36) return null;
|
|
6636
|
+
weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 4);
|
|
6637
|
+
i0 = GAL_I_NOMINAL + view.getFloat32(b + 24, true) * PI2;
|
|
6638
|
+
sqrtA += GAL_SQRT_A_NOMINAL;
|
|
6639
|
+
health = view.getUint16(b + 58, true);
|
|
6578
6640
|
}
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6641
|
+
return {
|
|
6642
|
+
system: sys,
|
|
6643
|
+
prn: `${sys}${String(prn).padStart(2, "0")}`,
|
|
6644
|
+
weekAlm,
|
|
6645
|
+
toaSec: toa,
|
|
6646
|
+
sqrtA,
|
|
6647
|
+
e: view.getFloat32(b + 16, true),
|
|
6648
|
+
i0OrDeltaI: i0,
|
|
6649
|
+
omega0: view.getFloat32(b + 36, true) * PI2,
|
|
6650
|
+
omega: view.getFloat32(b + 40, true) * PI2,
|
|
6651
|
+
m0: view.getFloat32(b + 44, true) * PI2,
|
|
6652
|
+
omegaDot: view.getFloat32(b + 28, true) * PI2,
|
|
6653
|
+
af0: view.getFloat32(b + 52, true),
|
|
6654
|
+
af1: view.getFloat32(b + 48, true),
|
|
6655
|
+
health
|
|
6656
|
+
};
|
|
6589
6657
|
}
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
}
|
|
6615
|
-
return n === 0 || n === 2 && cs === 1;
|
|
6658
|
+
function decodeBdsAlm(view, b) {
|
|
6659
|
+
const prnStr = svidToPrn(view.getUint8(b + 14));
|
|
6660
|
+
if (!prnStr || prnStr[0] !== "C") return null;
|
|
6661
|
+
const prn = parseInt(prnStr.slice(1), 10);
|
|
6662
|
+
const toa = view.getUint32(b + 16, true);
|
|
6663
|
+
if (toa === U4_DNU) return null;
|
|
6664
|
+
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
6665
|
+
const geo = prn <= 5 || prn >= 59;
|
|
6666
|
+
return {
|
|
6667
|
+
system: "C",
|
|
6668
|
+
prn: prnStr,
|
|
6669
|
+
weekAlm: adjustWeek(bdsWeekRef, view.getUint8(b + 15), 256),
|
|
6670
|
+
toaSec: toa,
|
|
6671
|
+
sqrtA: view.getFloat32(b + 20, true),
|
|
6672
|
+
e: view.getFloat32(b + 24, true),
|
|
6673
|
+
i0OrDeltaI: (geo ? 0 : I_REF_03) + view.getFloat32(b + 44, true) * PI2,
|
|
6674
|
+
omega0: view.getFloat32(b + 36, true) * PI2,
|
|
6675
|
+
omega: view.getFloat32(b + 28, true) * PI2,
|
|
6676
|
+
m0: view.getFloat32(b + 32, true) * PI2,
|
|
6677
|
+
omegaDot: view.getFloat32(b + 40, true) * PI2,
|
|
6678
|
+
af0: view.getFloat32(b + 48, true),
|
|
6679
|
+
af1: view.getFloat32(b + 52, true),
|
|
6680
|
+
health: view.getUint16(b + 56, true)
|
|
6681
|
+
};
|
|
6616
6682
|
}
|
|
6617
|
-
function
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
const
|
|
6622
|
-
|
|
6623
|
-
i += 2;
|
|
6624
|
-
const tkH = getBitU(b, i, 5);
|
|
6625
|
-
i += 5;
|
|
6626
|
-
const tkM = getBitU(b, i, 6);
|
|
6627
|
-
i += 6;
|
|
6628
|
-
const tkS = getBitU(b, i, 1) * 30;
|
|
6629
|
-
i += 1;
|
|
6630
|
-
const xDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6631
|
-
i += 24;
|
|
6632
|
-
const xAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6633
|
-
i += 5;
|
|
6634
|
-
const x = getBitG(b, i, 27) * 2 ** -11;
|
|
6635
|
-
i = 80 + 1;
|
|
6636
|
-
const frn2 = getBitU(b, i, 4);
|
|
6637
|
-
i += 4;
|
|
6638
|
-
const bn = getBitU(b, i, 1);
|
|
6639
|
-
i += 1 + 2;
|
|
6640
|
-
i += 1;
|
|
6641
|
-
const tb = getBitU(b, i, 7);
|
|
6642
|
-
i += 7 + 5;
|
|
6643
|
-
const yDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6644
|
-
i += 24;
|
|
6645
|
-
const yAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6646
|
-
i += 5;
|
|
6647
|
-
const y = getBitG(b, i, 27) * 2 ** -11;
|
|
6648
|
-
i = 160 + 1;
|
|
6649
|
-
const frn3 = getBitU(b, i, 4);
|
|
6650
|
-
i += 4;
|
|
6651
|
-
i += 1;
|
|
6652
|
-
const gammaN = getBitG(b, i, 11) * 2 ** -40;
|
|
6653
|
-
i += 11 + 1;
|
|
6654
|
-
i += 2;
|
|
6655
|
-
i += 1;
|
|
6656
|
-
const zDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6657
|
-
i += 24;
|
|
6658
|
-
const zAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6659
|
-
i += 5;
|
|
6660
|
-
const z = getBitG(b, i, 27) * 2 ** -11;
|
|
6661
|
-
i = 240 + 1;
|
|
6662
|
-
const frn4 = getBitU(b, i, 4);
|
|
6663
|
-
i += 4;
|
|
6664
|
-
const tauN = getBitG(b, i, 22) * 2 ** -30;
|
|
6665
|
-
i += 22;
|
|
6666
|
-
i += 5;
|
|
6667
|
-
i += 5;
|
|
6668
|
-
i += 14;
|
|
6669
|
-
i += 1;
|
|
6670
|
-
i += 4;
|
|
6671
|
-
i += 3;
|
|
6672
|
-
i += 11;
|
|
6673
|
-
const slot = getBitU(b, i, 5);
|
|
6674
|
-
if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3 || frn4 !== 4) return null;
|
|
6675
|
-
if (slot < 1 || slot > 27) return null;
|
|
6676
|
-
const utcSec = (getUtcDate(refDate).getTime() - GPS_EPOCH_MS7) / 1e3;
|
|
6677
|
-
const week = Math.floor(utcSec / SEC_PER_WEEK7);
|
|
6678
|
-
let tow = utcSec - week * SEC_PER_WEEK7;
|
|
6679
|
-
const tod = tow % SEC_PER_DAY;
|
|
6680
|
-
tow -= tod;
|
|
6681
|
-
let tof = tkH * 3600 + tkM * 60 + tkS - 10800;
|
|
6682
|
-
if (tof < tod - 43200) tof += SEC_PER_DAY;
|
|
6683
|
-
else if (tof > tod + 43200) tof -= SEC_PER_DAY;
|
|
6684
|
-
let toe = tb * 900 - 10800;
|
|
6685
|
-
if (toe < tod - 43200) toe += SEC_PER_DAY;
|
|
6686
|
-
else if (toe > tod + 43200) toe -= SEC_PER_DAY;
|
|
6683
|
+
function decodeGloAlm(view, b) {
|
|
6684
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
6685
|
+
if (!prn || prn[0] !== "R") return null;
|
|
6686
|
+
const wnc = view.getUint16(b + 12, true);
|
|
6687
|
+
const toa = view.getUint32(b + 20, true);
|
|
6688
|
+
if (toa === U4_DNU) return null;
|
|
6687
6689
|
return {
|
|
6688
6690
|
system: "R",
|
|
6689
|
-
prn
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6691
|
+
prn,
|
|
6692
|
+
freqNr: view.getUint8(b + 15) - 8,
|
|
6693
|
+
weekAlm: adjustWeek(wnc, view.getUint8(b + 52), 256),
|
|
6694
|
+
toaSec: toa,
|
|
6695
|
+
epsilon: view.getFloat32(b + 16, true),
|
|
6696
|
+
deltaI: view.getFloat32(b + 24, true) * PI2,
|
|
6697
|
+
lambda: view.getFloat32(b + 28, true) * PI2,
|
|
6698
|
+
tLambda: view.getFloat32(b + 32, true),
|
|
6699
|
+
omega: view.getFloat32(b + 36, true) * PI2,
|
|
6700
|
+
deltaT: view.getFloat32(b + 40, true),
|
|
6701
|
+
deltaTDot: view.getFloat32(b + 44, true),
|
|
6702
|
+
tau: view.getFloat32(b + 48, true),
|
|
6703
|
+
health: view.getUint8(b + 53),
|
|
6704
|
+
nDay: view.getUint16(b + 54, true),
|
|
6705
|
+
n4: view.getUint8(b + 57)
|
|
6706
|
+
};
|
|
6707
|
+
}
|
|
6708
|
+
function parseSbfAlmanac(data) {
|
|
6709
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6710
|
+
const almanacs = [];
|
|
6711
|
+
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
6712
|
+
let alm = null;
|
|
6713
|
+
if (id === 5892 && len >= 60) alm = decodeGpsGalAlm(view, b, "G");
|
|
6714
|
+
else if (id === 4003 && len >= 61) alm = decodeGpsGalAlm(view, b, "E");
|
|
6715
|
+
else if (id === 4119 && len >= 60) alm = decodeBdsAlm(view, b);
|
|
6716
|
+
else if (id === 4005 && len >= 60) alm = decodeGloAlm(view, b);
|
|
6717
|
+
if (alm) almanacs.push(alm);
|
|
6718
|
+
});
|
|
6719
|
+
return { almanacs, badCrc };
|
|
6720
|
+
}
|
|
6721
|
+
|
|
6722
|
+
// src/sbf/iono.ts
|
|
6723
|
+
var F4_DNU2 = -2e10;
|
|
6724
|
+
function parseSbfIonoUtc(data) {
|
|
6725
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6726
|
+
const ionoCorrections = {};
|
|
6727
|
+
let leapSeconds = null;
|
|
6728
|
+
const f4s = (b, n) => {
|
|
6729
|
+
const out = [];
|
|
6730
|
+
for (let k = 0; k < n; k++) {
|
|
6731
|
+
const v = view.getFloat32(b + 4 * k, true);
|
|
6732
|
+
if (v === F4_DNU2) return null;
|
|
6733
|
+
out.push(v);
|
|
6734
|
+
}
|
|
6735
|
+
return out;
|
|
6736
|
+
};
|
|
6737
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
6738
|
+
if ((id === 5893 || id === 4120) && len >= 48) {
|
|
6739
|
+
const alpha = f4s(b + 16, 4);
|
|
6740
|
+
const beta = f4s(b + 32, 4);
|
|
6741
|
+
if (!alpha || !beta) return;
|
|
6742
|
+
const sys = id === 5893 ? "GPS" : "BDS";
|
|
6743
|
+
ionoCorrections[`${sys}A`] = alpha;
|
|
6744
|
+
ionoCorrections[`${sys}B`] = beta;
|
|
6745
|
+
} else if (id === 4030 && len >= 28) {
|
|
6746
|
+
const ai = f4s(b + 16, 3);
|
|
6747
|
+
if (ai) ionoCorrections["GAL"] = ai;
|
|
6748
|
+
} else if (id === 5894 && len >= 37) {
|
|
6749
|
+
leapSeconds = view.getInt8(b + 33);
|
|
6750
|
+
}
|
|
6751
|
+
});
|
|
6752
|
+
return { ionoCorrections, leapSeconds };
|
|
6753
|
+
}
|
|
6754
|
+
|
|
6755
|
+
// src/sbf/rawnav.ts
|
|
6756
|
+
var SIGNAL_OF_BLOCK = {
|
|
6757
|
+
4018: "L2C",
|
|
6758
|
+
4019: "L5"
|
|
6759
|
+
};
|
|
6760
|
+
function parseSbfCnav(data) {
|
|
6761
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6762
|
+
const ephemerides = [];
|
|
6763
|
+
const assemblers = {
|
|
6764
|
+
L2C: new CnavAssembler(),
|
|
6765
|
+
L5: new CnavAssembler()
|
|
6709
6766
|
};
|
|
6767
|
+
let badCrc = 0;
|
|
6768
|
+
let messages = 0;
|
|
6769
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
6770
|
+
const signal = SIGNAL_OF_BLOCK[id];
|
|
6771
|
+
if (!signal || len < 60) return;
|
|
6772
|
+
messages++;
|
|
6773
|
+
const msg = new Uint8Array(40);
|
|
6774
|
+
for (let k = 0; k < 10; k++) {
|
|
6775
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
6776
|
+
msg[4 * k] = w >>> 24;
|
|
6777
|
+
msg[4 * k + 1] = w >>> 16 & 255;
|
|
6778
|
+
msg[4 * k + 2] = w >>> 8 & 255;
|
|
6779
|
+
msg[4 * k + 3] = w & 255;
|
|
6780
|
+
}
|
|
6781
|
+
if (!cnavCrcOk(msg)) {
|
|
6782
|
+
badCrc++;
|
|
6783
|
+
return;
|
|
6784
|
+
}
|
|
6785
|
+
const eph = assemblers[signal].push(msg);
|
|
6786
|
+
if (eph) ephemerides.push({ ...eph, signal });
|
|
6787
|
+
});
|
|
6788
|
+
return { ephemerides, badCrc, messages };
|
|
6710
6789
|
}
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6790
|
+
|
|
6791
|
+
// src/sbf/rawnav-gal.ts
|
|
6792
|
+
var INAV_SOURCES = [17, 21, 22];
|
|
6793
|
+
var FNAV_SOURCES = [20, 22];
|
|
6794
|
+
function parseSbfGalNav(data) {
|
|
6795
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6796
|
+
const ephemerides = [];
|
|
6797
|
+
const inav = new GalInavAssembler();
|
|
6798
|
+
const fnav = new GalFnavAssembler();
|
|
6799
|
+
let badCrc = 0;
|
|
6800
|
+
let messages = 0;
|
|
6801
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
6802
|
+
if (id !== 4022 && id !== 4023 || len < 52) return;
|
|
6803
|
+
messages++;
|
|
6804
|
+
const prnStr = svidToPrn(data[b + 14]);
|
|
6805
|
+
if (!prnStr || prnStr[0] !== "E") return;
|
|
6806
|
+
const prn = parseInt(prnStr.slice(1), 10);
|
|
6807
|
+
const source = data[b + 17] & 31;
|
|
6808
|
+
const page = new Uint8Array(32);
|
|
6809
|
+
for (let k = 0; k < 8; k++) {
|
|
6810
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
6811
|
+
page[4 * k] = w >>> 24;
|
|
6812
|
+
page[4 * k + 1] = w >>> 16 & 255;
|
|
6813
|
+
page[4 * k + 2] = w >>> 8 & 255;
|
|
6814
|
+
page[4 * k + 3] = w & 255;
|
|
6731
6815
|
}
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
if (
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6816
|
+
let eph;
|
|
6817
|
+
let src;
|
|
6818
|
+
if (id === 4023) {
|
|
6819
|
+
src = "inav";
|
|
6820
|
+
if (!INAV_SOURCES.includes(source)) return;
|
|
6821
|
+
if (getBitU22(page, 0) !== 0 || getBitU22(page, 114) !== 1) return;
|
|
6822
|
+
if (!galInavPageCrcOk(page)) {
|
|
6823
|
+
badCrc++;
|
|
6824
|
+
return;
|
|
6825
|
+
}
|
|
6826
|
+
eph = inav.push(prn, page);
|
|
6827
|
+
} else {
|
|
6828
|
+
src = "fnav";
|
|
6829
|
+
if (!FNAV_SOURCES.includes(source)) return;
|
|
6830
|
+
if (isFnavDummy(page)) return;
|
|
6831
|
+
if (!galFnavPageCrcOk(page)) {
|
|
6832
|
+
badCrc++;
|
|
6833
|
+
return;
|
|
6834
|
+
}
|
|
6835
|
+
eph = fnav.push(prn, page);
|
|
6836
|
+
}
|
|
6837
|
+
if (eph) ephemerides.push({ ...eph, source: src });
|
|
6838
|
+
});
|
|
6839
|
+
return { ephemerides, badCrc, messages };
|
|
6840
|
+
}
|
|
6841
|
+
function getBitU22(buff, pos) {
|
|
6842
|
+
return buff[pos >> 3] >> 7 - (pos & 7) & 1;
|
|
6843
|
+
}
|
|
6844
|
+
function isFnavDummy(page) {
|
|
6845
|
+
return page[0] >> 2 === 63;
|
|
6846
|
+
}
|
|
6743
6847
|
|
|
6744
6848
|
// src/sbf/rawnav-bds.ts
|
|
6745
|
-
var
|
|
6849
|
+
var GPS_EPOCH_MS9 = Date.UTC(1980, 0, 6);
|
|
6746
6850
|
var MS_PER_WEEK5 = 7 * 86400 * 1e3;
|
|
6747
6851
|
var TOW_DNU = 4294967295;
|
|
6748
6852
|
var WNC_DNU = 65535;
|
|
@@ -6812,7 +6916,7 @@ function parseSbfGloNav(data) {
|
|
|
6812
6916
|
const eph = assembler.push(
|
|
6813
6917
|
prn,
|
|
6814
6918
|
str,
|
|
6815
|
-
new Date(
|
|
6919
|
+
new Date(GPS_EPOCH_MS9 + wnc * MS_PER_WEEK5 + towMs),
|
|
6816
6920
|
view.getUint8(b + 18) - 8
|
|
6817
6921
|
// FreqNr, offset by 8
|
|
6818
6922
|
);
|
|
@@ -7346,7 +7450,7 @@ function parseSbfHas(data) {
|
|
|
7346
7450
|
|
|
7347
7451
|
// src/sbf/index.ts
|
|
7348
7452
|
var CLIGHT = 299792458;
|
|
7349
|
-
var
|
|
7453
|
+
var GPS_EPOCH_MS10 = Date.UTC(1980, 0, 6);
|
|
7350
7454
|
var MS_PER_WEEK6 = 7 * 864e5;
|
|
7351
7455
|
var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
|
|
7352
7456
|
var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
|
|
@@ -7446,23 +7550,23 @@ var MEAS2_SIG = [
|
|
|
7446
7550
|
["J", "1E"],
|
|
7447
7551
|
["J", "5P"]
|
|
7448
7552
|
];
|
|
7449
|
-
var
|
|
7553
|
+
var two4 = (n) => String(n).padStart(2, "0");
|
|
7450
7554
|
function m3Prn(navsys, svid) {
|
|
7451
7555
|
switch (navsys) {
|
|
7452
7556
|
case 0:
|
|
7453
|
-
return svid < 32 ? `G${
|
|
7557
|
+
return svid < 32 ? `G${two4(svid + 1)}` : null;
|
|
7454
7558
|
case 1:
|
|
7455
|
-
return svid < 27 ? `R${
|
|
7559
|
+
return svid < 27 ? `R${two4(svid + 1)}` : null;
|
|
7456
7560
|
case 2:
|
|
7457
|
-
return svid < 36 ? `E${
|
|
7561
|
+
return svid < 36 ? `E${two4(svid + 1)}` : null;
|
|
7458
7562
|
case 3:
|
|
7459
|
-
return svid < 50 ? `C${
|
|
7563
|
+
return svid < 50 ? `C${two4(svid + 1)}` : null;
|
|
7460
7564
|
case 4:
|
|
7461
|
-
return svid <= 38 ? `S${
|
|
7565
|
+
return svid <= 38 ? `S${two4(svid + 20)}` : null;
|
|
7462
7566
|
case 5:
|
|
7463
|
-
return svid < 10 ? `J${
|
|
7567
|
+
return svid < 10 ? `J${two4(svid + 1)}` : null;
|
|
7464
7568
|
case 6:
|
|
7465
|
-
return svid < 14 ? `I${
|
|
7569
|
+
return svid < 14 ? `I${two4(svid + 1)}` : null;
|
|
7466
7570
|
default:
|
|
7467
7571
|
return null;
|
|
7468
7572
|
}
|
|
@@ -8000,7 +8104,7 @@ function parseSbfMeas(data) {
|
|
|
8000
8104
|
const tow = view.getUint32(i + 8, true);
|
|
8001
8105
|
const wnc = view.getUint16(i + 12, true);
|
|
8002
8106
|
if (tow !== 4294967295 && wnc !== 65535) {
|
|
8003
|
-
ensureEpoch(
|
|
8107
|
+
ensureEpoch(GPS_EPOCH_MS10 + wnc * MS_PER_WEEK6 + tow);
|
|
8004
8108
|
if (id === 4109) decodeMeas3Ranges(i, tow);
|
|
8005
8109
|
else if (id === 4110) decodeMeas3CN0(i, len);
|
|
8006
8110
|
else if (id === 4111) decodeMeas3Doppler(i, len);
|
|
@@ -8065,15 +8169,15 @@ var ID_GALEPHEMERIS = 1122;
|
|
|
8065
8169
|
var ID_QZSSEPHEMERIS = 1336;
|
|
8066
8170
|
var ID_GPSEPHEM = 7;
|
|
8067
8171
|
var ID_BDSEPHEMERIS = 1696;
|
|
8068
|
-
var
|
|
8172
|
+
var GPS_EPOCH_MS11 = Date.UTC(1980, 0, 6);
|
|
8069
8173
|
var BDT_EPOCH_MS3 = Date.UTC(2006, 0, 1);
|
|
8070
|
-
var
|
|
8174
|
+
var SEC_PER_WEEK9 = 7 * 86400;
|
|
8071
8175
|
var SEC_PER_DAY2 = 86400;
|
|
8072
|
-
var MS_PER_WEEK7 =
|
|
8073
|
-
var gpsMs2 = (week, sec) =>
|
|
8074
|
-
var sowOf3 = (dateMs) => dateMs / 1e3 %
|
|
8176
|
+
var MS_PER_WEEK7 = SEC_PER_WEEK9 * 1e3;
|
|
8177
|
+
var gpsMs2 = (week, sec) => GPS_EPOCH_MS11 + week * MS_PER_WEEK7 + sec * 1e3;
|
|
8178
|
+
var sowOf3 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK9;
|
|
8075
8179
|
function nearWeekMs(refMs, sow) {
|
|
8076
|
-
const refWeek = Math.floor((refMs -
|
|
8180
|
+
const refWeek = Math.floor((refMs - GPS_EPOCH_MS11) / MS_PER_WEEK7);
|
|
8077
8181
|
let ms = gpsMs2(refWeek, sow);
|
|
8078
8182
|
if (ms < refMs - MS_PER_WEEK7 / 2) ms += MS_PER_WEEK7;
|
|
8079
8183
|
else if (ms > refMs + MS_PER_WEEK7 / 2) ms -= MS_PER_WEEK7;
|
|
@@ -8100,9 +8204,9 @@ function decodeGloEphemeris(view, p) {
|
|
|
8100
8204
|
tof += Math.floor(tow / SEC_PER_DAY2) * SEC_PER_DAY2;
|
|
8101
8205
|
if (tof < tow - 43200) tof += SEC_PER_DAY2;
|
|
8102
8206
|
else if (tof > tow + 43200) tof -= SEC_PER_DAY2;
|
|
8103
|
-
const toeGpsMs =
|
|
8104
|
-
const tofGpsMs =
|
|
8105
|
-
const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() -
|
|
8207
|
+
const toeGpsMs = GPS_EPOCH_MS11 + (week * SEC_PER_WEEK9 + tow) * 1e3;
|
|
8208
|
+
const tofGpsMs = GPS_EPOCH_MS11 + (week * SEC_PER_WEEK9 + tof) * 1e3;
|
|
8209
|
+
const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() - GPS_EPOCH_MS11) / 1e3;
|
|
8106
8210
|
return {
|
|
8107
8211
|
system: "R",
|
|
8108
8212
|
prn: `R${String(slot).padStart(2, "0")}`,
|
|
@@ -8110,7 +8214,7 @@ function decodeGloEphemeris(view, p) {
|
|
|
8110
8214
|
tauN: -view.getFloat64(p + 100, true),
|
|
8111
8215
|
// RINEX stores −τ_n
|
|
8112
8216
|
gammaN: view.getFloat64(p + 116, true),
|
|
8113
|
-
messageFrameTime: (tofUtcSec %
|
|
8217
|
+
messageFrameTime: (tofUtcSec % SEC_PER_WEEK9 + SEC_PER_WEEK9) % SEC_PER_WEEK9,
|
|
8114
8218
|
x: view.getFloat64(p + 28, true) / 1e3,
|
|
8115
8219
|
y: view.getFloat64(p + 36, true) / 1e3,
|
|
8116
8220
|
z: view.getFloat64(p + 44, true) / 1e3,
|
|
@@ -8167,7 +8271,7 @@ function decodeGalEphemeris(view, p, headerMs) {
|
|
|
8167
8271
|
omega: view.getFloat64(p + 60, true),
|
|
8168
8272
|
omegaDot: view.getFloat64(p + 140, true),
|
|
8169
8273
|
idot: view.getFloat64(p + 124, true),
|
|
8170
|
-
week: Math.floor((toeMs -
|
|
8274
|
+
week: Math.floor((toeMs - GPS_EPOCH_MS11) / MS_PER_WEEK7),
|
|
8171
8275
|
// RINEX Galileo SVH bit layout (E1B DVS/HS in bits 0-2, E5a in
|
|
8172
8276
|
// 3-5, E5b in 6-8) — same packing as RTKLIB.
|
|
8173
8277
|
svHealth: svhE5b << 7 | dvsE5b << 6 | svhE5a << 4 | dvsE5a << 3 | svhE1b << 1 | dvsE1b,
|
|
@@ -8339,7 +8443,7 @@ function parseNovatelNav(data) {
|
|
|
8339
8443
|
}
|
|
8340
8444
|
|
|
8341
8445
|
// src/novatel/index.ts
|
|
8342
|
-
var
|
|
8446
|
+
var GPS_EPOCH_MS12 = Date.UTC(1980, 0, 6);
|
|
8343
8447
|
var MS_PER_WEEK8 = 7 * 864e5;
|
|
8344
8448
|
var HDR = 28;
|
|
8345
8449
|
var ID_RANGE = 43;
|
|
@@ -8509,7 +8613,7 @@ function parseNovatelRange(data) {
|
|
|
8509
8613
|
if (!codes.includes(code)) codes.push(code);
|
|
8510
8614
|
}
|
|
8511
8615
|
epochs.push({
|
|
8512
|
-
timeMs:
|
|
8616
|
+
timeMs: GPS_EPOCH_MS12 + week * MS_PER_WEEK8 + towMs,
|
|
8513
8617
|
meas
|
|
8514
8618
|
});
|
|
8515
8619
|
}
|
|
@@ -8561,7 +8665,7 @@ function parseNovatelRange(data) {
|
|
|
8561
8665
|
if (!codes.includes(code)) codes.push(code);
|
|
8562
8666
|
}
|
|
8563
8667
|
epochs.push({
|
|
8564
|
-
timeMs:
|
|
8668
|
+
timeMs: GPS_EPOCH_MS12 + week * MS_PER_WEEK8 + towMs,
|
|
8565
8669
|
meas
|
|
8566
8670
|
});
|
|
8567
8671
|
}
|
|
@@ -8575,7 +8679,7 @@ function parseNovatelRange(data) {
|
|
|
8575
8679
|
var GM_GPS = 3986005e8;
|
|
8576
8680
|
var GM_GAL = 3986004418e5;
|
|
8577
8681
|
var GM_BDS = 3986004418e5;
|
|
8578
|
-
var
|
|
8682
|
+
var GPS_EPOCH_MS13 = Date.UTC(1980, 0, 6);
|
|
8579
8683
|
var BDT_EPOCH_MS4 = Date.UTC(2006, 0, 1);
|
|
8580
8684
|
var WEEK_MS = 6048e5;
|
|
8581
8685
|
var BDT_MINUS_GPST_MS = 14e3;
|
|
@@ -8584,7 +8688,7 @@ function almanacEpochMs(alm) {
|
|
|
8584
8688
|
if (alm.system === "C") {
|
|
8585
8689
|
return BDT_EPOCH_MS4 + alm.weekAlm * WEEK_MS + alm.toaSec * 1e3 + BDT_MINUS_GPST_MS;
|
|
8586
8690
|
}
|
|
8587
|
-
return
|
|
8691
|
+
return GPS_EPOCH_MS13 + alm.weekAlm * WEEK_MS + alm.toaSec * 1e3;
|
|
8588
8692
|
}
|
|
8589
8693
|
function almanacSatPosition(alm, timeMs) {
|
|
8590
8694
|
const GM = alm.system === "E" ? GM_GAL : alm.system === "C" ? GM_BDS : GM_GPS;
|
|
@@ -8750,8 +8854,7 @@ var GM_GPS2 = 3986005e8;
|
|
|
8750
8854
|
var GM_GAL2 = 3986004418e5;
|
|
8751
8855
|
var GM_BDS2 = 3986004418e5;
|
|
8752
8856
|
var GM_GLO = 39860044e7;
|
|
8753
|
-
var
|
|
8754
|
-
var BDS_GEO_MIN_SEMIMAJOR_AXIS_M = 4e7;
|
|
8857
|
+
var BDS_GEO_PRNS2 = /* @__PURE__ */ new Set([1, 2, 3, 4, 5, 59, 60, 61, 62, 63]);
|
|
8755
8858
|
var AE_GLO = 6378136;
|
|
8756
8859
|
var J2_GLO = 108263e-8;
|
|
8757
8860
|
var TWO_PI = 2 * Math.PI;
|
|
@@ -8798,7 +8901,7 @@ function keplerPosition(eph, t) {
|
|
|
8798
8901
|
const yp = rk * Math.sin(uk);
|
|
8799
8902
|
const xpDot = drkDot * Math.cos(uk) - rk * ukDot * Math.sin(uk);
|
|
8800
8903
|
const ypDot = drkDot * Math.sin(uk) + rk * ukDot * Math.cos(uk);
|
|
8801
|
-
const isBdsGeo = eph.system === "C" &&
|
|
8904
|
+
const isBdsGeo = eph.system === "C" && BDS_GEO_PRNS2.has(Number(eph.prn.slice(1)));
|
|
8802
8905
|
if (isBdsGeo) {
|
|
8803
8906
|
const omegak2 = eph.omega0 + eph.omegaDot * tk - OMEGA_E * eph.toe;
|
|
8804
8907
|
const cosO2 = Math.cos(omegak2);
|
|
@@ -9094,10 +9197,10 @@ function selectBest(ephs, timeMs) {
|
|
|
9094
9197
|
}
|
|
9095
9198
|
return best;
|
|
9096
9199
|
}
|
|
9097
|
-
var
|
|
9200
|
+
var GPS_EPOCH_MS14 = START_GPS_TIME.getTime();
|
|
9098
9201
|
var MS_PER_WEEK9 = 7 * 864e5;
|
|
9099
9202
|
var BDS_EPOCH_MS = START_BDS_TIME.getTime();
|
|
9100
|
-
var GAL_EPOCH_MS =
|
|
9203
|
+
var GAL_EPOCH_MS = GPS_EPOCH_MS14 + 1024 * MS_PER_WEEK9;
|
|
9101
9204
|
function ephInfoToEphemeris(info) {
|
|
9102
9205
|
const sys = info.prn.charAt(0);
|
|
9103
9206
|
if (sys === "R") {
|
|
@@ -9148,8 +9251,8 @@ function ephInfoToEphemeris(info) {
|
|
|
9148
9251
|
} else if (sys === "E") {
|
|
9149
9252
|
epochMs = GAL_EPOCH_MS;
|
|
9150
9253
|
} else {
|
|
9151
|
-
epochMs =
|
|
9152
|
-
const weeksNow = (info.lastReceived -
|
|
9254
|
+
epochMs = GPS_EPOCH_MS14;
|
|
9255
|
+
const weeksNow = (info.lastReceived - GPS_EPOCH_MS14) / MS_PER_WEEK9;
|
|
9153
9256
|
week += 1024 * Math.round((weeksNow - week) / 1024);
|
|
9154
9257
|
}
|
|
9155
9258
|
const tocDate = new Date(epochMs + week * MS_PER_WEEK9 + tocSec * 1e3);
|
|
@@ -9577,6 +9680,706 @@ function klobucharDelay(coeffs, latRad, lonRad, azRad, elRad, gpsTowSec) {
|
|
|
9577
9680
|
return F * (5e-9 + amp * (1 - x2 / 2 + x2 * x2 / 24));
|
|
9578
9681
|
}
|
|
9579
9682
|
|
|
9683
|
+
// src/positioning/rtk.ts
|
|
9684
|
+
var L1_CODES = {
|
|
9685
|
+
G: ["1C"],
|
|
9686
|
+
E: ["1C"],
|
|
9687
|
+
R: ["1C"],
|
|
9688
|
+
J: ["1C"],
|
|
9689
|
+
C: ["2I", "1P"]
|
|
9690
|
+
// B1I first (classic B1), then B1C
|
|
9691
|
+
};
|
|
9692
|
+
function toRtkEpoch(meas) {
|
|
9693
|
+
const out = /* @__PURE__ */ new Map();
|
|
9694
|
+
const rank = /* @__PURE__ */ new Map();
|
|
9695
|
+
for (const m of meas) {
|
|
9696
|
+
const prefs = L1_CODES[m.prn[0]];
|
|
9697
|
+
if (!prefs || m.pr === null || !Number.isFinite(m.pr)) continue;
|
|
9698
|
+
const r = prefs.indexOf(m.code);
|
|
9699
|
+
if (r < 0) continue;
|
|
9700
|
+
const prev = rank.get(m.prn);
|
|
9701
|
+
if (prev !== void 0 && prev <= r) continue;
|
|
9702
|
+
rank.set(m.prn, r);
|
|
9703
|
+
out.set(m.prn, {
|
|
9704
|
+
code: m.code,
|
|
9705
|
+
pr: m.pr,
|
|
9706
|
+
cp: m.cp ?? null,
|
|
9707
|
+
lockTimeMs: m.lockTimeS !== void 0 ? Math.round(m.lockTimeS * 1e3) : void 0,
|
|
9708
|
+
gloChannel: m.gloChannel ?? null
|
|
9709
|
+
});
|
|
9710
|
+
}
|
|
9711
|
+
return out;
|
|
9712
|
+
}
|
|
9713
|
+
function carrierFreqHz(sys, code, gloChannel) {
|
|
9714
|
+
const band = code[0];
|
|
9715
|
+
switch (sys) {
|
|
9716
|
+
case "G":
|
|
9717
|
+
case "J":
|
|
9718
|
+
return band === "1" ? 157542e4 : band === "2" ? 12276e5 : 117645e4;
|
|
9719
|
+
case "E":
|
|
9720
|
+
return band === "1" ? 157542e4 : band === "5" ? 117645e4 : band === "7" ? 120714e4 : band === "8" ? 1191795e3 : 127875e4;
|
|
9721
|
+
case "C":
|
|
9722
|
+
return band === "1" ? 157542e4 : band === "2" ? 1561098e3 : band === "5" ? 117645e4 : band === "7" ? 120714e4 : 126852e4;
|
|
9723
|
+
case "R": {
|
|
9724
|
+
if (gloChannel === null) return 0;
|
|
9725
|
+
if (band === "1") return 1602e6 + gloChannel * 562500;
|
|
9726
|
+
if (band === "2") return 1246e6 + gloChannel * 437500;
|
|
9727
|
+
return 1202025e3;
|
|
9728
|
+
}
|
|
9729
|
+
default:
|
|
9730
|
+
return 0;
|
|
9731
|
+
}
|
|
9732
|
+
}
|
|
9733
|
+
function sagnac(pos, travelTimeS) {
|
|
9734
|
+
const theta = OMEGA_E * travelTimeS;
|
|
9735
|
+
const c = Math.cos(theta);
|
|
9736
|
+
const s = Math.sin(theta);
|
|
9737
|
+
return [pos.x * c + pos.y * s, -pos.x * s + pos.y * c, pos.z];
|
|
9738
|
+
}
|
|
9739
|
+
function tropoDelay(elevationRad) {
|
|
9740
|
+
const sinEl = Math.sin(elevationRad);
|
|
9741
|
+
return 2.47 / (sinEl + 0.0121);
|
|
9742
|
+
}
|
|
9743
|
+
function resolveEphemeris(src, prn, timeMs) {
|
|
9744
|
+
if (Array.isArray(src))
|
|
9745
|
+
return selectEphemeris(src, prn, timeMs);
|
|
9746
|
+
return src.get(prn) ?? null;
|
|
9747
|
+
}
|
|
9748
|
+
function buildGeometry(rover, base, basePos, ephemerides, timeMs, maskRad, troposphere) {
|
|
9749
|
+
const out = [];
|
|
9750
|
+
for (const [prn, mR] of rover) {
|
|
9751
|
+
const sys = prn[0];
|
|
9752
|
+
if (!"GERCJ".includes(sys)) continue;
|
|
9753
|
+
const mB = base.get(prn);
|
|
9754
|
+
if (!mB || mB.code !== mR.code) continue;
|
|
9755
|
+
if (mR.pr === null || mB.pr === null || !Number.isFinite(mR.pr) || !Number.isFinite(mB.pr))
|
|
9756
|
+
continue;
|
|
9757
|
+
const eph = resolveEphemeris(ephemerides, prn, timeMs);
|
|
9758
|
+
if (!eph) continue;
|
|
9759
|
+
const satAt = (pr) => {
|
|
9760
|
+
const tTx = timeMs - pr / C_LIGHT * 1e3;
|
|
9761
|
+
const dts = satClockCorrection(eph, tTx);
|
|
9762
|
+
const sat = computeSatPosition(eph, tTx - dts * 1e3);
|
|
9763
|
+
return Number.isFinite(sat.x) ? sat : null;
|
|
9764
|
+
};
|
|
9765
|
+
const satB = satAt(mB.pr);
|
|
9766
|
+
const satR = satAt(mR.pr);
|
|
9767
|
+
if (!satB || !satR) continue;
|
|
9768
|
+
const travelB = Math.hypot(
|
|
9769
|
+
satB.x - basePos[0],
|
|
9770
|
+
satB.y - basePos[1],
|
|
9771
|
+
satB.z - basePos[2]
|
|
9772
|
+
) / C_LIGHT;
|
|
9773
|
+
const [bx, by, bz] = sagnac(satB, travelB);
|
|
9774
|
+
const rhoB = Math.hypot(bx - basePos[0], by - basePos[1], bz - basePos[2]);
|
|
9775
|
+
const elB = ecefToAzEl(basePos[0], basePos[1], basePos[2], bx, by, bz).el;
|
|
9776
|
+
if (elB < maskRad) continue;
|
|
9777
|
+
const gloK = sys === "R" ? mR.gloChannel ?? mB.gloChannel ?? eph.freqNum ?? null : null;
|
|
9778
|
+
const freq = carrierFreqHz(sys, mR.code, gloK);
|
|
9779
|
+
out.push({
|
|
9780
|
+
prn,
|
|
9781
|
+
group: sys + mR.code,
|
|
9782
|
+
lambda: freq > 0 ? C_LIGHT / freq : 0,
|
|
9783
|
+
satR,
|
|
9784
|
+
rhoB,
|
|
9785
|
+
elB,
|
|
9786
|
+
tropoB: troposphere ? tropoDelay(elB) : 0,
|
|
9787
|
+
prR: mR.pr,
|
|
9788
|
+
prB: mB.pr,
|
|
9789
|
+
cpR: mR.cp ?? null,
|
|
9790
|
+
cpB: mB.cp ?? null,
|
|
9791
|
+
lockR: mR.lockTimeMs,
|
|
9792
|
+
lockB: mB.lockTimeMs
|
|
9793
|
+
});
|
|
9794
|
+
}
|
|
9795
|
+
return out;
|
|
9796
|
+
}
|
|
9797
|
+
function roverTerms(g, x, y, z, troposphere) {
|
|
9798
|
+
const travel = Math.hypot(g.satR.x - x, g.satR.y - y, g.satR.z - z) / C_LIGHT;
|
|
9799
|
+
const [sx, sy, sz] = sagnac(g.satR, travel);
|
|
9800
|
+
const rho = Math.hypot(sx - x, sy - y, sz - z);
|
|
9801
|
+
const u = [
|
|
9802
|
+
(x - sx) / rho,
|
|
9803
|
+
(y - sy) / rho,
|
|
9804
|
+
(z - sz) / rho
|
|
9805
|
+
];
|
|
9806
|
+
let dTropo = 0;
|
|
9807
|
+
if (troposphere) {
|
|
9808
|
+
const elR = ecefToAzEl(x, y, z, sx, sy, sz).el;
|
|
9809
|
+
dTropo = tropoDelay(Math.max(elR, 0.05)) - g.tropoB;
|
|
9810
|
+
}
|
|
9811
|
+
return { rho, u, dTropo };
|
|
9812
|
+
}
|
|
9813
|
+
function sdVariance(sigmaM, elRad) {
|
|
9814
|
+
const sinEl = Math.max(Math.sin(elRad), 0.05);
|
|
9815
|
+
return 2 * sigmaM * sigmaM * (1 + 1 / (sinEl * sinEl));
|
|
9816
|
+
}
|
|
9817
|
+
function zeros(n, m) {
|
|
9818
|
+
return Array.from({ length: n }, () => new Array(m).fill(0));
|
|
9819
|
+
}
|
|
9820
|
+
function matInv(A) {
|
|
9821
|
+
const n = A.length;
|
|
9822
|
+
const M = A.map((row, i) => {
|
|
9823
|
+
const r = [...row, ...new Array(n).fill(0)];
|
|
9824
|
+
r[n + i] = 1;
|
|
9825
|
+
return r;
|
|
9826
|
+
});
|
|
9827
|
+
for (let col = 0; col < n; col++) {
|
|
9828
|
+
let pivot = col;
|
|
9829
|
+
for (let r = col + 1; r < n; r++) {
|
|
9830
|
+
if (Math.abs(M[r][col]) > Math.abs(M[pivot][col])) pivot = r;
|
|
9831
|
+
}
|
|
9832
|
+
if (Math.abs(M[pivot][col]) < 1e-13) return null;
|
|
9833
|
+
[M[col], M[pivot]] = [M[pivot], M[col]];
|
|
9834
|
+
const d = M[col][col];
|
|
9835
|
+
for (let c = 0; c < 2 * n; c++) M[col][c] /= d;
|
|
9836
|
+
for (let r = 0; r < n; r++) {
|
|
9837
|
+
if (r === col) continue;
|
|
9838
|
+
const f = M[r][col];
|
|
9839
|
+
if (f === 0) continue;
|
|
9840
|
+
for (let c = 0; c < 2 * n; c++) M[r][c] -= f * M[col][c];
|
|
9841
|
+
}
|
|
9842
|
+
}
|
|
9843
|
+
return M.map((row) => row.slice(n));
|
|
9844
|
+
}
|
|
9845
|
+
function matMul(A, B) {
|
|
9846
|
+
const n = A.length;
|
|
9847
|
+
const k = B.length;
|
|
9848
|
+
const m = B[0]?.length ?? 0;
|
|
9849
|
+
const C = zeros(n, m);
|
|
9850
|
+
for (let i = 0; i < n; i++) {
|
|
9851
|
+
for (let l = 0; l < k; l++) {
|
|
9852
|
+
const a = A[i][l];
|
|
9853
|
+
if (a === 0) continue;
|
|
9854
|
+
for (let j = 0; j < m; j++) C[i][j] += a * B[l][j];
|
|
9855
|
+
}
|
|
9856
|
+
}
|
|
9857
|
+
return C;
|
|
9858
|
+
}
|
|
9859
|
+
function transpose(A) {
|
|
9860
|
+
const n = A.length;
|
|
9861
|
+
const m = A[0]?.length ?? 0;
|
|
9862
|
+
const T = zeros(m, n);
|
|
9863
|
+
for (let i = 0; i < n; i++) for (let j = 0; j < m; j++) T[j][i] = A[i][j];
|
|
9864
|
+
return T;
|
|
9865
|
+
}
|
|
9866
|
+
function groupEntries(geom) {
|
|
9867
|
+
const groups = /* @__PURE__ */ new Map();
|
|
9868
|
+
for (const g of geom) {
|
|
9869
|
+
const list = groups.get(g.group);
|
|
9870
|
+
if (list) list.push(g);
|
|
9871
|
+
else groups.set(g.group, [g]);
|
|
9872
|
+
}
|
|
9873
|
+
return groups;
|
|
9874
|
+
}
|
|
9875
|
+
function ddCovariance(rows, sigmaCode, sigmaPhase) {
|
|
9876
|
+
const n = rows.length;
|
|
9877
|
+
const R = zeros(n, n);
|
|
9878
|
+
const sd2 = (r, g) => sdVariance(r.kind === "code" ? sigmaCode : sigmaPhase, g.elB);
|
|
9879
|
+
for (let i = 0; i < n; i++) {
|
|
9880
|
+
const ri = rows[i];
|
|
9881
|
+
R[i][i] = sd2(ri, ri.g) + sd2(ri, ri.ref);
|
|
9882
|
+
for (let j = i + 1; j < n; j++) {
|
|
9883
|
+
const rj = rows[j];
|
|
9884
|
+
if (ri.ref.prn === rj.ref.prn && ri.kind === rj.kind && ri.g.group === rj.g.group) {
|
|
9885
|
+
R[i][j] = R[j][i] = sd2(ri, ri.ref);
|
|
9886
|
+
}
|
|
9887
|
+
}
|
|
9888
|
+
}
|
|
9889
|
+
return R;
|
|
9890
|
+
}
|
|
9891
|
+
function solveDgnss(rover, base, basePos, ephemerides, timeMs, opts = {}) {
|
|
9892
|
+
const {
|
|
9893
|
+
elevationMaskDeg = 10,
|
|
9894
|
+
troposphere = true,
|
|
9895
|
+
maxIterations = 15,
|
|
9896
|
+
convergenceM = 1e-4,
|
|
9897
|
+
codeSigmaM = 0.3,
|
|
9898
|
+
rejectThresholdM = 20,
|
|
9899
|
+
initialPosition
|
|
9900
|
+
} = opts;
|
|
9901
|
+
let geom = buildGeometry(
|
|
9902
|
+
rover,
|
|
9903
|
+
base,
|
|
9904
|
+
basePos,
|
|
9905
|
+
ephemerides,
|
|
9906
|
+
timeMs,
|
|
9907
|
+
elevationMaskDeg * Math.PI / 180,
|
|
9908
|
+
troposphere
|
|
9909
|
+
);
|
|
9910
|
+
const rejected = [];
|
|
9911
|
+
for (; ; ) {
|
|
9912
|
+
const groups = groupEntries(geom);
|
|
9913
|
+
const rows = [];
|
|
9914
|
+
const refSatellites = {};
|
|
9915
|
+
for (const [group, list] of groups) {
|
|
9916
|
+
if (list.length < 2) continue;
|
|
9917
|
+
const ref = list.reduce((a, b) => b.elB > a.elB ? b : a);
|
|
9918
|
+
refSatellites[group] = ref.prn;
|
|
9919
|
+
for (const g of list) {
|
|
9920
|
+
if (g === ref) continue;
|
|
9921
|
+
rows.push({
|
|
9922
|
+
g,
|
|
9923
|
+
ref,
|
|
9924
|
+
kind: "code",
|
|
9925
|
+
z: g.prR - g.prB - (ref.prR - ref.prB)
|
|
9926
|
+
});
|
|
9927
|
+
}
|
|
9928
|
+
}
|
|
9929
|
+
if (rows.length < 3) return null;
|
|
9930
|
+
const W = matInv(ddCovariance(rows, codeSigmaM, codeSigmaM));
|
|
9931
|
+
if (!W) return null;
|
|
9932
|
+
let [x, y, z] = initialPosition ?? basePos;
|
|
9933
|
+
let iterations = 0;
|
|
9934
|
+
let converged = false;
|
|
9935
|
+
const residuals = {};
|
|
9936
|
+
let cov = null;
|
|
9937
|
+
for (let iter = 0; iter < maxIterations; iter++) {
|
|
9938
|
+
iterations = iter + 1;
|
|
9939
|
+
const H2 = [];
|
|
9940
|
+
const v = [];
|
|
9941
|
+
const terms = /* @__PURE__ */ new Map();
|
|
9942
|
+
const termOf = (g) => {
|
|
9943
|
+
let t = terms.get(g.prn);
|
|
9944
|
+
if (!t) {
|
|
9945
|
+
t = roverTerms(g, x, y, z, troposphere);
|
|
9946
|
+
terms.set(g.prn, t);
|
|
9947
|
+
}
|
|
9948
|
+
return t;
|
|
9949
|
+
};
|
|
9950
|
+
for (const row of rows) {
|
|
9951
|
+
const ts = termOf(row.g);
|
|
9952
|
+
const tr = termOf(row.ref);
|
|
9953
|
+
const pred = ts.rho - row.g.rhoB + ts.dTropo - (tr.rho - row.ref.rhoB + tr.dTropo);
|
|
9954
|
+
v.push(row.z - pred);
|
|
9955
|
+
H2.push([ts.u[0] - tr.u[0], ts.u[1] - tr.u[1], ts.u[2] - tr.u[2]]);
|
|
9956
|
+
}
|
|
9957
|
+
const Ht = transpose(H2);
|
|
9958
|
+
const HtW = matMul(Ht, W);
|
|
9959
|
+
const N = matMul(HtW, H2);
|
|
9960
|
+
const b = matMul(
|
|
9961
|
+
HtW,
|
|
9962
|
+
v.map((s) => [s])
|
|
9963
|
+
);
|
|
9964
|
+
const Ninv = matInv(N);
|
|
9965
|
+
if (!Ninv) return null;
|
|
9966
|
+
const dx = matMul(
|
|
9967
|
+
Ninv,
|
|
9968
|
+
b.map((r) => [r[0]])
|
|
9969
|
+
);
|
|
9970
|
+
x += dx[0][0];
|
|
9971
|
+
y += dx[1][0];
|
|
9972
|
+
z += dx[2][0];
|
|
9973
|
+
cov = Ninv;
|
|
9974
|
+
rows.forEach((row, i) => {
|
|
9975
|
+
residuals[row.g.prn] = v[i] - (H2[i][0] * dx[0][0] + H2[i][1] * dx[1][0] + H2[i][2] * dx[2][0]);
|
|
9976
|
+
});
|
|
9977
|
+
if (Math.hypot(dx[0][0], dx[1][0], dx[2][0]) < convergenceM) {
|
|
9978
|
+
converged = true;
|
|
9979
|
+
break;
|
|
9980
|
+
}
|
|
9981
|
+
}
|
|
9982
|
+
let worst = null;
|
|
9983
|
+
let worstAbs = rejectThresholdM;
|
|
9984
|
+
for (const [prn, r] of Object.entries(residuals)) {
|
|
9985
|
+
if (Math.abs(r) > worstAbs) {
|
|
9986
|
+
worstAbs = Math.abs(r);
|
|
9987
|
+
worst = prn;
|
|
9988
|
+
}
|
|
9989
|
+
}
|
|
9990
|
+
if (worst && geom.length > 4) {
|
|
9991
|
+
rejected.push(worst);
|
|
9992
|
+
geom = geom.filter((g) => g.prn !== worst);
|
|
9993
|
+
continue;
|
|
9994
|
+
}
|
|
9995
|
+
const usedSatellites = [
|
|
9996
|
+
...new Set(rows.flatMap((r) => [r.g.prn, r.ref.prn]))
|
|
9997
|
+
].sort();
|
|
9998
|
+
return {
|
|
9999
|
+
position: [x, y, z],
|
|
10000
|
+
baseline: [x - basePos[0], y - basePos[1], z - basePos[2]],
|
|
10001
|
+
sigmas: cov ? [
|
|
10002
|
+
Math.sqrt(Math.max(cov[0][0], 0)),
|
|
10003
|
+
Math.sqrt(Math.max(cov[1][1], 0)),
|
|
10004
|
+
Math.sqrt(Math.max(cov[2][2], 0))
|
|
10005
|
+
] : [0, 0, 0],
|
|
10006
|
+
usedSatellites,
|
|
10007
|
+
refSatellites,
|
|
10008
|
+
residuals,
|
|
10009
|
+
rejectedSatellites: rejected,
|
|
10010
|
+
nSats: usedSatellites.length,
|
|
10011
|
+
iterations,
|
|
10012
|
+
converged
|
|
10013
|
+
};
|
|
10014
|
+
}
|
|
10015
|
+
}
|
|
10016
|
+
var RtkFloatEngine = class {
|
|
10017
|
+
basePos;
|
|
10018
|
+
ephemerides;
|
|
10019
|
+
o;
|
|
10020
|
+
/** State vector [x, y, z, N₁…] and covariance; null before init. */
|
|
10021
|
+
x = null;
|
|
10022
|
+
P = [];
|
|
10023
|
+
amb = [];
|
|
10024
|
+
refs = /* @__PURE__ */ new Map();
|
|
10025
|
+
track = /* @__PURE__ */ new Map();
|
|
10026
|
+
lastMs = null;
|
|
10027
|
+
constructor(basePos, ephemerides, opts = {}) {
|
|
10028
|
+
this.basePos = basePos;
|
|
10029
|
+
this.ephemerides = ephemerides;
|
|
10030
|
+
this.o = {
|
|
10031
|
+
mode: opts.mode ?? "kinematic",
|
|
10032
|
+
elevationMaskDeg: opts.elevationMaskDeg ?? 10,
|
|
10033
|
+
troposphere: opts.troposphere ?? true,
|
|
10034
|
+
codeSigmaM: opts.codeSigmaM ?? 0.3,
|
|
10035
|
+
phaseSigmaM: opts.phaseSigmaM ?? 3e-3,
|
|
10036
|
+
processNoisePosM: opts.processNoisePosM ?? 0,
|
|
10037
|
+
kinematicSigmaM: opts.kinematicSigmaM ?? 30,
|
|
10038
|
+
ambProcessNoiseCycles: opts.ambProcessNoiseCycles ?? 1e-4,
|
|
10039
|
+
ambInitSigmaCycles: opts.ambInitSigmaCycles ?? 30,
|
|
10040
|
+
maxGapMs: opts.maxGapMs ?? 1e4,
|
|
10041
|
+
codeGateM: opts.codeGateM ?? 30,
|
|
10042
|
+
slipGateM: opts.slipGateM ?? 5,
|
|
10043
|
+
updateIterations: opts.updateIterations ?? 2
|
|
10044
|
+
};
|
|
10045
|
+
}
|
|
10046
|
+
/** Clear all filter state (position, ambiguities, lock history). */
|
|
10047
|
+
reset() {
|
|
10048
|
+
this.x = null;
|
|
10049
|
+
this.P = [];
|
|
10050
|
+
this.amb = [];
|
|
10051
|
+
this.refs.clear();
|
|
10052
|
+
this.track.clear();
|
|
10053
|
+
this.lastMs = null;
|
|
10054
|
+
}
|
|
10055
|
+
ambIndex(prn) {
|
|
10056
|
+
return this.amb.findIndex((a) => a.prn === prn);
|
|
10057
|
+
}
|
|
10058
|
+
/** Remove one ambiguity state (row/col) from x and P. */
|
|
10059
|
+
dropAmb(idx) {
|
|
10060
|
+
const s = 3 + idx;
|
|
10061
|
+
this.amb.splice(idx, 1);
|
|
10062
|
+
this.x.splice(s, 1);
|
|
10063
|
+
this.P.splice(s, 1);
|
|
10064
|
+
for (const row of this.P) row.splice(s, 1);
|
|
10065
|
+
}
|
|
10066
|
+
/** Append an ambiguity state with the given value and variance. */
|
|
10067
|
+
addAmb(entry, value, variance) {
|
|
10068
|
+
this.amb.push(entry);
|
|
10069
|
+
this.x.push(value);
|
|
10070
|
+
const n = this.x.length;
|
|
10071
|
+
for (const row of this.P) row.push(0);
|
|
10072
|
+
const newRow = new Array(n).fill(0);
|
|
10073
|
+
newRow[n - 1] = variance;
|
|
10074
|
+
this.P.push(newRow);
|
|
10075
|
+
}
|
|
10076
|
+
/**
|
|
10077
|
+
* Re-map a group's DD ambiguities from the old reference r to the
|
|
10078
|
+
* new reference r' (which must hold a state): N_i' = N_i − N_r' and
|
|
10079
|
+
* the old reference's slot becomes N_r = −N_r'. Exact linear
|
|
10080
|
+
* transform of state and covariance (P' = T P Tᵀ).
|
|
10081
|
+
*/
|
|
10082
|
+
retarget(group, oldRef, newRefIdx) {
|
|
10083
|
+
const n = this.x.length;
|
|
10084
|
+
const j = 3 + newRefIdx;
|
|
10085
|
+
const T = zeros(n, n);
|
|
10086
|
+
for (let i = 0; i < n; i++) T[i][i] = 1;
|
|
10087
|
+
for (let k = 0; k < this.amb.length; k++) {
|
|
10088
|
+
if (this.amb[k].group !== group) continue;
|
|
10089
|
+
const s = 3 + k;
|
|
10090
|
+
if (s === j) T[s][s] = -1;
|
|
10091
|
+
else T[s][j] -= 1;
|
|
10092
|
+
}
|
|
10093
|
+
this.x = matMul(
|
|
10094
|
+
T,
|
|
10095
|
+
this.x.map((v) => [v])
|
|
10096
|
+
).map((r) => r[0]);
|
|
10097
|
+
this.P = matMul(matMul(T, this.P), transpose(T));
|
|
10098
|
+
const entry = this.amb[newRefIdx];
|
|
10099
|
+
entry.prn = oldRef;
|
|
10100
|
+
}
|
|
10101
|
+
/**
|
|
10102
|
+
* Process one synchronized epoch (same nominal `timeMs` for both
|
|
10103
|
+
* receivers). Returns null until a first position can be
|
|
10104
|
+
* initialised (via an internal DGNSS solve) or when the epoch has
|
|
10105
|
+
* fewer than 3 usable DD rows.
|
|
10106
|
+
*/
|
|
10107
|
+
process(rover, base, timeMs) {
|
|
10108
|
+
const o = this.o;
|
|
10109
|
+
const geom = buildGeometry(
|
|
10110
|
+
rover,
|
|
10111
|
+
base,
|
|
10112
|
+
this.basePos,
|
|
10113
|
+
this.ephemerides,
|
|
10114
|
+
timeMs,
|
|
10115
|
+
o.elevationMaskDeg * Math.PI / 180,
|
|
10116
|
+
o.troposphere
|
|
10117
|
+
);
|
|
10118
|
+
const byPrn = new Map(geom.map((g) => [g.prn, g]));
|
|
10119
|
+
const dtS = this.lastMs !== null ? (timeMs - this.lastMs) / 1e3 : 0;
|
|
10120
|
+
if (this.x) {
|
|
10121
|
+
for (let i = this.amb.length - 1; i >= 0; i--) {
|
|
10122
|
+
const prn = this.amb[i].prn;
|
|
10123
|
+
const tr = this.track.get(prn);
|
|
10124
|
+
const seen = byPrn.get(prn);
|
|
10125
|
+
const hasPhase = seen && seen.cpR !== null && seen.cpB !== null;
|
|
10126
|
+
const staleMs = tr ? timeMs - tr.lastMs : Infinity;
|
|
10127
|
+
if (!hasPhase && staleMs > o.maxGapMs) this.dropAmb(i);
|
|
10128
|
+
}
|
|
10129
|
+
}
|
|
10130
|
+
const slipped = /* @__PURE__ */ new Set();
|
|
10131
|
+
for (const g of geom) {
|
|
10132
|
+
if (g.cpR === null || g.cpB === null) continue;
|
|
10133
|
+
const tr = this.track.get(g.prn);
|
|
10134
|
+
if (!tr) continue;
|
|
10135
|
+
if (timeMs - tr.lastMs > o.maxGapMs) slipped.add(g.prn);
|
|
10136
|
+
else if (g.lockR !== void 0 && tr.lockR !== void 0 && g.lockR < tr.lockR || g.lockB !== void 0 && tr.lockB !== void 0 && g.lockB < tr.lockB)
|
|
10137
|
+
slipped.add(g.prn);
|
|
10138
|
+
}
|
|
10139
|
+
if (!this.x) {
|
|
10140
|
+
const dg = solveDgnss(
|
|
10141
|
+
rover,
|
|
10142
|
+
base,
|
|
10143
|
+
this.basePos,
|
|
10144
|
+
this.ephemerides,
|
|
10145
|
+
timeMs,
|
|
10146
|
+
{
|
|
10147
|
+
elevationMaskDeg: o.elevationMaskDeg,
|
|
10148
|
+
troposphere: o.troposphere,
|
|
10149
|
+
codeSigmaM: o.codeSigmaM
|
|
10150
|
+
}
|
|
10151
|
+
);
|
|
10152
|
+
if (!dg) return null;
|
|
10153
|
+
this.x = [...dg.position];
|
|
10154
|
+
this.P = zeros(3, 3);
|
|
10155
|
+
for (let i = 0; i < 3; i++) {
|
|
10156
|
+
const s = Math.max(dg.sigmas[i], 1);
|
|
10157
|
+
this.P[i][i] = 25 * s * s;
|
|
10158
|
+
}
|
|
10159
|
+
this.amb = [];
|
|
10160
|
+
} else if (o.mode === "kinematic") {
|
|
10161
|
+
for (let i = 0; i < 3; i++) {
|
|
10162
|
+
for (let j = 0; j < this.x.length; j++) {
|
|
10163
|
+
this.P[i][j] = 0;
|
|
10164
|
+
this.P[j][i] = 0;
|
|
10165
|
+
}
|
|
10166
|
+
this.P[i][i] = o.kinematicSigmaM * o.kinematicSigmaM;
|
|
10167
|
+
}
|
|
10168
|
+
} else {
|
|
10169
|
+
const q = o.processNoisePosM * o.processNoisePosM * dtS;
|
|
10170
|
+
for (let i = 0; i < 3; i++) this.P[i][i] += q;
|
|
10171
|
+
}
|
|
10172
|
+
const qAmb = o.ambProcessNoiseCycles * o.ambProcessNoiseCycles * dtS;
|
|
10173
|
+
for (let k = 0; k < this.amb.length; k++) this.P[3 + k][3 + k] += qAmb;
|
|
10174
|
+
const groups = groupEntries(geom);
|
|
10175
|
+
const newRefs = /* @__PURE__ */ new Map();
|
|
10176
|
+
for (const [group, list] of groups) {
|
|
10177
|
+
if (list.length < 2) continue;
|
|
10178
|
+
const phase = list.filter(
|
|
10179
|
+
(g) => g.cpR !== null && g.cpB !== null && !slipped.has(g.prn)
|
|
10180
|
+
);
|
|
10181
|
+
const pool = phase.length >= 2 ? phase : list;
|
|
10182
|
+
const prevRef = this.refs.get(group);
|
|
10183
|
+
const best = pool.reduce((a, b) => b.elB > a.elB ? b : a);
|
|
10184
|
+
let ref = best;
|
|
10185
|
+
if (best.prn !== prevRef) {
|
|
10186
|
+
const anyState = this.amb.some((a) => a.group === group);
|
|
10187
|
+
if (anyState) {
|
|
10188
|
+
if (this.ambIndex(best.prn) >= 0) {
|
|
10189
|
+
this.retarget(group, prevRef ?? best.prn, this.ambIndex(best.prn));
|
|
10190
|
+
} else if (prevRef && pool.some((g) => g.prn === prevRef)) {
|
|
10191
|
+
ref = pool.find((g) => g.prn === prevRef);
|
|
10192
|
+
} else {
|
|
10193
|
+
for (let i = this.amb.length - 1; i >= 0; i--)
|
|
10194
|
+
if (this.amb[i].group === group) this.dropAmb(i);
|
|
10195
|
+
}
|
|
10196
|
+
}
|
|
10197
|
+
}
|
|
10198
|
+
newRefs.set(group, ref);
|
|
10199
|
+
this.refs.set(group, ref.prn);
|
|
10200
|
+
}
|
|
10201
|
+
for (const group of [...this.refs.keys()])
|
|
10202
|
+
if (!newRefs.has(group)) this.refs.delete(group);
|
|
10203
|
+
for (const prn of slipped) {
|
|
10204
|
+
const i = this.ambIndex(prn);
|
|
10205
|
+
if (i >= 0) this.dropAmb(i);
|
|
10206
|
+
}
|
|
10207
|
+
const initAmb = (g, ref) => {
|
|
10208
|
+
if (g.lambda <= 0 || ref.lambda <= 0) return;
|
|
10209
|
+
const zPhi = g.lambda * (g.cpR - g.cpB) - ref.lambda * (ref.cpR - ref.cpB);
|
|
10210
|
+
const zP = g.prR - g.prB - (ref.prR - ref.prB);
|
|
10211
|
+
const n0 = (zPhi - zP) / g.lambda;
|
|
10212
|
+
this.addAmb(
|
|
10213
|
+
{ prn: g.prn, group: g.group, lambda: g.lambda },
|
|
10214
|
+
n0,
|
|
10215
|
+
o.ambInitSigmaCycles * o.ambInitSigmaCycles
|
|
10216
|
+
);
|
|
10217
|
+
};
|
|
10218
|
+
for (const [group, ref] of newRefs) {
|
|
10219
|
+
for (const g of groups.get(group)) {
|
|
10220
|
+
if (g === ref || g.cpR === null || g.cpB === null) continue;
|
|
10221
|
+
if (ref.cpR === null || ref.cpB === null) continue;
|
|
10222
|
+
const i = this.ambIndex(g.prn);
|
|
10223
|
+
if (i >= 0) {
|
|
10224
|
+
this.amb[i].lambda = g.lambda;
|
|
10225
|
+
this.amb[i].group = g.group;
|
|
10226
|
+
continue;
|
|
10227
|
+
}
|
|
10228
|
+
initAmb(g, ref);
|
|
10229
|
+
}
|
|
10230
|
+
}
|
|
10231
|
+
for (let i = this.amb.length - 1; i >= 0; i--) {
|
|
10232
|
+
const a = this.amb[i];
|
|
10233
|
+
if (newRefs.get(a.group)?.prn === a.prn) this.dropAmb(i);
|
|
10234
|
+
}
|
|
10235
|
+
let rows = [];
|
|
10236
|
+
for (const [group, ref] of newRefs) {
|
|
10237
|
+
for (const g of groups.get(group)) {
|
|
10238
|
+
if (g === ref) continue;
|
|
10239
|
+
rows.push({
|
|
10240
|
+
g,
|
|
10241
|
+
ref,
|
|
10242
|
+
kind: "code",
|
|
10243
|
+
z: g.prR - g.prB - (ref.prR - ref.prB),
|
|
10244
|
+
ambIdx: -1
|
|
10245
|
+
});
|
|
10246
|
+
const i = this.ambIndex(g.prn);
|
|
10247
|
+
if (i >= 0 && g.cpR !== null && g.cpB !== null && ref.cpR !== null && ref.cpB !== null && g.lambda > 0 && ref.lambda > 0) {
|
|
10248
|
+
rows.push({
|
|
10249
|
+
g,
|
|
10250
|
+
ref,
|
|
10251
|
+
kind: "phase",
|
|
10252
|
+
z: g.lambda * (g.cpR - g.cpB) - ref.lambda * (ref.cpR - ref.cpB),
|
|
10253
|
+
ambIdx: i
|
|
10254
|
+
});
|
|
10255
|
+
}
|
|
10256
|
+
}
|
|
10257
|
+
}
|
|
10258
|
+
if (rows.length < 3) {
|
|
10259
|
+
this.lastMs = timeMs;
|
|
10260
|
+
return null;
|
|
10261
|
+
}
|
|
10262
|
+
const predict = (row, xs) => {
|
|
10263
|
+
const ts = roverTerms(row.g, xs[0], xs[1], xs[2], o.troposphere);
|
|
10264
|
+
const tr = roverTerms(row.ref, xs[0], xs[1], xs[2], o.troposphere);
|
|
10265
|
+
let pred = ts.rho - row.g.rhoB + ts.dTropo - (tr.rho - row.ref.rhoB + tr.dTropo);
|
|
10266
|
+
const h = new Array(this.x.length).fill(0);
|
|
10267
|
+
h[0] = ts.u[0] - tr.u[0];
|
|
10268
|
+
h[1] = ts.u[1] - tr.u[1];
|
|
10269
|
+
h[2] = ts.u[2] - tr.u[2];
|
|
10270
|
+
if (row.ambIdx >= 0) {
|
|
10271
|
+
pred += this.amb[row.ambIdx].lambda * this.x[3 + row.ambIdx];
|
|
10272
|
+
h[3 + row.ambIdx] = this.amb[row.ambIdx].lambda;
|
|
10273
|
+
}
|
|
10274
|
+
return { pred, h };
|
|
10275
|
+
};
|
|
10276
|
+
const dropPrns = /* @__PURE__ */ new Set();
|
|
10277
|
+
const codeInn = /* @__PURE__ */ new Map();
|
|
10278
|
+
for (const row of rows) {
|
|
10279
|
+
if (row.kind !== "code") continue;
|
|
10280
|
+
const v = row.z - predict(row, this.x).pred;
|
|
10281
|
+
codeInn.set(row.g.prn, v);
|
|
10282
|
+
if (Math.abs(v) > o.codeGateM) dropPrns.add(row.g.prn);
|
|
10283
|
+
}
|
|
10284
|
+
for (const row of rows) {
|
|
10285
|
+
if (row.kind !== "phase" || dropPrns.has(row.g.prn)) continue;
|
|
10286
|
+
const v = row.z - predict(row, this.x).pred;
|
|
10287
|
+
const vc = codeInn.get(row.g.prn) ?? 0;
|
|
10288
|
+
if (Math.abs(v - vc) > o.slipGateM) {
|
|
10289
|
+
const zP = row.g.prR - row.g.prB - (row.ref.prR - row.ref.prB);
|
|
10290
|
+
const lam = this.amb[row.ambIdx].lambda;
|
|
10291
|
+
this.x[3 + row.ambIdx] = (row.z - zP) / lam;
|
|
10292
|
+
const s = 3 + row.ambIdx;
|
|
10293
|
+
for (let j = 0; j < this.x.length; j++) {
|
|
10294
|
+
this.P[s][j] = 0;
|
|
10295
|
+
this.P[j][s] = 0;
|
|
10296
|
+
}
|
|
10297
|
+
this.P[s][s] = o.ambInitSigmaCycles * o.ambInitSigmaCycles;
|
|
10298
|
+
}
|
|
10299
|
+
}
|
|
10300
|
+
if (dropPrns.size) rows = rows.filter((r) => !dropPrns.has(r.g.prn));
|
|
10301
|
+
if (rows.length < 3) {
|
|
10302
|
+
this.lastMs = timeMs;
|
|
10303
|
+
return null;
|
|
10304
|
+
}
|
|
10305
|
+
const n = this.x.length;
|
|
10306
|
+
const R = ddCovariance(rows, o.codeSigmaM, o.phaseSigmaM);
|
|
10307
|
+
const xPrior = [...this.x];
|
|
10308
|
+
const PPrior = this.P;
|
|
10309
|
+
let xi = [...this.x];
|
|
10310
|
+
let K = [];
|
|
10311
|
+
let H2 = [];
|
|
10312
|
+
for (let it = 0; it < Math.max(o.updateIterations, 1); it++) {
|
|
10313
|
+
H2 = [];
|
|
10314
|
+
const v = [];
|
|
10315
|
+
for (const row of rows) {
|
|
10316
|
+
const { pred, h } = predict(row, xi);
|
|
10317
|
+
let corr = 0;
|
|
10318
|
+
for (let j = 0; j < n; j++) corr += h[j] * (xPrior[j] - xi[j]);
|
|
10319
|
+
v.push(row.z - pred - corr);
|
|
10320
|
+
H2.push(h);
|
|
10321
|
+
}
|
|
10322
|
+
const Ht = transpose(H2);
|
|
10323
|
+
const S = matMul(matMul(H2, PPrior), Ht);
|
|
10324
|
+
for (let i = 0; i < rows.length; i++)
|
|
10325
|
+
for (let j = 0; j < rows.length; j++) S[i][j] += R[i][j];
|
|
10326
|
+
const Sinv = matInv(S);
|
|
10327
|
+
if (!Sinv) {
|
|
10328
|
+
this.lastMs = timeMs;
|
|
10329
|
+
return null;
|
|
10330
|
+
}
|
|
10331
|
+
K = matMul(matMul(PPrior, Ht), Sinv);
|
|
10332
|
+
const dx = matMul(
|
|
10333
|
+
K,
|
|
10334
|
+
v.map((s) => [s])
|
|
10335
|
+
);
|
|
10336
|
+
xi = xPrior.map((s, i) => s + dx[i][0]);
|
|
10337
|
+
}
|
|
10338
|
+
this.x = xi;
|
|
10339
|
+
const KH = matMul(K, H2);
|
|
10340
|
+
const IKH = zeros(n, n);
|
|
10341
|
+
for (let i = 0; i < n; i++)
|
|
10342
|
+
for (let j = 0; j < n; j++) IKH[i][j] = (i === j ? 1 : 0) - KH[i][j];
|
|
10343
|
+
const Pnew = matMul(IKH, PPrior);
|
|
10344
|
+
for (let i = 0; i < n; i++)
|
|
10345
|
+
for (let j = i; j < n; j++) {
|
|
10346
|
+
const s = (Pnew[i][j] + Pnew[j][i]) / 2;
|
|
10347
|
+
Pnew[i][j] = s;
|
|
10348
|
+
Pnew[j][i] = s;
|
|
10349
|
+
}
|
|
10350
|
+
this.P = Pnew;
|
|
10351
|
+
for (const g of geom) {
|
|
10352
|
+
if (g.cpR === null || g.cpB === null) continue;
|
|
10353
|
+
this.track.set(g.prn, { lockR: g.lockR, lockB: g.lockB, lastMs: timeMs });
|
|
10354
|
+
}
|
|
10355
|
+
this.lastMs = timeMs;
|
|
10356
|
+
const ambiguities = {};
|
|
10357
|
+
for (let k = 0; k < this.amb.length; k++)
|
|
10358
|
+
ambiguities[this.amb[k].prn] = this.x[3 + k];
|
|
10359
|
+
const refSatellites = {};
|
|
10360
|
+
for (const [group, ref] of newRefs) refSatellites[group] = ref.prn;
|
|
10361
|
+
const nSats = new Set(rows.flatMap((r) => [r.g.prn, r.ref.prn])).size;
|
|
10362
|
+
return {
|
|
10363
|
+
timeMs,
|
|
10364
|
+
position: [this.x[0], this.x[1], this.x[2]],
|
|
10365
|
+
floatBaseline: [
|
|
10366
|
+
this.x[0] - this.basePos[0],
|
|
10367
|
+
this.x[1] - this.basePos[1],
|
|
10368
|
+
this.x[2] - this.basePos[2]
|
|
10369
|
+
],
|
|
10370
|
+
nSats,
|
|
10371
|
+
ratio: void 0,
|
|
10372
|
+
sigmas: [
|
|
10373
|
+
Math.sqrt(Math.max(this.P[0][0], 0)),
|
|
10374
|
+
Math.sqrt(Math.max(this.P[1][1], 0)),
|
|
10375
|
+
Math.sqrt(Math.max(this.P[2][2], 0))
|
|
10376
|
+
],
|
|
10377
|
+
ambiguities,
|
|
10378
|
+
refSatellites
|
|
10379
|
+
};
|
|
10380
|
+
}
|
|
10381
|
+
};
|
|
10382
|
+
|
|
9580
10383
|
// src/positioning/index.ts
|
|
9581
10384
|
var GM_GPS3 = 3986005e8;
|
|
9582
10385
|
var F_REL = -4442807633e-19;
|
|
@@ -9613,11 +10416,11 @@ var PRIMARY_FREQ_HZ = {
|
|
|
9613
10416
|
R: 1602e6
|
|
9614
10417
|
};
|
|
9615
10418
|
var F_L1 = 157542e4;
|
|
9616
|
-
function
|
|
10419
|
+
function tropoDelay2(elevationRad) {
|
|
9617
10420
|
const sinEl = Math.sin(elevationRad);
|
|
9618
10421
|
return 2.47 / (sinEl + 0.0121);
|
|
9619
10422
|
}
|
|
9620
|
-
function
|
|
10423
|
+
function sagnac2(pos, travelTimeS) {
|
|
9621
10424
|
const theta = OMEGA_E * travelTimeS;
|
|
9622
10425
|
const c = Math.cos(theta);
|
|
9623
10426
|
const s = Math.sin(theta);
|
|
@@ -9688,7 +10491,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
9688
10491
|
const isKepler3 = eph.system !== "R" && eph.system !== "S";
|
|
9689
10492
|
const dts = dtsClock - (tgd && isKepler3 ? eph.tgd : 0);
|
|
9690
10493
|
const travel = Math.hypot(sat.x - x2, sat.y - y2, sat.z - z2) / C_LIGHT;
|
|
9691
|
-
const [sx, sy, sz] =
|
|
10494
|
+
const [sx, sy, sz] = sagnac2(sat, travel);
|
|
9692
10495
|
const rho = Math.hypot(sx - x2, sy - y2, sz - z2);
|
|
9693
10496
|
const ux = (x2 - sx) / rho;
|
|
9694
10497
|
const uy = (y2 - sy) / rho;
|
|
@@ -9705,7 +10508,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
9705
10508
|
const sinEl = Math.max(Math.sin(elev), 0.1);
|
|
9706
10509
|
weight = sinEl * sinEl;
|
|
9707
10510
|
}
|
|
9708
|
-
const tropo = troposphere && positionSane ?
|
|
10511
|
+
const tropo = troposphere && positionSane ? tropoDelay2(elev) : 0;
|
|
9709
10512
|
let ionoM = 0;
|
|
9710
10513
|
if (iono && positionSane) {
|
|
9711
10514
|
const f = PRIMARY_FREQ_HZ[sys] ?? F_L1;
|
|
@@ -12111,6 +12914,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
12111
12914
|
RINEX_CODES,
|
|
12112
12915
|
RTCM3_MESSAGE_NAMES,
|
|
12113
12916
|
Rtcm3Decoder,
|
|
12917
|
+
RtkFloatEngine,
|
|
12114
12918
|
SECONDS_IN_DAY,
|
|
12115
12919
|
SECONDS_IN_HOUR,
|
|
12116
12920
|
SECONDS_IN_MINUTE,
|
|
@@ -12270,6 +13074,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
12270
13074
|
parseUbxCnav,
|
|
12271
13075
|
parseUbxIonoUtc,
|
|
12272
13076
|
parseUbxNav,
|
|
13077
|
+
parseUbxRawNav,
|
|
12273
13078
|
parseUbxRawx,
|
|
12274
13079
|
phiAltBOC,
|
|
12275
13080
|
phiBOCc,
|
|
@@ -12288,11 +13093,13 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
12288
13093
|
selectEphemeris,
|
|
12289
13094
|
setGloFreqNumber,
|
|
12290
13095
|
setRtcm3DebugHandler,
|
|
13096
|
+
solveDgnss,
|
|
12291
13097
|
solveSpp,
|
|
12292
13098
|
sp3Position,
|
|
12293
13099
|
stationHeaderLines,
|
|
12294
13100
|
systemCmp,
|
|
12295
13101
|
systemName,
|
|
13102
|
+
toRtkEpoch,
|
|
12296
13103
|
transformFrame,
|
|
12297
13104
|
ubxFrames,
|
|
12298
13105
|
updateStationMeta,
|