gnss-js 1.52.0 → 1.54.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.
@@ -355,8 +355,57 @@ function parseNovatelNav(data, opts = {}) {
355
355
  };
356
356
  }
357
357
 
358
- // src/novatel/index.ts
358
+ // src/novatel/pvt.ts
359
359
  var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
360
+ var BESTPOS = 42;
361
+ function posTypeToMode(t) {
362
+ if (t === 0) return "no-pvt";
363
+ if (t === 16) return "standalone";
364
+ if (t === 17) return "differential";
365
+ if (t === 18 || t === 20) return "sbas-aided";
366
+ if (t === 32 || t === 33 || t === 34) return "rtk-float";
367
+ if (t === 48 || t === 50) return "rtk-fixed";
368
+ if (t >= 68 && t <= 78) return "ppp";
369
+ return `mode-${t}`;
370
+ }
371
+ function parseNovatelPvt(data) {
372
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
373
+ const records = [];
374
+ let messages = 0;
375
+ const stats = { badCrc: 0 };
376
+ for (const f of oem4Frames(data, view, stats)) {
377
+ if (f.id !== BESTPOS || !f.binary || f.msgLen < 72) continue;
378
+ messages++;
379
+ const p = f.payload;
380
+ const posType = view.getUint32(p + 4, true);
381
+ const lat = view.getFloat64(p + 8, true);
382
+ const lon = view.getFloat64(p + 16, true);
383
+ const hgtMsl = view.getFloat64(p + 24, true);
384
+ const undulation = view.getFloat32(p + 32, true);
385
+ const latSig = view.getFloat32(p + 40, true);
386
+ const lonSig = view.getFloat32(p + 44, true);
387
+ const hgtSig = view.getFloat32(p + 48, true);
388
+ const nrSV = view.getUint8(p + 65);
389
+ const hasFix = posType !== 0 && Number.isFinite(lat) && Number.isFinite(lon);
390
+ records.push({
391
+ timeMs: GPS_EPOCH_MS2 + f.week * 6048e5 + f.towMs,
392
+ week: f.week,
393
+ tow: f.towMs / 1e3,
394
+ mode: posTypeToMode(posType),
395
+ latDeg: hasFix ? lat : null,
396
+ lonDeg: hasFix ? lon : null,
397
+ // BESTPOS height is above MSL; ellipsoidal = MSL + geoid undulation.
398
+ heightM: hasFix ? hgtMsl + undulation : null,
399
+ nrSV,
400
+ hAccuracyM: hasFix ? Math.hypot(latSig, lonSig) : null,
401
+ vAccuracyM: hasFix ? hgtSig : null
402
+ });
403
+ }
404
+ return { records, messages };
405
+ }
406
+
407
+ // src/novatel/index.ts
408
+ var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
360
409
  var MS_PER_WEEK2 = 7 * 864e5;
361
410
  var HDR = 28;
362
411
  var ID_RANGE = 43;
@@ -526,7 +575,7 @@ function parseNovatelRange(data) {
526
575
  if (!codes.includes(code)) codes.push(code);
527
576
  }
528
577
  epochs.push({
529
- timeMs: GPS_EPOCH_MS2 + week * MS_PER_WEEK2 + towMs,
578
+ timeMs: GPS_EPOCH_MS3 + week * MS_PER_WEEK2 + towMs,
530
579
  meas
531
580
  });
532
581
  }
@@ -578,7 +627,7 @@ function parseNovatelRange(data) {
578
627
  if (!codes.includes(code)) codes.push(code);
579
628
  }
580
629
  epochs.push({
581
- timeMs: GPS_EPOCH_MS2 + week * MS_PER_WEEK2 + towMs,
630
+ timeMs: GPS_EPOCH_MS3 + week * MS_PER_WEEK2 + towMs,
582
631
  meas
583
632
  });
584
633
  }
@@ -593,5 +642,6 @@ export {
593
642
  crc32,
594
643
  oem4Frames,
595
644
  parseNovatelNav,
645
+ parseNovatelPvt,
596
646
  parseNovatelRange
597
647
  };
@@ -306,8 +306,59 @@ function parseUbxRawNav(data, opts = {}) {
306
306
  return { ephemerides, counts, badParity };
307
307
  }
308
308
 
309
- // src/ubx/index.ts
309
+ // src/ubx/pvt.ts
310
310
  var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
