gnss-js 1.14.1 → 1.16.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 CHANGED
@@ -1,3 +1,156 @@
1
+ import { E as Ephemeris } from './nav-BAI1a9vK.cjs';
2
+
3
+ /**
4
+ * Septentrio SBF framing shared by the measurement and navigation
5
+ * decoders: 0x24 0x40 sync, CRC16-CCITT (poly 0x1021, init 0) as U2,
6
+ * block ID U2 (number in bits 0..12, revision in 13..15), total length
7
+ * U2 (multiple of 4, includes the 8-byte header). The CRC covers block
8
+ * ID through payload end.
9
+ */
10
+ declare function crc16(data: Uint8Array, start: number, end: number): number;
11
+ /**
12
+ * Scan a byte stream for valid SBF frames and invoke `onBlock` for each
13
+ * one, passing the block number (revision bits stripped), the frame
14
+ * start offset and the total frame length. Frames with bad CRC are
15
+ * counted and a resync continues at the next byte; the count of bad
16
+ * frames is returned.
17
+ */
18
+ declare function scanSbfFrames(data: Uint8Array, view: DataView, onBlock: (id: number, offset: number, len: number) => void): number;
19
+
20
+ /**
21
+ * Septentrio SBF decoded navigation and almanac blocks.
22
+ *
23
+ * Nav blocks (GPSNav 5891, GLONav 4004, GALNav 4002, BDSNav 4081,
24
+ * QZSNav 4095) carry the fully decoded broadcast ephemeris; they are
25
+ * mapped onto the same `Ephemeris` union the RINEX nav parser produces,
26
+ * with identical units and conventions (sqrtA in m^1/2, angles in rad,
27
+ * semicircle fields scaled by π, GLONASS tauN with the RINEX sign, BDS
28
+ * epochs/weeks on the BDT scale like a RINEX file).
29
+ *
30
+ * Almanac blocks (GPSAlm 5892, GALAlm 4003, GLOAlm 4005, BDSAlm 4119)
31
+ * are decoded into dedicated types with all per-system relative fields
32
+ * (GPS/BDS δi, Galileo δi and ΔsqrtA) normalized to absolute values.
33
+ *
34
+ * Ported from RTKLIB demo5 (rtklibexplorer), src/rcv/septentrio.c
35
+ * (decode_gpsnav / decode_glonav / decode_galnav / decode_cmpnav /
36
+ * decode_qzssnav / decode_gpsalm / decode_galalm / decode_cmpalm),
37
+ * BSD-2-Clause, and cross-checked field by field against the Septentrio
38
+ * mosaic-X5 reference guide, which also supplies the GLOAlm layout
39
+ * (not decoded by RTKLIB).
40
+ */
41
+
42
+ interface SbfNavResult {
43
+ ephemerides: Ephemeris[];
44
+ /** Frames whose CRC failed (corruption indicator). */
45
+ badCrc: number;
46
+ }
47
+ /**
48
+ * Decode every GPSNav/GLONav/GALNav/BDSNav/QZSNav block in an SBF byte
49
+ * stream into the RINEX-parser `Ephemeris` union. Every block is
50
+ * emitted (no issue-of-data dedup); other block types are skipped.
51
+ */
52
+ declare function parseSbfNav(data: Uint8Array): SbfNavResult;
53
+ /**
54
+ * GPS / Galileo / BeiDou almanac, normalized to ABSOLUTE Keplerian
55
+ * elements (units matching `KeplerEphemeris`: sqrtA in m^1/2, angles
56
+ * in rad, rates in rad/s) so a Kepler propagator can consume the
57
+ * record directly with all correction terms at zero.
58
+ *
59
+ * Normalizations applied per system:
60
+ * - GPS (`G`): the SIS δi (relative to 0.3 semicircles) is folded in —
61
+ * `i0OrDeltaI` = 0.3π + δi·π. `weekAlm` is the full GPS week
62
+ * (8-bit SIS week disambiguated against the receiver week).
63
+ * - Galileo (`E`): the SIS ΔsqrtA (relative to √29 600 000 m^1/2) and
64
+ * δi (relative to 56°) are folded in — `sqrtA` and `i0OrDeltaI` are
65
+ * absolute. `weekAlm` is the full GPS-aligned week (2-bit SIS week
66
+ * disambiguated). `health` is the raw SBF bit field: bit 0 set ⇒
67
+ * bits 1-2 are the L1-B HS, bit 3 set ⇒ bits 4-5 E5b HS, bit 6 set
68
+ * ⇒ bits 7-8 E5a HS (0 = healthy).
69
+ * - BeiDou (`C`): δi is relative to 0.3 semicircles for MEO/IGSO and
70
+ * to 0 for GEO satellites (PRN 1-5 and 59-63) per the BDS ICD; the
71
+ * reference is folded in. `weekAlm` is the full BDS week (add 1356
72
+ * for the GPS week) and `toaSec` is BDT seconds of week
73
+ * (BDT = GPST − 14 s). `health` is the 9-bit SIS health word.
74
+ */
75
+ interface SbfKeplerAlmanac {
76
+ system: 'G' | 'E' | 'C';
77
+ /** Satellite PRN, e.g. "G14". */
78
+ prn: string;
79
+ /** Full almanac reference week (see per-system notes above). */
80
+ weekAlm: number;
81
+ /** Almanac reference time of week in seconds (system time scale). */
82
+ toaSec: number;
83
+ /** Square root of semi-major axis in m^(1/2), absolute. */
84
+ sqrtA: number;
85
+ /** Eccentricity (dimensionless). */
86
+ e: number;
87
+ /** Inclination at reference time in rad, absolute (normalized). */
88
+ i0OrDeltaI: number;
89
+ /** Longitude of ascending node at the weekly epoch in rad. */
90
+ omega0: number;
91
+ /** Argument of perigee in rad. */
92
+ omega: number;
93
+ /** Mean anomaly at reference time in rad. */
94
+ m0: number;
95
+ /** Rate of right ascension in rad/s. */
96
+ omegaDot: number;
97
+ /** SV clock bias in seconds. */
98
+ af0: number;
99
+ /** SV clock drift in s/s. */
100
+ af1: number;
101
+ /** Health word, 0 = healthy (see per-system notes above). */
102
+ health: number;
103
+ }
104
+ /**
105
+ * GLONASS almanac (GLONASS ICD orbital parameters). Semicircle fields
106
+ * are converted to radians; everything else keeps the ICD units.
107
+ */
108
+ interface SbfGlonassAlmanac {
109
+ system: 'R';
110
+ /** Satellite slot, e.g. "R05". */
111
+ prn: string;
112
+ /** Frequency channel number HnA (−7…+6). */
113
+ freqNr: number;
114
+ /** Full GPS week of the almanac reference time. */
115
+ weekAlm: number;
116
+ /** Almanac reference time of week in seconds, GPS time frame. */
117
+ toaSec: number;
118
+ /** εnA: orbit eccentricity. */
119
+ epsilon: number;
120
+ /** λnA: longitude of first ascending node in rad. */
121
+ lambda: number;
122
+ /** tλnA: time of first ascending node passage in s of day. */
123
+ tLambda: number;
124
+ /** ΔinA: inclination correction to 63° in rad. */
125
+ deltaI: number;
126
+ /** ωnA: argument of perigee in rad. */
127
+ omega: number;
128
+ /** ΔTnA: correction to the mean Draconian period (s / orbit). */
129
+ deltaT: number;
130
+ /** dΔTnA: rate of change of ΔT (s / orbit²). */
131
+ deltaTDot: number;
132
+ /** τnA: coarse satellite clock correction in s. */
133
+ tau: number;
134
+ /** CnA general health flag: 1 = healthy. */
135
+ health: number;
136
+ /** NA: calendar day number within the 4-year period. */
137
+ nDay: number;
138
+ /** N4: 4-year interval number since 1996. */
139
+ n4: number;
140
+ }
141
+ type SbfAlmanac = SbfKeplerAlmanac | SbfGlonassAlmanac;
142
+ interface SbfAlmanacResult {
143
+ almanacs: SbfAlmanac[];
144
+ /** Frames whose CRC failed (corruption indicator). */
145
+ badCrc: number;
146
+ }
147
+ /**
148
+ * Decode every GPSAlm/GALAlm/GLOAlm/BDSAlm block in an SBF byte
149
+ * stream. Every block is emitted in stream order; other block types
150
+ * are skipped.
151
+ */
152
+ declare function parseSbfAlmanac(data: Uint8Array): SbfAlmanacResult;
153
+
1
154
  /**
2
155
  * Septentrio SBF raw-measurement decoding (MeasEpoch + Meas3) — the
3
156
  * path from a receiver log to RINEX-grade observables.
@@ -23,6 +176,7 @@
23
176
  * Septentrio mosaic-X5 reference guide (the guide documents MeasEpoch
24
177
  * fully; the Meas3 bit layout is only public through that decoder).
25
178
  */
