gnss-js 1.14.1 → 1.16.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-4LVD4DYL.js +146 -0
- package/dist/{chunk-5IXK25MX.js → chunk-6CX6EXRK.js} +120 -26
- package/dist/{chunk-SWKWJEGY.js → chunk-R33A2VA7.js} +142 -15
- package/dist/{chunk-YIHO74OS.js → chunk-YPOVPXNK.js} +392 -67
- package/dist/index.cjs +804 -124
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +26 -5
- package/dist/novatel.cjs +357 -14
- package/dist/novatel.d.cts +69 -1
- package/dist/novatel.d.ts +69 -1
- package/dist/novatel.js +12 -1
- package/dist/sbf.cjs +482 -68
- package/dist/sbf.d.cts +155 -1
- package/dist/sbf.d.ts +155 -1
- package/dist/sbf.js +13 -3
- package/dist/ubx.cjs +262 -29
- package/dist/ubx.d.cts +81 -1
- package/dist/ubx.d.ts +81 -1
- package/dist/ubx.js +8 -3
- package/package.json +1 -1
package/dist/novatel.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
+
OEM4_HLEN,
|
|
3
|
+
crc32,
|
|
4
|
+
oem4Frames,
|
|
5
|
+
parseNovatelNav,
|
|
2
6
|
parseNovatelRange
|
|
3
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-R33A2VA7.js";
|
|
8
|
+
import "./chunk-4LVD4DYL.js";
|
|
9
|
+
import "./chunk-HVXYFUCB.js";
|
|
10
|
+
import "./chunk-LEEU5OIO.js";
|
|
4
11
|
export {
|
|
12
|
+
OEM4_HLEN,
|
|
13
|
+
crc32,
|
|
14
|
+
oem4Frames,
|
|
15
|
+
parseNovatelNav,
|
|
5
16
|
parseNovatelRange
|
|
6
17
|
};
|
package/dist/sbf.cjs
CHANGED
|
@@ -20,12 +20,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/sbf/index.ts
|
|
21
21
|
var sbf_exports = {};
|
|
22
22
|
__export(sbf_exports, {
|
|
23
|
-
|
|
23
|
+
parseSbfAlmanac: () => parseSbfAlmanac,
|
|
24
|
+
parseSbfMeas: () => parseSbfMeas,
|
|
25
|
+
parseSbfNav: () => parseSbfNav,
|
|
26
|
+
sbfCrc16: () => crc16,
|
|
27
|
+
scanSbfFrames: () => scanSbfFrames
|
|
24
28
|
});
|
|
25
29
|
module.exports = __toCommonJS(sbf_exports);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
var MS_PER_WEEK = 7 * 864e5;
|
|
30
|
+
|
|
31
|
+
// src/sbf/frame.ts
|
|
29
32
|
var CRC_TABLE = (() => {
|
|
30
33
|
const t = new Uint16Array(256);
|
|
31
34
|
for (let n = 0; n < 256; n++) {
|
|
@@ -42,6 +45,449 @@ function crc16(data, start, end) {
|
|
|
42
45
|
crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
|
|
43
46
|
return crc;
|
|
44
47
|
}
|
|
48
|
+
function scanSbfFrames(data, view, onBlock) {
|
|
49
|
+
let badCrc = 0;
|
|
50
|
+
let i = 0;
|
|
51
|
+
while (i + 8 <= data.length) {
|
|
52
|
+
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
53
|
+
i++;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const len = view.getUint16(i + 6, true);
|
|
57
|
+
if (len < 8 || len % 4 !== 0) {
|
|
58
|
+
i++;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (i + len > data.length) break;
|
|
62
|
+
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
63
|
+
badCrc++;
|
|
64
|
+
i++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
onBlock(view.getUint16(i + 4, true) & 8191, i, len);
|
|
68
|
+
i += len;
|
|
69
|
+
}
|
|
70
|
+
return badCrc;
|
|
71
|
+
}
|
|
72
|
+
var two = (n) => String(n).padStart(2, "0");
|
|
73
|
+
function svidToPrn(svid) {
|
|
74
|
+
if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
|
|
75
|
+
if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
|
|
76
|
+
if (svid <= 62) return null;
|
|
77
|
+
if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
|
|
78
|
+
if (svid <= 70) return null;
|
|
79
|
+
if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
|
|
80
|
+
if (svid <= 119) return null;
|
|
81
|
+
if (svid <= 140) return `S${two(svid - 100)}`;
|
|
82
|
+
if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
|
|
83
|
+
if (svid <= 190) return `J${two(svid - 180)}`;
|
|
84
|
+
if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
|
|
85
|
+
if (svid <= 215) return `S${two(svid - 157)}`;
|
|
86
|
+
if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
|
|
87
|
+
if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/constants/time.ts
|
|
92
|
+
var MILLISECONDS_IN_SECOND = 1e3;
|
|
93
|
+
var MILLISECONDS_GPS_TAI = 19e3;
|
|
94
|
+
var START_NTP_TIME = /* @__PURE__ */ new Date("1900-01-01T00:00:00Z");
|
|
95
|
+
|
|
96
|
+
// src/time/gnss.ts
|
|
97
|
+
function getTaiDate(date) {
|
|
98
|
+
return new Date(date.getTime() + MILLISECONDS_GPS_TAI);
|
|
99
|
+
}
|
|
100
|
+
function getNtpTime(date) {
|
|
101
|
+
return getTaiDate(date).getTime() - START_NTP_TIME.getTime();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/time/utc.ts
|
|
105
|
+
var LEAP_SECONDS_TABLE = [
|
|
106
|
+
[3692217600, 37],
|
|
107
|
+
// 1 Jan 2017
|
|
108
|
+
[3644697600, 36],
|
|
109
|
+
// 1 Jul 2015
|
|
110
|
+
[3550089600, 35],
|
|
111
|
+
// 1 Jul 2012
|
|
112
|
+
[3439756800, 34],
|
|
113
|
+
// 1 Jan 2009
|
|
114
|
+
[3345062400, 33],
|
|
115
|
+
// 1 Jan 2006
|
|
116
|
+
[3124137600, 32],
|
|
117
|
+
// 1 Jan 1999
|
|
118
|
+
[3076704e3, 31],
|
|
119
|
+
// 1 Jul 1997
|
|
120
|
+
[3029443200, 30],
|
|
121
|
+
// 1 Jan 1996
|
|
122
|
+
[2982009600, 29],
|
|
123
|
+
// 1 Jul 1994
|
|
124
|
+
[2950473600, 28],
|
|
125
|
+
// 1 Jul 1993
|
|
126
|
+
[2918937600, 27],
|
|
127
|
+
// 1 Jul 1992
|
|
128
|
+
[2871676800, 26],
|
|
129
|
+
// 1 Jan 1991
|
|
130
|
+
[2840140800, 25],
|
|
131
|
+
// 1 Jan 1990
|
|
132
|
+
[2776982400, 24],
|
|
133
|
+
// 1 Jan 1988
|
|
134
|
+
[2698012800, 23],
|
|
135
|
+
// 1 Jul 1985
|
|
136
|
+
[2634854400, 22],
|
|
137
|
+
// 1 Jul 1983
|
|
138
|
+
[2603318400, 21],
|
|
139
|
+
// 1 Jul 1982
|
|
140
|
+
[2571782400, 20],
|
|
141
|
+
// 1 Jul 1981
|
|
142
|
+
[2524521600, 19],
|
|
143
|
+
// 1 Jan 1980
|
|
144
|
+
[2492985600, 18],
|
|
145
|
+
// 1 Jan 1979
|
|
146
|
+
[2461449600, 17],
|
|
147
|
+
// 1 Jan 1978
|
|
148
|
+
[2429913600, 16],
|
|
149
|
+
// 1 Jan 1977
|
|
150
|
+
[2398291200, 15],
|
|
151
|
+
// 1 Jan 1976
|
|
152
|
+
[2366755200, 14],
|
|
153
|
+
// 1 Jan 1975
|
|
154
|
+
[2335219200, 13],
|
|
155
|
+
// 1 Jan 1974
|
|
156
|
+
[2303683200, 12],
|
|
157
|
+
// 1 Jan 1973
|
|
158
|
+
[2287785600, 11],
|
|
159
|
+
// 1 Jul 1972
|
|
160
|
+
[2272060800, 10]
|
|
161
|
+
// 1 Jan 1972
|
|
162
|
+
];
|
|
163
|
+
function getLeap(date) {
|
|
164
|
+
const ntp_time = getNtpTime(date);
|
|
165
|
+
for (const [timestamp, leapSeconds] of LEAP_SECONDS_TABLE) {
|
|
166
|
+
if (ntp_time / MILLISECONDS_IN_SECOND - leapSeconds >= timestamp) {
|
|
167
|
+
return leapSeconds;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return 8;
|
|
171
|
+
}
|
|
172
|
+
function getGpsLeap(date) {
|
|
173
|
+
const leap_seconds = getLeap(date);
|
|
174
|
+
if (leap_seconds < 0) return 0;
|
|
175
|
+
return leap_seconds - 19;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/sbf/nav.ts
|
|
179
|
+
var PI = Math.PI;
|
|
180
|
+
var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
|
|
181
|
+
var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
|
|
182
|
+
var SEC_PER_WEEK = 7 * 86400;
|
|
183
|
+
var MS_PER_WEEK = SEC_PER_WEEK * 1e3;
|
|
184
|
+
var F4_DNU = -2e10;
|
|
185
|
+
var U4_DNU = 4294967295;
|
|
186
|
+
function adjustWeek(refWeek, wn, mod) {
|
|
187
|
+
let offset = refWeek % mod - wn;
|
|
188
|
+
if (offset > mod / 2) offset -= mod;
|
|
189
|
+
if (offset < -(mod / 2 - 1)) offset += mod;
|
|
190
|
+
return refWeek - offset;
|
|
191
|
+
}
|
|
192
|
+
var gpsMs = (week, sec) => GPS_EPOCH_MS + week * MS_PER_WEEK + sec * 1e3;
|
|
193
|
+
var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
|
|
194
|
+
function decodeGpsQzsNav(view, b, sys) {
|
|
195
|
+
const svid = view.getUint8(b + 14);
|
|
196
|
+
const prn = sys === "G" ? svid : svid - 180;
|
|
197
|
+
const max = sys === "G" ? 32 : 10;
|
|
198
|
+
if (prn < 1 || prn > max) return null;
|
|
199
|
+
const wnc = view.getUint16(b + 12, true);
|
|
200
|
+
const tgdRaw = view.getFloat32(b + 28, true);
|
|
201
|
+
const tocs = view.getUint32(b + 32, true);
|
|
202
|
+
const toes = view.getUint32(b + 88, true);
|
|
203
|
+
const wnToc = adjustWeek(wnc, view.getUint16(b + 136, true), 1024);
|
|
204
|
+
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
205
|
+
return {
|
|
206
|
+
system: sys,
|
|
207
|
+
prn: `${sys}${String(prn).padStart(2, "0")}`,
|
|
208
|
+
toc: sowOf(tocDate.getTime()),
|
|
209
|
+
tocDate,
|
|
210
|
+
af0: view.getFloat32(b + 44, true),
|
|
211
|
+
af1: view.getFloat32(b + 40, true),
|
|
212
|
+
af2: view.getFloat32(b + 36, true),
|
|
213
|
+
iode: view.getUint8(b + 24),
|
|
214
|
+
crs: view.getFloat32(b + 48, true),
|
|
215
|
+
deltaN: view.getFloat32(b + 52, true) * PI,
|
|
216
|
+
m0: view.getFloat64(b + 56, true) * PI,
|
|
217
|
+
cuc: view.getFloat32(b + 64, true),
|
|
218
|
+
e: view.getFloat64(b + 68, true),
|
|
219
|
+
cus: view.getFloat32(b + 76, true),
|
|
220
|
+
sqrtA: view.getFloat64(b + 80, true),
|
|
221
|
+
toe: toes,
|
|
222
|
+
cic: view.getFloat32(b + 92, true),
|
|
223
|
+
omega0: view.getFloat64(b + 96, true) * PI,
|
|
224
|
+
cis: view.getFloat32(b + 104, true),
|
|
225
|
+
i0: view.getFloat64(b + 108, true) * PI,
|
|
226
|
+
crc: view.getFloat32(b + 116, true),
|
|
227
|
+
omega: view.getFloat64(b + 120, true) * PI,
|
|
228
|
+
omegaDot: view.getFloat32(b + 128, true) * PI,
|
|
229
|
+
idot: view.getFloat32(b + 132, true) * PI,
|
|
230
|
+
// RINEX "GPS week to go with toe": RTKLIB writes WNc for GPS and
|
|
231
|
+
// the week of toc for QZSS — mirrored here so records compare
|
|
232
|
+
// 1:1 against convbin output. wnToe is still used for `toe` above.
|
|
233
|
+
week: sys === "G" ? wnc : wnToc,
|
|
234
|
+
svHealth: view.getUint8(b + 20),
|
|
235
|
+
tgd: tgdRaw !== F4_DNU ? tgdRaw : 0
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function decodeGalNav(view, b) {
|
|
239
|
+
const prn = view.getUint8(b + 14) - 70;
|
|
240
|
+
if (prn < 1 || prn > 36) return null;
|
|
241
|
+
const source = view.getUint8(b + 15);
|
|
242
|
+
if (source !== 2 && source !== 16) return null;
|
|
243
|
+
const wnc = view.getUint16(b + 12, true);
|
|
244
|
+
const toes = view.getUint32(b + 100, true);
|
|
245
|
+
const tocs = view.getUint32(b + 104, true);
|
|
246
|
+
const wnToc = adjustWeek(wnc, view.getUint16(b + 126, true), 4096);
|
|
247
|
+
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
248
|
+
const health = view.getUint16(b + 130, true);
|
|
249
|
+
let svh = 0;
|
|
250
|
+
if (health & 1) svh |= health >> 1 & 7;
|
|
251
|
+
if (health & 16) svh |= (health >> 5 & 7) << 6;
|
|
252
|
+
if (health & 256) svh |= (health >> 9 & 7) << 3;
|
|
253
|
+
const bgdE5a = view.getFloat32(b + 136, true);
|
|
254
|
+
return {
|
|
255
|
+
system: "E",
|
|
256
|
+
prn: `E${String(prn).padStart(2, "0")}`,
|
|
257
|
+
toc: sowOf(tocDate.getTime()),
|
|
258
|
+
tocDate,
|
|
259
|
+
af0: view.getFloat64(b + 116, true),
|
|
260
|
+
af1: view.getFloat32(b + 112, true),
|
|
261
|
+
af2: view.getFloat32(b + 108, true),
|
|
262
|
+
iode: view.getUint16(b + 128, true),
|
|
263
|
+
// IODnav
|
|
264
|
+
crs: view.getFloat32(b + 88, true),
|
|
265
|
+
deltaN: view.getFloat32(b + 72, true) * PI,
|
|
266
|
+
m0: view.getFloat64(b + 24, true) * PI,
|
|
267
|
+
cuc: view.getFloat32(b + 76, true),
|
|
268
|
+
e: view.getFloat64(b + 32, true),
|
|
269
|
+
cus: view.getFloat32(b + 80, true),
|
|
270
|
+
sqrtA: view.getFloat64(b + 16, true),
|
|
271
|
+
toe: toes,
|
|
272
|
+
cic: view.getFloat32(b + 92, true),
|
|
273
|
+
omega0: view.getFloat64(b + 56, true) * PI,
|
|
274
|
+
cis: view.getFloat32(b + 96, true),
|
|
275
|
+
i0: view.getFloat64(b + 40, true) * PI,
|
|
276
|
+
crc: view.getFloat32(b + 84, true),
|
|
277
|
+
omega: view.getFloat64(b + 48, true) * PI,
|
|
278
|
+
omegaDot: view.getFloat32(b + 64, true) * PI,
|
|
279
|
+
idot: view.getFloat32(b + 68, true) * PI,
|
|
280
|
+
week: wnc,
|
|
281
|
+
// RINEX GAL week is GPS-aligned and continuous
|
|
282
|
+
svHealth: svh,
|
|
283
|
+
tgd: bgdE5a !== F4_DNU ? bgdE5a : 0
|
|
284
|
+
// BGD E5a/E1 (RINEX slot)
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function decodeBdsNav(view, b) {
|
|
288
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
289
|
+
if (!prn || prn[0] !== "C") return null;
|
|
290
|
+
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
291
|
+
const tgd1 = view.getFloat32(b + 24, true);
|
|
292
|
+
const tocs = view.getUint32(b + 32, true);
|
|
293
|
+
const toes = view.getUint32(b + 88, true);
|
|
294
|
+
const wnToc = adjustWeek(bdsWeekRef, view.getUint16(b + 136, true), 8192);
|
|
295
|
+
const wnToe = adjustWeek(bdsWeekRef, view.getUint16(b + 138, true), 8192);
|
|
296
|
+
const tocDate = new Date(BDT_EPOCH_MS + wnToc * MS_PER_WEEK + tocs * 1e3);
|
|
297
|
+
return {
|
|
298
|
+
system: "C",
|
|
299
|
+
prn,
|
|
300
|
+
toc: sowOf(tocDate.getTime()),
|
|
301
|
+
tocDate,
|
|
302
|
+
af0: view.getFloat32(b + 44, true),
|
|
303
|
+
af1: view.getFloat32(b + 40, true),
|
|
304
|
+
af2: view.getFloat32(b + 36, true),
|
|
305
|
+
iode: view.getUint8(b + 21),
|
|
306
|
+
// AODE
|
|
307
|
+
crs: view.getFloat32(b + 48, true),
|
|
308
|
+
deltaN: view.getFloat32(b + 52, true) * PI,
|
|
309
|
+
m0: view.getFloat64(b + 56, true) * PI,
|
|
310
|
+
cuc: view.getFloat32(b + 64, true),
|
|
311
|
+
e: view.getFloat64(b + 68, true),
|
|
312
|
+
cus: view.getFloat32(b + 76, true),
|
|
313
|
+
sqrtA: view.getFloat64(b + 80, true),
|
|
314
|
+
toe: toes,
|
|
315
|
+
cic: view.getFloat32(b + 92, true),
|
|
316
|
+
omega0: view.getFloat64(b + 96, true) * PI,
|
|
317
|
+
cis: view.getFloat32(b + 104, true),
|
|
318
|
+
i0: view.getFloat64(b + 108, true) * PI,
|
|
319
|
+
crc: view.getFloat32(b + 116, true),
|
|
320
|
+
omega: view.getFloat64(b + 120, true) * PI,
|
|
321
|
+
omegaDot: view.getFloat32(b + 128, true) * PI,
|
|
322
|
+
idot: view.getFloat32(b + 132, true) * PI,
|
|
323
|
+
week: wnToe,
|
|
324
|
+
// RINEX BDS week field is the BDT week
|
|
325
|
+
svHealth: view.getUint8(b + 19),
|
|
326
|
+
// SatH1
|
|
327
|
+
tgd: tgd1 !== F4_DNU ? tgd1 : 0
|
|
328
|
+
// TGD1 B1/B3 (RINEX slot)
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function decodeGloNav(view, b) {
|
|
332
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
333
|
+
if (!prn || prn[0] !== "R") return null;
|
|
334
|
+
const wnc = view.getUint16(b + 12, true);
|
|
335
|
+
const toes = view.getUint32(b + 76, true);
|
|
336
|
+
const wnToe = adjustWeek(wnc, view.getUint16(b + 80, true), 1024);
|
|
337
|
+
const toeGpsMs = gpsMs(wnToe, toes);
|
|
338
|
+
const leapMs = getGpsLeap(new Date(toeGpsMs)) * 1e3;
|
|
339
|
+
const tocDate = new Date(toeGpsMs - leapMs);
|
|
340
|
+
const tofGpsMs = gpsMs(wnc, 0) + view.getUint32(b + 8, true);
|
|
341
|
+
const tofLeapMs = getGpsLeap(new Date(tofGpsMs)) * 1e3;
|
|
342
|
+
const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS) / 1e3 % SEC_PER_WEEK;
|
|
343
|
+
return {
|
|
344
|
+
system: "R",
|
|
345
|
+
prn,
|
|
346
|
+
tocDate,
|
|
347
|
+
// RINEX stores −τn as the clock bias; SBF carries τn (ICD sign)
|
|
348
|
+
tauN: -view.getFloat32(b + 68, true),
|
|
349
|
+
gammaN: view.getFloat32(b + 64, true),
|
|
350
|
+
messageFrameTime,
|
|
351
|
+
x: view.getFloat64(b + 16, true),
|
|
352
|
+
// SBF unit is km, like RINEX
|
|
353
|
+
xDot: view.getFloat32(b + 40, true),
|
|
354
|
+
xAcc: view.getFloat32(b + 52, true),
|
|
355
|
+
y: view.getFloat64(b + 24, true),
|
|
356
|
+
yDot: view.getFloat32(b + 44, true),
|
|
357
|
+
yAcc: view.getFloat32(b + 56, true),
|
|
358
|
+
z: view.getFloat64(b + 32, true),
|
|
359
|
+
zDot: view.getFloat32(b + 48, true),
|
|
360
|
+
zAcc: view.getFloat32(b + 60, true),
|
|
361
|
+
// MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
|
|
362
|
+
health: view.getUint8(b + 85) >> 2,
|
|
363
|
+
freqNum: view.getUint8(b + 15) - 8
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function parseSbfNav(data) {
|
|
367
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
368
|
+
const ephemerides = [];
|
|
369
|
+
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
370
|
+
let eph = null;
|
|
371
|
+
if (id === 5891 && len >= 140) eph = decodeGpsQzsNav(view, b, "G");
|
|
372
|
+
else if (id === 4095 && len >= 140) eph = decodeGpsQzsNav(view, b, "J");
|
|
373
|
+
else if (id === 4002 && len >= 149) eph = decodeGalNav(view, b);
|
|
374
|
+
else if (id === 4081 && len >= 140) eph = decodeBdsNav(view, b);
|
|
375
|
+
else if (id === 4004 && len >= 96) eph = decodeGloNav(view, b);
|
|
376
|
+
if (eph) ephemerides.push(eph);
|
|
377
|
+
});
|
|
378
|
+
return { ephemerides, badCrc };
|
|
379
|
+
}
|
|
380
|
+
var GAL_SQRT_A_NOMINAL = Math.sqrt(296e5);
|
|
381
|
+
var GAL_I_NOMINAL = 56 / 180 * PI;
|
|
382
|
+
var I_REF_03 = 0.3 * PI;
|
|
383
|
+
function decodeGpsGalAlm(view, b, sys) {
|
|
384
|
+
const wnc = view.getUint16(b + 12, true);
|
|
385
|
+
const toa = view.getUint32(b + 20, true);
|
|
386
|
+
if (toa === U4_DNU) return null;
|
|
387
|
+
let prn;
|
|
388
|
+
let weekAlm;
|
|
389
|
+
let i0;
|
|
390
|
+
let sqrtA = view.getFloat32(b + 32, true);
|
|
391
|
+
let health;
|
|
392
|
+
if (sys === "G") {
|
|
393
|
+
prn = view.getUint8(b + 14);
|
|
394
|
+
if (prn < 1 || prn > 32) return null;
|
|
395
|
+
weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 256);
|
|
396
|
+
i0 = I_REF_03 + view.getFloat32(b + 24, true) * PI;
|
|
397
|
+
health = view.getUint8(b + 58);
|
|
398
|
+
} else {
|
|
399
|
+
prn = view.getUint8(b + 57) - 70;
|
|
400
|
+
if (prn < 1 || prn > 36) return null;
|
|
401
|
+
weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 4);
|
|
402
|
+
i0 = GAL_I_NOMINAL + view.getFloat32(b + 24, true) * PI;
|
|
403
|
+
sqrtA += GAL_SQRT_A_NOMINAL;
|
|
404
|
+
health = view.getUint16(b + 58, true);
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
system: sys,
|
|
408
|
+
prn: `${sys}${String(prn).padStart(2, "0")}`,
|
|
409
|
+
weekAlm,
|
|
410
|
+
toaSec: toa,
|
|
411
|
+
sqrtA,
|
|
412
|
+
e: view.getFloat32(b + 16, true),
|
|
413
|
+
i0OrDeltaI: i0,
|
|
414
|
+
omega0: view.getFloat32(b + 36, true) * PI,
|
|
415
|
+
omega: view.getFloat32(b + 40, true) * PI,
|
|
416
|
+
m0: view.getFloat32(b + 44, true) * PI,
|
|
417
|
+
omegaDot: view.getFloat32(b + 28, true) * PI,
|
|
418
|
+
af0: view.getFloat32(b + 52, true),
|
|
419
|
+
af1: view.getFloat32(b + 48, true),
|
|
420
|
+
health
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function decodeBdsAlm(view, b) {
|
|
424
|
+
const prnStr = svidToPrn(view.getUint8(b + 14));
|
|
425
|
+
if (!prnStr || prnStr[0] !== "C") return null;
|
|
426
|
+
const prn = parseInt(prnStr.slice(1), 10);
|
|
427
|
+
const toa = view.getUint32(b + 16, true);
|
|
428
|
+
if (toa === U4_DNU) return null;
|
|
429
|
+
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
430
|
+
const geo = prn <= 5 || prn >= 59;
|
|
431
|
+
return {
|
|
432
|
+
system: "C",
|
|
433
|
+
prn: prnStr,
|
|
434
|
+
weekAlm: adjustWeek(bdsWeekRef, view.getUint8(b + 15), 256),
|
|
435
|
+
toaSec: toa,
|
|
436
|
+
sqrtA: view.getFloat32(b + 20, true),
|
|
437
|
+
e: view.getFloat32(b + 24, true),
|
|
438
|
+
i0OrDeltaI: (geo ? 0 : I_REF_03) + view.getFloat32(b + 44, true) * PI,
|
|
439
|
+
omega0: view.getFloat32(b + 36, true) * PI,
|
|
440
|
+
omega: view.getFloat32(b + 28, true) * PI,
|
|
441
|
+
m0: view.getFloat32(b + 32, true) * PI,
|
|
442
|
+
omegaDot: view.getFloat32(b + 40, true) * PI,
|
|
443
|
+
af0: view.getFloat32(b + 48, true),
|
|
444
|
+
af1: view.getFloat32(b + 52, true),
|
|
445
|
+
health: view.getUint16(b + 56, true)
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
function decodeGloAlm(view, b) {
|
|
449
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
450
|
+
if (!prn || prn[0] !== "R") return null;
|
|
451
|
+
const wnc = view.getUint16(b + 12, true);
|
|
452
|
+
const toa = view.getUint32(b + 20, true);
|
|
453
|
+
if (toa === U4_DNU) return null;
|
|
454
|
+
return {
|
|
455
|
+
system: "R",
|
|
456
|
+
prn,
|
|
457
|
+
freqNr: view.getUint8(b + 15) - 8,
|
|
458
|
+
weekAlm: adjustWeek(wnc, view.getUint8(b + 52), 256),
|
|
459
|
+
toaSec: toa,
|
|
460
|
+
epsilon: view.getFloat32(b + 16, true),
|
|
461
|
+
deltaI: view.getFloat32(b + 24, true) * PI,
|
|
462
|
+
lambda: view.getFloat32(b + 28, true) * PI,
|
|
463
|
+
tLambda: view.getFloat32(b + 32, true),
|
|
464
|
+
omega: view.getFloat32(b + 36, true) * PI,
|
|
465
|
+
deltaT: view.getFloat32(b + 40, true),
|
|
466
|
+
deltaTDot: view.getFloat32(b + 44, true),
|
|
467
|
+
tau: view.getFloat32(b + 48, true),
|
|
468
|
+
health: view.getUint8(b + 53),
|
|
469
|
+
nDay: view.getUint16(b + 54, true),
|
|
470
|
+
n4: view.getUint8(b + 57)
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function parseSbfAlmanac(data) {
|
|
474
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
475
|
+
const almanacs = [];
|
|
476
|
+
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
477
|
+
let alm = null;
|
|
478
|
+
if (id === 5892 && len >= 60) alm = decodeGpsGalAlm(view, b, "G");
|
|
479
|
+
else if (id === 4003 && len >= 61) alm = decodeGpsGalAlm(view, b, "E");
|
|
480
|
+
else if (id === 4119 && len >= 60) alm = decodeBdsAlm(view, b);
|
|
481
|
+
else if (id === 4005 && len >= 60) alm = decodeGloAlm(view, b);
|
|
482
|
+
if (alm) almanacs.push(alm);
|
|
483
|
+
});
|
|
484
|
+
return { almanacs, badCrc };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/sbf/index.ts
|
|
488
|
+
var CLIGHT = 299792458;
|
|
489
|
+
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
490
|
+
var MS_PER_WEEK2 = 7 * 864e5;
|
|
45
491
|
var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
|
|
46
492
|
var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
|
|
47
493
|
var M3_INTERVALS = [
|
|
@@ -140,44 +586,27 @@ var MEAS2_SIG = [
|
|
|
140
586
|
["J", "1E"],
|
|
141
587
|
["J", "5P"]
|
|
142
588
|
];
|
|
143
|
-
var
|
|
589
|
+
var two2 = (n) => String(n).padStart(2, "0");
|
|
144
590
|
function m3Prn(navsys, svid) {
|
|
145
591
|
switch (navsys) {
|
|
146
592
|
case 0:
|
|
147
|
-
return svid < 32 ? `G${
|
|
593
|
+
return svid < 32 ? `G${two2(svid + 1)}` : null;
|
|
148
594
|
case 1:
|
|
149
|
-
return svid < 27 ? `R${
|
|
595
|
+
return svid < 27 ? `R${two2(svid + 1)}` : null;
|
|
150
596
|
case 2:
|
|
151
|
-
return svid < 36 ? `E${
|
|
597
|
+
return svid < 36 ? `E${two2(svid + 1)}` : null;
|
|
152
598
|
case 3:
|
|
153
|
-
return svid < 50 ? `C${
|
|
599
|
+
return svid < 50 ? `C${two2(svid + 1)}` : null;
|
|
154
600
|
case 4:
|
|
155
|
-
return svid <= 38 ? `S${
|
|
601
|
+
return svid <= 38 ? `S${two2(svid + 20)}` : null;
|
|
156
602
|
case 5:
|
|
157
|
-
return svid < 10 ? `J${
|
|
603
|
+
return svid < 10 ? `J${two2(svid + 1)}` : null;
|
|
158
604
|
case 6:
|
|
159
|
-
return svid < 14 ? `I${
|
|
605
|
+
return svid < 14 ? `I${two2(svid + 1)}` : null;
|
|
160
606
|
default:
|
|
161
607
|
return null;
|
|
162
608
|
}
|
|
163
609
|
}
|
|
164
|
-
function svidToPrn(svid) {
|
|
165
|
-
if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
|
|
166
|
-
if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
|
|
167
|
-
if (svid <= 62) return null;
|
|
168
|
-
if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
|
|
169
|
-
if (svid <= 70) return null;
|
|
170
|
-
if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
|
|
171
|
-
if (svid <= 119) return null;
|
|
172
|
-
if (svid <= 140) return `S${two(svid - 100)}`;
|
|
173
|
-
if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
|
|
174
|
-
if (svid <= 190) return `J${two(svid - 180)}`;
|
|
175
|
-
if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
|
|
176
|
-
if (svid <= 215) return `S${two(svid - 157)}`;
|
|
177
|
-
if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
|
|
178
|
-
if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
610
|
function carrierFreq(sys, code, fcn) {
|
|
182
611
|
const band = code.charCodeAt(0) - 48;
|
|
183
612
|
switch (sys) {
|
|
@@ -240,7 +669,6 @@ function parseSbfMeas(data) {
|
|
|
240
669
|
const epochs = [];
|
|
241
670
|
const messageCounts = {};
|
|
242
671
|
const obsCodes = {};
|
|
243
|
-
let badCrc = 0;
|
|
244
672
|
let ref = freshRefEpoch(-1);
|
|
245
673
|
let curTime = NaN;
|
|
246
674
|
let curMeas = [];
|
|
@@ -271,8 +699,8 @@ function parseSbfMeas(data) {
|
|
|
271
699
|
curTime = timeMs;
|
|
272
700
|
}
|
|
273
701
|
};
|
|
274
|
-
const decodeMeas3Ranges = (
|
|
275
|
-
const o =
|
|
702
|
+
const decodeMeas3Ranges = (i, tow) => {
|
|
703
|
+
const o = i + 8;
|
|
276
704
|
const constellations = view.getUint16(o + 8, true) & 127;
|
|
277
705
|
const misc = data[o + 10];
|
|
278
706
|
const version = data[o + 11];
|
|
@@ -550,8 +978,8 @@ function parseSbfMeas(data) {
|
|
|
550
978
|
}
|
|
551
979
|
}
|
|
552
980
|
};
|
|
553
|
-
const decodeMeas3CN0 = (
|
|
554
|
-
const flags = view.getUint16(
|
|
981
|
+
const decodeMeas3CN0 = (i, len) => {
|
|
982
|
+
const flags = view.getUint16(i + 14, true);
|
|
555
983
|
if ((flags & 7) !== 0) return;
|
|
556
984
|
const maxNibbles = (len - 16) * 2;
|
|
557
985
|
let off = 0;
|
|
@@ -561,7 +989,7 @@ function parseSbfMeas(data) {
|
|
|
561
989
|
for (let k = 0; k < e.slots.length; k++) {
|
|
562
990
|
const slot = e.slots[k];
|
|
563
991
|
if (k > 0 && !slot) break;
|
|
564
|
-
const nib = (data[
|
|
992
|
+
const nib = (data[i + 16 + (off >> 1)] ?? 0) >> (off & 1) * 4 & 15;
|
|
565
993
|
if (slot && slot.cn0 !== null) slot.cn0 += nib * 0.0625 - 0.5;
|
|
566
994
|
off++;
|
|
567
995
|
}
|
|
@@ -588,14 +1016,14 @@ function parseSbfMeas(data) {
|
|
|
588
1016
|
if ((v & 1) === 1) value = -value;
|
|
589
1017
|
return { value, size };
|
|
590
1018
|
};
|
|
591
|
-
const decodeMeas3Doppler = (
|
|
592
|
-
const flags = view.getUint16(
|
|
1019
|
+
const decodeMeas3Doppler = (i, len) => {
|
|
1020
|
+
const flags = view.getUint16(i + 14, true);
|
|
593
1021
|
if ((flags & 7) !== 0) return;
|
|
594
1022
|
let off = 0;
|
|
595
1023
|
for (const e of curSats) {
|
|
596
1024
|
if (off + 16 >= len) break;
|
|
597
1025
|
if (!e.prn) continue;
|
|
598
|
-
const master = readPrRate(
|
|
1026
|
+
const master = readPrRate(i + 16, off);
|
|
599
1027
|
off += master.size;
|
|
600
1028
|
if (master.value === -268435455) continue;
|
|
601
1029
|
const refPrRate = ref.sats[e.navsys][e.svid]?.prRate ?? 0;
|
|
@@ -605,20 +1033,20 @@ function parseSbfMeas(data) {
|
|
|
605
1033
|
for (let k = 1; k < e.slots.length; k++) {
|
|
606
1034
|
const slot = e.slots[k];
|
|
607
1035
|
if (!slot) break;
|
|
608
|
-
const slave = readPrRate(
|
|
1036
|
+
const slave = readPrRate(i + 16, off);
|
|
609
1037
|
off += slave.size;
|
|
610
1038
|
if (m)
|
|
611
1039
|
slot.doppler = (mD * (CLIGHT / m.freq) * 1e3 - slave.value) * 1e-3 / (CLIGHT / slot.freq);
|
|
612
1040
|
}
|
|
613
1041
|
}
|
|
614
1042
|
};
|
|
615
|
-
const decodeMeasEpoch = (
|
|
616
|
-
const n1 = data[
|
|
617
|
-
const len1 = data[
|
|
618
|
-
const len2 = data[
|
|
619
|
-
if ((data[
|
|
620
|
-
const end =
|
|
621
|
-
let p =
|
|
1043
|
+
const decodeMeasEpoch = (i, len) => {
|
|
1044
|
+
const n1 = data[i + 14];
|
|
1045
|
+
const len1 = data[i + 15];
|
|
1046
|
+
const len2 = data[i + 16];
|
|
1047
|
+
if ((data[i + 17] & 128) !== 0) return;
|
|
1048
|
+
const end = i + len;
|
|
1049
|
+
let p = i + 20;
|
|
622
1050
|
for (let b1 = 0; b1 < n1 && p + 20 <= end; b1++) {
|
|
623
1051
|
const ant = data[p + 1] >> 5;
|
|
624
1052
|
let sig = data[p + 1] & 31;
|
|
@@ -705,43 +1133,29 @@ function parseSbfMeas(data) {
|
|
|
705
1133
|
}
|
|
706
1134
|
}
|
|
707
1135
|
};
|
|
708
|
-
|
|
709
|
-
while (i + 8 <= data.length) {
|
|
710
|
-
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
711
|
-
i++;
|
|
712
|
-
continue;
|
|
713
|
-
}
|
|
714
|
-
const len = view.getUint16(i + 6, true);
|
|
715
|
-
if (len < 8 || len % 4 !== 0) {
|
|
716
|
-
i++;
|
|
717
|
-
continue;
|
|
718
|
-
}
|
|
719
|
-
if (i + len > data.length) break;
|
|
720
|
-
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
721
|
-
badCrc++;
|
|
722
|
-
i++;
|
|
723
|
-
continue;
|
|
724
|
-
}
|
|
725
|
-
const id = view.getUint16(i + 4, true) & 8191;
|
|
1136
|
+
const badCrc = scanSbfFrames(data, view, (id, i, len) => {
|
|
726
1137
|
const key = String(id);
|
|
727
1138
|
messageCounts[key] = (messageCounts[key] ?? 0) + 1;
|
|
728
1139
|
if ((id === 4027 || id === 4109 || id === 4110 || id === 4111) && len >= 16) {
|
|
729
1140
|
const tow = view.getUint32(i + 8, true);
|
|
730
1141
|
const wnc = view.getUint16(i + 12, true);
|
|
731
1142
|
if (tow !== 4294967295 && wnc !== 65535) {
|
|
732
|
-
ensureEpoch(
|
|
1143
|
+
ensureEpoch(GPS_EPOCH_MS2 + wnc * MS_PER_WEEK2 + tow);
|
|
733
1144
|
if (id === 4109) decodeMeas3Ranges(i, tow);
|
|
734
1145
|
else if (id === 4110) decodeMeas3CN0(i, len);
|
|
735
1146
|
else if (id === 4111) decodeMeas3Doppler(i, len);
|
|
736
1147
|
else decodeMeasEpoch(i, len);
|
|
737
1148
|
}
|
|
738
1149
|
}
|
|
739
|
-
|
|
740
|
-
}
|
|
1150
|
+
});
|
|
741
1151
|
flush();
|
|
742
1152
|
return { epochs, messageCounts, obsCodes, badCrc };
|
|
743
1153
|
}
|
|
744
1154
|
// Annotate the CommonJS export names for ESM import in node:
|
|
745
1155
|
0 && (module.exports = {
|
|
746
|
-
|
|
1156
|
+
parseSbfAlmanac,
|
|
1157
|
+
parseSbfMeas,
|
|
1158
|
+
parseSbfNav,
|
|
1159
|
+
sbfCrc16,
|
|
1160
|
+
scanSbfFrames
|
|
747
1161
|
});
|