gnss-js 1.18.0 → 1.20.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.
Files changed (43) hide show
  1. package/dist/{chunk-2IM5VXNI.js → chunk-7RKLWE6R.js} +23 -2
  2. package/dist/{chunk-QXZZNXE3.js → chunk-B5LU4746.js} +1 -1
  3. package/dist/{chunk-FEGLXNWA.js → chunk-DLXTTHRK.js} +2 -4
  4. package/dist/{chunk-OMVL3X7V.js → chunk-JNFV5BYB.js} +186 -8
  5. package/dist/{chunk-HXF7XVW7.js → chunk-NOQAAUGY.js} +297 -11
  6. package/dist/{chunk-CG7SF45F.js → chunk-VJEPOC76.js} +908 -14
  7. package/dist/{chunk-Y5IWUPJQ.js → chunk-WZDFZ76K.js} +151 -8
  8. package/dist/index.cjs +3283 -1908
  9. package/dist/index.d.cts +5 -5
  10. package/dist/index.d.ts +5 -5
  11. package/dist/index.js +25 -8
  12. package/dist/nav-BYmYoh9A.d.cts +137 -0
  13. package/dist/nav-CN8rxL4h.d.ts +137 -0
  14. package/dist/nav-DImXUdbp.d.cts +223 -0
  15. package/dist/nav-DImXUdbp.d.ts +223 -0
  16. package/dist/novatel.cjs +230 -15
  17. package/dist/novatel.d.cts +8 -1
  18. package/dist/novatel.d.ts +8 -1
  19. package/dist/novatel.js +2 -2
  20. package/dist/orbit.cjs +190 -8
  21. package/dist/orbit.d.cts +91 -2
  22. package/dist/orbit.d.ts +91 -2
  23. package/dist/orbit.js +9 -1
  24. package/dist/positioning.d.cts +2 -1
  25. package/dist/positioning.d.ts +2 -1
  26. package/dist/positioning.js +2 -2
  27. package/dist/rinex.cjs +304 -11
  28. package/dist/rinex.d.cts +45 -3
  29. package/dist/rinex.d.ts +45 -3
  30. package/dist/rinex.js +6 -1
  31. package/dist/sbf.cjs +908 -11
  32. package/dist/sbf.d.cts +114 -137
  33. package/dist/sbf.d.ts +114 -137
  34. package/dist/sbf.js +8 -3
  35. package/dist/ubx.d.cts +1 -2
  36. package/dist/ubx.d.ts +1 -2
  37. package/dist/ubx.js +2 -3
  38. package/package.json +1 -1
  39. package/dist/chunk-IQ5OIMQD.js +0 -148
  40. package/dist/cnav-CRns0ECn.d.cts +0 -96
  41. package/dist/cnav-CRns0ECn.d.ts +0 -96
  42. package/dist/nav-BAI1a9vK.d.cts +0 -108
  43. package/dist/nav-BAI1a9vK.d.ts +0 -108
@@ -1,3 +1,9 @@
1
+ import {
2
+ CNAV_A_REF,
3
+ CNAV_OMEGA_DOT_REF,
4
+ GPS_PI
5
+ } from "./chunk-WZDFZ76K.js";
6
+
1
7
  // src/rinex/format.ts
