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
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
getGpsLeap
|
|
3
|
+
} from "./chunk-HVXYFUCB.js";
|
|
4
|
+
|
|
5
|
+
// src/sbf/frame.ts
|
|
5
6
|
var CRC_TABLE = (() => {
|
|
6
7
|
const t = new Uint16Array(256);
|
|
7
8
|
for (let n = 0; n < 256; n++) {
|
|
@@ -18,6 +19,362 @@ function crc16(data, start, end) {
|
|
|
18
19
|
crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
|
|
19
20
|
return crc;
|
|
20
21
|
}
|
|
22
|
+
function scanSbfFrames(data, view, onBlock) {
|
|
23
|
+
let badCrc = 0;
|
|
24
|
+
let i = 0;
|
|
25
|
+
while (i + 8 <= data.length) {
|
|
26
|
+
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
27
|
+
i++;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const len = view.getUint16(i + 6, true);
|
|
31
|
+
if (len < 8 || len % 4 !== 0) {
|
|
32
|
+
i++;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (i + len > data.length) break;
|
|
36
|
+
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
37
|
+
badCrc++;
|
|
38
|
+
i++;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
onBlock(view.getUint16(i + 4, true) & 8191, i, len);
|
|
42
|
+
i += len;
|
|
43
|
+
}
|
|
44
|
+
return badCrc;
|
|
45
|
+
}
|
|
46
|
+
var two = (n) => String(n).padStart(2, "0");
|
|
47
|
+
function svidToPrn(svid) {
|
|
48
|
+
if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
|
|
49
|
+
if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
|
|
50
|
+
if (svid <= 62) return null;
|
|
51
|
+
if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
|
|
52
|
+
if (svid <= 70) return null;
|
|
53
|
+
if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
|
|
54
|
+
if (svid <= 119) return null;
|
|
55
|
+
if (svid <= 140) return `S${two(svid - 100)}`;
|
|
56
|
+
if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
|
|
57
|
+
if (svid <= 190) return `J${two(svid - 180)}`;
|
|
58
|
+
if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
|
|
59
|
+
if (svid <= 215) return `S${two(svid - 157)}`;
|
|
60
|
+
if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
|
|
61
|
+
if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/sbf/nav.ts
|
|
66
|
+
var PI = Math.PI;
|
|
67
|
+
var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
|
|
68
|
+
var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
|
|
69
|
+
var SEC_PER_WEEK = 7 * 86400;
|
|
70
|
+
var MS_PER_WEEK = SEC_PER_WEEK * 1e3;
|
|
71
|
+
var F4_DNU = -2e10;
|
|
72
|
+
var U4_DNU = 4294967295;
|
|
73
|
+
function adjustWeek(refWeek, wn, mod) {
|
|
74
|
+
let offset = refWeek % mod - wn;
|
|
75
|
+
if (offset > mod / 2) offset -= mod;
|
|
76
|
+
if (offset < -(mod / 2 - 1)) offset += mod;
|
|
77
|
+
return refWeek - offset;
|
|
78
|
+
}
|
|
79
|
+
var gpsMs = (week, sec) => GPS_EPOCH_MS + week * MS_PER_WEEK + sec * 1e3;
|
|
80
|
+
var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
|
|
81
|
+
function decodeGpsQzsNav(view, b, sys) {
|
|
82
|
+
const svid = view.getUint8(b + 14);
|
|
83
|
+
const prn = sys === "G" ? svid : svid - 180;
|
|
84
|
+
const max = sys === "G" ? 32 : 10;
|
|
85
|
+
if (prn < 1 || prn > max) return null;
|
|
86
|
+
const wnc = view.getUint16(b + 12, true);
|
|
87
|
+
const tgdRaw = view.getFloat32(b + 28, true);
|
|
88
|
+
const tocs = view.getUint32(b + 32, true);
|
|
89
|
+
const toes = view.getUint32(b + 88, true);
|
|
90
|
+
const wnToc = adjustWeek(wnc, view.getUint16(b + 136, true), 1024);
|
|
91
|
+
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
92
|
+
return {
|
|
93
|
+
system: sys,
|
|
94
|
+
prn: `${sys}${String(prn).padStart(2, "0")}`,
|
|
95
|
+
toc: sowOf(tocDate.getTime()),
|
|
96
|
+
tocDate,
|
|
97
|
+
af0: view.getFloat32(b + 44, true),
|
|
98
|
+
af1: view.getFloat32(b + 40, true),
|
|
99
|
+
af2: view.getFloat32(b + 36, true),
|
|
100
|
+
iode: view.getUint8(b + 24),
|
|
101
|
+
crs: view.getFloat32(b + 48, true),
|
|
102
|
+
deltaN: view.getFloat32(b + 52, true) * PI,
|
|
103
|
+
m0: view.getFloat64(b + 56, true) * PI,
|
|
104
|
+
cuc: view.getFloat32(b + 64, true),
|
|
105
|
+
e: view.getFloat64(b + 68, true),
|
|
106
|
+
cus: view.getFloat32(b + 76, true),
|
|
107
|
+
sqrtA: view.getFloat64(b + 80, true),
|
|
108
|
+
toe: toes,
|
|
109
|
+
cic: view.getFloat32(b + 92, true),
|
|
110
|
+
omega0: view.getFloat64(b + 96, true) * PI,
|
|
111
|
+
cis: view.getFloat32(b + 104, true),
|
|
112
|
+
i0: view.getFloat64(b + 108, true) * PI,
|
|
113
|
+
crc: view.getFloat32(b + 116, true),
|
|
114
|
+
omega: view.getFloat64(b + 120, true) * PI,
|
|
115
|
+
omegaDot: view.getFloat32(b + 128, true) * PI,
|
|
116
|
+
idot: view.getFloat32(b + 132, true) * PI,
|
|
117
|
+
// RINEX "GPS week to go with toe": RTKLIB writes WNc for GPS and
|
|
118
|
+
// the week of toc for QZSS — mirrored here so records compare
|
|
119
|
+
// 1:1 against convbin output. wnToe is still used for `toe` above.
|
|
120
|
+
week: sys === "G" ? wnc : wnToc,
|
|
121
|
+
svHealth: view.getUint8(b + 20),
|
|
122
|
+
tgd: tgdRaw !== F4_DNU ? tgdRaw : 0
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function decodeGalNav(view, b) {
|
|
126
|
+
const prn = view.getUint8(b + 14) - 70;
|
|
127
|
+
if (prn < 1 || prn > 36) return null;
|
|
128
|
+
const source = view.getUint8(b + 15);
|
|
129
|
+
if (source !== 2 && source !== 16) return null;
|
|
130
|
+
const wnc = view.getUint16(b + 12, true);
|
|
131
|
+
const toes = view.getUint32(b + 100, true);
|
|
132
|
+
const tocs = view.getUint32(b + 104, true);
|
|
133
|
+
const wnToc = adjustWeek(wnc, view.getUint16(b + 126, true), 4096);
|
|
134
|
+
const tocDate = new Date(gpsMs(wnToc, tocs));
|
|
135
|
+
const health = view.getUint16(b + 130, true);
|
|
136
|
+
let svh = 0;
|
|
137
|
+
if (health & 1) svh |= health >> 1 & 7;
|
|
138
|
+
if (health & 16) svh |= (health >> 5 & 7) << 6;
|
|
139
|
+
if (health & 256) svh |= (health >> 9 & 7) << 3;
|
|
140
|
+
const bgdE5a = view.getFloat32(b + 136, true);
|
|
141
|
+
return {
|
|
142
|
+
system: "E",
|
|
143
|
+
prn: `E${String(prn).padStart(2, "0")}`,
|
|
144
|
+
toc: sowOf(tocDate.getTime()),
|
|
145
|
+
tocDate,
|
|
146
|
+
af0: view.getFloat64(b + 116, true),
|
|
147
|
+
af1: view.getFloat32(b + 112, true),
|
|
148
|
+
af2: view.getFloat32(b + 108, true),
|
|
149
|
+
iode: view.getUint16(b + 128, true),
|
|
150
|
+
// IODnav
|
|
151
|
+
crs: view.getFloat32(b + 88, true),
|
|
152
|
+
deltaN: view.getFloat32(b + 72, true) * PI,
|
|
153
|
+
m0: view.getFloat64(b + 24, true) * PI,
|
|
154
|
+
cuc: view.getFloat32(b + 76, true),
|
|
155
|
+
e: view.getFloat64(b + 32, true),
|
|
156
|
+
cus: view.getFloat32(b + 80, true),
|
|
157
|
+
sqrtA: view.getFloat64(b + 16, true),
|
|
158
|
+
toe: toes,
|
|
159
|
+
cic: view.getFloat32(b + 92, true),
|
|
160
|
+
omega0: view.getFloat64(b + 56, true) * PI,
|
|
161
|
+
cis: view.getFloat32(b + 96, true),
|
|
162
|
+
i0: view.getFloat64(b + 40, true) * PI,
|
|
163
|
+
crc: view.getFloat32(b + 84, true),
|
|
164
|
+
omega: view.getFloat64(b + 48, true) * PI,
|
|
165
|
+
omegaDot: view.getFloat32(b + 64, true) * PI,
|
|
166
|
+
idot: view.getFloat32(b + 68, true) * PI,
|
|
167
|
+
week: wnc,
|
|
168
|
+
// RINEX GAL week is GPS-aligned and continuous
|
|
169
|
+
svHealth: svh,
|
|
170
|
+
tgd: bgdE5a !== F4_DNU ? bgdE5a : 0
|
|
171
|
+
// BGD E5a/E1 (RINEX slot)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function decodeBdsNav(view, b) {
|
|
175
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
176
|
+
if (!prn || prn[0] !== "C") return null;
|
|
177
|
+
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
178
|
+
const tgd1 = view.getFloat32(b + 24, true);
|
|
179
|
+
const tocs = view.getUint32(b + 32, true);
|
|
180
|
+
const toes = view.getUint32(b + 88, true);
|
|
181
|
+
const wnToc = adjustWeek(bdsWeekRef, view.getUint16(b + 136, true), 8192);
|
|
182
|
+
const wnToe = adjustWeek(bdsWeekRef, view.getUint16(b + 138, true), 8192);
|
|
183
|
+
const tocDate = new Date(BDT_EPOCH_MS + wnToc * MS_PER_WEEK + tocs * 1e3);
|
|
184
|
+
return {
|
|
185
|
+
system: "C",
|
|
186
|
+
prn,
|
|
187
|
+
toc: sowOf(tocDate.getTime()),
|
|
188
|
+
tocDate,
|
|
189
|
+
af0: view.getFloat32(b + 44, true),
|
|
190
|
+
af1: view.getFloat32(b + 40, true),
|
|
191
|
+
af2: view.getFloat32(b + 36, true),
|
|
192
|
+
iode: view.getUint8(b + 21),
|
|
193
|
+
// AODE
|
|
194
|
+
crs: view.getFloat32(b + 48, true),
|
|
195
|
+
deltaN: view.getFloat32(b + 52, true) * PI,
|
|
196
|
+
m0: view.getFloat64(b + 56, true) * PI,
|
|
197
|
+
cuc: view.getFloat32(b + 64, true),
|
|
198
|
+
e: view.getFloat64(b + 68, true),
|
|
199
|
+
cus: view.getFloat32(b + 76, true),
|
|
200
|
+
sqrtA: view.getFloat64(b + 80, true),
|
|
201
|
+
toe: toes,
|
|
202
|
+
cic: view.getFloat32(b + 92, true),
|
|
203
|
+
omega0: view.getFloat64(b + 96, true) * PI,
|
|
204
|
+
cis: view.getFloat32(b + 104, true),
|
|
205
|
+
i0: view.getFloat64(b + 108, true) * PI,
|
|
206
|
+
crc: view.getFloat32(b + 116, true),
|
|
207
|
+
omega: view.getFloat64(b + 120, true) * PI,
|
|
208
|
+
omegaDot: view.getFloat32(b + 128, true) * PI,
|
|
209
|
+
idot: view.getFloat32(b + 132, true) * PI,
|
|
210
|
+
week: wnToe,
|
|
211
|
+
// RINEX BDS week field is the BDT week
|
|
212
|
+
svHealth: view.getUint8(b + 19),
|
|
213
|
+
// SatH1
|
|
214
|
+
tgd: tgd1 !== F4_DNU ? tgd1 : 0
|
|
215
|
+
// TGD1 B1/B3 (RINEX slot)
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function decodeGloNav(view, b) {
|
|
219
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
220
|
+
if (!prn || prn[0] !== "R") return null;
|
|
221
|
+
const wnc = view.getUint16(b + 12, true);
|
|
222
|
+
const toes = view.getUint32(b + 76, true);
|
|
223
|
+
const wnToe = adjustWeek(wnc, view.getUint16(b + 80, true), 1024);
|
|
224
|
+
const toeGpsMs = gpsMs(wnToe, toes);
|
|
225
|
+
const leapMs = getGpsLeap(new Date(toeGpsMs)) * 1e3;
|
|
226
|
+
const tocDate = new Date(toeGpsMs - leapMs);
|
|
227
|
+
const tofGpsMs = gpsMs(wnc, 0) + view.getUint32(b + 8, true);
|
|
228
|
+
const tofLeapMs = getGpsLeap(new Date(tofGpsMs)) * 1e3;
|
|
229
|
+
const messageFrameTime = (tofGpsMs - tofLeapMs - GPS_EPOCH_MS) / 1e3 % SEC_PER_WEEK;
|
|
230
|
+
return {
|
|
231
|
+
system: "R",
|
|
232
|
+
prn,
|
|
233
|
+
tocDate,
|
|
234
|
+
// RINEX stores −τn as the clock bias; SBF carries τn (ICD sign)
|
|
235
|
+
tauN: -view.getFloat32(b + 68, true),
|
|
236
|
+
gammaN: view.getFloat32(b + 64, true),
|
|
237
|
+
messageFrameTime,
|
|
238
|
+
x: view.getFloat64(b + 16, true),
|
|
239
|
+
// SBF unit is km, like RINEX
|
|
240
|
+
xDot: view.getFloat32(b + 40, true),
|
|
241
|
+
xAcc: view.getFloat32(b + 52, true),
|
|
242
|
+
y: view.getFloat64(b + 24, true),
|
|
243
|
+
yDot: view.getFloat32(b + 44, true),
|
|
244
|
+
yAcc: view.getFloat32(b + 56, true),
|
|
245
|
+
z: view.getFloat64(b + 32, true),
|
|
246
|
+
zDot: view.getFloat32(b + 48, true),
|
|
247
|
+
zAcc: view.getFloat32(b + 60, true),
|
|
248
|
+
// MSB of the 3-bit Bn word — the unhealthy flag RINEX carries
|
|
249
|
+
health: view.getUint8(b + 85) >> 2,
|
|
250
|
+
freqNum: view.getUint8(b + 15) - 8
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function parseSbfNav(data) {
|
|
254
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
255
|
+
const ephemerides = [];
|
|
256
|
+
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
257
|
+
let eph = null;
|
|
258
|
+
if (id === 5891 && len >= 140) eph = decodeGpsQzsNav(view, b, "G");
|
|
259
|
+
else if (id === 4095 && len >= 140) eph = decodeGpsQzsNav(view, b, "J");
|
|
260
|
+
else if (id === 4002 && len >= 149) eph = decodeGalNav(view, b);
|
|
261
|
+
else if (id === 4081 && len >= 140) eph = decodeBdsNav(view, b);
|
|
262
|
+
else if (id === 4004 && len >= 96) eph = decodeGloNav(view, b);
|
|
263
|
+
if (eph) ephemerides.push(eph);
|
|
264
|
+
});
|
|
265
|
+
return { ephemerides, badCrc };
|
|
266
|
+
}
|
|
267
|
+
var GAL_SQRT_A_NOMINAL = Math.sqrt(296e5);
|
|
268
|
+
var GAL_I_NOMINAL = 56 / 180 * PI;
|
|
269
|
+
var I_REF_03 = 0.3 * PI;
|
|
270
|
+
function decodeGpsGalAlm(view, b, sys) {
|
|
271
|
+
const wnc = view.getUint16(b + 12, true);
|
|
272
|
+
const toa = view.getUint32(b + 20, true);
|
|
273
|
+
if (toa === U4_DNU) return null;
|
|
274
|
+
let prn;
|
|
275
|
+
let weekAlm;
|
|
276
|
+
let i0;
|
|
277
|
+
let sqrtA = view.getFloat32(b + 32, true);
|
|
278
|
+
let health;
|
|
279
|
+
if (sys === "G") {
|
|
280
|
+
prn = view.getUint8(b + 14);
|
|
281
|
+
if (prn < 1 || prn > 32) return null;
|
|
282
|
+
weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 256);
|
|
283
|
+
i0 = I_REF_03 + view.getFloat32(b + 24, true) * PI;
|
|
284
|
+
health = view.getUint8(b + 58);
|
|
285
|
+
} else {
|
|
286
|
+
prn = view.getUint8(b + 57) - 70;
|
|
287
|
+
if (prn < 1 || prn > 36) return null;
|
|
288
|
+
weekAlm = adjustWeek(wnc, view.getUint8(b + 56), 4);
|
|
289
|
+
i0 = GAL_I_NOMINAL + view.getFloat32(b + 24, true) * PI;
|
|
290
|
+
sqrtA += GAL_SQRT_A_NOMINAL;
|
|
291
|
+
health = view.getUint16(b + 58, true);
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
system: sys,
|
|
295
|
+
prn: `${sys}${String(prn).padStart(2, "0")}`,
|
|
296
|
+
weekAlm,
|
|
297
|
+
toaSec: toa,
|
|
298
|
+
sqrtA,
|
|
299
|
+
e: view.getFloat32(b + 16, true),
|
|
300
|
+
i0OrDeltaI: i0,
|
|
301
|
+
omega0: view.getFloat32(b + 36, true) * PI,
|
|
302
|
+
omega: view.getFloat32(b + 40, true) * PI,
|
|
303
|
+
m0: view.getFloat32(b + 44, true) * PI,
|
|
304
|
+
omegaDot: view.getFloat32(b + 28, true) * PI,
|
|
305
|
+
af0: view.getFloat32(b + 52, true),
|
|
306
|
+
af1: view.getFloat32(b + 48, true),
|
|
307
|
+
health
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function decodeBdsAlm(view, b) {
|
|
311
|
+
const prnStr = svidToPrn(view.getUint8(b + 14));
|
|
312
|
+
if (!prnStr || prnStr[0] !== "C") return null;
|
|
313
|
+
const prn = parseInt(prnStr.slice(1), 10);
|
|
314
|
+
const toa = view.getUint32(b + 16, true);
|
|
315
|
+
if (toa === U4_DNU) return null;
|
|
316
|
+
const bdsWeekRef = view.getUint16(b + 12, true) - 1356;
|
|
317
|
+
const geo = prn <= 5 || prn >= 59;
|
|
318
|
+
return {
|
|
319
|
+
system: "C",
|
|
320
|
+
prn: prnStr,
|
|
321
|
+
weekAlm: adjustWeek(bdsWeekRef, view.getUint8(b + 15), 256),
|
|
322
|
+
toaSec: toa,
|
|
323
|
+
sqrtA: view.getFloat32(b + 20, true),
|
|
324
|
+
e: view.getFloat32(b + 24, true),
|
|
325
|
+
i0OrDeltaI: (geo ? 0 : I_REF_03) + view.getFloat32(b + 44, true) * PI,
|
|
326
|
+
omega0: view.getFloat32(b + 36, true) * PI,
|
|
327
|
+
omega: view.getFloat32(b + 28, true) * PI,
|
|
328
|
+
m0: view.getFloat32(b + 32, true) * PI,
|
|
329
|
+
omegaDot: view.getFloat32(b + 40, true) * PI,
|
|
330
|
+
af0: view.getFloat32(b + 48, true),
|
|
331
|
+
af1: view.getFloat32(b + 52, true),
|
|
332
|
+
health: view.getUint16(b + 56, true)
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function decodeGloAlm(view, b) {
|
|
336
|
+
const prn = svidToPrn(view.getUint8(b + 14));
|
|
337
|
+
if (!prn || prn[0] !== "R") return null;
|
|
338
|
+
const wnc = view.getUint16(b + 12, true);
|
|
339
|
+
const toa = view.getUint32(b + 20, true);
|
|
340
|
+
if (toa === U4_DNU) return null;
|
|
341
|
+
return {
|
|
342
|
+
system: "R",
|
|
343
|
+
prn,
|
|
344
|
+
freqNr: view.getUint8(b + 15) - 8,
|
|
345
|
+
weekAlm: adjustWeek(wnc, view.getUint8(b + 52), 256),
|
|
346
|
+
toaSec: toa,
|
|
347
|
+
epsilon: view.getFloat32(b + 16, true),
|
|
348
|
+
deltaI: view.getFloat32(b + 24, true) * PI,
|
|
349
|
+
lambda: view.getFloat32(b + 28, true) * PI,
|
|
350
|
+
tLambda: view.getFloat32(b + 32, true),
|
|
351
|
+
omega: view.getFloat32(b + 36, true) * PI,
|
|
352
|
+
deltaT: view.getFloat32(b + 40, true),
|
|
353
|
+
deltaTDot: view.getFloat32(b + 44, true),
|
|
354
|
+
tau: view.getFloat32(b + 48, true),
|
|
355
|
+
health: view.getUint8(b + 53),
|
|
356
|
+
nDay: view.getUint16(b + 54, true),
|
|
357
|
+
n4: view.getUint8(b + 57)
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function parseSbfAlmanac(data) {
|
|
361
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
362
|
+
const almanacs = [];
|
|
363
|
+
const badCrc = scanSbfFrames(data, view, (id, b, len) => {
|
|
364
|
+
let alm = null;
|
|
365
|
+
if (id === 5892 && len >= 60) alm = decodeGpsGalAlm(view, b, "G");
|
|
366
|
+
else if (id === 4003 && len >= 61) alm = decodeGpsGalAlm(view, b, "E");
|
|
367
|
+
else if (id === 4119 && len >= 60) alm = decodeBdsAlm(view, b);
|
|
368
|
+
else if (id === 4005 && len >= 60) alm = decodeGloAlm(view, b);
|
|
369
|
+
if (alm) almanacs.push(alm);
|
|
370
|
+
});
|
|
371
|
+
return { almanacs, badCrc };
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/sbf/index.ts
|
|
375
|
+
var CLIGHT = 299792458;
|
|
376
|
+
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
377
|
+
var MS_PER_WEEK2 = 7 * 864e5;
|
|
21
378
|
var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
|
|
22
379
|
var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
|
|
23
380
|
var M3_INTERVALS = [
|
|
@@ -116,44 +473,27 @@ var MEAS2_SIG = [
|
|
|
116
473
|
["J", "1E"],
|
|
117
474
|
["J", "5P"]
|
|
118
475
|
];
|
|
119
|
-
var
|
|
476
|
+
var two2 = (n) => String(n).padStart(2, "0");
|
|
120
477
|
function m3Prn(navsys, svid) {
|
|
121
478
|
switch (navsys) {
|
|
122
479
|
case 0:
|
|
123
|
-
return svid < 32 ? `G${
|
|
480
|
+
return svid < 32 ? `G${two2(svid + 1)}` : null;
|
|
124
481
|
case 1:
|
|
125
|
-
return svid < 27 ? `R${
|
|
482
|
+
return svid < 27 ? `R${two2(svid + 1)}` : null;
|
|
126
483
|
case 2:
|
|
127
|
-
return svid < 36 ? `E${
|
|
484
|
+
return svid < 36 ? `E${two2(svid + 1)}` : null;
|
|
128
485
|
case 3:
|
|
129
|
-
return svid < 50 ? `C${
|
|
486
|
+
return svid < 50 ? `C${two2(svid + 1)}` : null;
|
|
130
487
|
case 4:
|
|
131
|
-
return svid <= 38 ? `S${
|
|
488
|
+
return svid <= 38 ? `S${two2(svid + 20)}` : null;
|
|
132
489
|
case 5:
|
|
133
|
-
return svid < 10 ? `J${
|
|
490
|
+
return svid < 10 ? `J${two2(svid + 1)}` : null;
|
|
134
491
|
case 6:
|
|
135
|
-
return svid < 14 ? `I${
|
|
492
|
+
return svid < 14 ? `I${two2(svid + 1)}` : null;
|
|
136
493
|
default:
|
|
137
494
|
return null;
|
|
138
495
|
}
|
|
139
496
|
}
|
|
140
|
-
function svidToPrn(svid) {
|
|
141
|
-
if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
|
|
142
|
-
if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
|
|
143
|
-
if (svid <= 62) return null;
|
|
144
|
-
if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
|
|
145
|
-
if (svid <= 70) return null;
|
|
146
|
-
if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
|
|
147
|
-
if (svid <= 119) return null;
|
|
148
|
-
if (svid <= 140) return `S${two(svid - 100)}`;
|
|
149
|
-
if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
|
|
150
|
-
if (svid <= 190) return `J${two(svid - 180)}`;
|
|
151
|
-
if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
|
|
152
|
-
if (svid <= 215) return `S${two(svid - 157)}`;
|
|
153
|
-
if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
|
|
154
|
-
if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
497
|
function carrierFreq(sys, code, fcn) {
|
|
158
498
|
const band = code.charCodeAt(0) - 48;
|
|
159
499
|
switch (sys) {
|
|
@@ -216,7 +556,6 @@ function parseSbfMeas(data) {
|
|
|
216
556
|
const epochs = [];
|
|
217
557
|
const messageCounts = {};
|
|
218
558
|
const obsCodes = {};
|
|
219
|
-
let badCrc = 0;
|
|
220
559
|
let ref = freshRefEpoch(-1);
|
|
221
560
|
let curTime = NaN;
|
|
222
561
|
let curMeas = [];
|
|
@@ -247,8 +586,8 @@ function parseSbfMeas(data) {
|
|
|
247
586
|
curTime = timeMs;
|
|
248
587
|
}
|
|
249
588
|
};
|
|
250
|
-
const decodeMeas3Ranges = (
|
|
251
|
-
const o =
|
|
589
|
+
const decodeMeas3Ranges = (i, tow) => {
|
|
590
|
+
const o = i + 8;
|
|
252
591
|
const constellations = view.getUint16(o + 8, true) & 127;
|
|
253
592
|
const misc = data[o + 10];
|
|
254
593
|
const version = data[o + 11];
|
|
@@ -526,8 +865,8 @@ function parseSbfMeas(data) {
|
|
|
526
865
|
}
|
|
527
866
|
}
|
|
528
867
|
};
|
|
529
|
-
const decodeMeas3CN0 = (
|
|
530
|
-
const flags = view.getUint16(
|
|
868
|
+
const decodeMeas3CN0 = (i, len) => {
|
|
869
|
+
const flags = view.getUint16(i + 14, true);
|
|
531
870
|
if ((flags & 7) !== 0) return;
|
|
532
871
|
const maxNibbles = (len - 16) * 2;
|
|
533
872
|
let off = 0;
|
|
@@ -537,7 +876,7 @@ function parseSbfMeas(data) {
|
|
|
537
876
|
for (let k = 0; k < e.slots.length; k++) {
|
|
538
877
|
const slot = e.slots[k];
|
|
539
878
|
if (k > 0 && !slot) break;
|
|
540
|
-
const nib = (data[
|
|
879
|
+
const nib = (data[i + 16 + (off >> 1)] ?? 0) >> (off & 1) * 4 & 15;
|
|
541
880
|
if (slot && slot.cn0 !== null) slot.cn0 += nib * 0.0625 - 0.5;
|
|
542
881
|
off++;
|
|
543
882
|
}
|
|
@@ -564,14 +903,14 @@ function parseSbfMeas(data) {
|
|
|
564
903
|
if ((v & 1) === 1) value = -value;
|
|
565
904
|
return { value, size };
|
|
566
905
|
};
|
|
567
|
-
const decodeMeas3Doppler = (
|
|
568
|
-
const flags = view.getUint16(
|
|
906
|
+
const decodeMeas3Doppler = (i, len) => {
|
|
907
|
+
const flags = view.getUint16(i + 14, true);
|
|
569
908
|
if ((flags & 7) !== 0) return;
|
|
570
909
|
let off = 0;
|
|
571
910
|
for (const e of curSats) {
|
|
572
911
|
if (off + 16 >= len) break;
|
|
573
912
|
if (!e.prn) continue;
|
|
574
|
-
const master = readPrRate(
|
|
913
|
+
const master = readPrRate(i + 16, off);
|
|
575
914
|
off += master.size;
|
|
576
915
|
if (master.value === -268435455) continue;
|
|
577
916
|
const refPrRate = ref.sats[e.navsys][e.svid]?.prRate ?? 0;
|
|
@@ -581,20 +920,20 @@ function parseSbfMeas(data) {
|
|
|
581
920
|
for (let k = 1; k < e.slots.length; k++) {
|
|
582
921
|
const slot = e.slots[k];
|
|
583
922
|
if (!slot) break;
|
|
584
|
-
const slave = readPrRate(
|
|
923
|
+
const slave = readPrRate(i + 16, off);
|
|
585
924
|
off += slave.size;
|
|
586
925
|
if (m)
|
|
587
926
|
slot.doppler = (mD * (CLIGHT / m.freq) * 1e3 - slave.value) * 1e-3 / (CLIGHT / slot.freq);
|
|
588
927
|
}
|
|
589
928
|
}
|
|
590
929
|
};
|
|
591
|
-
const decodeMeasEpoch = (
|
|
592
|
-
const n1 = data[
|
|
593
|
-
const len1 = data[
|
|
594
|
-
const len2 = data[
|
|
595
|
-
if ((data[
|
|
596
|
-
const end =
|
|
597
|
-
let p =
|
|
930
|
+
const decodeMeasEpoch = (i, len) => {
|
|
931
|
+
const n1 = data[i + 14];
|
|
932
|
+
const len1 = data[i + 15];
|
|
933
|
+
const len2 = data[i + 16];
|
|
934
|
+
if ((data[i + 17] & 128) !== 0) return;
|
|
935
|
+
const end = i + len;
|
|
936
|
+
let p = i + 20;
|
|
598
937
|
for (let b1 = 0; b1 < n1 && p + 20 <= end; b1++) {
|
|
599
938
|
const ant = data[p + 1] >> 5;
|
|
600
939
|
let sig = data[p + 1] & 31;
|
|
@@ -681,43 +1020,27 @@ function parseSbfMeas(data) {
|
|
|
681
1020
|
}
|
|
682
1021
|
}
|
|
683
1022
|
};
|
|
684
|
-
|
|
685
|
-
while (i + 8 <= data.length) {
|
|
686
|
-
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
687
|
-
i++;
|
|
688
|
-
continue;
|
|
689
|
-
}
|
|
690
|
-
const len = view.getUint16(i + 6, true);
|
|
691
|
-
if (len < 8 || len % 4 !== 0) {
|
|
692
|
-
i++;
|
|
693
|
-
continue;
|
|
694
|
-
}
|
|
695
|
-
if (i + len > data.length) break;
|
|
696
|
-
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
697
|
-
badCrc++;
|
|
698
|
-
i++;
|
|
699
|
-
continue;
|
|
700
|
-
}
|
|
701
|
-
const id = view.getUint16(i + 4, true) & 8191;
|
|
1023
|
+
const badCrc = scanSbfFrames(data, view, (id, i, len) => {
|
|
702
1024
|
const key = String(id);
|
|
703
1025
|
messageCounts[key] = (messageCounts[key] ?? 0) + 1;
|
|
704
1026
|
if ((id === 4027 || id === 4109 || id === 4110 || id === 4111) && len >= 16) {
|
|
705
1027
|
const tow = view.getUint32(i + 8, true);
|
|
706
1028
|
const wnc = view.getUint16(i + 12, true);
|
|
707
1029
|
if (tow !== 4294967295 && wnc !== 65535) {
|
|
708
|
-
ensureEpoch(
|
|
1030
|
+
ensureEpoch(GPS_EPOCH_MS2 + wnc * MS_PER_WEEK2 + tow);
|
|
709
1031
|
if (id === 4109) decodeMeas3Ranges(i, tow);
|
|
710
1032
|
else if (id === 4110) decodeMeas3CN0(i, len);
|
|
711
1033
|
else if (id === 4111) decodeMeas3Doppler(i, len);
|
|
712
1034
|
else decodeMeasEpoch(i, len);
|
|
713
1035
|
}
|
|
714
1036
|
}
|
|
715
|
-
|
|
716
|
-
}
|
|
1037
|
+
});
|
|
717
1038
|
flush();
|
|
718
1039
|
return { epochs, messageCounts, obsCodes, badCrc };
|
|
719
1040
|
}
|
|
720
1041
|
|
|
721
1042
|
export {
|
|
1043
|
+
parseSbfNav,
|
|
1044
|
+
parseSbfAlmanac,
|
|
722
1045
|
parseSbfMeas
|
|
723
1046
|
};
|