gnss-js 1.13.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.
package/dist/sbf.d.cts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Septentrio SBF raw-measurement decoding (MeasEpoch + Meas3) — the
3
+ * path from a receiver log to RINEX-grade observables.
4
+ *
5
+ * Framing: 0x24 0x40, CRC16-CCITT (poly 0x1021, init 0) as U2, block
6
+ * ID U2 (number in bits 0..12, revision in 13..15), total length U2
7
+ * (multiple of 4, includes the 8-byte header). The CRC covers block ID
8
+ * through payload end. Every measurement block starts with the receiver
9
+ * time stamp: TOW (U4, ms of GPS week) + WNc (U2, continuous week).
10
+ *
11
+ * Two measurement families are decoded:
12
+ * - MeasEpoch (4027): classic per-channel absolute + delta sub-blocks.
13
+ * - Meas3Ranges (4109) with the Meas3CN0HiRes (4110) and Meas3Doppler
14
+ * (4111) extensions: a compressed format alternating reference
15
+ * epochs (full measurements) and delta epochs (differences against
16
+ * the last reference), with per-constellation master signals and
17
+ * slave signals encoded relative to the master. Decoding is stateful
18
+ * across blocks; delta epochs before the first reference epoch in
19
+ * the stream cannot be decoded and are dropped.
20
+ *
21
+ * The Meas3 decoding logic is ported from RTKLIB demo5 (rtklibexplorer),
22
+ * src/rcv/septentrio.c, BSD-2-Clause, and cross-checked against the
23
+ * Septentrio mosaic-X5 reference guide (the guide documents MeasEpoch
24
+ * fully; the Meas3 bit layout is only public through that decoder).
25
+ */
26
+ interface SbfMeasurement {
27
+ /** RINEX PRN, e.g. "G04", "R11", "S23" (SBAS PRN-100). */
28
+ prn: string;
29
+ /** RINEX band+attribute, e.g. "1C", "2W", "5Q", "2I". */
30
+ code: string;
31
+ /** Pseudorange (m), null when flagged invalid. */
32
+ pr: number | null;
33
+ /** Carrier phase (cycles), null when flagged invalid. */
34
+ cp: number | null;
35
+ /** Doppler (Hz), null when the log carries no Doppler for the signal. */
36
+ doppler: number | null;
37
+ /** C/N0 (dB-Hz), null when flagged invalid. */
38
+ cn0: number | null;
39
+ /** Continuous PLL lock time (ms), null when unknown. */
40
+ lockTimeMs: number | null;
41
+ }
42
+ interface SbfMeasEpoch {
43
+ /** Receiver epoch (GPS-scale ms — same convention as the RINEX parser). */
44
+ timeMs: number;
45
+ meas: SbfMeasurement[];
46
+ }
47
+ interface SbfParseResult {
48
+ epochs: SbfMeasEpoch[];
49
+ /** Count per SBF block number, keyed "4109" style, for diagnostics. */
50
+ messageCounts: Record<string, number>;
51
+ /** Observation codes seen per system letter, in first-seen order. */
52
+ obsCodes: Record<string, string[]>;
53
+ /** Frames whose CRC failed (corruption indicator). */
54
+ badCrc: number;
55
+ }
56
+ /**
57
+ * Decode every valid MeasEpoch/Meas3 block in an SBF byte stream.
58
+ * Other block types are counted and skipped; frames with bad CRC are
59
+ * counted and a resync continues at the next byte.
60
+ */
61
+ declare function parseSbfMeas(data: Uint8Array): SbfParseResult;
62
+
63
+ export { type SbfMeasEpoch, type SbfMeasurement, type SbfParseResult, parseSbfMeas };
package/dist/sbf.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Septentrio SBF raw-measurement decoding (MeasEpoch + Meas3) — the
3
+ * path from a receiver log to RINEX-grade observables.
4
+ *
5
+ * Framing: 0x24 0x40, CRC16-CCITT (poly 0x1021, init 0) as U2, block
6
+ * ID U2 (number in bits 0..12, revision in 13..15), total length U2
7
+ * (multiple of 4, includes the 8-byte header). The CRC covers block ID
8
+ * through payload end. Every measurement block starts with the receiver
9
+ * time stamp: TOW (U4, ms of GPS week) + WNc (U2, continuous week).
10
+ *
11
+ * Two measurement families are decoded:
12
+ * - MeasEpoch (4027): classic per-channel absolute + delta sub-blocks.
13
+ * - Meas3Ranges (4109) with the Meas3CN0HiRes (4110) and Meas3Doppler
14
+ * (4111) extensions: a compressed format alternating reference
15
+ * epochs (full measurements) and delta epochs (differences against
16
+ * the last reference), with per-constellation master signals and
17
+ * slave signals encoded relative to the master. Decoding is stateful
18
+ * across blocks; delta epochs before the first reference epoch in
19
+ * the stream cannot be decoded and are dropped.
20
+ *
21
+ * The Meas3 decoding logic is ported from RTKLIB demo5 (rtklibexplorer),
22
+ * src/rcv/septentrio.c, BSD-2-Clause, and cross-checked against the
23
+ * Septentrio mosaic-X5 reference guide (the guide documents MeasEpoch
24
+ * fully; the Meas3 bit layout is only public through that decoder).
25
+ */
26
+ interface SbfMeasurement {
27
+ /** RINEX PRN, e.g. "G04", "R11", "S23" (SBAS PRN-100). */
28
+ prn: string;
29
+ /** RINEX band+attribute, e.g. "1C", "2W", "5Q", "2I". */
30
+ code: string;
31
+ /** Pseudorange (m), null when flagged invalid. */
32
+ pr: number | null;
33
+ /** Carrier phase (cycles), null when flagged invalid. */
34
+ cp: number | null;
35
+ /** Doppler (Hz), null when the log carries no Doppler for the signal. */
36
+ doppler: number | null;
37
+ /** C/N0 (dB-Hz), null when flagged invalid. */
38
+ cn0: number | null;
39
+ /** Continuous PLL lock time (ms), null when unknown. */
40
+ lockTimeMs: number | null;
41
+ }
42
+ interface SbfMeasEpoch {
43
+ /** Receiver epoch (GPS-scale ms — same convention as the RINEX parser). */
44
+ timeMs: number;
45
+ meas: SbfMeasurement[];
46
+ }
47
+ interface SbfParseResult {
48
+ epochs: SbfMeasEpoch[];
49
+ /** Count per SBF block number, keyed "4109" style, for diagnostics. */
50
+ messageCounts: Record<string, number>;
51
+ /** Observation codes seen per system letter, in first-seen order. */
52
+ obsCodes: Record<string, string[]>;
53
+ /** Frames whose CRC failed (corruption indicator). */
54
+ badCrc: number;
55
+ }
56
+ /**
57
+ * Decode every valid MeasEpoch/Meas3 block in an SBF byte stream.
58
+ * Other block types are counted and skipped; frames with bad CRC are
59
+ * counted and a resync continues at the next byte.
60
+ */
61
+ declare function parseSbfMeas(data: Uint8Array): SbfParseResult;
62
+
63
+ export { type SbfMeasEpoch, type SbfMeasurement, type SbfParseResult, parseSbfMeas };
package/dist/sbf.js ADDED
@@ -0,0 +1,6 @@
1
+ import {
2
+ parseSbfMeas
3
+ } from "./chunk-YIHO74OS.js";
4
+ export {
5
+ parseSbfMeas
6
+ };
package/dist/ubx.cjs ADDED
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/ubx/index.ts
21
+ var ubx_exports = {};
22
+ __export(ubx_exports, {
23
+ parseUbxRawx: () => parseUbxRawx
24
+ });
25
+ module.exports = __toCommonJS(ubx_exports);
26
+ var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
27
+ var MS_PER_WEEK = 7 * 864e5;
28
+ var SIGNALS = {
29
+ 0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
30
+ // GPS L1C/A, L2CL, L2CM
31
+ 1: { 0: ["S", "1C"] },
32
+ // SBAS L1C/A
33
+ 2: { 0: ["E", "1X"], 1: ["E", "1X"], 5: ["E", "7X"], 6: ["E", "7X"] },
34
+ // E1C/B, E5bI/Q
35
+ 3: { 0: ["C", "2I"], 1: ["C", "2I"], 2: ["C", "7I"], 3: ["C", "7I"] },
36
+ // B1I D1/D2, B2I D1/D2
37
+ 5: { 0: ["J", "1C"], 4: ["J", "2X"], 5: ["J", "2X"] },
38
+ // QZSS L1C/A, L2CM/CL
39
+ 6: { 0: ["R", "1C"], 2: ["R", "2C"] }
40
+ // GLONASS L1OF, L2OF
41
+ };
42
+ function prnFor(gnssId, svId) {
43
+ const two = (n) => String(n).padStart(2, "0");
44
+ switch (gnssId) {
45
+ case 0:
46
+ return svId >= 1 && svId <= 32 ? `G${two(svId)}` : null;
47
+ case 1:
48
+ return svId >= 120 && svId <= 158 ? `S${two(svId - 100)}` : null;
49
+ case 2:
50
+ return svId >= 1 && svId <= 36 ? `E${two(svId)}` : null;
51
+ case 3:
52
+ return svId >= 1 && svId <= 63 ? `C${two(svId)}` : null;
53
+ case 5:
54
+ return svId >= 1 && svId <= 10 ? `J${two(svId)}` : null;
55
+ case 6:
56
+ return svId >= 1 && svId <= 32 ? `R${two(svId)}` : null;
57
+ default:
58
+ return null;
59
+ }
60
+ }
61
+ function parseUbxRawx(data) {
62
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
63
+ const epochs = [];
64
+ const messageCounts = {};
65
+ const obsCodes = {};
66
+ let badChecksums = 0;
67
+ let i = 0;
68
+ while (i + 8 <= data.length) {
69
+ if (data[i] !== 181 || data[i + 1] !== 98) {
70
+ i++;
71
+ continue;
72
+ }
73
+ const cls = data[i + 2];
74
+ const id = data[i + 3];
75
+ const len = data[i + 4] | data[i + 5] << 8;
76
+ const end = i + 6 + len + 2;
77
+ if (end > data.length) break;
78
+ let ckA = 0;
79
+ let ckB = 0;
80
+ for (let j = i + 2; j < i + 6 + len; j++) {
81
+ ckA = ckA + data[j] & 255;
82
+ ckB = ckB + ckA & 255;
83
+ }
84
+ if (ckA !== data[i + 6 + len] || ckB !== data[i + 7 + len]) {
85
+ badChecksums++;
86
+ i++;
87
+ continue;
88
+ }
89
+ const key = `${cls.toString(16).padStart(2, "0")}-${id.toString(16).padStart(2, "0")}`;
90
+ messageCounts[key] = (messageCounts[key] ?? 0) + 1;
91
+ if (cls === 2 && id === 21 && len >= 16) {
92
+ const p = i + 6;
93
+ const rcvTow = view.getFloat64(p, true);
94
+ const week = view.getUint16(p + 8, true);
95
+ const leapS = view.getInt8(p + 10);
96
+ const numMeas = data[p + 11];
97
+ const recStat = data[p + 12];
98
+ const leapValid = (recStat & 1) !== 0;
99
+ const meas = [];
100
+ for (let k = 0; k < numMeas; k++) {
101
+ const off = p + 16 + 32 * k;
102
+ if (off + 32 > p + len) break;
103
+ const gnssId = data[off + 20];
104
+ const svId = data[off + 21];
105
+ const sigId = data[off + 22];
106
+ const trkStat = data[off + 30];
107
+ const prn = prnFor(gnssId, svId);
108
+ const sig = SIGNALS[gnssId]?.[sigId];
109
+ if (!prn || !sig) continue;
110
+ const prValid = (trkStat & 1) !== 0;
111
+ const cpValid = (trkStat & 2) !== 0;
112
+ const cp = view.getFloat64(off + 8, true);
113
+ meas.push({
114
+ prn,
115
+ code: sig[1],
116
+ pr: prValid ? view.getFloat64(off, true) : null,
117
+ cp: cpValid && cp !== 0 ? cp : null,
118
+ doppler: view.getFloat32(off + 16, true),
119
+ cn0: data[off + 26],
120
+ halfCycleAmbiguous: (trkStat & 4) !== 0,
121
+ lockTimeMs: view.getUint16(off + 24, true)
122
+ });
123
+ const sys = sig[0];
124
+ const codes = obsCodes[sys] ??= [];
125
+ if (!codes.includes(sig[1])) codes.push(sig[1]);
126
+ }
127
+ epochs.push({
128
+ timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + Math.round(rcvTow * 1e3),
129
+ leapS: leapValid ? leapS : null,
130
+ meas
131
+ });
132
+ }
133
+ i = end;
134
+ }
135
+ return { epochs, messageCounts, obsCodes, badChecksums };
136
+ }
137
+ // Annotate the CommonJS export names for ESM import in node:
138
+ 0 && (module.exports = {
139
+ parseUbxRawx
140
+ });
package/dist/ubx.d.cts ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * u-blox UBX raw-measurement decoding (RXM-RAWX) — the path from a
3
+ * receiver log to RINEX-grade observables.
4
+ *
5
+ * Framing: 0xB5 0x62, class, id, little-endian length, payload, and an
6
+ * 8-bit Fletcher checksum over class..payload. RXM-RAWX (0x02 0x15)
7
+ * carries the receiver time (GPS week + tow) and, per measurement,
8
+ * pseudorange (m), carrier phase (cycles), Doppler (Hz), C/N0 and the
9
+ * (gnssId, svId, sigId) triple that maps onto a RINEX observation code.
10
+ *
11
+ * The signal table follows the u-blox interface description, with the
12
+ * RINEX attributes chosen to match RTKLIB's u-blox conversion (the de
13
+ * facto reference: GPS L2 CL/CM as 2X, Galileo E1 as 1X, E5b as 7X).
14
+ */
15
+ interface UbxMeasurement {
16
+ /** RINEX PRN, e.g. "G04", "R11", "S23". */
17
+ prn: string;
18
+ /** RINEX band+attribute, e.g. "1C", "2X", "7I". */
19
+ code: string;
20
+ /** Pseudorange (m), null when flagged invalid. */
21
+ pr: number | null;
22
+ /** Carrier phase (cycles), null when flagged invalid. */
23
+ cp: number | null;
24
+ /** Doppler (Hz). */
25
+ doppler: number;
26
+ /** C/N0 (dB-Hz). */
27
+ cn0: number;
28
+ /** Carrier phase may be off by half a cycle (trkStat bit 2). */
29
+ halfCycleAmbiguous: boolean;
30
+ /** Continuous-lock time (ms, saturates at 64500). */
31
+ lockTimeMs: number;
32
+ }
33
+ interface UbxRawxEpoch {
34
+ /** Receiver epoch (GPS-scale ms — same convention as the RINEX parser). */
35
+ timeMs: number;
36
+ /** GPS leap seconds at this epoch, if the receiver knows them. */
37
+ leapS: number | null;
38
+ meas: UbxMeasurement[];
39
+ }
40
+ interface UbxParseResult {
41
+ epochs: UbxRawxEpoch[];
42
+ /** Count per UBX (class, id), keyed "02-15" style, for diagnostics. */
43
+ messageCounts: Record<string, number>;
44
+ /** Observation codes seen per system letter, in first-seen order. */
45
+ obsCodes: Record<string, string[]>;
46
+ /** Frames whose checksum failed (corruption indicator). */
47
+ badChecksums: number;
48
+ }
49
+ /**
50
+ * Decode every valid RXM-RAWX message in a UBX byte stream. Non-RAWX
51
+ * messages are counted and skipped; frames with bad checksums are
52
+ * skipped and counted (a resync then continues at the next byte).
53
+ */
54
+ declare function parseUbxRawx(data: Uint8Array): UbxParseResult;
55
+
56
+ export { type UbxMeasurement, type UbxParseResult, type UbxRawxEpoch, parseUbxRawx };
package/dist/ubx.d.ts ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * u-blox UBX raw-measurement decoding (RXM-RAWX) — the path from a
3
+ * receiver log to RINEX-grade observables.
4
+ *
5
+ * Framing: 0xB5 0x62, class, id, little-endian length, payload, and an
6
+ * 8-bit Fletcher checksum over class..payload. RXM-RAWX (0x02 0x15)
7
+ * carries the receiver time (GPS week + tow) and, per measurement,
8
+ * pseudorange (m), carrier phase (cycles), Doppler (Hz), C/N0 and the
9
+ * (gnssId, svId, sigId) triple that maps onto a RINEX observation code.
10
+ *
11
+ * The signal table follows the u-blox interface description, with the
12
+ * RINEX attributes chosen to match RTKLIB's u-blox conversion (the de
13
+ * facto reference: GPS L2 CL/CM as 2X, Galileo E1 as 1X, E5b as 7X).
14
+ */
15
+ interface UbxMeasurement {
16
+ /** RINEX PRN, e.g. "G04", "R11", "S23". */
17
+ prn: string;
18
+ /** RINEX band+attribute, e.g. "1C", "2X", "7I". */
19
+ code: string;
20
+ /** Pseudorange (m), null when flagged invalid. */
21
+ pr: number | null;
22
+ /** Carrier phase (cycles), null when flagged invalid. */
23
+ cp: number | null;
24
+ /** Doppler (Hz). */
25
+ doppler: number;
26
+ /** C/N0 (dB-Hz). */
27
+ cn0: number;
28
+ /** Carrier phase may be off by half a cycle (trkStat bit 2). */
29
+ halfCycleAmbiguous: boolean;
30
+ /** Continuous-lock time (ms, saturates at 64500). */
31
+ lockTimeMs: number;
32
+ }
33
+ interface UbxRawxEpoch {
34
+ /** Receiver epoch (GPS-scale ms — same convention as the RINEX parser). */
35
+ timeMs: number;
36
+ /** GPS leap seconds at this epoch, if the receiver knows them. */
37
+ leapS: number | null;
38
+ meas: UbxMeasurement[];
39
+ }
40
+ interface UbxParseResult {
41
+ epochs: UbxRawxEpoch[];
42
+ /** Count per UBX (class, id), keyed "02-15" style, for diagnostics. */
43
+ messageCounts: Record<string, number>;
44
+ /** Observation codes seen per system letter, in first-seen order. */
45
+ obsCodes: Record<string, string[]>;
46
+ /** Frames whose checksum failed (corruption indicator). */
47
+ badChecksums: number;
48
+ }
49
+ /**
50
+ * Decode every valid RXM-RAWX message in a UBX byte stream. Non-RAWX
51
+ * messages are counted and skipped; frames with bad checksums are
52
+ * skipped and counted (a resync then continues at the next byte).
53
+ */
54
+ declare function parseUbxRawx(data: Uint8Array): UbxParseResult;
55
+
56
+ export { type UbxMeasurement, type UbxParseResult, type UbxRawxEpoch, parseUbxRawx };
package/dist/ubx.js ADDED
@@ -0,0 +1,6 @@
1
+ import {
2
+ parseUbxRawx
3
+ } from "./chunk-5IXK25MX.js";
4
+ export {
5
+ parseUbxRawx
6
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "1.13.0",
3
+ "version": "1.14.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,
@@ -30,6 +30,21 @@
30
30
  "import": "./dist/rinex.js",
31
31
  "require": "./dist/rinex.cjs"
32
32
  },
33
+ "./ubx": {
34
+ "types": "./dist/ubx.d.ts",
35
+ "import": "./dist/ubx.js",
36
+ "require": "./dist/ubx.cjs"
37
+ },
38
+ "./novatel": {
39
+ "types": "./dist/novatel.d.ts",
40
+ "import": "./dist/novatel.js",
41
+ "require": "./dist/novatel.cjs"
42
+ },
43
+ "./sbf": {
44
+ "types": "./dist/sbf.d.ts",
45
+ "import": "./dist/sbf.js",
46
+ "require": "./dist/sbf.cjs"
47
+ },
33
48
  "./rtcm3": {
34
49
  "types": "./dist/rtcm3.d.ts",
35
50
  "import": "./dist/rtcm3.js",