gnss-js 1.14.1 → 1.15.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-YIHO74OS.js → chunk-BEQSEJMU.js} +390 -67
- package/dist/{chunk-SWKWJEGY.js → chunk-I4UV45NT.js} +269 -15
- package/dist/index.cjs +671 -96
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +15 -3
- package/dist/novatel.cjs +357 -14
- package/dist/novatel.d.cts +69 -1
- package/dist/novatel.d.ts +69 -1
- package/dist/novatel.js +11 -1
- package/dist/sbf.cjs +478 -68
- package/dist/sbf.d.cts +138 -1
- package/dist/sbf.d.ts +138 -1
- package/dist/sbf.js +9 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -76,6 +76,7 @@ __export(src_exports, {
|
|
|
76
76
|
MILLISECONDS_IN_WEEK: () => MILLISECONDS_IN_WEEK,
|
|
77
77
|
MILLISECONDS_TT_TAI: () => MILLISECONDS_TT_TAI,
|
|
78
78
|
MultipathAccumulator: () => MultipathAccumulator,
|
|
79
|
+
OEM4_HLEN: () => OEM4_HLEN,
|
|
79
80
|
OMEGA_E: () => OMEGA_E,
|
|
80
81
|
REFERENCE_FRAMES: () => REFERENCE_FRAMES,
|
|
81
82
|
RINEX_CODES: () => RINEX_CODES,
|
|
@@ -119,6 +120,7 @@ __export(src_exports, {
|
|
|
119
120
|
computeVisibility: () => computeVisibility,
|
|
120
121
|
computeVisibilityFromPositions: () => computeVisibilityFromPositions,
|
|
121
122
|
connectToMountpoint: () => connectToMountpoint,
|
|
123
|
+
crc32: () => crc32,
|
|
122
124
|
createGzipLineSink: () => createGzipLineSink,
|
|
123
125
|
createStationMeta: () => createStationMeta,
|
|
124
126
|
createStreamStats: () => createStreamStats,
|
|
@@ -208,6 +210,7 @@ __export(src_exports, {
|
|
|
208
210
|
msmEpochToDate: () => msmEpochToDate,
|
|
209
211
|
navTimesFromEph: () => navTimesFromEph,
|
|
210
212
|
nmeaCoordToDecimal: () => nmeaCoordToDecimal,
|
|
213
|
+
oem4Frames: () => oem4Frames,
|
|
211
214
|
padL: () => padL,
|
|
212
215
|
padR: () => padR,
|
|
213
216
|
parseAntex: () => parseAntex,
|
|
@@ -215,9 +218,12 @@ __export(src_exports, {
|
|
|
215
218
|
parseIonex: () => parseIonex,
|
|
216
219
|
parseNavFile: () => parseNavFile,
|
|
217
220
|
parseNmeaFile: () => parseNmeaFile,
|
|
221
|
+
parseNovatelNav: () => parseNovatelNav,
|
|
218
222
|
parseNovatelRange: () => parseNovatelRange,
|
|
219
223
|
parseRinexStream: () => parseRinexStream,
|
|
224
|
+
parseSbfAlmanac: () => parseSbfAlmanac,
|
|
220
225
|
parseSbfMeas: () => parseSbfMeas,
|
|
226
|
+
parseSbfNav: () => parseSbfNav,
|
|
221
227
|
parseSinexBiasDcb: () => parseSinexBiasDcb,
|
|
222
228
|
parseSourcetable: () => parseSourcetable,
|
|
223
229
|
parseSp3: () => parseSp3,
|
|
@@ -4610,20 +4616,20 @@ var SIGNALS = {
|
|
|
4610
4616
|
// GLONASS L1OF, L2OF
|
|
4611
4617
|
};
|
|
4612
4618
|
function prnFor(gnssId, svId) {
|
|
4613
|
-
const
|
|
4619
|
+
const two3 = (n) => String(n).padStart(2, "0");
|
|
4614
4620
|
switch (gnssId) {
|
|
4615
4621
|
case 0:
|
|
4616
|
-
return svId >= 1 && svId <= 32 ? `G${
|
|
4622
|
+
return svId >= 1 && svId <= 32 ? `G${two3(svId)}` : null;
|
|
4617
4623
|
case 1:
|
|
4618
|
-
return svId >= 120 && svId <= 158 ? `S${
|
|
4624
|
+
return svId >= 120 && svId <= 158 ? `S${two3(svId - 100)}` : null;
|
|
4619
4625
|
case 2:
|
|
4620
|
-
return svId >= 1 && svId <= 36 ? `E${
|
|
4626
|
+
return svId >= 1 && svId <= 36 ? `E${two3(svId)}` : null;
|
|
4621
4627
|
case 3:
|
|
4622
|
-
return svId >= 1 && svId <= 63 ? `C${
|
|
4628
|
+
return svId >= 1 && svId <= 63 ? `C${two3(svId)}` : null;
|
|
4623
4629
|
case 5:
|
|
4624
|
-
return svId >= 1 && svId <= 10 ? `J${
|
|
4630
|
+
return svId >= 1 && svId <= 10 ? `J${two3(svId)}` : null;
|
|
4625
4631
|
case 6:
|
|
4626
|
-
return svId >= 1 && svId <= 32 ? `R${
|
|
4632
|
+
return svId >= 1 && svId <= 32 ? `R${two3(svId)}` : null;
|
|
4627
4633
|
default:
|
|
4628
4634
|
return null;
|
|
4629
4635
|
}
|
|
@@ -4705,10 +4711,7 @@ function parseUbxRawx(data) {
|
|
|
4705
4711
|
return { epochs, messageCounts, obsCodes, badChecksums };
|
|
4706
4712
|
}
|
|
4707
4713
|
|
|
4708
|
-
// src/sbf/
|
|
4709
|
-
var CLIGHT = 299792458;
|
|
4710
|
-
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
4711
|
-
var MS_PER_WEEK3 = 7 * 864e5;
|
|
4714
|
+
// src/sbf/frame.ts
|
|
4712
4715
|
var CRC_TABLE = (() => {
|
|
4713
4716
|
const t = new Uint16Array(256);
|
|
4714
4717
|
for (let n = 0; n < 256; n++) {
|
|
@@ -4725,6 +4728,362 @@ function crc16(data, start, end) {
|
|
|
4725
4728
|
crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
|
|
4726
4729
|
return crc;
|
|
4727
4730
|
}
|
|
4731
|
+
function scanSbfFrames(data, view, onBlock) {
|
|
4732
|
+
let badCrc = 0;
|
|
4733
|
+
let i = 0;
|
|
4734
|
+
while (i + 8 <= data.length) {
|
|
4735
|
+
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
4736
|
+
i++;
|
|
4737
|
+
continue;
|
|
4738
|
+
}
|
|
4739
|
+
const len = view.getUint16(i + 6, true);
|
|
4740
|
+
if (len < 8 || len % 4 !== 0) {
|
|
4741
|
+
i++;
|
|
4742
|
+
continue;
|
|
4743
|
+
}
|
|
4744
|
+
if (i + len > data.length) break;
|
|
4745
|
+
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
4746
|
+
badCrc++;
|
|
4747
|
+
i++;
|
|
4748
|
+
continue;
|
|
4749
|
+
}
|
|
4750
|
+
onBlock(view.getUint16(i + 4, true) & 8191, i, len);
|
|
4751
|
+
i += len;
|
|
4752
|
+
}
|
|
4753
|
+
return badCrc;
|
|
4754
|
+
}
|
|
4755
|
+
var two = (n) => String(n).padStart(2, "0");
|
|
4756
|
+
function svidToPrn(svid) {
|
|
4757
|
+
if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
|
|
4758
|
+
if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
|
|
4759
|
+
if (svid <= 62) return null;
|
|
4760
|
+
if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
|
|
4761
|
+
if (svid <= 70) return null;
|
|
4762
|
+
if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
|
|
4763
|
+
if (svid <= 119) return null;
|
|
4764
|
+
if (svid <= 140) return `S${two(svid - 100)}`;
|
|
4765
|
+
if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
|
|
4766
|
+
if (svid <= 190) return `J${two(svid - 180)}`;
|
|
4767
|
+
if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
|
|
4768
|
+
if (svid <= 215) return `S${two(svid - 157)}`;
|
|
4769
|
+
if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
|
|
4770
|
+
if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
|
|
4771
|
+
return null;
|
|
4772
|
+
}
|
|
4773
|
+
|
|
4774
|
+
// src/sbf/nav.ts
|
|
4775
|
+
var PI2 = Math.PI;
|
|
4776
|
+
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
4777
|
+
var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
|
|
4778
|
+
var SEC_PER_WEEK = 7 * 86400;
|
|
4779
|
+
var MS_PER_WEEK3 = SEC_PER_WEEK * 1e3;
|
|
4780
|
+
var F4_DNU = -2e10;
|
|
4781
|
+
var U4_DNU = 4294967295;
|
|
4782
|
+
function adjustWeek(refWeek, wn, mod) {
|
|
4783
|
+
let offset = refWeek % mod - wn;
|
|
4784
|
+
if (offset > mod / 2) offset -= mod;
|
|
4785
|
+
if (offset < -(mod / 2 - 1)) offset += mod;
|
|
4786
|
+
return refWeek - offset;
|
|
4787
|
+
}
|
|
4788
|
+
var gpsMs = (week, sec) => GPS_EPOCH_MS2 + week * MS_PER_WEEK3 + sec * 1e3;
|
|
4789
|
+
var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
|
|
4790
|
+
function decodeGpsQzsNav(view, b, sys) {
|
|
4791
|
+
const svid = view.getUint8(b + 14);
|
|
4792
|
+
const prn = sys === "G" ? svid : svid - 180;
|
|
4793
|
+
const max = sys === "G" ? 32 : 10;
|
|
4794
|
+
if (prn < 1 || prn > max) return null;
|
|
4795
|
+
const wnc = view.getUint16(b + 12, true);
|
|
4796
|
+
const tgdRaw = view.getFloat32(b + 28, true);
|
|
4797
|
+
const tocs = view.getUint32(b + 32, true);
|
|
4798
|
+
const toes = view.getUint32(b + 88, true);
|
|
4799
|
+
const wnToc = adjustWeek(wnc, view.getUint16(b + 136, true), 1024);
|
|
4800
|
+
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
4801
|
+
return {
|
|
4802
|
+
system: sys,
|
|
4803
|
+
prn: `${sys}${String(prn).padStart(2, "0")}`,
|
|
4804
|
+
toc: sowOf(tocDate.getTime()),
|
|
4805
|
+
tocDate,
|
|
4806
|
+
af0: view.getFloat32(b + 44, true),
|
|
4807
|
+
af1: view.getFloat32(b + 40, true),
|
|
4808
|
+
af2: view.getFloat32(b + 36, true),
|
|
4809
|
+
iode: view.getUint8(b + 24),
|
|
4810
|
+
crs: view.getFloat32(b + 48, true),
|
|
4811
|
+
deltaN: view.getFloat32(b + 52, true) * PI2,
|
|
4812
|
+
m0: view.getFloat64(b + 56, true) * PI2,
|
|
4813
|
+
cuc: view.getFloat32(b + 64, true),
|
|
4814
|
+
e: view.getFloat64(b + 68, true),
|
|
4815
|
+
cus: view.getFloat32(b + 76, true),
|
|
4816
|
+
sqrtA: view.getFloat64(b + 80, true),
|
|
4817
|
+
toe: toes,
|
|
4818
|
+
cic: view.getFloat32(b + 92, true),
|
|
4819
|
+
omega0: view.getFloat64(b + 96, true) * PI2,
|
|
4820
|
+
cis: view.getFloat32(b + 104, true),
|
|
4821
|
+
i0: view.getFloat64(b + 108, true) * PI2,
|
|
4822
|
+
crc: view.getFloat32(b + 116, true),
|
|
4823
|
+
omega: view.getFloat64(b + 120, true) * PI2,
|
|
4824
|
+
omegaDot: view.getFloat32(b + 128, true) * PI2,
|
|
4825
|
+
idot: view.getFloat32(b + 132, true) * PI2,
|
|
4826
|
+
// RINEX "GPS week to go with toe": RTKLIB writes WNc for GPS and
|
|
4827
|
+
// the week of toc for QZSS — mirrored here so records compare
|
|
4828
|
+
// 1:1 against convbin output. wnToe is still used for `toe` above.
|
|
4829
|
+
week: sys === "G" ? wnc : wnToc,
|
|
4830
|
+
svHealth: view.getUint8(b + 20),
|
|
4831
|
+
tgd: tgdRaw !== F4_DNU ? tgdRaw : 0
|
|
4832
|
+
};
|
|
4833
|
+
}
|
|
4834
|
+
function decodeGalNav(view, b) {
|
|
4835
|
+
const prn = view.getUint8(b + 14) - 70;
|
|
4836
|
+
if (prn < 1 || prn > 36) return null;
|
|
4837
|
+
const source = view.getUint8(b + 15);
|
|
4838
|
+
if (source !== 2 && source !== 16) return null;
|
|
4839
|
+
const wnc = view.getUint16(b + 12, true);
|
|
4840
|
+
const toes = view.getUint32(b + 100, true);
|
|
4841
|
+
const tocs = view.getUint32(b + 104, true);
|
|
4842
|
+
const wnToc = adjustWeek(wnc, view.getUint16(b + 126, true), 4096);
|
|
4843
|
+
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
4844
|
+
const health = view.getUint16(b + 130, true);
|
|
4845
|
+
let svh = 0;
|
|
4846
|
+
if (health & 1) svh |= health >> 1 & 7;
|
|
4847
|
+
if (health & 16) svh |= (health >> 5 & 7) << 6;
|
|
4848
|
+
if (health & 256) svh |= (health >> 9 & 7) << 3;
|
|
4849
|
+
const bgdE5a = view.getFloat32(b + 136, true);
|
|
4850
|
+
return {
|
|
4851
|
+
system: "E",
|
|
4852
|
+
prn: `E${String(prn).padStart(2, "0")}`,
|
|
4853
|
+
toc: sowOf(tocDate.getTime()),
|
|
4854
|
+
tocDate,
|
|
4855
|
+
af0: view.getFloat64(b + 116, true),
|
|
4856
|
+
af1: view.getFloat32(b + 112, true),
|
|
4857
|
+
af2: view.getFloat32(b + 108, true),
|
|
4858
|
+
iode: view.getUint16(b + 128, true),
|
|
4859
|
+
// IODnav
|
|
4860
|
+
crs: view.getFloat32(b + 88, true),
|
|
4861
|
+
deltaN: view.getFloat32(b + 72, true) * PI2,
|
|
4862
|
+
m0: view.getFloat64(b + 24, true) * PI2,
|
|
4863
|
+
cuc: view.getFloat32(b + 76, true),
|
|
4864
|
+
e: view.getFloat64(b + 32, true),
|
|
4865
|
+
cus: view.getFloat32(b + 80, true),
|
|
4866
|
+
sqrtA: view.getFloat64(b + 16, true),
|
|
4867
|
+
toe: toes,
|
|
4868
|
+
cic: view.getFloat32(b + 92, true),
|
|
4869
|
+
omega0: view.getFloat64(b + 56, true) * PI2,
|
|
4870
|
+
cis: view.getFloat32(b + 96, true),
|
|
4871
|
+
i0: view.getFloat64(b + 40, true) * PI2,
|
|
4872
|
+
crc: view.getFloat32(b + 84, true),
|
|
4873
|
+
omega: view.getFloat64(b + 48, true) * PI2,
|
|
4874
|
+
omegaDot: view.getFloat32(b + 64, true) * PI2,
|
|
4875
|
+
idot: view.getFloat32(b + 68, true) * PI2,
|
|
4876
|
+
week: wnc,
|
|
4877
|
+
// RINEX GAL week is GPS-aligned and continuous
|
|
4878
|
+
svHealth: svh,
|
|
4879
|
+
tgd: bgdE5a !== F4_DNU ? bgdE5a : 0
|
|
4880
|
+
// BGD E5a/E1 (RINEX slot)
|
|
4881
|
+
};
|
|
4882
|
+
}
|
|
4883
|
+
function decodeBdsNav(view, b) {
|
|
4884
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
4885
|
+
if (!prn || prn[0] !== "C") return null;
|
|
4886
|
+
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
4887
|
+
const tgd1 = view.getFloat32(b + 24, true);
|
|
4888
|
+
const tocs = view.getUint32(b + 32, true);
|
|
4889
|
+
const toes = view.getUint32(b + 88, true);
|
|
4890
|
+
const wnToc = adjustWeek(bdsWeekRef, view.getUint16(b + 136, true), 8192);
|
|
4891
|
+
const wnToe = adjustWeek(bdsWeekRef, view.getUint16(b + 138, true), 8192);
|
|
4892
|
+
const tocDate = new Date(BDT_EPOCH_MS + wnToc * MS_PER_WEEK3 + tocs * 1e3);
|
|
4893
|
+
return {
|
|
4894
|
+
system: "C",
|
|
4895
|
+
prn,
|
|
4896
|
+
toc: sowOf(tocDate.getTime()),
|
|
4897
|
+
tocDate,
|
|
4898
|
+
af0: view.getFloat32(b + 44, true),
|
|
4899
|
+
af1: view.getFloat32(b + 40, true),
|
|
4900
|
+
af2: view.getFloat32(b + 36, true),
|
|
4901
|
+
iode: view.getUint8(b + 21),
|
|
4902
|
+
// AODE
|
|
4903
|
+
crs: view.getFloat32(b + 48, true),
|
|
4904
|
+
deltaN: view.getFloat32(b + 52, true) * PI2,
|
|
4905
|
+
m0: view.getFloat64(b + 56, true) * PI2,
|
|
4906
|
+
cuc: view.getFloat32(b + 64, true),
|
|
4907
|
+
e: view.getFloat64(b + 68, true),
|
|
4908
|
+
cus: view.getFloat32(b + 76, true),
|
|
4909
|
+
sqrtA: view.getFloat64(b + 80, true),
|
|
4910
|
+
toe: toes,
|
|
4911
|
+
cic: view.getFloat32(b + 92, true),
|
|
4912
|
+
omega0: view.getFloat64(b + 96, true) * PI2,
|
|
4913
|
+
cis: view.getFloat32(b + 104, true),
|
|
4914
|
+
i0: view.getFloat64(b + 108, true) * PI2,
|
|
4915
|
+
crc: view.getFloat32(b + 116, true),
|
|
4916
|
+
omega: view.getFloat64(b + 120, true) * PI2,
|
|
4917
|
+
omegaDot: view.getFloat32(b + 128, true) * PI2,
|
|
4918
|
+
idot: view.getFloat32(b + 132, true) * PI2,
|
|
4919
|
+
week: wnToe,
|
|
4920
|
+
// RINEX BDS week field is the BDT week
|
|
4921
|
+
svHealth: view.getUint8(b + 19),
|
|
4922
|
+
// SatH1
|
|
4923
|
+
tgd: tgd1 !== F4_DNU ? tgd1 : 0
|
|
4924
|
+
// TGD1 B1/B3 (RINEX slot)
|
|
4925
|
+
};
|
|
4926
|
+
}
|
|
4927
|
+
function decodeGloNav(view, b) {
|
|
4928
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
4929
|
+
if (!prn || prn[0] !== "R") return null;
|
|
4930
|
+
const wnc = view.getUint16(b + 12, true);
|
|
4931
|
+
const toes = view.getUint32(b + 76, true);
|
|
4932
|
+
const wnToe = adjustWeek(wnc, view.getUint16(b + 80, true), 1024);
|
|
4933
|
+
const toeGpsMs = gpsMs(wnToe, toes);
|
|
4934
|
+
const leapMs = getGpsLeap(new Date(toeGpsMs)) * 1e3;
|
|
4935
|
+
const tocDate = new Date(toeGpsMs - leapMs);
|
|
4936
|
+
const tofGpsMs = gpsMs(wnc, 0) + view.getUint32(b + 8, true);
|
|
4937
|
+
const tofLeapMs = getGpsLeap(new Date(tofGpsMs)) * 1e3;
|
|
4938
|
+
const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS2) / 1e3 % SEC_PER_WEEK;
|
|
4939
|
+
return {
|
|
4940
|
+
system: "R",
|
|
4941
|
+
prn,
|
|
4942
|
+
tocDate,
|
|
4943
|
+
// RINEX stores −τn as the clock bias; SBF carries τn (ICD sign)
|
|
4944
|
+
tauN: -view.getFloat32(b + 68, true),
|
|
4945
|
+
gammaN: view.getFloat32(b + 64, true),
|
|
4946
|
+
messageFrameTime,
|
|
4947
|
+
x: view.getFloat64(b + 16, true),
|
|
4948
|
+
// SBF unit is km, like RINEX
|
|
4949
|
+
xDot: view.getFloat32(b + 40, true),
|
|
4950
|
+
xAcc: view.getFloat32(b + 52, true),
|
|
4951
|
+
y: view.getFloat64(b + 24, true),
|
|
4952
|
+
yDot: view.getFloat32(b + 44, true),
|
|
4953
|
+
yAcc: view.getFloat32(b + 56, true),
|
|
4954
|
+
z: view.getFloat64(b + 32, true),
|
|
4955
|
+
zDot: view.getFloat32(b + 48, true),
|
|
4956
|
+
zAcc: view.getFloat32(b + 60, true),
|
|
4957
|
+
// MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
|
|
4958
|
+
health: view.getUint8(b + 85) >> 2,
|
|
4959
|
+
freqNum: view.getUint8(b + 15) - 8
|
|
4960
|
+
};
|
|
4961
|
+
}
|
|
4962
|
+
function parseSbfNav(data) {
|
|
4963
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
4964
|
+
const ephemerides = [];
|
|
4965
|
+
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
4966
|
+
let eph = null;
|
|
4967
|
+
if (id === 5891 && len >= 140) eph = decodeGpsQzsNav(view, b, "G");
|
|
4968
|
+
else if (id === 4095 && len >= 140) eph = decodeGpsQzsNav(view, b, "J");
|
|
4969
|
+
else if (id === 4002 && len >= 149) eph = decodeGalNav(view, b);
|
|
4970
|
+
else if (id === 4081 && len >= 140) eph = decodeBdsNav(view, b);
|
|
4971
|
+
else if (id === 4004 && len >= 96) eph = decodeGloNav(view, b);
|
|
4972
|
+
if (eph) ephemerides.push(eph);
|
|
4973
|
+
});
|
|
4974
|
+
return { ephemerides, badCrc };
|
|
4975
|
+
}
|
|
4976
|
+
var GAL_SQRT_A_NOMINAL = Math.sqrt(296e5);
|
|
4977
|
+
var GAL_I_NOMINAL = 56 / 180 * PI2;
|
|
4978
|
+
var I_REF_03 = 0.3 * PI2;
|
|
4979
|
+
function decodeGpsGalAlm(view, b, sys) {
|
|
4980
|
+
const wnc = view.getUint16(b + 12, true);
|
|
4981
|
+
const toa = view.getUint32(b + 20, true);
|
|
4982
|
+
if (toa === U4_DNU) return null;
|
|
4983
|
+
let prn;
|
|
4984
|
+
let weekAlm;
|
|
4985
|
+
let i0;
|
|
4986
|
+
let sqrtA = view.getFloat32(b + 32, true);
|
|
4987
|
+
let health;
|
|
4988
|
+
if (sys === "G") {
|
|
4989
|
+
prn = view.getUint8(b + 14);
|
|
4990
|
+
if (prn < 1 || prn > 32) return null;
|
|
4991
|
+
weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 256);
|
|
4992
|
+
i0 = I_REF_03 + view.getFloat32(b + 24, true) * PI2;
|
|
4993
|
+
health = view.getUint8(b + 58);
|
|
4994
|
+
} else {
|
|
4995
|
+
prn = view.getUint8(b + 57) - 70;
|
|
4996
|
+
if (prn < 1 || prn > 36) return null;
|
|
4997
|
+
weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 4);
|
|
4998
|
+
i0 = GAL_I_NOMINAL + view.getFloat32(b + 24, true) * PI2;
|
|
4999
|
+
sqrtA += GAL_SQRT_A_NOMINAL;
|
|
5000
|
+
health = view.getUint16(b + 58, true);
|
|
5001
|
+
}
|
|
5002
|
+
return {
|
|
5003
|
+
system: sys,
|
|
5004
|
+
prn: `${sys}${String(prn).padStart(2, "0")}`,
|
|
5005
|
+
weekAlm,
|
|
5006
|
+
toaSec: toa,
|
|
5007
|
+
sqrtA,
|
|
5008
|
+
e: view.getFloat32(b + 16, true),
|
|
5009
|
+
i0OrDeltaI: i0,
|
|
5010
|
+
omega0: view.getFloat32(b + 36, true) * PI2,
|
|
5011
|
+
omega: view.getFloat32(b + 40, true) * PI2,
|
|
5012
|
+
m0: view.getFloat32(b + 44, true) * PI2,
|
|
5013
|
+
omegaDot: view.getFloat32(b + 28, true) * PI2,
|
|
5014
|
+
af0: view.getFloat32(b + 52, true),
|
|
5015
|
+
af1: view.getFloat32(b + 48, true),
|
|
5016
|
+
health
|
|
5017
|
+
};
|
|
5018
|
+
}
|
|
5019
|
+
function decodeBdsAlm(view, b) {
|
|
5020
|
+
const prnStr = svidToPrn(view.getUint8(b + 14));
|
|
5021
|
+
if (!prnStr || prnStr[0] !== "C") return null;
|
|
5022
|
+
const prn = parseInt(prnStr.slice(1), 10);
|
|
5023
|
+
const toa = view.getUint32(b + 16, true);
|
|
5024
|
+
if (toa === U4_DNU) return null;
|
|
5025
|
+
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
5026
|
+
const geo = prn <= 5 || prn >= 59;
|
|
5027
|
+
return {
|
|
5028
|
+
system: "C",
|
|
5029
|
+
prn: prnStr,
|
|
5030
|
+
weekAlm: adjustWeek(bdsWeekRef, view.getUint8(b + 15), 256),
|
|
5031
|
+
toaSec: toa,
|
|
5032
|
+
sqrtA: view.getFloat32(b + 20, true),
|
|
5033
|
+
e: view.getFloat32(b + 24, true),
|
|
5034
|
+
i0OrDeltaI: (geo ? 0 : I_REF_03) + view.getFloat32(b + 44, true) * PI2,
|
|
5035
|
+
omega0: view.getFloat32(b + 36, true) * PI2,
|
|
5036
|
+
omega: view.getFloat32(b + 28, true) * PI2,
|
|
5037
|
+
m0: view.getFloat32(b + 32, true) * PI2,
|
|
5038
|
+
omegaDot: view.getFloat32(b + 40, true) * PI2,
|
|
5039
|
+
af0: view.getFloat32(b + 48, true),
|
|
5040
|
+
af1: view.getFloat32(b + 52, true),
|
|
5041
|
+
health: view.getUint16(b + 56, true)
|
|
5042
|
+
};
|
|
5043
|
+
}
|
|
5044
|
+
function decodeGloAlm(view, b) {
|
|
5045
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
5046
|
+
if (!prn || prn[0] !== "R") return null;
|
|
5047
|
+
const wnc = view.getUint16(b + 12, true);
|
|
5048
|
+
const toa = view.getUint32(b + 20, true);
|
|
5049
|
+
if (toa === U4_DNU) return null;
|
|
5050
|
+
return {
|
|
5051
|
+
system: "R",
|
|
5052
|
+
prn,
|
|
5053
|
+
freqNr: view.getUint8(b + 15) - 8,
|
|
5054
|
+
weekAlm: adjustWeek(wnc, view.getUint8(b + 52), 256),
|
|
5055
|
+
toaSec: toa,
|
|
5056
|
+
epsilon: view.getFloat32(b + 16, true),
|
|
5057
|
+
deltaI: view.getFloat32(b + 24, true) * PI2,
|
|
5058
|
+
lambda: view.getFloat32(b + 28, true) * PI2,
|
|
5059
|
+
tLambda: view.getFloat32(b + 32, true),
|
|
5060
|
+
omega: view.getFloat32(b + 36, true) * PI2,
|
|
5061
|
+
deltaT: view.getFloat32(b + 40, true),
|
|
5062
|
+
deltaTDot: view.getFloat32(b + 44, true),
|
|
5063
|
+
tau: view.getFloat32(b + 48, true),
|
|
5064
|
+
health: view.getUint8(b + 53),
|
|
5065
|
+
nDay: view.getUint16(b + 54, true),
|
|
5066
|
+
n4: view.getUint8(b + 57)
|
|
5067
|
+
};
|
|
5068
|
+
}
|
|
5069
|
+
function parseSbfAlmanac(data) {
|
|
5070
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
5071
|
+
const almanacs = [];
|
|
5072
|
+
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
5073
|
+
let alm = null;
|
|
5074
|
+
if (id === 5892 && len >= 60) alm = decodeGpsGalAlm(view, b, "G");
|
|
5075
|
+
else if (id === 4003 && len >= 61) alm = decodeGpsGalAlm(view, b, "E");
|
|
5076
|
+
else if (id === 4119 && len >= 60) alm = decodeBdsAlm(view, b);
|
|
5077
|
+
else if (id === 4005 && len >= 60) alm = decodeGloAlm(view, b);
|
|
5078
|
+
if (alm) almanacs.push(alm);
|
|
5079
|
+
});
|
|
5080
|
+
return { almanacs, badCrc };
|
|
5081
|
+
}
|
|
5082
|
+
|
|
5083
|
+
// src/sbf/index.ts
|
|
5084
|
+
var CLIGHT = 299792458;
|
|
5085
|
+
var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
|
|
5086
|
+
var MS_PER_WEEK4 = 7 * 864e5;
|
|
4728
5087
|
var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
|
|
4729
5088
|
var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
|
|
4730
5089
|
var M3_INTERVALS = [
|
|
@@ -4823,44 +5182,27 @@ var MEAS2_SIG = [
|
|
|
4823
5182
|
["J", "1E"],
|
|
4824
5183
|
["J", "5P"]
|
|
4825
5184
|
];
|
|
4826
|
-
var
|
|
5185
|
+
var two2 = (n) => String(n).padStart(2, "0");
|
|
4827
5186
|
function m3Prn(navsys, svid) {
|
|
4828
5187
|
switch (navsys) {
|
|
4829
5188
|
case 0:
|
|
4830
|
-
return svid < 32 ? `G${
|
|
5189
|
+
return svid < 32 ? `G${two2(svid + 1)}` : null;
|
|
4831
5190
|
case 1:
|
|
4832
|
-
return svid < 27 ? `R${
|
|
5191
|
+
return svid < 27 ? `R${two2(svid + 1)}` : null;
|
|
4833
5192
|
case 2:
|
|
4834
|
-
return svid < 36 ? `E${
|
|
5193
|
+
return svid < 36 ? `E${two2(svid + 1)}` : null;
|
|
4835
5194
|
case 3:
|
|
4836
|
-
return svid < 50 ? `C${
|
|
5195
|
+
return svid < 50 ? `C${two2(svid + 1)}` : null;
|
|
4837
5196
|
case 4:
|
|
4838
|
-
return svid <= 38 ? `S${
|
|
5197
|
+
return svid <= 38 ? `S${two2(svid + 20)}` : null;
|
|
4839
5198
|
case 5:
|
|
4840
|
-
return svid < 10 ? `J${
|
|
5199
|
+
return svid < 10 ? `J${two2(svid + 1)}` : null;
|
|
4841
5200
|
case 6:
|
|
4842
|
-
return svid < 14 ? `I${
|
|
5201
|
+
return svid < 14 ? `I${two2(svid + 1)}` : null;
|
|
4843
5202
|
default:
|
|
4844
5203
|
return null;
|
|
4845
5204
|
}
|
|
4846
5205
|
}
|
|
4847
|
-
function svidToPrn(svid) {
|
|
4848
|
-
if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
|
|
4849
|
-
if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
|
|
4850
|
-
if (svid <= 62) return null;
|
|
4851
|
-
if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
|
|
4852
|
-
if (svid <= 70) return null;
|
|
4853
|
-
if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
|
|
4854
|
-
if (svid <= 119) return null;
|
|
4855
|
-
if (svid <= 140) return `S${two(svid - 100)}`;
|
|
4856
|
-
if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
|
|
4857
|
-
if (svid <= 190) return `J${two(svid - 180)}`;
|
|
4858
|
-
if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
|
|
4859
|
-
if (svid <= 215) return `S${two(svid - 157)}`;
|
|
4860
|
-
if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
|
|
4861
|
-
if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
|
|
4862
|
-
return null;
|
|
4863
|
-
}
|
|
4864
5206
|
function carrierFreq(sys, code, fcn) {
|
|
4865
5207
|
const band = code.charCodeAt(0) - 48;
|
|
4866
5208
|
switch (sys) {
|
|
@@ -4923,7 +5265,6 @@ function parseSbfMeas(data) {
|
|
|
4923
5265
|
const epochs = [];
|
|
4924
5266
|
const messageCounts = {};
|
|
4925
5267
|
const obsCodes = {};
|
|
4926
|
-
let badCrc = 0;
|
|
4927
5268
|
let ref = freshRefEpoch(-1);
|
|
4928
5269
|
let curTime = NaN;
|
|
4929
5270
|
let curMeas = [];
|
|
@@ -4954,8 +5295,8 @@ function parseSbfMeas(data) {
|
|
|
4954
5295
|
curTime = timeMs;
|
|
4955
5296
|
}
|
|
4956
5297
|
};
|
|
4957
|
-
const decodeMeas3Ranges = (
|
|
4958
|
-
const o =
|
|
5298
|
+
const decodeMeas3Ranges = (i, tow) => {
|
|
5299
|
+
const o = i + 8;
|
|
4959
5300
|
const constellations = view.getUint16(o + 8, true) & 127;
|
|
4960
5301
|
const misc = data[o + 10];
|
|
4961
5302
|
const version = data[o + 11];
|
|
@@ -5233,8 +5574,8 @@ function parseSbfMeas(data) {
|
|
|
5233
5574
|
}
|
|
5234
5575
|
}
|
|
5235
5576
|
};
|
|
5236
|
-
const decodeMeas3CN0 = (
|
|
5237
|
-
const flags = view.getUint16(
|
|
5577
|
+
const decodeMeas3CN0 = (i, len) => {
|
|
5578
|
+
const flags = view.getUint16(i + 14, true);
|
|
5238
5579
|
if ((flags & 7) !== 0) return;
|
|
5239
5580
|
const maxNibbles = (len - 16) * 2;
|
|
5240
5581
|
let off = 0;
|
|
@@ -5244,7 +5585,7 @@ function parseSbfMeas(data) {
|
|
|
5244
5585
|
for (let k = 0; k < e.slots.length; k++) {
|
|
5245
5586
|
const slot = e.slots[k];
|
|
5246
5587
|
if (k > 0 && !slot) break;
|
|
5247
|
-
const nib = (data[
|
|
5588
|
+
const nib = (data[i + 16 + (off >> 1)] ?? 0) >> (off & 1) * 4 & 15;
|
|
5248
5589
|
if (slot && slot.cn0 !== null) slot.cn0 += nib * 0.0625 - 0.5;
|
|
5249
5590
|
off++;
|
|
5250
5591
|
}
|
|
@@ -5271,14 +5612,14 @@ function parseSbfMeas(data) {
|
|
|
5271
5612
|
if ((v & 1) === 1) value = -value;
|
|
5272
5613
|
return { value, size };
|
|
5273
5614
|
};
|
|
5274
|
-
const decodeMeas3Doppler = (
|
|
5275
|
-
const flags = view.getUint16(
|
|
5615
|
+
const decodeMeas3Doppler = (i, len) => {
|
|
5616
|
+
const flags = view.getUint16(i + 14, true);
|
|
5276
5617
|
if ((flags & 7) !== 0) return;
|
|
5277
5618
|
let off = 0;
|
|
5278
5619
|
for (const e of curSats) {
|
|
5279
5620
|
if (off + 16 >= len) break;
|
|
5280
5621
|
if (!e.prn) continue;
|
|
5281
|
-
const master = readPrRate(
|
|
5622
|
+
const master = readPrRate(i + 16, off);
|
|
5282
5623
|
off += master.size;
|
|
5283
5624
|
if (master.value === -268435455) continue;
|
|
5284
5625
|
const refPrRate = ref.sats[e.navsys][e.svid]?.prRate ?? 0;
|
|
@@ -5288,20 +5629,20 @@ function parseSbfMeas(data) {
|
|
|
5288
5629
|
for (let k = 1; k < e.slots.length; k++) {
|
|
5289
5630
|
const slot = e.slots[k];
|
|
5290
5631
|
if (!slot) break;
|
|
5291
|
-
const slave = readPrRate(
|
|
5632
|
+
const slave = readPrRate(i + 16, off);
|
|
5292
5633
|
off += slave.size;
|
|
5293
5634
|
if (m)
|
|
5294
5635
|
slot.doppler = (mD * (CLIGHT / m.freq) * 1e3 - slave.value) * 1e-3 / (CLIGHT / slot.freq);
|
|
5295
5636
|
}
|
|
5296
5637
|
}
|
|
5297
5638
|
};
|
|
5298
|
-
const decodeMeasEpoch = (
|
|
5299
|
-
const n1 = data[
|
|
5300
|
-
const len1 = data[
|
|
5301
|
-
const len2 = data[
|
|
5302
|
-
if ((data[
|
|
5303
|
-
const end =
|
|
5304
|
-
let p =
|
|
5639
|
+
const decodeMeasEpoch = (i, len) => {
|
|
5640
|
+
const n1 = data[i + 14];
|
|
5641
|
+
const len1 = data[i + 15];
|
|
5642
|
+
const len2 = data[i + 16];
|
|
5643
|
+
if ((data[i + 17] & 128) !== 0) return;
|
|
5644
|
+
const end = i + len;
|
|
5645
|
+
let p = i + 20;
|
|
5305
5646
|
for (let b1 = 0; b1 < n1 && p + 20 <= end; b1++) {
|
|
5306
5647
|
const ant = data[p + 1] >> 5;
|
|
5307
5648
|
let sig = data[p + 1] & 31;
|
|
@@ -5388,50 +5729,288 @@ function parseSbfMeas(data) {
|
|
|
5388
5729
|
}
|
|
5389
5730
|
}
|
|
5390
5731
|
};
|
|
5391
|
-
|
|
5392
|
-
while (i + 8 <= data.length) {
|
|
5393
|
-
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
5394
|
-
i++;
|
|
5395
|
-
continue;
|
|
5396
|
-
}
|
|
5397
|
-
const len = view.getUint16(i + 6, true);
|
|
5398
|
-
if (len < 8 || len % 4 !== 0) {
|
|
5399
|
-
i++;
|
|
5400
|
-
continue;
|
|
5401
|
-
}
|
|
5402
|
-
if (i + len > data.length) break;
|
|
5403
|
-
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
5404
|
-
badCrc++;
|
|
5405
|
-
i++;
|
|
5406
|
-
continue;
|
|
5407
|
-
}
|
|
5408
|
-
const id = view.getUint16(i + 4, true) & 8191;
|
|
5732
|
+
const badCrc = scanSbfFrames(data, view, (id, i, len) => {
|
|
5409
5733
|
const key = String(id);
|
|
5410
5734
|
messageCounts[key] = (messageCounts[key] ?? 0) + 1;
|
|
5411
5735
|
if ((id === 4027 || id === 4109 || id === 4110 || id === 4111) && len >= 16) {
|
|
5412
5736
|
const tow = view.getUint32(i + 8, true);
|
|
5413
5737
|
const wnc = view.getUint16(i + 12, true);
|
|
5414
5738
|
if (tow !== 4294967295 && wnc !== 65535) {
|
|
5415
|
-
ensureEpoch(
|
|
5739
|
+
ensureEpoch(GPS_EPOCH_MS3 + wnc * MS_PER_WEEK4 + tow);
|
|
5416
5740
|
if (id === 4109) decodeMeas3Ranges(i, tow);
|
|
5417
5741
|
else if (id === 4110) decodeMeas3CN0(i, len);
|
|
5418
5742
|
else if (id === 4111) decodeMeas3Doppler(i, len);
|
|
5419
5743
|
else decodeMeasEpoch(i, len);
|
|
5420
5744
|
}
|
|
5421
5745
|
}
|
|
5422
|
-
|
|
5423
|
-
}
|
|
5746
|
+
});
|
|
5424
5747
|
flush();
|
|
5425
5748
|
return { epochs, messageCounts, obsCodes, badCrc };
|
|
5426
5749
|
}
|
|
5427
5750
|
|
|
5751
|
+
// src/novatel/frame.ts
|
|
5752
|
+
var OEM4_HLEN = 28;
|
|
5753
|
+
function crc32(data, start, len) {
|
|
5754
|
+
let crc = 0;
|
|
5755
|
+
for (let i = start; i < start + len; i++) {
|
|
5756
|
+
crc ^= data[i];
|
|
5757
|
+
for (let j = 0; j < 8; j++) {
|
|
5758
|
+
crc = crc & 1 ? crc >>> 1 ^ 3988292384 : crc >>> 1;
|
|
5759
|
+
}
|
|
5760
|
+
}
|
|
5761
|
+
return crc >>> 0;
|
|
5762
|
+
}
|
|
5763
|
+
function* oem4Frames(data, view, stats) {
|
|
5764
|
+
let i = 0;
|
|
5765
|
+
while (i + OEM4_HLEN + 4 <= data.length) {
|
|
5766
|
+
if (data[i] !== 170 || data[i + 1] !== 68 || data[i + 2] !== 18) {
|
|
5767
|
+
i++;
|
|
5768
|
+
continue;
|
|
5769
|
+
}
|
|
5770
|
+
const hlen = data[i + 3];
|
|
5771
|
+
const msgLen = view.getUint16(i + 8, true);
|
|
5772
|
+
const total = hlen + msgLen + 4;
|
|
5773
|
+
if (hlen < OEM4_HLEN || i + total > data.length) {
|
|
5774
|
+
i++;
|
|
5775
|
+
continue;
|
|
5776
|
+
}
|
|
5777
|
+
if (crc32(data, i, hlen + msgLen) !== view.getUint32(i + hlen + msgLen, true)) {
|
|
5778
|
+
stats.badCrc++;
|
|
5779
|
+
i++;
|
|
5780
|
+
continue;
|
|
5781
|
+
}
|
|
5782
|
+
yield {
|
|
5783
|
+
id: view.getUint16(i + 4, true),
|
|
5784
|
+
start: i,
|
|
5785
|
+
payload: i + hlen,
|
|
5786
|
+
msgLen,
|
|
5787
|
+
binary: (data[i + 6] >> 4 & 3) === 0,
|
|
5788
|
+
week: view.getUint16(i + 14, true),
|
|
5789
|
+
towMs: view.getUint32(i + 16, true)
|
|
5790
|
+
};
|
|
5791
|
+
i += total;
|
|
5792
|
+
}
|
|
5793
|
+
}
|
|
5794
|
+
|
|
5795
|
+
// src/navbits/index.ts
|
|
5796
|
+
var GPS_PI = 3.1415926535898;
|
|
5797
|
+
var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
|
|
5798
|
+
var SEC_PER_WEEK2 = 7 * 86400;
|
|
5799
|
+
function getBitU(buff, pos, len) {
|
|
5800
|
+
let bits = 0;
|
|
5801
|
+
for (let i = pos; i < pos + len; i++) {
|
|
5802
|
+
bits = bits * 2 + (buff[i >> 3] >> 7 - (i & 7) & 1);
|
|
5803
|
+
}
|
|
5804
|
+
return bits;
|
|
5805
|
+
}
|
|
5806
|
+
function getBitS(buff, pos, len) {
|
|
5807
|
+
const bits = getBitU(buff, pos, len);
|
|
5808
|
+
if (len <= 0 || bits < 2 ** (len - 1)) return bits;
|
|
5809
|
+
return bits - 2 ** len;
|
|
5810
|
+
}
|
|
5811
|
+
function decodeGpsLnavFrame(subframes, opts = {}) {
|
|
5812
|
+
if (subframes.length < 90) return null;
|
|
5813
|
+
const b = subframes;
|
|
5814
|
+
let i = 24;
|
|
5815
|
+
const tow1 = getBitU(b, i, 17) * 6;
|
|
5816
|
+
i += 17 + 2;
|
|
5817
|
+
const id1 = getBitU(b, i, 3);
|
|
5818
|
+
i += 3 + 2;
|
|
5819
|
+
const week10 = getBitU(b, i, 10);
|
|
5820
|
+
i += 10;
|
|
5821
|
+
i += 2;
|
|
5822
|
+
i += 4;
|
|
5823
|
+
const svHealth = getBitU(b, i, 6);
|
|
5824
|
+
i += 6;
|
|
5825
|
+
const iodc0 = getBitU(b, i, 2);
|
|
5826
|
+
i += 2;
|
|
5827
|
+
i += 1 + 87;
|
|
5828
|
+
const tgdRaw = getBitS(b, i, 8);
|
|
5829
|
+
i += 8;
|
|
5830
|
+
const iodc1 = getBitU(b, i, 8);
|
|
5831
|
+
i += 8;
|
|
5832
|
+
const tocSec = getBitU(b, i, 16) * 16;
|
|
5833
|
+
i += 16;
|
|
5834
|
+
const af2 = getBitS(b, i, 8) * 2 ** -55;
|
|
5835
|
+
i += 8;
|
|
5836
|
+
const af1 = getBitS(b, i, 16) * 2 ** -43;
|
|
5837
|
+
i += 16;
|
|
5838
|
+
const af0 = getBitS(b, i, 22) * 2 ** -31;
|
|
5839
|
+
i = 240 + 24;
|
|
5840
|
+
i += 17 + 2;
|
|
5841
|
+
const id2 = getBitU(b, i, 3);
|
|
5842
|
+
i += 3 + 2;
|
|
5843
|
+
const iode = getBitU(b, i, 8);
|
|
5844
|
+
i += 8;
|
|
5845
|
+
const crs = getBitS(b, i, 16) * 2 ** -5;
|
|
5846
|
+
i += 16;
|
|
5847
|
+
const deltaN = getBitS(b, i, 16) * 2 ** -43 * GPS_PI;
|
|
5848
|
+
i += 16;
|
|
5849
|
+
const m0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
5850
|
+
i += 32;
|
|
5851
|
+
const cuc = getBitS(b, i, 16) * 2 ** -29;
|
|
5852
|
+
i += 16;
|
|
5853
|
+
const e = getBitU(b, i, 32) * 2 ** -33;
|
|
5854
|
+
i += 32;
|
|
5855
|
+
const cus = getBitS(b, i, 16) * 2 ** -29;
|
|
5856
|
+
i += 16;
|
|
5857
|
+
const sqrtA = getBitU(b, i, 32) * 2 ** -19;
|
|
5858
|
+
i += 32;
|
|
5859
|
+
const toes = getBitU(b, i, 16) * 16;
|
|
5860
|
+
i = 480 + 24;
|
|
5861
|
+
i += 17 + 2;
|
|
5862
|
+
const id3 = getBitU(b, i, 3);
|
|
5863
|
+
i += 3 + 2;
|
|
5864
|
+
const cic = getBitS(b, i, 16) * 2 ** -29;
|
|
5865
|
+
i += 16;
|
|
5866
|
+
const omega0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
5867
|
+
i += 32;
|
|
5868
|
+
const cis = getBitS(b, i, 16) * 2 ** -29;
|
|
5869
|
+
i += 16;
|
|
5870
|
+
const i0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
5871
|
+
i += 32;
|
|
5872
|
+
const crc = getBitS(b, i, 16) * 2 ** -5;
|
|
5873
|
+
i += 16;
|
|
5874
|
+
const omega = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
5875
|
+
i += 32;
|
|
5876
|
+
const omegaDot = getBitS(b, i, 24) * 2 ** -43 * GPS_PI;
|
|
5877
|
+
i += 24;
|
|
5878
|
+
const iode3 = getBitU(b, i, 8);
|
|
5879
|
+
i += 8;
|
|
5880
|
+
const idot = getBitS(b, i, 14) * 2 ** -43 * GPS_PI;
|
|
5881
|
+
const iodc = iodc0 * 256 + iodc1;
|
|
5882
|
+
if (id1 !== 1 || id2 !== 2 || id3 !== 3) return null;
|
|
5883
|
+
if (iode3 !== iode || iode !== (iodc & 255)) return null;
|
|
5884
|
+
const ref = opts.refWeek ?? Math.floor((Date.now() - GPS_EPOCH_MS4) / 1e3 / SEC_PER_WEEK2);
|
|
5885
|
+
let week = week10 + 1024 * Math.round((ref - week10) / 1024);
|
|
5886
|
+
if (toes < tow1 - 302400) week++;
|
|
5887
|
+
else if (toes > tow1 + 302400) week--;
|
|
5888
|
+
const prn = opts.prn ?? "G00";
|
|
5889
|
+
const tocDate = new Date(
|
|
5890
|
+
GPS_EPOCH_MS4 + (week * SEC_PER_WEEK2 + tocSec) * 1e3
|
|
5891
|
+
);
|
|
5892
|
+
return {
|
|
5893
|
+
system: prn[0] === "J" ? "J" : "G",
|
|
5894
|
+
prn,
|
|
5895
|
+
tocDate,
|
|
5896
|
+
// Same seconds-of-week convention as parseNavFile (rinex/nav.ts).
|
|
5897
|
+
toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK2,
|
|
5898
|
+
af0,
|
|
5899
|
+
af1,
|
|
5900
|
+
af2,
|
|
5901
|
+
iode,
|
|
5902
|
+
crs,
|
|
5903
|
+
deltaN,
|
|
5904
|
+
m0,
|
|
5905
|
+
cuc,
|
|
5906
|
+
e,
|
|
5907
|
+
cus,
|
|
5908
|
+
sqrtA,
|
|
5909
|
+
toe: toes,
|
|
5910
|
+
cic,
|
|
5911
|
+
omega0,
|
|
5912
|
+
cis,
|
|
5913
|
+
i0,
|
|
5914
|
+
crc,
|
|
5915
|
+
omega,
|
|
5916
|
+
omegaDot,
|
|
5917
|
+
idot,
|
|
5918
|
+
week,
|
|
5919
|
+
svHealth,
|
|
5920
|
+
tgd: tgdRaw === -128 ? 0 : tgdRaw * 2 ** -31
|
|
5921
|
+
// IS-GPS-200: -128 reserved
|
|
5922
|
+
};
|
|
5923
|
+
}
|
|
5924
|
+
|
|
5925
|
+
// src/novatel/nav.ts
|
|
5926
|
+
var ID_RAWEPHEM = 41;
|
|
5927
|
+
var ID_GLOEPHEMERIS = 723;
|
|
5928
|
+
var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
|
|
5929
|
+
var SEC_PER_WEEK3 = 7 * 86400;
|
|
5930
|
+
var SEC_PER_DAY = 86400;
|
|
5931
|
+
function decodeRawEphem(data, view, p) {
|
|
5932
|
+
const prn = view.getUint32(p, true);
|
|
5933
|
+
if (prn < 1 || prn > 32) return null;
|
|
5934
|
+
const refWeek = view.getUint32(p + 4, true);
|
|
5935
|
+
return decodeGpsLnavFrame(data.subarray(p + 12, p + 102), {
|
|
5936
|
+
prn: `G${String(prn).padStart(2, "0")}`,
|
|
5937
|
+
refWeek
|
|
5938
|
+
});
|
|
5939
|
+
}
|
|
5940
|
+
function decodeGloEphemeris(view, p) {
|
|
5941
|
+
const slot = view.getUint16(p, true) - 37;
|
|
5942
|
+
if (slot < 1 || slot > 27) return null;
|
|
5943
|
+
const freqNum = view.getUint16(p + 2, true) - 7;
|
|
5944
|
+
const week = view.getUint16(p + 6, true);
|
|
5945
|
+
const tow = Math.floor(view.getUint32(p + 8, true) / 1e3 + 0.5);
|
|
5946
|
+
const toff = view.getUint32(p + 12, true);
|
|
5947
|
+
const health = view.getUint32(p + 24, true) < 4 ? 0 : 1;
|
|
5948
|
+
let tof = view.getUint32(p + 124, true) - toff;
|
|
5949
|
+
tof += Math.floor(tow / SEC_PER_DAY) * SEC_PER_DAY;
|
|
5950
|
+
if (tof < tow - 43200) tof += SEC_PER_DAY;
|
|
5951
|
+
else if (tof > tow + 43200) tof -= SEC_PER_DAY;
|
|
5952
|
+
const toeGpsMs = GPS_EPOCH_MS5 + (week * SEC_PER_WEEK3 + tow) * 1e3;
|
|
5953
|
+
const tofGpsMs = GPS_EPOCH_MS5 + (week * SEC_PER_WEEK3 + tof) * 1e3;
|
|
5954
|
+
const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() - GPS_EPOCH_MS5) / 1e3;
|
|
5955
|
+
return {
|
|
5956
|
+
system: "R",
|
|
5957
|
+
prn: `R${String(slot).padStart(2, "0")}`,
|
|
5958
|
+
tocDate: getUtcDate(new Date(toeGpsMs)),
|
|
5959
|
+
tauN: -view.getFloat64(p + 100, true),
|
|
5960
|
+
// RINEX stores −τ_n
|
|
5961
|
+
gammaN: view.getFloat64(p + 116, true),
|
|
5962
|
+
messageFrameTime: (tofUtcSec % SEC_PER_WEEK3 + SEC_PER_WEEK3) % SEC_PER_WEEK3,
|
|
5963
|
+
x: view.getFloat64(p + 28, true) / 1e3,
|
|
5964
|
+
y: view.getFloat64(p + 36, true) / 1e3,
|
|
5965
|
+
z: view.getFloat64(p + 44, true) / 1e3,
|
|
5966
|
+
xDot: view.getFloat64(p + 52, true) / 1e3,
|
|
5967
|
+
yDot: view.getFloat64(p + 60, true) / 1e3,
|
|
5968
|
+
zDot: view.getFloat64(p + 68, true) / 1e3,
|
|
5969
|
+
xAcc: view.getFloat64(p + 76, true) / 1e3,
|
|
5970
|
+
yAcc: view.getFloat64(p + 84, true) / 1e3,
|
|
5971
|
+
zAcc: view.getFloat64(p + 92, true) / 1e3,
|
|
5972
|
+
health,
|
|
5973
|
+
freqNum
|
|
5974
|
+
};
|
|
5975
|
+
}
|
|
5976
|
+
function parseNovatelNav(data) {
|
|
5977
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
5978
|
+
const stats = { badCrc: 0 };
|
|
5979
|
+
const ephemerides = [];
|
|
5980
|
+
const lastGps = /* @__PURE__ */ new Map();
|
|
5981
|
+
const lastGlo = /* @__PURE__ */ new Map();
|
|
5982
|
+
for (const frame of oem4Frames(data, view, stats)) {
|
|
5983
|
+
if (!frame.binary) continue;
|
|
5984
|
+
if (frame.id === ID_RAWEPHEM && frame.msgLen >= 102) {
|
|
5985
|
+
const eph = decodeRawEphem(data, view, frame.payload);
|
|
5986
|
+
if (!eph) continue;
|
|
5987
|
+
const prev = lastGps.get(eph.prn);
|
|
5988
|
+
if (prev && prev.iode === eph.iode && prev.tocDate.getTime() === eph.tocDate.getTime()) {
|
|
5989
|
+
continue;
|
|
5990
|
+
}
|
|
5991
|
+
lastGps.set(eph.prn, eph);
|
|
5992
|
+
ephemerides.push(eph);
|
|
5993
|
+
} else if (frame.id === ID_GLOEPHEMERIS && frame.msgLen >= 144) {
|
|
5994
|
+
const eph = decodeGloEphemeris(view, frame.payload);
|
|
5995
|
+
if (!eph) continue;
|
|
5996
|
+
const prev = lastGlo.get(eph.prn);
|
|
5997
|
+
if (prev && Math.abs(prev.tocDate.getTime() - eph.tocDate.getTime()) < 1e3 && prev.health === eph.health) {
|
|
5998
|
+
continue;
|
|
5999
|
+
}
|
|
6000
|
+
lastGlo.set(eph.prn, eph);
|
|
6001
|
+
ephemerides.push(eph);
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
6004
|
+
return { ephemerides, badCrc: stats.badCrc };
|
|
6005
|
+
}
|
|
6006
|
+
|
|
5428
6007
|
// src/novatel/index.ts
|
|
5429
|
-
var
|
|
5430
|
-
var
|
|
6008
|
+
var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
|
|
6009
|
+
var MS_PER_WEEK5 = 7 * 864e5;
|
|
5431
6010
|
var HDR = 28;
|
|
5432
6011
|
var ID_RANGE = 43;
|
|
5433
6012
|
var ID_RANGECMP = 140;
|
|
5434
|
-
var
|
|
6013
|
+
var ID_GLOEPHEMERIS2 = 723;
|
|
5435
6014
|
var C_LIGHT2 = 299792458;
|
|
5436
6015
|
var ADR_ROLL = 8388608;
|
|
5437
6016
|
function carrierFreq2(sys, code, gloK) {
|
|
@@ -5459,16 +6038,6 @@ function carrierFreq2(sys, code, gloK) {
|
|
|
5459
6038
|
function exsign(v, bits) {
|
|
5460
6039
|
return v & 1 << bits - 1 ? v - 2 ** bits : v;
|
|
5461
6040
|
}
|
|
5462
|
-
function crc32(data, start, len) {
|
|
5463
|
-
let crc = 0;
|
|
5464
|
-
for (let i = start; i < start + len; i++) {
|
|
5465
|
-
crc ^= data[i];
|
|
5466
|
-
for (let j = 0; j < 8; j++) {
|
|
5467
|
-
crc = crc & 1 ? crc >>> 1 ^ 3988292384 : crc >>> 1;
|
|
5468
|
-
}
|
|
5469
|
-
}
|
|
5470
|
-
return crc >>> 0;
|
|
5471
|
-
}
|
|
5472
6041
|
var SIGNALS2 = {
|
|
5473
6042
|
0: {
|
|
5474
6043
|
0: ["G", "1C"],
|
|
@@ -5522,7 +6091,7 @@ function parseNovatelRange(data) {
|
|
|
5522
6091
|
const hl = data[j + 3];
|
|
5523
6092
|
const id = view.getUint16(j + 4, true);
|
|
5524
6093
|
const ln = view.getUint16(j + 8, true);
|
|
5525
|
-
if (id !==
|
|
6094
|
+
if (id !== ID_GLOEPHEMERIS2 || hl < HDR || j + hl + ln + 4 > data.length)
|
|
5526
6095
|
continue;
|
|
5527
6096
|
if (crc32(data, j, hl + ln) !== view.getUint32(j + hl + ln, true)) continue;
|
|
5528
6097
|
const slot = view.getUint16(j + hl, true) - 37;
|
|
@@ -5606,7 +6175,7 @@ function parseNovatelRange(data) {
|
|
|
5606
6175
|
if (!codes.includes(code)) codes.push(code);
|
|
5607
6176
|
}
|
|
5608
6177
|
epochs.push({
|
|
5609
|
-
timeMs:
|
|
6178
|
+
timeMs: GPS_EPOCH_MS6 + week * MS_PER_WEEK5 + towMs,
|
|
5610
6179
|
meas
|
|
5611
6180
|
});
|
|
5612
6181
|
}
|
|
@@ -5658,7 +6227,7 @@ function parseNovatelRange(data) {
|
|
|
5658
6227
|
if (!codes.includes(code)) codes.push(code);
|
|
5659
6228
|
}
|
|
5660
6229
|
epochs.push({
|
|
5661
|
-
timeMs:
|
|
6230
|
+
timeMs: GPS_EPOCH_MS6 + week * MS_PER_WEEK5 + towMs,
|
|
5662
6231
|
meas
|
|
5663
6232
|
});
|
|
5664
6233
|
}
|
|
@@ -6017,10 +6586,10 @@ function selectBest(ephs, timeMs) {
|
|
|
6017
6586
|
}
|
|
6018
6587
|
return best;
|
|
6019
6588
|
}
|
|
6020
|
-
var
|
|
6021
|
-
var
|
|
6589
|
+
var GPS_EPOCH_MS7 = START_GPS_TIME.getTime();
|
|
6590
|
+
var MS_PER_WEEK6 = 7 * 864e5;
|
|
6022
6591
|
var BDS_EPOCH_MS = START_BDS_TIME.getTime();
|
|
6023
|
-
var GAL_EPOCH_MS =
|
|
6592
|
+
var GAL_EPOCH_MS = GPS_EPOCH_MS7 + 1024 * MS_PER_WEEK6;
|
|
6024
6593
|
function ephInfoToEphemeris(info) {
|
|
6025
6594
|
const sys = info.prn.charAt(0);
|
|
6026
6595
|
if (sys === "R") {
|
|
@@ -6071,11 +6640,11 @@ function ephInfoToEphemeris(info) {
|
|
|
6071
6640
|
} else if (sys === "E") {
|
|
6072
6641
|
epochMs = GAL_EPOCH_MS;
|
|
6073
6642
|
} else {
|
|
6074
|
-
epochMs =
|
|
6075
|
-
const weeksNow = (info.lastReceived -
|
|
6643
|
+
epochMs = GPS_EPOCH_MS7;
|
|
6644
|
+
const weeksNow = (info.lastReceived - GPS_EPOCH_MS7) / MS_PER_WEEK6;
|
|
6076
6645
|
week += 1024 * Math.round((weeksNow - week) / 1024);
|
|
6077
6646
|
}
|
|
6078
|
-
const tocDate = new Date(epochMs + week *
|
|
6647
|
+
const tocDate = new Date(epochMs + week * MS_PER_WEEK6 + tocSec * 1e3);
|
|
6079
6648
|
return {
|
|
6080
6649
|
system: sys,
|
|
6081
6650
|
prn: info.prn,
|
|
@@ -9028,6 +9597,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
9028
9597
|
MILLISECONDS_IN_WEEK,
|
|
9029
9598
|
MILLISECONDS_TT_TAI,
|
|
9030
9599
|
MultipathAccumulator,
|
|
9600
|
+
OEM4_HLEN,
|
|
9031
9601
|
OMEGA_E,
|
|
9032
9602
|
REFERENCE_FRAMES,
|
|
9033
9603
|
RINEX_CODES,
|
|
@@ -9071,6 +9641,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
9071
9641
|
computeVisibility,
|
|
9072
9642
|
computeVisibilityFromPositions,
|
|
9073
9643
|
connectToMountpoint,
|
|
9644
|
+
crc32,
|
|
9074
9645
|
createGzipLineSink,
|
|
9075
9646
|
createStationMeta,
|
|
9076
9647
|
createStreamStats,
|
|
@@ -9160,6 +9731,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
9160
9731
|
msmEpochToDate,
|
|
9161
9732
|
navTimesFromEph,
|
|
9162
9733
|
nmeaCoordToDecimal,
|
|
9734
|
+
oem4Frames,
|
|
9163
9735
|
padL,
|
|
9164
9736
|
padR,
|
|
9165
9737
|
parseAntex,
|
|
@@ -9167,9 +9739,12 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
9167
9739
|
parseIonex,
|
|
9168
9740
|
parseNavFile,
|
|
9169
9741
|
parseNmeaFile,
|
|
9742
|
+
parseNovatelNav,
|
|
9170
9743
|
parseNovatelRange,
|
|
9171
9744
|
parseRinexStream,
|
|
9745
|
+
parseSbfAlmanac,
|
|
9172
9746
|
parseSbfMeas,
|
|
9747
|
+
parseSbfNav,
|
|
9173
9748
|
parseSinexBiasDcb,
|
|
9174
9749
|
parseSourcetable,
|
|
9175
9750
|
parseSp3,
|