gnss-js 1.14.0 → 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-FGRC3PB3.js → chunk-BRT6XZ7B.js} +4 -3
- package/dist/{chunk-SWKWJEGY.js → chunk-I4UV45NT.js} +269 -15
- package/dist/{chunk-E5CP37P2.js → chunk-QXZZNXE3.js} +1 -1
- package/dist/index.cjs +672 -99
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +53 -41
- 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/orbit.js +2 -2
- package/dist/positioning.js +3 -3
- package/dist/rtcm3.cjs +91 -8
- package/dist/rtcm3.d.cts +8 -0
- package/dist/rtcm3.d.ts +8 -0
- package/dist/rtcm3.js +2 -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/{chunk-VAHMHYID.js → chunk-OMVL3X7V.js} +3 -3
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,
|
|
@@ -4224,7 +4230,7 @@ function msmEpochToDate(sys, epochMs, refTime = /* @__PURE__ */ new Date()) {
|
|
|
4224
4230
|
let t2 = refMs - refMs % dayMs + diff * dayMs + msOfDay - 3 * 36e5;
|
|
4225
4231
|
while (t2 - refMs > 3 * dayMs) t2 -= 7 * dayMs;
|
|
4226
4232
|
while (refMs - t2 > 3 * dayMs) t2 += 7 * dayMs;
|
|
4227
|
-
return new Date(t2);
|
|
4233
|
+
return new Date(t2 + getGpsLeap(new Date(t2)) * 1e3);
|
|
4228
4234
|
}
|
|
4229
4235
|
if (sys === "C") {
|
|
4230
4236
|
const BDS_EPOCH = START_BDS_TIME.getTime();
|
|
@@ -4232,14 +4238,12 @@ function msmEpochToDate(sys, epochMs, refTime = /* @__PURE__ */ new Date()) {
|
|
|
4232
4238
|
let t2 = BDS_EPOCH + weeksSinceEpoch2 * MS_PER_WEEK + epochMs;
|
|
4233
4239
|
if (t2 - refMs > MS_PER_WEEK / 2) t2 -= MS_PER_WEEK;
|
|
4234
4240
|
else if (refMs - t2 > MS_PER_WEEK / 2) t2 += MS_PER_WEEK;
|
|
4235
|
-
t2 -= 18e3;
|
|
4236
4241
|
return new Date(t2);
|
|
4237
4242
|
}
|
|
4238
4243
|
const weeksSinceEpoch = Math.floor((refMs - GPS_EPOCH) / MS_PER_WEEK);
|
|
4239
4244
|
let t = GPS_EPOCH + weeksSinceEpoch * MS_PER_WEEK + epochMs;
|
|
4240
4245
|
if (t - refMs > MS_PER_WEEK / 2) t -= MS_PER_WEEK;
|
|
4241
4246
|
else if (refMs - t > MS_PER_WEEK / 2) t += MS_PER_WEEK;
|
|
4242
|
-
t -= 18e3;
|
|
4243
4247
|
return new Date(t);
|
|
4244
4248
|
}
|
|
4245
4249
|
function setGloFreqNumber(slot, k) {
|
|
@@ -4612,20 +4616,20 @@ var SIGNALS = {
|
|
|
4612
4616
|
// GLONASS L1OF, L2OF
|
|
4613
4617
|
};
|
|
4614
4618
|
function prnFor(gnssId, svId) {
|
|
4615
|
-
const
|
|
4619
|
+
const two3 = (n) => String(n).padStart(2, "0");
|
|
4616
4620
|
switch (gnssId) {
|
|
4617
4621
|
case 0:
|
|
4618
|
-
return svId >= 1 && svId <= 32 ? `G${
|
|
4622
|
+
return svId >= 1 && svId <= 32 ? `G${two3(svId)}` : null;
|
|
4619
4623
|
case 1:
|
|
4620
|
-
return svId >= 120 && svId <= 158 ? `S${
|
|
4624
|
+
return svId >= 120 && svId <= 158 ? `S${two3(svId - 100)}` : null;
|
|
4621
4625
|
case 2:
|
|
4622
|
-
return svId >= 1 && svId <= 36 ? `E${
|
|
4626
|
+
return svId >= 1 && svId <= 36 ? `E${two3(svId)}` : null;
|
|
4623
4627
|
case 3:
|
|
4624
|
-
return svId >= 1 && svId <= 63 ? `C${
|
|
4628
|
+
return svId >= 1 && svId <= 63 ? `C${two3(svId)}` : null;
|
|
4625
4629
|
case 5:
|
|
4626
|
-
return svId >= 1 && svId <= 10 ? `J${
|
|
4630
|
+
return svId >= 1 && svId <= 10 ? `J${two3(svId)}` : null;
|
|
4627
4631
|
case 6:
|
|
4628
|
-
return svId >= 1 && svId <= 32 ? `R${
|
|
4632
|
+
return svId >= 1 && svId <= 32 ? `R${two3(svId)}` : null;
|
|
4629
4633
|
default:
|
|
4630
4634
|
return null;
|
|
4631
4635
|
}
|
|
@@ -4707,10 +4711,7 @@ function parseUbxRawx(data) {
|
|
|
4707
4711
|
return { epochs, messageCounts, obsCodes, badChecksums };
|
|
4708
4712
|
}
|
|
4709
4713
|
|
|
4710
|
-
// src/sbf/
|
|
4711
|
-
var CLIGHT = 299792458;
|
|
4712
|
-
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
4713
|
-
var MS_PER_WEEK3 = 7 * 864e5;
|
|
4714
|
+
// src/sbf/frame.ts
|
|
4714
4715
|
var CRC_TABLE = (() => {
|
|
4715
4716
|
const t = new Uint16Array(256);
|
|
4716
4717
|
for (let n = 0; n < 256; n++) {
|
|
@@ -4727,6 +4728,362 @@ function crc16(data, start, end) {
|
|
|
4727
4728
|
crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
|
|
4728
4729
|
return crc;
|
|
4729
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;
|
|
4730
5087
|
var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
|
|
4731
5088
|
var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
|
|
4732
5089
|
var M3_INTERVALS = [
|
|
@@ -4825,44 +5182,27 @@ var MEAS2_SIG = [
|
|
|
4825
5182
|
["J", "1E"],
|
|
4826
5183
|
["J", "5P"]
|
|
4827
5184
|
];
|
|
4828
|
-
var
|
|
5185
|
+
var two2 = (n) => String(n).padStart(2, "0");
|
|
4829
5186
|
function m3Prn(navsys, svid) {
|
|
4830
5187
|
switch (navsys) {
|
|
4831
5188
|
case 0:
|
|
4832
|
-
return svid < 32 ? `G${
|
|
5189
|
+
return svid < 32 ? `G${two2(svid + 1)}` : null;
|
|
4833
5190
|
case 1:
|
|
4834
|
-
return svid < 27 ? `R${
|
|
5191
|
+
return svid < 27 ? `R${two2(svid + 1)}` : null;
|
|
4835
5192
|
case 2:
|
|
4836
|
-
return svid < 36 ? `E${
|
|
5193
|
+
return svid < 36 ? `E${two2(svid + 1)}` : null;
|
|
4837
5194
|
case 3:
|
|
4838
|
-
return svid < 50 ? `C${
|
|
5195
|
+
return svid < 50 ? `C${two2(svid + 1)}` : null;
|
|
4839
5196
|
case 4:
|
|
4840
|
-
return svid <= 38 ? `S${
|
|
5197
|
+
return svid <= 38 ? `S${two2(svid + 20)}` : null;
|
|
4841
5198
|
case 5:
|
|
4842
|
-
return svid < 10 ? `J${
|
|
5199
|
+
return svid < 10 ? `J${two2(svid + 1)}` : null;
|
|
4843
5200
|
case 6:
|
|
4844
|
-
return svid < 14 ? `I${
|
|
5201
|
+
return svid < 14 ? `I${two2(svid + 1)}` : null;
|
|
4845
5202
|
default:
|
|
4846
5203
|
return null;
|
|
4847
5204
|
}
|
|
4848
5205
|
}
|
|
4849
|
-
function svidToPrn(svid) {
|
|
4850
|
-
if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
|
|
4851
|
-
if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
|
|
4852
|
-
if (svid <= 62) return null;
|
|
4853
|
-
if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
|
|
4854
|
-
if (svid <= 70) return null;
|
|
4855
|
-
if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
|
|
4856
|
-
if (svid <= 119) return null;
|
|
4857
|
-
if (svid <= 140) return `S${two(svid - 100)}`;
|
|
4858
|
-
if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
|
|
4859
|
-
if (svid <= 190) return `J${two(svid - 180)}`;
|
|
4860
|
-
if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
|
|
4861
|
-
if (svid <= 215) return `S${two(svid - 157)}`;
|
|
4862
|
-
if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
|
|
4863
|
-
if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
|
|
4864
|
-
return null;
|
|
4865
|
-
}
|
|
4866
5206
|
function carrierFreq(sys, code, fcn) {
|
|
4867
5207
|
const band = code.charCodeAt(0) - 48;
|
|
4868
5208
|
switch (sys) {
|
|
@@ -4925,7 +5265,6 @@ function parseSbfMeas(data) {
|
|
|
4925
5265
|
const epochs = [];
|
|
4926
5266
|
const messageCounts = {};
|
|
4927
5267
|
const obsCodes = {};
|
|
4928
|
-
let badCrc = 0;
|
|
4929
5268
|
let ref = freshRefEpoch(-1);
|
|
4930
5269
|
let curTime = NaN;
|
|
4931
5270
|
let curMeas = [];
|
|
@@ -4956,8 +5295,8 @@ function parseSbfMeas(data) {
|
|
|
4956
5295
|
curTime = timeMs;
|
|
4957
5296
|
}
|
|
4958
5297
|
};
|
|
4959
|
-
const decodeMeas3Ranges = (
|
|
4960
|
-
const o =
|
|
5298
|
+
const decodeMeas3Ranges = (i, tow) => {
|
|
5299
|
+
const o = i + 8;
|
|
4961
5300
|
const constellations = view.getUint16(o + 8, true) & 127;
|
|
4962
5301
|
const misc = data[o + 10];
|
|
4963
5302
|
const version = data[o + 11];
|
|
@@ -5235,8 +5574,8 @@ function parseSbfMeas(data) {
|
|
|
5235
5574
|
}
|
|
5236
5575
|
}
|
|
5237
5576
|
};
|
|
5238
|
-
const decodeMeas3CN0 = (
|
|
5239
|
-
const flags = view.getUint16(
|
|
5577
|
+
const decodeMeas3CN0 = (i, len) => {
|
|
5578
|
+
const flags = view.getUint16(i + 14, true);
|
|
5240
5579
|
if ((flags & 7) !== 0) return;
|
|
5241
5580
|
const maxNibbles = (len - 16) * 2;
|
|
5242
5581
|
let off = 0;
|
|
@@ -5246,7 +5585,7 @@ function parseSbfMeas(data) {
|
|
|
5246
5585
|
for (let k = 0; k < e.slots.length; k++) {
|
|
5247
5586
|
const slot = e.slots[k];
|
|
5248
5587
|
if (k > 0 && !slot) break;
|
|
5249
|
-
const nib = (data[
|
|
5588
|
+
const nib = (data[i + 16 + (off >> 1)] ?? 0) >> (off & 1) * 4 & 15;
|
|
5250
5589
|
if (slot && slot.cn0 !== null) slot.cn0 += nib * 0.0625 - 0.5;
|
|
5251
5590
|
off++;
|
|
5252
5591
|
}
|
|
@@ -5273,14 +5612,14 @@ function parseSbfMeas(data) {
|
|
|
5273
5612
|
if ((v & 1) === 1) value = -value;
|
|
5274
5613
|
return { value, size };
|
|
5275
5614
|
};
|
|
5276
|
-
const decodeMeas3Doppler = (
|
|
5277
|
-
const flags = view.getUint16(
|
|
5615
|
+
const decodeMeas3Doppler = (i, len) => {
|
|
5616
|
+
const flags = view.getUint16(i + 14, true);
|
|
5278
5617
|
if ((flags & 7) !== 0) return;
|
|
5279
5618
|
let off = 0;
|
|
5280
5619
|
for (const e of curSats) {
|
|
5281
5620
|
if (off + 16 >= len) break;
|
|
5282
5621
|
if (!e.prn) continue;
|
|
5283
|
-
const master = readPrRate(
|
|
5622
|
+
const master = readPrRate(i + 16, off);
|
|
5284
5623
|
off += master.size;
|
|
5285
5624
|
if (master.value === -268435455) continue;
|
|
5286
5625
|
const refPrRate = ref.sats[e.navsys][e.svid]?.prRate ?? 0;
|
|
@@ -5290,20 +5629,20 @@ function parseSbfMeas(data) {
|
|
|
5290
5629
|
for (let k = 1; k < e.slots.length; k++) {
|
|
5291
5630
|
const slot = e.slots[k];
|
|
5292
5631
|
if (!slot) break;
|
|
5293
|
-
const slave = readPrRate(
|
|
5632
|
+
const slave = readPrRate(i + 16, off);
|
|
5294
5633
|
off += slave.size;
|
|
5295
5634
|
if (m)
|
|
5296
5635
|
slot.doppler = (mD * (CLIGHT / m.freq) * 1e3 - slave.value) * 1e-3 / (CLIGHT / slot.freq);
|
|
5297
5636
|
}
|
|
5298
5637
|
}
|
|
5299
5638
|
};
|
|
5300
|
-
const decodeMeasEpoch = (
|
|
5301
|
-
const n1 = data[
|
|
5302
|
-
const len1 = data[
|
|
5303
|
-
const len2 = data[
|
|
5304
|
-
if ((data[
|
|
5305
|
-
const end =
|
|
5306
|
-
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;
|
|
5307
5646
|
for (let b1 = 0; b1 < n1 && p + 20 <= end; b1++) {
|
|
5308
5647
|
const ant = data[p + 1] >> 5;
|
|
5309
5648
|
let sig = data[p + 1] & 31;
|
|
@@ -5390,50 +5729,288 @@ function parseSbfMeas(data) {
|
|
|
5390
5729
|
}
|
|
5391
5730
|
}
|
|
5392
5731
|
};
|
|
5393
|
-
|
|
5394
|
-
while (i + 8 <= data.length) {
|
|
5395
|
-
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
5396
|
-
i++;
|
|
5397
|
-
continue;
|
|
5398
|
-
}
|
|
5399
|
-
const len = view.getUint16(i + 6, true);
|
|
5400
|
-
if (len < 8 || len % 4 !== 0) {
|
|
5401
|
-
i++;
|
|
5402
|
-
continue;
|
|
5403
|
-
}
|
|
5404
|
-
if (i + len > data.length) break;
|
|
5405
|
-
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
5406
|
-
badCrc++;
|
|
5407
|
-
i++;
|
|
5408
|
-
continue;
|
|
5409
|
-
}
|
|
5410
|
-
const id = view.getUint16(i + 4, true) & 8191;
|
|
5732
|
+
const badCrc = scanSbfFrames(data, view, (id, i, len) => {
|
|
5411
5733
|
const key = String(id);
|
|
5412
5734
|
messageCounts[key] = (messageCounts[key] ?? 0) + 1;
|
|
5413
5735
|
if ((id === 4027 || id === 4109 || id === 4110 || id === 4111) && len >= 16) {
|
|
5414
5736
|
const tow = view.getUint32(i + 8, true);
|
|
5415
5737
|
const wnc = view.getUint16(i + 12, true);
|
|
5416
5738
|
if (tow !== 4294967295 && wnc !== 65535) {
|
|
5417
|
-
ensureEpoch(
|
|
5739
|
+
ensureEpoch(GPS_EPOCH_MS3 + wnc * MS_PER_WEEK4 + tow);
|
|
5418
5740
|
if (id === 4109) decodeMeas3Ranges(i, tow);
|
|
5419
5741
|
else if (id === 4110) decodeMeas3CN0(i, len);
|
|
5420
5742
|
else if (id === 4111) decodeMeas3Doppler(i, len);
|
|
5421
5743
|
else decodeMeasEpoch(i, len);
|
|
5422
5744
|
}
|
|
5423
5745
|
}
|
|
5424
|
-
|
|
5425
|
-
}
|
|
5746
|
+
});
|
|
5426
5747
|
flush();
|
|
5427
5748
|
return { epochs, messageCounts, obsCodes, badCrc };
|
|
5428
5749
|
}
|
|
5429
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
|
+
|
|
5430
6007
|
// src/novatel/index.ts
|
|
5431
|
-
var
|
|
5432
|
-
var
|
|
6008
|
+
var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
|
|
6009
|
+
var MS_PER_WEEK5 = 7 * 864e5;
|
|
5433
6010
|
var HDR = 28;
|
|
5434
6011
|
var ID_RANGE = 43;
|
|
5435
6012
|
var ID_RANGECMP = 140;
|
|
5436
|
-
var
|
|
6013
|
+
var ID_GLOEPHEMERIS2 = 723;
|
|
5437
6014
|
var C_LIGHT2 = 299792458;
|
|
5438
6015
|
var ADR_ROLL = 8388608;
|
|
5439
6016
|
function carrierFreq2(sys, code, gloK) {
|
|
@@ -5461,16 +6038,6 @@ function carrierFreq2(sys, code, gloK) {
|
|
|
5461
6038
|
function exsign(v, bits) {
|
|
5462
6039
|
return v & 1 << bits - 1 ? v - 2 ** bits : v;
|
|
5463
6040
|
}
|
|
5464
|
-
function crc32(data, start, len) {
|
|
5465
|
-
let crc = 0;
|
|
5466
|
-
for (let i = start; i < start + len; i++) {
|
|
5467
|
-
crc ^= data[i];
|
|
5468
|
-
for (let j = 0; j < 8; j++) {
|
|
5469
|
-
crc = crc & 1 ? crc >>> 1 ^ 3988292384 : crc >>> 1;
|
|
5470
|
-
}
|
|
5471
|
-
}
|
|
5472
|
-
return crc >>> 0;
|
|
5473
|
-
}
|
|
5474
6041
|
var SIGNALS2 = {
|
|
5475
6042
|
0: {
|
|
5476
6043
|
0: ["G", "1C"],
|
|
@@ -5524,7 +6091,7 @@ function parseNovatelRange(data) {
|
|
|
5524
6091
|
const hl = data[j + 3];
|
|
5525
6092
|
const id = view.getUint16(j + 4, true);
|
|
5526
6093
|
const ln = view.getUint16(j + 8, true);
|
|
5527
|
-
if (id !==
|
|
6094
|
+
if (id !== ID_GLOEPHEMERIS2 || hl < HDR || j + hl + ln + 4 > data.length)
|
|
5528
6095
|
continue;
|
|
5529
6096
|
if (crc32(data, j, hl + ln) !== view.getUint32(j + hl + ln, true)) continue;
|
|
5530
6097
|
const slot = view.getUint16(j + hl, true) - 37;
|
|
@@ -5608,7 +6175,7 @@ function parseNovatelRange(data) {
|
|
|
5608
6175
|
if (!codes.includes(code)) codes.push(code);
|
|
5609
6176
|
}
|
|
5610
6177
|
epochs.push({
|
|
5611
|
-
timeMs:
|
|
6178
|
+
timeMs: GPS_EPOCH_MS6 + week * MS_PER_WEEK5 + towMs,
|
|
5612
6179
|
meas
|
|
5613
6180
|
});
|
|
5614
6181
|
}
|
|
@@ -5660,7 +6227,7 @@ function parseNovatelRange(data) {
|
|
|
5660
6227
|
if (!codes.includes(code)) codes.push(code);
|
|
5661
6228
|
}
|
|
5662
6229
|
epochs.push({
|
|
5663
|
-
timeMs:
|
|
6230
|
+
timeMs: GPS_EPOCH_MS6 + week * MS_PER_WEEK5 + towMs,
|
|
5664
6231
|
meas
|
|
5665
6232
|
});
|
|
5666
6233
|
}
|
|
@@ -6019,10 +6586,10 @@ function selectBest(ephs, timeMs) {
|
|
|
6019
6586
|
}
|
|
6020
6587
|
return best;
|
|
6021
6588
|
}
|
|
6022
|
-
var
|
|
6023
|
-
var
|
|
6589
|
+
var GPS_EPOCH_MS7 = START_GPS_TIME.getTime();
|
|
6590
|
+
var MS_PER_WEEK6 = 7 * 864e5;
|
|
6024
6591
|
var BDS_EPOCH_MS = START_BDS_TIME.getTime();
|
|
6025
|
-
var GAL_EPOCH_MS =
|
|
6592
|
+
var GAL_EPOCH_MS = GPS_EPOCH_MS7 + 1024 * MS_PER_WEEK6;
|
|
6026
6593
|
function ephInfoToEphemeris(info) {
|
|
6027
6594
|
const sys = info.prn.charAt(0);
|
|
6028
6595
|
if (sys === "R") {
|
|
@@ -6073,11 +6640,11 @@ function ephInfoToEphemeris(info) {
|
|
|
6073
6640
|
} else if (sys === "E") {
|
|
6074
6641
|
epochMs = GAL_EPOCH_MS;
|
|
6075
6642
|
} else {
|
|
6076
|
-
epochMs =
|
|
6077
|
-
const weeksNow = (info.lastReceived -
|
|
6643
|
+
epochMs = GPS_EPOCH_MS7;
|
|
6644
|
+
const weeksNow = (info.lastReceived - GPS_EPOCH_MS7) / MS_PER_WEEK6;
|
|
6078
6645
|
week += 1024 * Math.round((weeksNow - week) / 1024);
|
|
6079
6646
|
}
|
|
6080
|
-
const tocDate = new Date(epochMs + week *
|
|
6647
|
+
const tocDate = new Date(epochMs + week * MS_PER_WEEK6 + tocSec * 1e3);
|
|
6081
6648
|
return {
|
|
6082
6649
|
system: sys,
|
|
6083
6650
|
prn: info.prn,
|
|
@@ -9030,6 +9597,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
9030
9597
|
MILLISECONDS_IN_WEEK,
|
|
9031
9598
|
MILLISECONDS_TT_TAI,
|
|
9032
9599
|
MultipathAccumulator,
|
|
9600
|
+
OEM4_HLEN,
|
|
9033
9601
|
OMEGA_E,
|
|
9034
9602
|
REFERENCE_FRAMES,
|
|
9035
9603
|
RINEX_CODES,
|
|
@@ -9073,6 +9641,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
9073
9641
|
computeVisibility,
|
|
9074
9642
|
computeVisibilityFromPositions,
|
|
9075
9643
|
connectToMountpoint,
|
|
9644
|
+
crc32,
|
|
9076
9645
|
createGzipLineSink,
|
|
9077
9646
|
createStationMeta,
|
|
9078
9647
|
createStreamStats,
|
|
@@ -9162,6 +9731,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
9162
9731
|
msmEpochToDate,
|
|
9163
9732
|
navTimesFromEph,
|
|
9164
9733
|
nmeaCoordToDecimal,
|
|
9734
|
+
oem4Frames,
|
|
9165
9735
|
padL,
|
|
9166
9736
|
padR,
|
|
9167
9737
|
parseAntex,
|
|
@@ -9169,9 +9739,12 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
9169
9739
|
parseIonex,
|
|
9170
9740
|
parseNavFile,
|
|
9171
9741
|
parseNmeaFile,
|
|
9742
|
+
parseNovatelNav,
|
|
9172
9743
|
parseNovatelRange,
|
|
9173
9744
|
parseRinexStream,
|
|
9745
|
+
parseSbfAlmanac,
|
|
9174
9746
|
parseSbfMeas,
|
|
9747
|
+
parseSbfNav,
|
|
9175
9748
|
parseSinexBiasDcb,
|
|
9176
9749
|
parseSourcetable,
|
|
9177
9750
|
parseSp3,
|