gnss-js 1.42.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.
package/dist/index.d.cts CHANGED
@@ -11,7 +11,7 @@ export { C as ClkFile, I as IonexGrid, S as Sp3File, a as Sp3Sample, c as clkBia
11
11
  export { B as BitReader, E as EphemerisInfo, R as Rtcm3DebugHandler, a as Rtcm3Decoder, b as Rtcm3Frame, d as decodeEphemeris, r as readString, c as reportDecodeError, s as setRtcm3DebugHandler } from './ephemeris-Ohl6NAfw.cjs';
12
12
  export { MessageTypeStats, MsmEpoch, MsmSatObs, MsmSignal, RTCM3_MESSAGE_NAMES, SatCn0, SignalCn0, StationMeta, StreamStats, createStationMeta, createStreamStats, decodeMsmFull, msmEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats } from './rtcm3.cjs';
13
13
  export { UbxCnavResult, UbxFrame, UbxIonoUtcResult, UbxMeasurement, UbxNavOptions, UbxNavResult, UbxParseResult, UbxRawNavOptions, UbxRawNavResult, UbxRawxEpoch, parseUbxCnav, parseUbxIonoUtc, parseUbxNav, parseUbxRawNav, parseUbxRawx, ubxFrames } from './ubx.cjs';
14
- export { HasMessage, SbfBdsNavResult, SbfCnavEphemeris, SbfCnavResult, SbfGalEphemeris, SbfGalNavResult, SbfGloNavResult, SbfGpsNavResult, SbfHasMessage, SbfHasResult, SbfIonoUtcResult, SbfMeasEpoch, SbfMeasurement, SbfNavCounts, SbfNavigation, SbfParseResult, SbfReceiverSetup, decodeSbfNavigation, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfReceiverSetup, sbfCrc16, scanSbfFrames } from './sbf.cjs';
14
+ export { HasMessage, SbfBdsNavResult, SbfCnavEphemeris, SbfCnavResult, SbfGalEphemeris, SbfGalNavResult, SbfGeoNavResult, SbfGloNavResult, SbfGpsNavResult, SbfHasMessage, SbfHasResult, SbfIonoUtcResult, SbfMeasEpoch, SbfMeasurement, SbfNavCounts, SbfNavigation, SbfParseResult, SbfReceiverSetup, decodeSbfNavigation, feedGeoBlock, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGeoNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfReceiverSetup, sbfCrc16, scanSbfFrames } from './sbf.cjs';
15
15
  export { NovatelEpoch, NovatelMeasurement, NovatelNavResult, NovatelParseResult, OEM4_HLEN, Oem4Frame, crc32, oem4Frames, parseNovatelNav, parseNovatelRange } from './novatel.cjs';
16
16
  export { TrimbleEpoch, TrimbleFrame, TrimbleMeasurement, TrimbleNavResult, TrimbleParseResult, parseTrimble, parseTrimbleNav, trimbleFrames } from './trimble.cjs';
17
17
  export { BinexEpoch, BinexMeasurement, BinexParseResult, BinexRecord, binexCrc16, binexCrc32, binexCsum8, binexRecords, decodeBinexEph, getBnxi, parseBinex } from './binex.cjs';
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export { C as ClkFile, I as IonexGrid, S as Sp3File, a as Sp3Sample, c as clkBia
11
11
  export { B as BitReader, E as EphemerisInfo, R as Rtcm3DebugHandler, a as Rtcm3Decoder, b as Rtcm3Frame, d as decodeEphemeris, r as readString, c as reportDecodeError, s as setRtcm3DebugHandler } from './ephemeris-Ohl6NAfw.js';
12
12
  export { MessageTypeStats, MsmEpoch, MsmSatObs, MsmSignal, RTCM3_MESSAGE_NAMES, SatCn0, SignalCn0, StationMeta, StreamStats, createStationMeta, createStreamStats, decodeMsmFull, msmEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats } from './rtcm3.js';
13
13
  export { UbxCnavResult, UbxFrame, UbxIonoUtcResult, UbxMeasurement, UbxNavOptions, UbxNavResult, UbxParseResult, UbxRawNavOptions, UbxRawNavResult, UbxRawxEpoch, parseUbxCnav, parseUbxIonoUtc, parseUbxNav, parseUbxRawNav, parseUbxRawx, ubxFrames } from './ubx.js';
14
- export { HasMessage, SbfBdsNavResult, SbfCnavEphemeris, SbfCnavResult, SbfGalEphemeris, SbfGalNavResult, SbfGloNavResult, SbfGpsNavResult, SbfHasMessage, SbfHasResult, SbfIonoUtcResult, SbfMeasEpoch, SbfMeasurement, SbfNavCounts, SbfNavigation, SbfParseResult, SbfReceiverSetup, decodeSbfNavigation, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfReceiverSetup, sbfCrc16, scanSbfFrames } from './sbf.js';
14
+ export { HasMessage, SbfBdsNavResult, SbfCnavEphemeris, SbfCnavResult, SbfGalEphemeris, SbfGalNavResult, SbfGeoNavResult, SbfGloNavResult, SbfGpsNavResult, SbfHasMessage, SbfHasResult, SbfIonoUtcResult, SbfMeasEpoch, SbfMeasurement, SbfNavCounts, SbfNavigation, SbfParseResult, SbfReceiverSetup, decodeSbfNavigation, feedGeoBlock, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGeoNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfReceiverSetup, sbfCrc16, scanSbfFrames } from './sbf.js';
15
15
  export { NovatelEpoch, NovatelMeasurement, NovatelNavResult, NovatelParseResult, OEM4_HLEN, Oem4Frame, crc32, oem4Frames, parseNovatelNav, parseNovatelRange } from './novatel.js';
16
16
  export { TrimbleEpoch, TrimbleFrame, TrimbleMeasurement, TrimbleNavResult, TrimbleParseResult, parseTrimble, parseTrimbleNav, trimbleFrames } from './trimble.js';
17
17
  export { BinexEpoch, BinexMeasurement, BinexParseResult, BinexRecord, binexCrc16, binexCrc32, binexCsum8, binexRecords, decodeBinexEph, getBnxi, parseBinex } from './binex.js';
package/dist/index.js CHANGED
@@ -50,14 +50,16 @@ import {
50
50
  parseUbxRawNav,
51
51
  parseUbxRawx,
52
52
  ubxFrames
53
- } from "./chunk-ZQDLYLQA.js";
53
+ } from "./chunk-Z5VLTH33.js";
54
54
  import {
55
55
  crc16,
56
56
  decodeSbfNavigation,
57
+ feedGeoBlock,
57
58
  parseSbfAlmanac,
58
59
  parseSbfBdsNav,
59
60
  parseSbfCnav,
60
61
  parseSbfGalNav,
62
+ parseSbfGeoNav,
61
63
  parseSbfGloNav,
62
64
  parseSbfGpsNav,
63
65
  parseSbfHas,
@@ -66,8 +68,8 @@ import {
66
68
  parseSbfNav,
67
69
  parseSbfReceiverSetup,
68
70
  scanSbfFrames
69
- } from "./chunk-4TAN27LO.js";
70
- import "./chunk-FA2UTLEQ.js";
71
+ } from "./chunk-6EZUM2MB.js";
72
+ import "./chunk-QV3XGDUU.js";
71
73
  import {
72
74
  OEM4_HLEN,
73
75
  crc32,
@@ -486,6 +488,7 @@ export {
486
488
  estimateWidelaneFcb,
487
489
  euclidean3D,
488
490
  extractWidelaneArcs,
491
+ feedGeoBlock,
489
492
  fetchSourcetable,
490
493
  findSatBias,
491
494
  fixPppPosition,
@@ -591,6 +594,7 @@ export {
591
594
  parseSbfBdsNav,
592
595
  parseSbfCnav,
593
596
  parseSbfGalNav,
597
+ parseSbfGeoNav,
594
598
  parseSbfGloNav,
595
599
  parseSbfGpsNav,
596
600
  parseSbfHas,
package/dist/sbf.cjs CHANGED
@@ -21,10 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var sbf_exports = {};
22
22
  __export(sbf_exports, {
23
23
  decodeSbfNavigation: () => decodeSbfNavigation,
24
+ feedGeoBlock: () => feedGeoBlock,
24
25
  parseSbfAlmanac: () => parseSbfAlmanac,
25
26
  parseSbfBdsNav: () => parseSbfBdsNav,
26
27
  parseSbfCnav: () => parseSbfCnav,
27
28
  parseSbfGalNav: () => parseSbfGalNav,
29
+ parseSbfGeoNav: () => parseSbfGeoNav,
28
30
  parseSbfGloNav: () => parseSbfGloNav,
29
31
  parseSbfGpsNav: () => parseSbfGpsNav,
30
32
  parseSbfHas: () => parseSbfHas,
@@ -1935,6 +1937,92 @@ function parseSbfGloNav(data) {
1935
1937
  return { ephemerides, badCrc, messages };
1936
1938
  }
1937
1939
 
1940
+ // src/navbits/sbas.ts
1941
+ var GPS_EPOCH_MS7 = Date.UTC(1980, 0, 6);
1942
+ var MS_PER_WEEK4 = 7 * 86400 * 1e3;
1943
+ var two3 = (n) => String(n).padStart(2, "0");
1944
+ var SBAS_MT_GEO_NAV = 9;
1945
+ function sbasMessageType(msg) {
1946
+ return getBitU(msg, 8, 6);
1947
+ }
1948
+ function sbasCrcOk(msg) {
1949
+ if (msg.length < 32) return false;
1950
+ return crc24q(msg, 226) === getBitU(msg, 226, 24);
1951
+ }
1952
+ function decodeSbasGeoNav(msg, prn, week, tow) {
1953
+ if (prn < 120 || prn > 158) return null;
1954
+ if (sbasMessageType(msg) !== SBAS_MT_GEO_NAV) return null;
1955
+ let t = getBitU(msg, 22, 13) * 16 - tow % 86400;
1956
+ if (t <= -43200) t += 86400;
1957
+ else if (t > 43200) t -= 86400;
1958
+ const t0Sec = tow + t;
1959
+ const ura = getBitU(msg, 35, 4);
1960
+ return {
1961
+ system: "S",
1962
+ prn: `S${two3(prn - 100)}`,
1963
+ tocDate: new Date(GPS_EPOCH_MS7 + week * MS_PER_WEEK4 + t0Sec * 1e3),
1964
+ tauN: getBitS(msg, 206, 12) * 2 ** -31,
1965
+ // a_Gf0, clock offset (s)
1966
+ gammaN: getBitS(msg, 218, 8) * 2 ** -39 / 2,
1967
+ // a_Gf1, clock drift (s/s)
1968
+ messageFrameTime: (tow % 86400 + 86400) % 86400,
1969
+ x: getBitS(msg, 39, 30) * 0.08 / 1e3,
1970
+ y: getBitS(msg, 69, 30) * 0.08 / 1e3,
1971
+ z: getBitS(msg, 99, 25) * 0.4 / 1e3,
1972
+ xDot: getBitS(msg, 124, 17) * 625e-6 / 1e3,
1973
+ yDot: getBitS(msg, 141, 17) * 625e-6 / 1e3,
1974
+ zDot: getBitS(msg, 158, 18) * 4e-3 / 1e3,
1975
+ xAcc: getBitS(msg, 176, 10) * 125e-7 / 1e3,
1976
+ yAcc: getBitS(msg, 186, 10) * 125e-7 / 1e3,
1977
+ zAcc: getBitS(msg, 196, 10) * 625e-7 / 1e3,
1978
+ health: ura === 15 ? 1 : 0,
1979
+ freqNum: 0
1980
+ };
1981
+ }
1982
+
1983
+ // src/sbf/rawnav-sbas.ts
1984
+ function feedGeoBlock(view, b) {
1985
+ const tow = view.getUint32(b + 8, true);
1986
+ const wnc = view.getUint16(b + 12, true);
1987
+ const svid = view.getUint8(b + 14);
1988
+ const prnStr = svidToPrn(svid);
1989
+ if (!prnStr || prnStr[0] !== "S") return {};
1990
+ const msg = new Uint8Array(32);
1991
+ for (let k = 0; k < 8; k++) {
1992
+ const w = view.getUint32(b + 20 + 4 * k, true);
1993
+ msg[4 * k] = w >>> 24;
1994
+ msg[4 * k + 1] = w >>> 16 & 255;
1995
+ msg[4 * k + 2] = w >>> 8 & 255;
1996
+ msg[4 * k + 3] = w & 255;
1997
+ }
1998
+ if (!sbasCrcOk(msg)) return { badCrc: true };
1999
+ const prn = Number(prnStr.slice(1)) + 100;
2000
+ const eph = decodeSbasGeoNav(msg, prn, wnc, tow / 1e3);
2001
+ return eph ? { eph } : {};
2002
+ }
2003
+ function parseSbfGeoNav(data) {
2004
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
2005
+ const ephemerides = [];
2006
+ const seen = /* @__PURE__ */ new Set();
2007
+ let badCrc = 0;
2008
+ let messages = 0;
2009
+ scanSbfFrames(data, view, (id, b, len) => {
2010
+ if (id !== 4020 || len < 52) return;
2011
+ messages++;
2012
+ const r = feedGeoBlock(view, b);
2013
+ if (r.eph) {
2014
+ const key = `${r.eph.prn}|${r.eph.tocDate.getTime()}`;
2015
+ if (!seen.has(key)) {
2016
+ seen.add(key);
2017
+ ephemerides.push(r.eph);
2018
+ }
2019
+ } else if (r.badCrc) {
2020
+ badCrc++;
2021
+ }
2022
+ });
2023
+ return { ephemerides, badCrc, messages };
2024
+ }
2025
+
1938
2026
  // src/sbf/navigation.ts
1939
2027
  var F4_DNU4 = -2e10;
1940
2028
  function ephKey(e) {
@@ -1955,6 +2043,7 @@ function decodeSbfNavigation(data) {
1955
2043
  galRaw: 0,
1956
2044
  gloRaw: 0,
1957
2045
  bdsRaw: 0,
2046
+ sbasRaw: 0,
1958
2047
  badFrames: 0
1959
2048
  };
1960
2049
  const cnavAsm = newCnavAssemblers();
@@ -2058,6 +2147,14 @@ function decodeSbfNavigation(data) {
2058
2147
  else if (r.badCrc) counts.badFrames++;
2059
2148
  return;
2060
2149
  }
2150
+ case 4020: {
2151
+ if (len < 52) return;
2152
+ counts.sbasRaw++;
2153
+ const r = feedGeoBlock(view, b);
2154
+ if (r.eph) addEph(r.eph);
2155
+ else if (r.badCrc) counts.badFrames++;
2156
+ return;
2157
+ }
2061
2158
  /* ---- iono / UTC ---- */
2062
2159
  case 5893:
2063
2160
  // GPSIon
@@ -2610,8 +2707,8 @@ function parseSbfHas(data) {
2610
2707
 
2611
2708
  // src/sbf/index.ts
2612
2709
  var CLIGHT = 299792458;
2613
- var GPS_EPOCH_MS7 = Date.UTC(1980, 0, 6);
2614
- var MS_PER_WEEK4 = 7 * 864e5;
2710
+ var GPS_EPOCH_MS8 = Date.UTC(1980, 0, 6);
2711
+ var MS_PER_WEEK5 = 7 * 864e5;
2615
2712
  var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
2616
2713
  var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
2617
2714
  var M3_INTERVALS = [
@@ -2710,23 +2807,23 @@ var MEAS2_SIG = [
2710
2807
  ["J", "1E"],
2711
2808
  ["J", "5P"]
2712
2809
  ];
2713
- var two3 = (n) => String(n).padStart(2, "0");
2810
+ var two4 = (n) => String(n).padStart(2, "0");
2714
2811
  function m3Prn(navsys, svid) {
2715
2812
  switch (navsys) {
2716
2813
  case 0:
2717
- return svid < 32 ? `G${two3(svid + 1)}` : null;
2814
+ return svid < 32 ? `G${two4(svid + 1)}` : null;
2718
2815
  case 1:
2719
- return svid < 27 ? `R${two3(svid + 1)}` : null;
2816
+ return svid < 27 ? `R${two4(svid + 1)}` : null;
2720
2817
  case 2:
2721
- return svid < 36 ? `E${two3(svid + 1)}` : null;
2818
+ return svid < 36 ? `E${two4(svid + 1)}` : null;
2722
2819
  case 3:
2723
- return svid < 50 ? `C${two3(svid + 1)}` : null;
2820
+ return svid < 50 ? `C${two4(svid + 1)}` : null;
2724
2821
  case 4:
2725
- return svid <= 38 ? `S${two3(svid + 20)}` : null;
2822
+ return svid <= 38 ? `S${two4(svid + 20)}` : null;
2726
2823
  case 5:
2727
- return svid < 10 ? `J${two3(svid + 1)}` : null;
2824
+ return svid < 10 ? `J${two4(svid + 1)}` : null;
2728
2825
  case 6:
2729
- return svid < 14 ? `I${two3(svid + 1)}` : null;
2826
+ return svid < 14 ? `I${two4(svid + 1)}` : null;
2730
2827
  default:
2731
2828
  return null;
2732
2829
  }
@@ -3264,7 +3361,7 @@ function parseSbfMeas(data) {
3264
3361
  const tow = view.getUint32(i + 8, true);
3265
3362
  const wnc = view.getUint16(i + 12, true);
3266
3363
  if (tow !== 4294967295 && wnc !== 65535) {
3267
- ensureEpoch(GPS_EPOCH_MS7 + wnc * MS_PER_WEEK4 + tow);
3364
+ ensureEpoch(GPS_EPOCH_MS8 + wnc * MS_PER_WEEK5 + tow);
3268
3365
  if (id === 4109) decodeMeas3Ranges(i, tow);
3269
3366
  else if (id === 4110) decodeMeas3CN0(i, len);
3270
3367
  else if (id === 4111) decodeMeas3Doppler(i, len);
@@ -3278,10 +3375,12 @@ function parseSbfMeas(data) {
3278
3375
  // Annotate the CommonJS export names for ESM import in node:
3279
3376
  0 && (module.exports = {
3280
3377
  decodeSbfNavigation,
3378
+ feedGeoBlock,
3281
3379
  parseSbfAlmanac,
3282
3380
  parseSbfBdsNav,
3283
3381
  parseSbfCnav,
3284
3382
  parseSbfGalNav,
3383
+ parseSbfGeoNav,
3285
3384
  parseSbfGloNav,
3286
3385
  parseSbfGpsNav,
3287
3386
  parseSbfHas,
package/dist/sbf.d.cts CHANGED
@@ -206,7 +206,7 @@ declare function parseSbfCnav(data: Uint8Array): SbfCnavResult;
206
206
  * A Septentrio broadcasts navigation data as a mix of *decoded* blocks
207
207
  * (GPSNav/GALNav/GLONav/BDSNav/QZSNav — already-parsed ephemeris) and
208
208
  * *raw* navigation-frame blocks (GPSRawCA, GALRawINAV/FNAV, GLORawCA,
209
- * BDSRaw, GPSRawL2C/L5) whose bits must be assembled — and which class a
209
+ * BDSRaw, GPSRawL2C/L5, GEORawL1) whose bits must be assembled — and which class a
210
210
  * given receiver emits depends on its configuration. The granular
211
211
  * `parseSbf*` functions each re-scan the whole stream for one class;
212
212
  * this decoder walks the byte stream **once**, routing every navigation
@@ -236,6 +236,8 @@ interface SbfNavCounts {
236
236
  gloRaw: number;
237
237
  /** Raw BeiDou D1/D2 subframe blocks (BDSRaw). */
238
238
  bdsRaw: number;
239
+ /** Raw SBAS L1 GEO message blocks (GEORawL1). */
240
+ sbasRaw: number;
239
241
  /** Frames dropped by a CRC/parity check across all raw decoders. */
240
242
  badFrames: number;
241
243
  }
@@ -364,6 +366,33 @@ interface SbfGloNavResult {
364
366
  }
365
367
  declare function parseSbfGloNav(data: Uint8Array): SbfGloNavResult;
366
368
 
369
+ /**
370
+ * Process one GEORawL1 block at frame offset `b`: extract the 250-bit
371
+ * SBAS L1 message, CRC-24Q gate it, and decode a GEO ephemeris if it is
372
+ * message type 9. Returns the ephemeris, `{ badCrc: true }` on a failed
373
+ * CRC, or `{}` for any other (non–type-9) message.
374
+ */
375
+ declare function feedGeoBlock(view: DataView, b: number): {
376
+ eph?: GlonassEphemeris;
377
+ badCrc?: boolean;
378
+ };
379
+ interface SbfGeoNavResult {
380
+ /** SBAS GEO ephemerides in stream order, unchanged repeats suppressed. */
381
+ ephemerides: GlonassEphemeris[];
382
+ /** GEORawL1 messages whose CRC-24Q check failed (dropped). */
383
+ badCrc: number;
384
+ /** Total GEORawL1 blocks seen (with valid SBF framing). */
385
+ messages: number;
386
+ }
387
+ /**
388
+ * Decode every GEORawL1 block in an SBF byte stream into SBAS GEO
389
+ * ephemerides (message type 9), de-duping unchanged rebroadcasts by
390
+ * (PRN, time-of-clock). The one-pass {@link decodeSbfNavigation} routes
391
+ * the same blocks through {@link feedGeoBlock}; this standalone parser
392
+ * mirrors the per-constellation `parseSbf*` helpers.
393
+ */
394
+ declare function parseSbfGeoNav(data: Uint8Array): SbfGeoNavResult;
395
+
367
396
  /** Constellations HAS corrects: GPS (GNSS ID 0), Galileo (GNSS ID 2). */
368
397
  type HasSystem = 'G' | 'E';
369
398
  /** One system's satellite/signal mask (ICD §6.4.3). */
@@ -599,4 +628,4 @@ interface SbfParseResult {
599
628
  */
600
629
  declare function parseSbfMeas(data: Uint8Array): SbfParseResult;
601
630
 
602
- export { CnavEphemeris, type HasMessage, type SbfBdsNavResult, type SbfCnavEphemeris, type SbfCnavResult, type SbfGalEphemeris, type SbfGalNavResult, type SbfGloNavResult, type SbfGpsNavResult, type SbfHasMessage, type SbfHasResult, type SbfIonoUtcResult, type SbfMeasEpoch, type SbfMeasurement, type SbfNavCounts, type SbfNavigation, type SbfParseResult, type SbfReceiverSetup, decodeSbfNavigation, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfReceiverSetup, crc16 as sbfCrc16, scanSbfFrames };
631
+ export { CnavEphemeris, type HasMessage, type SbfBdsNavResult, type SbfCnavEphemeris, type SbfCnavResult, type SbfGalEphemeris, type SbfGalNavResult, type SbfGeoNavResult, type SbfGloNavResult, type SbfGpsNavResult, type SbfHasMessage, type SbfHasResult, type SbfIonoUtcResult, type SbfMeasEpoch, type SbfMeasurement, type SbfNavCounts, type SbfNavigation, type SbfParseResult, type SbfReceiverSetup, decodeSbfNavigation, feedGeoBlock, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGeoNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfReceiverSetup, crc16 as sbfCrc16, scanSbfFrames };
package/dist/sbf.d.ts CHANGED
@@ -206,7 +206,7 @@ declare function parseSbfCnav(data: Uint8Array): SbfCnavResult;
206
206
  * A Septentrio broadcasts navigation data as a mix of *decoded* blocks
207
207
  * (GPSNav/GALNav/GLONav/BDSNav/QZSNav — already-parsed ephemeris) and
208
208
  * *raw* navigation-frame blocks (GPSRawCA, GALRawINAV/FNAV, GLORawCA,
209
- * BDSRaw, GPSRawL2C/L5) whose bits must be assembled — and which class a
209
+ * BDSRaw, GPSRawL2C/L5, GEORawL1) whose bits must be assembled — and which class a
210
210
  * given receiver emits depends on its configuration. The granular
211
211
  * `parseSbf*` functions each re-scan the whole stream for one class;
212
212
  * this decoder walks the byte stream **once**, routing every navigation
@@ -236,6 +236,8 @@ interface SbfNavCounts {
236
236
  gloRaw: number;
237
237
  /** Raw BeiDou D1/D2 subframe blocks (BDSRaw). */
238
238
  bdsRaw: number;
239
+ /** Raw SBAS L1 GEO message blocks (GEORawL1). */
240
+ sbasRaw: number;
239
241
  /** Frames dropped by a CRC/parity check across all raw decoders. */
240
242
  badFrames: number;
241
243
  }
@@ -364,6 +366,33 @@ interface SbfGloNavResult {
364
366
  }
365
367
  declare function parseSbfGloNav(data: Uint8Array): SbfGloNavResult;
366
368
 
369
+ /**
370
+ * Process one GEORawL1 block at frame offset `b`: extract the 250-bit
371
+ * SBAS L1 message, CRC-24Q gate it, and decode a GEO ephemeris if it is
372
+ * message type 9. Returns the ephemeris, `{ badCrc: true }` on a failed
373
+ * CRC, or `{}` for any other (non–type-9) message.
374
+ */
375
+ declare function feedGeoBlock(view: DataView, b: number): {
376
+ eph?: GlonassEphemeris;
377
+ badCrc?: boolean;
378
+ };
379
+ interface SbfGeoNavResult {
380
+ /** SBAS GEO ephemerides in stream order, unchanged repeats suppressed. */
381
+ ephemerides: GlonassEphemeris[];
382
+ /** GEORawL1 messages whose CRC-24Q check failed (dropped). */
383
+ badCrc: number;
384
+ /** Total GEORawL1 blocks seen (with valid SBF framing). */
385
+ messages: number;
386
+ }
387
+ /**
388
+ * Decode every GEORawL1 block in an SBF byte stream into SBAS GEO
389
+ * ephemerides (message type 9), de-duping unchanged rebroadcasts by
390
+ * (PRN, time-of-clock). The one-pass {@link decodeSbfNavigation} routes
391
+ * the same blocks through {@link feedGeoBlock}; this standalone parser
392
+ * mirrors the per-constellation `parseSbf*` helpers.
393
+ */
394
+ declare function parseSbfGeoNav(data: Uint8Array): SbfGeoNavResult;
395
+
367
396
  /** Constellations HAS corrects: GPS (GNSS ID 0), Galileo (GNSS ID 2). */
368
397
  type HasSystem = 'G' | 'E';
369
398
  /** One system's satellite/signal mask (ICD §6.4.3). */
@@ -599,4 +628,4 @@ interface SbfParseResult {
599
628
  */
600
629
  declare function parseSbfMeas(data: Uint8Array): SbfParseResult;
601
630
 
602
- export { CnavEphemeris, type HasMessage, type SbfBdsNavResult, type SbfCnavEphemeris, type SbfCnavResult, type SbfGalEphemeris, type SbfGalNavResult, type SbfGloNavResult, type SbfGpsNavResult, type SbfHasMessage, type SbfHasResult, type SbfIonoUtcResult, type SbfMeasEpoch, type SbfMeasurement, type SbfNavCounts, type SbfNavigation, type SbfParseResult, type SbfReceiverSetup, decodeSbfNavigation, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfReceiverSetup, crc16 as sbfCrc16, scanSbfFrames };
631
+ export { CnavEphemeris, type HasMessage, type SbfBdsNavResult, type SbfCnavEphemeris, type SbfCnavResult, type SbfGalEphemeris, type SbfGalNavResult, type SbfGeoNavResult, type SbfGloNavResult, type SbfGpsNavResult, type SbfHasMessage, type SbfHasResult, type SbfIonoUtcResult, type SbfMeasEpoch, type SbfMeasurement, type SbfNavCounts, type SbfNavigation, type SbfParseResult, type SbfReceiverSetup, decodeSbfNavigation, feedGeoBlock, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGeoNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfReceiverSetup, crc16 as sbfCrc16, scanSbfFrames };
package/dist/sbf.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  crc16,
3
3
  decodeSbfNavigation,
4
+ feedGeoBlock,
4
5
  parseSbfAlmanac,
5
6
  parseSbfBdsNav,
6
7
  parseSbfCnav,
7
8
  parseSbfGalNav,
9
+ parseSbfGeoNav,
8
10
  parseSbfGloNav,
9
11
  parseSbfGpsNav,
10
12
  parseSbfHas,
@@ -13,8 +15,8 @@ import {
13
15
  parseSbfNav,
14
16
  parseSbfReceiverSetup,
15
17
  scanSbfFrames
16
- } from "./chunk-4TAN27LO.js";
17
- import "./chunk-FA2UTLEQ.js";
18
+ } from "./chunk-6EZUM2MB.js";
19
+ import "./chunk-QV3XGDUU.js";
18
20
  import "./chunk-37QNKGTC.js";
19
21
  import "./chunk-6FAL6P4G.js";
20
22
  import "./chunk-YUW33VXK.js";
@@ -23,10 +25,12 @@ import "./chunk-HVXYFUCB.js";
23
25
  import "./chunk-LEEU5OIO.js";
24
26
  export {
25
27
  decodeSbfNavigation,
28
+ feedGeoBlock,
26
29
  parseSbfAlmanac,
27
30
  parseSbfBdsNav,
28
31
  parseSbfCnav,
29
32
  parseSbfGalNav,
33
+ parseSbfGeoNav,
30
34
  parseSbfGloNav,
31
35
  parseSbfGpsNav,
32
36
  parseSbfHas,
@@ -47,8 +47,20 @@ declare function trimbleFrames(data: Uint8Array, stats: {
47
47
  * RETSVDATA table (st[]: 1 = GPS Eph, 9 = GLONASS Eph, 11 = Galileo Eph,
48
48
  * 21 = BeiDou Eph) — but RTKLIB only *decodes* subtypes 1 and 3, so the
49
49
  * GLONASS/BeiDou field layouts here were reverse-engineered and validated
50
- * against physics + the 2026-07-27 BRDC. Still not decoded: the
51
- * QZSS / almanac subtypes (13/23/27 on this stream).
50
+ * against physics + the 2026-07-27 BRDC.
51
+ *
52
+ * Deliberately NOT decoded (investigated on the DLF100NLD1 capture; none
53
+ * are broadcast ephemeris, so none affect positioning, and none are in
54
+ * RTKLIB's table or have a clean validation oracle):
55
+ * - subtype 27 (×30, 184+ B): paged BeiDou supplementary nav — p+1 is a
56
+ * BeiDou PRN, p+2 a page index 3/4/5 (10 sats × 3 pages), i.e. B-CNAV /
57
+ * a reduced-almanac supplement needing multi-page reassembly. BeiDou is
58
+ * already positionable via subtype 21, so this is redundant unless
59
+ * almanac-based pass prediction is wanted.
60
+ * - subtype 23 (×6, 98 B): time/UTC-model parameters (p+1 = a model id,
61
+ * not a satellite) — GNSS-time offsets / UTC model, not per-satellite.
62
+ * - subtype 13 (×1, 87 B): a single auxiliary record (lone almanac or a
63
+ * GPS UTC/health entry).
52
64
  */
53
65
 
54
66
  interface TrimbleNavResult {
package/dist/trimble.d.ts CHANGED
@@ -47,8 +47,20 @@ declare function trimbleFrames(data: Uint8Array, stats: {
47
47
  * RETSVDATA table (st[]: 1 = GPS Eph, 9 = GLONASS Eph, 11 = Galileo Eph,
48
48
  * 21 = BeiDou Eph) — but RTKLIB only *decodes* subtypes 1 and 3, so the
49
49
  * GLONASS/BeiDou field layouts here were reverse-engineered and validated
50
- * against physics + the 2026-07-27 BRDC. Still not decoded: the
51
- * QZSS / almanac subtypes (13/23/27 on this stream).
50
+ * against physics + the 2026-07-27 BRDC.
51
+ *
52
+ * Deliberately NOT decoded (investigated on the DLF100NLD1 capture; none
53
+ * are broadcast ephemeris, so none affect positioning, and none are in
54
+ * RTKLIB's table or have a clean validation oracle):
55
+ * - subtype 27 (×30, 184+ B): paged BeiDou supplementary nav — p+1 is a
56
+ * BeiDou PRN, p+2 a page index 3/4/5 (10 sats × 3 pages), i.e. B-CNAV /
57
+ * a reduced-almanac supplement needing multi-page reassembly. BeiDou is
58
+ * already positionable via subtype 21, so this is redundant unless
59
+ * almanac-based pass prediction is wanted.
60
+ * - subtype 23 (×6, 98 B): time/UTC-model parameters (p+1 = a model id,
61
+ * not a satellite) — GNSS-time offsets / UTC model, not per-satellite.
62
+ * - subtype 13 (×1, 87 B): a single auxiliary record (lone almanac or a
63
+ * GPS UTC/health entry).
52
64
  */
53
65
 
54
66
  interface TrimbleNavResult {
package/dist/ubx.cjs CHANGED
@@ -1249,19 +1249,65 @@ var GloStringAssembler = class {
1249
1249
  }
1250
1250
  };
1251
1251
 
1252
- // src/ubx/rawnav.ts
1252
+ // src/navbits/sbas.ts
1253
1253
  var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
1254
- var SEC_PER_WEEK6 = 7 * 86400;
1254
+ var MS_PER_WEEK2 = 7 * 86400 * 1e3;
1255
1255
  var two2 = (n) => String(n).padStart(2, "0");
1256
+ var SBAS_MT_GEO_NAV = 9;
1257
+ function sbasMessageType(msg) {
1258
+ return getBitU(msg, 8, 6);
1259
+ }
1260
+ function sbasCrcOk(msg) {
1261
+ if (msg.length < 32) return false;
1262
+ return crc24q(msg, 226) === getBitU(msg, 226, 24);
1263
+ }
1264
+ function decodeSbasGeoNav(msg, prn, week, tow) {
1265
+ if (prn < 120 || prn > 158) return null;
1266
+ if (sbasMessageType(msg) !== SBAS_MT_GEO_NAV) return null;
1267
+ let t = getBitU(msg, 22, 13) * 16 - tow % 86400;
1268
+ if (t <= -43200) t += 86400;
1269
+ else if (t > 43200) t -= 86400;
1270
+ const t0Sec = tow + t;
1271
+ const ura = getBitU(msg, 35, 4);
1272
+ return {
1273
+ system: "S",
1274
+ prn: `S${two2(prn - 100)}`,
1275
+ tocDate: new Date(GPS_EPOCH_MS5 + week * MS_PER_WEEK2 + t0Sec * 1e3),
1276
+ tauN: getBitS(msg, 206, 12) * 2 ** -31,
1277
+ // a_Gf0, clock offset (s)
1278
+ gammaN: getBitS(msg, 218, 8) * 2 ** -39 / 2,
1279
+ // a_Gf1, clock drift (s/s)
1280
+ messageFrameTime: (tow % 86400 + 86400) % 86400,
1281
+ x: getBitS(msg, 39, 30) * 0.08 / 1e3,
1282
+ y: getBitS(msg, 69, 30) * 0.08 / 1e3,
1283
+ z: getBitS(msg, 99, 25) * 0.4 / 1e3,
1284
+ xDot: getBitS(msg, 124, 17) * 625e-6 / 1e3,
1285
+ yDot: getBitS(msg, 141, 17) * 625e-6 / 1e3,
1286
+ zDot: getBitS(msg, 158, 18) * 4e-3 / 1e3,
1287
+ xAcc: getBitS(msg, 176, 10) * 125e-7 / 1e3,
1288
+ yAcc: getBitS(msg, 186, 10) * 125e-7 / 1e3,
1289
+ zAcc: getBitS(msg, 196, 10) * 625e-7 / 1e3,
1290
+ health: ura === 15 ? 1 : 0,
1291
+ freqNum: 0
1292
+ };
1293
+ }
1294
+
1295
+ // src/ubx/rawnav.ts
1296
+ var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
1297
+ var SEC_PER_WEEK6 = 7 * 86400;
1298
+ var two3 = (n) => String(n).padStart(2, "0");
1256
1299
  function parseUbxRawNav(data, opts = {}) {
1257
1300
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
1258
1301
  const ephemerides = [];
1259
- const counts = { gal: 0, bds: 0, glo: 0 };
1302
+ const counts = { gal: 0, bds: 0, glo: 0, sbas: 0 };
1260
1303
  let badParity = 0;
1261
1304
  const inav = new GalInavAssembler();
1262
1305
  const bds = new BdsAssembler();
1263
1306
  const glo = new GloStringAssembler();
1264
1307
  let refDate = null;
1308
+ let refWeek = 0;
1309
+ let refTow = 0;
1310
+ const sbasSeen = /* @__PURE__ */ new Set();
1265
1311
  for (const f of ubxFrames(data)) {
1266
1312
  if (f.msgClass !== 2) continue;
1267
1313
  if (f.msgId === 21 && f.payload.length >= 16) {
@@ -1270,8 +1316,10 @@ function parseUbxRawNav(data, opts = {}) {
1270
1316
  if (week === 0 && opts.refWeek !== void 0) week = opts.refWeek;
1271
1317
  if (week > 0) {
1272
1318
  refDate = new Date(
1273
- GPS_EPOCH_MS5 + (week * SEC_PER_WEEK6 + rcvTow) * 1e3
1319
+ GPS_EPOCH_MS6 + (week * SEC_PER_WEEK6 + rcvTow) * 1e3
1274
1320
  );
1321
+ refWeek = week;
1322
+ refTow = rcvTow;
1275
1323
  }
1276
1324
  continue;
1277
1325
  }
@@ -1323,7 +1371,7 @@ function parseUbxRawNav(data, opts = {}) {
1323
1371
  badParity++;
1324
1372
  continue;
1325
1373
  }
1326
- const eph = bds.push(`C${two2(svId)}`, sf);
1374
+ const eph = bds.push(`C${two3(svId)}`, sf);
1327
1375
  if (eph) ephemerides.push(eph);
1328
1376
  } else if (gnssId === 6) {
1329
1377
  if (p.length < 8 + 16) continue;
@@ -1342,16 +1390,41 @@ function parseUbxRawNav(data, opts = {}) {
1342
1390
  continue;
1343
1391
  }
1344
1392
  if (!refDate) continue;
1345
- const eph = glo.push(`R${two2(svId)}`, str, refDate, p[3] - 7);
1393
+ const eph = glo.push(`R${two3(svId)}`, str, refDate, p[3] - 7);
1346
1394
  if (eph) ephemerides.push(eph);
1395
+ } else if (gnssId === 1) {
1396
+ if (p.length < 8 + 32) continue;
1397
+ counts.sbas++;
1398
+ if (svId < 120 || svId > 158) continue;
1399
+ const msg = new Uint8Array(32);
1400
+ for (let k = 0; k < 8; k++) {
1401
+ const w = view.getUint32(base + 4 * k, true);
1402
+ msg[4 * k] = w >>> 24;
1403
+ msg[4 * k + 1] = w >>> 16 & 255;
1404
+ msg[4 * k + 2] = w >>> 8 & 255;
1405
+ msg[4 * k + 3] = w & 255;
1406
+ }
1407
+ if (!sbasCrcOk(msg)) {
1408
+ badParity++;
1409
+ continue;
1410
+ }
1411
+ if (refWeek === 0) continue;
1412
+ const eph = decodeSbasGeoNav(msg, svId, refWeek, refTow);
1413
+ if (eph) {
1414
+ const key = `${eph.prn}@${eph.tocDate.getTime()}`;
1415
+ if (!sbasSeen.has(key)) {
1416
+ sbasSeen.add(key);
1417
+ ephemerides.push(eph);
1418
+ }
1419
+ }
1347
1420
  }
1348
1421
  }
1349
1422
  return { ephemerides, counts, badParity };
1350
1423
  }
1351
1424
 
1352
1425
  // src/ubx/index.ts
1353
- var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
1354
- var MS_PER_WEEK2 = 7 * 864e5;
1426
+ var GPS_EPOCH_MS7 = Date.UTC(1980, 0, 6);
1427
+ var MS_PER_WEEK3 = 7 * 864e5;
1355
1428
  var SIGNALS = {
1356
1429
  0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
1357
1430
  // GPS L1C/A, L2CL, L2CM
@@ -1367,20 +1440,20 @@ var SIGNALS = {
1367
1440
  // GLONASS L1OF, L2OF
1368
1441
  };
1369
1442
  function prnFor(gnssId, svId) {
1370
- const two3 = (n) => String(n).padStart(2, "0");
1443
+ const two4 = (n) => String(n).padStart(2, "0");
1371
1444
  switch (gnssId) {
1372
1445
  case 0:
1373
- return svId >= 1 && svId <= 32 ? `G${two3(svId)}` : null;
1446
+ return svId >= 1 && svId <= 32 ? `G${two4(svId)}` : null;
1374
1447
  case 1:
1375
- return svId >= 120 && svId <= 158 ? `S${two3(svId - 100)}` : null;
1448
+ return svId >= 120 && svId <= 158 ? `S${two4(svId - 100)}` : null;
1376
1449
  case 2:
1377
- return svId >= 1 && svId <= 36 ? `E${two3(svId)}` : null;
1450
+ return svId >= 1 && svId <= 36 ? `E${two4(svId)}` : null;
1378
1451
  case 3:
1379
- return svId >= 1 && svId <= 63 ? `C${two3(svId)}` : null;
1452
+ return svId >= 1 && svId <= 63 ? `C${two4(svId)}` : null;
1380
1453
  case 5:
1381
- return svId >= 1 && svId <= 10 ? `J${two3(svId)}` : null;
1454
+ return svId >= 1 && svId <= 10 ? `J${two4(svId)}` : null;
1382
1455
  case 6:
1383
- return svId >= 1 && svId <= 32 ? `R${two3(svId)}` : null;
1456
+ return svId >= 1 && svId <= 32 ? `R${two4(svId)}` : null;
1384
1457
  default:
1385
1458
  return null;
1386
1459
  }
@@ -1433,7 +1506,7 @@ function parseUbxRawx(data) {
1433
1506
  if (!codes.includes(sig[1])) codes.push(sig[1]);
1434
1507
  }
1435
1508
  epochs.push({
1436
- timeMs: GPS_EPOCH_MS6 + week * MS_PER_WEEK2 + Math.round(rcvTow * 1e3),
1509
+ timeMs: GPS_EPOCH_MS7 + week * MS_PER_WEEK3 + Math.round(rcvTow * 1e3),
1437
1510
  leapS: leapValid ? leapS : null,
1438
1511
  meas
1439
1512
  });