gnss-js 1.12.0 → 1.14.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.
@@ -0,0 +1,116 @@
1
+ // src/ubx/index.ts
2
+ var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
3
+ var MS_PER_WEEK = 7 * 864e5;
4
+ var SIGNALS = {
5
+ 0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
6
+ // GPS L1C/A, L2CL, L2CM
7
+ 1: { 0: ["S", "1C"] },
8
+ // SBAS L1C/A
9
+ 2: { 0: ["E", "1X"], 1: ["E", "1X"], 5: ["E", "7X"], 6: ["E", "7X"] },
10
+ // E1C/B, E5bI/Q
11
+ 3: { 0: ["C", "2I"], 1: ["C", "2I"], 2: ["C", "7I"], 3: ["C", "7I"] },
12
+ // B1I D1/D2, B2I D1/D2
13
+ 5: { 0: ["J", "1C"], 4: ["J", "2X"], 5: ["J", "2X"] },
14
+ // QZSS L1C/A, L2CM/CL
15
+ 6: { 0: ["R", "1C"], 2: ["R", "2C"] }
16
+ // GLONASS L1OF, L2OF
17
+ };
18
+ function prnFor(gnssId, svId) {
19
+ const two = (n) => String(n).padStart(2, "0");
20
+ switch (gnssId) {
21
+ case 0:
22
+ return svId >= 1 && svId <= 32 ? `G${two(svId)}` : null;
23
+ case 1:
24
+ return svId >= 120 && svId <= 158 ? `S${two(svId - 100)}` : null;
25
+ case 2:
26
+ return svId >= 1 && svId <= 36 ? `E${two(svId)}` : null;
27
+ case 3:
28
+ return svId >= 1 && svId <= 63 ? `C${two(svId)}` : null;
29
+ case 5:
30
+ return svId >= 1 && svId <= 10 ? `J${two(svId)}` : null;
31
+ case 6:
32
+ return svId >= 1 && svId <= 32 ? `R${two(svId)}` : null;
33
+ default:
34
+ return null;
35
+ }
36
+ }
37
+ function parseUbxRawx(data) {
38
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
39
+ const epochs = [];
40
+ const messageCounts = {};
41
+ const obsCodes = {};
42
+ let badChecksums = 0;
43
+ let i = 0;
44
+ while (i + 8 <= data.length) {
45
+ if (data[i] !== 181 || data[i + 1] !== 98) {
46
+ i++;
47
+ continue;
48
+ }
49
+ const cls = data[i + 2];
50
+ const id = data[i + 3];
51
+ const len = data[i + 4] | data[i + 5] << 8;
52
+ const end = i + 6 + len + 2;
53
+ if (end > data.length) break;
54
+ let ckA = 0;
55
+ let ckB = 0;
56
+ for (let j = i + 2; j < i + 6 + len; j++) {
57
+ ckA = ckA + data[j] & 255;
58
+ ckB = ckB + ckA & 255;
59
+ }
60
+ if (ckA !== data[i + 6 + len] || ckB !== data[i + 7 + len]) {
61
+ badChecksums++;
62
+ i++;
63
+ continue;
64
+ }
65
+ const key = `${cls.toString(16).padStart(2, "0")}-${id.toString(16).padStart(2, "0")}`;
66
+ messageCounts[key] = (messageCounts[key] ?? 0) + 1;
67
+ if (cls === 2 && id === 21 && len >= 16) {
68
+ const p = i + 6;
69
+ const rcvTow = view.getFloat64(p, true);
70
+ const week = view.getUint16(p + 8, true);
71
+ const leapS = view.getInt8(p + 10);
72
+ const numMeas = data[p + 11];
73
+ const recStat = data[p + 12];
74
+ const leapValid = (recStat & 1) !== 0;
75
+ const meas = [];
76
+ for (let k = 0; k < numMeas; k++) {
77
+ const off = p + 16 + 32 * k;
78
+ if (off + 32 > p + len) break;
79
+ const gnssId = data[off + 20];
80
+ const svId = data[off + 21];
81
+ const sigId = data[off + 22];
82
+ const trkStat = data[off + 30];
83
+ const prn = prnFor(gnssId, svId);
84
+ const sig = SIGNALS[gnssId]?.[sigId];
85
+ if (!prn || !sig) continue;
86
+ const prValid = (trkStat & 1) !== 0;
87
+ const cpValid = (trkStat & 2) !== 0;
88
+ const cp = view.getFloat64(off + 8, true);
89
+ meas.push({
90
+ prn,
91
+ code: sig[1],
92
+ pr: prValid ? view.getFloat64(off, true) : null,
93
+ cp: cpValid && cp !== 0 ? cp : null,
94
+ doppler: view.getFloat32(off + 16, true),
95
+ cn0: data[off + 26],
96
+ halfCycleAmbiguous: (trkStat & 4) !== 0,
97
+ lockTimeMs: view.getUint16(off + 24, true)
98
+ });
99
+ const sys = sig[0];
100
+ const codes = obsCodes[sys] ??= [];
101
+ if (!codes.includes(sig[1])) codes.push(sig[1]);
102
+ }
103
+ epochs.push({
104
+ timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + Math.round(rcvTow * 1e3),
105
+ leapS: leapValid ? leapS : null,
106
+ meas
107
+ });
108
+ }
109
+ i = end;
110
+ }
111
+ return { epochs, messageCounts, obsCodes, badChecksums };
112
+ }
113
+
114
+ export {
115
+ parseUbxRawx
116
+ };
@@ -2,7 +2,7 @@ import {
2
2
  computeDop,
3
3
  computeSatPosition,
4
4
  ecefToAzEl
5
- } from "./chunk-DMNFB7Q6.js";
5
+ } from "./chunk-VAHMHYID.js";
6
6
  import {
7
7
  ecefToGeodetic
8
8
  } from "./chunk-37QNKGTC.js";
