gnss-js 1.20.0 → 1.22.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-DLXTTHRK.js → chunk-MCAVU3XL.js} +118 -9
- package/dist/{chunk-VJEPOC76.js → chunk-PZNVFCKJ.js} +549 -766
- package/dist/chunk-VTX3VCL4.js +769 -0
- package/dist/index.cjs +1654 -1027
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -2
- package/dist/sbf.cjs +525 -0
- package/dist/sbf.d.cts +173 -1
- package/dist/sbf.d.ts +173 -1
- package/dist/sbf.js +4 -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/index.cjs
CHANGED
|
@@ -231,6 +231,7 @@ __export(src_exports, {
|
|
|
231
231
|
parseSbfCnav: () => parseSbfCnav,
|
|
232
232
|
parseSbfGalNav: () => parseSbfGalNav,
|
|
233
233
|
parseSbfGloNav: () => parseSbfGloNav,
|
|
234
|
+
parseSbfHas: () => parseSbfHas,
|
|
234
235
|
parseSbfIonoUtc: () => parseSbfIonoUtc,
|
|
235
236
|
parseSbfMeas: () => parseSbfMeas,
|
|
236
237
|
parseSbfNav: () => parseSbfNav,
|
|
@@ -240,6 +241,7 @@ __export(src_exports, {
|
|
|
240
241
|
parseUbxCnav: () => parseUbxCnav,
|
|
241
242
|
parseUbxIonoUtc: () => parseUbxIonoUtc,
|
|
242
243
|
parseUbxNav: () => parseUbxNav,
|
|
244
|
+
parseUbxRawNav: () => parseUbxRawNav,
|
|
243
245
|
parseUbxRawx: () => parseUbxRawx,
|
|
244
246
|
phiAltBOC: () => phiAltBOC,
|
|
245
247
|
phiBOCc: () => phiBOCc,
|
|
@@ -5403,541 +5405,288 @@ function parseUbxCnav(data) {
|
|
|
5403
5405
|
return { ephemerides, badCrc, messages };
|
|
5404
5406
|
}
|
|
5405
5407
|
|
|
5406
|
-
// src/
|
|
5407
|
-
var
|
|
5408
|
-
var
|
|
5409
|
-
var
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
// E1C/B, E5bI/Q
|
|
5416
|
-
3: { 0: ["C", "2I"], 1: ["C", "2I"], 2: ["C", "7I"], 3: ["C", "7I"] },
|
|
5417
|
-
// B1I D1/D2, B2I D1/D2
|
|
5418
|
-
5: { 0: ["J", "1C"], 4: ["J", "2X"], 5: ["J", "2X"] },
|
|
5419
|
-
// QZSS L1C/A, L2CM/CL
|
|
5420
|
-
6: { 0: ["R", "1C"], 2: ["R", "2C"] }
|
|
5421
|
-
// GLONASS L1OF, L2OF
|
|
5422
|
-
};
|
|
5423
|
-
function prnFor(gnssId, svId) {
|
|
5424
|
-
const two4 = (n) => String(n).padStart(2, "0");
|
|
5425
|
-
switch (gnssId) {
|
|
5426
|
-
case 0:
|
|
5427
|
-
return svId >= 1 && svId <= 32 ? `G${two4(svId)}` : null;
|
|
5428
|
-
case 1:
|
|
5429
|
-
return svId >= 120 && svId <= 158 ? `S${two4(svId - 100)}` : null;
|
|
5430
|
-
case 2:
|
|
5431
|
-
return svId >= 1 && svId <= 36 ? `E${two4(svId)}` : null;
|
|
5432
|
-
case 3:
|
|
5433
|
-
return svId >= 1 && svId <= 63 ? `C${two4(svId)}` : null;
|
|
5434
|
-
case 5:
|
|
5435
|
-
return svId >= 1 && svId <= 10 ? `J${two4(svId)}` : null;
|
|
5436
|
-
case 6:
|
|
5437
|
-
return svId >= 1 && svId <= 32 ? `R${two4(svId)}` : null;
|
|
5438
|
-
default:
|
|
5439
|
-
return null;
|
|
5440
|
-
}
|
|
5408
|
+
// src/navbits/bds.ts
|
|
5409
|
+
var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
|
|
5410
|
+
var SEC_PER_WEEK4 = 7 * 86400;
|
|
5411
|
+
var MS_PER_WEEK2 = SEC_PER_WEEK4 * 1e3;
|
|
5412
|
+
var HALF_WEEK2 = 302400;
|
|
5413
|
+
var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK4;
|
|
5414
|
+
var BDS_SUBFRAME_BYTES = 38;
|
|
5415
|
+
function getBitU2(b, p1, l1, p2, l2) {
|
|
5416
|
+
return getBitU(b, p1, l1) * 2 ** l2 + getBitU(b, p2, l2);
|
|
5441
5417
|
}
|
|
5442
|
-
function
|
|
5443
|
-
|
|
5444
|
-
const epochs = [];
|
|
5445
|
-
const messageCounts = {};
|
|
5446
|
-
const obsCodes = {};
|
|
5447
|
-
const stats = { badChecksums: 0 };
|
|
5448
|
-
for (const frame of ubxFrames(data, stats)) {
|
|
5449
|
-
const { msgClass: cls, msgId: id } = frame;
|
|
5450
|
-
const len = frame.payload.length;
|
|
5451
|
-
const key = `${cls.toString(16).padStart(2, "0")}-${id.toString(16).padStart(2, "0")}`;
|
|
5452
|
-
messageCounts[key] = (messageCounts[key] ?? 0) + 1;
|
|
5453
|
-
if (cls === 2 && id === 21 && len >= 16) {
|
|
5454
|
-
const p = frame.payloadStart;
|
|
5455
|
-
const rcvTow = view.getFloat64(p, true);
|
|
5456
|
-
const week = view.getUint16(p + 8, true);
|
|
5457
|
-
const leapS = view.getInt8(p + 10);
|
|
5458
|
-
const numMeas = data[p + 11];
|
|
5459
|
-
const recStat = data[p + 12];
|
|
5460
|
-
const leapValid = (recStat & 1) !== 0;
|
|
5461
|
-
const meas = [];
|
|
5462
|
-
for (let k = 0; k < numMeas; k++) {
|
|
5463
|
-
const off = p + 16 + 32 * k;
|
|
5464
|
-
if (off + 32 > p + len) break;
|
|
5465
|
-
const gnssId = data[off + 20];
|
|
5466
|
-
const svId = data[off + 21];
|
|
5467
|
-
const sigId = data[off + 22];
|
|
5468
|
-
const trkStat = data[off + 30];
|
|
5469
|
-
const prn = prnFor(gnssId, svId);
|
|
5470
|
-
const sig = SIGNALS[gnssId]?.[sigId];
|
|
5471
|
-
if (!prn || !sig) continue;
|
|
5472
|
-
const prValid = (trkStat & 1) !== 0;
|
|
5473
|
-
const cpValid = (trkStat & 2) !== 0;
|
|
5474
|
-
const cp = view.getFloat64(off + 8, true);
|
|
5475
|
-
meas.push({
|
|
5476
|
-
prn,
|
|
5477
|
-
code: sig[1],
|
|
5478
|
-
pr: prValid ? view.getFloat64(off, true) : null,
|
|
5479
|
-
cp: cpValid && cp !== 0 ? cp : null,
|
|
5480
|
-
doppler: view.getFloat32(off + 16, true),
|
|
5481
|
-
cn0: data[off + 26],
|
|
5482
|
-
halfCycleAmbiguous: (trkStat & 4) !== 0,
|
|
5483
|
-
lockTimeMs: view.getUint16(off + 24, true)
|
|
5484
|
-
});
|
|
5485
|
-
const sys = sig[0];
|
|
5486
|
-
const codes = obsCodes[sys] ??= [];
|
|
5487
|
-
if (!codes.includes(sig[1])) codes.push(sig[1]);
|
|
5488
|
-
}
|
|
5489
|
-
epochs.push({
|
|
5490
|
-
timeMs: GPS_EPOCH_MS4 + week * MS_PER_WEEK2 + Math.round(rcvTow * 1e3),
|
|
5491
|
-
leapS: leapValid ? leapS : null,
|
|
5492
|
-
meas
|
|
5493
|
-
});
|
|
5494
|
-
}
|
|
5495
|
-
}
|
|
5496
|
-
return { epochs, messageCounts, obsCodes, badChecksums: stats.badChecksums };
|
|
5418
|
+
function getBitS2(b, p1, l1, p2, l2) {
|
|
5419
|
+
return getBitS(b, p1, l1) * 2 ** l2 + getBitU(b, p2, l2);
|
|
5497
5420
|
}
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
var CRC_TABLE = (() => {
|
|
5501
|
-
const t = new Uint16Array(256);
|
|
5502
|
-
for (let n = 0; n < 256; n++) {
|
|
5503
|
-
let c = n << 8;
|
|
5504
|
-
for (let k = 0; k < 8; k++)
|
|
5505
|
-
c = c & 32768 ? (c << 1 ^ 4129) & 65535 : c << 1 & 65535;
|
|
5506
|
-
t[n] = c;
|
|
5507
|
-
}
|
|
5508
|
-
return t;
|
|
5509
|
-
})();
|
|
5510
|
-
function crc16(data, start, end) {
|
|
5511
|
-
let crc = 0;
|
|
5512
|
-
for (let i = start; i < end; i++)
|
|
5513
|
-
crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
|
|
5514
|
-
return crc;
|
|
5421
|
+
function getBitU3(b, p1, l1, p2, l2, p3, l3) {
|
|
5422
|
+
return getBitU(b, p1, l1) * 2 ** (l2 + l3) + getBitU(b, p2, l2) * 2 ** l3 + getBitU(b, p3, l3);
|
|
5515
5423
|
}
|
|
5516
|
-
function
|
|
5517
|
-
|
|
5518
|
-
let i = 0;
|
|
5519
|
-
while (i + 8 <= data.length) {
|
|
5520
|
-
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
5521
|
-
i++;
|
|
5522
|
-
continue;
|
|
5523
|
-
}
|
|
5524
|
-
const len = view.getUint16(i + 6, true);
|
|
5525
|
-
if (len < 8 || len % 4 !== 0) {
|
|
5526
|
-
i++;
|
|
5527
|
-
continue;
|
|
5528
|
-
}
|
|
5529
|
-
if (i + len > data.length) break;
|
|
5530
|
-
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
5531
|
-
badCrc++;
|
|
5532
|
-
i++;
|
|
5533
|
-
continue;
|
|
5534
|
-
}
|
|
5535
|
-
onBlock(view.getUint16(i + 4, true) & 8191, i, len);
|
|
5536
|
-
i += len;
|
|
5537
|
-
}
|
|
5538
|
-
return badCrc;
|
|
5424
|
+
function getBitS3(b, p1, l1, p2, l2, p3, l3) {
|
|
5425
|
+
return getBitS(b, p1, l1) * 2 ** (l2 + l3) + getBitU(b, p2, l2) * 2 ** l3 + getBitU(b, p3, l3);
|
|
5539
5426
|
}
|
|
5540
|
-
var
|
|
5541
|
-
function
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
if (svid <= 70) return null;
|
|
5547
|
-
if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
|
|
5548
|
-
if (svid <= 119) return null;
|
|
5549
|
-
if (svid <= 140) return `S${two(svid - 100)}`;
|
|
5550
|
-
if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
|
|
5551
|
-
if (svid <= 190) return `J${two(svid - 180)}`;
|
|
5552
|
-
if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
|
|
5553
|
-
if (svid <= 215) return `S${two(svid - 157)}`;
|
|
5554
|
-
if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
|
|
5555
|
-
if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
|
|
5556
|
-
return null;
|
|
5427
|
+
var mergeS = (hi, lo, n) => hi * 2 ** n + lo;
|
|
5428
|
+
function bchOk(cw) {
|
|
5429
|
+
for (let i = 14; i >= 4; i--) {
|
|
5430
|
+
if (cw & 1 << i) cw ^= 19 << i - 4;
|
|
5431
|
+
}
|
|
5432
|
+
return cw === 0;
|
|
5557
5433
|
}
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
let offset = refWeek % mod - wn;
|
|
5569
|
-
if (offset > mod / 2) offset -= mod;
|
|
5570
|
-
if (offset < -(mod / 2 - 1)) offset += mod;
|
|
5571
|
-
return refWeek - offset;
|
|
5434
|
+
function bdsSubframeParityOk(subframe) {
|
|
5435
|
+
if (subframe.length < BDS_SUBFRAME_BYTES) return false;
|
|
5436
|
+
if (!bchOk(getBitU(subframe, 15, 15))) return false;
|
|
5437
|
+
for (let w = 1; w < 10; w++) {
|
|
5438
|
+
const base = 30 * w;
|
|
5439
|
+
const cw1 = getBitU(subframe, base, 11) * 16 + getBitU(subframe, base + 22, 4);
|
|
5440
|
+
const cw2 = getBitU(subframe, base + 11, 11) * 16 + getBitU(subframe, base + 26, 4);
|
|
5441
|
+
if (!bchOk(cw1) || !bchOk(cw2)) return false;
|
|
5442
|
+
}
|
|
5443
|
+
return true;
|
|
5572
5444
|
}
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
const
|
|
5578
|
-
const max = sys === "G" ? 32 : 10;
|
|
5579
|
-
if (prn < 1 || prn > max) return null;
|
|
5580
|
-
const wnc = view.getUint16(b + 12, true);
|
|
5581
|
-
const tgdRaw = view.getFloat32(b + 28, true);
|
|
5582
|
-
const tocs = view.getUint32(b + 32, true);
|
|
5583
|
-
const toes = view.getUint32(b + 88, true);
|
|
5584
|
-
const wnToc = adjustWeek(wnc, view.getUint16(b + 136, true), 1024);
|
|
5585
|
-
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
5445
|
+
function buildBdsEphemeris(prn, f) {
|
|
5446
|
+
let week = f.week;
|
|
5447
|
+
if (f.toes < f.sow - HALF_WEEK2) week++;
|
|
5448
|
+
else if (f.toes > f.sow + HALF_WEEK2) week--;
|
|
5449
|
+
const tocDate = new Date(BDT_EPOCH_MS + week * MS_PER_WEEK2 + f.tocSec * 1e3);
|
|
5586
5450
|
return {
|
|
5587
|
-
system:
|
|
5588
|
-
prn
|
|
5451
|
+
system: "C",
|
|
5452
|
+
prn,
|
|
5589
5453
|
toc: sowOf(tocDate.getTime()),
|
|
5590
5454
|
tocDate,
|
|
5591
|
-
af0:
|
|
5592
|
-
af1:
|
|
5593
|
-
af2:
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
week
|
|
5615
|
-
svHealth:
|
|
5616
|
-
|
|
5455
|
+
af0: f.af0,
|
|
5456
|
+
af1: f.af1,
|
|
5457
|
+
af2: f.af2,
|
|
5458
|
+
// AODE is not in the ephemeris subframes; RTKLIB derives the RINEX
|
|
5459
|
+
// IODE/AODE slot from toc per the BDS ICD update schedule.
|
|
5460
|
+
iode: Math.floor(f.tocSec / 720) % 240,
|
|
5461
|
+
crs: f.crs,
|
|
5462
|
+
deltaN: f.deltaN,
|
|
5463
|
+
m0: f.m0,
|
|
5464
|
+
cuc: f.cuc,
|
|
5465
|
+
e: f.e,
|
|
5466
|
+
cus: f.cus,
|
|
5467
|
+
sqrtA: f.sqrtA,
|
|
5468
|
+
toe: f.toes,
|
|
5469
|
+
cic: f.cic,
|
|
5470
|
+
omega0: f.omega0,
|
|
5471
|
+
cis: f.cis,
|
|
5472
|
+
i0: f.i0,
|
|
5473
|
+
crc: f.crc,
|
|
5474
|
+
omega: f.omega,
|
|
5475
|
+
omegaDot: f.omegaDot,
|
|
5476
|
+
idot: f.idot,
|
|
5477
|
+
week,
|
|
5478
|
+
// RINEX BDS week field is the BDT week of toe
|
|
5479
|
+
svHealth: f.svh,
|
|
5480
|
+
// SatH1
|
|
5481
|
+
tgd: f.tgd1
|
|
5482
|
+
// TGD1 (B1) — RINEX slot
|
|
5617
5483
|
};
|
|
5618
5484
|
}
|
|
5619
|
-
function
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
const
|
|
5625
|
-
const
|
|
5626
|
-
const
|
|
5627
|
-
const
|
|
5628
|
-
const
|
|
5629
|
-
const
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
const
|
|
5635
|
-
|
|
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
|
-
af1: view.getFloat32(b + 40, true),
|
|
5685
|
-
af2: view.getFloat32(b + 36, true),
|
|
5686
|
-
iode: view.getUint8(b + 21),
|
|
5687
|
-
// AODE
|
|
5688
|
-
crs: view.getFloat32(b + 48, true),
|
|
5689
|
-
deltaN: view.getFloat32(b + 52, true) * PI2,
|
|
5690
|
-
m0: view.getFloat64(b + 56, true) * PI2,
|
|
5691
|
-
cuc: view.getFloat32(b + 64, true),
|
|
5692
|
-
e: view.getFloat64(b + 68, true),
|
|
5693
|
-
cus: view.getFloat32(b + 76, true),
|
|
5694
|
-
sqrtA: view.getFloat64(b + 80, true),
|
|
5695
|
-
toe: toes,
|
|
5696
|
-
cic: view.getFloat32(b + 92, true),
|
|
5697
|
-
omega0: view.getFloat64(b + 96, true) * PI2,
|
|
5698
|
-
cis: view.getFloat32(b + 104, true),
|
|
5699
|
-
i0: view.getFloat64(b + 108, true) * PI2,
|
|
5700
|
-
crc: view.getFloat32(b + 116, true),
|
|
5701
|
-
omega: view.getFloat64(b + 120, true) * PI2,
|
|
5702
|
-
omegaDot: view.getFloat32(b + 128, true) * PI2,
|
|
5703
|
-
idot: view.getFloat32(b + 132, true) * PI2,
|
|
5704
|
-
week: wnToe,
|
|
5705
|
-
// RINEX BDS week field is the BDT week
|
|
5706
|
-
svHealth: view.getUint8(b + 19),
|
|
5707
|
-
// SatH1
|
|
5708
|
-
tgd: tgd1 !== F4_DNU ? tgd1 : 0
|
|
5709
|
-
// TGD1 B1/B3 (RINEX slot)
|
|
5710
|
-
};
|
|
5711
|
-
}
|
|
5712
|
-
function decodeGloNav(view, b) {
|
|
5713
|
-
const prn = svidToPrn(view.getUint8(b + 14));
|
|
5714
|
-
if (!prn || prn[0] !== "R") return null;
|
|
5715
|
-
const wnc = view.getUint16(b + 12, true);
|
|
5716
|
-
const toes = view.getUint32(b + 76, true);
|
|
5717
|
-
const wnToe = adjustWeek(wnc, view.getUint16(b + 80, true), 1024);
|
|
5718
|
-
const toeGpsMs = gpsMs(wnToe, toes);
|
|
5719
|
-
const leapMs = getGpsLeap(new Date(toeGpsMs)) * 1e3;
|
|
5720
|
-
const tocDate = new Date(toeGpsMs - leapMs);
|
|
5721
|
-
const tofGpsMs = gpsMs(wnc, 0) + view.getUint32(b + 8, true);
|
|
5722
|
-
const tofLeapMs = getGpsLeap(new Date(tofGpsMs)) * 1e3;
|
|
5723
|
-
const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS5) / 1e3 % SEC_PER_WEEK4;
|
|
5724
|
-
return {
|
|
5725
|
-
system: "R",
|
|
5726
|
-
prn,
|
|
5727
|
-
tocDate,
|
|
5728
|
-
// RINEX stores −τn as the clock bias; SBF carries τn (ICD sign)
|
|
5729
|
-
tauN: -view.getFloat32(b + 68, true),
|
|
5730
|
-
gammaN: view.getFloat32(b + 64, true),
|
|
5731
|
-
messageFrameTime,
|
|
5732
|
-
x: view.getFloat64(b + 16, true),
|
|
5733
|
-
// SBF unit is km, like RINEX
|
|
5734
|
-
xDot: view.getFloat32(b + 40, true),
|
|
5735
|
-
xAcc: view.getFloat32(b + 52, true),
|
|
5736
|
-
y: view.getFloat64(b + 24, true),
|
|
5737
|
-
yDot: view.getFloat32(b + 44, true),
|
|
5738
|
-
yAcc: view.getFloat32(b + 56, true),
|
|
5739
|
-
z: view.getFloat64(b + 32, true),
|
|
5740
|
-
zDot: view.getFloat32(b + 48, true),
|
|
5741
|
-
zAcc: view.getFloat32(b + 60, true),
|
|
5742
|
-
// MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
|
|
5743
|
-
health: view.getUint8(b + 85) >> 2,
|
|
5744
|
-
freqNum: view.getUint8(b + 15) - 8
|
|
5745
|
-
};
|
|
5746
|
-
}
|
|
5747
|
-
function parseSbfNav(data) {
|
|
5748
|
-
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
5749
|
-
const ephemerides = [];
|
|
5750
|
-
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
5751
|
-
let eph = null;
|
|
5752
|
-
if (id === 5891 && len >= 140) eph = decodeGpsQzsNav(view, b, "G");
|
|
5753
|
-
else if (id === 4095 && len >= 140) eph = decodeGpsQzsNav(view, b, "J");
|
|
5754
|
-
else if (id === 4002 && len >= 149) eph = decodeGalNav(view, b);
|
|
5755
|
-
else if (id === 4081 && len >= 140) eph = decodeBdsNav(view, b);
|
|
5756
|
-
else if (id === 4004 && len >= 96) eph = decodeGloNav(view, b);
|
|
5757
|
-
if (eph) ephemerides.push(eph);
|
|
5485
|
+
function decodeBdsD1Frame(subframes, opts = {}) {
|
|
5486
|
+
if (subframes.length < 3 * BDS_SUBFRAME_BYTES) return null;
|
|
5487
|
+
const b = subframes;
|
|
5488
|
+
let i = 8 * 38 * 0;
|
|
5489
|
+
const frn1 = getBitU(b, i + 15, 3);
|
|
5490
|
+
const sow1 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5491
|
+
const svh = getBitU(b, i + 42, 1);
|
|
5492
|
+
const week = getBitU(b, i + 60, 13);
|
|
5493
|
+
const tocSec = getBitU2(b, i + 73, 9, i + 90, 8) * 8;
|
|
5494
|
+
const tgd1 = getBitS(b, i + 98, 10) * 0.1 * 1e-9;
|
|
5495
|
+
const af2 = getBitS(b, i + 214, 11) * 2 ** -66;
|
|
5496
|
+
const af0 = getBitS2(b, i + 225, 7, i + 240, 17) * 2 ** -33;
|
|
5497
|
+
const af1 = getBitS2(b, i + 257, 5, i + 270, 17) * 2 ** -50;
|
|
5498
|
+
i = 8 * 38 * 1;
|
|
5499
|
+
const frn2 = getBitU(b, i + 15, 3);
|
|
5500
|
+
const sow2 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5501
|
+
const deltaN = getBitS2(b, i + 42, 10, i + 60, 6) * 2 ** -43 * GPS_PI;
|
|
5502
|
+
const cuc = getBitS2(b, i + 66, 16, i + 90, 2) * 2 ** -31;
|
|
5503
|
+
const m0 = getBitS2(b, i + 92, 20, i + 120, 12) * 2 ** -31 * GPS_PI;
|
|
5504
|
+
const e = getBitU2(b, i + 132, 10, i + 150, 22) * 2 ** -33;
|
|
5505
|
+
const cus = getBitS(b, i + 180, 18) * 2 ** -31;
|
|
5506
|
+
const crc = getBitS2(b, i + 198, 4, i + 210, 14) * 2 ** -6;
|
|
5507
|
+
const crs = getBitS2(b, i + 224, 8, i + 240, 10) * 2 ** -6;
|
|
5508
|
+
const sqrtA = getBitU2(b, i + 250, 12, i + 270, 20) * 2 ** -19;
|
|
5509
|
+
const toe1 = getBitU(b, i + 290, 2);
|
|
5510
|
+
i = 8 * 38 * 2;
|
|
5511
|
+
const frn3 = getBitU(b, i + 15, 3);
|
|
5512
|
+
const sow3 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5513
|
+
const toe2 = getBitU2(b, i + 42, 10, i + 60, 5);
|
|
5514
|
+
const i0 = getBitS2(b, i + 65, 17, i + 90, 15) * 2 ** -31 * GPS_PI;
|
|
5515
|
+
const cic = getBitS2(b, i + 105, 7, i + 120, 11) * 2 ** -31;
|
|
5516
|
+
const omegaDot = getBitS2(b, i + 131, 11, i + 150, 13) * 2 ** -43 * GPS_PI;
|
|
5517
|
+
const cis = getBitS2(b, i + 163, 9, i + 180, 9) * 2 ** -31;
|
|
5518
|
+
const idot = getBitS2(b, i + 189, 13, i + 210, 1) * 2 ** -43 * GPS_PI;
|
|
5519
|
+
const omega0 = getBitS2(b, i + 211, 21, i + 240, 11) * 2 ** -31 * GPS_PI;
|
|
5520
|
+
const omega = getBitS2(b, i + 251, 11, i + 270, 21) * 2 ** -31 * GPS_PI;
|
|
5521
|
+
const toes = (toe1 * 2 ** 15 + toe2) * 8;
|
|
5522
|
+
if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3) return null;
|
|
5523
|
+
if (sow2 !== sow1 + 6 || sow3 !== sow2 + 6) return null;
|
|
5524
|
+
if (tocSec !== toes) return null;
|
|
5525
|
+
return buildBdsEphemeris(opts.prn ?? "C00", {
|
|
5526
|
+
week,
|
|
5527
|
+
sow: sow1,
|
|
5528
|
+
toes,
|
|
5529
|
+
tocSec,
|
|
5530
|
+
svh,
|
|
5531
|
+
tgd1,
|
|
5532
|
+
af0,
|
|
5533
|
+
af1,
|
|
5534
|
+
af2,
|
|
5535
|
+
crs,
|
|
5536
|
+
deltaN,
|
|
5537
|
+
m0,
|
|
5538
|
+
cuc,
|
|
5539
|
+
e,
|
|
5540
|
+
cus,
|
|
5541
|
+
sqrtA,
|
|
5542
|
+
cic,
|
|
5543
|
+
omega0,
|
|
5544
|
+
cis,
|
|
5545
|
+
i0,
|
|
5546
|
+
crc,
|
|
5547
|
+
omega,
|
|
5548
|
+
omegaDot,
|
|
5549
|
+
idot
|
|
5758
5550
|
});
|
|
5759
|
-
return { ephemerides, badCrc };
|
|
5760
5551
|
}
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
const
|
|
5766
|
-
const
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5552
|
+
function decodeBdsD2Frame(pages, opts = {}) {
|
|
5553
|
+
if (pages.length < 10 * BDS_SUBFRAME_BYTES) return null;
|
|
5554
|
+
const b = pages;
|
|
5555
|
+
let i = 8 * 38 * 0;
|
|
5556
|
+
const pgn1 = getBitU(b, i + 42, 4);
|
|
5557
|
+
const sow1 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5558
|
+
const svh = getBitU(b, i + 46, 1);
|
|
5559
|
+
const week = getBitU(b, i + 64, 13);
|
|
5560
|
+
const tocSec = getBitU2(b, i + 77, 5, i + 90, 12) * 8;
|
|
5561
|
+
const tgd1 = getBitS(b, i + 102, 10) * 0.1 * 1e-9;
|
|
5562
|
+
i = 8 * 38 * 2;
|
|
5563
|
+
const pgn3 = getBitU(b, i + 42, 4);
|
|
5564
|
+
const sow3 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5565
|
+
const af0 = getBitS2(b, i + 100, 12, i + 120, 12) * 2 ** -33;
|
|
5566
|
+
const af1p3 = getBitS(b, i + 132, 4);
|
|
5567
|
+
i = 8 * 38 * 3;
|
|
5568
|
+
const pgn4 = getBitU(b, i + 42, 4);
|
|
5569
|
+
const sow4 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5570
|
+
const af1p4 = getBitU2(b, i + 46, 6, i + 60, 12);
|
|
5571
|
+
const af2 = getBitS2(b, i + 72, 10, i + 90, 1) * 2 ** -66;
|
|
5572
|
+
const deltaN = getBitS(b, i + 96, 16) * 2 ** -43 * GPS_PI;
|
|
5573
|
+
const cucp4 = getBitS(b, i + 120, 14);
|
|
5574
|
+
i = 8 * 38 * 4;
|
|
5575
|
+
const pgn5 = getBitU(b, i + 42, 4);
|
|
5576
|
+
const sow5 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5577
|
+
const cucp5 = getBitU(b, i + 46, 4);
|
|
5578
|
+
const m0 = getBitS3(b, i + 50, 2, i + 60, 22, i + 90, 8) * 2 ** -31 * GPS_PI;
|
|
5579
|
+
const cus = getBitS2(b, i + 98, 14, i + 120, 4) * 2 ** -31;
|
|
5580
|
+
const ep5 = getBitS(b, i + 124, 10);
|
|
5581
|
+
i = 8 * 38 * 5;
|
|
5582
|
+
const pgn6 = getBitU(b, i + 42, 4);
|
|
5583
|
+
const sow6 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5584
|
+
const ep6 = getBitU2(b, i + 46, 6, i + 60, 16);
|
|
5585
|
+
const sqrtA = getBitU3(b, i + 76, 6, i + 90, 22, i + 120, 4) * 2 ** -19;
|
|
5586
|
+
const cicp6 = getBitS(b, i + 124, 10);
|
|
5587
|
+
i = 8 * 38 * 6;
|
|
5588
|
+
const pgn7 = getBitU(b, i + 42, 4);
|
|
5589
|
+
const sow7 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5590
|
+
const cicp7 = getBitU2(b, i + 46, 6, i + 60, 2);
|
|
5591
|
+
const cis = getBitS(b, i + 62, 18) * 2 ** -31;
|
|
5592
|
+
const toes = getBitU2(b, i + 80, 2, i + 90, 15) * 8;
|
|
5593
|
+
const i0p7 = getBitS2(b, i + 105, 7, i + 120, 14);
|
|
5594
|
+
i = 8 * 38 * 7;
|
|
5595
|
+
const pgn8 = getBitU(b, i + 42, 4);
|
|
5596
|
+
const sow8 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5597
|
+
const i0p8 = getBitU2(b, i + 46, 6, i + 60, 5);
|
|
5598
|
+
const crc = getBitS2(b, i + 65, 17, i + 90, 1) * 2 ** -6;
|
|
5599
|
+
const crs = getBitS(b, i + 91, 18) * 2 ** -6;
|
|
5600
|
+
const omegaDotP8 = getBitS2(b, i + 109, 3, i + 120, 16);
|
|
5601
|
+
i = 8 * 38 * 8;
|
|
5602
|
+
const pgn9 = getBitU(b, i + 42, 4);
|
|
5603
|
+
const sow9 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5604
|
+
const omegaDotP9 = getBitU(b, i + 46, 5);
|
|
5605
|
+
const omega0 = getBitS3(b, i + 51, 1, i + 60, 22, i + 90, 9) * 2 ** -31 * GPS_PI;
|
|
5606
|
+
const omegaP9 = getBitS2(b, i + 99, 13, i + 120, 14);
|
|
5607
|
+
i = 8 * 38 * 9;
|
|
5608
|
+
const pgn10 = getBitU(b, i + 42, 4);
|
|
5609
|
+
const sow10 = getBitU2(b, i + 18, 8, i + 30, 12);
|
|
5610
|
+
const omegaP10 = getBitU(b, i + 46, 5);
|
|
5611
|
+
const idot = getBitS2(b, i + 51, 1, i + 60, 13) * 2 ** -43 * GPS_PI;
|
|
5612
|
+
if (pgn1 !== 1 || pgn3 !== 3 || pgn4 !== 4 || pgn5 !== 5 || pgn6 !== 6 || pgn7 !== 7 || pgn8 !== 8 || pgn9 !== 9 || pgn10 !== 10) {
|
|
5613
|
+
return null;
|
|
5786
5614
|
}
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5615
|
+
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) {
|
|
5616
|
+
return null;
|
|
5617
|
+
}
|
|
5618
|
+
if (tocSec !== toes) return null;
|
|
5619
|
+
return buildBdsEphemeris(opts.prn ?? "C00", {
|
|
5620
|
+
week,
|
|
5621
|
+
sow: sow1,
|
|
5622
|
+
toes,
|
|
5623
|
+
tocSec,
|
|
5624
|
+
svh,
|
|
5625
|
+
tgd1,
|
|
5626
|
+
af0,
|
|
5627
|
+
af1: mergeS(af1p3, af1p4, 18) * 2 ** -50,
|
|
5628
|
+
af2,
|
|
5629
|
+
crs,
|
|
5630
|
+
deltaN,
|
|
5631
|
+
m0,
|
|
5632
|
+
cuc: mergeS(cucp4, cucp5, 4) * 2 ** -31,
|
|
5633
|
+
e: mergeS(ep5, ep6, 22) * 2 ** -33,
|
|
5634
|
+
cus,
|
|
5792
5635
|
sqrtA,
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
health
|
|
5802
|
-
};
|
|
5803
|
-
}
|
|
5804
|
-
function decodeBdsAlm(view, b) {
|
|
5805
|
-
const prnStr = svidToPrn(view.getUint8(b + 14));
|
|
5806
|
-
if (!prnStr || prnStr[0] !== "C") return null;
|
|
5807
|
-
const prn = parseInt(prnStr.slice(1), 10);
|
|
5808
|
-
const toa = view.getUint32(b + 16, true);
|
|
5809
|
-
if (toa === U4_DNU) return null;
|
|
5810
|
-
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
5811
|
-
const geo = prn <= 5 || prn >= 59;
|
|
5812
|
-
return {
|
|
5813
|
-
system: "C",
|
|
5814
|
-
prn: prnStr,
|
|
5815
|
-
weekAlm: adjustWeek(bdsWeekRef, view.getUint8(b + 15), 256),
|
|
5816
|
-
toaSec: toa,
|
|
5817
|
-
sqrtA: view.getFloat32(b + 20, true),
|
|
5818
|
-
e: view.getFloat32(b + 24, true),
|
|
5819
|
-
i0OrDeltaI: (geo ? 0 : I_REF_03) + view.getFloat32(b + 44, true) * PI2,
|
|
5820
|
-
omega0: view.getFloat32(b + 36, true) * PI2,
|
|
5821
|
-
omega: view.getFloat32(b + 28, true) * PI2,
|
|
5822
|
-
m0: view.getFloat32(b + 32, true) * PI2,
|
|
5823
|
-
omegaDot: view.getFloat32(b + 40, true) * PI2,
|
|
5824
|
-
af0: view.getFloat32(b + 48, true),
|
|
5825
|
-
af1: view.getFloat32(b + 52, true),
|
|
5826
|
-
health: view.getUint16(b + 56, true)
|
|
5827
|
-
};
|
|
5828
|
-
}
|
|
5829
|
-
function decodeGloAlm(view, b) {
|
|
5830
|
-
const prn = svidToPrn(view.getUint8(b + 14));
|
|
5831
|
-
if (!prn || prn[0] !== "R") return null;
|
|
5832
|
-
const wnc = view.getUint16(b + 12, true);
|
|
5833
|
-
const toa = view.getUint32(b + 20, true);
|
|
5834
|
-
if (toa === U4_DNU) return null;
|
|
5835
|
-
return {
|
|
5836
|
-
system: "R",
|
|
5837
|
-
prn,
|
|
5838
|
-
freqNr: view.getUint8(b + 15) - 8,
|
|
5839
|
-
weekAlm: adjustWeek(wnc, view.getUint8(b + 52), 256),
|
|
5840
|
-
toaSec: toa,
|
|
5841
|
-
epsilon: view.getFloat32(b + 16, true),
|
|
5842
|
-
deltaI: view.getFloat32(b + 24, true) * PI2,
|
|
5843
|
-
lambda: view.getFloat32(b + 28, true) * PI2,
|
|
5844
|
-
tLambda: view.getFloat32(b + 32, true),
|
|
5845
|
-
omega: view.getFloat32(b + 36, true) * PI2,
|
|
5846
|
-
deltaT: view.getFloat32(b + 40, true),
|
|
5847
|
-
deltaTDot: view.getFloat32(b + 44, true),
|
|
5848
|
-
tau: view.getFloat32(b + 48, true),
|
|
5849
|
-
health: view.getUint8(b + 53),
|
|
5850
|
-
nDay: view.getUint16(b + 54, true),
|
|
5851
|
-
n4: view.getUint8(b + 57)
|
|
5852
|
-
};
|
|
5853
|
-
}
|
|
5854
|
-
function parseSbfAlmanac(data) {
|
|
5855
|
-
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
5856
|
-
const almanacs = [];
|
|
5857
|
-
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
5858
|
-
let alm = null;
|
|
5859
|
-
if (id === 5892 && len >= 60) alm = decodeGpsGalAlm(view, b, "G");
|
|
5860
|
-
else if (id === 4003 && len >= 61) alm = decodeGpsGalAlm(view, b, "E");
|
|
5861
|
-
else if (id === 4119 && len >= 60) alm = decodeBdsAlm(view, b);
|
|
5862
|
-
else if (id === 4005 && len >= 60) alm = decodeGloAlm(view, b);
|
|
5863
|
-
if (alm) almanacs.push(alm);
|
|
5636
|
+
cic: mergeS(cicp6, cicp7, 8) * 2 ** -31,
|
|
5637
|
+
omega0,
|
|
5638
|
+
cis,
|
|
5639
|
+
i0: mergeS(i0p7, i0p8, 11) * 2 ** -31 * GPS_PI,
|
|
5640
|
+
crc,
|
|
5641
|
+
omega: mergeS(omegaP9, omegaP10, 5) * 2 ** -31 * GPS_PI,
|
|
5642
|
+
omegaDot: mergeS(omegaDotP8, omegaDotP9, 5) * 2 ** -43 * GPS_PI,
|
|
5643
|
+
idot
|
|
5864
5644
|
});
|
|
5865
|
-
return { almanacs, badCrc };
|
|
5866
5645
|
}
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
var F4_DNU2 = -2e10;
|
|
5870
|
-
function parseSbfIonoUtc(data) {
|
|
5871
|
-
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
5872
|
-
const ionoCorrections = {};
|
|
5873
|
-
let leapSeconds = null;
|
|
5874
|
-
const f4s = (b, n) => {
|
|
5875
|
-
const out = [];
|
|
5876
|
-
for (let k = 0; k < n; k++) {
|
|
5877
|
-
const v = view.getFloat32(b + 4 * k, true);
|
|
5878
|
-
if (v === F4_DNU2) return null;
|
|
5879
|
-
out.push(v);
|
|
5880
|
-
}
|
|
5881
|
-
return out;
|
|
5882
|
-
};
|
|
5883
|
-
scanSbfFrames(data, view, (id, b, len) => {
|
|
5884
|
-
if ((id === 5893 || id === 4120) && len >= 48) {
|
|
5885
|
-
const alpha = f4s(b + 16, 4);
|
|
5886
|
-
const beta = f4s(b + 32, 4);
|
|
5887
|
-
if (!alpha || !beta) return;
|
|
5888
|
-
const sys = id === 5893 ? "GPS" : "BDS";
|
|
5889
|
-
ionoCorrections[`${sys}A`] = alpha;
|
|
5890
|
-
ionoCorrections[`${sys}B`] = beta;
|
|
5891
|
-
} else if (id === 4030 && len >= 28) {
|
|
5892
|
-
const ai = f4s(b + 16, 3);
|
|
5893
|
-
if (ai) ionoCorrections["GAL"] = ai;
|
|
5894
|
-
} else if (id === 5894 && len >= 37) {
|
|
5895
|
-
leapSeconds = view.getInt8(b + 33);
|
|
5896
|
-
}
|
|
5897
|
-
});
|
|
5898
|
-
return { ionoCorrections, leapSeconds };
|
|
5646
|
+
function isBdsGeoPrn(prn) {
|
|
5647
|
+
return prn < 6 || prn > 58;
|
|
5899
5648
|
}
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
if (!signal || len < 60) return;
|
|
5918
|
-
messages++;
|
|
5919
|
-
const msg = new Uint8Array(40);
|
|
5920
|
-
for (let k = 0; k < 10; k++) {
|
|
5921
|
-
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
5922
|
-
msg[4 * k] = w >>> 24;
|
|
5923
|
-
msg[4 * k + 1] = w >>> 16 & 255;
|
|
5924
|
-
msg[4 * k + 2] = w >>> 8 & 255;
|
|
5925
|
-
msg[4 * k + 3] = w & 255;
|
|
5649
|
+
var BdsAssembler = class {
|
|
5650
|
+
sats = /* @__PURE__ */ new Map();
|
|
5651
|
+
/**
|
|
5652
|
+
* Push one 300-bit subframe (38+ bytes, bit 0 = first bit of the
|
|
5653
|
+
* preamble) for the satellite `prn` ("C06"). Returns the newly
|
|
5654
|
+
* completed ephemeris, or null.
|
|
5655
|
+
*/
|
|
5656
|
+
push(prn, subframe) {
|
|
5657
|
+
if (subframe.length < BDS_SUBFRAME_BYTES) return null;
|
|
5658
|
+
const num = parseInt(prn.slice(1), 10);
|
|
5659
|
+
if (!Number.isFinite(num) || num < 1 || num > 63) return null;
|
|
5660
|
+
const id = getBitU(subframe, 15, 3);
|
|
5661
|
+
if (id < 1 || id > 5) return null;
|
|
5662
|
+
let sat = this.sats.get(prn);
|
|
5663
|
+
if (!sat) {
|
|
5664
|
+
sat = { buf: new Uint8Array(10 * BDS_SUBFRAME_BYTES) };
|
|
5665
|
+
this.sats.set(prn, sat);
|
|
5926
5666
|
}
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5667
|
+
let eph = null;
|
|
5668
|
+
if (!isBdsGeoPrn(num)) {
|
|
5669
|
+
sat.buf.set(subframe.subarray(0, BDS_SUBFRAME_BYTES), (id - 1) * 38);
|
|
5670
|
+
if (id === 3) eph = decodeBdsD1Frame(sat.buf, { prn });
|
|
5671
|
+
} else {
|
|
5672
|
+
if (id !== 1) return null;
|
|
5673
|
+
const pgn = getBitU(subframe, 42, 4);
|
|
5674
|
+
if (pgn < 1 || pgn > 10) return null;
|
|
5675
|
+
sat.buf.set(subframe.subarray(0, BDS_SUBFRAME_BYTES), (pgn - 1) * 38);
|
|
5676
|
+
if (pgn === 10) eph = decodeBdsD2Frame(sat.buf, { prn });
|
|
5930
5677
|
}
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5678
|
+
if (!eph) return null;
|
|
5679
|
+
const key = `${eph.week}:${eph.toe}`;
|
|
5680
|
+
if (key === sat.lastKey) return null;
|
|
5681
|
+
sat.lastKey = key;
|
|
5682
|
+
return eph;
|
|
5683
|
+
}
|
|
5684
|
+
};
|
|
5936
5685
|
|
|
5937
5686
|
// src/navbits/gal.ts
|
|
5938
|
-
var
|
|
5687
|
+
var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
|
|
5939
5688
|
var SEC_PER_WEEK5 = 7 * 86400;
|
|
5940
|
-
var
|
|
5689
|
+
var HALF_WEEK3 = 302400;
|
|
5941
5690
|
var GST_GPS_WEEK_OFFSET = 1024;
|
|
5942
5691
|
function galInavPageCrcOk(page) {
|
|
5943
5692
|
if (page.length < 28) return false;
|
|
@@ -5948,14 +5697,14 @@ function galFnavPageCrcOk(page) {
|
|
|
5948
5697
|
return crc24q(page, 214) === getBitU(page, 214, 24);
|
|
5949
5698
|
}
|
|
5950
5699
|
function gpsDate2(week, sec) {
|
|
5951
|
-
return new Date(
|
|
5700
|
+
return new Date(GPS_EPOCH_MS4 + (week * SEC_PER_WEEK5 + sec) * 1e3);
|
|
5952
5701
|
}
|
|
5953
5702
|
function weekOfToe(week, tow, toe) {
|
|
5954
|
-
if (toe - tow >
|
|
5955
|
-
if (toe - tow < -
|
|
5703
|
+
if (toe - tow > HALF_WEEK3) return week - 1;
|
|
5704
|
+
if (toe - tow < -HALF_WEEK3) return week + 1;
|
|
5956
5705
|
return week;
|
|
5957
5706
|
}
|
|
5958
|
-
var
|
|
5707
|
+
var two = (n) => String(n).padStart(2, "0");
|
|
5959
5708
|
function decodeGalInavWords(words) {
|
|
5960
5709
|
if (words.length < 96) return null;
|
|
5961
5710
|
const b = words;
|
|
@@ -6043,7 +5792,7 @@ function decodeGalInavWords(words) {
|
|
|
6043
5792
|
const tocDate = gpsDate2(week, tocs);
|
|
6044
5793
|
return {
|
|
6045
5794
|
system: "E",
|
|
6046
|
-
prn: `E${
|
|
5795
|
+
prn: `E${two(svid)}`,
|
|
6047
5796
|
toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK5,
|
|
6048
5797
|
tocDate,
|
|
6049
5798
|
af0,
|
|
@@ -6151,7 +5900,7 @@ function decodeGalFnavPages(pages) {
|
|
|
6151
5900
|
const tocDate = gpsDate2(week, tocs);
|
|
6152
5901
|
return {
|
|
6153
5902
|
system: "E",
|
|
6154
|
-
prn: `E${
|
|
5903
|
+
prn: `E${two(svid)}`,
|
|
6155
5904
|
toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK5,
|
|
6156
5905
|
tocDate,
|
|
6157
5906
|
af0,
|
|
@@ -6207,7 +5956,7 @@ var GalInavAssembler = class {
|
|
|
6207
5956
|
sat.words[type * 16 + 15] = getBitU(page, 124, 8);
|
|
6208
5957
|
if (type !== 5) return null;
|
|
6209
5958
|
const eph = decodeGalInavWords(sat.words);
|
|
6210
|
-
if (!eph || eph.prn !== `E${
|
|
5959
|
+
if (!eph || eph.prn !== `E${two(prn)}`) return null;
|
|
6211
5960
|
const key = dedupKey(eph);
|
|
6212
5961
|
if (key === sat.lastKey) return null;
|
|
6213
5962
|
sat.lastKey = key;
|
|
@@ -6231,517 +5980,870 @@ var GalFnavAssembler = class {
|
|
|
6231
5980
|
sat = { words: new Uint8Array(124) };
|
|
6232
5981
|
this.sats.set(prn, sat);
|
|
6233
5982
|
}
|
|
6234
|
-
sat.words.set(page.subarray(0, 31), (type - 1) * 31);
|
|
6235
|
-
if (type !== 4) return null;
|
|
6236
|
-
const eph = decodeGalFnavPages(sat.words);
|
|
6237
|
-
if (!eph || eph.prn !== `E${
|
|
6238
|
-
const key = dedupKey(eph);
|
|
6239
|
-
if (key === sat.lastKey) return null;
|
|
6240
|
-
sat.lastKey = key;
|
|
6241
|
-
return eph;
|
|
5983
|
+
sat.words.set(page.subarray(0, 31), (type - 1) * 31);
|
|
5984
|
+
if (type !== 4) return null;
|
|
5985
|
+
const eph = decodeGalFnavPages(sat.words);
|
|
5986
|
+
if (!eph || eph.prn !== `E${two(prn)}`) return null;
|
|
5987
|
+
const key = dedupKey(eph);
|
|
5988
|
+
if (key === sat.lastKey) return null;
|
|
5989
|
+
sat.lastKey = key;
|
|
5990
|
+
return eph;
|
|
5991
|
+
}
|
|
5992
|
+
};
|
|
5993
|
+
|
|
5994
|
+
// src/navbits/glo.ts
|
|
5995
|
+
var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
|
|
5996
|
+
var SEC_PER_WEEK6 = 7 * 86400;
|
|
5997
|
+
var SEC_PER_DAY = 86400;
|
|
5998
|
+
var GLO_STRING_BYTES = 10;
|
|
5999
|
+
function getBitG(b, pos, len) {
|
|
6000
|
+
const value = getBitU(b, pos + 1, len - 1);
|
|
6001
|
+
return getBitU(b, pos, 1) && value !== 0 ? -value : value;
|
|
6002
|
+
}
|
|
6003
|
+
var HAMMING_MASKS = [
|
|
6004
|
+
[85, 85, 90, 170, 170, 170, 181, 85, 106, 216, 8],
|
|
6005
|
+
[102, 102, 108, 204, 204, 204, 217, 153, 179, 104, 16],
|
|
6006
|
+
[135, 135, 143, 15, 15, 15, 30, 30, 60, 112, 32],
|
|
6007
|
+
[7, 248, 15, 240, 15, 240, 31, 224, 63, 128, 64],
|
|
6008
|
+
[248, 0, 15, 255, 240, 0, 31, 255, 192, 0, 128],
|
|
6009
|
+
[0, 0, 15, 255, 255, 255, 224, 0, 0, 1, 0],
|
|
6010
|
+
[255, 255, 240, 0, 0, 0, 0, 0, 0, 2, 0],
|
|
6011
|
+
[255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248]
|
|
6012
|
+
];
|
|
6013
|
+
function testGloString(buff) {
|
|
6014
|
+
if (buff.length < 11) return false;
|
|
6015
|
+
let n = 0;
|
|
6016
|
+
let cs = 0;
|
|
6017
|
+
for (const mask of HAMMING_MASKS) {
|
|
6018
|
+
cs = 0;
|
|
6019
|
+
for (let j = 0; j < 11; j++) {
|
|
6020
|
+
let x = buff[j] & mask[j];
|
|
6021
|
+
x ^= x >> 4;
|
|
6022
|
+
x ^= x >> 2;
|
|
6023
|
+
x ^= x >> 1;
|
|
6024
|
+
cs ^= x & 1;
|
|
6025
|
+
}
|
|
6026
|
+
if (cs) n++;
|
|
6027
|
+
}
|
|
6028
|
+
return n === 0 || n === 2 && cs === 1;
|
|
6029
|
+
}
|
|
6030
|
+
function decodeGloStrings(strings, refDate, opts = {}) {
|
|
6031
|
+
if (strings.length < 4 * GLO_STRING_BYTES) return null;
|
|
6032
|
+
const b = strings;
|
|
6033
|
+
let i = 1;
|
|
6034
|
+
const frn1 = getBitU(b, i, 4);
|
|
6035
|
+
i += 4 + 2;
|
|
6036
|
+
i += 2;
|
|
6037
|
+
const tkH = getBitU(b, i, 5);
|
|
6038
|
+
i += 5;
|
|
6039
|
+
const tkM = getBitU(b, i, 6);
|
|
6040
|
+
i += 6;
|
|
6041
|
+
const tkS = getBitU(b, i, 1) * 30;
|
|
6042
|
+
i += 1;
|
|
6043
|
+
const xDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6044
|
+
i += 24;
|
|
6045
|
+
const xAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6046
|
+
i += 5;
|
|
6047
|
+
const x = getBitG(b, i, 27) * 2 ** -11;
|
|
6048
|
+
i = 80 + 1;
|
|
6049
|
+
const frn2 = getBitU(b, i, 4);
|
|
6050
|
+
i += 4;
|
|
6051
|
+
const bn = getBitU(b, i, 1);
|
|
6052
|
+
i += 1 + 2;
|
|
6053
|
+
i += 1;
|
|
6054
|
+
const tb = getBitU(b, i, 7);
|
|
6055
|
+
i += 7 + 5;
|
|
6056
|
+
const yDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6057
|
+
i += 24;
|
|
6058
|
+
const yAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6059
|
+
i += 5;
|
|
6060
|
+
const y = getBitG(b, i, 27) * 2 ** -11;
|
|
6061
|
+
i = 160 + 1;
|
|
6062
|
+
const frn3 = getBitU(b, i, 4);
|
|
6063
|
+
i += 4;
|
|
6064
|
+
i += 1;
|
|
6065
|
+
const gammaN = getBitG(b, i, 11) * 2 ** -40;
|
|
6066
|
+
i += 11 + 1;
|
|
6067
|
+
i += 2;
|
|
6068
|
+
i += 1;
|
|
6069
|
+
const zDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6070
|
+
i += 24;
|
|
6071
|
+
const zAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6072
|
+
i += 5;
|
|
6073
|
+
const z = getBitG(b, i, 27) * 2 ** -11;
|
|
6074
|
+
i = 240 + 1;
|
|
6075
|
+
const frn4 = getBitU(b, i, 4);
|
|
6076
|
+
i += 4;
|
|
6077
|
+
const tauN = getBitG(b, i, 22) * 2 ** -30;
|
|
6078
|
+
i += 22;
|
|
6079
|
+
i += 5;
|
|
6080
|
+
i += 5;
|
|
6081
|
+
i += 14;
|
|
6082
|
+
i += 1;
|
|
6083
|
+
i += 4;
|
|
6084
|
+
i += 3;
|
|
6085
|
+
i += 11;
|
|
6086
|
+
const slot = getBitU(b, i, 5);
|
|
6087
|
+
if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3 || frn4 !== 4) return null;
|
|
6088
|
+
if (slot < 1 || slot > 27) return null;
|
|
6089
|
+
const utcSec = (getUtcDate(refDate).getTime() - GPS_EPOCH_MS5) / 1e3;
|
|
6090
|
+
const week = Math.floor(utcSec / SEC_PER_WEEK6);
|
|
6091
|
+
let tow = utcSec - week * SEC_PER_WEEK6;
|
|
6092
|
+
const tod = tow % SEC_PER_DAY;
|
|
6093
|
+
tow -= tod;
|
|
6094
|
+
let tof = tkH * 3600 + tkM * 60 + tkS - 10800;
|
|
6095
|
+
if (tof < tod - 43200) tof += SEC_PER_DAY;
|
|
6096
|
+
else if (tof > tod + 43200) tof -= SEC_PER_DAY;
|
|
6097
|
+
let toe = tb * 900 - 10800;
|
|
6098
|
+
if (toe < tod - 43200) toe += SEC_PER_DAY;
|
|
6099
|
+
else if (toe > tod + 43200) toe -= SEC_PER_DAY;
|
|
6100
|
+
return {
|
|
6101
|
+
system: "R",
|
|
6102
|
+
prn: `R${String(slot).padStart(2, "0")}`,
|
|
6103
|
+
// RINEX GLONASS epochs are UTC: build the UTC toe Date directly.
|
|
6104
|
+
tocDate: new Date(GPS_EPOCH_MS5 + (week * SEC_PER_WEEK6 + tow + toe) * 1e3),
|
|
6105
|
+
tauN: -tauN,
|
|
6106
|
+
// RINEX stores −τn; the SIS carries τn (ICD sign)
|
|
6107
|
+
gammaN,
|
|
6108
|
+
// v3 message frame time: seconds of the UTC week (RTKLIB tof).
|
|
6109
|
+
messageFrameTime: ((tow + tof) % SEC_PER_WEEK6 + SEC_PER_WEEK6) % SEC_PER_WEEK6,
|
|
6110
|
+
x,
|
|
6111
|
+
xDot,
|
|
6112
|
+
xAcc,
|
|
6113
|
+
y,
|
|
6114
|
+
yDot,
|
|
6115
|
+
yAcc,
|
|
6116
|
+
z,
|
|
6117
|
+
zDot,
|
|
6118
|
+
zAcc,
|
|
6119
|
+
// MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
|
|
6120
|
+
health: bn,
|
|
6121
|
+
freqNum: opts.freqNum ?? 0
|
|
6122
|
+
};
|
|
6123
|
+
}
|
|
6124
|
+
var GloStringAssembler = class {
|
|
6125
|
+
sats = /* @__PURE__ */ new Map();
|
|
6126
|
+
/**
|
|
6127
|
+
* Push one navigation string (10+ bytes, bit 0 = string bit 85) for
|
|
6128
|
+
* the satellite `prn` ("R09"), received at the GPS-scale `time`.
|
|
6129
|
+
* Returns the newly completed ephemeris, or null. The decoded
|
|
6130
|
+
* string-4 slot number must match `prn`, or nothing is emitted.
|
|
6131
|
+
*/
|
|
6132
|
+
push(prn, str, time, freqNum = 0) {
|
|
6133
|
+
if (str.length < GLO_STRING_BYTES) return null;
|
|
6134
|
+
const m = getBitU(str, 1, 4);
|
|
6135
|
+
if (m < 1) return null;
|
|
6136
|
+
const sec = Math.floor(time.getTime() / 1e3);
|
|
6137
|
+
let sat = this.sats.get(prn);
|
|
6138
|
+
if (!sat) {
|
|
6139
|
+
sat = { buf: new Uint8Array(4 * GLO_STRING_BYTES), batchSec: sec };
|
|
6140
|
+
this.sats.set(prn, sat);
|
|
6141
|
+
} else if (Math.abs(sec - sat.batchSec) > 30) {
|
|
6142
|
+
sat.buf.fill(0);
|
|
6143
|
+
sat.batchSec = sec;
|
|
6144
|
+
}
|
|
6145
|
+
if (m > 4) return null;
|
|
6146
|
+
sat.buf.set(str.subarray(0, GLO_STRING_BYTES), (m - 1) * 10);
|
|
6147
|
+
if (m !== 4) return null;
|
|
6148
|
+
const eph = decodeGloStrings(sat.buf, time, { freqNum });
|
|
6149
|
+
if (!eph || eph.prn !== prn) return null;
|
|
6150
|
+
const key = `${eph.tocDate.getTime()}:${eph.health}`;
|
|
6151
|
+
if (key === sat.lastKey) return null;
|
|
6152
|
+
sat.lastKey = key;
|
|
6153
|
+
return eph;
|
|
6154
|
+
}
|
|
6155
|
+
};
|
|
6156
|
+
|
|
6157
|
+
// src/ubx/rawnav.ts
|
|
6158
|
+
var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
|
|
6159
|
+
var SEC_PER_WEEK7 = 7 * 86400;
|
|
6160
|
+
var two2 = (n) => String(n).padStart(2, "0");
|
|
6161
|
+
function parseUbxRawNav(data, opts = {}) {
|
|
6162
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6163
|
+
const ephemerides = [];
|
|
6164
|
+
const counts = { gal: 0, bds: 0, glo: 0 };
|
|
6165
|
+
let badParity = 0;
|
|
6166
|
+
const inav = new GalInavAssembler();
|
|
6167
|
+
const bds = new BdsAssembler();
|
|
6168
|
+
const glo = new GloStringAssembler();
|
|
6169
|
+
let refDate = null;
|
|
6170
|
+
for (const f of ubxFrames(data)) {
|
|
6171
|
+
if (f.msgClass !== 2) continue;
|
|
6172
|
+
if (f.msgId === 21 && f.payload.length >= 16) {
|
|
6173
|
+
const rcvTow = view.getFloat64(f.payloadStart, true);
|
|
6174
|
+
let week = view.getUint16(f.payloadStart + 8, true);
|
|
6175
|
+
if (week === 0 && opts.refWeek !== void 0) week = opts.refWeek;
|
|
6176
|
+
if (week > 0) {
|
|
6177
|
+
refDate = new Date(
|
|
6178
|
+
GPS_EPOCH_MS6 + (week * SEC_PER_WEEK7 + rcvTow) * 1e3
|
|
6179
|
+
);
|
|
6180
|
+
}
|
|
6181
|
+
continue;
|
|
6182
|
+
}
|
|
6183
|
+
if (f.msgId !== 19) continue;
|
|
6184
|
+
const p = f.payload;
|
|
6185
|
+
if (p.length < 8) continue;
|
|
6186
|
+
const gnssId = p[0];
|
|
6187
|
+
const svId = p[1];
|
|
6188
|
+
const sigId = p[2];
|
|
6189
|
+
const base = f.payloadStart + 8;
|
|
6190
|
+
if (gnssId === 2) {
|
|
6191
|
+
if (sigId === 3 || sigId === 8) continue;
|
|
6192
|
+
if (p.length < 8 + 32) continue;
|
|
6193
|
+
counts.gal++;
|
|
6194
|
+
if (svId < 1 || svId > 36) continue;
|
|
6195
|
+
const buff = new Uint8Array(32);
|
|
6196
|
+
for (let k = 0; k < 8; k++) {
|
|
6197
|
+
const w = view.getUint32(base + 4 * k, true);
|
|
6198
|
+
buff[4 * k] = w >>> 24;
|
|
6199
|
+
buff[4 * k + 1] = w >>> 16 & 255;
|
|
6200
|
+
buff[4 * k + 2] = w >>> 8 & 255;
|
|
6201
|
+
buff[4 * k + 3] = w & 255;
|
|
6202
|
+
}
|
|
6203
|
+
const page = new Uint8Array(30);
|
|
6204
|
+
for (let k = 0; k < 14; k++) page[k] = buff[k];
|
|
6205
|
+
setBitU(page, 112, 2, getBitU(buff, 112, 2));
|
|
6206
|
+
for (let k = 0; k < 15; k++) {
|
|
6207
|
+
setBitU(page, 114 + 8 * k, 8, getBitU(buff, 128 + 8 * k, 8));
|
|
6208
|
+
}
|
|
6209
|
+
if (getBitU(page, 0, 1) !== 0 || getBitU(page, 114, 1) !== 1) continue;
|
|
6210
|
+
if (getBitU(page, 1, 1) === 1 || getBitU(page, 115, 1) === 1) continue;
|
|
6211
|
+
if (!galInavPageCrcOk(page)) {
|
|
6212
|
+
badParity++;
|
|
6213
|
+
continue;
|
|
6214
|
+
}
|
|
6215
|
+
const eph = inav.push(svId, page);
|
|
6216
|
+
if (eph) ephemerides.push(eph);
|
|
6217
|
+
} else if (gnssId === 3) {
|
|
6218
|
+
if (sigId === 6 || sigId === 8) continue;
|
|
6219
|
+
if (p.length < 8 + 40) continue;
|
|
6220
|
+
counts.bds++;
|
|
6221
|
+
if (svId < 1 || svId > 63) continue;
|
|
6222
|
+
const sf = new Uint8Array(38);
|
|
6223
|
+
for (let k = 0; k < 10; k++) {
|
|
6224
|
+
const w = view.getUint32(base + 4 * k, true);
|
|
6225
|
+
setBitU(sf, 30 * k, 30, w & 1073741823);
|
|
6226
|
+
}
|
|
6227
|
+
if (!bdsSubframeParityOk(sf)) {
|
|
6228
|
+
badParity++;
|
|
6229
|
+
continue;
|
|
6230
|
+
}
|
|
6231
|
+
const eph = bds.push(`C${two2(svId)}`, sf);
|
|
6232
|
+
if (eph) ephemerides.push(eph);
|
|
6233
|
+
} else if (gnssId === 6) {
|
|
6234
|
+
if (p.length < 8 + 16) continue;
|
|
6235
|
+
counts.glo++;
|
|
6236
|
+
if (svId < 1 || svId > 32) continue;
|
|
6237
|
+
const str = new Uint8Array(16);
|
|
6238
|
+
for (let k = 0; k < 4; k++) {
|
|
6239
|
+
const w = view.getUint32(base + 4 * k, true);
|
|
6240
|
+
str[4 * k] = w >>> 24;
|
|
6241
|
+
str[4 * k + 1] = w >>> 16 & 255;
|
|
6242
|
+
str[4 * k + 2] = w >>> 8 & 255;
|
|
6243
|
+
str[4 * k + 3] = w & 255;
|
|
6244
|
+
}
|
|
6245
|
+
if (!testGloString(str)) {
|
|
6246
|
+
badParity++;
|
|
6247
|
+
continue;
|
|
6248
|
+
}
|
|
6249
|
+
if (!refDate) continue;
|
|
6250
|
+
const eph = glo.push(`R${two2(svId)}`, str, refDate, p[3] - 7);
|
|
6251
|
+
if (eph) ephemerides.push(eph);
|
|
6252
|
+
}
|
|
6253
|
+
}
|
|
6254
|
+
return { ephemerides, counts, badParity };
|
|
6255
|
+
}
|
|
6256
|
+
|
|
6257
|
+
// src/ubx/index.ts
|
|
6258
|
+
var GPS_EPOCH_MS7 = Date.UTC(1980, 0, 6);
|
|
6259
|
+
var MS_PER_WEEK3 = 7 * 864e5;
|
|
6260
|
+
var SIGNALS = {
|
|
6261
|
+
0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
|
|
6262
|
+
// GPS L1C/A, L2CL, L2CM
|
|
6263
|
+
1: { 0: ["S", "1C"] },
|
|
6264
|
+
// SBAS L1C/A
|
|
6265
|
+
2: { 0: ["E", "1X"], 1: ["E", "1X"], 5: ["E", "7X"], 6: ["E", "7X"] },
|
|
6266
|
+
// E1C/B, E5bI/Q
|
|
6267
|
+
3: { 0: ["C", "2I"], 1: ["C", "2I"], 2: ["C", "7I"], 3: ["C", "7I"] },
|
|
6268
|
+
// B1I D1/D2, B2I D1/D2
|
|
6269
|
+
5: { 0: ["J", "1C"], 4: ["J", "2X"], 5: ["J", "2X"] },
|
|
6270
|
+
// QZSS L1C/A, L2CM/CL
|
|
6271
|
+
6: { 0: ["R", "1C"], 2: ["R", "2C"] }
|
|
6272
|
+
// GLONASS L1OF, L2OF
|
|
6273
|
+
};
|
|
6274
|
+
function prnFor(gnssId, svId) {
|
|
6275
|
+
const two5 = (n) => String(n).padStart(2, "0");
|
|
6276
|
+
switch (gnssId) {
|
|
6277
|
+
case 0:
|
|
6278
|
+
return svId >= 1 && svId <= 32 ? `G${two5(svId)}` : null;
|
|
6279
|
+
case 1:
|
|
6280
|
+
return svId >= 120 && svId <= 158 ? `S${two5(svId - 100)}` : null;
|
|
6281
|
+
case 2:
|
|
6282
|
+
return svId >= 1 && svId <= 36 ? `E${two5(svId)}` : null;
|
|
6283
|
+
case 3:
|
|
6284
|
+
return svId >= 1 && svId <= 63 ? `C${two5(svId)}` : null;
|
|
6285
|
+
case 5:
|
|
6286
|
+
return svId >= 1 && svId <= 10 ? `J${two5(svId)}` : null;
|
|
6287
|
+
case 6:
|
|
6288
|
+
return svId >= 1 && svId <= 32 ? `R${two5(svId)}` : null;
|
|
6289
|
+
default:
|
|
6290
|
+
return null;
|
|
6291
|
+
}
|
|
6292
|
+
}
|
|
6293
|
+
function parseUbxRawx(data) {
|
|
6294
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6295
|
+
const epochs = [];
|
|
6296
|
+
const messageCounts = {};
|
|
6297
|
+
const obsCodes = {};
|
|
6298
|
+
const stats = { badChecksums: 0 };
|
|
6299
|
+
for (const frame of ubxFrames(data, stats)) {
|
|
6300
|
+
const { msgClass: cls, msgId: id } = frame;
|
|
6301
|
+
const len = frame.payload.length;
|
|
6302
|
+
const key = `${cls.toString(16).padStart(2, "0")}-${id.toString(16).padStart(2, "0")}`;
|
|
6303
|
+
messageCounts[key] = (messageCounts[key] ?? 0) + 1;
|
|
6304
|
+
if (cls === 2 && id === 21 && len >= 16) {
|
|
6305
|
+
const p = frame.payloadStart;
|
|
6306
|
+
const rcvTow = view.getFloat64(p, true);
|
|
6307
|
+
const week = view.getUint16(p + 8, true);
|
|
6308
|
+
const leapS = view.getInt8(p + 10);
|
|
6309
|
+
const numMeas = data[p + 11];
|
|
6310
|
+
const recStat = data[p + 12];
|
|
6311
|
+
const leapValid = (recStat & 1) !== 0;
|
|
6312
|
+
const meas = [];
|
|
6313
|
+
for (let k = 0; k < numMeas; k++) {
|
|
6314
|
+
const off = p + 16 + 32 * k;
|
|
6315
|
+
if (off + 32 > p + len) break;
|
|
6316
|
+
const gnssId = data[off + 20];
|
|
6317
|
+
const svId = data[off + 21];
|
|
6318
|
+
const sigId = data[off + 22];
|
|
6319
|
+
const trkStat = data[off + 30];
|
|
6320
|
+
const prn = prnFor(gnssId, svId);
|
|
6321
|
+
const sig = SIGNALS[gnssId]?.[sigId];
|
|
6322
|
+
if (!prn || !sig) continue;
|
|
6323
|
+
const prValid = (trkStat & 1) !== 0;
|
|
6324
|
+
const cpValid = (trkStat & 2) !== 0;
|
|
6325
|
+
const cp = view.getFloat64(off + 8, true);
|
|
6326
|
+
meas.push({
|
|
6327
|
+
prn,
|
|
6328
|
+
code: sig[1],
|
|
6329
|
+
pr: prValid ? view.getFloat64(off, true) : null,
|
|
6330
|
+
cp: cpValid && cp !== 0 ? cp : null,
|
|
6331
|
+
doppler: view.getFloat32(off + 16, true),
|
|
6332
|
+
cn0: data[off + 26],
|
|
6333
|
+
halfCycleAmbiguous: (trkStat & 4) !== 0,
|
|
6334
|
+
lockTimeMs: view.getUint16(off + 24, true)
|
|
6335
|
+
});
|
|
6336
|
+
const sys = sig[0];
|
|
6337
|
+
const codes = obsCodes[sys] ??= [];
|
|
6338
|
+
if (!codes.includes(sig[1])) codes.push(sig[1]);
|
|
6339
|
+
}
|
|
6340
|
+
epochs.push({
|
|
6341
|
+
timeMs: GPS_EPOCH_MS7 + week * MS_PER_WEEK3 + Math.round(rcvTow * 1e3),
|
|
6342
|
+
leapS: leapValid ? leapS : null,
|
|
6343
|
+
meas
|
|
6344
|
+
});
|
|
6345
|
+
}
|
|
6242
6346
|
}
|
|
6243
|
-
};
|
|
6347
|
+
return { epochs, messageCounts, obsCodes, badChecksums: stats.badChecksums };
|
|
6348
|
+
}
|
|
6244
6349
|
|
|
6245
|
-
// src/sbf/
|
|
6246
|
-
var
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6350
|
+
// src/sbf/frame.ts
|
|
6351
|
+
var CRC_TABLE = (() => {
|
|
6352
|
+
const t = new Uint16Array(256);
|
|
6353
|
+
for (let n = 0; n < 256; n++) {
|
|
6354
|
+
let c = n << 8;
|
|
6355
|
+
for (let k = 0; k < 8; k++)
|
|
6356
|
+
c = c & 32768 ? (c << 1 ^ 4129) & 65535 : c << 1 & 65535;
|
|
6357
|
+
t[n] = c;
|
|
6358
|
+
}
|
|
6359
|
+
return t;
|
|
6360
|
+
})();
|
|
6361
|
+
function crc16(data, start, end) {
|
|
6362
|
+
let crc = 0;
|
|
6363
|
+
for (let i = start; i < end; i++)
|
|
6364
|
+
crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
|
|
6365
|
+
return crc;
|
|
6366
|
+
}
|
|
6367
|
+
function scanSbfFrames(data, view, onBlock) {
|
|
6253
6368
|
let badCrc = 0;
|
|
6254
|
-
let
|
|
6255
|
-
|
|
6256
|
-
if (
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
if (!prnStr || prnStr[0] !== "E") return;
|
|
6260
|
-
const prn = parseInt(prnStr.slice(1), 10);
|
|
6261
|
-
const source = data[b + 17] & 31;
|
|
6262
|
-
const page = new Uint8Array(32);
|
|
6263
|
-
for (let k = 0; k < 8; k++) {
|
|
6264
|
-
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
6265
|
-
page[4 * k] = w >>> 24;
|
|
6266
|
-
page[4 * k + 1] = w >>> 16 & 255;
|
|
6267
|
-
page[4 * k + 2] = w >>> 8 & 255;
|
|
6268
|
-
page[4 * k + 3] = w & 255;
|
|
6369
|
+
let i = 0;
|
|
6370
|
+
while (i + 8 <= data.length) {
|
|
6371
|
+
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
6372
|
+
i++;
|
|
6373
|
+
continue;
|
|
6269
6374
|
}
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
if (!INAV_SOURCES.includes(source)) return;
|
|
6275
|
-
if (getBitU2(page, 0) !== 0 || getBitU2(page, 114) !== 1) return;
|
|
6276
|
-
if (!galInavPageCrcOk(page)) {
|
|
6277
|
-
badCrc++;
|
|
6278
|
-
return;
|
|
6279
|
-
}
|
|
6280
|
-
eph = inav.push(prn, page);
|
|
6281
|
-
} else {
|
|
6282
|
-
src = "fnav";
|
|
6283
|
-
if (!FNAV_SOURCES.includes(source)) return;
|
|
6284
|
-
if (isFnavDummy(page)) return;
|
|
6285
|
-
if (!galFnavPageCrcOk(page)) {
|
|
6286
|
-
badCrc++;
|
|
6287
|
-
return;
|
|
6288
|
-
}
|
|
6289
|
-
eph = fnav.push(prn, page);
|
|
6375
|
+
const len = view.getUint16(i + 6, true);
|
|
6376
|
+
if (len < 8 || len % 4 !== 0) {
|
|
6377
|
+
i++;
|
|
6378
|
+
continue;
|
|
6290
6379
|
}
|
|
6291
|
-
if (
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6380
|
+
if (i + len > data.length) break;
|
|
6381
|
+
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
6382
|
+
badCrc++;
|
|
6383
|
+
i++;
|
|
6384
|
+
continue;
|
|
6385
|
+
}
|
|
6386
|
+
onBlock(view.getUint16(i + 4, true) & 8191, i, len);
|
|
6387
|
+
i += len;
|
|
6388
|
+
}
|
|
6389
|
+
return badCrc;
|
|
6297
6390
|
}
|
|
6298
|
-
|
|
6299
|
-
|
|
6391
|
+
var two3 = (n) => String(n).padStart(2, "0");
|
|
6392
|
+
function svidToPrn(svid) {
|
|
6393
|
+
if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two3(svid)}` : null;
|
|
6394
|
+
if (svid <= 61) return svid - 37 <= 27 ? `R${two3(svid - 37)}` : null;
|
|
6395
|
+
if (svid <= 62) return null;
|
|
6396
|
+
if (svid <= 68) return svid - 38 <= 27 ? `R${two3(svid - 38)}` : null;
|
|
6397
|
+
if (svid <= 70) return null;
|
|
6398
|
+
if (svid <= 106) return svid - 70 <= 36 ? `E${two3(svid - 70)}` : null;
|
|
6399
|
+
if (svid <= 119) return null;
|
|
6400
|
+
if (svid <= 140) return `S${two3(svid - 100)}`;
|
|
6401
|
+
if (svid <= 180) return svid - 140 <= 50 ? `C${two3(svid - 140)}` : null;
|
|
6402
|
+
if (svid <= 190) return `J${two3(svid - 180)}`;
|
|
6403
|
+
if (svid <= 197) return svid - 190 <= 14 ? `I${two3(svid - 190)}` : null;
|
|
6404
|
+
if (svid <= 215) return `S${two3(svid - 157)}`;
|
|
6405
|
+
if (svid <= 222) return svid - 208 <= 14 ? `I${two3(svid - 208)}` : null;
|
|
6406
|
+
if (svid <= 245) return svid - 182 <= 50 ? `C${two3(svid - 182)}` : null;
|
|
6407
|
+
return null;
|
|
6300
6408
|
}
|
|
6301
6409
|
|
|
6302
|
-
// src/
|
|
6410
|
+
// src/sbf/nav.ts
|
|
6411
|
+
var PI2 = Math.PI;
|
|
6412
|
+
var GPS_EPOCH_MS8 = Date.UTC(1980, 0, 6);
|
|
6303
6413
|
var BDT_EPOCH_MS2 = Date.UTC(2006, 0, 1);
|
|
6304
|
-
var
|
|
6305
|
-
var MS_PER_WEEK4 =
|
|
6306
|
-
var
|
|
6307
|
-
var
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
return getBitS(b, p1, l1) * 2 ** l2 + getBitU(b, p2, l2);
|
|
6314
|
-
}
|
|
6315
|
-
function getBitU3(b, p1, l1, p2, l2, p3, l3) {
|
|
6316
|
-
return getBitU(b, p1, l1) * 2 ** (l2 + l3) + getBitU(b, p2, l2) * 2 ** l3 + getBitU(b, p3, l3);
|
|
6317
|
-
}
|
|
6318
|
-
function getBitS3(b, p1, l1, p2, l2, p3, l3) {
|
|
6319
|
-
return getBitS(b, p1, l1) * 2 ** (l2 + l3) + getBitU(b, p2, l2) * 2 ** l3 + getBitU(b, p3, l3);
|
|
6414
|
+
var SEC_PER_WEEK8 = 7 * 86400;
|
|
6415
|
+
var MS_PER_WEEK4 = SEC_PER_WEEK8 * 1e3;
|
|
6416
|
+
var F4_DNU = -2e10;
|
|
6417
|
+
var U4_DNU = 4294967295;
|
|
6418
|
+
function adjustWeek(refWeek, wn, mod) {
|
|
6419
|
+
let offset = refWeek % mod - wn;
|
|
6420
|
+
if (offset > mod / 2) offset -= mod;
|
|
6421
|
+
if (offset < -(mod / 2 - 1)) offset += mod;
|
|
6422
|
+
return refWeek - offset;
|
|
6320
6423
|
}
|
|
6321
|
-
var
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6424
|
+
var gpsMs = (week, sec) => GPS_EPOCH_MS8 + week * MS_PER_WEEK4 + sec * 1e3;
|
|
6425
|
+
var sowOf2 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK8;
|
|
6426
|
+
function decodeGpsQzsNav(view, b, sys) {
|
|
6427
|
+
const svid = view.getUint8(b + 14);
|
|
6428
|
+
const prn = sys === "G" ? svid : svid - 180;
|
|
6429
|
+
const max = sys === "G" ? 32 : 10;
|
|
6430
|
+
if (prn < 1 || prn > max) return null;
|
|
6431
|
+
const wnc = view.getUint16(b + 12, true);
|
|
6432
|
+
const tgdRaw = view.getFloat32(b + 28, true);
|
|
6433
|
+
const tocs = view.getUint32(b + 32, true);
|
|
6434
|
+
const toes = view.getUint32(b + 88, true);
|
|
6435
|
+
const wnToc = adjustWeek(wnc, view.getUint16(b + 136, true), 1024);
|
|
6436
|
+
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
6437
|
+
return {
|
|
6438
|
+
system: sys,
|
|
6439
|
+
prn: `${sys}${String(prn).padStart(2, "0")}`,
|
|
6440
|
+
toc: sowOf2(tocDate.getTime()),
|
|
6441
|
+
tocDate,
|
|
6442
|
+
af0: view.getFloat32(b + 44, true),
|
|
6443
|
+
af1: view.getFloat32(b + 40, true),
|
|
6444
|
+
af2: view.getFloat32(b + 36, true),
|
|
6445
|
+
iode: view.getUint8(b + 24),
|
|
6446
|
+
crs: view.getFloat32(b + 48, true),
|
|
6447
|
+
deltaN: view.getFloat32(b + 52, true) * PI2,
|
|
6448
|
+
m0: view.getFloat64(b + 56, true) * PI2,
|
|
6449
|
+
cuc: view.getFloat32(b + 64, true),
|
|
6450
|
+
e: view.getFloat64(b + 68, true),
|
|
6451
|
+
cus: view.getFloat32(b + 76, true),
|
|
6452
|
+
sqrtA: view.getFloat64(b + 80, true),
|
|
6453
|
+
toe: toes,
|
|
6454
|
+
cic: view.getFloat32(b + 92, true),
|
|
6455
|
+
omega0: view.getFloat64(b + 96, true) * PI2,
|
|
6456
|
+
cis: view.getFloat32(b + 104, true),
|
|
6457
|
+
i0: view.getFloat64(b + 108, true) * PI2,
|
|
6458
|
+
crc: view.getFloat32(b + 116, true),
|
|
6459
|
+
omega: view.getFloat64(b + 120, true) * PI2,
|
|
6460
|
+
omegaDot: view.getFloat32(b + 128, true) * PI2,
|
|
6461
|
+
idot: view.getFloat32(b + 132, true) * PI2,
|
|
6462
|
+
// RINEX "GPS week to go with toe": RTKLIB writes WNc for GPS and
|
|
6463
|
+
// the week of toc for QZSS — mirrored here so records compare
|
|
6464
|
+
// 1:1 against convbin output. wnToe is still used for `toe` above.
|
|
6465
|
+
week: sys === "G" ? wnc : wnToc,
|
|
6466
|
+
svHealth: view.getUint8(b + 20),
|
|
6467
|
+
tgd: tgdRaw !== F4_DNU ? tgdRaw : 0
|
|
6468
|
+
};
|
|
6327
6469
|
}
|
|
6328
|
-
function
|
|
6329
|
-
|
|
6330
|
-
if (
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6470
|
+
function decodeGalNav(view, b) {
|
|
6471
|
+
const prn = view.getUint8(b + 14) - 70;
|
|
6472
|
+
if (prn < 1 || prn > 36) return null;
|
|
6473
|
+
const source = view.getUint8(b + 15);
|
|
6474
|
+
if (source !== 2 && source !== 16) return null;
|
|
6475
|
+
const wnc = view.getUint16(b + 12, true);
|
|
6476
|
+
const toes = view.getUint32(b + 100, true);
|
|
6477
|
+
const tocs = view.getUint32(b + 104, true);
|
|
6478
|
+
const wnToc = adjustWeek(wnc, view.getUint16(b + 126, true), 4096);
|
|
6479
|
+
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
6480
|
+
const health = view.getUint16(b + 130, true);
|
|
6481
|
+
let svh = 0;
|
|
6482
|
+
if (health & 1) svh |= health >> 1 & 7;
|
|
6483
|
+
if (health & 16) svh |= (health >> 5 & 7) << 6;
|
|
6484
|
+
if (health & 256) svh |= (health >> 9 & 7) << 3;
|
|
6485
|
+
const bgdE5a = view.getFloat32(b + 136, true);
|
|
6486
|
+
return {
|
|
6487
|
+
system: "E",
|
|
6488
|
+
prn: `E${String(prn).padStart(2, "0")}`,
|
|
6489
|
+
toc: sowOf2(tocDate.getTime()),
|
|
6490
|
+
tocDate,
|
|
6491
|
+
af0: view.getFloat64(b + 116, true),
|
|
6492
|
+
af1: view.getFloat32(b + 112, true),
|
|
6493
|
+
af2: view.getFloat32(b + 108, true),
|
|
6494
|
+
iode: view.getUint16(b + 128, true),
|
|
6495
|
+
// IODnav
|
|
6496
|
+
crs: view.getFloat32(b + 88, true),
|
|
6497
|
+
deltaN: view.getFloat32(b + 72, true) * PI2,
|
|
6498
|
+
m0: view.getFloat64(b + 24, true) * PI2,
|
|
6499
|
+
cuc: view.getFloat32(b + 76, true),
|
|
6500
|
+
e: view.getFloat64(b + 32, true),
|
|
6501
|
+
cus: view.getFloat32(b + 80, true),
|
|
6502
|
+
sqrtA: view.getFloat64(b + 16, true),
|
|
6503
|
+
toe: toes,
|
|
6504
|
+
cic: view.getFloat32(b + 92, true),
|
|
6505
|
+
omega0: view.getFloat64(b + 56, true) * PI2,
|
|
6506
|
+
cis: view.getFloat32(b + 96, true),
|
|
6507
|
+
i0: view.getFloat64(b + 40, true) * PI2,
|
|
6508
|
+
crc: view.getFloat32(b + 84, true),
|
|
6509
|
+
omega: view.getFloat64(b + 48, true) * PI2,
|
|
6510
|
+
omegaDot: view.getFloat32(b + 64, true) * PI2,
|
|
6511
|
+
idot: view.getFloat32(b + 68, true) * PI2,
|
|
6512
|
+
week: wnc,
|
|
6513
|
+
// RINEX GAL week is GPS-aligned and continuous
|
|
6514
|
+
svHealth: svh,
|
|
6515
|
+
tgd: bgdE5a !== F4_DNU ? bgdE5a : 0
|
|
6516
|
+
// BGD E5a/E1 (RINEX slot)
|
|
6517
|
+
};
|
|
6338
6518
|
}
|
|
6339
|
-
function
|
|
6340
|
-
|
|
6341
|
-
if (
|
|
6342
|
-
|
|
6343
|
-
const
|
|
6519
|
+
function decodeBdsNav(view, b) {
|
|
6520
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
6521
|
+
if (!prn || prn[0] !== "C") return null;
|
|
6522
|
+
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
6523
|
+
const tgd1 = view.getFloat32(b + 24, true);
|
|
6524
|
+
const tocs = view.getUint32(b + 32, true);
|
|
6525
|
+
const toes = view.getUint32(b + 88, true);
|
|
6526
|
+
const wnToc = adjustWeek(bdsWeekRef, view.getUint16(b + 136, true), 8192);
|
|
6527
|
+
const wnToe = adjustWeek(bdsWeekRef, view.getUint16(b + 138, true), 8192);
|
|
6528
|
+
const tocDate = new Date(BDT_EPOCH_MS2 + wnToc * MS_PER_WEEK4 + tocs * 1e3);
|
|
6344
6529
|
return {
|
|
6345
6530
|
system: "C",
|
|
6346
6531
|
prn,
|
|
6347
6532
|
toc: sowOf2(tocDate.getTime()),
|
|
6348
6533
|
tocDate,
|
|
6349
|
-
af0:
|
|
6350
|
-
af1:
|
|
6351
|
-
af2:
|
|
6352
|
-
|
|
6353
|
-
//
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
week
|
|
6372
|
-
|
|
6373
|
-
svHealth: f.svh,
|
|
6534
|
+
af0: view.getFloat32(b + 44, true),
|
|
6535
|
+
af1: view.getFloat32(b + 40, true),
|
|
6536
|
+
af2: view.getFloat32(b + 36, true),
|
|
6537
|
+
iode: view.getUint8(b + 21),
|
|
6538
|
+
// AODE
|
|
6539
|
+
crs: view.getFloat32(b + 48, true),
|
|
6540
|
+
deltaN: view.getFloat32(b + 52, true) * PI2,
|
|
6541
|
+
m0: view.getFloat64(b + 56, true) * PI2,
|
|
6542
|
+
cuc: view.getFloat32(b + 64, true),
|
|
6543
|
+
e: view.getFloat64(b + 68, true),
|
|
6544
|
+
cus: view.getFloat32(b + 76, true),
|
|
6545
|
+
sqrtA: view.getFloat64(b + 80, true),
|
|
6546
|
+
toe: toes,
|
|
6547
|
+
cic: view.getFloat32(b + 92, true),
|
|
6548
|
+
omega0: view.getFloat64(b + 96, true) * PI2,
|
|
6549
|
+
cis: view.getFloat32(b + 104, true),
|
|
6550
|
+
i0: view.getFloat64(b + 108, true) * PI2,
|
|
6551
|
+
crc: view.getFloat32(b + 116, true),
|
|
6552
|
+
omega: view.getFloat64(b + 120, true) * PI2,
|
|
6553
|
+
omegaDot: view.getFloat32(b + 128, true) * PI2,
|
|
6554
|
+
idot: view.getFloat32(b + 132, true) * PI2,
|
|
6555
|
+
week: wnToe,
|
|
6556
|
+
// RINEX BDS week field is the BDT week
|
|
6557
|
+
svHealth: view.getUint8(b + 19),
|
|
6374
6558
|
// SatH1
|
|
6375
|
-
tgd:
|
|
6376
|
-
// TGD1
|
|
6559
|
+
tgd: tgd1 !== F4_DNU ? tgd1 : 0
|
|
6560
|
+
// TGD1 B1/B3 (RINEX slot)
|
|
6377
6561
|
};
|
|
6378
6562
|
}
|
|
6379
|
-
function
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
const
|
|
6384
|
-
const
|
|
6385
|
-
const
|
|
6386
|
-
const
|
|
6387
|
-
const
|
|
6388
|
-
const
|
|
6389
|
-
const
|
|
6390
|
-
const
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
const omega0 = getBitS2(b, i + 211, 21, i + 240, 11) * 2 ** -31 * GPS_PI;
|
|
6414
|
-
const omega = getBitS2(b, i + 251, 11, i + 270, 21) * 2 ** -31 * GPS_PI;
|
|
6415
|
-
const toes = (toe1 * 2 ** 15 + toe2) * 8;
|
|
6416
|
-
if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3) return null;
|
|
6417
|
-
if (sow2 !== sow1 + 6 || sow3 !== sow2 + 6) return null;
|
|
6418
|
-
if (tocSec !== toes) return null;
|
|
6419
|
-
return buildBdsEphemeris(opts.prn ?? "C00", {
|
|
6420
|
-
week,
|
|
6421
|
-
sow: sow1,
|
|
6422
|
-
toes,
|
|
6423
|
-
tocSec,
|
|
6424
|
-
svh,
|
|
6425
|
-
tgd1,
|
|
6426
|
-
af0,
|
|
6427
|
-
af1,
|
|
6428
|
-
af2,
|
|
6429
|
-
crs,
|
|
6430
|
-
deltaN,
|
|
6431
|
-
m0,
|
|
6432
|
-
cuc,
|
|
6433
|
-
e,
|
|
6434
|
-
cus,
|
|
6435
|
-
sqrtA,
|
|
6436
|
-
cic,
|
|
6437
|
-
omega0,
|
|
6438
|
-
cis,
|
|
6439
|
-
i0,
|
|
6440
|
-
crc,
|
|
6441
|
-
omega,
|
|
6442
|
-
omegaDot,
|
|
6443
|
-
idot
|
|
6444
|
-
});
|
|
6563
|
+
function decodeGloNav(view, b) {
|
|
6564
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
6565
|
+
if (!prn || prn[0] !== "R") return null;
|
|
6566
|
+
const wnc = view.getUint16(b + 12, true);
|
|
6567
|
+
const toes = view.getUint32(b + 76, true);
|
|
6568
|
+
const wnToe = adjustWeek(wnc, view.getUint16(b + 80, true), 1024);
|
|
6569
|
+
const toeGpsMs = gpsMs(wnToe, toes);
|
|
6570
|
+
const leapMs = getGpsLeap(new Date(toeGpsMs)) * 1e3;
|
|
6571
|
+
const tocDate = new Date(toeGpsMs - leapMs);
|
|
6572
|
+
const tofGpsMs = gpsMs(wnc, 0) + view.getUint32(b + 8, true);
|
|
6573
|
+
const tofLeapMs = getGpsLeap(new Date(tofGpsMs)) * 1e3;
|
|
6574
|
+
const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS8) / 1e3 % SEC_PER_WEEK8;
|
|
6575
|
+
return {
|
|
6576
|
+
system: "R",
|
|
6577
|
+
prn,
|
|
6578
|
+
tocDate,
|
|
6579
|
+
// RINEX stores −τn as the clock bias; SBF carries τn (ICD sign)
|
|
6580
|
+
tauN: -view.getFloat32(b + 68, true),
|
|
6581
|
+
gammaN: view.getFloat32(b + 64, true),
|
|
6582
|
+
messageFrameTime,
|
|
6583
|
+
x: view.getFloat64(b + 16, true),
|
|
6584
|
+
// SBF unit is km, like RINEX
|
|
6585
|
+
xDot: view.getFloat32(b + 40, true),
|
|
6586
|
+
xAcc: view.getFloat32(b + 52, true),
|
|
6587
|
+
y: view.getFloat64(b + 24, true),
|
|
6588
|
+
yDot: view.getFloat32(b + 44, true),
|
|
6589
|
+
yAcc: view.getFloat32(b + 56, true),
|
|
6590
|
+
z: view.getFloat64(b + 32, true),
|
|
6591
|
+
zDot: view.getFloat32(b + 48, true),
|
|
6592
|
+
zAcc: view.getFloat32(b + 60, true),
|
|
6593
|
+
// MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
|
|
6594
|
+
health: view.getUint8(b + 85) >> 2,
|
|
6595
|
+
freqNum: view.getUint8(b + 15) - 8
|
|
6596
|
+
};
|
|
6445
6597
|
}
|
|
6446
|
-
function
|
|
6447
|
-
|
|
6448
|
-
const
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
const pgn3 = getBitU(b, i + 42, 4);
|
|
6458
|
-
const sow3 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6459
|
-
const af0 = getBitS2(b, i + 100, 12, i + 120, 12) * 2 ** -33;
|
|
6460
|
-
const af1p3 = getBitS(b, i + 132, 4);
|
|
6461
|
-
i = 8 * 38 * 3;
|
|
6462
|
-
const pgn4 = getBitU(b, i + 42, 4);
|
|
6463
|
-
const sow4 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6464
|
-
const af1p4 = getBitU22(b, i + 46, 6, i + 60, 12);
|
|
6465
|
-
const af2 = getBitS2(b, i + 72, 10, i + 90, 1) * 2 ** -66;
|
|
6466
|
-
const deltaN = getBitS(b, i + 96, 16) * 2 ** -43 * GPS_PI;
|
|
6467
|
-
const cucp4 = getBitS(b, i + 120, 14);
|
|
6468
|
-
i = 8 * 38 * 4;
|
|
6469
|
-
const pgn5 = getBitU(b, i + 42, 4);
|
|
6470
|
-
const sow5 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6471
|
-
const cucp5 = getBitU(b, i + 46, 4);
|
|
6472
|
-
const m0 = getBitS3(b, i + 50, 2, i + 60, 22, i + 90, 8) * 2 ** -31 * GPS_PI;
|
|
6473
|
-
const cus = getBitS2(b, i + 98, 14, i + 120, 4) * 2 ** -31;
|
|
6474
|
-
const ep5 = getBitS(b, i + 124, 10);
|
|
6475
|
-
i = 8 * 38 * 5;
|
|
6476
|
-
const pgn6 = getBitU(b, i + 42, 4);
|
|
6477
|
-
const sow6 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6478
|
-
const ep6 = getBitU22(b, i + 46, 6, i + 60, 16);
|
|
6479
|
-
const sqrtA = getBitU3(b, i + 76, 6, i + 90, 22, i + 120, 4) * 2 ** -19;
|
|
6480
|
-
const cicp6 = getBitS(b, i + 124, 10);
|
|
6481
|
-
i = 8 * 38 * 6;
|
|
6482
|
-
const pgn7 = getBitU(b, i + 42, 4);
|
|
6483
|
-
const sow7 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6484
|
-
const cicp7 = getBitU22(b, i + 46, 6, i + 60, 2);
|
|
6485
|
-
const cis = getBitS(b, i + 62, 18) * 2 ** -31;
|
|
6486
|
-
const toes = getBitU22(b, i + 80, 2, i + 90, 15) * 8;
|
|
6487
|
-
const i0p7 = getBitS2(b, i + 105, 7, i + 120, 14);
|
|
6488
|
-
i = 8 * 38 * 7;
|
|
6489
|
-
const pgn8 = getBitU(b, i + 42, 4);
|
|
6490
|
-
const sow8 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6491
|
-
const i0p8 = getBitU22(b, i + 46, 6, i + 60, 5);
|
|
6492
|
-
const crc = getBitS2(b, i + 65, 17, i + 90, 1) * 2 ** -6;
|
|
6493
|
-
const crs = getBitS(b, i + 91, 18) * 2 ** -6;
|
|
6494
|
-
const omegaDotP8 = getBitS2(b, i + 109, 3, i + 120, 16);
|
|
6495
|
-
i = 8 * 38 * 8;
|
|
6496
|
-
const pgn9 = getBitU(b, i + 42, 4);
|
|
6497
|
-
const sow9 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6498
|
-
const omegaDotP9 = getBitU(b, i + 46, 5);
|
|
6499
|
-
const omega0 = getBitS3(b, i + 51, 1, i + 60, 22, i + 90, 9) * 2 ** -31 * GPS_PI;
|
|
6500
|
-
const omegaP9 = getBitS2(b, i + 99, 13, i + 120, 14);
|
|
6501
|
-
i = 8 * 38 * 9;
|
|
6502
|
-
const pgn10 = getBitU(b, i + 42, 4);
|
|
6503
|
-
const sow10 = getBitU22(b, i + 18, 8, i + 30, 12);
|
|
6504
|
-
const omegaP10 = getBitU(b, i + 46, 5);
|
|
6505
|
-
const idot = getBitS2(b, i + 51, 1, i + 60, 13) * 2 ** -43 * GPS_PI;
|
|
6506
|
-
if (pgn1 !== 1 || pgn3 !== 3 || pgn4 !== 4 || pgn5 !== 5 || pgn6 !== 6 || pgn7 !== 7 || pgn8 !== 8 || pgn9 !== 9 || pgn10 !== 10) {
|
|
6507
|
-
return null;
|
|
6508
|
-
}
|
|
6509
|
-
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) {
|
|
6510
|
-
return null;
|
|
6511
|
-
}
|
|
6512
|
-
if (tocSec !== toes) return null;
|
|
6513
|
-
return buildBdsEphemeris(opts.prn ?? "C00", {
|
|
6514
|
-
week,
|
|
6515
|
-
sow: sow1,
|
|
6516
|
-
toes,
|
|
6517
|
-
tocSec,
|
|
6518
|
-
svh,
|
|
6519
|
-
tgd1,
|
|
6520
|
-
af0,
|
|
6521
|
-
af1: mergeS(af1p3, af1p4, 18) * 2 ** -50,
|
|
6522
|
-
af2,
|
|
6523
|
-
crs,
|
|
6524
|
-
deltaN,
|
|
6525
|
-
m0,
|
|
6526
|
-
cuc: mergeS(cucp4, cucp5, 4) * 2 ** -31,
|
|
6527
|
-
e: mergeS(ep5, ep6, 22) * 2 ** -33,
|
|
6528
|
-
cus,
|
|
6529
|
-
sqrtA,
|
|
6530
|
-
cic: mergeS(cicp6, cicp7, 8) * 2 ** -31,
|
|
6531
|
-
omega0,
|
|
6532
|
-
cis,
|
|
6533
|
-
i0: mergeS(i0p7, i0p8, 11) * 2 ** -31 * GPS_PI,
|
|
6534
|
-
crc,
|
|
6535
|
-
omega: mergeS(omegaP9, omegaP10, 5) * 2 ** -31 * GPS_PI,
|
|
6536
|
-
omegaDot: mergeS(omegaDotP8, omegaDotP9, 5) * 2 ** -43 * GPS_PI,
|
|
6537
|
-
idot
|
|
6598
|
+
function parseSbfNav(data) {
|
|
6599
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6600
|
+
const ephemerides = [];
|
|
6601
|
+
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
6602
|
+
let eph = null;
|
|
6603
|
+
if (id === 5891 && len >= 140) eph = decodeGpsQzsNav(view, b, "G");
|
|
6604
|
+
else if (id === 4095 && len >= 140) eph = decodeGpsQzsNav(view, b, "J");
|
|
6605
|
+
else if (id === 4002 && len >= 149) eph = decodeGalNav(view, b);
|
|
6606
|
+
else if (id === 4081 && len >= 140) eph = decodeBdsNav(view, b);
|
|
6607
|
+
else if (id === 4004 && len >= 96) eph = decodeGloNav(view, b);
|
|
6608
|
+
if (eph) ephemerides.push(eph);
|
|
6538
6609
|
});
|
|
6610
|
+
return { ephemerides, badCrc };
|
|
6539
6611
|
}
|
|
6540
|
-
|
|
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
|
-
} else {
|
|
6566
|
-
if (id !== 1) return null;
|
|
6567
|
-
const pgn = getBitU(subframe, 42, 4);
|
|
6568
|
-
if (pgn < 1 || pgn > 10) return null;
|
|
6569
|
-
sat.buf.set(subframe.subarray(0, BDS_SUBFRAME_BYTES), (pgn - 1) * 38);
|
|
6570
|
-
if (pgn === 10) eph = decodeBdsD2Frame(sat.buf, { prn });
|
|
6571
|
-
}
|
|
6572
|
-
if (!eph) return null;
|
|
6573
|
-
const key = `${eph.week}:${eph.toe}`;
|
|
6574
|
-
if (key === sat.lastKey) return null;
|
|
6575
|
-
sat.lastKey = key;
|
|
6576
|
-
return eph;
|
|
6612
|
+
var GAL_SQRT_A_NOMINAL = Math.sqrt(296e5);
|
|
6613
|
+
var GAL_I_NOMINAL = 56 / 180 * PI2;
|
|
6614
|
+
var I_REF_03 = 0.3 * PI2;
|
|
6615
|
+
function decodeGpsGalAlm(view, b, sys) {
|
|
6616
|
+
const wnc = view.getUint16(b + 12, true);
|
|
6617
|
+
const toa = view.getUint32(b + 20, true);
|
|
6618
|
+
if (toa === U4_DNU) return null;
|
|
6619
|
+
let prn;
|
|
6620
|
+
let weekAlm;
|
|
6621
|
+
let i0;
|
|
6622
|
+
let sqrtA = view.getFloat32(b + 32, true);
|
|
6623
|
+
let health;
|
|
6624
|
+
if (sys === "G") {
|
|
6625
|
+
prn = view.getUint8(b + 14);
|
|
6626
|
+
if (prn < 1 || prn > 32) return null;
|
|
6627
|
+
weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 256);
|
|
6628
|
+
i0 = I_REF_03 + view.getFloat32(b + 24, true) * PI2;
|
|
6629
|
+
health = view.getUint8(b + 58);
|
|
6630
|
+
} else {
|
|
6631
|
+
prn = view.getUint8(b + 57) - 70;
|
|
6632
|
+
if (prn < 1 || prn > 36) return null;
|
|
6633
|
+
weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 4);
|
|
6634
|
+
i0 = GAL_I_NOMINAL + view.getFloat32(b + 24, true) * PI2;
|
|
6635
|
+
sqrtA += GAL_SQRT_A_NOMINAL;
|
|
6636
|
+
health = view.getUint16(b + 58, true);
|
|
6577
6637
|
}
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6638
|
+
return {
|
|
6639
|
+
system: sys,
|
|
6640
|
+
prn: `${sys}${String(prn).padStart(2, "0")}`,
|
|
6641
|
+
weekAlm,
|
|
6642
|
+
toaSec: toa,
|
|
6643
|
+
sqrtA,
|
|
6644
|
+
e: view.getFloat32(b + 16, true),
|
|
6645
|
+
i0OrDeltaI: i0,
|
|
6646
|
+
omega0: view.getFloat32(b + 36, true) * PI2,
|
|
6647
|
+
omega: view.getFloat32(b + 40, true) * PI2,
|
|
6648
|
+
m0: view.getFloat32(b + 44, true) * PI2,
|
|
6649
|
+
omegaDot: view.getFloat32(b + 28, true) * PI2,
|
|
6650
|
+
af0: view.getFloat32(b + 52, true),
|
|
6651
|
+
af1: view.getFloat32(b + 48, true),
|
|
6652
|
+
health
|
|
6653
|
+
};
|
|
6588
6654
|
}
|
|
6589
|
-
|
|
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
|
-
return n === 0 || n === 2 && cs === 1;
|
|
6655
|
+
function decodeBdsAlm(view, b) {
|
|
6656
|
+
const prnStr = svidToPrn(view.getUint8(b + 14));
|
|
6657
|
+
if (!prnStr || prnStr[0] !== "C") return null;
|
|
6658
|
+
const prn = parseInt(prnStr.slice(1), 10);
|
|
6659
|
+
const toa = view.getUint32(b + 16, true);
|
|
6660
|
+
if (toa === U4_DNU) return null;
|
|
6661
|
+
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
6662
|
+
const geo = prn <= 5 || prn >= 59;
|
|
6663
|
+
return {
|
|
6664
|
+
system: "C",
|
|
6665
|
+
prn: prnStr,
|
|
6666
|
+
weekAlm: adjustWeek(bdsWeekRef, view.getUint8(b + 15), 256),
|
|
6667
|
+
toaSec: toa,
|
|
6668
|
+
sqrtA: view.getFloat32(b + 20, true),
|
|
6669
|
+
e: view.getFloat32(b + 24, true),
|
|
6670
|
+
i0OrDeltaI: (geo ? 0 : I_REF_03) + view.getFloat32(b + 44, true) * PI2,
|
|
6671
|
+
omega0: view.getFloat32(b + 36, true) * PI2,
|
|
6672
|
+
omega: view.getFloat32(b + 28, true) * PI2,
|
|
6673
|
+
m0: view.getFloat32(b + 32, true) * PI2,
|
|
6674
|
+
omegaDot: view.getFloat32(b + 40, true) * PI2,
|
|
6675
|
+
af0: view.getFloat32(b + 48, true),
|
|
6676
|
+
af1: view.getFloat32(b + 52, true),
|
|
6677
|
+
health: view.getUint16(b + 56, true)
|
|
6678
|
+
};
|
|
6615
6679
|
}
|
|
6616
|
-
function
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
const
|
|
6621
|
-
|
|
6622
|
-
i += 2;
|
|
6623
|
-
const tkH = getBitU(b, i, 5);
|
|
6624
|
-
i += 5;
|
|
6625
|
-
const tkM = getBitU(b, i, 6);
|
|
6626
|
-
i += 6;
|
|
6627
|
-
const tkS = getBitU(b, i, 1) * 30;
|
|
6628
|
-
i += 1;
|
|
6629
|
-
const xDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6630
|
-
i += 24;
|
|
6631
|
-
const xAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6632
|
-
i += 5;
|
|
6633
|
-
const x = getBitG(b, i, 27) * 2 ** -11;
|
|
6634
|
-
i = 80 + 1;
|
|
6635
|
-
const frn2 = getBitU(b, i, 4);
|
|
6636
|
-
i += 4;
|
|
6637
|
-
const bn = getBitU(b, i, 1);
|
|
6638
|
-
i += 1 + 2;
|
|
6639
|
-
i += 1;
|
|
6640
|
-
const tb = getBitU(b, i, 7);
|
|
6641
|
-
i += 7 + 5;
|
|
6642
|
-
const yDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6643
|
-
i += 24;
|
|
6644
|
-
const yAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6645
|
-
i += 5;
|
|
6646
|
-
const y = getBitG(b, i, 27) * 2 ** -11;
|
|
6647
|
-
i = 160 + 1;
|
|
6648
|
-
const frn3 = getBitU(b, i, 4);
|
|
6649
|
-
i += 4;
|
|
6650
|
-
i += 1;
|
|
6651
|
-
const gammaN = getBitG(b, i, 11) * 2 ** -40;
|
|
6652
|
-
i += 11 + 1;
|
|
6653
|
-
i += 2;
|
|
6654
|
-
i += 1;
|
|
6655
|
-
const zDot = getBitG(b, i, 24) * 2 ** -20;
|
|
6656
|
-
i += 24;
|
|
6657
|
-
const zAcc = getBitG(b, i, 5) * 2 ** -30;
|
|
6658
|
-
i += 5;
|
|
6659
|
-
const z = getBitG(b, i, 27) * 2 ** -11;
|
|
6660
|
-
i = 240 + 1;
|
|
6661
|
-
const frn4 = getBitU(b, i, 4);
|
|
6662
|
-
i += 4;
|
|
6663
|
-
const tauN = getBitG(b, i, 22) * 2 ** -30;
|
|
6664
|
-
i += 22;
|
|
6665
|
-
i += 5;
|
|
6666
|
-
i += 5;
|
|
6667
|
-
i += 14;
|
|
6668
|
-
i += 1;
|
|
6669
|
-
i += 4;
|
|
6670
|
-
i += 3;
|
|
6671
|
-
i += 11;
|
|
6672
|
-
const slot = getBitU(b, i, 5);
|
|
6673
|
-
if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3 || frn4 !== 4) return null;
|
|
6674
|
-
if (slot < 1 || slot > 27) return null;
|
|
6675
|
-
const utcSec = (getUtcDate(refDate).getTime() - GPS_EPOCH_MS7) / 1e3;
|
|
6676
|
-
const week = Math.floor(utcSec / SEC_PER_WEEK7);
|
|
6677
|
-
let tow = utcSec - week * SEC_PER_WEEK7;
|
|
6678
|
-
const tod = tow % SEC_PER_DAY;
|
|
6679
|
-
tow -= tod;
|
|
6680
|
-
let tof = tkH * 3600 + tkM * 60 + tkS - 10800;
|
|
6681
|
-
if (tof < tod - 43200) tof += SEC_PER_DAY;
|
|
6682
|
-
else if (tof > tod + 43200) tof -= SEC_PER_DAY;
|
|
6683
|
-
let toe = tb * 900 - 10800;
|
|
6684
|
-
if (toe < tod - 43200) toe += SEC_PER_DAY;
|
|
6685
|
-
else if (toe > tod + 43200) toe -= SEC_PER_DAY;
|
|
6680
|
+
function decodeGloAlm(view, b) {
|
|
6681
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
6682
|
+
if (!prn || prn[0] !== "R") return null;
|
|
6683
|
+
const wnc = view.getUint16(b + 12, true);
|
|
6684
|
+
const toa = view.getUint32(b + 20, true);
|
|
6685
|
+
if (toa === U4_DNU) return null;
|
|
6686
6686
|
return {
|
|
6687
6687
|
system: "R",
|
|
6688
|
-
prn
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
zDot,
|
|
6704
|
-
zAcc,
|
|
6705
|
-
// MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
|
|
6706
|
-
health: bn,
|
|
6707
|
-
freqNum: opts.freqNum ?? 0
|
|
6688
|
+
prn,
|
|
6689
|
+
freqNr: view.getUint8(b + 15) - 8,
|
|
6690
|
+
weekAlm: adjustWeek(wnc, view.getUint8(b + 52), 256),
|
|
6691
|
+
toaSec: toa,
|
|
6692
|
+
epsilon: view.getFloat32(b + 16, true),
|
|
6693
|
+
deltaI: view.getFloat32(b + 24, true) * PI2,
|
|
6694
|
+
lambda: view.getFloat32(b + 28, true) * PI2,
|
|
6695
|
+
tLambda: view.getFloat32(b + 32, true),
|
|
6696
|
+
omega: view.getFloat32(b + 36, true) * PI2,
|
|
6697
|
+
deltaT: view.getFloat32(b + 40, true),
|
|
6698
|
+
deltaTDot: view.getFloat32(b + 44, true),
|
|
6699
|
+
tau: view.getFloat32(b + 48, true),
|
|
6700
|
+
health: view.getUint8(b + 53),
|
|
6701
|
+
nDay: view.getUint16(b + 54, true),
|
|
6702
|
+
n4: view.getUint8(b + 57)
|
|
6708
6703
|
};
|
|
6709
6704
|
}
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
if (
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6705
|
+
function parseSbfAlmanac(data) {
|
|
6706
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6707
|
+
const almanacs = [];
|
|
6708
|
+
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
6709
|
+
let alm = null;
|
|
6710
|
+
if (id === 5892 && len >= 60) alm = decodeGpsGalAlm(view, b, "G");
|
|
6711
|
+
else if (id === 4003 && len >= 61) alm = decodeGpsGalAlm(view, b, "E");
|
|
6712
|
+
else if (id === 4119 && len >= 60) alm = decodeBdsAlm(view, b);
|
|
6713
|
+
else if (id === 4005 && len >= 60) alm = decodeGloAlm(view, b);
|
|
6714
|
+
if (alm) almanacs.push(alm);
|
|
6715
|
+
});
|
|
6716
|
+
return { almanacs, badCrc };
|
|
6717
|
+
}
|
|
6718
|
+
|
|
6719
|
+
// src/sbf/iono.ts
|
|
6720
|
+
var F4_DNU2 = -2e10;
|
|
6721
|
+
function parseSbfIonoUtc(data) {
|
|
6722
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6723
|
+
const ionoCorrections = {};
|
|
6724
|
+
let leapSeconds = null;
|
|
6725
|
+
const f4s = (b, n) => {
|
|
6726
|
+
const out = [];
|
|
6727
|
+
for (let k = 0; k < n; k++) {
|
|
6728
|
+
const v = view.getFloat32(b + 4 * k, true);
|
|
6729
|
+
if (v === F4_DNU2) return null;
|
|
6730
|
+
out.push(v);
|
|
6730
6731
|
}
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6732
|
+
return out;
|
|
6733
|
+
};
|
|
6734
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
6735
|
+
if ((id === 5893 || id === 4120) && len >= 48) {
|
|
6736
|
+
const alpha = f4s(b + 16, 4);
|
|
6737
|
+
const beta = f4s(b + 32, 4);
|
|
6738
|
+
if (!alpha || !beta) return;
|
|
6739
|
+
const sys = id === 5893 ? "GPS" : "BDS";
|
|
6740
|
+
ionoCorrections[`${sys}A`] = alpha;
|
|
6741
|
+
ionoCorrections[`${sys}B`] = beta;
|
|
6742
|
+
} else if (id === 4030 && len >= 28) {
|
|
6743
|
+
const ai = f4s(b + 16, 3);
|
|
6744
|
+
if (ai) ionoCorrections["GAL"] = ai;
|
|
6745
|
+
} else if (id === 5894 && len >= 37) {
|
|
6746
|
+
leapSeconds = view.getInt8(b + 33);
|
|
6747
|
+
}
|
|
6748
|
+
});
|
|
6749
|
+
return { ionoCorrections, leapSeconds };
|
|
6750
|
+
}
|
|
6751
|
+
|
|
6752
|
+
// src/sbf/rawnav.ts
|
|
6753
|
+
var SIGNAL_OF_BLOCK = {
|
|
6754
|
+
4018: "L2C",
|
|
6755
|
+
4019: "L5"
|
|
6741
6756
|
};
|
|
6757
|
+
function parseSbfCnav(data) {
|
|
6758
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6759
|
+
const ephemerides = [];
|
|
6760
|
+
const assemblers = {
|
|
6761
|
+
L2C: new CnavAssembler(),
|
|
6762
|
+
L5: new CnavAssembler()
|
|
6763
|
+
};
|
|
6764
|
+
let badCrc = 0;
|
|
6765
|
+
let messages = 0;
|
|
6766
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
6767
|
+
const signal = SIGNAL_OF_BLOCK[id];
|
|
6768
|
+
if (!signal || len < 60) return;
|
|
6769
|
+
messages++;
|
|
6770
|
+
const msg = new Uint8Array(40);
|
|
6771
|
+
for (let k = 0; k < 10; k++) {
|
|
6772
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
6773
|
+
msg[4 * k] = w >>> 24;
|
|
6774
|
+
msg[4 * k + 1] = w >>> 16 & 255;
|
|
6775
|
+
msg[4 * k + 2] = w >>> 8 & 255;
|
|
6776
|
+
msg[4 * k + 3] = w & 255;
|
|
6777
|
+
}
|
|
6778
|
+
if (!cnavCrcOk(msg)) {
|
|
6779
|
+
badCrc++;
|
|
6780
|
+
return;
|
|
6781
|
+
}
|
|
6782
|
+
const eph = assemblers[signal].push(msg);
|
|
6783
|
+
if (eph) ephemerides.push({ ...eph, signal });
|
|
6784
|
+
});
|
|
6785
|
+
return { ephemerides, badCrc, messages };
|
|
6786
|
+
}
|
|
6787
|
+
|
|
6788
|
+
// src/sbf/rawnav-gal.ts
|
|
6789
|
+
var INAV_SOURCES = [17, 21, 22];
|
|
6790
|
+
var FNAV_SOURCES = [20, 22];
|
|
6791
|
+
function parseSbfGalNav(data) {
|
|
6792
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
6793
|
+
const ephemerides = [];
|
|
6794
|
+
const inav = new GalInavAssembler();
|
|
6795
|
+
const fnav = new GalFnavAssembler();
|
|
6796
|
+
let badCrc = 0;
|
|
6797
|
+
let messages = 0;
|
|
6798
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
6799
|
+
if (id !== 4022 && id !== 4023 || len < 52) return;
|
|
6800
|
+
messages++;
|
|
6801
|
+
const prnStr = svidToPrn(data[b + 14]);
|
|
6802
|
+
if (!prnStr || prnStr[0] !== "E") return;
|
|
6803
|
+
const prn = parseInt(prnStr.slice(1), 10);
|
|
6804
|
+
const source = data[b + 17] & 31;
|
|
6805
|
+
const page = new Uint8Array(32);
|
|
6806
|
+
for (let k = 0; k < 8; k++) {
|
|
6807
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
6808
|
+
page[4 * k] = w >>> 24;
|
|
6809
|
+
page[4 * k + 1] = w >>> 16 & 255;
|
|
6810
|
+
page[4 * k + 2] = w >>> 8 & 255;
|
|
6811
|
+
page[4 * k + 3] = w & 255;
|
|
6812
|
+
}
|
|
6813
|
+
let eph;
|
|
6814
|
+
let src;
|
|
6815
|
+
if (id === 4023) {
|
|
6816
|
+
src = "inav";
|
|
6817
|
+
if (!INAV_SOURCES.includes(source)) return;
|
|
6818
|
+
if (getBitU22(page, 0) !== 0 || getBitU22(page, 114) !== 1) return;
|
|
6819
|
+
if (!galInavPageCrcOk(page)) {
|
|
6820
|
+
badCrc++;
|
|
6821
|
+
return;
|
|
6822
|
+
}
|
|
6823
|
+
eph = inav.push(prn, page);
|
|
6824
|
+
} else {
|
|
6825
|
+
src = "fnav";
|
|
6826
|
+
if (!FNAV_SOURCES.includes(source)) return;
|
|
6827
|
+
if (isFnavDummy(page)) return;
|
|
6828
|
+
if (!galFnavPageCrcOk(page)) {
|
|
6829
|
+
badCrc++;
|
|
6830
|
+
return;
|
|
6831
|
+
}
|
|
6832
|
+
eph = fnav.push(prn, page);
|
|
6833
|
+
}
|
|
6834
|
+
if (eph) ephemerides.push({ ...eph, source: src });
|
|
6835
|
+
});
|
|
6836
|
+
return { ephemerides, badCrc, messages };
|
|
6837
|
+
}
|
|
6838
|
+
function getBitU22(buff, pos) {
|
|
6839
|
+
return buff[pos >> 3] >> 7 - (pos & 7) & 1;
|
|
6840
|
+
}
|
|
6841
|
+
function isFnavDummy(page) {
|
|
6842
|
+
return page[0] >> 2 === 63;
|
|
6843
|
+
}
|
|
6742
6844
|
|
|
6743
6845
|
// src/sbf/rawnav-bds.ts
|
|
6744
|
-
var
|
|
6846
|
+
var GPS_EPOCH_MS9 = Date.UTC(1980, 0, 6);
|
|
6745
6847
|
var MS_PER_WEEK5 = 7 * 86400 * 1e3;
|
|
6746
6848
|
var TOW_DNU = 4294967295;
|
|
6747
6849
|
var WNC_DNU = 65535;
|
|
@@ -6811,7 +6913,7 @@ function parseSbfGloNav(data) {
|
|
|
6811
6913
|
const eph = assembler.push(
|
|
6812
6914
|
prn,
|
|
6813
6915
|
str,
|
|
6814
|
-
new Date(
|
|
6916
|
+
new Date(GPS_EPOCH_MS9 + wnc * MS_PER_WEEK5 + towMs),
|
|
6815
6917
|
view.getUint8(b + 18) - 8
|
|
6816
6918
|
// FreqNr, offset by 8
|
|
6817
6919
|
);
|
|
@@ -6820,9 +6922,532 @@ function parseSbfGloNav(data) {
|
|
|
6820
6922
|
return { ephemerides, badCrc, messages };
|
|
6821
6923
|
}
|
|
6822
6924
|
|
|
6925
|
+
// src/navbits/rs255.ts
|
|
6926
|
+
var RS255_N = 255;
|
|
6927
|
+
var RS255_K = 32;
|
|
6928
|
+
var EXP = new Uint8Array(510);
|
|
6929
|
+
var LOG = new Int16Array(256).fill(-1);
|
|
6930
|
+
{
|
|
6931
|
+
let x = 1;
|
|
6932
|
+
for (let i = 0; i < 255; i++) {
|
|
6933
|
+
EXP[i] = x;
|
|
6934
|
+
LOG[x] = i;
|
|
6935
|
+
x <<= 1;
|
|
6936
|
+
if (x & 256) x ^= 285;
|
|
6937
|
+
}
|
|
6938
|
+
for (let i = 255; i < 510; i++) EXP[i] = EXP[i - 255];
|
|
6939
|
+
}
|
|
6940
|
+
var gfMul = (a, b) => a === 0 || b === 0 ? 0 : EXP[LOG[a] + LOG[b]];
|
|
6941
|
+
var gfInv = (a) => EXP[255 - LOG[a]];
|
|
6942
|
+
var GEN_POLY = (() => {
|
|
6943
|
+
let g = new Uint8Array([1]);
|
|
6944
|
+
for (let i = 1; i <= 223; i++) {
|
|
6945
|
+
const root = EXP[i];
|
|
6946
|
+
const next = new Uint8Array(g.length + 1);
|
|
6947
|
+
for (let j = 0; j < g.length; j++) {
|
|
6948
|
+
next[j] ^= g[j];
|
|
6949
|
+
next[j + 1] ^= gfMul(g[j], root);
|
|
6950
|
+
}
|
|
6951
|
+
g = next;
|
|
6952
|
+
}
|
|
6953
|
+
return g;
|
|
6954
|
+
})();
|
|
6955
|
+
function rs255Encode(info) {
|
|
6956
|
+
if (info.length > RS255_K)
|
|
6957
|
+
throw new RangeError(`rs255Encode: info length ${info.length} > 32`);
|
|
6958
|
+
const cw = new Uint8Array(RS255_N);
|
|
6959
|
+
cw.set(info, 0);
|
|
6960
|
+
const work = new Uint8Array(RS255_N);
|
|
6961
|
+
work.set(info, 0);
|
|
6962
|
+
for (let i = 0; i < RS255_K; i++) {
|
|
6963
|
+
const coef = work[i];
|
|
6964
|
+
if (coef === 0) continue;
|
|
6965
|
+
for (let j = 1; j < GEN_POLY.length; j++)
|
|
6966
|
+
work[i + j] ^= gfMul(GEN_POLY[j], coef);
|
|
6967
|
+
}
|
|
6968
|
+
cw.set(work.subarray(RS255_K), RS255_K);
|
|
6969
|
+
return cw;
|
|
6970
|
+
}
|
|
6971
|
+
var genMatrix = null;
|
|
6972
|
+
function generatorMatrix() {
|
|
6973
|
+
if (genMatrix) return genMatrix;
|
|
6974
|
+
const rows = [];
|
|
6975
|
+
for (let r = 0; r < RS255_N; r++) rows.push(new Uint8Array(RS255_K));
|
|
6976
|
+
for (let c = 0; c < RS255_K; c++) {
|
|
6977
|
+
const e = new Uint8Array(RS255_K);
|
|
6978
|
+
e[c] = 1;
|
|
6979
|
+
const cw = rs255Encode(e);
|
|
6980
|
+
for (let r = 0; r < RS255_N; r++) rows[r][c] = cw[r];
|
|
6981
|
+
}
|
|
6982
|
+
genMatrix = rows;
|
|
6983
|
+
return rows;
|
|
6984
|
+
}
|
|
6985
|
+
function rs255DecodeErasures(pages, k) {
|
|
6986
|
+
if (k < 1 || k > RS255_K) return null;
|
|
6987
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6988
|
+
const use = [];
|
|
6989
|
+
for (const p of pages) {
|
|
6990
|
+
if (p.pageId < 1 || p.pageId > RS255_N || seen.has(p.pageId)) continue;
|
|
6991
|
+
seen.add(p.pageId);
|
|
6992
|
+
use.push(p);
|
|
6993
|
+
if (use.length === k) break;
|
|
6994
|
+
}
|
|
6995
|
+
if (use.length < k) return null;
|
|
6996
|
+
const width = use[0].octets.length;
|
|
6997
|
+
if (use.some((p) => p.octets.length !== width)) return null;
|
|
6998
|
+
const G = generatorMatrix();
|
|
6999
|
+
const a = use.map((p) => G[p.pageId - 1].slice(0, k));
|
|
7000
|
+
const inv = use.map((_, r) => {
|
|
7001
|
+
const row = new Uint8Array(k);
|
|
7002
|
+
row[r] = 1;
|
|
7003
|
+
return row;
|
|
7004
|
+
});
|
|
7005
|
+
for (let c = 0; c < k; c++) {
|
|
7006
|
+
let pivot = -1;
|
|
7007
|
+
for (let r = c; r < k; r++)
|
|
7008
|
+
if (a[r][c] !== 0) {
|
|
7009
|
+
pivot = r;
|
|
7010
|
+
break;
|
|
7011
|
+
}
|
|
7012
|
+
if (pivot < 0) return null;
|
|
7013
|
+
[a[c], a[pivot]] = [a[pivot], a[c]];
|
|
7014
|
+
[inv[c], inv[pivot]] = [inv[pivot], inv[c]];
|
|
7015
|
+
const s = gfInv(a[c][c]);
|
|
7016
|
+
for (let j = 0; j < k; j++) {
|
|
7017
|
+
a[c][j] = gfMul(a[c][j], s);
|
|
7018
|
+
inv[c][j] = gfMul(inv[c][j], s);
|
|
7019
|
+
}
|
|
7020
|
+
for (let r = 0; r < k; r++) {
|
|
7021
|
+
if (r === c || a[r][c] === 0) continue;
|
|
7022
|
+
const f = a[r][c];
|
|
7023
|
+
for (let j = 0; j < k; j++) {
|
|
7024
|
+
a[r][j] ^= gfMul(a[c][j], f);
|
|
7025
|
+
inv[r][j] ^= gfMul(inv[c][j], f);
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
7028
|
+
}
|
|
7029
|
+
const out = new Uint8Array(k * width);
|
|
7030
|
+
for (let r = 0; r < k; r++)
|
|
7031
|
+
for (let c = 0; c < width; c++) {
|
|
7032
|
+
let v = 0;
|
|
7033
|
+
for (let j = 0; j < k; j++) v ^= gfMul(inv[r][j], use[j].octets[c]);
|
|
7034
|
+
out[r * width + c] = v;
|
|
7035
|
+
}
|
|
7036
|
+
return out;
|
|
7037
|
+
}
|
|
7038
|
+
|
|
7039
|
+
// src/navbits/has.ts
|
|
7040
|
+
var HAS_DUMMY_HEADER = 11484099;
|
|
7041
|
+
var HAS_VALIDITY_SECONDS = [
|
|
7042
|
+
5,
|
|
7043
|
+
10,
|
|
7044
|
+
15,
|
|
7045
|
+
20,
|
|
7046
|
+
30,
|
|
7047
|
+
60,
|
|
7048
|
+
90,
|
|
7049
|
+
120,
|
|
7050
|
+
180,
|
|
7051
|
+
240,
|
|
7052
|
+
300,
|
|
7053
|
+
600,
|
|
7054
|
+
900,
|
|
7055
|
+
1800,
|
|
7056
|
+
3600,
|
|
7057
|
+
null
|
|
7058
|
+
];
|
|
7059
|
+
var HAS_GPS_SIGNALS = [
|
|
7060
|
+
"L1 C/A",
|
|
7061
|
+
"Reserved",
|
|
7062
|
+
"Reserved",
|
|
7063
|
+
"L1C(D)",
|
|
7064
|
+
"L1C(P)",
|
|
7065
|
+
"L1C(D+P)",
|
|
7066
|
+
"L2 CM",
|
|
7067
|
+
"L2 CL",
|
|
7068
|
+
"L2 CM+CL",
|
|
7069
|
+
"L2 P",
|
|
7070
|
+
"Reserved",
|
|
7071
|
+
"L5 I",
|
|
7072
|
+
"L5 Q",
|
|
7073
|
+
"L5 I+Q",
|
|
7074
|
+
"Reserved",
|
|
7075
|
+
"Reserved"
|
|
7076
|
+
];
|
|
7077
|
+
var HAS_GALILEO_SIGNALS = [
|
|
7078
|
+
"E1-B",
|
|
7079
|
+
"E1-C",
|
|
7080
|
+
"E1-B+E1-C",
|
|
7081
|
+
"E5a-I",
|
|
7082
|
+
"E5a-Q",
|
|
7083
|
+
"E5a-I+E5a-Q",
|
|
7084
|
+
"E5b-I",
|
|
7085
|
+
"E5b-Q",
|
|
7086
|
+
"E5b-I+E5b-Q",
|
|
7087
|
+
"E5-I",
|
|
7088
|
+
"E5-Q",
|
|
7089
|
+
"E5-I+E5-Q",
|
|
7090
|
+
"E6-B",
|
|
7091
|
+
"E6-C",
|
|
7092
|
+
"E6-B+E6-C",
|
|
7093
|
+
"Reserved"
|
|
7094
|
+
];
|
|
7095
|
+
var SYSTEM_OF_GNSS_ID = { 0: "G", 2: "E" };
|
|
7096
|
+
var IOD_BITS = { G: 8, E: 10 };
|
|
7097
|
+
function hasPageCrcOk(page) {
|
|
7098
|
+
if (page.length < 61) return false;
|
|
7099
|
+
return crc24q(page, 462) === getBitU(page, 462, 24);
|
|
7100
|
+
}
|
|
7101
|
+
function parseHasPage(page) {
|
|
7102
|
+
if (getBitU(page, 14, 24) === HAS_DUMMY_HEADER) return null;
|
|
7103
|
+
const body = new Uint8Array(53);
|
|
7104
|
+
for (let i = 0; i < 53; i++) body[i] = getBitU(page, 38 + 8 * i, 8);
|
|
7105
|
+
return {
|
|
7106
|
+
status: getBitU(page, 14, 2),
|
|
7107
|
+
messageType: getBitU(page, 18, 2),
|
|
7108
|
+
messageId: getBitU(page, 20, 5),
|
|
7109
|
+
messageSize: getBitU(page, 25, 5) + 1,
|
|
7110
|
+
pageId: getBitU(page, 30, 8),
|
|
7111
|
+
body
|
|
7112
|
+
};
|
|
7113
|
+
}
|
|
7114
|
+
var Cursor = class {
|
|
7115
|
+
constructor(buf, bits) {
|
|
7116
|
+
this.buf = buf;
|
|
7117
|
+
this.bits = bits;
|
|
7118
|
+
}
|
|
7119
|
+
pos = 0;
|
|
7120
|
+
u(len) {
|
|
7121
|
+
if (this.pos + len > this.bits) throw new RangeError("truncated");
|
|
7122
|
+
const v = getBitU(this.buf, this.pos, len);
|
|
7123
|
+
this.pos += len;
|
|
7124
|
+
return v;
|
|
7125
|
+
}
|
|
7126
|
+
s(len) {
|
|
7127
|
+
if (this.pos + len > this.bits) throw new RangeError("truncated");
|
|
7128
|
+
const v = getBitS(this.buf, this.pos, len);
|
|
7129
|
+
this.pos += len;
|
|
7130
|
+
return v;
|
|
7131
|
+
}
|
|
7132
|
+
};
|
|
7133
|
+
var prnOf = (system, idx) => `${system}${String(idx + 1).padStart(2, "0")}`;
|
|
7134
|
+
function parseMaskBlock(c, maskId) {
|
|
7135
|
+
const nSys = c.u(4);
|
|
7136
|
+
const systems = [];
|
|
7137
|
+
for (let s = 0; s < nSys; s++) {
|
|
7138
|
+
const gnssId = c.u(4);
|
|
7139
|
+
const system = SYSTEM_OF_GNSS_ID[gnssId];
|
|
7140
|
+
if (system === void 0) throw new RangeError(`unknown GNSS ID ${gnssId}`);
|
|
7141
|
+
const prns = [];
|
|
7142
|
+
for (let b = 0; b < 40; b++) if (c.u(1)) prns.push(prnOf(system, b));
|
|
7143
|
+
const signalIndices = [];
|
|
7144
|
+
for (let b = 0; b < 16; b++) if (c.u(1)) signalIndices.push(b);
|
|
7145
|
+
const signalNames = system === "G" ? HAS_GPS_SIGNALS : HAS_GALILEO_SIGNALS;
|
|
7146
|
+
let cellMask = null;
|
|
7147
|
+
if (c.u(1)) {
|
|
7148
|
+
cellMask = prns.map(() => signalIndices.map(() => c.u(1) === 1));
|
|
7149
|
+
}
|
|
7150
|
+
systems.push({
|
|
7151
|
+
gnssId,
|
|
7152
|
+
system,
|
|
7153
|
+
prns,
|
|
7154
|
+
signalIndices,
|
|
7155
|
+
signals: signalIndices.map((i) => signalNames[i]),
|
|
7156
|
+
cellMask,
|
|
7157
|
+
navMessage: c.u(3)
|
|
7158
|
+
});
|
|
7159
|
+
}
|
|
7160
|
+
c.u(6);
|
|
7161
|
+
return { maskId, systems };
|
|
7162
|
+
}
|
|
7163
|
+
function parseOrbitBlock(c, masks) {
|
|
7164
|
+
const validityIndex = c.u(4);
|
|
7165
|
+
const corrections = [];
|
|
7166
|
+
for (const sys of masks.systems) {
|
|
7167
|
+
for (const prn of sys.prns) {
|
|
7168
|
+
const gnssIod = c.u(IOD_BITS[sys.system]);
|
|
7169
|
+
const rad = c.s(13);
|
|
7170
|
+
const inT = c.s(12);
|
|
7171
|
+
const cross = c.s(12);
|
|
7172
|
+
corrections.push({
|
|
7173
|
+
system: sys.system,
|
|
7174
|
+
prn,
|
|
7175
|
+
gnssIod,
|
|
7176
|
+
deltaRadial: rad === -4096 ? null : rad * 25e-4,
|
|
7177
|
+
deltaInTrack: inT === -2048 ? null : inT * 8e-3,
|
|
7178
|
+
deltaCrossTrack: cross === -2048 ? null : cross * 8e-3
|
|
7179
|
+
});
|
|
7180
|
+
}
|
|
7181
|
+
}
|
|
7182
|
+
return {
|
|
7183
|
+
validityIndex,
|
|
7184
|
+
validitySeconds: HAS_VALIDITY_SECONDS[validityIndex],
|
|
7185
|
+
corrections
|
|
7186
|
+
};
|
|
7187
|
+
}
|
|
7188
|
+
function clockValue(c, system, prn, multiplier) {
|
|
7189
|
+
const raw = c.s(13);
|
|
7190
|
+
return {
|
|
7191
|
+
system,
|
|
7192
|
+
prn,
|
|
7193
|
+
deltaClock: raw === -4096 || raw === 4095 ? null : raw * 25e-4 * multiplier,
|
|
7194
|
+
notUsable: raw === 4095
|
|
7195
|
+
};
|
|
7196
|
+
}
|
|
7197
|
+
function parseClockFullBlock(c, masks) {
|
|
7198
|
+
const validityIndex = c.u(4);
|
|
7199
|
+
const multipliers = masks.systems.map((sys) => ({
|
|
7200
|
+
system: sys.system,
|
|
7201
|
+
multiplier: c.u(2) + 1
|
|
7202
|
+
}));
|
|
7203
|
+
const corrections = [];
|
|
7204
|
+
for (let s = 0; s < masks.systems.length; s++) {
|
|
7205
|
+
const sys = masks.systems[s];
|
|
7206
|
+
for (const prn of sys.prns)
|
|
7207
|
+
corrections.push(
|
|
7208
|
+
clockValue(c, sys.system, prn, multipliers[s].multiplier)
|
|
7209
|
+
);
|
|
7210
|
+
}
|
|
7211
|
+
return {
|
|
7212
|
+
validityIndex,
|
|
7213
|
+
validitySeconds: HAS_VALIDITY_SECONDS[validityIndex],
|
|
7214
|
+
multipliers,
|
|
7215
|
+
corrections
|
|
7216
|
+
};
|
|
7217
|
+
}
|
|
7218
|
+
function parseClockSubsetBlock(c, masks) {
|
|
7219
|
+
const validityIndex = c.u(4);
|
|
7220
|
+
const nSys = c.u(4);
|
|
7221
|
+
const multipliers = [];
|
|
7222
|
+
const corrections = [];
|
|
7223
|
+
for (let s = 0; s < nSys; s++) {
|
|
7224
|
+
const gnssId = c.u(4);
|
|
7225
|
+
const sys = masks.systems.find((m) => m.gnssId === gnssId);
|
|
7226
|
+
if (!sys) throw new RangeError(`subset GNSS ID ${gnssId} not in mask`);
|
|
7227
|
+
const multiplier = c.u(2) + 1;
|
|
7228
|
+
multipliers.push({ system: sys.system, multiplier });
|
|
7229
|
+
const selected = [];
|
|
7230
|
+
for (const prn of sys.prns) if (c.u(1)) selected.push(prn);
|
|
7231
|
+
for (const prn of selected)
|
|
7232
|
+
corrections.push(clockValue(c, sys.system, prn, multiplier));
|
|
7233
|
+
}
|
|
7234
|
+
return {
|
|
7235
|
+
validityIndex,
|
|
7236
|
+
validitySeconds: HAS_VALIDITY_SECONDS[validityIndex],
|
|
7237
|
+
multipliers,
|
|
7238
|
+
corrections
|
|
7239
|
+
};
|
|
7240
|
+
}
|
|
7241
|
+
function parseBiasBlock(c, masks, kind) {
|
|
7242
|
+
const validityIndex = c.u(4);
|
|
7243
|
+
const biases = [];
|
|
7244
|
+
for (const sys of masks.systems) {
|
|
7245
|
+
for (let s = 0; s < sys.prns.length; s++) {
|
|
7246
|
+
for (let g = 0; g < sys.signalIndices.length; g++) {
|
|
7247
|
+
if (sys.cellMask && !sys.cellMask[s][g]) continue;
|
|
7248
|
+
const raw = c.s(11);
|
|
7249
|
+
const entry = {
|
|
7250
|
+
system: sys.system,
|
|
7251
|
+
prn: sys.prns[s],
|
|
7252
|
+
signalIndex: sys.signalIndices[g],
|
|
7253
|
+
signal: sys.signals[g],
|
|
7254
|
+
bias: raw === -1024 ? null : raw * (kind === "code" ? 0.02 : 0.01)
|
|
7255
|
+
};
|
|
7256
|
+
if (kind === "phase") entry.discontinuity = c.u(2);
|
|
7257
|
+
biases.push(entry);
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7260
|
+
}
|
|
7261
|
+
return {
|
|
7262
|
+
validityIndex,
|
|
7263
|
+
validitySeconds: HAS_VALIDITY_SECONDS[validityIndex],
|
|
7264
|
+
biases
|
|
7265
|
+
};
|
|
7266
|
+
}
|
|
7267
|
+
function parseHasMt1(payload, header, cachedMasks) {
|
|
7268
|
+
const c = new Cursor(payload, payload.length * 8);
|
|
7269
|
+
const toh = c.u(12);
|
|
7270
|
+
const flags = {
|
|
7271
|
+
mask: c.u(1) === 1,
|
|
7272
|
+
orbit: c.u(1) === 1,
|
|
7273
|
+
clockFullSet: c.u(1) === 1,
|
|
7274
|
+
clockSubset: c.u(1) === 1,
|
|
7275
|
+
codeBias: c.u(1) === 1,
|
|
7276
|
+
phaseBias: c.u(1) === 1
|
|
7277
|
+
};
|
|
7278
|
+
c.u(4);
|
|
7279
|
+
const maskId = c.u(5);
|
|
7280
|
+
const iodSetId = c.u(5);
|
|
7281
|
+
const msg = {
|
|
7282
|
+
...header,
|
|
7283
|
+
toh,
|
|
7284
|
+
maskId,
|
|
7285
|
+
iodSetId,
|
|
7286
|
+
flags,
|
|
7287
|
+
maskFromCache: false,
|
|
7288
|
+
parseError: null
|
|
7289
|
+
};
|
|
7290
|
+
try {
|
|
7291
|
+
let masks;
|
|
7292
|
+
if (flags.mask) {
|
|
7293
|
+
masks = parseMaskBlock(c, maskId);
|
|
7294
|
+
} else if (cachedMasks) {
|
|
7295
|
+
masks = cachedMasks;
|
|
7296
|
+
msg.maskFromCache = true;
|
|
7297
|
+
} else {
|
|
7298
|
+
msg.parseError = "mask-unavailable";
|
|
7299
|
+
return msg;
|
|
7300
|
+
}
|
|
7301
|
+
msg.masks = masks;
|
|
7302
|
+
if (flags.orbit) msg.orbit = parseOrbitBlock(c, masks);
|
|
7303
|
+
if (flags.clockFullSet) msg.clockFullSet = parseClockFullBlock(c, masks);
|
|
7304
|
+
if (flags.clockSubset) msg.clockSubset = parseClockSubsetBlock(c, masks);
|
|
7305
|
+
if (flags.codeBias) msg.codeBias = parseBiasBlock(c, masks, "code");
|
|
7306
|
+
if (flags.phaseBias) msg.phaseBias = parseBiasBlock(c, masks, "phase");
|
|
7307
|
+
} catch (e) {
|
|
7308
|
+
msg.parseError = e instanceof Error ? e.message : String(e);
|
|
7309
|
+
}
|
|
7310
|
+
return msg;
|
|
7311
|
+
}
|
|
7312
|
+
var HasAssembler = class {
|
|
7313
|
+
slots = /* @__PURE__ */ new Map();
|
|
7314
|
+
lastMessageId = -1;
|
|
7315
|
+
maskCache = /* @__PURE__ */ new Map();
|
|
7316
|
+
/** Pages a message may span before its slot is restarted (s). */
|
|
7317
|
+
timeoutSec;
|
|
7318
|
+
stats = {
|
|
7319
|
+
pages: 0,
|
|
7320
|
+
dummyPages: 0,
|
|
7321
|
+
skippedPages: 0,
|
|
7322
|
+
duplicatePages: 0,
|
|
7323
|
+
resets: 0,
|
|
7324
|
+
messages: 0,
|
|
7325
|
+
parseErrors: 0
|
|
7326
|
+
};
|
|
7327
|
+
constructor(opts = {}) {
|
|
7328
|
+
this.timeoutSec = opts.timeoutSec ?? 30;
|
|
7329
|
+
}
|
|
7330
|
+
/**
|
|
7331
|
+
* Push one 492-bit C/NAV page (≥ 61 bytes; check the CRC first with
|
|
7332
|
+
* `hasPageCrcOk`). `tow` — a receiver time of week in seconds, any
|
|
7333
|
+
* origin as long as it is common to all pages — enables the slot
|
|
7334
|
+
* timeout and is stamped on completed messages.
|
|
7335
|
+
*/
|
|
7336
|
+
push(page, tow) {
|
|
7337
|
+
const p = parseHasPage(page);
|
|
7338
|
+
if (!p) {
|
|
7339
|
+
this.stats.dummyPages++;
|
|
7340
|
+
return null;
|
|
7341
|
+
}
|
|
7342
|
+
this.stats.pages++;
|
|
7343
|
+
if (p.messageType !== 1 || p.status > 1 || p.pageId === 0) {
|
|
7344
|
+
this.stats.skippedPages++;
|
|
7345
|
+
return null;
|
|
7346
|
+
}
|
|
7347
|
+
if (p.messageId === this.lastMessageId) return null;
|
|
7348
|
+
let slot = this.slots.get(p.messageId);
|
|
7349
|
+
const timedOut = slot?.firstTow !== void 0 && tow !== void 0 && tow - slot.firstTow > this.timeoutSec;
|
|
7350
|
+
if (slot && (slot.messageSize !== p.messageSize || timedOut)) {
|
|
7351
|
+
this.stats.resets++;
|
|
7352
|
+
slot = void 0;
|
|
7353
|
+
}
|
|
7354
|
+
if (!slot) {
|
|
7355
|
+
slot = {
|
|
7356
|
+
messageSize: p.messageSize,
|
|
7357
|
+
status: p.status,
|
|
7358
|
+
pages: [],
|
|
7359
|
+
bodies: /* @__PURE__ */ new Map(),
|
|
7360
|
+
firstTow: tow
|
|
7361
|
+
};
|
|
7362
|
+
this.slots.set(p.messageId, slot);
|
|
7363
|
+
}
|
|
7364
|
+
const bodyKey = Array.from(
|
|
7365
|
+
p.body,
|
|
7366
|
+
(b) => b.toString(16).padStart(2, "0")
|
|
7367
|
+
).join("");
|
|
7368
|
+
const existing = slot.bodies.get(p.pageId);
|
|
7369
|
+
if (existing !== void 0) {
|
|
7370
|
+
if (existing === bodyKey) {
|
|
7371
|
+
this.stats.duplicatePages++;
|
|
7372
|
+
return null;
|
|
7373
|
+
}
|
|
7374
|
+
this.stats.resets++;
|
|
7375
|
+
slot.pages = [];
|
|
7376
|
+
slot.bodies.clear();
|
|
7377
|
+
slot.firstTow = tow;
|
|
7378
|
+
}
|
|
7379
|
+
slot.bodies.set(p.pageId, bodyKey);
|
|
7380
|
+
slot.pages.push({ pageId: p.pageId, octets: p.body });
|
|
7381
|
+
if (slot.pages.length < slot.messageSize) return null;
|
|
7382
|
+
const payload = rs255DecodeErasures(slot.pages, slot.messageSize);
|
|
7383
|
+
if (!payload) return null;
|
|
7384
|
+
this.slots.delete(p.messageId);
|
|
7385
|
+
this.lastMessageId = p.messageId;
|
|
7386
|
+
const msg = parseHasMt1(
|
|
7387
|
+
payload,
|
|
7388
|
+
{
|
|
7389
|
+
status: slot.status,
|
|
7390
|
+
messageId: p.messageId,
|
|
7391
|
+
messageSize: slot.messageSize
|
|
7392
|
+
},
|
|
7393
|
+
void 0
|
|
7394
|
+
);
|
|
7395
|
+
const resolved = msg.parseError === "mask-unavailable" && this.maskCache.has(msg.maskId) ? parseHasMt1(
|
|
7396
|
+
payload,
|
|
7397
|
+
{
|
|
7398
|
+
status: slot.status,
|
|
7399
|
+
messageId: p.messageId,
|
|
7400
|
+
messageSize: slot.messageSize
|
|
7401
|
+
},
|
|
7402
|
+
this.maskCache.get(msg.maskId)
|
|
7403
|
+
) : msg;
|
|
7404
|
+
if (resolved.flags.mask && resolved.masks)
|
|
7405
|
+
this.maskCache.set(resolved.maskId, resolved.masks);
|
|
7406
|
+
if (tow !== void 0) resolved.tow = tow;
|
|
7407
|
+
this.stats.messages++;
|
|
7408
|
+
if (resolved.parseError !== null) this.stats.parseErrors++;
|
|
7409
|
+
return resolved;
|
|
7410
|
+
}
|
|
7411
|
+
};
|
|
7412
|
+
|
|
7413
|
+
// src/sbf/rawnav-has.ts
|
|
7414
|
+
function parseSbfHas(data) {
|
|
7415
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7416
|
+
const assembler = new HasAssembler();
|
|
7417
|
+
const messages = [];
|
|
7418
|
+
let pagesSeen = 0;
|
|
7419
|
+
let pagesBadCrc = 0;
|
|
7420
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
7421
|
+
if (id !== 4024 || len < 84) return;
|
|
7422
|
+
pagesSeen++;
|
|
7423
|
+
const page = new Uint8Array(64);
|
|
7424
|
+
for (let k = 0; k < 16; k++) {
|
|
7425
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
7426
|
+
page[4 * k] = w >>> 24;
|
|
7427
|
+
page[4 * k + 1] = w >>> 16 & 255;
|
|
7428
|
+
page[4 * k + 2] = w >>> 8 & 255;
|
|
7429
|
+
page[4 * k + 3] = w & 255;
|
|
7430
|
+
}
|
|
7431
|
+
if (!hasPageCrcOk(page)) {
|
|
7432
|
+
pagesBadCrc++;
|
|
7433
|
+
return;
|
|
7434
|
+
}
|
|
7435
|
+
const tow = view.getUint32(b + 8, true) / 1e3;
|
|
7436
|
+
const msg = assembler.push(page, tow);
|
|
7437
|
+
if (msg) messages.push({ ...msg, tow, wnc: view.getUint16(b + 12, true) });
|
|
7438
|
+
});
|
|
7439
|
+
return {
|
|
7440
|
+
messages,
|
|
7441
|
+
pagesSeen,
|
|
7442
|
+
pagesBadCrc,
|
|
7443
|
+
pagesDummy: assembler.stats.dummyPages,
|
|
7444
|
+
pagesHas: assembler.stats.pages
|
|
7445
|
+
};
|
|
7446
|
+
}
|
|
7447
|
+
|
|
6823
7448
|
// src/sbf/index.ts
|
|
6824
7449
|
var CLIGHT = 299792458;
|
|
6825
|
-
var
|
|
7450
|
+
var GPS_EPOCH_MS10 = Date.UTC(1980, 0, 6);
|
|
6826
7451
|
var MS_PER_WEEK6 = 7 * 864e5;
|
|
6827
7452
|
var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
|
|
6828
7453
|
var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
|
|
@@ -6922,23 +7547,23 @@ var MEAS2_SIG = [
|
|
|
6922
7547
|
["J", "1E"],
|
|
6923
7548
|
["J", "5P"]
|
|
6924
7549
|
];
|
|
6925
|
-
var
|
|
7550
|
+
var two4 = (n) => String(n).padStart(2, "0");
|
|
6926
7551
|
function m3Prn(navsys, svid) {
|
|
6927
7552
|
switch (navsys) {
|
|
6928
7553
|
case 0:
|
|
6929
|
-
return svid < 32 ? `G${
|
|
7554
|
+
return svid < 32 ? `G${two4(svid + 1)}` : null;
|
|
6930
7555
|
case 1:
|
|
6931
|
-
return svid < 27 ? `R${
|
|
7556
|
+
return svid < 27 ? `R${two4(svid + 1)}` : null;
|
|
6932
7557
|
case 2:
|
|
6933
|
-
return svid < 36 ? `E${
|
|
7558
|
+
return svid < 36 ? `E${two4(svid + 1)}` : null;
|
|
6934
7559
|
case 3:
|
|
6935
|
-
return svid < 50 ? `C${
|
|
7560
|
+
return svid < 50 ? `C${two4(svid + 1)}` : null;
|
|
6936
7561
|
case 4:
|
|
6937
|
-
return svid <= 38 ? `S${
|
|
7562
|
+
return svid <= 38 ? `S${two4(svid + 20)}` : null;
|
|
6938
7563
|
case 5:
|
|
6939
|
-
return svid < 10 ? `J${
|
|
7564
|
+
return svid < 10 ? `J${two4(svid + 1)}` : null;
|
|
6940
7565
|
case 6:
|
|
6941
|
-
return svid < 14 ? `I${
|
|
7566
|
+
return svid < 14 ? `I${two4(svid + 1)}` : null;
|
|
6942
7567
|
default:
|
|
6943
7568
|
return null;
|
|
6944
7569
|
}
|
|
@@ -7476,7 +8101,7 @@ function parseSbfMeas(data) {
|
|
|
7476
8101
|
const tow = view.getUint32(i + 8, true);
|
|
7477
8102
|
const wnc = view.getUint16(i + 12, true);
|
|
7478
8103
|
if (tow !== 4294967295 && wnc !== 65535) {
|
|
7479
|
-
ensureEpoch(
|
|
8104
|
+
ensureEpoch(GPS_EPOCH_MS10 + wnc * MS_PER_WEEK6 + tow);
|
|
7480
8105
|
if (id === 4109) decodeMeas3Ranges(i, tow);
|
|
7481
8106
|
else if (id === 4110) decodeMeas3CN0(i, len);
|
|
7482
8107
|
else if (id === 4111) decodeMeas3Doppler(i, len);
|
|
@@ -7541,15 +8166,15 @@ var ID_GALEPHEMERIS = 1122;
|
|
|
7541
8166
|
var ID_QZSSEPHEMERIS = 1336;
|
|
7542
8167
|
var ID_GPSEPHEM = 7;
|
|
7543
8168
|
var ID_BDSEPHEMERIS = 1696;
|
|
7544
|
-
var
|
|
8169
|
+
var GPS_EPOCH_MS11 = Date.UTC(1980, 0, 6);
|
|
7545
8170
|
var BDT_EPOCH_MS3 = Date.UTC(2006, 0, 1);
|
|
7546
|
-
var
|
|
8171
|
+
var SEC_PER_WEEK9 = 7 * 86400;
|
|
7547
8172
|
var SEC_PER_DAY2 = 86400;
|
|
7548
|
-
var MS_PER_WEEK7 =
|
|
7549
|
-
var gpsMs2 = (week, sec) =>
|
|
7550
|
-
var sowOf3 = (dateMs) => dateMs / 1e3 %
|
|
8173
|
+
var MS_PER_WEEK7 = SEC_PER_WEEK9 * 1e3;
|
|
8174
|
+
var gpsMs2 = (week, sec) => GPS_EPOCH_MS11 + week * MS_PER_WEEK7 + sec * 1e3;
|
|
8175
|
+
var sowOf3 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK9;
|
|
7551
8176
|
function nearWeekMs(refMs, sow) {
|
|
7552
|
-
const refWeek = Math.floor((refMs -
|
|
8177
|
+
const refWeek = Math.floor((refMs - GPS_EPOCH_MS11) / MS_PER_WEEK7);
|
|
7553
8178
|
let ms = gpsMs2(refWeek, sow);
|
|
7554
8179
|
if (ms < refMs - MS_PER_WEEK7 / 2) ms += MS_PER_WEEK7;
|
|
7555
8180
|
else if (ms > refMs + MS_PER_WEEK7 / 2) ms -= MS_PER_WEEK7;
|
|
@@ -7576,9 +8201,9 @@ function decodeGloEphemeris(view, p) {
|
|
|
7576
8201
|
tof += Math.floor(tow / SEC_PER_DAY2) * SEC_PER_DAY2;
|
|
7577
8202
|
if (tof < tow - 43200) tof += SEC_PER_DAY2;
|
|
7578
8203
|
else if (tof > tow + 43200) tof -= SEC_PER_DAY2;
|
|
7579
|
-
const toeGpsMs =
|
|
7580
|
-
const tofGpsMs =
|
|
7581
|
-
const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() -
|
|
8204
|
+
const toeGpsMs = GPS_EPOCH_MS11 + (week * SEC_PER_WEEK9 + tow) * 1e3;
|
|
8205
|
+
const tofGpsMs = GPS_EPOCH_MS11 + (week * SEC_PER_WEEK9 + tof) * 1e3;
|
|
8206
|
+
const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() - GPS_EPOCH_MS11) / 1e3;
|
|
7582
8207
|
return {
|
|
7583
8208
|
system: "R",
|
|
7584
8209
|
prn: `R${String(slot).padStart(2, "0")}`,
|
|
@@ -7586,7 +8211,7 @@ function decodeGloEphemeris(view, p) {
|
|
|
7586
8211
|
tauN: -view.getFloat64(p + 100, true),
|
|
7587
8212
|
// RINEX stores −τ_n
|
|
7588
8213
|
gammaN: view.getFloat64(p + 116, true),
|
|
7589
|
-
messageFrameTime: (tofUtcSec %
|
|
8214
|
+
messageFrameTime: (tofUtcSec % SEC_PER_WEEK9 + SEC_PER_WEEK9) % SEC_PER_WEEK9,
|
|
7590
8215
|
x: view.getFloat64(p + 28, true) / 1e3,
|
|
7591
8216
|
y: view.getFloat64(p + 36, true) / 1e3,
|
|
7592
8217
|
z: view.getFloat64(p + 44, true) / 1e3,
|
|
@@ -7643,7 +8268,7 @@ function decodeGalEphemeris(view, p, headerMs) {
|
|
|
7643
8268
|
omega: view.getFloat64(p + 60, true),
|
|
7644
8269
|
omegaDot: view.getFloat64(p + 140, true),
|
|
7645
8270
|
idot: view.getFloat64(p + 124, true),
|
|
7646
|
-
week: Math.floor((toeMs -
|
|
8271
|
+
week: Math.floor((toeMs - GPS_EPOCH_MS11) / MS_PER_WEEK7),
|
|
7647
8272
|
// RINEX Galileo SVH bit layout (E1B DVS/HS in bits 0-2, E5a in
|
|
7648
8273
|
// 3-5, E5b in 6-8) — same packing as RTKLIB.
|
|
7649
8274
|
svHealth: svhE5b << 7 | dvsE5b << 6 | svhE5a << 4 | dvsE5a << 3 | svhE1b << 1 | dvsE1b,
|
|
@@ -7815,7 +8440,7 @@ function parseNovatelNav(data) {
|
|
|
7815
8440
|
}
|
|
7816
8441
|
|
|
7817
8442
|
// src/novatel/index.ts
|
|
7818
|
-
var
|
|
8443
|
+
var GPS_EPOCH_MS12 = Date.UTC(1980, 0, 6);
|
|
7819
8444
|
var MS_PER_WEEK8 = 7 * 864e5;
|
|
7820
8445
|
var HDR = 28;
|
|
7821
8446
|
var ID_RANGE = 43;
|
|
@@ -7985,7 +8610,7 @@ function parseNovatelRange(data) {
|
|
|
7985
8610
|
if (!codes.includes(code)) codes.push(code);
|
|
7986
8611
|
}
|
|
7987
8612
|
epochs.push({
|
|
7988
|
-
timeMs:
|
|
8613
|
+
timeMs: GPS_EPOCH_MS12 + week * MS_PER_WEEK8 + towMs,
|
|
7989
8614
|
meas
|
|
7990
8615
|
});
|
|
7991
8616
|
}
|
|
@@ -8037,7 +8662,7 @@ function parseNovatelRange(data) {
|
|
|
8037
8662
|
if (!codes.includes(code)) codes.push(code);
|
|
8038
8663
|
}
|
|
8039
8664
|
epochs.push({
|
|
8040
|
-
timeMs:
|
|
8665
|
+
timeMs: GPS_EPOCH_MS12 + week * MS_PER_WEEK8 + towMs,
|
|
8041
8666
|
meas
|
|
8042
8667
|
});
|
|
8043
8668
|
}
|
|
@@ -8051,7 +8676,7 @@ function parseNovatelRange(data) {
|
|
|
8051
8676
|
var GM_GPS = 3986005e8;
|
|
8052
8677
|
var GM_GAL = 3986004418e5;
|
|
8053
8678
|
var GM_BDS = 3986004418e5;
|
|
8054
|
-
var
|
|
8679
|
+
var GPS_EPOCH_MS13 = Date.UTC(1980, 0, 6);
|
|
8055
8680
|
var BDT_EPOCH_MS4 = Date.UTC(2006, 0, 1);
|
|
8056
8681
|
var WEEK_MS = 6048e5;
|
|
8057
8682
|
var BDT_MINUS_GPST_MS = 14e3;
|
|
@@ -8060,7 +8685,7 @@ function almanacEpochMs(alm) {
|
|
|
8060
8685
|
if (alm.system === "C") {
|
|
8061
8686
|
return BDT_EPOCH_MS4 + alm.weekAlm * WEEK_MS + alm.toaSec * 1e3 + BDT_MINUS_GPST_MS;
|
|
8062
8687
|
}
|
|
8063
|
-
return
|
|
8688
|
+
return GPS_EPOCH_MS13 + alm.weekAlm * WEEK_MS + alm.toaSec * 1e3;
|
|
8064
8689
|
}
|
|
8065
8690
|
function almanacSatPosition(alm, timeMs) {
|
|
8066
8691
|
const GM = alm.system === "E" ? GM_GAL : alm.system === "C" ? GM_BDS : GM_GPS;
|
|
@@ -8570,10 +9195,10 @@ function selectBest(ephs, timeMs) {
|
|
|
8570
9195
|
}
|
|
8571
9196
|
return best;
|
|
8572
9197
|
}
|
|
8573
|
-
var
|
|
9198
|
+
var GPS_EPOCH_MS14 = START_GPS_TIME.getTime();
|
|
8574
9199
|
var MS_PER_WEEK9 = 7 * 864e5;
|
|
8575
9200
|
var BDS_EPOCH_MS = START_BDS_TIME.getTime();
|
|
8576
|
-
var GAL_EPOCH_MS =
|
|
9201
|
+
var GAL_EPOCH_MS = GPS_EPOCH_MS14 + 1024 * MS_PER_WEEK9;
|
|
8577
9202
|
function ephInfoToEphemeris(info) {
|
|
8578
9203
|
const sys = info.prn.charAt(0);
|
|
8579
9204
|
if (sys === "R") {
|
|
@@ -8624,8 +9249,8 @@ function ephInfoToEphemeris(info) {
|
|
|
8624
9249
|
} else if (sys === "E") {
|
|
8625
9250
|
epochMs = GAL_EPOCH_MS;
|
|
8626
9251
|
} else {
|
|
8627
|
-
epochMs =
|
|
8628
|
-
const weeksNow = (info.lastReceived -
|
|
9252
|
+
epochMs = GPS_EPOCH_MS14;
|
|
9253
|
+
const weeksNow = (info.lastReceived - GPS_EPOCH_MS14) / MS_PER_WEEK9;
|
|
8629
9254
|
week += 1024 * Math.round((weeksNow - week) / 1024);
|
|
8630
9255
|
}
|
|
8631
9256
|
const tocDate = new Date(epochMs + week * MS_PER_WEEK9 + tocSec * 1e3);
|
|
@@ -11736,6 +12361,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
11736
12361
|
parseSbfCnav,
|
|
11737
12362
|
parseSbfGalNav,
|
|
11738
12363
|
parseSbfGloNav,
|
|
12364
|
+
parseSbfHas,
|
|
11739
12365
|
parseSbfIonoUtc,
|
|
11740
12366
|
parseSbfMeas,
|
|
11741
12367
|
parseSbfNav,
|
|
@@ -11745,6 +12371,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
11745
12371
|
parseUbxCnav,
|
|
11746
12372
|
parseUbxIonoUtc,
|
|
11747
12373
|
parseUbxNav,
|
|
12374
|
+
parseUbxRawNav,
|
|
11748
12375
|
parseUbxRawx,
|
|
11749
12376
|
phiAltBOC,
|
|
11750
12377
|
phiBOCc,
|