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/ubx.d.ts CHANGED
@@ -1,3 +1,122 @@
1
+ import { E as Ephemeris } from './nav-BAI1a9vK.js';
2
+
3
+ /**
4
+ * u-blox UBX framing shared by the measurement and navigation decoders:
5
+ * 0xB5 0x62 sync, message class and ID, little-endian payload length,
6
+ * payload, and an 8-bit Fletcher checksum over class..payload.
7
+ */
8
+ /** One checksum-valid UBX frame located in a byte stream. */
9
+ interface UbxFrame {
10
+ /** Message class, e.g. 0x02 for RXM. */
11
+ msgClass: number;
12
+ /** Message ID within the class, e.g. 0x15 for RXM-RAWX. */
13
+ msgId: number;
14
+ /** Payload bytes (a view into the input buffer, not a copy). */
15
+ payload: Uint8Array;
16
+ /** Byte offset of the payload within the input buffer. */
17
+ payloadStart: number;
18
+ }
19
+ /**
20
+ * Iterate every checksum-valid UBX frame in `data`. Bad checksums are
21
+ * counted in `stats.badChecksums` and a resync continues at the next
22
+ * byte; a frame candidate that overruns the buffer ends the scan
23
+ * (truncated capture).
24
+ */
25
+ declare function ubxFrames(data: Uint8Array, stats?: {
26
+ badChecksums: number;
27
+ }): Generator<UbxFrame>;
28
+
29
+ /**
30
+ * u-blox UBX navigation-message decoding: GPS/QZSS LNAV broadcast
31
+ * ephemerides from RXM-SFRBX (class 0x02, id 0x13).
32
+ *
33
+ * Ported from RTKLIB demo5 (rtklibexplorer fork, src/rcv/ublox.c:
34
+ * decode_rxmsfrbx / decode_nav / decode_eph, Copyright (c) 2007-2020
35
+ * T. Takasu, BSD-2-Clause); the subframe fields themselves are decoded
36
+ * by `decodeGpsLnavFrame` (the decode_frame port in src/navbits).
37
+ *
38
+ * On D30* polarity: the receiver has already checked parity and
39
+ * resolved the polarity inversion of the data bits before writing the
40
+ * 30-bit words into the 30 LSBs of each `dwrd`, so — exactly like
41
+ * RTKLIB's decode_nav (`U4(p)>>6`, "24 x 10 bits w/o parity") — the
42
+ * conversion to parity-stripped 24-bit words is a plain 6-bit shift
43
+ * with NO D30* re-inversion. (Verified against the TLM preamble 0x8B
44
+ * at bits 29..22 of word 1 in every GPS/QZSS LNAV message of the
45
+ * reference F9P capture.)
46
+ *
47
+ * Other constellations in RXM-SFRBX (Galileo I/NAV, BDS D1/D2, GLONASS
48
+ * strings, SBAS) and GPS/QZSS CNAV are out of scope and skipped
49
+ * silently. LNAV subframes 4/5 are skipped here except that
50
+ * `parseUbxIonoUtc` (./iono.ts) decodes the subframe-4 page-18
51
+ * iono/UTC parameters through the shared `readLnavSubframe` helper.
52
+ */
53
+
54
+ interface UbxNavOptions {
55
+ /**
56
+ * Full GPS week used to resolve the 10-bit broadcast week. When
57
+ * omitted, the week is harvested from the first RXM-RAWX message in
58
+ * the same stream (u-blox raw logs carry both); a stream with
59
+ * neither yields no ephemerides — the system clock is never used.
60
+ */
61
+ refWeek?: number;
62
+ }
63
+ interface UbxNavResult {
64
+ /** Broadcast ephemerides in stream order, duplicates suppressed. */
65
+ ephemerides: Ephemeris[];
66
+ /**
67
+ * Subframes rejected for inconsistency: an out-of-range subframe ID
68
+ * in the HOW word, or a complete subframe-1/2/3 set whose
69
+ * IODC/IODE cross-check failed (mixed issues of data).
70
+ */
71
+ badParity: number;
72
+ }
73
+ /**
74
+ * Decode every GPS/QZSS LNAV ephemeris broadcast in a UBX byte stream
75
+ * (RXM-SFRBX). Subframes 1–3 are assembled per satellite; a frame is
76
+ * decoded when subframe 3 arrives with 1 and 2 buffered (RTKLIB
77
+ * decode_nav), and repeated broadcasts of an unchanged ephemeris are
78
+ * suppressed by issue of data and clock epoch like `parseNovatelNav`.
79
+ */
80
+ declare function parseUbxNav(data: Uint8Array, opts?: UbxNavOptions): UbxNavResult;
81
+
82
+ /**
83
+ * u-blox UBX ionosphere / UTC decoding: the GPS LNAV subframe 4
84
+ * page 18 broadcast (Klobuchar coefficients + GPS-UTC parameters)
85
+ * from RXM-SFRBX.
86
+ *
87
+ * Ported from RTKLIB demo5 (rtklibexplorer fork, src/rcvraw.c:
88
+ * decode_frame_ion / decode_frame_utc, Copyright (c) 2007-2020
89
+ * T. Takasu, BSD-2-Clause); frame extraction is shared with
90
+ * `parseUbxNav` through `readLnavSubframe` (./nav.ts).
91
+ *
92
+ * Scope is GPS satellites (gnssId 0) only: QZSS also broadcasts
93
+ * page-18-format iono parameters, but those are the separate
94
+ * wide-area/Japan-area QZSS coefficient set, not the GPS one a RINEX
95
+ * `GPSA`/`GPSB` header carries. Scale factors follow IS-GPS-200
96
+ * §20.3.3.5.1 (alpha_n in s/semicircle^n, beta_n in s/semicircle^n) —
97
+ * the same semicircle-based units the RINEX nav header prints, so the
98
+ * output matches `parseNavFile` on a converted header. Like
99
+ * `parseUbxNav`, the decoder never consults the system clock (nothing
100
+ * here needs a week reference).
101
+ */
102
+ interface UbxIonoUtcResult {
103
+ /**
104
+ * Iono coefficient sets keyed like `NavHeader.ionoCorrections`:
105
+ * `GPSA` (alpha_0..3) and `GPSB` (beta_0..3). The page repeats
106
+ * (nominally every 12.5 min per satellite); the last broadcast in
107
+ * the stream wins.
108
+ */
109
+ ionoCorrections: Record<string, number[]>;
110
+ /** GPS-UTC ΔtLS from the last page-18 broadcast, if any. */
111
+ leapSeconds: number | null;
112
+ }
113
+ /**
114
+ * Decode every GPS LNAV subframe 4 page 18 (data ID 1, SV ID 56) in a
115
+ * UBX byte stream (RXM-SFRBX) into Klobuchar iono coefficients and the
116
+ * current leap-second count.
117
+ */
118
+ declare function parseUbxIonoUtc(data: Uint8Array): UbxIonoUtcResult;
119
+
1
120
  /**
2
121
  * u-blox UBX raw-measurement decoding (RXM-RAWX) — the path from a
3
122
  * receiver log to RINEX-grade observables.
@@ -12,6 +131,7 @@
12
131
  * RINEX attributes chosen to match RTKLIB's u-blox conversion (the de
13
132
  * facto reference: GPS L2 CL/CM as 2X, Galileo E1 as 1X, E5b as 7X).
14
133
  */