@@ -0,0 +1,246 @@
1
+ // src/novatel/index.ts
2
+ var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
3
+ var MS_PER_WEEK = 7 * 864e5;
4
+ var HDR = 28;
5
+ var ID_RANGE = 43;
6
+ var ID_RANGECMP = 140;
7
+ var ID_GLOEPHEMERIS = 723;
8
+ var C_LIGHT = 299792458;
9
+ var ADR_ROLL = 8388608;
10
+ function carrierFreq(sys, code, gloK) {
11
+ const band = code[0];
12
+ switch (sys) {
13
+ case "G":
14
+ case "J":
15
+ return band === "1" ? 157542e4 : band === "2" ? 12276e5 : 117645e4;
16
+ case "S":
17
+ return band === "1" ? 157542e4 : 117645e4;
18
+ case "E":
19
+ return band === "1" ? 157542e4 : band === "5" ? 117645e4 : band === "7" ? 120714e4 : band === "8" ? 1191795e3 : 127875e4;
20
+ case "C":
21
+ return band === "1" ? 157542e4 : band === "2" ? 1561098e3 : band === "5" ? 117645e4 : band === "7" ? 120714e4 : 126852e4;
22
+ case "R": {
23
+ if (gloK === null) return 0;
24
+ if (band === "1") return 1602e6 + gloK * 562500;
25
+ if (band === "2") return 1246e6 + gloK * 437500;
26
+ return 1202025e3;
27
+ }
28
+ default:
29
+ return 0;
30
+ }
31
+ }
32
+ function exsign(v, bits) {
33
+ return v & 1 << bits - 1 ? v - 2 ** bits : v;
34
+ }
35
+ function crc32(data, start, len) {
36
+ let crc = 0;
37
+ for (let i = start; i < start + len; i++) {
38
+ crc ^= data[i];
39
+ for (let j = 0; j < 8; j++) {
40
+ crc = crc & 1 ? crc >>> 1 ^ 3988292384 : crc >>> 1;
41
+ }
42
+ }
43
+ return crc >>> 0;
44
+ }
45
+ var SIGNALS = {
46
+ 0: {
47
+ 0: ["G", "1C"],
48
+ 5: ["G", "2P"],
49
+ 9: ["G", "2W"],
50
+ 14: ["G", "5Q"],
51
+ 16: ["G", "1L"],
52
+ 17: ["G", "2S"]
53
+ },
54
+ 1: { 0: ["R", "1C"], 1: ["R", "2C"], 5: ["R", "2P"], 6: ["R", "3Q"] },
55
+ 2: { 0: ["S", "1C"], 6: ["S", "5I"] },
56
+ 3: {
57
+ 1: ["E", "1C"],
58
+ 2: ["E", "1C"],
59
+ 6: ["E", "6B"],
60
+ 7: ["E", "6C"],
61
+ 12: ["E", "5Q"],
62
+ 17: ["E", "7Q"],
63
+ 20: ["E", "8Q"]
64
+ },
65
+ 4: {
66
+ 0: ["C", "2I"],
67
+ 1: ["C", "7I"],
68
+ 2: ["C", "6I"],
69
+ 4: ["C", "2I"],
70
+ 5: ["C", "7I"],
71
+ 6: ["C", "6I"],
72
+ 7: ["C", "1P"],
73
+ 9: ["C", "5P"],
74
+ 11: ["C", "7D"]
75
+ },
76
+ 5: {
77
+ 0: ["J", "1C"],
78
+ 14: ["J", "5Q"],
79
+ 16: ["J", "1L"],
80
+ 17: ["J", "2S"],
81
+ 27: ["J", "6L"]
82
+ },
83
+ 6: { 0: ["I", "5A"] }
84
+ };
85
+ function parseNovatelRange(data) {
86
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
87
+ const epochs = [];
88
+ const messageCounts = {};
89
+ const obsCodes = {};
90
+ let badCrc = 0;
91
+ const gloFcn = /* @__PURE__ */ new Map();
92
+ for (let j = 0; j + HDR + 4 <= data.length; j++) {
93
+ if (data[j] !== 170 || data[j + 1] !== 68 || data[j + 2] !== 18)
94
+ continue;
95
+ const hl = data[j + 3];
96
+ const id = view.getUint16(j + 4, true);
97
+ const ln = view.getUint16(j + 8, true);
98
+ if (id !== ID_GLOEPHEMERIS || hl < HDR || j + hl + ln + 4 > data.length)
99
+ continue;
100
+ if (crc32(data, j, hl + ln) !== view.getUint32(j + hl + ln, true)) continue;
101
+ const slot = view.getUint16(j + hl, true) - 37;
102
+ const k = view.getUint16(j + hl + 2, true) - 7;
103
+ if (slot >= 1 && slot <= 32) gloFcn.set(slot, k);
104
+ j += hl + ln + 3;
105
+ }
106
+ let i = 0;
107
+ while (i + HDR + 4 <= data.length) {
108
+ if (data[i] !== 170 || data[i + 1] !== 68 || data[i + 2] !== 18) {
109
+ i++;
110
+ continue;
111
+ }
112
+ const hlen = data[i + 3];
113
+ const msgId = view.getUint16(i + 4, true);
114
+ const msgLen = view.getUint16(i + 8, true);
115
+ const total = hlen + msgLen + 4;
116
+ if (hlen < HDR || i + total > data.length) {
117
+ i++;
118
+ continue;
119
+ }
120
+ if (crc32(data, i, hlen + msgLen) !== view.getUint32(i + hlen + msgLen, true)) {
121
+ badCrc++;
122
+ i++;
123
+ continue;
124
+ }
125
+ messageCounts[msgId] = (messageCounts[msgId] ?? 0) + 1;
126
+ const binary = (data[i + 6] >> 4 & 3) === 0;
127
+ const week = view.getUint16(i + 14, true);
128
+ const towMs = view.getUint32(i + 16, true);
129
+ if (msgId === ID_RANGECMP && binary && week > 0) {
130
+ const p = i + hlen;
131
+ const nobs = view.getUint32(p, true);
132
+ if (p + 4 + nobs * 24 <= i + hlen + msgLen) {
133
+ const meas = [];
134
+ for (let k = 0; k < nobs; k++) {
135
+ const o = p + 4 + 24 * k;
136
+ const stat = view.getUint32(o, true);
137
+ const satsys = stat >>> 16 & 7;
138
+ const sigtype = stat >>> 21 & 31;
139
+ const plock = stat >>> 10 & 1;
140
+ const parity = stat >>> 11 & 1;
141
+ const clock = stat >>> 12 & 1;
142
+ const sig = SIGNALS[satsys]?.[sigtype];
143
+ if (!sig) continue;
144
+ let [sysL, code] = sig;
145
+ let prnNum = data[o + 17];
146
+ if (sysL === "R") prnNum -= 37;
147
+ if (sysL === "S" && prnNum >= 183 && prnNum <= 191 && code === "1C") {
148
+ sysL = "J";
149
+ code = "1Z";
150
+ prnNum -= 182;
151
+ } else if (sysL === "S") {
152
+ prnNum -= 100;
153
+ }
154
+ if (prnNum < 1 || prnNum > 99) continue;
155
+ if (sysL === "R" && !parity) continue;
156
+ const gloK = sysL === "R" ? gloFcn.get(prnNum) ?? null : null;
157
+ const dop = exsign(view.getUint32(o + 4, true) & 268435455, 28) / 256;
158
+ const psr = (view.getUint32(o + 7, true) >>> 4) / 128 + data[o + 11] * 2097152;
159
+ let cp = null;
160
+ const freq = carrierFreq(sysL, code, gloK);
161
+ if (freq > 0) {
162
+ const adr = view.getInt32(o + 12, true) / 256;
163
+ const rolls = psr * freq / C_LIGHT / ADR_ROLL + adr / ADR_ROLL;
164
+ cp = -adr + ADR_ROLL * Math.floor(rolls + (rolls <= 0 ? -0.5 : 0.5));
165
+ }
166
+ const lockt = (view.getUint32(o + 18, true) & 2097151) / 32;
167
+ const cn0 = ((view.getUint16(o + 20, true) & 1023) >>> 5) + 20;
168
+ meas.push({
169
+ prn: `${sysL}${String(prnNum).padStart(2, "0")}`,
170
+ code,
171
+ pr: clock ? psr : null,
172
+ cp: plock ? cp : null,
173
+ doppler: plock ? dop : null,
174
+ cn0,
175
+ lockTimeS: lockt,
176
+ gloChannel: gloK
177
+ });
178
+ const codes = obsCodes[sysL] ??= [];
179
+ if (!codes.includes(code)) codes.push(code);
180
+ }
181
+ epochs.push({
182
+ timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + towMs,
183
+ meas
184
+ });
185
+ }
186
+ } else if (msgId === ID_RANGE && binary && week > 0) {
187
+ const p = i + hlen;
188
+ const nobs = view.getUint32(p, true);
189
+ if (p + 4 + nobs * 44 <= i + hlen + msgLen) {
190
+ const meas = [];
191
+ for (let k = 0; k < nobs; k++) {
192
+ const o = p + 4 + 44 * k;
193
+ const stat = view.getUint32(o + 40, true);
194
+ const satsys = stat >>> 16 & 7;
195
+ const sigtype = stat >>> 21 & 31;
196
+ const plock = stat >>> 10 & 1;
197
+ const parity = stat >>> 11 & 1;
198
+ const clock = stat >>> 12 & 1;
199
+ const sig = SIGNALS[satsys]?.[sigtype];
200
+ if (!sig) continue;
201
+ let [sysL, code] = sig;
202
+ let prnNum = view.getUint16(o, true);
203
+ if (sysL === "R") prnNum -= 37;
204
+ if (sysL === "S" && prnNum >= 183 && prnNum <= 191 && code === "1C") {
205
+ sysL = "J";
206
+ code = "1Z";
207
+ prnNum -= 182;
208
+ } else if (sysL === "S") {
209
+ prnNum -= 100;
210
+ }
211
+ if (prnNum < 1 || prnNum > 99) continue;
212
+ if (sysL === "R" && !parity) continue;
213
+ const prn = `${sysL}${String(prnNum).padStart(2, "0")}`;
214
+ const gfrq = view.getUint16(o + 2, true);
215
+ const psr = view.getFloat64(o + 4, true);
216
+ const adr = view.getFloat64(o + 16, true);
217
+ const dop = view.getFloat32(o + 28, true);
218
+ const snr = view.getFloat32(o + 32, true);
219
+ const lockt = view.getFloat32(o + 36, true);
220
+ meas.push({
221
+ prn,
222
+ code,
223
+ pr: clock ? psr : null,
224
+ cp: plock ? -adr : null,
225
+ doppler: plock ? dop : null,
226
+ cn0: snr,
227
+ lockTimeS: lockt,
228
+ gloChannel: sysL === "R" ? gfrq - 8 : null
229
+ });
230
+ const codes = obsCodes[sysL] ??= [];
231
+ if (!codes.includes(code)) codes.push(code);
232
+ }
233
+ epochs.push({
234
+ timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + towMs,
235
+ meas
236
+ });
237
+ }
238
+ }
239
+ i += total;
240
+ }
241
+ return { epochs, messageCounts, obsCodes, badCrc };
242
+ }
243
+
244
+ export {
245
+ parseNovatelRange
246
+ };
@@ -506,8 +506,14 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
506
506
  const stepMs = stepSec * 1e3;
507
507
  const times = [];
508
508
  for (let t = startMs; t <= endMs; t += stepMs) times.push(t);
509
+ return computeVisibilityFromPositions(
510
+ computeAllPositions(ephemerides, times, rxPos),
511
+ elevationMaskDeg
512
+ );
513
+ }
514
+ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
515
+ const times = all.times;
509
516
  const maskRadFor = maskRadForAzimuth(elevationMaskDeg);
510
- const all = computeAllPositions(ephemerides, times, rxPos);
511
517
  const elevation = {};
512
518
  const azimuth = {};
513
519
  const subLat = {};
@@ -605,5 +611,6 @@ export {
605
611
  ephInfoToEphemeris,
606
612
  computeLiveSkyPositions,
607
613
  maskRadForAzimuth,
608
- computeVisibility
614
+ computeVisibility,
615
+ computeVisibilityFromPositions
609
616
  };