gnss-js 1.36.0 → 1.38.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/binex.js +2 -2
- package/dist/{chunk-AFEDNWXI.js → chunk-4TAN27LO.js} +368 -111
- package/dist/{chunk-F4CM6Q4F.js → chunk-5X4AG4MK.js} +2 -2
- package/dist/{chunk-5RNDQQ6J.js → chunk-6SWJTN6H.js} +2 -2
- package/dist/{chunk-OK4GGNOO.js → chunk-D25WNE43.js} +1 -1
- package/dist/{chunk-5AUK42AL.js → chunk-FA2UTLEQ.js} +2 -2
- package/dist/{chunk-IQ5OIMQD.js → chunk-V2D5KUKH.js} +33 -0
- package/dist/{chunk-726LQBGM.js → chunk-YUW33VXK.js} +1 -1
- package/dist/{chunk-FEOMYZEU.js → chunk-ZQDLYLQA.js} +9 -28
- package/dist/index.cjs +410 -142
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +14 -8
- package/dist/novatel.js +3 -3
- package/dist/rinex.js +3 -3
- package/dist/sbf.cjs +536 -111
- package/dist/sbf.d.cts +169 -14
- package/dist/sbf.d.ts +169 -14
- package/dist/sbf.js +12 -4
- package/dist/ubx.cjs +37 -24
- package/dist/ubx.js +4 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -142,6 +142,7 @@ __export(src_exports, {
|
|
|
142
142
|
decodeBinexEph: () => decodeBinexEph,
|
|
143
143
|
decodeEphemeris: () => decodeEphemeris,
|
|
144
144
|
decodeMsmFull: () => decodeMsmFull,
|
|
145
|
+
decodeSbfNavigation: () => decodeSbfNavigation,
|
|
145
146
|
deg2dms: () => deg2dms,
|
|
146
147
|
deg2rad: () => deg2rad,
|
|
147
148
|
detrendIonoArcs: () => detrendIonoArcs,
|
|
@@ -260,10 +261,12 @@ __export(src_exports, {
|
|
|
260
261
|
parseSbfCnav: () => parseSbfCnav,
|
|
261
262
|
parseSbfGalNav: () => parseSbfGalNav,
|
|
262
263
|
parseSbfGloNav: () => parseSbfGloNav,
|
|
264
|
+
parseSbfGpsNav: () => parseSbfGpsNav,
|
|
263
265
|
parseSbfHas: () => parseSbfHas,
|
|
264
266
|
parseSbfIonoUtc: () => parseSbfIonoUtc,
|
|
265
267
|
parseSbfMeas: () => parseSbfMeas,
|
|
266
268
|
parseSbfNav: () => parseSbfNav,
|
|
269
|
+
parseSbfReceiverSetup: () => parseSbfReceiverSetup,
|
|
267
270
|
parseSinexBiasDcb: () => parseSinexBiasDcb,
|
|
268
271
|
parseSourcetable: () => parseSourcetable,
|
|
269
272
|
parseSp3: () => parseSp3,
|
|
@@ -2699,6 +2702,38 @@ function setBitU(buff, pos, len, data) {
|
|
|
2699
2702
|
}
|
|
2700
2703
|
}
|
|
2701
2704
|
}
|
|
2705
|
+
var GpsLnavAssembler = class _GpsLnavAssembler {
|
|
2706
|
+
buffers = /* @__PURE__ */ new Map();
|
|
2707
|
+
last = /* @__PURE__ */ new Map();
|
|
2708
|
+
/** Subframe ID (1–5) from the HOW word of a parity-stripped subframe. */
|
|
2709
|
+
static subframeId(subframe) {
|
|
2710
|
+
return getBitU(subframe, 43, 3);
|
|
2711
|
+
}
|
|
2712
|
+
/**
|
|
2713
|
+
* Push one parity-stripped 30-byte LNAV subframe for `prn`, resolving
|
|
2714
|
+
* the 10-bit week against `refWeek`. See {@link LnavPush} for outcomes.
|
|
2715
|
+
*/
|
|
2716
|
+
push(prn, subframe, refWeek) {
|
|
2717
|
+
const id = _GpsLnavAssembler.subframeId(subframe);
|
|
2718
|
+
if (id < 1 || id > 3) return { kind: "skipped" };
|
|
2719
|
+
let sf = this.buffers.get(prn);
|
|
2720
|
+
if (!sf) {
|
|
2721
|
+
sf = { buf: new Uint8Array(90), have: 0 };
|
|
2722
|
+
this.buffers.set(prn, sf);
|
|
2723
|
+
}
|
|
2724
|
+
sf.buf.set(subframe, (id - 1) * 30);
|
|
2725
|
+
sf.have |= 1 << id - 1;
|
|
2726
|
+
if (id !== 3 || sf.have !== 7) return { kind: "incomplete" };
|
|
2727
|
+
const eph = decodeGpsLnavFrame(sf.buf, { prn, refWeek });
|
|
2728
|
+
if (!eph) return { kind: "decodeFailed" };
|
|
2729
|
+
const prev = this.last.get(prn);
|
|
2730
|
+
if (prev && prev.iode === eph.iode && prev.tocDate.getTime() === eph.tocDate.getTime()) {
|
|
2731
|
+
return { kind: "duplicate" };
|
|
2732
|
+
}
|
|
2733
|
+
this.last.set(prn, eph);
|
|
2734
|
+
return { kind: "eph", eph };
|
|
2735
|
+
}
|
|
2736
|
+
};
|
|
2702
2737
|
function decodeGpsLnavFrame(subframes, opts = {}) {
|
|
2703
2738
|
if (subframes.length < 90) return null;
|
|
2704
2739
|
const b = subframes;
|
|
@@ -5918,38 +5953,19 @@ function parseUbxNav(data, opts = {}) {
|
|
|
5918
5953
|
}
|
|
5919
5954
|
}
|
|
5920
5955
|
if (refWeek === void 0) return { ephemerides, badParity };
|
|
5921
|
-
const
|
|
5922
|
-
const last = /* @__PURE__ */ new Map();
|
|
5956
|
+
const assembler = new GpsLnavAssembler();
|
|
5923
5957
|
for (const f of ubxFrames(data)) {
|
|
5924
5958
|
if (f.msgClass !== 2 || f.msgId !== 19) continue;
|
|
5925
5959
|
const lnav = readLnavSubframe(view, f);
|
|
5926
5960
|
if (!lnav) continue;
|
|
5927
|
-
const { prn,
|
|
5961
|
+
const { prn, buff, id } = lnav;
|
|
5928
5962
|
if (id < 1 || id > 5) {
|
|
5929
5963
|
badParity++;
|
|
5930
5964
|
continue;
|
|
5931
5965
|
}
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
if (!sf) {
|
|
5936
|
-
sf = { buf: new Uint8Array(90), have: 0 };
|
|
5937
|
-
subframes.set(key, sf);
|
|
5938
|
-
}
|
|
5939
|
-
sf.buf.set(buff, (id - 1) * 30);
|
|
5940
|
-
sf.have |= 1 << id - 1;
|
|
5941
|
-
if (id !== 3 || sf.have !== 7) continue;
|
|
5942
|
-
const eph = decodeGpsLnavFrame(sf.buf, { prn, refWeek });
|
|
5943
|
-
if (!eph) {
|
|
5944
|
-
badParity++;
|
|
5945
|
-
continue;
|
|
5946
|
-
}
|
|
5947
|
-
const prev = last.get(prn);
|
|
5948
|
-
if (prev && prev.iode === eph.iode && prev.tocDate.getTime() === eph.tocDate.getTime()) {
|
|
5949
|
-
continue;
|
|
5950
|
-
}
|
|
5951
|
-
last.set(prn, eph);
|
|
5952
|
-
ephemerides.push(eph);
|
|
5966
|
+
const r = assembler.push(prn, buff, refWeek);
|
|
5967
|
+
if (r.kind === "eph") ephemerides.push(r.eph);
|
|
5968
|
+
else if (r.kind === "decodeFailed") badParity++;
|
|
5953
5969
|
}
|
|
5954
5970
|
return { ephemerides, badParity };
|
|
5955
5971
|
}
|
|
@@ -6740,9 +6756,9 @@ var GloStringAssembler = class {
|
|
|
6740
6756
|
* Returns the newly completed ephemeris, or null. The decoded
|
|
6741
6757
|
* string-4 slot number must match `prn`, or nothing is emitted.
|
|
6742
6758
|
*/
|
|
6743
|
-
push(prn,
|
|
6744
|
-
if (
|
|
6745
|
-
const m = getBitU(
|
|
6759
|
+
push(prn, str2, time, freqNum = 0) {
|
|
6760
|
+
if (str2.length < GLO_STRING_BYTES) return null;
|
|
6761
|
+
const m = getBitU(str2, 1, 4);
|
|
6746
6762
|
if (m < 1) return null;
|
|
6747
6763
|
const sec = Math.floor(time.getTime() / 1e3);
|
|
6748
6764
|
let sat = this.sats.get(prn);
|
|
@@ -6754,7 +6770,7 @@ var GloStringAssembler = class {
|
|
|
6754
6770
|
sat.batchSec = sec;
|
|
6755
6771
|
}
|
|
6756
6772
|
if (m > 4) return null;
|
|
6757
|
-
sat.buf.set(
|
|
6773
|
+
sat.buf.set(str2.subarray(0, GLO_STRING_BYTES), (m - 1) * 10);
|
|
6758
6774
|
if (m !== 4) return null;
|
|
6759
6775
|
const eph = decodeGloStrings(sat.buf, time, { freqNum });
|
|
6760
6776
|
if (!eph || eph.prn !== prn) return null;
|
|
@@ -6845,20 +6861,20 @@ function parseUbxRawNav(data, opts = {}) {
|
|
|
6845
6861
|
if (p.length < 8 + 16) continue;
|
|
6846
6862
|
counts.glo++;
|
|
6847
6863
|
if (svId < 1 || svId > 32) continue;
|
|
6848
|
-
const
|
|
6864
|
+
const str2 = new Uint8Array(16);
|
|
6849
6865
|
for (let k = 0; k < 4; k++) {
|
|
6850
6866
|
const w = view.getUint32(base + 4 * k, true);
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6867
|
+
str2[4 * k] = w >>> 24;
|
|
6868
|
+
str2[4 * k + 1] = w >>> 16 & 255;
|
|
6869
|
+
str2[4 * k + 2] = w >>> 8 & 255;
|
|
6870
|
+
str2[4 * k + 3] = w & 255;
|
|
6855
6871
|
}
|
|
6856
|
-
if (!testGloString(
|
|
6872
|
+
if (!testGloString(str2)) {
|
|
6857
6873
|
badParity++;
|
|
6858
6874
|
continue;
|
|
6859
6875
|
}
|
|
6860
6876
|
if (!refDate) continue;
|
|
6861
|
-
const eph = glo.push(`R${two2(svId)}`,
|
|
6877
|
+
const eph = glo.push(`R${two2(svId)}`, str2, refDate, p[3] - 7);
|
|
6862
6878
|
if (eph) ephemerides.push(eph);
|
|
6863
6879
|
}
|
|
6864
6880
|
}
|
|
@@ -7360,38 +7376,132 @@ function parseSbfIonoUtc(data) {
|
|
|
7360
7376
|
return { ionoCorrections, leapSeconds };
|
|
7361
7377
|
}
|
|
7362
7378
|
|
|
7379
|
+
// src/sbf/setup.ts
|
|
7380
|
+
var F8_DNU = -2e10;
|
|
7381
|
+
var F4_DNU3 = -2e10;
|
|
7382
|
+
function str(data, off, n) {
|
|
7383
|
+
let end = off;
|
|
7384
|
+
const stop = off + n;
|
|
7385
|
+
while (end < stop && data[end] !== 0) end++;
|
|
7386
|
+
let s = "";
|
|
7387
|
+
for (let i = off; i < end; i++) s += String.fromCharCode(data[i]);
|
|
7388
|
+
return s.trim();
|
|
7389
|
+
}
|
|
7390
|
+
function parseSbfReceiverSetup(data) {
|
|
7391
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7392
|
+
let out = null;
|
|
7393
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
7394
|
+
if (id !== 5902 || len < 256) return;
|
|
7395
|
+
const setup = {
|
|
7396
|
+
markerName: str(data, b + 16, 60),
|
|
7397
|
+
markerNumber: str(data, b + 76, 20),
|
|
7398
|
+
observer: str(data, b + 96, 20),
|
|
7399
|
+
agency: str(data, b + 116, 40),
|
|
7400
|
+
rxSerialNumber: str(data, b + 156, 20),
|
|
7401
|
+
rxName: str(data, b + 176, 20),
|
|
7402
|
+
rxVersion: str(data, b + 196, 20),
|
|
7403
|
+
antSerialNumber: str(data, b + 216, 20),
|
|
7404
|
+
antType: str(data, b + 236, 20),
|
|
7405
|
+
deltaH: len >= 268 ? view.getFloat32(b + 256, true) : 0,
|
|
7406
|
+
deltaE: len >= 268 ? view.getFloat32(b + 260, true) : 0,
|
|
7407
|
+
deltaN: len >= 268 ? view.getFloat32(b + 264, true) : 0,
|
|
7408
|
+
markerType: len >= 288 ? str(data, b + 268, 20) : null,
|
|
7409
|
+
gnssFwVersion: len >= 328 ? str(data, b + 288, 40) : null,
|
|
7410
|
+
productName: len >= 368 ? str(data, b + 328, 40) : null,
|
|
7411
|
+
latitude: null,
|
|
7412
|
+
longitude: null,
|
|
7413
|
+
height: null,
|
|
7414
|
+
position: null,
|
|
7415
|
+
stationCode: len >= 398 ? str(data, b + 388, 10) : null
|
|
7416
|
+
};
|
|
7417
|
+
if (len >= 388) {
|
|
7418
|
+
const lat = view.getFloat64(b + 368, true);
|
|
7419
|
+
const lon = view.getFloat64(b + 376, true);
|
|
7420
|
+
const h = view.getFloat32(b + 384, true);
|
|
7421
|
+
if (lat !== F8_DNU && lon !== F8_DNU && h !== F4_DNU3) {
|
|
7422
|
+
setup.latitude = lat;
|
|
7423
|
+
setup.longitude = lon;
|
|
7424
|
+
setup.height = h;
|
|
7425
|
+
setup.position = geodeticToEcef(lat, lon, h);
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
7428
|
+
out = setup;
|
|
7429
|
+
});
|
|
7430
|
+
return out;
|
|
7431
|
+
}
|
|
7432
|
+
|
|
7433
|
+
// src/sbf/rawnav-gps.ts
|
|
7434
|
+
var WNC_DNU = 65535;
|
|
7435
|
+
function extractSbfLnavSubframe(view, b) {
|
|
7436
|
+
const sf = new Uint8Array(30);
|
|
7437
|
+
for (let k = 0; k < 10; k++) {
|
|
7438
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
7439
|
+
setBitU(sf, 24 * k, 24, w >>> 6 & 16777215);
|
|
7440
|
+
}
|
|
7441
|
+
return sf;
|
|
7442
|
+
}
|
|
7443
|
+
function feedGpsLnavBlock(view, b, assembler) {
|
|
7444
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
7445
|
+
if (!prn || prn[0] !== "G" && prn[0] !== "J") return {};
|
|
7446
|
+
if (view.getUint8(b + 15) !== 1) return {};
|
|
7447
|
+
const wnc = view.getUint16(b + 12, true);
|
|
7448
|
+
if (wnc === WNC_DNU) return {};
|
|
7449
|
+
const r = assembler.push(prn, extractSbfLnavSubframe(view, b), wnc);
|
|
7450
|
+
if (r.kind === "eph") return { eph: r.eph };
|
|
7451
|
+
if (r.kind === "decodeFailed") return { badParity: true };
|
|
7452
|
+
return {};
|
|
7453
|
+
}
|
|
7454
|
+
function parseSbfGpsNav(data) {
|
|
7455
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7456
|
+
const ephemerides = [];
|
|
7457
|
+
const assembler = new GpsLnavAssembler();
|
|
7458
|
+
let badParity = 0;
|
|
7459
|
+
let messages = 0;
|
|
7460
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
7461
|
+
if (id !== 4017 && id !== 4066 || len < 60) return;
|
|
7462
|
+
messages++;
|
|
7463
|
+
const r = feedGpsLnavBlock(view, b, assembler);
|
|
7464
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
7465
|
+
else if (r.badParity) badParity++;
|
|
7466
|
+
});
|
|
7467
|
+
return { ephemerides, badParity, messages };
|
|
7468
|
+
}
|
|
7469
|
+
|
|
7363
7470
|
// src/sbf/rawnav.ts
|
|
7364
|
-
var
|
|
7471
|
+
var CNAV_SIGNAL_OF_BLOCK = {
|
|
7365
7472
|
4018: "L2C",
|
|
7366
7473
|
4019: "L5"
|
|
7367
7474
|
};
|
|
7475
|
+
function newCnavAssemblers() {
|
|
7476
|
+
return { L2C: new CnavAssembler(), L5: new CnavAssembler() };
|
|
7477
|
+
}
|
|
7478
|
+
function feedCnavBlock(view, b, id, asm) {
|
|
7479
|
+
const signal = CNAV_SIGNAL_OF_BLOCK[id];
|
|
7480
|
+
if (!signal) return {};
|
|
7481
|
+
const msg = new Uint8Array(40);
|
|
7482
|
+
for (let k = 0; k < 10; k++) {
|
|
7483
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
7484
|
+
msg[4 * k] = w >>> 24;
|
|
7485
|
+
msg[4 * k + 1] = w >>> 16 & 255;
|
|
7486
|
+
msg[4 * k + 2] = w >>> 8 & 255;
|
|
7487
|
+
msg[4 * k + 3] = w & 255;
|
|
7488
|
+
}
|
|
7489
|
+
if (!cnavCrcOk(msg)) return { badCrc: true };
|
|
7490
|
+
const eph = asm[signal].push(msg);
|
|
7491
|
+
return eph ? { eph: { ...eph, signal } } : {};
|
|
7492
|
+
}
|
|
7368
7493
|
function parseSbfCnav(data) {
|
|
7369
7494
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7370
7495
|
const ephemerides = [];
|
|
7371
|
-
const
|
|
7372
|
-
L2C: new CnavAssembler(),
|
|
7373
|
-
L5: new CnavAssembler()
|
|
7374
|
-
};
|
|
7496
|
+
const asm = newCnavAssemblers();
|
|
7375
7497
|
let badCrc = 0;
|
|
7376
7498
|
let messages = 0;
|
|
7377
7499
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
7378
|
-
|
|
7379
|
-
if (!signal || len < 60) return;
|
|
7500
|
+
if (!CNAV_SIGNAL_OF_BLOCK[id] || len < 60) return;
|
|
7380
7501
|
messages++;
|
|
7381
|
-
const
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
msg[4 * k] = w >>> 24;
|
|
7385
|
-
msg[4 * k + 1] = w >>> 16 & 255;
|
|
7386
|
-
msg[4 * k + 2] = w >>> 8 & 255;
|
|
7387
|
-
msg[4 * k + 3] = w & 255;
|
|
7388
|
-
}
|
|
7389
|
-
if (!cnavCrcOk(msg)) {
|
|
7390
|
-
badCrc++;
|
|
7391
|
-
return;
|
|
7392
|
-
}
|
|
7393
|
-
const eph = assemblers[signal].push(msg);
|
|
7394
|
-
if (eph) ephemerides.push({ ...eph, signal });
|
|
7502
|
+
const r = feedCnavBlock(view, b, id, asm);
|
|
7503
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
7504
|
+
else if (r.badCrc) badCrc++;
|
|
7395
7505
|
});
|
|
7396
7506
|
return { ephemerides, badCrc, messages };
|
|
7397
7507
|
}
|
|
@@ -7399,50 +7509,51 @@ function parseSbfCnav(data) {
|
|
|
7399
7509
|
// src/sbf/rawnav-gal.ts
|
|
7400
7510
|
var INAV_SOURCES = [17, 21, 22];
|
|
7401
7511
|
var FNAV_SOURCES = [20, 22];
|
|
7512
|
+
function newGalAssemblers() {
|
|
7513
|
+
return { inav: new GalInavAssembler(), fnav: new GalFnavAssembler() };
|
|
7514
|
+
}
|
|
7515
|
+
function feedGalBlock(data, view, b, id, asm) {
|
|
7516
|
+
const prnStr = svidToPrn(data[b + 14]);
|
|
7517
|
+
if (!prnStr || prnStr[0] !== "E") return {};
|
|
7518
|
+
const prn = parseInt(prnStr.slice(1), 10);
|
|
7519
|
+
const source = data[b + 17] & 31;
|
|
7520
|
+
const page = new Uint8Array(32);
|
|
7521
|
+
for (let k = 0; k < 8; k++) {
|
|
7522
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
7523
|
+
page[4 * k] = w >>> 24;
|
|
7524
|
+
page[4 * k + 1] = w >>> 16 & 255;
|
|
7525
|
+
page[4 * k + 2] = w >>> 8 & 255;
|
|
7526
|
+
page[4 * k + 3] = w & 255;
|
|
7527
|
+
}
|
|
7528
|
+
let eph;
|
|
7529
|
+
let src;
|
|
7530
|
+
if (id === 4023) {
|
|
7531
|
+
src = "inav";
|
|
7532
|
+
if (!INAV_SOURCES.includes(source)) return {};
|
|
7533
|
+
if (getBitU22(page, 0) !== 0 || getBitU22(page, 114) !== 1) return {};
|
|
7534
|
+
if (!galInavPageCrcOk(page)) return { badCrc: true };
|
|
7535
|
+
eph = asm.inav.push(prn, page);
|
|
7536
|
+
} else {
|
|
7537
|
+
src = "fnav";
|
|
7538
|
+
if (!FNAV_SOURCES.includes(source)) return {};
|
|
7539
|
+
if (isFnavDummy(page)) return {};
|
|
7540
|
+
if (!galFnavPageCrcOk(page)) return { badCrc: true };
|
|
7541
|
+
eph = asm.fnav.push(prn, page);
|
|
7542
|
+
}
|
|
7543
|
+
return eph ? { eph: { ...eph, source: src } } : {};
|
|
7544
|
+
}
|
|
7402
7545
|
function parseSbfGalNav(data) {
|
|
7403
7546
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7404
7547
|
const ephemerides = [];
|
|
7405
|
-
const
|
|
7406
|
-
const fnav = new GalFnavAssembler();
|
|
7548
|
+
const asm = newGalAssemblers();
|
|
7407
7549
|
let badCrc = 0;
|
|
7408
7550
|
let messages = 0;
|
|
7409
7551
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
7410
7552
|
if (id !== 4022 && id !== 4023 || len < 52) return;
|
|
7411
7553
|
messages++;
|
|
7412
|
-
const
|
|
7413
|
-
if (
|
|
7414
|
-
|
|
7415
|
-
const source = data[b + 17] & 31;
|
|
7416
|
-
const page = new Uint8Array(32);
|
|
7417
|
-
for (let k = 0; k < 8; k++) {
|
|
7418
|
-
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
7419
|
-
page[4 * k] = w >>> 24;
|
|
7420
|
-
page[4 * k + 1] = w >>> 16 & 255;
|
|
7421
|
-
page[4 * k + 2] = w >>> 8 & 255;
|
|
7422
|
-
page[4 * k + 3] = w & 255;
|
|
7423
|
-
}
|
|
7424
|
-
let eph;
|
|
7425
|
-
let src;
|
|
7426
|
-
if (id === 4023) {
|
|
7427
|
-
src = "inav";
|
|
7428
|
-
if (!INAV_SOURCES.includes(source)) return;
|
|
7429
|
-
if (getBitU22(page, 0) !== 0 || getBitU22(page, 114) !== 1) return;
|
|
7430
|
-
if (!galInavPageCrcOk(page)) {
|
|
7431
|
-
badCrc++;
|
|
7432
|
-
return;
|
|
7433
|
-
}
|
|
7434
|
-
eph = inav.push(prn, page);
|
|
7435
|
-
} else {
|
|
7436
|
-
src = "fnav";
|
|
7437
|
-
if (!FNAV_SOURCES.includes(source)) return;
|
|
7438
|
-
if (isFnavDummy(page)) return;
|
|
7439
|
-
if (!galFnavPageCrcOk(page)) {
|
|
7440
|
-
badCrc++;
|
|
7441
|
-
return;
|
|
7442
|
-
}
|
|
7443
|
-
eph = fnav.push(prn, page);
|
|
7444
|
-
}
|
|
7445
|
-
if (eph) ephemerides.push({ ...eph, source: src });
|
|
7554
|
+
const r = feedGalBlock(data, view, b, id, asm);
|
|
7555
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
7556
|
+
else if (r.badCrc) badCrc++;
|
|
7446
7557
|
});
|
|
7447
7558
|
return { ephemerides, badCrc, messages };
|
|
7448
7559
|
}
|
|
@@ -7457,7 +7568,26 @@ function isFnavDummy(page) {
|
|
|
7457
7568
|
var GPS_EPOCH_MS10 = Date.UTC(1980, 0, 6);
|
|
7458
7569
|
var MS_PER_WEEK5 = 7 * 86400 * 1e3;
|
|
7459
7570
|
var TOW_DNU = 4294967295;
|
|
7460
|
-
var
|
|
7571
|
+
var WNC_DNU2 = 65535;
|
|
7572
|
+
function feedBdsBlock(view, b, assembler) {
|
|
7573
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
7574
|
+
if (!prn || prn[0] !== "C") return {};
|
|
7575
|
+
const sf = new Uint8Array(38);
|
|
7576
|
+
for (let k = 0; k < 10; k++) {
|
|
7577
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
7578
|
+
sf[4 * k] = w >>> 24;
|
|
7579
|
+
if (k < 9) {
|
|
7580
|
+
sf[4 * k + 1] = w >>> 16 & 255;
|
|
7581
|
+
sf[4 * k + 2] = w >>> 8 & 255;
|
|
7582
|
+
sf[4 * k + 3] = w & 255;
|
|
7583
|
+
} else {
|
|
7584
|
+
sf[37] = w >>> 16 & 240;
|
|
7585
|
+
}
|
|
7586
|
+
}
|
|
7587
|
+
if (!bdsSubframeParityOk(sf)) return { badCrc: true };
|
|
7588
|
+
const eph = assembler.push(prn, sf);
|
|
7589
|
+
return eph ? { eph } : {};
|
|
7590
|
+
}
|
|
7461
7591
|
function parseSbfBdsNav(data) {
|
|
7462
7592
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7463
7593
|
const ephemerides = [];
|
|
@@ -7467,29 +7597,41 @@ function parseSbfBdsNav(data) {
|
|
|
7467
7597
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
7468
7598
|
if (id !== 4047 || len < 60) return;
|
|
7469
7599
|
messages++;
|
|
7470
|
-
const
|
|
7471
|
-
if (
|
|
7472
|
-
|
|
7473
|
-
for (let k = 0; k < 10; k++) {
|
|
7474
|
-
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
7475
|
-
sf[4 * k] = w >>> 24;
|
|
7476
|
-
if (k < 9) {
|
|
7477
|
-
sf[4 * k + 1] = w >>> 16 & 255;
|
|
7478
|
-
sf[4 * k + 2] = w >>> 8 & 255;
|
|
7479
|
-
sf[4 * k + 3] = w & 255;
|
|
7480
|
-
} else {
|
|
7481
|
-
sf[37] = w >>> 16 & 240;
|
|
7482
|
-
}
|
|
7483
|
-
}
|
|
7484
|
-
if (!bdsSubframeParityOk(sf)) {
|
|
7485
|
-
badCrc++;
|
|
7486
|
-
return;
|
|
7487
|
-
}
|
|
7488
|
-
const eph = assembler.push(prn, sf);
|
|
7489
|
-
if (eph) ephemerides.push(eph);
|
|
7600
|
+
const r = feedBdsBlock(view, b, assembler);
|
|
7601
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
7602
|
+
else if (r.badCrc) badCrc++;
|
|
7490
7603
|
});
|
|
7491
7604
|
return { ephemerides, badCrc, messages };
|
|
7492
7605
|
}
|
|
7606
|
+
function feedGloBlock(view, b, assembler) {
|
|
7607
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
7608
|
+
if (!prn || prn[0] !== "R") return {};
|
|
7609
|
+
const towMs = view.getUint32(b + 8, true);
|
|
7610
|
+
const wnc = view.getUint16(b + 12, true);
|
|
7611
|
+
if (towMs === TOW_DNU || wnc === WNC_DNU2) return {};
|
|
7612
|
+
const str2 = new Uint8Array(11);
|
|
7613
|
+
for (let k = 0; k < 3; k++) {
|
|
7614
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
7615
|
+
str2[4 * k] = w >>> 24;
|
|
7616
|
+
if (k < 2) {
|
|
7617
|
+
str2[4 * k + 1] = w >>> 16 & 255;
|
|
7618
|
+
str2[4 * k + 2] = w >>> 8 & 255;
|
|
7619
|
+
str2[4 * k + 3] = w & 255;
|
|
7620
|
+
} else {
|
|
7621
|
+
str2[9] = w >>> 16 & 255;
|
|
7622
|
+
str2[10] = w >>> 8 & 248;
|
|
7623
|
+
}
|
|
7624
|
+
}
|
|
7625
|
+
if (!testGloString(str2)) return { badCrc: true };
|
|
7626
|
+
const eph = assembler.push(
|
|
7627
|
+
prn,
|
|
7628
|
+
str2,
|
|
7629
|
+
new Date(GPS_EPOCH_MS10 + wnc * MS_PER_WEEK5 + towMs),
|
|
7630
|
+
view.getUint8(b + 18) - 8
|
|
7631
|
+
// FreqNr, offset by 8
|
|
7632
|
+
);
|
|
7633
|
+
return eph ? { eph } : {};
|
|
7634
|
+
}
|
|
7493
7635
|
function parseSbfGloNav(data) {
|
|
7494
7636
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7495
7637
|
const ephemerides = [];
|
|
@@ -7499,38 +7641,161 @@ function parseSbfGloNav(data) {
|
|
|
7499
7641
|
scanSbfFrames(data, view, (id, b, len) => {
|
|
7500
7642
|
if (id !== 4026 || len < 32) return;
|
|
7501
7643
|
messages++;
|
|
7502
|
-
const
|
|
7503
|
-
if (
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7644
|
+
const r = feedGloBlock(view, b, assembler);
|
|
7645
|
+
if (r.eph) ephemerides.push(r.eph);
|
|
7646
|
+
else if (r.badCrc) badCrc++;
|
|
7647
|
+
});
|
|
7648
|
+
return { ephemerides, badCrc, messages };
|
|
7649
|
+
}
|
|
7650
|
+
|
|
7651
|
+
// src/sbf/navigation.ts
|
|
7652
|
+
var F4_DNU4 = -2e10;
|
|
7653
|
+
function ephKey(e) {
|
|
7654
|
+
const source = "source" in e ? e.source : "";
|
|
7655
|
+
return `${e.prn}|${e.tocDate.getTime()}|${source}`;
|
|
7656
|
+
}
|
|
7657
|
+
function decodeSbfNavigation(data) {
|
|
7658
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7659
|
+
const ephemerides = [];
|
|
7660
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7661
|
+
const cnav = [];
|
|
7662
|
+
const ionoCorrections = {};
|
|
7663
|
+
let leapSeconds = null;
|
|
7664
|
+
const counts = {
|
|
7665
|
+
decodedNav: 0,
|
|
7666
|
+
gpsLnavRaw: 0,
|
|
7667
|
+
cnavRaw: 0,
|
|
7668
|
+
galRaw: 0,
|
|
7669
|
+
gloRaw: 0,
|
|
7670
|
+
bdsRaw: 0,
|
|
7671
|
+
badFrames: 0
|
|
7672
|
+
};
|
|
7673
|
+
const cnavAsm = newCnavAssemblers();
|
|
7674
|
+
const galAsm = newGalAssemblers();
|
|
7675
|
+
const gloAsm = new GloStringAssembler();
|
|
7676
|
+
const bdsAsm = new BdsAssembler();
|
|
7677
|
+
const lnavAsm = new GpsLnavAssembler();
|
|
7678
|
+
const addEph = (e) => {
|
|
7679
|
+
if (!e) return;
|
|
7680
|
+
const key = ephKey(e);
|
|
7681
|
+
if (seen.has(key)) return;
|
|
7682
|
+
seen.add(key);
|
|
7683
|
+
ephemerides.push(e);
|
|
7684
|
+
};
|
|
7685
|
+
const f4s = (b, n) => {
|
|
7686
|
+
const out = [];
|
|
7687
|
+
for (let k = 0; k < n; k++) {
|
|
7688
|
+
const v = view.getFloat32(b + 4 * k, true);
|
|
7689
|
+
if (v === F4_DNU4) return null;
|
|
7690
|
+
out.push(v);
|
|
7519
7691
|
}
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7692
|
+
return out;
|
|
7693
|
+
};
|
|
7694
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
7695
|
+
switch (id) {
|
|
7696
|
+
/* ---- decoded ephemeris blocks ---- */
|
|
7697
|
+
case 5891:
|
|
7698
|
+
if (len >= 140) {
|
|
7699
|
+
counts.decodedNav++;
|
|
7700
|
+
addEph(decodeGpsQzsNav(view, b, "G"));
|
|
7701
|
+
}
|
|
7702
|
+
return;
|
|
7703
|
+
case 4095:
|
|
7704
|
+
if (len >= 140) {
|
|
7705
|
+
counts.decodedNav++;
|
|
7706
|
+
addEph(decodeGpsQzsNav(view, b, "J"));
|
|
7707
|
+
}
|
|
7708
|
+
return;
|
|
7709
|
+
case 4002:
|
|
7710
|
+
if (len >= 149) {
|
|
7711
|
+
counts.decodedNav++;
|
|
7712
|
+
addEph(decodeGalNav(view, b));
|
|
7713
|
+
}
|
|
7714
|
+
return;
|
|
7715
|
+
case 4081:
|
|
7716
|
+
if (len >= 140) {
|
|
7717
|
+
counts.decodedNav++;
|
|
7718
|
+
addEph(decodeBdsNav(view, b));
|
|
7719
|
+
}
|
|
7720
|
+
return;
|
|
7721
|
+
case 4004:
|
|
7722
|
+
if (len >= 96) {
|
|
7723
|
+
counts.decodedNav++;
|
|
7724
|
+
addEph(decodeGloNav(view, b));
|
|
7725
|
+
}
|
|
7726
|
+
return;
|
|
7727
|
+
/* ---- raw navigation-frame blocks ---- */
|
|
7728
|
+
case 4017:
|
|
7729
|
+
// GPSRawCA
|
|
7730
|
+
case 4066: {
|
|
7731
|
+
if (len < 60) return;
|
|
7732
|
+
counts.gpsLnavRaw++;
|
|
7733
|
+
const r = feedGpsLnavBlock(view, b, lnavAsm);
|
|
7734
|
+
if (r.eph) addEph(r.eph);
|
|
7735
|
+
else if (r.badParity) counts.badFrames++;
|
|
7736
|
+
return;
|
|
7737
|
+
}
|
|
7738
|
+
case 4018:
|
|
7739
|
+
// GPSRawL2C
|
|
7740
|
+
case 4019: {
|
|
7741
|
+
if (len < 60) return;
|
|
7742
|
+
counts.cnavRaw++;
|
|
7743
|
+
const r = feedCnavBlock(view, b, id, cnavAsm);
|
|
7744
|
+
if (r.eph) cnav.push(r.eph);
|
|
7745
|
+
else if (r.badCrc) counts.badFrames++;
|
|
7746
|
+
return;
|
|
7747
|
+
}
|
|
7748
|
+
case 4022:
|
|
7749
|
+
// GALRawFNAV
|
|
7750
|
+
case 4023: {
|
|
7751
|
+
if (len < 52) return;
|
|
7752
|
+
counts.galRaw++;
|
|
7753
|
+
const r = feedGalBlock(data, view, b, id, galAsm);
|
|
7754
|
+
if (r.eph) addEph(r.eph);
|
|
7755
|
+
else if (r.badCrc) counts.badFrames++;
|
|
7756
|
+
return;
|
|
7757
|
+
}
|
|
7758
|
+
case 4026: {
|
|
7759
|
+
if (len < 32) return;
|
|
7760
|
+
counts.gloRaw++;
|
|
7761
|
+
const r = feedGloBlock(view, b, gloAsm);
|
|
7762
|
+
if (r.eph) addEph(r.eph);
|
|
7763
|
+
else if (r.badCrc) counts.badFrames++;
|
|
7764
|
+
return;
|
|
7765
|
+
}
|
|
7766
|
+
case 4047: {
|
|
7767
|
+
if (len < 60) return;
|
|
7768
|
+
counts.bdsRaw++;
|
|
7769
|
+
const r = feedBdsBlock(view, b, bdsAsm);
|
|
7770
|
+
if (r.eph) addEph(r.eph);
|
|
7771
|
+
else if (r.badCrc) counts.badFrames++;
|
|
7772
|
+
return;
|
|
7773
|
+
}
|
|
7774
|
+
/* ---- iono / UTC ---- */
|
|
7775
|
+
case 5893:
|
|
7776
|
+
// GPSIon
|
|
7777
|
+
case 4120: {
|
|
7778
|
+
if (len < 48) return;
|
|
7779
|
+
const alpha = f4s(b + 16, 4);
|
|
7780
|
+
const beta = f4s(b + 32, 4);
|
|
7781
|
+
if (!alpha || !beta) return;
|
|
7782
|
+
const sys = id === 5893 ? "GPS" : "BDS";
|
|
7783
|
+
ionoCorrections[`${sys}A`] = alpha;
|
|
7784
|
+
ionoCorrections[`${sys}B`] = beta;
|
|
7785
|
+
return;
|
|
7786
|
+
}
|
|
7787
|
+
case 4030: {
|
|
7788
|
+
if (len < 28) return;
|
|
7789
|
+
const ai = f4s(b + 16, 3);
|
|
7790
|
+
if (ai) ionoCorrections["GAL"] = ai;
|
|
7791
|
+
return;
|
|
7792
|
+
}
|
|
7793
|
+
case 5894:
|
|
7794
|
+
if (len >= 37) leapSeconds = view.getInt8(b + 33);
|
|
7795
|
+
return;
|
|
7523
7796
|
}
|
|
7524
|
-
const eph = assembler.push(
|
|
7525
|
-
prn,
|
|
7526
|
-
str,
|
|
7527
|
-
new Date(GPS_EPOCH_MS10 + wnc * MS_PER_WEEK5 + towMs),
|
|
7528
|
-
view.getUint8(b + 18) - 8
|
|
7529
|
-
// FreqNr, offset by 8
|
|
7530
|
-
);
|
|
7531
|
-
if (eph) ephemerides.push(eph);
|
|
7532
7797
|
});
|
|
7533
|
-
return { ephemerides,
|
|
7798
|
+
return { ephemerides, cnav, ionoCorrections, leapSeconds, counts };
|
|
7534
7799
|
}
|
|
7535
7800
|
|
|
7536
7801
|
// src/navbits/rs255.ts
|
|
@@ -16398,6 +16663,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
16398
16663
|
decodeBinexEph,
|
|
16399
16664
|
decodeEphemeris,
|
|
16400
16665
|
decodeMsmFull,
|
|
16666
|
+
decodeSbfNavigation,
|
|
16401
16667
|
deg2dms,
|
|
16402
16668
|
deg2rad,
|
|
16403
16669
|
detrendIonoArcs,
|
|
@@ -16516,10 +16782,12 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
16516
16782
|
parseSbfCnav,
|
|
16517
16783
|
parseSbfGalNav,
|
|
16518
16784
|
parseSbfGloNav,
|
|
16785
|
+
parseSbfGpsNav,
|
|
16519
16786
|
parseSbfHas,
|
|
16520
16787
|
parseSbfIonoUtc,
|
|
16521
16788
|
parseSbfMeas,
|
|
16522
16789
|
parseSbfNav,
|
|
16790
|
+
parseSbfReceiverSetup,
|
|
16523
16791
|
parseSinexBiasDcb,
|
|
16524
16792
|
parseSourcetable,
|
|
16525
16793
|
parseSp3,
|