gnss-js 1.41.0 → 1.43.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.
@@ -55,6 +55,7 @@ var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
55
55
  var SUB_GPS_EPHEMERIS = 1;
56
56
  var SUB_ION_UTC = 3;
57
57
  var SUB_GLO_EPHEMERIS = 9;
58
+ var SUB_GAL_EPHEMERIS = 11;
58
59
  var SUB_BDS_EPHEMERIS = 21;
59
60
  function readKepler(view, base) {
60
61
  return {
@@ -147,6 +148,46 @@ function decodeBdsEphemeris(view, base) {
147
148
  ...keplerCommon(r)
148
149
  };
149
150
  }
151
+ function decodeGalEphemeris(view, base) {
152
+ const svid = view.getUint8(base + 5);
153
+ if (svid < 1 || svid > 36) return null;
154
+ const g = (o) => view.getFloat64(base + o, false);
155
+ const week = view.getUint16(base + 7, false);
156
+ const toe = view.getUint32(base + 9, false);
157
+ const tocMs = gpsMs(week, toe);
158
+ return {
159
+ system: "E",
160
+ prn: `E${String(svid).padStart(2, "0")}`,
161
+ toc: sowOf(tocMs),
162
+ tocDate: new Date(tocMs),
163
+ toe,
164
+ week,
165
+ iode: view.getUint16(base + 13, false),
166
+ // IODnav
167
+ af0: g(146),
168
+ af1: g(154),
169
+ af2: g(162),
170
+ crs: g(19),
171
+ deltaN: g(27) * GPS_PI,
172
+ m0: g(35) * GPS_PI,
173
+ cuc: g(43) * GPS_PI,
174
+ e: g(51),
175
+ cus: g(59) * GPS_PI,
176
+ sqrtA: g(67),
177
+ cic: g(75) * GPS_PI,
178
+ omega0: g(83) * GPS_PI,
179
+ cis: g(91) * GPS_PI,
180
+ i0: g(99) * GPS_PI,
181
+ crc: g(107),
182
+ omega: g(115) * GPS_PI,
183
+ omegaDot: g(123) * GPS_PI,
184
+ idot: g(131) * GPS_PI,
185
+ svHealth: 0,
186
+ // Galileo SISA/health word not located; observed sats healthy
187
+ tgd: g(179)
188
+ // BGD E5a/E1 (the RINEX Galileo tgd slot)
189
+ };
190
+ }
150
191
  function decodeGloEphemeris(view, base) {
151
192
  const slot = view.getUint8(base + 5);
152
193
  if (slot < 1 || slot > 24) return null;
@@ -197,6 +238,12 @@ function parseTrimbleNav(data) {
197
238
  if (lastIode.get(eph.prn) === eph.iode) continue;
198
239
  lastIode.set(eph.prn, eph.iode);
199
240
  ephemerides.push(eph);
241
+ } else if (sub === SUB_GAL_EPHEMERIS && f.len >= 183) {
242
+ const eph = decodeGalEphemeris(view, f.start);
243
+ if (!eph) continue;
244
+ if (lastIode.get(eph.prn) === eph.iode) continue;
245
+ lastIode.set(eph.prn, eph.iode);
246
+ ephemerides.push(eph);
200
247
  } else if (sub === SUB_ION_UTC && f.len >= 102) {
201
248
  const p = f.start;
202
249
  const alpha = [];
@@ -4,10 +4,12 @@ import {
4
4
  GalInavAssembler,
5
5
  GloStringAssembler,
6
6
  bdsSubframeParityOk,
7
+ decodeSbasGeoNav,
7
8
  galFnavPageCrcOk,
8
9
  galInavPageCrcOk,
10
+ sbasCrcOk,
9
11
  testGloString
10
- } from "./chunk-FA2UTLEQ.js";
12
+ } from "./chunk-QV3XGDUU.js";
11
13
  import {
12
14
  geodeticToEcef
13
15
  } from "./chunk-37QNKGTC.js";
@@ -700,6 +702,49 @@ function parseSbfGloNav(data) {
700
702
  return { ephemerides, badCrc, messages };
701
703
  }
702
704
 
705
+ // src/sbf/rawnav-sbas.ts
706
+ function feedGeoBlock(view, b) {
707
+ const tow = view.getUint32(b + 8, true);
708
+ const wnc = view.getUint16(b + 12, true);
709
+ const svid = view.getUint8(b + 14);
710
+ const prnStr = svidToPrn(svid);
711
+ if (!prnStr || prnStr[0] !== "S") return {};
712
+ const msg = new Uint8Array(32);
713
+ for (let k = 0; k < 8; k++) {
714
+ const w = view.getUint32(b + 20 + 4 * k, true);
715
+ msg[4 * k] = w >>> 24;
716
+ msg[4 * k + 1] = w >>> 16 & 255;
717
+ msg[4 * k + 2] = w >>> 8 & 255;
718
+ msg[4 * k + 3] = w & 255;
719
+ }
720
+ if (!sbasCrcOk(msg)) return { badCrc: true };
721
+ const prn = Number(prnStr.slice(1)) + 100;
722
+ const eph = decodeSbasGeoNav(msg, prn, wnc, tow / 1e3);
723
+ return eph ? { eph } : {};
724
+ }
725
+ function parseSbfGeoNav(data) {
726
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
727
+ const ephemerides = [];
728
+ const seen = /* @__PURE__ */ new Set();
729
+ let badCrc = 0;
730
+ let messages = 0;
731
+ scanSbfFrames(data, view, (id, b, len) => {
732
+ if (id !== 4020 || len < 52) return;
733
+ messages++;
734
+ const r = feedGeoBlock(view, b);
735
+ if (r.eph) {
736
+ const key = `${r.eph.prn}|${r.eph.tocDate.getTime()}`;
737
+ if (!seen.has(key)) {
738
+ seen.add(key);
739
+ ephemerides.push(r.eph);
740
+ }
741
+ } else if (r.badCrc) {
742
+ badCrc++;
743
+ }
744
+ });
745
+ return { ephemerides, badCrc, messages };
746
+ }
747
+
703
748
  // src/sbf/navigation.ts
704
749
  var F4_DNU4 = -2e10;
705
750
  function ephKey(e) {
@@ -720,6 +765,7 @@ function decodeSbfNavigation(data) {
720
765
  galRaw: 0,
721
766
  gloRaw: 0,
722
767
  bdsRaw: 0,
768
+ sbasRaw: 0,
723
769
  badFrames: 0
724
770
  };
725
771
  const cnavAsm = newCnavAssemblers();
@@ -823,6 +869,14 @@ function decodeSbfNavigation(data) {
823
869
  else if (r.badCrc) counts.badFrames++;
824
870
  return;
825
871
  }
872
+ case 4020: {
873
+ if (len < 52) return;
874
+ counts.sbasRaw++;
875
+ const r = feedGeoBlock(view, b);
876
+ if (r.eph) addEph(r.eph);
877
+ else if (r.badCrc) counts.badFrames++;
878
+ return;
879
+ }
826
880
  /* ---- iono / UTC ---- */
827
881
  case 5893:
828
882
  // GPSIon
@@ -2053,6 +2107,8 @@ export {
2053
2107
  parseSbfGalNav,
2054
2108
  parseSbfBdsNav,
2055
2109
  parseSbfGloNav,
2110
+ feedGeoBlock,
2111
+ parseSbfGeoNav,
2056
2112
  decodeSbfNavigation,
2057
2113
  parseSbfHas,
2058
2114
  parseSbfMeas
@@ -759,6 +759,49 @@ var GloStringAssembler = class {
759
759
  }
760
760
  };
761
761
 
762
+ // src/navbits/sbas.ts
763
+ var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
764
+ var MS_PER_WEEK2 = 7 * 86400 * 1e3;
765
+ var two2 = (n) => String(n).padStart(2, "0");
766
+ var SBAS_MT_GEO_NAV = 9;
767
+ function sbasMessageType(msg) {
768
+ return getBitU(msg, 8, 6);
769
+ }
770
+ function sbasCrcOk(msg) {
771
+ if (msg.length < 32) return false;
772
+ return crc24q(msg, 226) === getBitU(msg, 226, 24);
773
+ }
774
+ function decodeSbasGeoNav(msg, prn, week, tow) {
775
+ if (prn < 120 || prn > 158) return null;
776
+ if (sbasMessageType(msg) !== SBAS_MT_GEO_NAV) return null;
777
+ let t = getBitU(msg, 22, 13) * 16 - tow % 86400;
778
+ if (t <= -43200) t += 86400;
779
+ else if (t > 43200) t -= 86400;
780
+ const t0Sec = tow + t;
781
+ const ura = getBitU(msg, 35, 4);
782
+ return {
783
+ system: "S",
784
+ prn: `S${two2(prn - 100)}`,
785
+ tocDate: new Date(GPS_EPOCH_MS3 + week * MS_PER_WEEK2 + t0Sec * 1e3),
786
+ tauN: getBitS(msg, 206, 12) * 2 ** -31,
787
+ // a_Gf0, clock offset (s)
788
+ gammaN: getBitS(msg, 218, 8) * 2 ** -39 / 2,
789
+ // a_Gf1, clock drift (s/s)
790
+ messageFrameTime: (tow % 86400 + 86400) % 86400,
791
+ x: getBitS(msg, 39, 30) * 0.08 / 1e3,
792
+ y: getBitS(msg, 69, 30) * 0.08 / 1e3,
793
+ z: getBitS(msg, 99, 25) * 0.4 / 1e3,
794
+ xDot: getBitS(msg, 124, 17) * 625e-6 / 1e3,
795
+ yDot: getBitS(msg, 141, 17) * 625e-6 / 1e3,
796
+ zDot: getBitS(msg, 158, 18) * 4e-3 / 1e3,
797
+ xAcc: getBitS(msg, 176, 10) * 125e-7 / 1e3,
798
+ yAcc: getBitS(msg, 186, 10) * 125e-7 / 1e3,
799
+ zAcc: getBitS(msg, 196, 10) * 625e-7 / 1e3,
800
+ health: ura === 15 ? 1 : 0,
801
+ freqNum: 0
802
+ };
803
+ }
804
+
762
805
  export {
763
806
  bdsSubframeParityOk,
764
807
  BdsAssembler,
@@ -767,5 +810,7 @@ export {
767
810
  GalInavAssembler,
768
811
  GalFnavAssembler,
769
812
  testGloString,
770
- GloStringAssembler
813
+ GloStringAssembler,
814
+ sbasCrcOk,
815
+ decodeSbasGeoNav
771
816
  };
@@ -3,9 +3,11 @@ import {
3
3
  GalInavAssembler,
4
4
  GloStringAssembler,
5
5
  bdsSubframeParityOk,
6
+ decodeSbasGeoNav,
6
7
  galInavPageCrcOk,
8
+ sbasCrcOk,
7
9
  testGloString
8
- } from "./chunk-FA2UTLEQ.js";
10
+ } from "./chunk-QV3XGDUU.js";
9
11
  import {
10
12
  CnavAssembler,
11
13
  cnavCrcOk
@@ -178,12 +180,15 @@ var two = (n) => String(n).padStart(2, "0");
178
180
  function parseUbxRawNav(data, opts = {}) {
179
181
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
180
182
  const ephemerides = [];
181
- const counts = { gal: 0, bds: 0, glo: 0 };
183
+ const counts = { gal: 0, bds: 0, glo: 0, sbas: 0 };
182
184
  let badParity = 0;
183
185
  const inav = new GalInavAssembler();
184
186
  const bds = new BdsAssembler();
185
187
  const glo = new GloStringAssembler();
186
188
  let refDate = null;
189
+ let refWeek = 0;
190
+ let refTow = 0;
191
+ const sbasSeen = /* @__PURE__ */ new Set();
187
192
  for (const f of ubxFrames(data)) {
188
193
  if (f.msgClass !== 2) continue;
189
194
  if (f.msgId === 21 && f.payload.length >= 16) {
@@ -194,6 +199,8 @@ function parseUbxRawNav(data, opts = {}) {
194
199
  refDate = new Date(
195
200
  GPS_EPOCH_MS + (week * SEC_PER_WEEK + rcvTow) * 1e3
196
201
  );
202
+ refWeek = week;
203
+ refTow = rcvTow;
197
204
  }
198
205
  continue;
199
206
  }
@@ -266,6 +273,31 @@ function parseUbxRawNav(data, opts = {}) {
266
273
  if (!refDate) continue;
267
274
  const eph = glo.push(`R${two(svId)}`, str, refDate, p[3] - 7);
268
275
  if (eph) ephemerides.push(eph);
276
+ } else if (gnssId === 1) {
277
+ if (p.length < 8 + 32) continue;
278
+ counts.sbas++;
279
+ if (svId < 120 || svId > 158) continue;
280
+ const msg = new Uint8Array(32);
281
+ for (let k = 0; k < 8; k++) {
282
+ const w = view.getUint32(base + 4 * k, true);
283
+ msg[4 * k] = w >>> 24;
284
+ msg[4 * k + 1] = w >>> 16 & 255;
285
+ msg[4 * k + 2] = w >>> 8 & 255;
286
+ msg[4 * k + 3] = w & 255;
287
+ }
288
+ if (!sbasCrcOk(msg)) {
289
+ badParity++;
290
+ continue;
291
+ }
292
+ if (refWeek === 0) continue;
293
+ const eph = decodeSbasGeoNav(msg, svId, refWeek, refTow);
294
+ if (eph) {
295
+ const key = `${eph.prn}@${eph.tocDate.getTime()}`;
296
+ if (!sbasSeen.has(key)) {
297
+ sbasSeen.add(key);
298
+ ephemerides.push(eph);
299
+ }
300
+ }
269
301
  }
270
302
  }
271
303
  return { ephemerides, counts, badParity };