134
+
15
135
  interface UbxMeasurement {
16
136
  /** RINEX PRN, e.g. "G04", "R11", "S23". */
17
137
  prn: string;
@@ -53,4 +173,4 @@ interface UbxParseResult {
53
173
  */
54
174
  declare function parseUbxRawx(data: Uint8Array): UbxParseResult;
55
175
 
56
- export { type UbxMeasurement, type UbxParseResult, type UbxRawxEpoch, parseUbxRawx };
176
+ export { type UbxFrame, type UbxIonoUtcResult, type UbxMeasurement, type UbxNavOptions, type UbxNavResult, type UbxParseResult, type UbxRawxEpoch, parseUbxIonoUtc, parseUbxNav, parseUbxRawx, ubxFrames };
package/dist/ubx.js CHANGED
@@ -1,6 +1,13 @@
1
1
  import {
2
- parseUbxRawx
3
- } from "./chunk-5IXK25MX.js";
2
+ parseUbxIonoUtc,
3
+ parseUbxNav,
4
+ parseUbxRawx,
5
+ ubxFrames
6
+ } from "./chunk-IG5CDNDS.js";
7
+ import "./chunk-REYOYF7O.js";
4
8
  export {
5
- parseUbxRawx
9
+ parseUbxIonoUtc,
10
+ parseUbxNav,
11
+ parseUbxRawx,
12
+ ubxFrames
6
13
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "1.15.0",
3
+ "version": "1.17.0",
4
4
  "description": "Comprehensive GNSS library for JavaScript — time scales, coordinates, RINEX parsing, RTCM3 decoding, orbit computation, and signal analysis",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1,116 +0,0 @@
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
- };