311
+ function parseUbxPvt(data) {
312
+ const records = [];
313
+ let messages = 0;
314
+ for (const f of ubxFrames(data)) {
315
+ if (f.msgClass !== 1 || f.msgId !== 7 || f.payload.length < 92)
316
+ continue;
317
+ messages++;
318
+ const v = new DataView(
319
+ f.payload.buffer,
320
+ f.payload.byteOffset,
321
+ f.payload.byteLength
322
+ );
323
+ const iTow = v.getUint32(0, true);
324
+ const year = v.getUint16(4, true);
325
+ const month = v.getUint8(6);
326
+ const day = v.getUint8(7);
327
+ const hour = v.getUint8(8);
328
+ const min = v.getUint8(9);
329
+ const sec = v.getUint8(10);
330
+ const fixType = v.getUint8(20);
331
+ const flags = v.getUint8(21);
332
+ const numSV = v.getUint8(23);
333
+ const lon = v.getInt32(24, true) * 1e-7;
334
+ const lat = v.getInt32(28, true) * 1e-7;
335
+ const height = v.getInt32(32, true) / 1e3;
336
+ const hAcc = v.getUint32(40, true) / 1e3;
337
+ const vAcc = v.getUint32(44, true) / 1e3;
338
+ const carrSoln = flags >> 6 & 3;
339
+ const diffSoln = flags >> 1 & 1;
340
+ const mode = carrSoln === 2 ? "rtk-fixed" : carrSoln === 1 ? "rtk-float" : diffSoln ? "differential" : fixType >= 2 ? "standalone" : "no-pvt";
341
+ const hasFix = fixType >= 2;
342
+ const timeMs = year > 1980 ? Date.UTC(year, month - 1, day, hour, min, sec) : GPS_EPOCH_MS2 + iTow;
343
+ const week = Math.floor((timeMs - GPS_EPOCH_MS2) / 1e3 / 604800);
344
+ records.push({
345
+ timeMs,
346
+ week,
347
+ tow: iTow / 1e3,
348
+ mode,
349
+ latDeg: hasFix ? lat : null,
350
+ lonDeg: hasFix ? lon : null,
351
+ heightM: hasFix ? height : null,
352
+ nrSV: numSV,
353
+ hAccuracyM: hasFix ? hAcc : null,
354
+ vAccuracyM: hasFix ? vAcc : null
355
+ });
356
+ }
357
+ return { records, messages };
358
+ }
359
+
360
+ // src/ubx/index.ts
361
+ var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
311
362
  var MS_PER_WEEK = 7 * 864e5;
