gnss-js 1.42.0 → 1.44.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-EQNC24OB.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,7 +68,7 @@ import {
66
68
  parseSbfNav,
67
69
  parseSbfReceiverSetup,
68
70
  scanSbfFrames
69
- } from "./chunk-4TAN27LO.js";
71
+ } from "./chunk-MEDAWNWM.js";
70
72
  import "./chunk-FA2UTLEQ.js";
71
73
  import {
72
74
  OEM4_HLEN,
@@ -74,7 +76,8 @@ import {
74
76
  oem4Frames,
75
77
  parseNovatelNav,
76
78
  parseNovatelRange
77
- } from "./chunk-5X4AG4MK.js";
79
+ } from "./chunk-ROVSSEGZ.js";
80
+ import "./chunk-ZIGMN3E5.js";
78
81
  import {
79
82
  parseTrimble,
80
83
  parseTrimbleNav,
@@ -486,6 +489,7 @@ export {
486
489
  estimateWidelaneFcb,
487
490
  euclidean3D,
488
491
  extractWidelaneArcs,
492
+ feedGeoBlock,
489
493
  fetchSourcetable,
490
494
  findSatBias,
491
495
  fixPppPosition,
@@ -591,6 +595,7 @@ export {
591
595
  parseSbfBdsNav,
592
596
  parseSbfCnav,
593
597
  parseSbfGalNav,
598
+ parseSbfGeoNav,
594
599
  parseSbfGloNav,
595
600
  parseSbfGpsNav,
596
601
  parseSbfHas,
package/dist/novatel.cjs CHANGED
@@ -398,6 +398,45 @@ var CnavAssembler = class {
398
398
  }
399
399
  };
400
400
 
401
+ // src/navbits/sbas.ts
402
+ var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
403
+ var MS_PER_WEEK = 7 * 86400 * 1e3;
404
+ var two = (n) => String(n).padStart(2, "0");
405
+ var SBAS_MT_GEO_NAV = 9;
406
+ function sbasMessageType(msg) {
407
+ return getBitU(msg, 8, 6);
408
+ }
409
+ function decodeSbasGeoNav(msg, prn, week, tow) {
410
+ if (prn < 120 || prn > 158) return null;
411
+ if (sbasMessageType(msg) !== SBAS_MT_GEO_NAV) return null;
412
+ let t = getBitU(msg, 22, 13) * 16 - tow % 86400;
413
+ if (t <= -43200) t += 86400;
414
+ else if (t > 43200) t -= 86400;
415
+ const t0Sec = tow + t;
416
+ const ura = getBitU(msg, 35, 4);
417
+ return {
418
+ system: "S",
419
+ prn: `S${two(prn - 100)}`,
420
+ tocDate: new Date(GPS_EPOCH_MS3 + week * MS_PER_WEEK + t0Sec * 1e3),
421
+ tauN: getBitS(msg, 206, 12) * 2 ** -31,
422
+ // a_Gf0, clock offset (s)
423
+ gammaN: getBitS(msg, 218, 8) * 2 ** -39 / 2,
424
+ // a_Gf1, clock drift (s/s)
425
+ messageFrameTime: (tow % 86400 + 86400) % 86400,
426
+ x: getBitS(msg, 39, 30) * 0.08 / 1e3,
427
+ y: getBitS(msg, 69, 30) * 0.08 / 1e3,
428
+ z: getBitS(msg, 99, 25) * 0.4 / 1e3,
429
+ xDot: getBitS(msg, 124, 17) * 625e-6 / 1e3,
430
+ yDot: getBitS(msg, 141, 17) * 625e-6 / 1e3,
431
+ zDot: getBitS(msg, 158, 18) * 4e-3 / 1e3,
432
+ xAcc: getBitS(msg, 176, 10) * 125e-7 / 1e3,
433
+ yAcc: getBitS(msg, 186, 10) * 125e-7 / 1e3,
434
+ zAcc: getBitS(msg, 196, 10) * 625e-7 / 1e3,
435
+ health: ura === 15 ? 1 : 0,
436
+ freqNum: 0
437
+ };
438
+ }
439
+
401
440
  // src/constants/time.ts
402
441
  var MILLISECONDS_IN_SECOND = 1e3;
403
442
  var MILLISECONDS_GPS_TAI = 19e3;
@@ -494,18 +533,20 @@ var ID_GALEPHEMERIS = 1122;
494
533
  var ID_QZSSEPHEMERIS = 1336;
495
534
  var ID_GPSEPHEM = 7;
496
535
  var ID_BDSEPHEMERIS = 1696;
497
- var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
536
+ var ID_RAWWAASFRAME = 287;
537
+ var ID_RAWSBASFRAME = 973;
538
+ var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
498
539
  var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
499
540
  var SEC_PER_WEEK3 = 7 * 86400;
500
541
  var SEC_PER_DAY = 86400;
501
- var MS_PER_WEEK = SEC_PER_WEEK3 * 1e3;
502
- var gpsMs = (week, sec) => GPS_EPOCH_MS3 + week * MS_PER_WEEK + sec * 1e3;
542
+ var MS_PER_WEEK2 = SEC_PER_WEEK3 * 1e3;
543
+ var gpsMs = (week, sec) => GPS_EPOCH_MS4 + week * MS_PER_WEEK2 + sec * 1e3;
503
544
  var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK3;
504
545
  function nearWeekMs(refMs, sow) {
505
- const refWeek = Math.floor((refMs - GPS_EPOCH_MS3) / MS_PER_WEEK);
546
+ const refWeek = Math.floor((refMs - GPS_EPOCH_MS4) / MS_PER_WEEK2);
506
547
  let ms = gpsMs(refWeek, sow);
507
- if (ms < refMs - MS_PER_WEEK / 2) ms += MS_PER_WEEK;
508
- else if (ms > refMs + MS_PER_WEEK / 2) ms -= MS_PER_WEEK;
548
+ if (ms < refMs - MS_PER_WEEK2 / 2) ms += MS_PER_WEEK2;
549
+ else if (ms > refMs + MS_PER_WEEK2 / 2) ms -= MS_PER_WEEK2;
509
550
  return ms;
510
551
  }
511
552
  function decodeRawEphem(data, view, p) {
@@ -529,9 +570,9 @@ function decodeGloEphemeris(view, p) {
529
570
  tof += Math.floor(tow / SEC_PER_DAY) * SEC_PER_DAY;
530
571
  if (tof < tow - 43200) tof += SEC_PER_DAY;
531
572
  else if (tof > tow + 43200) tof -= SEC_PER_DAY;
532
- const toeGpsMs = GPS_EPOCH_MS3 + (week * SEC_PER_WEEK3 + tow) * 1e3;
533
- const tofGpsMs = GPS_EPOCH_MS3 + (week * SEC_PER_WEEK3 + tof) * 1e3;
534
- const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() - GPS_EPOCH_MS3) / 1e3;
573
+ const toeGpsMs = GPS_EPOCH_MS4 + (week * SEC_PER_WEEK3 + tow) * 1e3;
574
+ const tofGpsMs = GPS_EPOCH_MS4 + (week * SEC_PER_WEEK3 + tof) * 1e3;
575
+ const tofUtcSec = (getUtcDate(new Date(tofGpsMs)).getTime() - GPS_EPOCH_MS4) / 1e3;
535
576
  return {
536
577
  system: "R",
537
578
  prn: `R${String(slot).padStart(2, "0")}`,
@@ -596,7 +637,7 @@ function decodeGalEphemeris(view, p, headerMs) {
596
637
  omega: view.getFloat64(p + 60, true),
597
638
  omegaDot: view.getFloat64(p + 140, true),
598
639
  idot: view.getFloat64(p + 124, true),
599
- week: Math.floor((toeMs - GPS_EPOCH_MS3) / MS_PER_WEEK),
640
+ week: Math.floor((toeMs - GPS_EPOCH_MS4) / MS_PER_WEEK2),
600
641
  // RINEX Galileo SVH bit layout (E1B DVS/HS in bits 0-2, E5a in
601
642
  // 3-5, E5b in 6-8) — same packing as RTKLIB.
602
643
  svHealth: svhE5b << 7 | dvsE5b << 6 | svhE5a << 4 | dvsE5a << 3 | svhE1b << 1 | dvsE1b,
@@ -613,7 +654,7 @@ function decodeBdsEphemeris(view, p) {
613
654
  const tgd1 = view.getFloat64(p + 20, true);
614
655
  const tocs = view.getUint32(p + 40, true);
615
656
  const toes = view.getUint32(p + 72, true);
616
- const tocDate = new Date(BDT_EPOCH_MS + week * MS_PER_WEEK + tocs * 1e3);
657
+ const tocDate = new Date(BDT_EPOCH_MS + week * MS_PER_WEEK2 + tocs * 1e3);
617
658
  return {
618
659
  system: "C",
619
660
  prn: `C${String(prn).padStart(2, "0")}`,
@@ -755,6 +796,20 @@ function parseNovatelNav(data) {
755
796
  }
756
797
  lastGlo.set(eph.prn, eph);
757
798
  ephemerides.push(eph);
799
+ } else if ((frame.id === ID_RAWSBASFRAME || frame.id === ID_RAWWAASFRAME) && frame.msgLen >= 41) {
800
+ if (frame.week === 0) continue;
801
+ const p = frame.payload;
802
+ const prn = view.getUint32(p + 4, true);
803
+ const msg = new Uint8Array(32);
804
+ msg.set(data.subarray(p + 12, p + 41));
805
+ const eph = decodeSbasGeoNav(msg, prn, frame.week, frame.towMs / 1e3);
806
+ if (!eph) continue;
807
+ const prev = lastGlo.get(eph.prn);
808
+ if (prev && Math.abs(prev.tocDate.getTime() - eph.tocDate.getTime()) < 1e3 && prev.health === eph.health) {
809
+ continue;
810
+ }
811
+ lastGlo.set(eph.prn, eph);
812
+ ephemerides.push(eph);
758
813
  }
759
814
  }
760
815
  return {
@@ -768,8 +823,8 @@ function parseNovatelNav(data) {
768
823
  }
769
824
 
770
825
  // src/novatel/index.ts
771
- var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
772
- var MS_PER_WEEK2 = 7 * 864e5;
826
+ var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
827
+ var MS_PER_WEEK3 = 7 * 864e5;
773
828
  var HDR = 28;
774
829
  var ID_RANGE = 43;
775
830
  var ID_RANGECMP = 140;
@@ -938,7 +993,7 @@ function parseNovatelRange(data) {
938
993
  if (!codes.includes(code)) codes.push(code);
939
994
  }
940
995
  epochs.push({
941
- timeMs: GPS_EPOCH_MS4 + week * MS_PER_WEEK2 + towMs,
996
+ timeMs: GPS_EPOCH_MS5 + week * MS_PER_WEEK3 + towMs,
942
997
  meas
943
998
  });
944
999
  }
@@ -990,7 +1045,7 @@ function parseNovatelRange(data) {
990
1045
  if (!codes.includes(code)) codes.push(code);
991
1046
  }
992
1047
  epochs.push({
993
- timeMs: GPS_EPOCH_MS4 + week * MS_PER_WEEK2 + towMs,
1048
+ timeMs: GPS_EPOCH_MS5 + week * MS_PER_WEEK3 + towMs,
994
1049
  meas
995
1050
  });
996
1051
  }
@@ -39,16 +39,28 @@ declare function oem4Frames(data: Uint8Array, view: DataView, stats: {
39
39
  * NovAtel navigation-message decoding: RAWEPHEM (message 41, raw GPS
40
40
  * LNAV subframes 1–3), GLOEPHEMERIS (message 723, decoded GLONASS
41
41
  * L1 C/A ephemeris), GALEPHEMERIS (1122), BDSEPHEMERIS (1696),
42
- * QZSSEPHEMERIS (1336) and IONUTC (8, GPS Klobuchar + UTC parameters).
42
+ * QZSSEPHEMERIS (1336), RAWSBASFRAME (973) / RAWWAASFRAME (287, its
43
+ * OEMV-era twin — raw SBAS L1 GEO messages) and IONUTC (8, GPS
44
+ * Klobuchar + UTC parameters).
43
45
  *
44
46
  * Ported from RTKLIB demo5 (rtklibexplorer fork, src/rcv/novatel.c:
45
47
  * decode_rawephemb / decode_gloephemerisb / decode_galephemerisb /
46
- * decode_bdsephemerisb / decode_ionutcb, Copyright (c) 2007-2020
47
- * T. Takasu, BSD-2-Clause) and cross-checked against the OEM7
48
- * Commands and Logs Reference Manual. QZSSEPHEMERIS has no RTKLIB
49
- * decoder (RTKLIB only handles the raw-subframe QZSSRAWEPHEM 1331);
50
- * its layout is taken from the OEM7 manual §3.150 and is therefore
51
- * synthetic-tested only, like SBF's QZSNav.
48
+ * decode_bdsephemerisb / decode_rawsbasframeb / decode_ionutcb,
49
+ * Copyright (c) 2007-2020 T. Takasu, BSD-2-Clause) and cross-checked
50
+ * against the OEM7 Commands and Logs Reference Manual. QZSSEPHEMERIS
51
+ * has no RTKLIB decoder (RTKLIB only handles the raw-subframe
52
+ * QZSSRAWEPHEM 1331); its layout is taken from the OEM7 manual §3.150
53
+ * and is therefore synthetic-tested only, like SBF's QZSNav.
54
+ *
55
+ * RAWSBASFRAME/RAWWAASFRAME carry the 250-bit SBAS L1 message as a
56
+ * 29-byte field (OEM7 manual §3.169: after the header, frame-decoder
57
+ * u4, PRN u4, a u4, then 29 bytes of message). Message type 9 (GEO
58
+ * navigation) is decoded by the shared `decodeSbasGeoNav`, the same
59
+ * decoder the u-blox and SBF paths use; other message types (fast/long
60
+ * corrections, etc.) are skipped. Because only 29 bytes (232 bits) are
61
+ * carried — enough for every MT9 field but 18 bits short of the 24-bit
62
+ * SBAS CRC — the SBAS CRC is not re-checked; the OEM4 CRC-32 over the
63
+ * whole log already guarantees the transported bytes.
52
64
  *
53
65
  * Output records mirror what `parseNavFile` produces for the
54
66
  * equivalent RINEX 3 navigation file: Keplerian `tocDate` is a
package/dist/novatel.d.ts CHANGED
@@ -39,16 +39,28 @@ declare function oem4Frames(data: Uint8Array, view: DataView, stats: {
39
39
  * NovAtel navigation-message decoding: RAWEPHEM (message 41, raw GPS
40
40
  * LNAV subframes 1–3), GLOEPHEMERIS (message 723, decoded GLONASS
41
41
  * L1 C/A ephemeris), GALEPHEMERIS (1122), BDSEPHEMERIS (1696),
42
- * QZSSEPHEMERIS (1336) and IONUTC (8, GPS Klobuchar + UTC parameters).
42
+ * QZSSEPHEMERIS (1336), RAWSBASFRAME (973) / RAWWAASFRAME (287, its
43
+ * OEMV-era twin — raw SBAS L1 GEO messages) and IONUTC (8, GPS
44
+ * Klobuchar + UTC parameters).
43
45
  *
44
46
  * Ported from RTKLIB demo5 (rtklibexplorer fork, src/rcv/novatel.c:
45
47
  * decode_rawephemb / decode_gloephemerisb / decode_galephemerisb /
46
- * decode_bdsephemerisb / decode_ionutcb, Copyright (c) 2007-2020
47
- * T. Takasu, BSD-2-Clause) and cross-checked against the OEM7
48
- * Commands and Logs Reference Manual. QZSSEPHEMERIS has no RTKLIB
49
- * decoder (RTKLIB only handles the raw-subframe QZSSRAWEPHEM 1331);
50
- * its layout is taken from the OEM7 manual §3.150 and is therefore
51
- * synthetic-tested only, like SBF's QZSNav.
48
+ * decode_bdsephemerisb / decode_rawsbasframeb / decode_ionutcb,
49
+ * Copyright (c) 2007-2020 T. Takasu, BSD-2-Clause) and cross-checked
50
+ * against the OEM7 Commands and Logs Reference Manual. QZSSEPHEMERIS
51
+ * has no RTKLIB decoder (RTKLIB only handles the raw-subframe
52
+ * QZSSRAWEPHEM 1331); its layout is taken from the OEM7 manual §3.150
53
+ * and is therefore synthetic-tested only, like SBF's QZSNav.
54
+ *
55
+ * RAWSBASFRAME/RAWWAASFRAME carry the 250-bit SBAS L1 message as a
56
+ * 29-byte field (OEM7 manual §3.169: after the header, frame-decoder
57
+ * u4, PRN u4, a u4, then 29 bytes of message). Message type 9 (GEO
58
+ * navigation) is decoded by the shared `decodeSbasGeoNav`, the same
59
+ * decoder the u-blox and SBF paths use; other message types (fast/long
60
+ * corrections, etc.) are skipped. Because only 29 bytes (232 bits) are
61
+ * carried — enough for every MT9 field but 18 bits short of the 24-bit
62
+ * SBAS CRC — the SBAS CRC is not re-checked; the OEM4 CRC-32 over the
63
+ * whole log already guarantees the transported bytes.
52
64
  *
53
65
  * Output records mirror what `parseNavFile` produces for the
54
66
  * equivalent RINEX 3 navigation file: Keplerian `tocDate` is a
package/dist/novatel.js CHANGED
@@ -4,7 +4,8 @@ import {
4
4
  oem4Frames,
5
5
  parseNovatelNav,
6
6
  parseNovatelRange
7
- } from "./chunk-5X4AG4MK.js";
7
+ } from "./chunk-ROVSSEGZ.js";
8
+ import "./chunk-ZIGMN3E5.js";
8
9
  import "./chunk-YUW33VXK.js";
9
10
  import "./chunk-V2D5KUKH.js";
10
11
  import "./chunk-HVXYFUCB.js";
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,9 @@ import {
13
15
  parseSbfNav,
14
16
  parseSbfReceiverSetup,
15
17
  scanSbfFrames
16
- } from "./chunk-4TAN27LO.js";
18
+ } from "./chunk-MEDAWNWM.js";
17
19
  import "./chunk-FA2UTLEQ.js";
20
+ import "./chunk-ZIGMN3E5.js";
18
21
  import "./chunk-37QNKGTC.js";
19
22
  import "./chunk-6FAL6P4G.js";
20
23
  import "./chunk-YUW33VXK.js";
@@ -23,10 +26,12 @@ import "./chunk-HVXYFUCB.js";
23
26
  import "./chunk-LEEU5OIO.js";
24
27
  export {
25
28
  decodeSbfNavigation,
29
+ feedGeoBlock,
26
30
  parseSbfAlmanac,
27
31
  parseSbfBdsNav,
28
32
  parseSbfCnav,
29
33
  parseSbfGalNav,
34
+ parseSbfGeoNav,
30
35
  parseSbfGloNav,
31
36
  parseSbfGpsNav,
32
37
  parseSbfHas,