gnss-js 1.15.0 → 1.17.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-I4UV45NT.js → chunk-7QR2KPDU.js} +197 -148
- package/dist/chunk-IG5CDNDS.js +245 -0
- package/dist/chunk-REYOYF7O.js +147 -0
- package/dist/{chunk-BEQSEJMU.js → chunk-ZNCQNHNI.js} +36 -0
- package/dist/index.cjs +532 -181
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +18 -5
- package/dist/novatel.cjs +187 -11
- package/dist/novatel.d.cts +36 -13
- package/dist/novatel.d.ts +36 -13
- package/dist/novatel.js +2 -1
- package/dist/sbf.cjs +41 -2
- package/dist/sbf.d.cts +58 -1
- package/dist/sbf.d.ts +58 -1
- package/dist/sbf.js +9 -3
- package/dist/ubx.cjs +297 -29
- package/dist/ubx.d.cts +121 -1
- package/dist/ubx.d.ts +121 -1
- package/dist/ubx.js +10 -3
- package/package.json +1 -1
- package/dist/chunk-5IXK25MX.js +0 -116
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decodeGpsLnavFrame
|
|
3
|
+
} from "./chunk-REYOYF7O.js";
|
|
1
4
|
import {
|
|
2
5
|
getUtcDate
|
|
3
6
|
} from "./chunk-HVXYFUCB.js";
|
|
@@ -46,142 +49,27 @@ function* oem4Frames(data, view, stats) {
|
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
// src/navbits/index.ts
|
|
50
|
-
var GPS_PI = 3.1415926535898;
|
|
51
|
-
var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
|
|
52
|
-
var SEC_PER_WEEK = 7 * 86400;
|
|
53
|
-
function getBitU(buff, pos, len) {
|
|
54
|
-
let bits = 0;
|
|
55
|
-
for (let i = pos; i < pos + len; i++) {
|
|
56
|
-
bits = bits * 2 + (buff[i >> 3] >> 7 - (i & 7) & 1);
|
|
57
|
-
}
|
|
58
|
-
return bits;
|
|
59
|
-
}
|
|
60
|
-
function getBitS(buff, pos, len) {
|
|
61
|
-
const bits = getBitU(buff, pos, len);
|
|
62
|
-
if (len <= 0 || bits < 2 ** (len - 1)) return bits;
|
|
63
|
-
return bits - 2 ** len;
|
|
64
|
-
}
|
|
65
|
-
function decodeGpsLnavFrame(subframes, opts = {}) {
|
|
66
|
-
if (subframes.length < 90) return null;
|
|
67
|
-
const b = subframes;
|
|
68
|
-
let i = 24;
|
|
69
|
-
const tow1 = getBitU(b, i, 17) * 6;
|
|
70
|
-
i += 17 + 2;
|
|
71
|
-
const id1 = getBitU(b, i, 3);
|
|
72
|
-
i += 3 + 2;
|
|
73
|
-
const week10 = getBitU(b, i, 10);
|
|
74
|
-
i += 10;
|
|
75
|
-
i += 2;
|
|
76
|
-
i += 4;
|
|
77
|
-
const svHealth = getBitU(b, i, 6);
|
|
78
|
-
i += 6;
|
|
79
|
-
const iodc0 = getBitU(b, i, 2);
|
|
80
|
-
i += 2;
|
|
81
|
-
i += 1 + 87;
|
|
82
|
-
const tgdRaw = getBitS(b, i, 8);
|
|
83
|
-
i += 8;
|
|
84
|
-
const iodc1 = getBitU(b, i, 8);
|
|
85
|
-
i += 8;
|
|
86
|
-
const tocSec = getBitU(b, i, 16) * 16;
|
|
87
|
-
i += 16;
|
|
88
|
-
const af2 = getBitS(b, i, 8) * 2 ** -55;
|
|
89
|
-
i += 8;
|
|
90
|
-
const af1 = getBitS(b, i, 16) * 2 ** -43;
|
|
91
|
-
i += 16;
|
|
92
|
-
const af0 = getBitS(b, i, 22) * 2 ** -31;
|
|
93
|
-
i = 240 + 24;
|
|
94
|
-
i += 17 + 2;
|
|
95
|
-
const id2 = getBitU(b, i, 3);
|
|
96
|
-
i += 3 + 2;
|
|
97
|
-
const iode = getBitU(b, i, 8);
|
|
98
|
-
i += 8;
|
|
99
|
-
const crs = getBitS(b, i, 16) * 2 ** -5;
|
|
100
|
-
i += 16;
|
|
101
|
-
const deltaN = getBitS(b, i, 16) * 2 ** -43 * GPS_PI;
|
|
102
|
-
i += 16;
|
|
103
|
-
const m0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
104
|
-
i += 32;
|
|
105
|
-
const cuc = getBitS(b, i, 16) * 2 ** -29;
|
|
106
|
-
i += 16;
|
|
107
|
-
const e = getBitU(b, i, 32) * 2 ** -33;
|
|
108
|
-
i += 32;
|
|
109
|
-
const cus = getBitS(b, i, 16) * 2 ** -29;
|
|
110
|
-
i += 16;
|
|
111
|
-
const sqrtA = getBitU(b, i, 32) * 2 ** -19;
|
|
112
|
-
i += 32;
|
|
113
|
-
const toes = getBitU(b, i, 16) * 16;
|
|
114
|
-
i = 480 + 24;
|
|
115
|
-
i += 17 + 2;
|
|
116
|
-
const id3 = getBitU(b, i, 3);
|
|
117
|
-
i += 3 + 2;
|
|
118
|
-
const cic = getBitS(b, i, 16) * 2 ** -29;
|
|
119
|
-
i += 16;
|
|
120
|
-
const omega0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
121
|
-
i += 32;
|
|
122
|
-
const cis = getBitS(b, i, 16) * 2 ** -29;
|
|
123
|
-
i += 16;
|
|
124
|
-
const i0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
125
|
-
i += 32;
|
|
126
|
-
const crc = getBitS(b, i, 16) * 2 ** -5;
|
|
127
|
-
i += 16;
|
|
128
|
-
const omega = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
|
|
129
|
-
i += 32;
|
|
130
|
-
const omegaDot = getBitS(b, i, 24) * 2 ** -43 * GPS_PI;
|
|
131
|
-
i += 24;
|
|
132
|
-
const iode3 = getBitU(b, i, 8);
|
|
133
|
-
i += 8;
|
|
134
|
-
const idot = getBitS(b, i, 14) * 2 ** -43 * GPS_PI;
|
|
135
|
-
const iodc = iodc0 * 256 + iodc1;
|
|
136
|
-
if (id1 !== 1 || id2 !== 2 || id3 !== 3) return null;
|
|
137
|
-
if (iode3 !== iode || iode !== (iodc & 255)) return null;
|
|
138
|
-
const ref = opts.refWeek ?? Math.floor((Date.now() - GPS_EPOCH_MS) / 1e3 / SEC_PER_WEEK);
|
|
139
|
-
let week = week10 + 1024 * Math.round((ref - week10) / 1024);
|
|
140
|
-
if (toes < tow1 - 302400) week++;
|
|
141
|
-
else if (toes > tow1 + 302400) week--;
|
|
142
|
-
const prn = opts.prn ?? "G00";
|
|
143
|
-
const tocDate = new Date(
|
|
144
|
-
GPS_EPOCH_MS + (week * SEC_PER_WEEK + tocSec) * 1e3
|
|
145
|
-
);
|
|
146
|
-
return {
|
|
147
|
-
system: prn[0] === "J" ? "J" : "G",
|
|
148
|
-
prn,
|
|
149
|
-
tocDate,
|
|
150
|
-
// Same seconds-of-week convention as parseNavFile (rinex/nav.ts).
|
|
151
|
-
toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK,
|
|
152
|
-
af0,
|
|
153
|
-
af1,
|
|
154
|
-
af2,
|
|
155
|
-
iode,
|
|
156
|
-
crs,
|
|
157
|
-
deltaN,
|
|
158
|
-
m0,
|
|
159
|
-
cuc,
|
|
160
|
-
e,
|
|
161
|
-
cus,
|
|
162
|
-
sqrtA,
|
|
163
|
-
toe: toes,
|
|
164
|
-
cic,
|
|
165
|
-
omega0,
|
|
166
|
-
cis,
|
|
167
|
-
i0,
|
|
168
|
-
crc,
|
|
169
|
-
omega,
|
|
170
|
-
omegaDot,
|
|
171
|
-
idot,
|
|
172
|
-
week,
|
|
173
|
-
svHealth,
|
|
174
|
-
tgd: tgdRaw === -128 ? 0 : tgdRaw * 2 ** -31
|
|
175
|
-
// IS-GPS-200: -128 reserved
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
52
|
// src/novatel/nav.ts
|
|
53
|
+
var ID_IONUTC = 8;
|
|
180
54
|
var ID_RAWEPHEM = 41;
|
|
181
55
|
var ID_GLOEPHEMERIS = 723;
|
|
182
|
-
var
|
|
183
|
-
var
|
|
56
|
+
var ID_GALEPHEMERIS = 1122;
|
|
57
|
+
var ID_QZSSEPHEMERIS = 1336;
|
|
58
|
+
var ID_BDSEPHEMERIS = 1696;
|
|
59
|
+
var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
|
|
60
|
+
var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
|
|
61
|
+
var SEC_PER_WEEK = 7 * 86400;
|
|
184
62
|
var SEC_PER_DAY = 86400;
|
|
63
|
+
var MS_PER_WEEK = SEC_PER_WEEK * 1e3;
|
|
64
|
+
var gpsMs = (week, sec) => GPS_EPOCH_MS + week * MS_PER_WEEK + sec * 1e3;
|
|
65
|
+
var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
|
|
66
|
+
function nearWeekMs(refMs, sow) {
|
|
67
|
+
const refWeek = Math.floor((refMs - GPS_EPOCH_MS) / MS_PER_WEEK);
|
|
68
|
+
let ms = gpsMs(refWeek, sow);
|
|
69
|
+
if (ms < refMs - MS_PER_WEEK / 2) ms += MS_PER_WEEK;
|
|
70
|
+
else if (ms > refMs + MS_PER_WEEK / 2) ms -= MS_PER_WEEK;
|
|
71
|
+
return ms;
|
|
72
|
+
}
|
|
185
73
|
function decodeRawEphem(data, view, p) {
|
|
186
74
|
const prn = view.getUint32(p, true);
|
|
187
75
|
if (prn < 1 || prn > 32) return null;
|
|
@@ -203,9 +91,9 @@ function decodeGloEphemeris(view, p) {
|
|
|
203
91
|
tof += Math.floor(tow / SEC_PER_DAY) * SEC_PER_DAY;
|
|
204
92
|
if (tof < tow - 43200) tof += SEC_PER_DAY;
|
|
205
93
|
else if (tof > tow + 43200) tof -= SEC_PER_DAY;
|
|
206
|
-
const toeGpsMs =
|
|
207
|
-
const tofGpsMs =
|
|
208
|
-
const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() -
|
|
94
|
+
const toeGpsMs = GPS_EPOCH_MS + (week * SEC_PER_WEEK + tow) * 1e3;
|
|
95
|
+
const tofGpsMs = GPS_EPOCH_MS + (week * SEC_PER_WEEK + tof) * 1e3;
|
|
96
|
+
const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() - GPS_EPOCH_MS) / 1e3;
|
|
209
97
|
return {
|
|
210
98
|
system: "R",
|
|
211
99
|
prn: `R${String(slot).padStart(2, "0")}`,
|
|
@@ -213,7 +101,7 @@ function decodeGloEphemeris(view, p) {
|
|
|
213
101
|
tauN: -view.getFloat64(p + 100, true),
|
|
214
102
|
// RINEX stores −τ_n
|
|
215
103
|
gammaN: view.getFloat64(p + 116, true),
|
|
216
|
-
messageFrameTime: (tofUtcSec %
|
|
104
|
+
messageFrameTime: (tofUtcSec % SEC_PER_WEEK + SEC_PER_WEEK) % SEC_PER_WEEK,
|
|
217
105
|
x: view.getFloat64(p + 28, true) / 1e3,
|
|
218
106
|
y: view.getFloat64(p + 36, true) / 1e3,
|
|
219
107
|
z: view.getFloat64(p + 44, true) / 1e3,
|
|
@@ -227,23 +115,184 @@ function decodeGloEphemeris(view, p) {
|
|
|
227
115
|
freqNum
|
|
228
116
|
};
|
|
229
117
|
}
|
|
118
|
+
function decodeGalEphemeris(view, p, headerMs) {
|
|
119
|
+
const prn = view.getUint32(p, true);
|
|
120
|
+
if (prn < 1 || prn > 36) return null;
|
|
121
|
+
const fnav = (view.getUint32(p + 4, true) & 1) !== 0;
|
|
122
|
+
const svhE1b = view.getUint8(p + 12) & 3;
|
|
123
|
+
const svhE5a = view.getUint8(p + 13) & 3;
|
|
124
|
+
const svhE5b = view.getUint8(p + 14) & 3;
|
|
125
|
+
const dvsE1b = view.getUint8(p + 15) & 1;
|
|
126
|
+
const dvsE5a = view.getUint8(p + 16) & 1;
|
|
127
|
+
const dvsE5b = view.getUint8(p + 17) & 1;
|
|
128
|
+
const iodNav = view.getUint32(p + 20, true);
|
|
129
|
+
const toes = view.getUint32(p + 24, true);
|
|
130
|
+
const c = fnav ? p + 148 : p + 176;
|
|
131
|
+
const tocs = view.getUint32(c, true);
|
|
132
|
+
const toeMs = nearWeekMs(headerMs, toes);
|
|
133
|
+
const tocDate = new Date(nearWeekMs(headerMs, tocs));
|
|
134
|
+
return {
|
|
135
|
+
fnav,
|
|
136
|
+
eph: {
|
|
137
|
+
system: "E",
|
|
138
|
+
prn: `E${String(prn).padStart(2, "0")}`,
|
|
139
|
+
toc: sowOf(tocDate.getTime()),
|
|
140
|
+
tocDate,
|
|
141
|
+
af0: view.getFloat64(c + 4, true),
|
|
142
|
+
af1: view.getFloat64(c + 12, true),
|
|
143
|
+
af2: view.getFloat64(c + 20, true),
|
|
144
|
+
iode: iodNav,
|
|
145
|
+
crs: view.getFloat64(p + 92, true),
|
|
146
|
+
deltaN: view.getFloat64(p + 36, true),
|
|
147
|
+
m0: view.getFloat64(p + 44, true),
|
|
148
|
+
cuc: view.getFloat64(p + 68, true),
|
|
149
|
+
e: view.getFloat64(p + 52, true),
|
|
150
|
+
cus: view.getFloat64(p + 76, true),
|
|
151
|
+
sqrtA: view.getFloat64(p + 28, true),
|
|
152
|
+
toe: toes,
|
|
153
|
+
cic: view.getFloat64(p + 100, true),
|
|
154
|
+
omega0: view.getFloat64(p + 132, true),
|
|
155
|
+
cis: view.getFloat64(p + 108, true),
|
|
156
|
+
i0: view.getFloat64(p + 116, true),
|
|
157
|
+
crc: view.getFloat64(p + 84, true),
|
|
158
|
+
omega: view.getFloat64(p + 60, true),
|
|
159
|
+
omegaDot: view.getFloat64(p + 140, true),
|
|
160
|
+
idot: view.getFloat64(p + 124, true),
|
|
161
|
+
week: Math.floor((toeMs - GPS_EPOCH_MS) / MS_PER_WEEK),
|
|
162
|
+
// RINEX Galileo SVH bit layout (E1B DVS/HS in bits 0-2, E5a in
|
|
163
|
+
// 3-5, E5b in 6-8) — same packing as RTKLIB.
|
|
164
|
+
svHealth: svhE5b << 7 | dvsE5b << 6 | svhE5a << 4 | dvsE5a << 3 | svhE1b << 1 | dvsE1b,
|
|
165
|
+
tgd: view.getFloat64(p + 204, true)
|
|
166
|
+
// BGD E5a/E1 (RINEX slot)
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function decodeBdsEphemeris(view, p) {
|
|
171
|
+
const prn = view.getUint32(p, true);
|
|
172
|
+
if (prn < 1 || prn > 63) return null;
|
|
173
|
+
const week = view.getUint32(p + 4, true);
|
|
174
|
+
const health = view.getUint32(p + 16, true) & 1;
|
|
175
|
+
const tgd1 = view.getFloat64(p + 20, true);
|
|
176
|
+
const tocs = view.getUint32(p + 40, true);
|
|
177
|
+
const toes = view.getUint32(p + 72, true);
|
|
178
|
+
const tocDate = new Date(BDT_EPOCH_MS + week * MS_PER_WEEK + tocs * 1e3);
|
|
179
|
+
return {
|
|
180
|
+
system: "C",
|
|
181
|
+
prn: `C${String(prn).padStart(2, "0")}`,
|
|
182
|
+
toc: sowOf(tocDate.getTime()),
|
|
183
|
+
tocDate,
|
|
184
|
+
af0: view.getFloat64(p + 44, true),
|
|
185
|
+
af1: view.getFloat64(p + 52, true),
|
|
186
|
+
af2: view.getFloat64(p + 60, true),
|
|
187
|
+
iode: view.getUint32(p + 68, true),
|
|
188
|
+
// AODE
|
|
189
|
+
crs: view.getFloat64(p + 172, true),
|
|
190
|
+
deltaN: view.getFloat64(p + 100, true),
|
|
191
|
+
m0: view.getFloat64(p + 108, true),
|
|
192
|
+
cuc: view.getFloat64(p + 148, true),
|
|
193
|
+
e: view.getFloat64(p + 84, true),
|
|
194
|
+
cus: view.getFloat64(p + 156, true),
|
|
195
|
+
sqrtA: view.getFloat64(p + 76, true),
|
|
196
|
+
toe: toes,
|
|
197
|
+
cic: view.getFloat64(p + 180, true),
|
|
198
|
+
omega0: view.getFloat64(p + 116, true),
|
|
199
|
+
cis: view.getFloat64(p + 188, true),
|
|
200
|
+
i0: view.getFloat64(p + 132, true),
|
|
201
|
+
crc: view.getFloat64(p + 164, true),
|
|
202
|
+
omega: view.getFloat64(p + 92, true),
|
|
203
|
+
omegaDot: view.getFloat64(p + 124, true),
|
|
204
|
+
idot: view.getFloat64(p + 140, true),
|
|
205
|
+
week,
|
|
206
|
+
// RINEX BDS week field is the BDT week
|
|
207
|
+
svHealth: health,
|
|
208
|
+
tgd: tgd1
|
|
209
|
+
// TGD1 (B1) — RINEX slot
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function decodeQzssEphemeris(view, p) {
|
|
213
|
+
const prn = view.getUint32(p, true);
|
|
214
|
+
if (prn < 193 || prn > 202) return null;
|
|
215
|
+
const health = view.getUint32(p + 12, true) & 63;
|
|
216
|
+
const iode1 = view.getUint32(p + 16, true);
|
|
217
|
+
const week = view.getUint32(p + 24, true);
|
|
218
|
+
const toes = view.getFloat64(p + 32, true);
|
|
219
|
+
const toeMs = gpsMs(week, toes);
|
|
220
|
+
const tocs = view.getFloat64(p + 164, true);
|
|
221
|
+
const tocDate = new Date(nearWeekMs(toeMs, tocs));
|
|
222
|
+
return {
|
|
223
|
+
system: "J",
|
|
224
|
+
prn: `J${String(prn - 192).padStart(2, "0")}`,
|
|
225
|
+
toc: sowOf(tocDate.getTime()),
|
|
226
|
+
tocDate,
|
|
227
|
+
af0: view.getFloat64(p + 180, true),
|
|
228
|
+
af1: view.getFloat64(p + 188, true),
|
|
229
|
+
af2: view.getFloat64(p + 196, true),
|
|
230
|
+
iode: iode1,
|
|
231
|
+
crs: view.getFloat64(p + 104, true),
|
|
232
|
+
deltaN: view.getFloat64(p + 48, true),
|
|
233
|
+
m0: view.getFloat64(p + 56, true),
|
|
234
|
+
cuc: view.getFloat64(p + 80, true),
|
|
235
|
+
e: view.getFloat64(p + 64, true),
|
|
236
|
+
cus: view.getFloat64(p + 88, true),
|
|
237
|
+
sqrtA: Math.sqrt(view.getFloat64(p + 40, true)),
|
|
238
|
+
toe: toes,
|
|
239
|
+
cic: view.getFloat64(p + 112, true),
|
|
240
|
+
omega0: view.getFloat64(p + 144, true),
|
|
241
|
+
cis: view.getFloat64(p + 120, true),
|
|
242
|
+
i0: view.getFloat64(p + 128, true),
|
|
243
|
+
crc: view.getFloat64(p + 96, true),
|
|
244
|
+
omega: view.getFloat64(p + 72, true),
|
|
245
|
+
omegaDot: view.getFloat64(p + 152, true),
|
|
246
|
+
idot: view.getFloat64(p + 136, true),
|
|
247
|
+
week,
|
|
248
|
+
svHealth: health,
|
|
249
|
+
tgd: view.getFloat64(p + 172, true)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
230
252
|
function parseNovatelNav(data) {
|
|
231
253
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
232
254
|
const stats = { badCrc: 0 };
|
|
233
255
|
const ephemerides = [];
|
|
234
|
-
const
|
|
256
|
+
const ionoCorrections = {};
|
|
257
|
+
let leapSeconds = null;
|
|
258
|
+
const lastKep = /* @__PURE__ */ new Map();
|
|
235
259
|
const lastGlo = /* @__PURE__ */ new Map();
|
|
260
|
+
const pushKepler = (key, eph) => {
|
|
261
|
+
const prev = lastKep.get(key);
|
|
262
|
+
if (prev && prev.iode === eph.iode && prev.toe === eph.toe && prev.tocDate.getTime() === eph.tocDate.getTime()) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
lastKep.set(key, eph);
|
|
266
|
+
ephemerides.push(eph);
|
|
267
|
+
};
|
|
236
268
|
for (const frame of oem4Frames(data, view, stats)) {
|
|
237
269
|
if (!frame.binary) continue;
|
|
238
270
|
if (frame.id === ID_RAWEPHEM && frame.msgLen >= 102) {
|
|
239
271
|
const eph = decodeRawEphem(data, view, frame.payload);
|
|
240
|
-
if (
|
|
241
|
-
|
|
242
|
-
if (
|
|
243
|
-
|
|
272
|
+
if (eph) pushKepler(eph.prn, eph);
|
|
273
|
+
} else if (frame.id === ID_GALEPHEMERIS && frame.msgLen >= 220) {
|
|
274
|
+
if (frame.week === 0) continue;
|
|
275
|
+
const headerMs = gpsMs(frame.week, frame.towMs / 1e3);
|
|
276
|
+
const dec = decodeGalEphemeris(view, frame.payload, headerMs);
|
|
277
|
+
if (dec)
|
|
278
|
+
pushKepler(`${dec.eph.prn}:${dec.fnav ? "fnav" : "inav"}`, dec.eph);
|
|
279
|
+
} else if (frame.id === ID_BDSEPHEMERIS && frame.msgLen >= 196) {
|
|
280
|
+
const eph = decodeBdsEphemeris(view, frame.payload);
|
|
281
|
+
if (eph) pushKepler(eph.prn, eph);
|
|
282
|
+
} else if (frame.id === ID_QZSSEPHEMERIS && frame.msgLen >= 204) {
|
|
283
|
+
const eph = decodeQzssEphemeris(view, frame.payload);
|
|
284
|
+
if (eph) pushKepler(eph.prn, eph);
|
|
285
|
+
} else if (frame.id === ID_IONUTC && frame.msgLen >= 108) {
|
|
286
|
+
const p = frame.payload;
|
|
287
|
+
const alpha = [];
|
|
288
|
+
const beta = [];
|
|
289
|
+
for (let i = 0; i < 4; i++) {
|
|
290
|
+
alpha.push(view.getFloat64(p + i * 8, true));
|
|
291
|
+
beta.push(view.getFloat64(p + 32 + i * 8, true));
|
|
244
292
|
}
|
|
245
|
-
|
|
246
|
-
|
|
293
|
+
ionoCorrections["GPSA"] = alpha;
|
|
294
|
+
ionoCorrections["GPSB"] = beta;
|
|
295
|
+
leapSeconds = view.getInt32(p + 96, true);
|
|
247
296
|
} else if (frame.id === ID_GLOEPHEMERIS && frame.msgLen >= 144) {
|
|
248
297
|
const eph = decodeGloEphemeris(view, frame.payload);
|
|
249
298
|
if (!eph) continue;
|
|
@@ -255,12 +304,12 @@ function parseNovatelNav(data) {
|
|
|
255
304
|
ephemerides.push(eph);
|
|
256
305
|
}
|
|
257
306
|
}
|
|
258
|
-
return { ephemerides, badCrc: stats.badCrc };
|
|
307
|
+
return { ephemerides, ionoCorrections, leapSeconds, badCrc: stats.badCrc };
|
|
259
308
|
}
|
|
260
309
|
|
|
261
310
|
// src/novatel/index.ts
|
|
262
|
-
var
|
|
263
|
-
var
|
|
311
|
+
var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
|
|
312
|
+
var MS_PER_WEEK2 = 7 * 864e5;
|
|
264
313
|
var HDR = 28;
|
|
265
314
|
var ID_RANGE = 43;
|
|
266
315
|
var ID_RANGECMP = 140;
|
|
@@ -429,7 +478,7 @@ function parseNovatelRange(data) {
|
|
|
429
478
|
if (!codes.includes(code)) codes.push(code);
|
|
430
479
|
}
|
|
431
480
|
epochs.push({
|
|
432
|
-
timeMs:
|
|
481
|
+
timeMs: GPS_EPOCH_MS2 + week * MS_PER_WEEK2 + towMs,
|
|
433
482
|
meas
|
|
434
483
|
});
|
|
435
484
|
}
|
|
@@ -481,7 +530,7 @@ function parseNovatelRange(data) {
|
|
|
481
530
|
if (!codes.includes(code)) codes.push(code);
|
|
482
531
|
}
|
|
483
532
|
epochs.push({
|
|
484
|
-
timeMs:
|
|
533
|
+
timeMs: GPS_EPOCH_MS2 + week * MS_PER_WEEK2 + towMs,
|
|
485
534
|
meas
|
|
486
535
|
});
|
|
487
536
|
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decodeGpsLnavFrame,
|
|
3
|
+
getBitS,
|
|
4
|
+
getBitU,
|
|
5
|
+
setBitU
|
|
6
|
+
} from "./chunk-REYOYF7O.js";
|
|
7
|
+
|
|
8
|
+
// src/ubx/frame.ts
|
|
9
|
+
function* ubxFrames(data, stats = { badChecksums: 0 }) {
|
|
10
|
+
let i = 0;
|
|
11
|
+
while (i + 8 <= data.length) {
|
|
12
|
+
if (data[i] !== 181 || data[i + 1] !== 98) {
|
|
13
|
+
i++;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const len = data[i + 4] | data[i + 5] << 8;
|
|
17
|
+
const end = i + 6 + len + 2;
|
|
18
|
+
if (end > data.length) break;
|
|
19
|
+
let ckA = 0;
|
|
20
|
+
let ckB = 0;
|
|
21
|
+
for (let j = i + 2; j < i + 6 + len; j++) {
|
|
22
|
+
ckA = ckA + data[j] & 255;
|
|
23
|
+
ckB = ckB + ckA & 255;
|
|
24
|
+
}
|
|
25
|
+
if (ckA !== data[i + 6 + len] || ckB !== data[i + 7 + len]) {
|
|
26
|
+
stats.badChecksums++;
|
|
27
|
+
i++;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
yield {
|
|
31
|
+
msgClass: data[i + 2],
|
|
32
|
+
msgId: data[i + 3],
|
|
33
|
+
payload: data.subarray(i + 6, i + 6 + len),
|
|
34
|
+
payloadStart: i + 6
|
|
35
|
+
};
|
|
36
|
+
i = end;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/ubx/nav.ts
|
|
41
|
+
var PREAMB_CNAV = 139;
|
|
42
|
+
function readLnavSubframe(view, f) {
|
|
43
|
+
const p = f.payload;
|
|
44
|
+
if (p.length < 8) return null;
|
|
45
|
+
const gnssId = p[0];
|
|
46
|
+
const svId = p[1];
|
|
47
|
+
let prn;
|
|
48
|
+
if (gnssId === 0 && svId >= 1 && svId <= 32) {
|
|
49
|
+
prn = `G${String(svId).padStart(2, "0")}`;
|
|
50
|
+
} else if (gnssId === 5 && svId >= 1 && svId <= 10) {
|
|
51
|
+
if (p.length === 44) return null;
|
|
52
|
+
prn = `J${String(svId).padStart(2, "0")}`;
|
|
53
|
+
} else {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (p.length < 8 + 40) return null;
|
|
57
|
+
const base = f.payloadStart + 8;
|
|
58
|
+
if (view.getUint32(base, true) >>> 24 === PREAMB_CNAV) return null;
|
|
59
|
+
const buff = new Uint8Array(30);
|
|
60
|
+
for (let k = 0; k < 10; k++) {
|
|
61
|
+
const dwrd = view.getUint32(base + 4 * k, true);
|
|
62
|
+
setBitU(buff, 24 * k, 24, dwrd >>> 6 & 16777215);
|
|
63
|
+
}
|
|
64
|
+
return { prn, gnssId, svId, buff, id: getBitU(buff, 43, 3) };
|
|
65
|
+
}
|
|
66
|
+
function parseUbxNav(data, opts = {}) {
|
|
67
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
68
|
+
const ephemerides = [];
|
|
69
|
+
let badParity = 0;
|
|
70
|
+
let refWeek = opts.refWeek;
|
|
71
|
+
if (refWeek === void 0) {
|
|
72
|
+
for (const f of ubxFrames(data)) {
|
|
73
|
+
if (f.msgClass === 2 && f.msgId === 21 && f.payload.length >= 16) {
|
|
74
|
+
const week = view.getUint16(f.payloadStart + 8, true);
|
|
75
|
+
if (week > 0) {
|
|
76
|
+
refWeek = week;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (refWeek === void 0) return { ephemerides, badParity };
|
|
83
|
+
const subframes = /* @__PURE__ */ new Map();
|
|
84
|
+
const last = /* @__PURE__ */ new Map();
|
|
85
|
+
for (const f of ubxFrames(data)) {
|
|
86
|
+
if (f.msgClass !== 2 || f.msgId !== 19) continue;
|
|
87
|
+
const lnav = readLnavSubframe(view, f);
|
|
88
|
+
if (!lnav) continue;
|
|
89
|
+
const { prn, gnssId, svId, buff, id } = lnav;
|
|
90
|
+
if (id < 1 || id > 5) {
|
|
91
|
+
badParity++;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (id > 3) continue;
|
|
95
|
+
const key = gnssId * 256 + svId;
|
|
96
|
+
let sf = subframes.get(key);
|
|
97
|
+
if (!sf) {
|
|
98
|
+
sf = { buf: new Uint8Array(90), have: 0 };
|
|
99
|
+
subframes.set(key, sf);
|
|
100
|
+
}
|
|
101
|
+
sf.buf.set(buff, (id - 1) * 30);
|
|
102
|
+
sf.have |= 1 << id - 1;
|
|
103
|
+
if (id !== 3 || sf.have !== 7) continue;
|
|
104
|
+
const eph = decodeGpsLnavFrame(sf.buf, { prn, refWeek });
|
|
105
|
+
if (!eph) {
|
|
106
|
+
badParity++;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const prev = last.get(prn);
|
|
110
|
+
if (prev && prev.iode === eph.iode && prev.tocDate.getTime() === eph.tocDate.getTime()) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
last.set(prn, eph);
|
|
114
|
+
ephemerides.push(eph);
|
|
115
|
+
}
|
|
116
|
+
return { ephemerides, badParity };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/ubx/iono.ts
|
|
120
|
+
function parseUbxIonoUtc(data) {
|
|
121
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
122
|
+
const ionoCorrections = {};
|
|
123
|
+
let leapSeconds = null;
|
|
124
|
+
for (const f of ubxFrames(data)) {
|
|
125
|
+
if (f.msgClass !== 2 || f.msgId !== 19) continue;
|
|
126
|
+
const sf = readLnavSubframe(view, f);
|
|
127
|
+
if (!sf || sf.gnssId !== 0 || sf.id !== 4) continue;
|
|
128
|
+
const b = sf.buff;
|
|
129
|
+
if (getBitU(b, 48, 2) !== 1 || getBitU(b, 50, 6) !== 56) continue;
|
|
130
|
+
ionoCorrections["GPSA"] = [
|
|
131
|
+
getBitS(b, 56, 8) * 2 ** -30,
|
|
132
|
+
getBitS(b, 64, 8) * 2 ** -27,
|
|
133
|
+
getBitS(b, 72, 8) * 2 ** -24,
|
|
134
|
+
getBitS(b, 80, 8) * 2 ** -24
|
|
135
|
+
];
|
|
136
|
+
ionoCorrections["GPSB"] = [
|
|
137
|
+
getBitS(b, 88, 8) * 2 ** 11,
|
|
138
|
+
getBitS(b, 96, 8) * 2 ** 14,
|
|
139
|
+
getBitS(b, 104, 8) * 2 ** 16,
|
|
140
|
+
getBitS(b, 112, 8) * 2 ** 16
|
|
141
|
+
];
|
|
142
|
+
leapSeconds = getBitS(b, 192, 8);
|
|
143
|
+
}
|
|
144
|
+
return { ionoCorrections, leapSeconds };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/ubx/index.ts
|
|
148
|
+
var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
|
|
149
|
+
var MS_PER_WEEK = 7 * 864e5;
|
|
150
|
+
var SIGNALS = {
|
|
151
|
+
0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
|
|
152
|
+
// GPS L1C/A, L2CL, L2CM
|
|
153
|
+
1: { 0: ["S", "1C"] },
|
|
154
|
+
// SBAS L1C/A
|
|
155
|
+
2: { 0: ["E", "1X"], 1: ["E", "1X"], 5: ["E", "7X"], 6: ["E", "7X"] },
|
|
156
|
+
// E1C/B, E5bI/Q
|
|
157
|
+
3: { 0: ["C", "2I"], 1: ["C", "2I"], 2: ["C", "7I"], 3: ["C", "7I"] },
|
|
158
|
+
// B1I D1/D2, B2I D1/D2
|
|
159
|
+
5: { 0: ["J", "1C"], 4: ["J", "2X"], 5: ["J", "2X"] },
|
|
160
|
+
// QZSS L1C/A, L2CM/CL
|
|
161
|
+
6: { 0: ["R", "1C"], 2: ["R", "2C"] }
|
|
162
|
+
// GLONASS L1OF, L2OF
|
|
163
|
+
};
|
|
164
|
+
function prnFor(gnssId, svId) {
|
|
165
|
+
const two = (n) => String(n).padStart(2, "0");
|
|
166
|
+
switch (gnssId) {
|
|
167
|
+
case 0:
|
|
168
|
+
return svId >= 1 && svId <= 32 ? `G${two(svId)}` : null;
|
|
169
|
+
case 1:
|
|
170
|
+
return svId >= 120 && svId <= 158 ? `S${two(svId - 100)}` : null;
|
|
171
|
+
case 2:
|
|
172
|
+
return svId >= 1 && svId <= 36 ? `E${two(svId)}` : null;
|
|
173
|
+
case 3:
|
|
174
|
+
return svId >= 1 && svId <= 63 ? `C${two(svId)}` : null;
|
|
175
|
+
case 5:
|
|
176
|
+
return svId >= 1 && svId <= 10 ? `J${two(svId)}` : null;
|
|
177
|
+
case 6:
|
|
178
|
+
return svId >= 1 && svId <= 32 ? `R${two(svId)}` : null;
|
|
179
|
+
default:
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function parseUbxRawx(data) {
|
|
184
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
185
|
+
const epochs = [];
|
|
186
|
+
const messageCounts = {};
|
|
187
|
+
const obsCodes = {};
|
|
188
|
+
const stats = { badChecksums: 0 };
|
|
189
|
+
for (const frame of ubxFrames(data, stats)) {
|
|
190
|
+
const { msgClass: cls, msgId: id } = frame;
|
|
191
|
+
const len = frame.payload.length;
|
|
192
|
+
const key = `${cls.toString(16).padStart(2, "0")}-${id.toString(16).padStart(2, "0")}`;
|
|
193
|
+
messageCounts[key] = (messageCounts[key] ?? 0) + 1;
|
|
194
|
+
if (cls === 2 && id === 21 && len >= 16) {
|
|
195
|
+
const p = frame.payloadStart;
|
|
196
|
+
const rcvTow = view.getFloat64(p, true);
|
|
197
|
+
const week = view.getUint16(p + 8, true);
|
|
198
|
+
const leapS = view.getInt8(p + 10);
|
|
199
|
+
const numMeas = data[p + 11];
|
|
200
|
+
const recStat = data[p + 12];
|
|
201
|
+
const leapValid = (recStat & 1) !== 0;
|
|
202
|
+
const meas = [];
|
|
203
|
+
for (let k = 0; k < numMeas; k++) {
|
|
204
|
+
const off = p + 16 + 32 * k;
|
|
205
|
+
if (off + 32 > p + len) break;
|
|
206
|
+
const gnssId = data[off + 20];
|
|
207
|
+
const svId = data[off + 21];
|
|
208
|
+
const sigId = data[off + 22];
|
|
209
|
+
const trkStat = data[off + 30];
|
|
210
|
+
const prn = prnFor(gnssId, svId);
|
|
211
|
+
const sig = SIGNALS[gnssId]?.[sigId];
|
|
212
|
+
if (!prn || !sig) continue;
|
|
213
|
+
const prValid = (trkStat & 1) !== 0;
|
|
214
|
+
const cpValid = (trkStat & 2) !== 0;
|
|
215
|
+
const cp = view.getFloat64(off + 8, true);
|
|
216
|
+
meas.push({
|
|
217
|
+
prn,
|
|
218
|
+
code: sig[1],
|
|
219
|
+
pr: prValid ? view.getFloat64(off, true) : null,
|
|
220
|
+
cp: cpValid && cp !== 0 ? cp : null,
|
|
221
|
+
doppler: view.getFloat32(off + 16, true),
|
|
222
|
+
cn0: data[off + 26],
|
|
223
|
+
halfCycleAmbiguous: (trkStat & 4) !== 0,
|
|
224
|
+
lockTimeMs: view.getUint16(off + 24, true)
|
|
225
|
+
});
|
|
226
|
+
const sys = sig[0];
|
|
227
|
+
const codes = obsCodes[sys] ??= [];
|
|
228
|
+
if (!codes.includes(sig[1])) codes.push(sig[1]);
|
|
229
|
+
}
|
|
230
|
+
epochs.push({
|
|
231
|
+
timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + Math.round(rcvTow * 1e3),
|
|
232
|
+
leapS: leapValid ? leapS : null,
|
|
233
|
+
meas
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { epochs, messageCounts, obsCodes, badChecksums: stats.badChecksums };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export {
|
|
241
|
+
ubxFrames,
|
|
242
|
+
parseUbxNav,
|
|
243
|
+
parseUbxIonoUtc,
|
|
244
|
+
parseUbxRawx
|
|
245
|
+
};
|