2
8
  function padL(s, w) {
3
9
  return s.length >= w ? s.slice(0, w) : s + " ".repeat(w - s.length);
@@ -212,19 +218,19 @@ var R4_MSG_LINES = {
212
218
  SBAS: 4,
213
219
  FDMA: 5,
214
220
  // RINEX 4 adds orbit-4 (status flags) vs 3 in RINEX 3
215
- // EPH types we skip (different field layout)
221
+ // EPH type parsed into RinexCnavEphemeris for G/J (skipped otherwise)
216
222
  CNAV: 9,
223
+ // EPH types we skip (different field layout)
217
224
  CNV1: 10,
218
225
  CNV2: 10,
219
226
  CNV3: 9,
220
227
  L1NV: 8,
221
228
  L1OC: 9,
222
- L3OC: 9,
223
- // Non-EPH records
224
- STO: 2,
225
- EOP: 3,
226
- ION: 3
229
+ L3OC: 9
227
230
  };
231
+ var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
232
+ var SEC_PER_WEEK = 7 * 86400;
233
+ var QZSS_CNAV_A_REF = 42164200;
228
234
  var SUPPORTED_EPH_MSGS = /* @__PURE__ */ new Set([
229
235
  "LNAV",
230
236
  "INAV",
@@ -288,6 +294,20 @@ function parseDataLine(line, colOffset = 4) {
288
294
  }
289
295
  return values;
290
296
  }
297
+ function parseDataLineSlots(line) {
298
+ const values = [];
299
+ for (let i = 0; i < 4; i++) {
300
+ const start = 4 + i * 19;
301
+ const s = start < line.length ? line.substring(start, start + 19).trim() : "";
302
+ if (s.length === 0) {
303
+ values.push(null);
304
+ continue;
305
+ }
306
+ const v = parseFloat19(s);
307
+ values.push(Number.isFinite(v) ? v : null);
308
+ }
309
+ return values;
310
+ }
291
311
  function buildKeplerEphemeris(sys, prn, date, epochVals, data) {
292
312
  const d = [];
293
313
  for (const line of data) {
@@ -350,6 +370,68 @@ function buildStateVectorEphemeris(sys, prn, date, epochVals, data) {
350
370
  zAcc: d[10] ?? 0
351
371
  };
352
372
  }
373
+ function buildCnavEphemeris(sys, prn, date, epochVals, d) {
374
+ const num = (r, c) => d[r]?.[c] ?? 0;
375
+ const opt = (r, c) => d[r]?.[c] ?? null;
376
+ const t = (date.getTime() - GPS_EPOCH_MS) / 1e3;
377
+ const week = Math.floor(t / SEC_PER_WEEK);
378
+ const toc = t - week * SEC_PER_WEEK;
379
+ const sqrtA = num(1, 3);
380
+ const a = sqrtA * sqrtA;
381
+ const aRef = sys === "J" ? QZSS_CNAV_A_REF : CNAV_A_REF;
382
+ const omegaDot = num(3, 3);
383
+ const wnOp = d[7]?.[1];
384
+ return {
385
+ system: sys,
386
+ prn,
387
+ week,
388
+ health: num(5, 1),
389
+ uraEd: num(5, 0),
390
+ uraNed0: num(4, 2),
391
+ uraNed1: num(4, 3),
392
+ uraNed2: num(5, 3),
393
+ top: num(2, 0),
394
+ toe: toc,
395
+ toeDate: date,
396
+ a,
397
+ deltaA: a - aRef,
398
+ aDot: num(0, 0),
399
+ deltaN0: num(0, 2),
400
+ deltaN0Dot: num(4, 1),
401
+ m0: num(0, 3),
402
+ e: num(1, 1),
403
+ omega: num(3, 2),
404
+ omega0: num(2, 2),
405
+ i0: num(3, 0),
406
+ omegaDot,
407
+ deltaOmegaDot: omegaDot - CNAV_OMEGA_DOT_REF * GPS_PI,
408
+ i0Dot: num(4, 0),
409
+ cis: num(2, 3),
410
+ cic: num(2, 1),
411
+ crs: num(0, 1),
412
+ crc: num(3, 1),
413
+ cus: num(1, 2),
414
+ cuc: num(1, 0),
415
+ toc,
416
+ tocDate: date,
417
+ af0: epochVals[0] ?? 0,
418
+ af1: epochVals[1] ?? 0,
419
+ af2: epochVals[2] ?? 0,
420
+ clockMsgType: 0,
421
+ // not recorded in RINEX
422
+ tgd: opt(5, 2),
423
+ iscL1ca: opt(6, 0),
424
+ iscL2c: opt(6, 1),
425
+ iscL5i5: opt(6, 2),
426
+ iscL5q5: opt(6, 3),
427
+ ...wnOp != null && { wnOp },
428
+ integrityFlag: false,
429
+ // not recorded in RINEX
430
+ l2cPhasing: false,
431
+ // not recorded in RINEX
432
+ tow: num(7, 0)
433
+ };
434
+ }
353
435
  function parseNavFile(text) {
354
436
  const lines = text.split("\n");
355
437
  const header = {
@@ -359,6 +441,7 @@ function parseNavFile(text) {
359
441
  ionoCorrections: {}
360
442
  };
361
443
  const ephemerides = [];
444
+ const cnav = [];
362
445
  let inHeader = true;
363
446
  let i = 0;
364
447
  while (i < lines.length) {
@@ -419,8 +502,15 @@ function parseNavFile(text) {
419
502
  if (isV4 && line.charAt(0) === ">") {
420
503
  const parts = line.substring(2).trim().split(/\s+/);
421
504
  const recType = parts[0] ?? "";
505
+ const svSys = (parts[1] ?? "").charAt(0);
422
506
  const msgType = parts[2] ?? recType;
423
- if (recType !== "EPH" || !SUPPORTED_EPH_MSGS.has(msgType)) {
507
+ if (recType !== "EPH") {
508
+ const totalLines = recType === "STO" ? 2 : recType === "EOP" ? 3 : recType === "ION" ? svSys === "E" ? 2 : 3 : 2;
509
+ i += totalLines + 1;
510
+ continue;
511
+ }
512
+ const isCnav = msgType === "CNAV" && (svSys === "G" || svSys === "J");
513
+ if (!SUPPORTED_EPH_MSGS.has(msgType) && !isCnav) {
424
514
  const totalLines = R4_MSG_LINES[msgType] ?? 2;
425
515
  i += totalLines + 1;
426
516
  continue;
@@ -432,6 +522,21 @@ function parseNavFile(text) {
432
522
  const sys2 = epochLine.charAt(0);
433
523
  const parsed = parseNavEpochV3(epochLine);
434
524
  const prn2 = `${sys2}${parsed.prn.substring(1)}`;
525
+ if (isCnav) {
526
+ const slotLines = [];
527
+ for (let j = 0; j < numDataLines2; j++) {
528
+ i++;
529
+ if (i >= lines.length) break;
530
+ slotLines.push(parseDataLineSlots(lines[i]));
531
+ }
532
+ if (slotLines.length === numDataLines2 && (sys2 === "G" || sys2 === "J")) {
533
+ cnav.push(
534
+ buildCnavEphemeris(sys2, prn2, parsed.date, parsed.values, slotLines)
535
+ );
536
+ }
537
+ i++;
538
+ continue;
539
+ }
435
540
  const dataLines2 = [];
436
541
  for (let j = 0; j < numDataLines2; j++) {
437
542
  i++;
@@ -505,7 +610,7 @@ function parseNavFile(text) {
505
610
  }
506
611
  i++;
507
612
  }
508
- return { header, ephemerides };
613
+ return cnav.length > 0 ? { header, ephemerides, cnav } : { header, ephemerides };
509
614
  }
510
615
 
511
616
  // src/rinex/nav-writer.ts
@@ -513,9 +618,13 @@ function fmtD(val) {
513
618
  if (val === 0) return " 0.000000000000E+00";
514
619
  const sign = val < 0 ? "-" : " ";
515
620
  const abs = Math.abs(val);
516
- const exp = Math.floor(Math.log10(abs));
621
+ let exp = Math.floor(Math.log10(abs));
517
622
  const mantissa = abs / 10 ** exp;
518
- const mStr = mantissa.toFixed(12);
623
+ let mStr = mantissa.toFixed(12);
624
+ if (mStr.length > 14) {
625
+ exp += 1;
626
+ mStr = (mantissa / 10).toFixed(12);
627
+ }
519
628
  const expSign = exp >= 0 ? "+" : "-";
520
629
  const expStr = String(Math.abs(exp)).padStart(2, "0");
521
630
  return `${sign}${mStr}E${expSign}${expStr}`;
@@ -590,7 +699,7 @@ function writeKeplerRecord(eph) {
590
699
  function writeGlonassRecord(eph) {
591
700
  const lines = [];
592
701
  lines.push(
593
- `${padL(eph.prn, 3)} ${fmtEpoch(eph.tocDate)}${fmtD(-eph.tauN)}${fmtD(eph.gammaN)}${fmtD(eph.messageFrameTime)}`
702
+ `${padL(eph.prn, 3)} ${fmtEpoch(eph.tocDate)}${fmtD(eph.tauN)}${fmtD(eph.gammaN)}${fmtD(eph.messageFrameTime)}`
594
703
  );
595
704
  lines.push(orbitLine([eph.x, eph.xDot, eph.xAcc, eph.health]));
596
705
  lines.push(orbitLine([eph.y, eph.yDot, eph.yAcc, eph.freqNum]));
@@ -610,6 +719,181 @@ function writeRinexNav(nav) {
610
719
  return header + records.join("\n") + "\n";
611
720
  }
612
721
 
722
+ // src/rinex/nav-writer-v4.ts
723
+ var BLANK19 = " ".repeat(19);
724
+ function fmtEpoch4(d) {
725
+ const p2 = (v) => String(v).padStart(2, "0");
726
+ return `${d.getUTCFullYear()} ${p2(d.getUTCMonth() + 1)} ${p2(d.getUTCDate())} ${p2(d.getUTCHours())} ${p2(d.getUTCMinutes())} ${p2(d.getUTCSeconds())}`;
727
+ }
728
+ function orbitLine4(values) {
729
+ const line = " " + values.map((v) => v == null ? BLANK19 : fmtD(v)).join("");
730
+ return line.replace(/ +$/, "");
731
+ }
732
+ function epochLine4(prn, date, vals) {
733
+ return `${padL(prn, 3)} ${fmtEpoch4(date)}${vals.map(fmtD).join("")}`;
734
+ }
735
+ var BDS_GEO = /* @__PURE__ */ new Set([
736
+ "C01",
737
+ "C02",
738
+ "C03",
739
+ "C04",
740
+ "C05",
741
+ "C59",
742
+ "C60",
743
+ "C61",
744
+ "C62",
745
+ "C63"
746
+ ]);
747
+ function msgLabel(eph) {
748
+ switch (eph.system) {
749
+ case "E":
750
+ return "INAV";
751
+ case "C":
752
+ return BDS_GEO.has(eph.prn) ? "D2" : "D1";
753
+ case "R":
754
+ return "FDMA";
755
+ case "S":
756
+ return "SBAS";
757
+ default:
758
+ return "LNAV";
759
+ }
760
+ }
761
+ function isKepler2(eph) {
762
+ return "af0" in eph;
763
+ }
764
+ function keplerRecord(eph) {
765
+ return [
766
+ epochLine4(eph.prn, eph.tocDate, [eph.af0, eph.af1, eph.af2]),
767
+ orbitLine4([eph.iode, eph.crs, eph.deltaN, eph.m0]),
768
+ orbitLine4([eph.cuc, eph.e, eph.cus, eph.sqrtA]),
769
+ orbitLine4([eph.toe, eph.cic, eph.omega0, eph.cis]),
770
+ orbitLine4([eph.i0, eph.crc, eph.omega, eph.omegaDot]),
771
+ orbitLine4([eph.idot, 0, eph.week, 0]),
772
+ orbitLine4([0, eph.svHealth, eph.tgd, 0]),
773
+ orbitLine4([eph.toe, 0, 0, 0])
774
+ ].join("\n");
775
+ }
776
+ function stateVectorRecord(eph) {
777
+ const lines = [
778
+ epochLine4(eph.prn, eph.tocDate, [
779
+ eph.tauN,
780
+ eph.gammaN,
781
+ eph.messageFrameTime
782
+ ]),
783
+ orbitLine4([eph.x, eph.xDot, eph.xAcc, eph.health]),
784
+ orbitLine4([eph.y, eph.yDot, eph.yAcc, eph.freqNum]),
785
+ orbitLine4([eph.z, eph.zDot, eph.zAcc, 0])
786
+ ];
787
+ if (eph.system === "R") {
788
+ lines.push(orbitLine4([null, 999999999999e-3, 15, null]));
789
+ }
790
+ return lines.join("\n");
791
+ }
792
+ function resolveWnOp(week, wnOp) {
793
+ if (wnOp == null) return week;
794
+ return week - ((week - wnOp) % 256 + 256) % 256;
795
+ }
796
+ function cnavRecord(eph) {
797
+ return [
798
+ epochLine4(eph.prn, eph.tocDate, [eph.af0, eph.af1, eph.af2]),
799
+ orbitLine4([eph.aDot, eph.crs, eph.deltaN0, eph.m0]),
800
+ orbitLine4([eph.cuc, eph.e, eph.cus, Math.sqrt(eph.a)]),
801
+ orbitLine4([eph.top, eph.cic, eph.omega0, eph.cis]),
802
+ orbitLine4([eph.i0, eph.crc, eph.omega, eph.omegaDot]),
803
+ orbitLine4([eph.i0Dot, eph.deltaN0Dot, eph.uraNed0, eph.uraNed1]),
804
+ orbitLine4([eph.uraEd, eph.health, eph.tgd, eph.uraNed2]),
805
+ orbitLine4([eph.iscL1ca, eph.iscL2c, eph.iscL5i5, eph.iscL5q5]),
806
+ orbitLine4([eph.tow, resolveWnOp(eph.week, eph.wnOp)])
807
+ ].join("\n");
808
+ }
809
+ var KLOBUCHAR_SOURCES = [
810
+ ["GPSA", "GPSB", "G", "LNAV"],
811
+ ["QZSA", "QZSB", "J", "LNAV"],
812
+ ["BDSA", "BDSB", "C", "D1D2"],
813
+ ["IRNA", "IRNB", "I", "LNAV"]
814
+ ];
815
+ function ionRecords(header, epoch) {
816
+ const records = [];
817
+ const iono = header.ionoCorrections;
818
+ for (const [alphaKey, betaKey, sys, msg] of KLOBUCHAR_SOURCES) {
819
+ const alpha = iono[alphaKey];
820
+ if (!alpha) continue;
821
+ const beta = iono[betaKey] ?? [0, 0, 0, 0];
822
+ records.push(
823
+ [
824
+ `> ION ${sys} ${msg}`,
825
+ ` ${fmtEpoch4(epoch)}${[alpha[0] ?? 0, alpha[1] ?? 0, alpha[2] ?? 0].map(fmtD).join("")}`,
826
+ orbitLine4([alpha[3] ?? 0, beta[0] ?? 0, beta[1] ?? 0, beta[2] ?? 0]),
827
+ orbitLine4([beta[3] ?? 0, null])
828
+ ].join("\n")
829
+ );
830
+ }
831
+ const gal = iono["GAL"];
832
+ if (gal) {
833
+ records.push(
834
+ [
835
+ "> ION E IFNV",
836
+ ` ${fmtEpoch4(epoch)}${[gal[0] ?? 0, gal[1] ?? 0, gal[2] ?? 0].map(fmtD).join("")}`,
837
+ orbitLine4([gal[3] ?? 0])
838
+ ].join("\n")
839
+ );
840
+ }
841
+ return records;
842
+ }
843
+ function writeHeader4(header) {
844
+ const lines = [];
845
+ lines.push(
846
+ hdrLine(
847
+ padL(" 4.01", 9) + " " + padL("NAVIGATION DATA", 20) + padL("M", 20),
848
+ "RINEX VERSION / TYPE"
849
+ )
850
+ );
851
+ const now = /* @__PURE__ */ new Date();
852
+ const dateStr = now.getUTCFullYear().toString() + String(now.getUTCMonth() + 1).padStart(2, "0") + String(now.getUTCDate()).padStart(2, "0") + " " + String(now.getUTCHours()).padStart(2, "0") + String(now.getUTCMinutes()).padStart(2, "0") + String(now.getUTCSeconds()).padStart(2, "0") + " UTC";
853
+ lines.push(
854
+ hdrLine(
855
+ padL("GNSSCalc", 20) + padL("", 20) + padL(dateStr, 20),
856
+ "PGM / RUN BY / DATE"
857
+ )
858
+ );
859
+ if (header.leapSeconds != null) {
860
+ lines.push(hdrLine(padR(String(header.leapSeconds), 6), "LEAP SECONDS"));
861
+ }
862
+ lines.push(hdrLine("", "END OF HEADER"));
863
+ return lines.join("\n") + "\n";
864
+ }
865
+ function writeRinexNav4(nav) {
866
+ const records = [];
867
+ for (const eph of nav.ephemerides) {
868
+ const label = msgLabel(eph);
869
+ records.push({
870
+ prn: eph.prn,
871
+ time: eph.tocDate.getTime(),
872
+ label,
873
+ body: `> EPH ${padL(eph.prn, 3)} ${label}
874
+ ` + (isKepler2(eph) ? keplerRecord(eph) : stateVectorRecord(eph))
875
+ });
876
+ }
877
+ for (const eph of nav.cnav ?? []) {
878
+ records.push({
879
+ prn: eph.prn,
880
+ time: eph.tocDate.getTime(),
881
+ label: "CNAV",
882
+ body: `> EPH ${padL(eph.prn, 3)} CNAV
883
+ ` + cnavRecord(eph)
884
+ });
885
+ }
886
+ records.sort(
887
+ (a, b) => a.prn.localeCompare(b.prn) || a.time - b.time || a.label.localeCompare(b.label)
888
+ );
889
+ const bodies = records.map((r) => r.body);
890
+ if (records.length > 0) {
891
+ const earliest = new Date(Math.min(...records.map((r) => r.time)));
892
+ bodies.push(...ionRecords(nav.header, earliest));
893
+ }
894
+ return writeHeader4(nav.header) + (bodies.length > 0 ? bodies.join("\n") + "\n" : "");
895
+ }
896
+
613
897
  // src/rinex/ionex.ts
614
898
  function gridRange(l1, l2, dl) {
615
899
  const out = [];
@@ -1193,7 +1477,9 @@ export {
1193
1477
  EMPTY_WARNINGS,
1194
1478
  WarningAccumulator,
1195
1479
  parseNavFile,
1480
+ fmtD,
1196
1481
  writeRinexNav,
1482
+ writeRinexNav4,
1197
1483
  parseIonex,
1198
1484
  parseSp3,
1199
1485
  sp3Position,