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