179
+
26
180
  interface SbfMeasurement {
27
181
  /** RINEX PRN, e.g. "G04", "R11", "S23" (SBAS PRN-100). */
28
182
  prn: string;
@@ -60,4 +214,4 @@ interface SbfParseResult {
60
214
  */
61
215
  declare function parseSbfMeas(data: Uint8Array): SbfParseResult;
62
216
 
63
- export { type SbfMeasEpoch, type SbfMeasurement, type SbfParseResult, parseSbfMeas };
217
+ export { type SbfAlmanac, type SbfAlmanacResult, type SbfGlonassAlmanac, type SbfKeplerAlmanac, type SbfMeasEpoch, type SbfMeasurement, type SbfNavResult, type SbfParseResult, parseSbfAlmanac, parseSbfMeas, parseSbfNav, crc16 as sbfCrc16, scanSbfFrames };
package/dist/sbf.d.ts CHANGED
@@ -1,3 +1,156 @@
1
+ import { E as Ephemeris } from './nav-BAI1a9vK.js';
2
+
3
+ /**
4
+ * Septentrio SBF framing shared by the measurement and navigation
5
+ * decoders: 0x24 0x40 sync, CRC16-CCITT (poly 0x1021, init 0) as U2,
6
+ * block ID U2 (number in bits 0..12, revision in 13..15), total length
7
+ * U2 (multiple of 4, includes the 8-byte header). The CRC covers block
8
+ * ID through payload end.
9
+ */
10
+ declare function crc16(data: Uint8Array, start: number, end: number): number;
11
+ /**
12
+ * Scan a byte stream for valid SBF frames and invoke `onBlock` for each
13
+ * one, passing the block number (revision bits stripped), the frame
14
+ * start offset and the total frame length. Frames with bad CRC are
15
+ * counted and a resync continues at the next byte; the count of bad
16
+ * frames is returned.
17
+ */
18
+ declare function scanSbfFrames(data: Uint8Array, view: DataView, onBlock: (id: number, offset: number, len: number) => void): number;
19
+
20
+ /**
21
+ * Septentrio SBF decoded navigation and almanac blocks.
22
+ *
23
+ * Nav blocks (GPSNav 5891, GLONav 4004, GALNav 4002, BDSNav 4081,
24
+ * QZSNav 4095) carry the fully decoded broadcast ephemeris; they are
25
+ * mapped onto the same `Ephemeris` union the RINEX nav parser produces,
26
+ * with identical units and conventions (sqrtA in m^1/2, angles in rad,
27
+ * semicircle fields scaled by π, GLONASS tauN with the RINEX sign, BDS
28
+ * epochs/weeks on the BDT scale like a RINEX file).
29
+ *
30
+ * Almanac blocks (GPSAlm 5892, GALAlm 4003, GLOAlm 4005, BDSAlm 4119)
31
+ * are decoded into dedicated types with all per-system relative fields
32
+ * (GPS/BDS δi, Galileo δi and ΔsqrtA) normalized to absolute values.
33
+ *
34
+ * Ported from RTKLIB demo5 (rtklibexplorer), src/rcv/septentrio.c
35
+ * (decode_gpsnav / decode_glonav / decode_galnav / decode_cmpnav /
36
+ * decode_qzssnav / decode_gpsalm / decode_galalm / decode_cmpalm),
37
+ * BSD-2-Clause, and cross-checked field by field against the Septentrio
38
+ * mosaic-X5 reference guide, which also supplies the GLOAlm layout
39
+ * (not decoded by RTKLIB).
40
+ */
41
+
42
+ interface SbfNavResult {
43
+ ephemerides: Ephemeris[];
44
+ /** Frames whose CRC failed (corruption indicator). */
45
+ badCrc: number;
46
+ }
47
+ /**
48
+ * Decode every GPSNav/GLONav/GALNav/BDSNav/QZSNav block in an SBF byte
49
+ * stream into the RINEX-parser `Ephemeris` union. Every block is
50
+ * emitted (no issue-of-data dedup); other block types are skipped.
51
+ */
52
+ declare function parseSbfNav(data: Uint8Array): SbfNavResult;
53
+ /**
54
+ * GPS / Galileo / BeiDou almanac, normalized to ABSOLUTE Keplerian
55
+ * elements (units matching `KeplerEphemeris`: sqrtA in m^1/2, angles
56
+ * in rad, rates in rad/s) so a Kepler propagator can consume the
57
+ * record directly with all correction terms at zero.
58
+ *
59
+ * Normalizations applied per system:
60
+ * - GPS (`G`): the SIS δi (relative to 0.3 semicircles) is folded in —
61
+ * `i0OrDeltaI` = 0.3π + δi·π. `weekAlm` is the full GPS week
62
+ * (8-bit SIS week disambiguated against the receiver week).
63
+ * - Galileo (`E`): the SIS ΔsqrtA (relative to √29 600 000 m^1/2) and
64
+ * δi (relative to 56°) are folded in — `sqrtA` and `i0OrDeltaI` are
65
+ * absolute. `weekAlm` is the full GPS-aligned week (2-bit SIS week
66
+ * disambiguated). `health` is the raw SBF bit field: bit 0 set ⇒
67
+ * bits 1-2 are the L1-B HS, bit 3 set ⇒ bits 4-5 E5b HS, bit 6 set
68
+ * ⇒ bits 7-8 E5a HS (0 = healthy).
69
+ * - BeiDou (`C`): δi is relative to 0.3 semicircles for MEO/IGSO and
70
+ * to 0 for GEO satellites (PRN 1-5 and 59-63) per the BDS ICD; the
71
+ * reference is folded in. `weekAlm` is the full BDS week (add 1356
72
+ * for the GPS week) and `toaSec` is BDT seconds of week
73
+ * (BDT = GPST − 14 s). `health` is the 9-bit SIS health word.
74
+ */
75
+ interface SbfKeplerAlmanac {
76
+ system: 'G' | 'E' | 'C';
77
+ /** Satellite PRN, e.g. "G14". */
78
+ prn: string;
79
+ /** Full almanac reference week (see per-system notes above). */
80
+ weekAlm: number;
81
+ /** Almanac reference time of week in seconds (system time scale). */
82
+ toaSec: number;
83
+ /** Square root of semi-major axis in m^(1/2), absolute. */
84
+ sqrtA: number;
85
+ /** Eccentricity (dimensionless). */
86
+ e: number;
87
+ /** Inclination at reference time in rad, absolute (normalized). */
88
+ i0OrDeltaI: number;
89
+ /** Longitude of ascending node at the weekly epoch in rad. */
90
+ omega0: number;
91
+ /** Argument of perigee in rad. */
92
+ omega: number;
93
+ /** Mean anomaly at reference time in rad. */
94
+ m0: number;
95
+ /** Rate of right ascension in rad/s. */
96
+ omegaDot: number;
97
+ /** SV clock bias in seconds. */
98
+ af0: number;
99
+ /** SV clock drift in s/s. */
100
+ af1: number;
101
+ /** Health word, 0 = healthy (see per-system notes above). */
102
+ health: number;
103
+ }
104
+ /**
105
+ * GLONASS almanac (GLONASS ICD orbital parameters). Semicircle fields
106
+ * are converted to radians; everything else keeps the ICD units.
107
+ */
108
+ interface SbfGlonassAlmanac {
109
+ system: 'R';
110
+ /** Satellite slot, e.g. "R05". */
111
+ prn: string;
112
+ /** Frequency channel number HnA (−7…+6). */
113
+ freqNr: number;
114
+ /** Full GPS week of the almanac reference time. */
115
+ weekAlm: number;
116
+ /** Almanac reference time of week in seconds, GPS time frame. */
117
+ toaSec: number;
118
+ /** εnA: orbit eccentricity. */
119
+ epsilon: number;
120
+ /** λnA: longitude of first ascending node in rad. */
121
+ lambda: number;
122
+ /** tλnA: time of first ascending node passage in s of day. */
123
+ tLambda: number;
124
+ /** ΔinA: inclination correction to 63° in rad. */
125
+ deltaI: number;
126
+ /** ωnA: argument of perigee in rad. */
127
+ omega: number;
128
+ /** ΔTnA: correction to the mean Draconian period (s / orbit). */
129
+ deltaT: number;
130
+ /** dΔTnA: rate of change of ΔT (s / orbit²). */
131
+ deltaTDot: number;
132
+ /** τnA: coarse satellite clock correction in s. */
133
+ tau: number;
134
+ /** CnA general health flag: 1 = healthy. */
135
+ health: number;
136
+ /** NA: calendar day number within the 4-year period. */
137
+ nDay: number;
138
+ /** N4: 4-year interval number since 1996. */
139
+ n4: number;
140
+ }
141
+ type SbfAlmanac = SbfKeplerAlmanac | SbfGlonassAlmanac;
142
+ interface SbfAlmanacResult {
143
+ almanacs: SbfAlmanac[];
144
+ /** Frames whose CRC failed (corruption indicator). */
145
+ badCrc: number;
146
+ }
147
+ /**
148
+ * Decode every GPSAlm/GALAlm/GLOAlm/BDSAlm block in an SBF byte
149
+ * stream. Every block is emitted in stream order; other block types
150
+ * are skipped.
151
+ */
152
+ declare function parseSbfAlmanac(data: Uint8Array): SbfAlmanacResult;
153
+
1
154
  /**
2
155
  * Septentrio SBF raw-measurement decoding (MeasEpoch + Meas3) — the
3
156
  * path from a receiver log to RINEX-grade observables.
@@ -23,6 +176,7 @@
23
176
  * Septentrio mosaic-X5 reference guide (the guide documents MeasEpoch
24
177
  * fully; the Meas3 bit layout is only public through that decoder).
25
178
  */
179
+
26
180
  interface SbfMeasurement {
27
181
  /** RINEX PRN, e.g. "G04", "R11", "S23" (SBAS PRN-100). */
28
182
  prn: string;
@@ -60,4 +214,4 @@ interface SbfParseResult {
60
214
  */
61
215
  declare function parseSbfMeas(data: Uint8Array): SbfParseResult;
62
216
 
63
- export { type SbfMeasEpoch, type SbfMeasurement, type SbfParseResult, parseSbfMeas };
217
+ export { type SbfAlmanac, type SbfAlmanacResult, type SbfGlonassAlmanac, type SbfKeplerAlmanac, type SbfMeasEpoch, type SbfMeasurement, type SbfNavResult, type SbfParseResult, parseSbfAlmanac, parseSbfMeas, parseSbfNav, crc16 as sbfCrc16, scanSbfFrames };
package/dist/sbf.js CHANGED
@@ -1,6 +1,16 @@
1
1
  import {
2
- parseSbfMeas
3
- } from "./chunk-YIHO74OS.js";
2
+ crc16,
3
+ parseSbfAlmanac,
4
+ parseSbfMeas,
5
+ parseSbfNav,
6
+ scanSbfFrames
7
+ } from "./chunk-YPOVPXNK.js";
8
+ import "./chunk-HVXYFUCB.js";
9
+ import "./chunk-LEEU5OIO.js";
4
10
  export {
5
- parseSbfMeas
11
+ parseSbfAlmanac,
12
+ parseSbfMeas,
13
+ parseSbfNav,
14
+ crc16 as sbfCrc16,
15
+ scanSbfFrames
6
16
  };
package/dist/ubx.cjs CHANGED
@@ -20,10 +20,261 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/ubx/index.ts
21
21
  var ubx_exports = {};
22
22
  __export(ubx_exports, {
23
- parseUbxRawx: () => parseUbxRawx
23
+ parseUbxNav: () => parseUbxNav,
24
+ parseUbxRawx: () => parseUbxRawx,
25
+ ubxFrames: () => ubxFrames
24
26
  });
25
27
  module.exports = __toCommonJS(ubx_exports);
28
+
29
+ // src/ubx/frame.ts
30
+ function* ubxFrames(data, stats = { badChecksums: 0 }) {
31
+ let i = 0;
32
+ while (i + 8 <= data.length) {
33
+ if (data[i] !== 181 || data[i + 1] !== 98) {
34
+ i++;
35
+ continue;
36
+ }
37
+ const len = data[i + 4] | data[i + 5] << 8;
38
+ const end = i + 6 + len + 2;
39
+ if (end > data.length) break;
40
+ let ckA = 0;
41
+ let ckB = 0;
42
+ for (let j = i + 2; j < i + 6 + len; j++) {
43
+ ckA = ckA + data[j] & 255;
44
+ ckB = ckB + ckA & 255;
45
+ }
46
+ if (ckA !== data[i + 6 + len] || ckB !== data[i + 7 + len]) {
47
+ stats.badChecksums++;
48
+ i++;
49
+ continue;
50
+ }
51
+ yield {
52
+ msgClass: data[i + 2],
53
+ msgId: data[i + 3],
54
+ payload: data.subarray(i + 6, i + 6 + len),
55
+ payloadStart: i + 6
56
+ };
57
+ i = end;
58
+ }
59
+ }
60
+
61
+ // src/navbits/index.ts
62
+ var GPS_PI = 3.1415926535898;
26
63
  var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
64
+ var SEC_PER_WEEK = 7 * 86400;
65
+ function getBitU(buff, pos, len) {
66
+ let bits = 0;
67
+ for (let i = pos; i < pos + len; i++) {
68
+ bits = bits * 2 + (buff[i >> 3] >> 7 - (i & 7) & 1);
69
+ }
70
+ return bits;
71
+ }
72
+ function getBitS(buff, pos, len) {
73
+ const bits = getBitU(buff, pos, len);
74
+ if (len <= 0 || bits < 2 ** (len - 1)) return bits;
75
+ return bits - 2 ** len;
76
+ }
77
+ function setBitU(buff, pos, len, data) {
78
+ let mask = 2 ** (len - 1);
79
+ for (let i = pos; i < pos + len; i++, mask /= 2) {
80
+ if (data >= mask) {
81
+ buff[i >> 3] |= 1 << 7 - (i & 7);
82
+ data -= mask;
83
+ } else {
84
+ buff[i >> 3] &= ~(1 << 7 - (i & 7));
85
+ }
86
+ }
87
+ }
88
+ function decodeGpsLnavFrame(subframes, opts = {}) {
89
+ if (subframes.length < 90) return null;
90
+ const b = subframes;
91
+ let i = 24;
92
+ const tow1 = getBitU(b, i, 17) * 6;
93
+ i += 17 + 2;
94
+ const id1 = getBitU(b, i, 3);
95
+ i += 3 + 2;
96
+ const week10 = getBitU(b, i, 10);
97
+ i += 10;
98
+ i += 2;
99
+ i += 4;
100
+ const svHealth = getBitU(b, i, 6);
101
+ i += 6;
102
+ const iodc0 = getBitU(b, i, 2);
103
+ i += 2;
104
+ i += 1 + 87;
105
+ const tgdRaw = getBitS(b, i, 8);
106
+ i += 8;
107
+ const iodc1 = getBitU(b, i, 8);
108
+ i += 8;
109
+ const tocSec = getBitU(b, i, 16) * 16;
110
+ i += 16;
111
+ const af2 = getBitS(b, i, 8) * 2 ** -55;
112
+ i += 8;
113
+ const af1 = getBitS(b, i, 16) * 2 ** -43;
114
+ i += 16;
115
+ const af0 = getBitS(b, i, 22) * 2 ** -31;
116
+ i = 240 + 24;
117
+ i += 17 + 2;
118
+ const id2 = getBitU(b, i, 3);
119
+ i += 3 + 2;
120
+ const iode = getBitU(b, i, 8);
121
+ i += 8;
122
+ const crs = getBitS(b, i, 16) * 2 ** -5;
123
+ i += 16;
124
+ const deltaN = getBitS(b, i, 16) * 2 ** -43 * GPS_PI;
125
+ i += 16;
126
+ const m0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
127
+ i += 32;
128
+ const cuc = getBitS(b, i, 16) * 2 ** -29;
129
+ i += 16;
130
+ const e = getBitU(b, i, 32) * 2 ** -33;
131
+ i += 32;
132
+ const cus = getBitS(b, i, 16) * 2 ** -29;
133
+ i += 16;
134
+ const sqrtA = getBitU(b, i, 32) * 2 ** -19;
135
+ i += 32;
136
+ const toes = getBitU(b, i, 16) * 16;
137
+ i = 480 + 24;
138
+ i += 17 + 2;
139
+ const id3 = getBitU(b, i, 3);
140
+ i += 3 + 2;
141
+ const cic = getBitS(b, i, 16) * 2 ** -29;
142
+ i += 16;
143
+ const omega0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
144
+ i += 32;
145
+ const cis = getBitS(b, i, 16) * 2 ** -29;
146
+ i += 16;
147
+ const i0 = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
148
+ i += 32;
149
+ const crc = getBitS(b, i, 16) * 2 ** -5;
150
+ i += 16;
151
+ const omega = getBitS(b, i, 32) * 2 ** -31 * GPS_PI;
152
+ i += 32;
153
+ const omegaDot = getBitS(b, i, 24) * 2 ** -43 * GPS_PI;
154
+ i += 24;
155
+ const iode3 = getBitU(b, i, 8);
156
+ i += 8;
157
+ const idot = getBitS(b, i, 14) * 2 ** -43 * GPS_PI;
158
+ const iodc = iodc0 * 256 + iodc1;
159
+ if (id1 !== 1 || id2 !== 2 || id3 !== 3) return null;
160
+ if (iode3 !== iode || iode !== (iodc & 255)) return null;
161
+ const ref = opts.refWeek ?? Math.floor((Date.now() - GPS_EPOCH_MS) / 1e3 / SEC_PER_WEEK);
162
+ let week = week10 + 1024 * Math.round((ref - week10) / 1024);
163
+ if (toes < tow1 - 302400) week++;
164
+ else if (toes > tow1 + 302400) week--;
165
+ const prn = opts.prn ?? "G00";
166
+ const tocDate = new Date(
167
+ GPS_EPOCH_MS + (week * SEC_PER_WEEK + tocSec) * 1e3
168
+ );
169
+ return {
170
+ system: prn[0] === "J" ? "J" : "G",
171
+ prn,
172
+ tocDate,
173
+ // Same seconds-of-week convention as parseNavFile (rinex/nav.ts).
174
+ toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK,
175
+ af0,
176
+ af1,
177
+ af2,
178
+ iode,
179
+ crs,
180
+ deltaN,
181
+ m0,
182
+ cuc,
183
+ e,
184
+ cus,
185
+ sqrtA,
186
+ toe: toes,
187
+ cic,
188
+ omega0,
189
+ cis,
190
+ i0,
191
+ crc,
192
+ omega,
193
+ omegaDot,
194
+ idot,
195
+ week,
196
+ svHealth,
197
+ tgd: tgdRaw === -128 ? 0 : tgdRaw * 2 ** -31
198
+ // IS-GPS-200: -128 reserved
199
+ };
200
+ }
201
+
202
+ // src/ubx/nav.ts
203
+ var PREAMB_CNAV = 139;
204
+ function parseUbxNav(data, opts = {}) {
205
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
206
+ const ephemerides = [];
207
+ let badParity = 0;
208
+ let refWeek = opts.refWeek;
209
+ if (refWeek === void 0) {
210
+ for (const f of ubxFrames(data)) {
211
+ if (f.msgClass === 2 && f.msgId === 21 && f.payload.length >= 16) {
212
+ const week = view.getUint16(f.payloadStart + 8, true);
213
+ if (week > 0) {
214
+ refWeek = week;
215
+ break;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ if (refWeek === void 0) return { ephemerides, badParity };
221
+ const subframes = /* @__PURE__ */ new Map();
222
+ const last = /* @__PURE__ */ new Map();
223
+ for (const f of ubxFrames(data)) {
224
+ if (f.msgClass !== 2 || f.msgId !== 19) continue;
225
+ const p = f.payload;
226
+ if (p.length < 8) continue;
227
+ const gnssId = p[0];
228
+ const svId = p[1];
229
+ let prn;
230
+ if (gnssId === 0 && svId >= 1 && svId <= 32) {
231
+ prn = `G${String(svId).padStart(2, "0")}`;
232
+ } else if (gnssId === 5 && svId >= 1 && svId <= 10) {
233
+ if (p.length === 44) continue;
234
+ prn = `J${String(svId).padStart(2, "0")}`;
235
+ } else {
236
+ continue;
237
+ }
238
+ if (p.length < 8 + 40) continue;
239
+ const base = f.payloadStart + 8;
240
+ if (view.getUint32(base, true) >>> 24 === PREAMB_CNAV) continue;
241
+ const buff = new Uint8Array(30);
242
+ for (let k = 0; k < 10; k++) {
243
+ const dwrd = view.getUint32(base + 4 * k, true);
244
+ setBitU(buff, 24 * k, 24, dwrd >>> 6 & 16777215);
245
+ }
246
+ const id = getBitU(buff, 43, 3);
247
+ if (id < 1 || id > 5) {
248
+ badParity++;
249
+ continue;
250
+ }
251
+ if (id > 3) continue;
252
+ const key = gnssId * 256 + svId;
253
+ let sf = subframes.get(key);
254
+ if (!sf) {
255
+ sf = { buf: new Uint8Array(90), have: 0 };
256
+ subframes.set(key, sf);
257
+ }
258
+ sf.buf.set(buff, (id - 1) * 30);
259
+ sf.have |= 1 << id - 1;
260
+ if (id !== 3 || sf.have !== 7) continue;
261
+ const eph = decodeGpsLnavFrame(sf.buf, { prn, refWeek });
262
+ if (!eph) {
263
+ badParity++;
264
+ continue;
265
+ }
266
+ const prev = last.get(prn);
267
+ if (prev && prev.iode === eph.iode && prev.tocDate.getTime() === eph.tocDate.getTime()) {
268
+ continue;
269
+ }
270
+ last.set(prn, eph);
271
+ ephemerides.push(eph);
272
+ }
273
+ return { ephemerides, badParity };
274
+ }
275
+
276
+ // src/ubx/index.ts
277
+ var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
27
278
  var MS_PER_WEEK = 7 * 864e5;
28
279
  var SIGNALS = {
29
280
  0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
@@ -63,33 +314,14 @@ function parseUbxRawx(data) {
63
314
  const epochs = [];
64
315
  const messageCounts = {};
65
316
  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
- }
317
+ const stats = { badChecksums: 0 };
318
+ for (const frame of ubxFrames(data, stats)) {
319
+ const { msgClass: cls, msgId: id } = frame;
320
+ const len = frame.payload.length;
89
321
  const key = `${cls.toString(16).padStart(2, "0")}-${id.toString(16).padStart(2, "0")}`;
90
322
  messageCounts[key] = (messageCounts[key] ?? 0) + 1;
91
323
  if (cls === 2 && id === 21 && len >= 16) {
92
- const p = i + 6;
324
+ const p = frame.payloadStart;
93
325
  const rcvTow = view.getFloat64(p, true);
94
326
  const week = view.getUint16(p + 8, true);
95
327
  const leapS = view.getInt8(p + 10);
@@ -125,16 +357,17 @@ function parseUbxRawx(data) {
125
357
  if (!codes.includes(sig[1])) codes.push(sig[1]);
126
358
  }
127
359
  epochs.push({
128
- timeMs: GPS_EPOCH_MS + week * MS_PER_WEEK + Math.round(rcvTow * 1e3),
360
+ timeMs: GPS_EPOCH_MS2 + week * MS_PER_WEEK + Math.round(rcvTow * 1e3),
129
361
  leapS: leapValid ? leapS : null,
130
362
  meas
131
363
  });
132
364
  }
133
- i = end;
134
365
  }
135
- return { epochs, messageCounts, obsCodes, badChecksums };
366
+ return { epochs, messageCounts, obsCodes, badChecksums: stats.badChecksums };
136
367
  }
137
368
  // Annotate the CommonJS export names for ESM import in node:
138
369
  0 && (module.exports = {
139
- parseUbxRawx
370
+ parseUbxNav,
371
+ parseUbxRawx,
372
+ ubxFrames
140
373
  });