gnss-js 2.3.0 → 2.5.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.cjs CHANGED
@@ -23,8 +23,10 @@ __export(sbf_exports, {
23
23
  SBAS_L5_MT_NAMES: () => SBAS_L5_MT_NAMES,
24
24
  SBAS_L5_PREAMBLE_UW: () => SBAS_L5_PREAMBLE_UW,
25
25
  decodeSbfNavigation: () => decodeSbfNavigation,
26
+ extractSbfNavicSubframe: () => extractSbfNavicSubframe,
26
27
  feedGeoBlock: () => feedGeoBlock,
27
28
  feedGeoL5Block: () => feedGeoL5Block,
29
+ feedNavicBlock: () => feedNavicBlock,
28
30
  isSbasL1Preamble: () => isSbasL1Preamble,
29
31
  isSbasL5Correction: () => isSbasL5Correction,
30
32
  parseSbfAlmanac: () => parseSbfAlmanac,
@@ -39,6 +41,7 @@ __export(sbf_exports, {
39
41
  parseSbfIonoUtc: () => parseSbfIonoUtc,
40
42
  parseSbfMeas: () => parseSbfMeas,
41
43
  parseSbfNav: () => parseSbfNav,
44
+ parseSbfNavic: () => parseSbfNavic,
42
45
  parseSbfPvt: () => parseSbfPvt,
43
46
  parseSbfReceiverSetup: () => parseSbfReceiverSetup,
44
47
  sbasL5MessageName: () => sbasL5MessageName,
@@ -1068,6 +1071,173 @@ var CnavAssembler = class {
1068
1071
  }
1069
1072
  };
1070
1073
 
1074
+ // src/navbits/navic.ts
1075
+ var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
1076
+ var SEC_PER_WEEK4 = 604800;
1077
+ var DATA = 30;
1078
+ function navicSubframeCrcOk(sf) {
1079
+ return crc24q(sf, 262) === getBitU(sf, 262, 24);
1080
+ }
1081
+ function navicSubframeId(sf) {
1082
+ return getBitU(sf, 27, 2) + 1;
1083
+ }
1084
+ function decodeNavicSubframes(sf1, sf2, opts = {}) {
1085
+ let i = DATA;
1086
+ getBitU(sf1, i, 10);
1087
+ i += 10;
1088
+ const af0 = getBitS(sf1, i, 22) * 2 ** -31;
1089
+ i += 22;
1090
+ const af1 = getBitS(sf1, i, 16) * 2 ** -43;
1091
+ i += 16;
1092
+ const af2 = getBitS(sf1, i, 8) * 2 ** -55;
1093
+ i += 8;
1094
+ i += 4;
1095
+ const tocSec = getBitU(sf1, i, 16) * 16;
1096
+ i += 16;
1097
+ const tgd = getBitS(sf1, i, 8) * 2 ** -31;
1098
+ i += 8;
1099
+ const deltaN = getBitS(sf1, i, 22) * 2 ** -41 * GPS_PI;
1100
+ i += 22;
1101
+ const iode = getBitU(sf1, i, 8);
1102
+ i += 8;
1103
+ i += 10;
1104
+ const l5Health = getBitU(sf1, i, 1);
1105
+ i += 1;
1106
+ const sHealth = getBitU(sf1, i, 1);
1107
+ i += 1;
1108
+ const cuc = getBitS(sf1, i, 15) * 2 ** -28;
1109
+ i += 15;
1110
+ const cus = getBitS(sf1, i, 15) * 2 ** -28;
1111
+ i += 15;
1112
+ const cic = getBitS(sf1, i, 15) * 2 ** -28;
1113
+ i += 15;
1114
+ const cis = getBitS(sf1, i, 15) * 2 ** -28;
1115
+ i += 15;
1116
+ const crc = getBitS(sf1, i, 15) * 2 ** -4;
1117
+ i += 15;
1118
+ const crs = getBitS(sf1, i, 15) * 2 ** -4;
1119
+ i += 15;
1120
+ const idot = getBitS(sf1, i, 14) * 2 ** -43 * GPS_PI;
1121
+ let j = DATA;
1122
+ const m0 = getBitS(sf2, j, 32) * 2 ** -31 * GPS_PI;
1123
+ j += 32;
1124
+ const toeSec = getBitU(sf2, j, 16) * 16;
1125
+ j += 16;
1126
+ const e = getBitU(sf2, j, 32) * 2 ** -33;
1127
+ j += 32;
1128
+ const sqrtA = getBitU(sf2, j, 32) * 2 ** -19;
1129
+ j += 32;
1130
+ const omega0 = getBitS(sf2, j, 32) * 2 ** -31 * GPS_PI;
1131
+ j += 32;
1132
+ const omega = getBitS(sf2, j, 32) * 2 ** -31 * GPS_PI;
1133
+ j += 32;
1134
+ const omegaDot = getBitS(sf2, j, 22) * 2 ** -41 * GPS_PI;
1135
+ j += 22;
1136
+ const i0 = getBitS(sf2, j, 32) * 2 ** -31 * GPS_PI;
1137
+ if (sqrtA <= 0 || e >= 1) return null;
1138
+ const week = opts.refWeek ?? Math.floor((Date.now() - GPS_EPOCH_MS5) / 1e3 / SEC_PER_WEEK4);
1139
+ const prn = opts.prn ?? "I00";
1140
+ const tocDate = new Date(
1141
+ GPS_EPOCH_MS5 + (week * SEC_PER_WEEK4 + tocSec) * 1e3
1142
+ );
1143
+ return {
1144
+ system: "I",
1145
+ prn,
1146
+ tocDate,
1147
+ toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK4,
1148
+ af0,
1149
+ af1,
1150
+ af2,
1151
+ iode,
1152
+ crs,
1153
+ deltaN,
1154
+ m0,
1155
+ cuc,
1156
+ e,
1157
+ cus,
1158
+ sqrtA,
1159
+ toe: toeSec,
1160
+ cic,
1161
+ omega0,
1162
+ cis,
1163
+ i0,
1164
+ crc,
1165
+ omega,
1166
+ omegaDot,
1167
+ idot,
1168
+ week,
1169
+ svHealth: l5Health << 1 | sHealth,
1170
+ tgd
1171
+ };
1172
+ }
1173
+ var NavicAssembler = class {
1174
+ sf1 = /* @__PURE__ */ new Map();
1175
+ last = /* @__PURE__ */ new Map();
1176
+ /**
1177
+ * Push one CRC-valid subframe. Subframe 1 opens a master frame (subframe 2
1178
+ * immediately follows it in the NavIC frame order), so an ephemeris is only
1179
+ * emitted when a subframe 2 pairs with the subframe 1 that opened the *same*
1180
+ * frame — never a stale subframe 1 left over across CRC drops.
1181
+ */
1182
+ push(prn, subframe, refWeek) {
1183
+ if (getBitU(subframe, 25, 1)) return null;
1184
+ const id = navicSubframeId(subframe);
1185
+ if (id === 1) {
1186
+ this.sf1.set(prn, subframe);
1187
+ return null;
1188
+ }
1189
+ if (id !== 2) return null;
1190
+ const a = this.sf1.get(prn);
1191
+ if (!a) return null;
1192
+ this.sf1.delete(prn);
1193
+ const eph = decodeNavicSubframes(a, subframe, { prn, refWeek });
1194
+ if (!eph) return null;
1195
+ const key = `${eph.toc}|${eph.iode}`;
1196
+ if (this.last.get(prn) === key) return null;
1197
+ this.last.set(prn, key);
1198
+ return eph;
1199
+ }
1200
+ };
1201
+
1202
+ // src/sbf/rawnav-navic.ts
1203
+ var WNC_DNU2 = 65535;
1204
+ function extractSbfNavicSubframe(view, b) {
1205
+ const sf = new Uint8Array(40);
1206
+ for (let k = 0; k < 10; k++) {
1207
+ const w = view.getUint32(b + 20 + 4 * k, true);
1208
+ sf[4 * k] = w >>> 24;
1209
+ sf[4 * k + 1] = w >>> 16 & 255;
1210
+ sf[4 * k + 2] = w >>> 8 & 255;
1211
+ sf[4 * k + 3] = w & 255;
1212
+ }
1213
+ return sf;
1214
+ }
1215
+ function feedNavicBlock(view, b, assembler) {
1216
+ const prn = svidToPrn(view.getUint8(b + 14));
1217
+ if (!prn || prn[0] !== "I") return {};
1218
+ const wnc = view.getUint16(b + 12, true);
1219
+ if (wnc === WNC_DNU2) return {};
1220
+ const sf = extractSbfNavicSubframe(view, b);
1221
+ if (!navicSubframeCrcOk(sf)) return { badCrc: true };
1222
+ const eph = assembler.push(prn, sf, wnc);
1223
+ return eph ? { eph } : {};
1224
+ }
1225
+ function parseSbfNavic(data) {
1226
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
1227
+ const ephemerides = [];
1228
+ const assembler = new NavicAssembler();
1229
+ let badCrc = 0;
1230
+ let messages = 0;
1231
+ scanSbfFrames(data, view, (id, b, len) => {
1232
+ if (id !== 4093 || len < 60) return;
1233
+ messages++;
1234
+ const r = feedNavicBlock(view, b, assembler);
1235
+ if (r.eph) ephemerides.push(r.eph);
1236
+ else if (r.badCrc) badCrc++;
1237
+ });
1238
+ return { ephemerides, badCrc, messages };
1239
+ }
1240
+
1071
1241
  // src/sbf/rawnav.ts
1072
1242
  var CNAV_SIGNAL_OF_BLOCK = {
1073
1243
  4018: "L2C",
@@ -1108,8 +1278,8 @@ function parseSbfCnav(data) {
1108
1278
  }
1109
1279
 
1110
1280
  // src/navbits/gal.ts
1111
- var GPS_EPOCH_MS5 = Date.UTC(1980, 0, 6);
1112
- var SEC_PER_WEEK4 = 7 * 86400;
1281
+ var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
1282
+ var SEC_PER_WEEK5 = 7 * 86400;
1113
1283
  var HALF_WEEK2 = 302400;
1114
1284
  var GST_GPS_WEEK_OFFSET = 1024;
1115
1285
  function galInavPageCrcOk(page) {
@@ -1121,7 +1291,7 @@ function galFnavPageCrcOk(page) {
1121
1291
  return crc24q(page, 214) === getBitU(page, 214, 24);
1122
1292
  }
1123
1293
  function gpsDate2(week, sec) {
1124
- return new Date(GPS_EPOCH_MS5 + (week * SEC_PER_WEEK4 + sec) * 1e3);
1294
+ return new Date(GPS_EPOCH_MS6 + (week * SEC_PER_WEEK5 + sec) * 1e3);
1125
1295
  }
1126
1296
  function weekOfToe(week, tow, toe) {
1127
1297
  if (toe - tow > HALF_WEEK2) return week - 1;
@@ -1217,7 +1387,7 @@ function decodeGalInavWords(words) {
1217
1387
  return {
1218
1388
  system: "E",
1219
1389
  prn: `E${two2(svid)}`,
1220
- toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK4,
1390
+ toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK5,
1221
1391
  tocDate,
1222
1392
  af0,
1223
1393
  af1,
@@ -1325,7 +1495,7 @@ function decodeGalFnavPages(pages) {
1325
1495
  return {
1326
1496
  system: "E",
1327
1497
  prn: `E${two2(svid)}`,
1328
- toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK4,
1498
+ toc: tocDate.getTime() / 1e3 % SEC_PER_WEEK5,
1329
1499
  tocDate,
1330
1500
  af0,
1331
1501
  af1,
@@ -1475,10 +1645,10 @@ function isFnavDummy(page) {
1475
1645
 
1476
1646
  // src/navbits/bds.ts
1477
1647
  var BDT_EPOCH_MS2 = Date.UTC(2006, 0, 1);
1478
- var SEC_PER_WEEK5 = 7 * 86400;
1479
- var MS_PER_WEEK2 = SEC_PER_WEEK5 * 1e3;
1648
+ var SEC_PER_WEEK6 = 7 * 86400;
1649
+ var MS_PER_WEEK2 = SEC_PER_WEEK6 * 1e3;
1480
1650
  var HALF_WEEK3 = 302400;
1481
- var sowOf2 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK5;
1651
+ var sowOf2 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK6;
1482
1652
  var BDS_SUBFRAME_BYTES = 38;
1483
1653
  function getBitU22(b, p1, l1, p2, l2) {
1484
1654
  return getBitU(b, p1, l1) * 2 ** l2 + getBitU(b, p2, l2);
@@ -1752,8 +1922,8 @@ var BdsAssembler = class {
1752
1922
  };
1753
1923
 
1754
1924
  // src/navbits/glo.ts
1755
- var GPS_EPOCH_MS6 = Date.UTC(1980, 0, 6);
1756
- var SEC_PER_WEEK6 = 7 * 86400;
1925
+ var GPS_EPOCH_MS7 = Date.UTC(1980, 0, 6);
1926
+ var SEC_PER_WEEK7 = 7 * 86400;
1757
1927
  var SEC_PER_DAY = 86400;
1758
1928
  var GLO_STRING_BYTES = 10;
1759
1929
  function getBitG(b, pos, len) {
@@ -1846,9 +2016,9 @@ function decodeGloStrings(strings, refDate, opts = {}) {
1846
2016
  const slot = getBitU(b, i, 5);
1847
2017
  if (frn1 !== 1 || frn2 !== 2 || frn3 !== 3 || frn4 !== 4) return null;
1848
2018
  if (slot < 1 || slot > 27) return null;
1849
- const utcSec = (getUtcDate(refDate).getTime() - GPS_EPOCH_MS6) / 1e3;
1850
- const week = Math.floor(utcSec / SEC_PER_WEEK6);
1851
- let tow = utcSec - week * SEC_PER_WEEK6;
2019
+ const utcSec = (getUtcDate(refDate).getTime() - GPS_EPOCH_MS7) / 1e3;
2020
+ const week = Math.floor(utcSec / SEC_PER_WEEK7);
2021
+ let tow = utcSec - week * SEC_PER_WEEK7;
1852
2022
  const tod = tow % SEC_PER_DAY;
1853
2023
  tow -= tod;
1854
2024
  let tof = tkH * 3600 + tkM * 60 + tkS - 10800;
@@ -1861,12 +2031,12 @@ function decodeGloStrings(strings, refDate, opts = {}) {
1861
2031
  system: "R",
1862
2032
  prn: `R${String(slot).padStart(2, "0")}`,
1863
2033
  // RINEX GLONASS epochs are UTC: build the UTC toe Date directly.
1864
- tocDate: new Date(GPS_EPOCH_MS6 + (week * SEC_PER_WEEK6 + tow + toe) * 1e3),
2034
+ tocDate: new Date(GPS_EPOCH_MS7 + (week * SEC_PER_WEEK7 + tow + toe) * 1e3),
1865
2035
  tauN: -tauN,
1866
2036
  // RINEX stores −τn; the SIS carries τn (ICD sign)
1867
2037
  gammaN,
1868
2038
  // v3 message frame time: seconds of the UTC week (RTKLIB tof).
1869
- messageFrameTime: ((tow + tof) % SEC_PER_WEEK6 + SEC_PER_WEEK6) % SEC_PER_WEEK6,
2039
+ messageFrameTime: ((tow + tof) % SEC_PER_WEEK7 + SEC_PER_WEEK7) % SEC_PER_WEEK7,
1870
2040
  x,
1871
2041
  xDot,
1872
2042
  xAcc,
@@ -1915,10 +2085,10 @@ var GloStringAssembler = class {
1915
2085
  };
1916
2086
 
1917
2087
  // src/sbf/rawnav-bds.ts
1918
- var GPS_EPOCH_MS7 = Date.UTC(1980, 0, 6);
2088
+ var GPS_EPOCH_MS8 = Date.UTC(1980, 0, 6);
1919
2089
  var MS_PER_WEEK3 = 7 * 86400 * 1e3;
1920
2090
  var TOW_DNU = 4294967295;
1921
- var WNC_DNU2 = 65535;
2091
+ var WNC_DNU3 = 65535;
1922
2092
  function feedBdsBlock(view, b, assembler) {
1923
2093
  const prn = svidToPrn(view.getUint8(b + 14));
1924
2094
  if (!prn || prn[0] !== "C") return {};
@@ -1958,7 +2128,7 @@ function feedGloBlock(view, b, assembler) {
1958
2128
  if (!prn || prn[0] !== "R") return {};
1959
2129
  const towMs = view.getUint32(b + 8, true);
1960
2130
  const wnc = view.getUint16(b + 12, true);
1961
- if (towMs === TOW_DNU || wnc === WNC_DNU2) return {};
2131
+ if (towMs === TOW_DNU || wnc === WNC_DNU3) return {};
1962
2132
  const str2 = new Uint8Array(11);
1963
2133
  for (let k = 0; k < 3; k++) {
1964
2134
  const w = view.getUint32(b + 20 + 4 * k, true);
@@ -1976,7 +2146,7 @@ function feedGloBlock(view, b, assembler) {
1976
2146
  const eph = assembler.push(
1977
2147
  prn,
1978
2148
  str2,
1979
- new Date(GPS_EPOCH_MS7 + wnc * MS_PER_WEEK3 + towMs),
2149
+ new Date(GPS_EPOCH_MS8 + wnc * MS_PER_WEEK3 + towMs),
1980
2150
  view.getUint8(b + 18) - 8
1981
2151
  // FreqNr, offset by 8
1982
2152
  );
@@ -1999,7 +2169,7 @@ function parseSbfGloNav(data) {
1999
2169
  }
2000
2170
 
2001
2171
  // src/navbits/sbas.ts
2002
- var GPS_EPOCH_MS8 = Date.UTC(1980, 0, 6);
2172
+ var GPS_EPOCH_MS9 = Date.UTC(1980, 0, 6);
2003
2173
  var MS_PER_WEEK4 = 7 * 86400 * 1e3;
2004
2174
  var two3 = (n) => String(n).padStart(2, "0");
2005
2175
  var SBAS_MT_GEO_NAV = 9;
@@ -2021,7 +2191,7 @@ function decodeSbasGeoNav(msg, prn, week, tow) {
2021
2191
  return {
2022
2192
  system: "S",
2023
2193
  prn: `S${two3(prn - 100)}`,
2024
- tocDate: new Date(GPS_EPOCH_MS8 + week * MS_PER_WEEK4 + t0Sec * 1e3),
2194
+ tocDate: new Date(GPS_EPOCH_MS9 + week * MS_PER_WEEK4 + t0Sec * 1e3),
2025
2195
  tauN: getBitS(msg, 206, 12) * 2 ** -31,
2026
2196
  // a_Gf0, clock offset (s)
2027
2197
  gammaN: getBitS(msg, 218, 8) * 2 ** -39 / 2,
@@ -2194,6 +2364,7 @@ function decodeSbfNavigation(data, opts = {}) {
2194
2364
  bdsRaw: 0,
2195
2365
  sbasRaw: 0,
2196
2366
  sbasL5Raw: 0,
2367
+ navicRaw: 0,
2197
2368
  badFrames: 0
2198
2369
  };
2199
2370
  const dfmcByType = {};
@@ -2203,6 +2374,7 @@ function decodeSbfNavigation(data, opts = {}) {
2203
2374
  const gloAsm = new GloStringAssembler();
2204
2375
  const bdsAsm = new BdsAssembler();
2205
2376
  const lnavAsm = new GpsLnavAssembler();
2377
+ const navicAsm = new NavicAssembler();
2206
2378
  const addEph = (e) => {
2207
2379
  if (!e) return;
2208
2380
  const key = ephKey(e);
@@ -2263,6 +2435,14 @@ function decodeSbfNavigation(data, opts = {}) {
2263
2435
  else if (r.badParity) counts.badFrames++;
2264
2436
  return;
2265
2437
  }
2438
+ case 4093: {
2439
+ if (len < 60) return;
2440
+ counts.navicRaw++;
2441
+ const r = feedNavicBlock(view, b, navicAsm);
2442
+ if (r.eph) addEph(r.eph);
2443
+ else if (r.badCrc) counts.badFrames++;
2444
+ return;
2445
+ }
2266
2446
  case 4018:
2267
2447
  // GPSRawL2C
2268
2448
  case 4019: {
@@ -2883,7 +3063,7 @@ function parseSbfHas(data) {
2883
3063
 
2884
3064
  // src/sbf/index.ts
2885
3065
  var CLIGHT = 299792458;
2886
- var GPS_EPOCH_MS9 = Date.UTC(1980, 0, 6);
3066
+ var GPS_EPOCH_MS10 = Date.UTC(1980, 0, 6);
2887
3067
  var MS_PER_WEEK5 = 7 * 864e5;
2888
3068
  var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
2889
3069
  var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
@@ -3537,7 +3717,7 @@ function parseSbfMeas(data) {
3537
3717
  const tow = view.getUint32(i + 8, true);
3538
3718
  const wnc = view.getUint16(i + 12, true);
3539
3719
  if (tow !== 4294967295 && wnc !== 65535) {
3540
- ensureEpoch(GPS_EPOCH_MS9 + wnc * MS_PER_WEEK5 + tow);
3720
+ ensureEpoch(GPS_EPOCH_MS10 + wnc * MS_PER_WEEK5 + tow);
3541
3721
  if (id === 4109) decodeMeas3Ranges(i, tow);
3542
3722
  else if (id === 4110) decodeMeas3CN0(i, len);
3543
3723
  else if (id === 4111) decodeMeas3Doppler(i, len);
@@ -3553,8 +3733,10 @@ function parseSbfMeas(data) {
3553
3733
  SBAS_L5_MT_NAMES,
3554
3734
  SBAS_L5_PREAMBLE_UW,
3555
3735
  decodeSbfNavigation,
3736
+ extractSbfNavicSubframe,
3556
3737
  feedGeoBlock,
3557
3738
  feedGeoL5Block,
3739
+ feedNavicBlock,
3558
3740
  isSbasL1Preamble,
3559
3741
  isSbasL5Correction,
3560
3742
  parseSbfAlmanac,
@@ -3569,6 +3751,7 @@ function parseSbfMeas(data) {
3569
3751
  parseSbfIonoUtc,
3570
3752
  parseSbfMeas,
3571
3753
  parseSbfNav,
3754
+ parseSbfNavic,
3572
3755
  parseSbfPvt,
3573
3756
  parseSbfReceiverSetup,
3574
3757
  sbasL5MessageName,
package/dist/sbf.d.cts CHANGED
@@ -174,6 +174,65 @@ interface SbfGpsNavResult {
174
174
  */
175
175
  declare function parseSbfGpsNav(data: Uint8Array): SbfGpsNavResult;
176
176
 
177
+ /**
178
+ * Streaming assembler: feed 292-bit NavIC subframes per PRN; emits a fresh
179
+ * ephemeris once a satellite's subframe 1 and 2 are both in hand (deduped by
180
+ * toc + IODEC). Mirrors {@link GpsLnavAssembler} for the raw-frame formats.
181
+ */
182
+ declare class NavicAssembler {
183
+ private readonly sf1;
184
+ private readonly last;
185
+ /**
186
+ * Push one CRC-valid subframe. Subframe 1 opens a master frame (subframe 2
187
+ * immediately follows it in the NavIC frame order), so an ephemeris is only
188
+ * emitted when a subframe 2 pairs with the subframe 1 that opened the *same*
189
+ * frame — never a stale subframe 1 left over across CRC drops.
190
+ */
191
+ push(prn: string, subframe: Uint8Array, refWeek: number): KeplerEphemeris | null;
192
+ }
193
+
194
+ /**
195
+ * Septentrio SBF NAVICRaw (4093) blocks: each carries one 292-bit NavIC L5/S
196
+ * SPS subframe. Subframes 1 & 2 are assembled per satellite into a Keplerian
197
+ * ephemeris by the shared {@link NavicAssembler}.
198
+ *
199
+ * Block layout (mosaic-X5 reference guide §4): after the 8-byte SBF header,
200
+ * TOW u4 + WNc u2 (full GPS week), SVID u1, CRCPassed u1, ViterbiCnt u1,
201
+ * Source u1, FreqNr u1, RxChannel u1, then NAVBits as u4[10] — the first
202
+ * received bit is the MSB of NAVBits[0] (each u4 little-endian in the stream,
203
+ * message bits MSB-first within the word). Only the first 292 bits are used.
204
+ *
205
+ * The receiver's own CRCPassed flag is ignored in favour of re-running CRC-24Q
206
+ * on the transported bits, so `badCrc` counts exactly what this library rejects.
207
+ */
208
+
209
+ interface SbfNavicResult {
210
+ /** Assembled NavIC ephemerides in stream order, repeats suppressed. */
211
+ ephemerides: KeplerEphemeris[];
212
+ /** Subframes whose CRC-24Q check failed (dropped). */
213
+ badCrc: number;
214
+ /** Total NAVICRaw blocks seen (with valid SBF framing). */
215
+ messages: number;
216
+ }
217
+ /** Extract the 292-bit NavIC subframe (MSB-first) from a NAVICRaw block. */
218
+ declare function extractSbfNavicSubframe(view: DataView, b: number): Uint8Array;
219
+ /**
220
+ * Process one NAVICRaw (4093) block at frame offset `b`: map SVID → NavIC PRN,
221
+ * extract + CRC-24Q gate the subframe, and push it to the shared assembler
222
+ * with the block's full WNc week. Returns a fresh ephemeris, `{ badCrc: true }`
223
+ * on a failed CRC, or `{}`. Shared by {@link parseSbfNavic} and the one-pass
224
+ * {@link decodeSbfNavigation}.
225
+ */
226
+ declare function feedNavicBlock(view: DataView, b: number, assembler: NavicAssembler): {
227
+ eph?: KeplerEphemeris;
228
+ badCrc?: boolean;
229
+ };
230
+ /**
231
+ * Decode every NAVICRaw (4093) block in an SBF byte stream and assemble the
232
+ * carried subframes into NavIC ephemerides (`system` 'I').
233
+ */
234
+ declare function parseSbfNavic(data: Uint8Array): SbfNavicResult;
235
+
177
236
  /**
178
237
  * Septentrio SBF raw CNAV navigation-bit blocks: GPSRawL2C (4018) and
179
238
  * GPSRawL5 (4019) each carry one 300-bit GPS CNAV message.
@@ -339,6 +398,8 @@ interface SbfNavCounts {
339
398
  sbasRaw: number;
340
399
  /** Raw SBAS L5 GEO message blocks (GEORawL5) — DO-229-on-L5 + native DFMC. */
341
400
  sbasL5Raw: number;
401
+ /** Raw NavIC L5/S SPS subframe blocks (NAVICRaw). */
402
+ navicRaw: number;
342
403
  /** Frames dropped by a CRC/parity check across all raw decoders. */
343
404
  badFrames: number;
344
405
  }
@@ -610,4 +671,4 @@ interface SbfParseResult {
610
671
  */
611
672
  declare function parseSbfMeas(data: Uint8Array): SbfParseResult;
612
673
 
613
- export { CnavEphemeris, type DfmcCensus, type DfmcMessageCb, type GeoL5Result, HasMessage, ReceiverPvt, SBAS_L5_MT_NAMES, SBAS_L5_PREAMBLE_UW, type SbasMessageCb, type SbfBdsNavResult, type SbfCnavEphemeris, type SbfCnavResult, type SbfGalEphemeris, type SbfGalNavResult, type SbfGeoL5Result, type SbfGeoNavResult, type SbfGloNavResult, type SbfGpsNavResult, type SbfHasMessage, type SbfHasResult, type SbfIonoUtcResult, type SbfMeasEpoch, type SbfMeasurement, type SbfNavCounts, type SbfNavigation, type SbfParseResult, type SbfPvt, type SbfPvtResult, type SbfReceiverSetup, decodeSbfNavigation, feedGeoBlock, feedGeoL5Block, isSbasL1Preamble, isSbasL5Correction, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGeoL5, parseSbfGeoNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfPvt, parseSbfReceiverSetup, sbasL5MessageName, sbasL5MessageType, crc16 as sbfCrc16, scanSbfFrames };
674
+ export { CnavEphemeris, type DfmcCensus, type DfmcMessageCb, type GeoL5Result, HasMessage, ReceiverPvt, SBAS_L5_MT_NAMES, SBAS_L5_PREAMBLE_UW, type SbasMessageCb, type SbfBdsNavResult, type SbfCnavEphemeris, type SbfCnavResult, type SbfGalEphemeris, type SbfGalNavResult, type SbfGeoL5Result, type SbfGeoNavResult, type SbfGloNavResult, type SbfGpsNavResult, type SbfHasMessage, type SbfHasResult, type SbfIonoUtcResult, type SbfMeasEpoch, type SbfMeasurement, type SbfNavCounts, type SbfNavicResult, type SbfNavigation, type SbfParseResult, type SbfPvt, type SbfPvtResult, type SbfReceiverSetup, decodeSbfNavigation, extractSbfNavicSubframe, feedGeoBlock, feedGeoL5Block, feedNavicBlock, isSbasL1Preamble, isSbasL5Correction, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGeoL5, parseSbfGeoNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfNavic, parseSbfPvt, parseSbfReceiverSetup, sbasL5MessageName, sbasL5MessageType, crc16 as sbfCrc16, scanSbfFrames };
package/dist/sbf.d.ts CHANGED
@@ -174,6 +174,65 @@ interface SbfGpsNavResult {
174
174
  */
175
175
  declare function parseSbfGpsNav(data: Uint8Array): SbfGpsNavResult;
176
176
 
177
+ /**
178
+ * Streaming assembler: feed 292-bit NavIC subframes per PRN; emits a fresh
179
+ * ephemeris once a satellite's subframe 1 and 2 are both in hand (deduped by
180
+ * toc + IODEC). Mirrors {@link GpsLnavAssembler} for the raw-frame formats.
181
+ */
182
+ declare class NavicAssembler {
183
+ private readonly sf1;
184
+ private readonly last;
185
+ /**
186
+ * Push one CRC-valid subframe. Subframe 1 opens a master frame (subframe 2
187
+ * immediately follows it in the NavIC frame order), so an ephemeris is only
188
+ * emitted when a subframe 2 pairs with the subframe 1 that opened the *same*
189
+ * frame — never a stale subframe 1 left over across CRC drops.
190
+ */
191
+ push(prn: string, subframe: Uint8Array, refWeek: number): KeplerEphemeris | null;
192
+ }
193
+
194
+ /**
195
+ * Septentrio SBF NAVICRaw (4093) blocks: each carries one 292-bit NavIC L5/S
196
+ * SPS subframe. Subframes 1 & 2 are assembled per satellite into a Keplerian
197
+ * ephemeris by the shared {@link NavicAssembler}.
198
+ *
199
+ * Block layout (mosaic-X5 reference guide §4): after the 8-byte SBF header,
200
+ * TOW u4 + WNc u2 (full GPS week), SVID u1, CRCPassed u1, ViterbiCnt u1,
201
+ * Source u1, FreqNr u1, RxChannel u1, then NAVBits as u4[10] — the first
202
+ * received bit is the MSB of NAVBits[0] (each u4 little-endian in the stream,
203
+ * message bits MSB-first within the word). Only the first 292 bits are used.
204
+ *
205
+ * The receiver's own CRCPassed flag is ignored in favour of re-running CRC-24Q
206
+ * on the transported bits, so `badCrc` counts exactly what this library rejects.
207
+ */
208
+
209
+ interface SbfNavicResult {
210
+ /** Assembled NavIC ephemerides in stream order, repeats suppressed. */
211
+ ephemerides: KeplerEphemeris[];
212
+ /** Subframes whose CRC-24Q check failed (dropped). */
213
+ badCrc: number;
214
+ /** Total NAVICRaw blocks seen (with valid SBF framing). */
215
+ messages: number;
216
+ }
217
+ /** Extract the 292-bit NavIC subframe (MSB-first) from a NAVICRaw block. */
218
+ declare function extractSbfNavicSubframe(view: DataView, b: number): Uint8Array;
219
+ /**
220
+ * Process one NAVICRaw (4093) block at frame offset `b`: map SVID → NavIC PRN,
221
+ * extract + CRC-24Q gate the subframe, and push it to the shared assembler
222
+ * with the block's full WNc week. Returns a fresh ephemeris, `{ badCrc: true }`
223
+ * on a failed CRC, or `{}`. Shared by {@link parseSbfNavic} and the one-pass
224
+ * {@link decodeSbfNavigation}.
225
+ */
226
+ declare function feedNavicBlock(view: DataView, b: number, assembler: NavicAssembler): {
227
+ eph?: KeplerEphemeris;
228
+ badCrc?: boolean;
229
+ };
230
+ /**
231
+ * Decode every NAVICRaw (4093) block in an SBF byte stream and assemble the
232
+ * carried subframes into NavIC ephemerides (`system` 'I').
233
+ */
234
+ declare function parseSbfNavic(data: Uint8Array): SbfNavicResult;
235
+
177
236
  /**
178
237
  * Septentrio SBF raw CNAV navigation-bit blocks: GPSRawL2C (4018) and
179
238
  * GPSRawL5 (4019) each carry one 300-bit GPS CNAV message.
@@ -339,6 +398,8 @@ interface SbfNavCounts {
339
398
  sbasRaw: number;
340
399
  /** Raw SBAS L5 GEO message blocks (GEORawL5) — DO-229-on-L5 + native DFMC. */
341
400
  sbasL5Raw: number;
401
+ /** Raw NavIC L5/S SPS subframe blocks (NAVICRaw). */
402
+ navicRaw: number;
342
403
  /** Frames dropped by a CRC/parity check across all raw decoders. */
343
404
  badFrames: number;
344
405
  }
@@ -610,4 +671,4 @@ interface SbfParseResult {
610
671
  */
611
672
  declare function parseSbfMeas(data: Uint8Array): SbfParseResult;
612
673
 
613
- export { CnavEphemeris, type DfmcCensus, type DfmcMessageCb, type GeoL5Result, HasMessage, ReceiverPvt, SBAS_L5_MT_NAMES, SBAS_L5_PREAMBLE_UW, type SbasMessageCb, type SbfBdsNavResult, type SbfCnavEphemeris, type SbfCnavResult, type SbfGalEphemeris, type SbfGalNavResult, type SbfGeoL5Result, type SbfGeoNavResult, type SbfGloNavResult, type SbfGpsNavResult, type SbfHasMessage, type SbfHasResult, type SbfIonoUtcResult, type SbfMeasEpoch, type SbfMeasurement, type SbfNavCounts, type SbfNavigation, type SbfParseResult, type SbfPvt, type SbfPvtResult, type SbfReceiverSetup, decodeSbfNavigation, feedGeoBlock, feedGeoL5Block, isSbasL1Preamble, isSbasL5Correction, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGeoL5, parseSbfGeoNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfPvt, parseSbfReceiverSetup, sbasL5MessageName, sbasL5MessageType, crc16 as sbfCrc16, scanSbfFrames };
674
+ export { CnavEphemeris, type DfmcCensus, type DfmcMessageCb, type GeoL5Result, HasMessage, ReceiverPvt, SBAS_L5_MT_NAMES, SBAS_L5_PREAMBLE_UW, type SbasMessageCb, type SbfBdsNavResult, type SbfCnavEphemeris, type SbfCnavResult, type SbfGalEphemeris, type SbfGalNavResult, type SbfGeoL5Result, type SbfGeoNavResult, type SbfGloNavResult, type SbfGpsNavResult, type SbfHasMessage, type SbfHasResult, type SbfIonoUtcResult, type SbfMeasEpoch, type SbfMeasurement, type SbfNavCounts, type SbfNavicResult, type SbfNavigation, type SbfParseResult, type SbfPvt, type SbfPvtResult, type SbfReceiverSetup, decodeSbfNavigation, extractSbfNavicSubframe, feedGeoBlock, feedGeoL5Block, feedNavicBlock, isSbasL1Preamble, isSbasL5Correction, parseSbfBdsNav, parseSbfCnav, parseSbfGalNav, parseSbfGeoL5, parseSbfGeoNav, parseSbfGloNav, parseSbfGpsNav, parseSbfHas, parseSbfIonoUtc, parseSbfMeas, parseSbfNavic, parseSbfPvt, parseSbfReceiverSetup, sbasL5MessageName, sbasL5MessageType, crc16 as sbfCrc16, scanSbfFrames };
package/dist/sbf.js CHANGED
@@ -3,8 +3,10 @@ import {
3
3
  SBAS_L5_PREAMBLE_UW,
4
4
  crc16,
5
5
  decodeSbfNavigation,
6
+ extractSbfNavicSubframe,
6
7
  feedGeoBlock,
7
8
  feedGeoL5Block,
9
+ feedNavicBlock,
8
10
  isSbasL1Preamble,
9
11
  isSbasL5Correction,
10
12
  parseSbfAlmanac,
@@ -19,12 +21,13 @@ import {
19
21
  parseSbfIonoUtc,
20
22
  parseSbfMeas,
21
23
  parseSbfNav,
24
+ parseSbfNavic,
22
25
  parseSbfPvt,
23
26
  parseSbfReceiverSetup,
24
27
  sbasL5MessageName,
25
28
  sbasL5MessageType,
26
29
  scanSbfFrames
27
- } from "./chunk-WW3DKCDN.js";
30
+ } from "./chunk-74IVYOQV.js";
28
31
  import "./chunk-3KSDL3CO.js";
29
32
  import "./chunk-WQX354KD.js";
30
33
  import "./chunk-HVXYFUCB.js";
@@ -37,8 +40,10 @@ export {
37
40
  SBAS_L5_MT_NAMES,
38
41
  SBAS_L5_PREAMBLE_UW,
39
42
  decodeSbfNavigation,
43
+ extractSbfNavicSubframe,
40
44
  feedGeoBlock,
41
45
  feedGeoL5Block,
46
+ feedNavicBlock,
42
47
  isSbasL1Preamble,
43
48
  isSbasL5Correction,
44
49
  parseSbfAlmanac,
@@ -53,6 +58,7 @@ export {
53
58
  parseSbfIonoUtc,
54
59
  parseSbfMeas,
55
60
  parseSbfNav,
61
+ parseSbfNavic,
56
62
  parseSbfPvt,
57
63
  parseSbfReceiverSetup,
58
64
  sbasL5MessageName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "Comprehensive GNSS library for JavaScript — time scales, coordinates, RINEX parsing, RTCM3 decoding, orbit computation, and signal analysis",
5
5
  "type": "module",
6
6
  "sideEffects": false,