312
363
  var SIGNALS = {
313
364
  0: { 0: ["G", "1C"], 3: ["G", "2X"], 4: ["G", "2X"] },
@@ -390,7 +441,7 @@ function parseUbxRawx(data) {
390
441
  if (!codes.includes(sig[1])) codes.push(sig[1]);
391
442
  }
392
443
  epochs.push({
393
- timeMs: GPS_EPOCH_MS2 + week * MS_PER_WEEK + Math.round(rcvTow * 1e3),
444
+ timeMs: GPS_EPOCH_MS3 + week * MS_PER_WEEK + Math.round(rcvTow * 1e3),
394
445
  leapS: leapValid ? leapS : null,
395
446
  meas
396
447
  });
@@ -405,5 +456,6 @@ export {
405
456
  parseUbxIonoUtc,
406
457
  parseUbxCnav,
407
458
  parseUbxRawNav,
459
+ parseUbxPvt,
408
460
  parseUbxRawx
409
461
  };
@@ -486,6 +486,58 @@ function parseSbfReceiverSetup(data) {
486
486
  return out;
487
487
  }
488
488
 
489
+ // src/sbf/pvt.ts
490
+ var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
491
+ var RAD2DEG = 180 / Math.PI;
492
+ var DNU_F8 = -2e10;
493
+ var MODE_LABELS = {
494
+ 0: "no-pvt",
495
+ 1: "standalone",
496
+ 2: "differential",
497
+ 3: "fixed-location",
498
+ 4: "rtk-fixed",
499
+ 5: "rtk-float",
500
+ 6: "sbas-aided",
501
+ 7: "moving-base-rtk-fixed",
502
+ 8: "moving-base-rtk-float",
503
+ 10: "ppp"
504
+ };
505
+ function parseSbfPvt(data) {
506
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
507
+ const records = [];
508
+ let messages = 0;
509
+ scanSbfFrames(data, view, (id, b, len) => {
510
+ if (id !== 4007 || len < 96) return;
511
+ messages++;
512
+ const tow = view.getUint32(b + 8, true);
513
+ const wnc = view.getUint16(b + 12, true);
514
+ if (tow === 4294967295 || wnc === 65535) return;
515
+ const modeByte = view.getUint8(b + 14);
516
+ const modeType = modeByte & 15;
517
+ const lat = view.getFloat64(b + 16, true);
518
+ const lon = view.getFloat64(b + 24, true);
519
+ const hgt = view.getFloat64(b + 32, true);
520
+ const nrSV = view.getUint8(b + 74);
521
+ const hAcc = view.getUint16(b + 90, true);
522
+ const vAcc = view.getUint16(b + 92, true);
523
+ const hasFix = lat > DNU_F8 && lon > DNU_F8 && hgt > DNU_F8;
524
+ records.push({
525
+ timeMs: GPS_EPOCH_MS2 + wnc * 6048e5 + tow,
526
+ week: wnc,
527
+ tow: tow / 1e3,
528
+ modeType,
529
+ mode: MODE_LABELS[modeType] ?? `mode-${modeType}`,
530
+ latDeg: hasFix ? lat * RAD2DEG : null,
531
+ lonDeg: hasFix ? lon * RAD2DEG : null,
532
+ heightM: hasFix ? hgt : null,
533
+ nrSV: nrSV === 255 ? null : nrSV,
534
+ hAccuracyM: hAcc === 65535 ? null : hAcc / 100,
535
+ vAccuracyM: vAcc === 65535 ? null : vAcc / 100
536
+ });
537
+ });
538
+ return { records, messages };
539
+ }
540
+
489
541
  // src/sbf/rawnav-gps.ts
490
542
  var WNC_DNU = 65535;
491
543
  function extractSbfLnavSubframe(view, b) {
@@ -621,7 +673,7 @@ function isFnavDummy(page) {
621
673
  }
622
674
 
623
675
  // src/sbf/rawnav-bds.ts
624
- var GPS_EPOCH_MS2 = Date.UTC(1980, 0, 6);
676
+ var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
625
677
  var MS_PER_WEEK2 = 7 * 86400 * 1e3;
626
678
  var TOW_DNU = 4294967295;
627
679
  var WNC_DNU2 = 65535;
@@ -682,7 +734,7 @@ function feedGloBlock(view, b, assembler) {
682
734
  const eph = assembler.push(
683
735
  prn,
684
736
  str2,
685
- new Date(GPS_EPOCH_MS2 + wnc * MS_PER_WEEK2 + towMs),
737
+ new Date(GPS_EPOCH_MS3 + wnc * MS_PER_WEEK2 + towMs),
686
738
  view.getUint8(b + 18) - 8
687
739
  // FreqNr, offset by 8
688
740
  );
@@ -1432,7 +1484,7 @@ function parseSbfHas(data) {
1432
1484
 
1433
1485
  // src/sbf/index.ts
1434
1486
  var CLIGHT = 299792458;
1435
- var GPS_EPOCH_MS3 = Date.UTC(1980, 0, 6);
1487
+ var GPS_EPOCH_MS4 = Date.UTC(1980, 0, 6);
1436
1488
  var MS_PER_WEEK3 = 7 * 864e5;
1437
1489
  var M3_SYS = ["G", "R", "E", "C", "S", "J", "I"];
1438
1490
  var M3_PR_BASE = [19e6, 19e6, 22e6, 2e7, 34e6, 34e6, 34e6];
@@ -2086,7 +2138,7 @@ function parseSbfMeas(data) {
2086
2138
  const tow = view.getUint32(i + 8, true);
2087
2139
  const wnc = view.getUint16(i + 12, true);
2088
2140
  if (tow !== 4294967295 && wnc !== 65535) {
2089
- ensureEpoch(GPS_EPOCH_MS3 + wnc * MS_PER_WEEK3 + tow);
2141
+ ensureEpoch(GPS_EPOCH_MS4 + wnc * MS_PER_WEEK3 + tow);
2090
2142
  if (id === 4109) decodeMeas3Ranges(i, tow);
2091
2143
  else if (id === 4110) decodeMeas3CN0(i, len);
2092
2144
  else if (id === 4111) decodeMeas3Doppler(i, len);
@@ -2105,6 +2157,7 @@ export {
2105
2157
  parseSbfAlmanac,
2106
2158
  parseSbfIonoUtc,
2107
2159
  parseSbfReceiverSetup,
2160
+ parseSbfPvt,
2108
2161
  parseSbfGpsNav,
2109
2162
  parseSbfCnav,
2110
2163
  parseSbfGalNav,