gnss-js 1.8.0 → 1.10.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.
@@ -610,6 +610,91 @@ function writeRinexNav(nav) {
610
610
  return header + records.join("\n") + "\n";
611
611
  }
612
612
 
613
+ // src/rinex/ionex.ts
614
+ function gridRange(l1, l2, dl) {
615
+ const out = [];
616
+ const n = Math.round((l2 - l1) / dl) + 1;
617
+ for (let i = 0; i < n; i++) out.push(l1 + i * dl);
618
+ return out;
619
+ }
620
+ function parseIonex(text) {
621
+ const lines = text.split("\n");
622
+ let exponent = -1;
623
+ let lats = [];
624
+ let lons = [];
625
+ const epochs = [];
626
+ const maps = [];
627
+ let i = 0;
628
+ for (; i < lines.length; i++) {
629
+ const line = lines[i];
630
+ const label = line.slice(60).trim();
631
+ if (label === "EXPONENT") {
632
+ exponent = parseInt(line.slice(0, 60).trim(), 10);
633
+ } else if (label === "LAT1 / LAT2 / DLAT") {
634
+ const [a, b, d] = line.trim().split(/\s+/).map(Number);
635
+ lats = gridRange(a, b, d);
636
+ } else if (label === "LON1 / LON2 / DLON") {
637
+ const [a, b, d] = line.trim().split(/\s+/).map(Number);
638
+ lons = gridRange(a, b, d);
639
+ } else if (label === "END OF HEADER") {
640
+ i++;
641
+ break;
642
+ }
643
+ }
644
+ if (lats.length === 0 || lons.length === 0) {
645
+ throw new Error("IONEX: missing grid definition");
646
+ }
647
+ const scale = Math.pow(10, exponent);
648
+ while (i < lines.length) {
649
+ const line = lines[i];
650
+ const label = line.slice(60).trim();
651
+ if (label === "START OF TEC MAP") {
652
+ const map = new Float32Array(lats.length * lons.length).fill(NaN);
653
+ let epochMs = 0;
654
+ i++;
655
+ while (i < lines.length) {
656
+ const l = lines[i];
657
+ const lab = l.slice(60).trim();
658
+ if (lab === "EPOCH OF CURRENT MAP") {
659
+ const f = l.slice(0, 60).trim().split(/\s+/).map(Number);
660
+ epochMs = Date.UTC(f[0], f[1] - 1, f[2], f[3], f[4], f[5]);
661
+ i++;
662
+ } else if (lab === "LAT/LON1/LON2/DLON/H") {
663
+ const lat = parseFloat(l.slice(2, 8));
664
+ const latIdx = lats.findIndex((v) => Math.abs(v - lat) < 1e-6);
665
+ i++;
666
+ let lonIdx = 0;
667
+ while (lonIdx < lons.length && i < lines.length) {
668
+ const row = lines[i];
669
+ for (let c = 0; c + 5 <= 80 && lonIdx < lons.length; c += 5) {
670
+ const vStr = row.slice(c, c + 5).trim();
671
+ if (vStr === "") continue;
672
+ const v = parseInt(vStr, 10);
673
+ if (latIdx >= 0) {
674
+ map[latIdx * lons.length + lonIdx] = v === 9999 ? NaN : v * scale;
675
+ }
676
+ lonIdx++;
677
+ }
678
+ i++;
679
+ }
680
+ } else if (lab === "END OF TEC MAP") {
681
+ i++;
682
+ break;
683
+ } else {
684
+ i++;
685
+ }
686
+ }
687
+ epochs.push(epochMs);
688
+ maps.push(map);
689
+ } else if (label === "START OF RMS MAP" || label === "END OF FILE") {
690
+ break;
691
+ } else {
692
+ i++;
693
+ }
694
+ }
695
+ return { epochs, lats, lons, maps };
696
+ }
697
+
613
698
  export {
614
699
  padL,
615
700
  padR,
@@ -618,5 +703,6 @@ export {
618
703
  EMPTY_WARNINGS,
619
704
  WarningAccumulator,
620
705
  parseNavFile,
621
- writeRinexNav
706
+ writeRinexNav,
707
+ parseIonex
622
708
  };
@@ -437,15 +437,35 @@ function computeLiveSkyPositions(ephemerides, rxPos, cn0Map) {
437
437
  }
438
438
  return result;
439
439
  }
440
+ function maskRadForAzimuth(maskDeg) {
441
+ if (typeof maskDeg === "number") {
442
+ const m = maskDeg * Math.PI / 180;
443
+ return () => m;
444
+ }
445
+ const n = maskDeg.length;
446
+ if (n === 0) return () => 0;
447
+ return (azRad) => {
448
+ const azDeg = (azRad * 180 / Math.PI % 360 + 360) % 360;
449
+ const pos = azDeg / 360 * n;
450
+ const i = Math.floor(pos) % n;
451
+ const f = pos - Math.floor(pos);
452
+ const a = maskDeg[i];
453
+ const b = maskDeg[(i + 1) % n];
454
+ return ((1 - f) * a + f * b) * Math.PI / 180;
455
+ };
456
+ }
440
457
  function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, elevationMaskDeg = 10) {
441
458
  const stepMs = stepSec * 1e3;
442
459
  const times = [];
443
460
  for (let t = startMs; t <= endMs; t += stepMs) times.push(t);
444
- const maskRad = elevationMaskDeg * Math.PI / 180;
461
+ const maskRadFor = maskRadForAzimuth(elevationMaskDeg);
445
462
  const all = computeAllPositions(ephemerides, times, rxPos);
446
463
  const elevation = {};
447
464
  const azimuth = {};
465
+ const subLat = {};
466
+ const subLon = {};
448
467
  const visibleCount = new Array(times.length).fill(0);
468
+ const visibleBySystem = {};
449
469
  const pdop = new Array(times.length).fill(null);
450
470
  const gdop = new Array(times.length).fill(null);
451
471
  const hdop = new Array(times.length).fill(null);
@@ -455,10 +475,14 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
455
475
  const series = all.positions[prn];
456
476
  elevation[prn] = series.map((p) => p ? p.el : null);
457
477
  azimuth[prn] = series.map((p) => p ? p.az : null);
478
+ subLat[prn] = series.map((p) => p ? p.lat : null);
479
+ subLon[prn] = series.map((p) => p ? p.lon : null);
458
480
  for (let i = 0; i < series.length; i++) {
459
481
  const p = series[i];
460
- if (p && p.el >= maskRad) {
482
+ if (p && p.el >= maskRadFor(p.az)) {
461
483
  visibleCount[i]++;
484
+ const sys = prn[0];
485
+ (visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
462
486
  visiblePerEpoch[i].push({ az: p.az, el: p.el });
463
487
  }
464
488
  }
@@ -478,7 +502,8 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
478
502
  let peakTime = 0;
479
503
  for (let i = 0; i <= els.length; i++) {
480
504
  const el = i < els.length ? els[i] : null;
481
- const above = el !== null && el !== void 0 && el >= maskRad;
505
+ const az = all.positions[prn][i]?.az ?? 0;
506
+ const above = el !== null && el !== void 0 && el >= maskRadFor(az);
482
507
  if (above) {
483
508
  if (start === -1) {
484
509
  start = i;
@@ -506,7 +531,10 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
506
531
  times,
507
532
  elevation,
508
533
  azimuth,
534
+ subLat,
535
+ subLon,
509
536
  visibleCount,
537
+ visibleBySystem,
510
538
  pdop,
511
539
  gdop,
512
540
  hdop,
@@ -526,5 +554,6 @@ export {
526
554
  computeAllPositions,
527
555
  ephInfoToEphemeris,
528
556
  computeLiveSkyPositions,
557
+ maskRadForAzimuth,
529
558
  computeVisibility
530
559
  };
@@ -2,7 +2,7 @@ import {
2
2
  computeDop,
3
3
  computeSatPosition,
4
4
  ecefToAzEl
5
- } from "./chunk-MN2DBDJL.js";
5
+ } from "./chunk-AK6LNWZX.js";
6
6
  import {
7
7
  C_LIGHT,
8
8
  OMEGA_E
package/dist/index.cjs CHANGED
@@ -200,6 +200,7 @@ __export(src_exports, {
200
200
  horizonDistance: () => horizonDistance,
201
201
  ionoFree: () => ionoFree,
202
202
  keplerPosition: () => keplerPosition,
203
+ maskRadForAzimuth: () => maskRadForAzimuth,
203
204
  msmEpochToDate: () => msmEpochToDate,
204
205
  navTimesFromEph: () => navTimesFromEph,
205
206
  nmeaCoordToDecimal: () => nmeaCoordToDecimal,
@@ -207,6 +208,7 @@ __export(src_exports, {
207
208
  padR: () => padR,
208
209
  parseAntex: () => parseAntex,
209
210
  parseCrxDataLine: () => parseCrxDataLine,
211
+ parseIonex: () => parseIonex,
210
212
  parseNavFile: () => parseNavFile,
211
213
  parseNmeaFile: () => parseNmeaFile,
212
214
  parseRinexStream: () => parseRinexStream,
@@ -2575,6 +2577,91 @@ function writeRinexNav(nav) {
2575
2577
  return header + records.join("\n") + "\n";
2576
2578
  }
2577
2579
 
2580
+ // src/rinex/ionex.ts
2581
+ function gridRange(l1, l2, dl) {
2582
+ const out = [];
2583
+ const n = Math.round((l2 - l1) / dl) + 1;
2584
+ for (let i = 0; i < n; i++) out.push(l1 + i * dl);
2585
+ return out;
2586
+ }
2587
+ function parseIonex(text) {
2588
+ const lines = text.split("\n");
2589
+ let exponent = -1;
2590
+ let lats = [];
2591
+ let lons = [];
2592
+ const epochs = [];
2593
+ const maps = [];
2594
+ let i = 0;
2595
+ for (; i < lines.length; i++) {
2596
+ const line = lines[i];
2597
+ const label2 = line.slice(60).trim();
2598
+ if (label2 === "EXPONENT") {
2599
+ exponent = parseInt(line.slice(0, 60).trim(), 10);
2600
+ } else if (label2 === "LAT1 / LAT2 / DLAT") {
2601
+ const [a, b, d] = line.trim().split(/\s+/).map(Number);
2602
+ lats = gridRange(a, b, d);
2603
+ } else if (label2 === "LON1 / LON2 / DLON") {
2604
+ const [a, b, d] = line.trim().split(/\s+/).map(Number);
2605
+ lons = gridRange(a, b, d);
2606
+ } else if (label2 === "END OF HEADER") {
2607
+ i++;
2608
+ break;
2609
+ }
2610
+ }
2611
+ if (lats.length === 0 || lons.length === 0) {
2612
+ throw new Error("IONEX: missing grid definition");
2613
+ }
2614
+ const scale = Math.pow(10, exponent);
2615
+ while (i < lines.length) {
2616
+ const line = lines[i];
2617
+ const label2 = line.slice(60).trim();
2618
+ if (label2 === "START OF TEC MAP") {
2619
+ const map = new Float32Array(lats.length * lons.length).fill(NaN);
2620
+ let epochMs = 0;
2621
+ i++;
2622
+ while (i < lines.length) {
2623
+ const l = lines[i];
2624
+ const lab = l.slice(60).trim();
2625
+ if (lab === "EPOCH OF CURRENT MAP") {
2626
+ const f = l.slice(0, 60).trim().split(/\s+/).map(Number);
2627
+ epochMs = Date.UTC(f[0], f[1] - 1, f[2], f[3], f[4], f[5]);
2628
+ i++;
2629
+ } else if (lab === "LAT/LON1/LON2/DLON/H") {
2630
+ const lat = parseFloat(l.slice(2, 8));
2631
+ const latIdx = lats.findIndex((v) => Math.abs(v - lat) < 1e-6);
2632
+ i++;
2633
+ let lonIdx = 0;
2634
+ while (lonIdx < lons.length && i < lines.length) {
2635
+ const row = lines[i];
2636
+ for (let c = 0; c + 5 <= 80 && lonIdx < lons.length; c += 5) {
2637
+ const vStr = row.slice(c, c + 5).trim();
2638
+ if (vStr === "") continue;
2639
+ const v = parseInt(vStr, 10);
2640
+ if (latIdx >= 0) {
2641
+ map[latIdx * lons.length + lonIdx] = v === 9999 ? NaN : v * scale;
2642
+ }
2643
+ lonIdx++;
2644
+ }
2645
+ i++;
2646
+ }
2647
+ } else if (lab === "END OF TEC MAP") {
2648
+ i++;
2649
+ break;
2650
+ } else {
2651
+ i++;
2652
+ }
2653
+ }
2654
+ epochs.push(epochMs);
2655
+ maps.push(map);
2656
+ } else if (label2 === "START OF RMS MAP" || label2 === "END OF FILE") {
2657
+ break;
2658
+ } else {
2659
+ i++;
2660
+ }
2661
+ }
2662
+ return { epochs, lats, lons, maps };
2663
+ }
2664
+
2578
2665
  // src/rtcm3/decoder.ts
2579
2666
  var debugHandler = null;
2580
2667
  function setRtcm3DebugHandler(fn) {
@@ -4427,15 +4514,35 @@ function computeLiveSkyPositions(ephemerides, rxPos, cn0Map) {
4427
4514
  }
4428
4515
  return result;
4429
4516
  }
4517
+ function maskRadForAzimuth(maskDeg) {
4518
+ if (typeof maskDeg === "number") {
4519
+ const m = maskDeg * Math.PI / 180;
4520
+ return () => m;
4521
+ }
4522
+ const n = maskDeg.length;
4523
+ if (n === 0) return () => 0;
4524
+ return (azRad) => {
4525
+ const azDeg = (azRad * 180 / Math.PI % 360 + 360) % 360;
4526
+ const pos = azDeg / 360 * n;
4527
+ const i = Math.floor(pos) % n;
4528
+ const f = pos - Math.floor(pos);
4529
+ const a = maskDeg[i];
4530
+ const b = maskDeg[(i + 1) % n];
4531
+ return ((1 - f) * a + f * b) * Math.PI / 180;
4532
+ };
4533
+ }
4430
4534
  function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, elevationMaskDeg = 10) {
4431
4535
  const stepMs = stepSec * 1e3;
4432
4536
  const times = [];
4433
4537
  for (let t = startMs; t <= endMs; t += stepMs) times.push(t);
4434
- const maskRad = elevationMaskDeg * Math.PI / 180;
4538
+ const maskRadFor = maskRadForAzimuth(elevationMaskDeg);
4435
4539
  const all = computeAllPositions(ephemerides, times, rxPos);
4436
4540
  const elevation = {};
4437
4541
  const azimuth = {};
4542
+ const subLat = {};
4543
+ const subLon = {};
4438
4544
  const visibleCount = new Array(times.length).fill(0);
4545
+ const visibleBySystem = {};
4439
4546
  const pdop = new Array(times.length).fill(null);
4440
4547
  const gdop = new Array(times.length).fill(null);
4441
4548
  const hdop = new Array(times.length).fill(null);
@@ -4445,10 +4552,14 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
4445
4552
  const series = all.positions[prn];
4446
4553
  elevation[prn] = series.map((p) => p ? p.el : null);
4447
4554
  azimuth[prn] = series.map((p) => p ? p.az : null);
4555
+ subLat[prn] = series.map((p) => p ? p.lat : null);
4556
+ subLon[prn] = series.map((p) => p ? p.lon : null);
4448
4557
  for (let i = 0; i < series.length; i++) {
4449
4558
  const p = series[i];
4450
- if (p && p.el >= maskRad) {
4559
+ if (p && p.el >= maskRadFor(p.az)) {
4451
4560
  visibleCount[i]++;
4561
+ const sys = prn[0];
4562
+ (visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
4452
4563
  visiblePerEpoch[i].push({ az: p.az, el: p.el });
4453
4564
  }
4454
4565
  }
@@ -4468,7 +4579,8 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
4468
4579
  let peakTime = 0;
4469
4580
  for (let i = 0; i <= els.length; i++) {
4470
4581
  const el = i < els.length ? els[i] : null;
4471
- const above = el !== null && el !== void 0 && el >= maskRad;
4582
+ const az = all.positions[prn][i]?.az ?? 0;
4583
+ const above = el !== null && el !== void 0 && el >= maskRadFor(az);
4472
4584
  if (above) {
4473
4585
  if (start === -1) {
4474
4586
  start = i;
@@ -4496,7 +4608,10 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
4496
4608
  times,
4497
4609
  elevation,
4498
4610
  azimuth,
4611
+ subLat,
4612
+ subLon,
4499
4613
  visibleCount,
4614
+ visibleBySystem,
4500
4615
  pdop,
4501
4616
  gdop,
4502
4617
  hdop,
@@ -7353,6 +7468,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
7353
7468
  horizonDistance,
7354
7469
  ionoFree,
7355
7470
  keplerPosition,
7471
+ maskRadForAzimuth,
7356
7472
  msmEpochToDate,
7357
7473
  navTimesFromEph,
7358
7474
  nmeaCoordToDecimal,
@@ -7360,6 +7476,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
7360
7476
  padR,
7361
7477
  parseAntex,
7362
7478
  parseCrxDataLine,
7479
+ parseIonex,
7363
7480
  parseNavFile,
7364
7481
  parseNmeaFile,
7365
7482
  parseRinexStream,
package/dist/index.d.cts CHANGED
@@ -4,12 +4,12 @@ export { A as ARC_GAP_FACTOR, B as BAND_LABELS, a as BDS_SATELLITES, b as BdsSat
4
4
  export { Scale, getBdsTime, getDateFromBdsTime, getDateFromDayOfWeek, getDateFromDayOfYear, getDateFromGalTime, getDateFromGloN, getDateFromGpsData, getDateFromGpsTime, getDateFromHourCode, getDateFromJulianDate, getDateFromMJD, getDateFromMJD2000, getDateFromRINEX, getDateFromTai, getDateFromTimeOfDay, getDateFromTt, getDateFromUnixTime, getDateFromUtc, getDayOfWeek, getDayOfYear, getGalTime, getGloN4, getGloNA, getGpsLeap, getGpsTime, getHourCode, getHoursFromTimeDifference, getJulianDate, getLeap, getMJD, getMJD2000, getMinutesFromTimeDifference, getNtpTime, getRINEX, getSecondsFromTimeDifference, getTaiDate, getTimeDifference, getTimeDifferenceFromDays, getTimeDifferenceFromHours, getTimeDifferenceFromMinutes, getTimeDifferenceFromObject, getTimeDifferenceFromSeconds, getTimeOfDay, getTimeOfWeek, getTotalDaysFromTimeDifference, getTtDate, getUnixTime, getUtcDate, getWeekNumber, getWeekOfYear } from './time.cjs';
5
5
  export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, rad2deg, rhumbLine, vincenty } from './coordinates.cjs';
6
6
  export { c as clampUnit, e as ecefToGeodetic, g as geodeticToEcef, a as getAer, b as getEnuDifference } from './ecef-CF0uAysr.cjs';
7
- export { CrxField, DiffState, EMPTY_WARNINGS, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, writeRinexNav } from './rinex.cjs';
7
+ export { CrxField, DiffState, EMPTY_WARNINGS, IonexGrid, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, parseIonex, writeRinexNav } from './rinex.cjs';
8
8
  export { E as EpochSummary, P as ParseOptions, R as RinexHeader, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.cjs';
9
9
  export { E as Ephemeris, G as GlonassEphemeris, K as KeplerEphemeris, N as NavHeader, a as NavResult, p as parseNavFile } from './nav-BAI1a9vK.cjs';
10
10
  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';
11
11
  export { MessageTypeStats, MsmEpoch, MsmSatObs, MsmSignal, RTCM3_MESSAGE_NAMES, SatCn0, SignalCn0, StationMeta, StreamStats, createStationMeta, createStreamStats, decodeMsmFull, msmEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats } from './rtcm3.cjs';
12
- export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, navTimesFromEph, selectEphemeris } from './orbit.cjs';
12
+ export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, selectEphemeris } from './orbit.cjs';
13
13
  export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.cjs';
14
14
  export { SppOptions, SppSolution, ionoFree, satClockCorrection, solveSpp } from './positioning.cjs';
15
15
  export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.cjs';
package/dist/index.d.ts CHANGED
@@ -4,12 +4,12 @@ export { A as ARC_GAP_FACTOR, B as BAND_LABELS, a as BDS_SATELLITES, b as BdsSat
4
4
  export { Scale, getBdsTime, getDateFromBdsTime, getDateFromDayOfWeek, getDateFromDayOfYear, getDateFromGalTime, getDateFromGloN, getDateFromGpsData, getDateFromGpsTime, getDateFromHourCode, getDateFromJulianDate, getDateFromMJD, getDateFromMJD2000, getDateFromRINEX, getDateFromTai, getDateFromTimeOfDay, getDateFromTt, getDateFromUnixTime, getDateFromUtc, getDayOfWeek, getDayOfYear, getGalTime, getGloN4, getGloNA, getGpsLeap, getGpsTime, getHourCode, getHoursFromTimeDifference, getJulianDate, getLeap, getMJD, getMJD2000, getMinutesFromTimeDifference, getNtpTime, getRINEX, getSecondsFromTimeDifference, getTaiDate, getTimeDifference, getTimeDifferenceFromDays, getTimeDifferenceFromHours, getTimeDifferenceFromMinutes, getTimeDifferenceFromObject, getTimeDifferenceFromSeconds, getTimeOfDay, getTimeOfWeek, getTotalDaysFromTimeDifference, getTtDate, getUnixTime, getUtcDate, getWeekNumber, getWeekOfYear } from './time.js';
5
5
  export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, rad2deg, rhumbLine, vincenty } from './coordinates.js';
6
6
  export { c as clampUnit, e as ecefToGeodetic, g as geodeticToEcef, a as getAer, b as getEnuDifference } from './ecef-CF0uAysr.js';
7
- export { CrxField, DiffState, EMPTY_WARNINGS, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, writeRinexNav } from './rinex.js';
7
+ export { CrxField, DiffState, EMPTY_WARNINGS, IonexGrid, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, parseIonex, writeRinexNav } from './rinex.js';
8
8
  export { E as EpochSummary, P as ParseOptions, R as RinexHeader, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.js';
9
9
  export { E as Ephemeris, G as GlonassEphemeris, K as KeplerEphemeris, N as NavHeader, a as NavResult, p as parseNavFile } from './nav-BAI1a9vK.js';
10
10
  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';
11
11
  export { MessageTypeStats, MsmEpoch, MsmSatObs, MsmSignal, RTCM3_MESSAGE_NAMES, SatCn0, SignalCn0, StationMeta, StreamStats, createStationMeta, createStreamStats, decodeMsmFull, msmEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats } from './rtcm3.js';
12
- export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, navTimesFromEph, selectEphemeris } from './orbit.js';
12
+ export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, selectEphemeris } from './orbit.js';
13
13
  export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.js';
14
14
  export { SppOptions, SppSolution, ionoFree, satClockCorrection, solveSpp } from './positioning.js';
15
15
  export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.js';
package/dist/index.js CHANGED
@@ -102,7 +102,7 @@ import {
102
102
  ionoFree,
103
103
  satClockCorrection,
104
104
  solveSpp
105
- } from "./chunk-GXK4MMHS.js";
105
+ } from "./chunk-ZTFKRZMM.js";
106
106
  import {
107
107
  computeAllPositions,
108
108
  computeDop,
@@ -113,9 +113,10 @@ import {
113
113
  ephInfoToEphemeris,
114
114
  glonassPosition,
115
115
  keplerPosition,
116
+ maskRadForAzimuth,
116
117
  navTimesFromEph,
117
118
  selectEphemeris
118
- } from "./chunk-MN2DBDJL.js";
119
+ } from "./chunk-AK6LNWZX.js";
119
120
  import {
120
121
  getBdsTime,
121
122
  getDateFromBdsTime,
@@ -172,9 +173,10 @@ import {
172
173
  hdrLine,
173
174
  padL,
174
175
  padR,
176
+ parseIonex,
175
177
  parseNavFile,
176
178
  writeRinexNav
177
- } from "./chunk-W4YMQKWH.js";
179
+ } from "./chunk-77VUSDNI.js";
178
180
  import {
179
181
  SYSTEM_ORDER,
180
182
  crxDecompress,
@@ -431,6 +433,7 @@ export {
431
433
  horizonDistance,
432
434
  ionoFree,
433
435
  keplerPosition,
436
+ maskRadForAzimuth,
434
437
  msmEpochToDate,
435
438
  navTimesFromEph,
436
439
  nmeaCoordToDecimal,
@@ -438,6 +441,7 @@ export {
438
441
  padR,
439
442
  parseAntex,
440
443
  parseCrxDataLine,
444
+ parseIonex,
441
445
  parseNavFile,
442
446
  parseNmeaFile,
443
447
  parseRinexStream,
package/dist/orbit.cjs CHANGED
@@ -31,6 +31,7 @@ __export(orbit_exports, {
31
31
  geodeticToEcef: () => geodeticToEcef,
32
32
  glonassPosition: () => glonassPosition,
33
33
  keplerPosition: () => keplerPosition,
34
+ maskRadForAzimuth: () => maskRadForAzimuth,
34
35
  navTimesFromEph: () => navTimesFromEph,
35
36
  selectEphemeris: () => selectEphemeris
36
37
  });
@@ -590,15 +591,35 @@ function computeLiveSkyPositions(ephemerides, rxPos, cn0Map) {
590
591
  }
591
592
  return result;
592
593
  }
594
+ function maskRadForAzimuth(maskDeg) {
595
+ if (typeof maskDeg === "number") {
596
+ const m = maskDeg * Math.PI / 180;
597
+ return () => m;
598
+ }
599
+ const n = maskDeg.length;
600
+ if (n === 0) return () => 0;
601
+ return (azRad) => {
602
+ const azDeg = (azRad * 180 / Math.PI % 360 + 360) % 360;
603
+ const pos = azDeg / 360 * n;
604
+ const i = Math.floor(pos) % n;
605
+ const f = pos - Math.floor(pos);
606
+ const a = maskDeg[i];
607
+ const b = maskDeg[(i + 1) % n];
608
+ return ((1 - f) * a + f * b) * Math.PI / 180;
609
+ };
610
+ }
593
611
  function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, elevationMaskDeg = 10) {
594
612
  const stepMs = stepSec * 1e3;
595
613
  const times = [];
596
614
  for (let t = startMs; t <= endMs; t += stepMs) times.push(t);
597
- const maskRad = elevationMaskDeg * Math.PI / 180;
615
+ const maskRadFor = maskRadForAzimuth(elevationMaskDeg);
598
616
  const all = computeAllPositions(ephemerides, times, rxPos);
599
617
  const elevation = {};
600
618
  const azimuth = {};
619
+ const subLat = {};
620
+ const subLon = {};
601
621
  const visibleCount = new Array(times.length).fill(0);
622
+ const visibleBySystem = {};
602
623
  const pdop = new Array(times.length).fill(null);
603
624
  const gdop = new Array(times.length).fill(null);
604
625
  const hdop = new Array(times.length).fill(null);
@@ -608,10 +629,14 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
608
629
  const series = all.positions[prn];
609
630
  elevation[prn] = series.map((p) => p ? p.el : null);
610
631
  azimuth[prn] = series.map((p) => p ? p.az : null);
632
+ subLat[prn] = series.map((p) => p ? p.lat : null);
633
+ subLon[prn] = series.map((p) => p ? p.lon : null);
611
634
  for (let i = 0; i < series.length; i++) {
612
635
  const p = series[i];
613
- if (p && p.el >= maskRad) {
636
+ if (p && p.el >= maskRadFor(p.az)) {
614
637
  visibleCount[i]++;
638
+ const sys = prn[0];
639
+ (visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
615
640
  visiblePerEpoch[i].push({ az: p.az, el: p.el });
616
641
  }
617
642
  }
@@ -631,7 +656,8 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
631
656
  let peakTime = 0;
632
657
  for (let i = 0; i <= els.length; i++) {
633
658
  const el = i < els.length ? els[i] : null;
634
- const above = el !== null && el !== void 0 && el >= maskRad;
659
+ const az = all.positions[prn][i]?.az ?? 0;
660
+ const above = el !== null && el !== void 0 && el >= maskRadFor(az);
635
661
  if (above) {
636
662
  if (start === -1) {
637
663
  start = i;
@@ -659,7 +685,10 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
659
685
  times,
660
686
  elevation,
661
687
  azimuth,
688
+ subLat,
689
+ subLon,
662
690
  visibleCount,
691
+ visibleBySystem,
663
692
  pdop,
664
693
  gdop,
665
694
  hdop,
@@ -680,6 +709,7 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
680
709
  geodeticToEcef,
681
710
  glonassPosition,
682
711
  keplerPosition,
712
+ maskRadForAzimuth,
683
713
  navTimesFromEph,
684
714
  selectEphemeris
685
715
  });
package/dist/orbit.d.cts CHANGED
@@ -135,8 +135,14 @@ interface VisibilityResult {
135
135
  elevation: Record<string, (number | null)[]>;
136
136
  /** Azimuth (radians) per PRN per epoch; null when no valid ephemeris. */
137
137
  azimuth: Record<string, (number | null)[]>;
138
+ /** Sub-satellite latitude (radians) per PRN per epoch. */
139
+ subLat: Record<string, (number | null)[]>;
140
+ /** Sub-satellite longitude (radians) per PRN per epoch. */
141
+ subLon: Record<string, (number | null)[]>;
138
142
  /** Number of satellites at or above the mask, per epoch. */
139
143
  visibleCount: number[];
144
+ /** Per-system visible counts per epoch (system letter → counts). */
145
+ visibleBySystem: Record<string, number[]>;
140
146
  /** PDOP per epoch (null when < 4 satellites above the mask). */
141
147
  pdop: (number | null)[];
142
148
  /** GDOP per epoch (null when < 4 satellites above the mask). */
@@ -148,6 +154,12 @@ interface VisibilityResult {
148
154
  /** Discrete above-mask passes, sorted by rise time. */
149
155
  passes: VisibilityPass[];
150
156
  }
157
+ /**
158
+ * Build a mask lookup (radians) from a scalar or per-azimuth array of
159
+ * mask values in degrees. Array entries span 0–360° uniformly and are
160
+ * linearly interpolated.
161
+ */
162
+ declare function maskRadForAzimuth(maskDeg: number | number[]): (azRad: number) => number;
151
163
  /**
152
164
  * Predict satellite visibility and DOP over a time window for a fixed
153
165
  * receiver location.
@@ -157,8 +169,11 @@ interface VisibilityResult {
157
169
  * @param startMs Window start (Unix ms).
158
170
  * @param endMs Window end (Unix ms).
159
171
  * @param stepSec Sample spacing in seconds (default 300).
160
- * @param elevationMaskDeg Elevation mask in degrees (default 10).
172
+ * @param elevationMaskDeg Elevation mask in degrees (default 10) — a
173
+ * scalar, or an array of per-azimuth mask values in degrees
174
+ * (uniformly spanning 0–360°, e.g. 36 sectors of 10°) describing a
175
+ * local horizon/obstruction profile.
161
176
  */
162
- declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, number, number], startMs: number, endMs: number, stepSec?: number, elevationMaskDeg?: number): VisibilityResult;
177
+ declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, number, number], startMs: number, endMs: number, stepSec?: number, elevationMaskDeg?: number | number[]): VisibilityResult;
163
178
 
164
- export { type AllPositionsData, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, navTimesFromEph, selectEphemeris };
179
+ export { type AllPositionsData, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, selectEphemeris };
package/dist/orbit.d.ts CHANGED
@@ -135,8 +135,14 @@ interface VisibilityResult {
135
135
  elevation: Record<string, (number | null)[]>;
136
136
  /** Azimuth (radians) per PRN per epoch; null when no valid ephemeris. */
137
137
  azimuth: Record<string, (number | null)[]>;
138
+ /** Sub-satellite latitude (radians) per PRN per epoch. */
139
+ subLat: Record<string, (number | null)[]>;
140
+ /** Sub-satellite longitude (radians) per PRN per epoch. */
141
+ subLon: Record<string, (number | null)[]>;
138
142
  /** Number of satellites at or above the mask, per epoch. */
139
143
  visibleCount: number[];
144
+ /** Per-system visible counts per epoch (system letter → counts). */
145
+ visibleBySystem: Record<string, number[]>;
140
146
  /** PDOP per epoch (null when < 4 satellites above the mask). */
141
147
  pdop: (number | null)[];
142
148
  /** GDOP per epoch (null when < 4 satellites above the mask). */
@@ -148,6 +154,12 @@ interface VisibilityResult {
148
154
  /** Discrete above-mask passes, sorted by rise time. */
149
155
  passes: VisibilityPass[];
150
156
  }
157
+ /**
158
+ * Build a mask lookup (radians) from a scalar or per-azimuth array of
159
+ * mask values in degrees. Array entries span 0–360° uniformly and are
160
+ * linearly interpolated.
161
+ */
162
+ declare function maskRadForAzimuth(maskDeg: number | number[]): (azRad: number) => number;
151
163
  /**
152
164
  * Predict satellite visibility and DOP over a time window for a fixed
153
165
  * receiver location.
@@ -157,8 +169,11 @@ interface VisibilityResult {
157
169
  * @param startMs Window start (Unix ms).
158
170
  * @param endMs Window end (Unix ms).
159
171
  * @param stepSec Sample spacing in seconds (default 300).
160
- * @param elevationMaskDeg Elevation mask in degrees (default 10).
172
+ * @param elevationMaskDeg Elevation mask in degrees (default 10) — a
173
+ * scalar, or an array of per-azimuth mask values in degrees
174
+ * (uniformly spanning 0–360°, e.g. 36 sectors of 10°) describing a
175
+ * local horizon/obstruction profile.
161
176
  */
162
- declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, number, number], startMs: number, endMs: number, stepSec?: number, elevationMaskDeg?: number): VisibilityResult;
177
+ declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, number, number], startMs: number, endMs: number, stepSec?: number, elevationMaskDeg?: number | number[]): VisibilityResult;
163
178
 
164
- export { type AllPositionsData, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, navTimesFromEph, selectEphemeris };
179
+ export { type AllPositionsData, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, selectEphemeris };
package/dist/orbit.js CHANGED
@@ -8,9 +8,10 @@ import {
8
8
  ephInfoToEphemeris,
9
9
  glonassPosition,
10
10
  keplerPosition,
11
+ maskRadForAzimuth,
11
12
  navTimesFromEph,
12
13
  selectEphemeris
13
- } from "./chunk-MN2DBDJL.js";
14
+ } from "./chunk-AK6LNWZX.js";
14
15
  import "./chunk-HVXYFUCB.js";
15
16
  import {
16
17
  ecefToGeodetic,
@@ -31,6 +32,7 @@ export {
31
32
  geodeticToEcef,
32
33
  glonassPosition,
33
34
  keplerPosition,
35
+ maskRadForAzimuth,
34
36
  navTimesFromEph,
35
37
  selectEphemeris
36
38
  };
@@ -2,8 +2,8 @@ import {
2
2
  ionoFree,
3
3
  satClockCorrection,
4
4
  solveSpp
5
- } from "./chunk-GXK4MMHS.js";
6
- import "./chunk-MN2DBDJL.js";
5
+ } from "./chunk-ZTFKRZMM.js";
6
+ import "./chunk-AK6LNWZX.js";
7
7
  import "./chunk-HVXYFUCB.js";
8
8
  import "./chunk-37QNKGTC.js";
9
9
  import "./chunk-6FAL6P4G.js";
package/dist/rinex.cjs CHANGED
@@ -30,6 +30,7 @@ __export(rinex_exports, {
30
30
  padL: () => padL,
31
31
  padR: () => padR,
32
32
  parseCrxDataLine: () => parseCrxDataLine,
33
+ parseIonex: () => parseIonex,
33
34
  parseNavFile: () => parseNavFile,
34
35
  parseRinexStream: () => parseRinexStream,
35
36
  systemCmp: () => systemCmp,
@@ -1449,6 +1450,91 @@ function writeRinexNav(nav) {
1449
1450
  );
1450
1451
  return header + records.join("\n") + "\n";
1451
1452
  }
1453
+
1454
+ // src/rinex/ionex.ts
1455
+ function gridRange(l1, l2, dl) {
1456
+ const out = [];
1457
+ const n = Math.round((l2 - l1) / dl) + 1;
1458
+ for (let i = 0; i < n; i++) out.push(l1 + i * dl);
1459
+ return out;
1460
+ }
1461
+ function parseIonex(text) {
1462
+ const lines = text.split("\n");
1463
+ let exponent = -1;
1464
+ let lats = [];
1465
+ let lons = [];
1466
+ const epochs = [];
1467
+ const maps = [];
1468
+ let i = 0;
1469
+ for (; i < lines.length; i++) {
1470
+ const line = lines[i];
1471
+ const label = line.slice(60).trim();
1472
+ if (label === "EXPONENT") {
1473
+ exponent = parseInt(line.slice(0, 60).trim(), 10);
1474
+ } else if (label === "LAT1 / LAT2 / DLAT") {
1475
+ const [a, b, d] = line.trim().split(/\s+/).map(Number);
1476
+ lats = gridRange(a, b, d);
1477
+ } else if (label === "LON1 / LON2 / DLON") {
1478
+ const [a, b, d] = line.trim().split(/\s+/).map(Number);
1479
+ lons = gridRange(a, b, d);
1480
+ } else if (label === "END OF HEADER") {
1481
+ i++;
1482
+ break;
1483
+ }
1484
+ }
1485
+ if (lats.length === 0 || lons.length === 0) {
1486
+ throw new Error("IONEX: missing grid definition");
1487
+ }
1488
+ const scale = Math.pow(10, exponent);
1489
+ while (i < lines.length) {
1490
+ const line = lines[i];
1491
+ const label = line.slice(60).trim();
1492
+ if (label === "START OF TEC MAP") {
1493
+ const map = new Float32Array(lats.length * lons.length).fill(NaN);
1494
+ let epochMs = 0;
1495
+ i++;
1496
+ while (i < lines.length) {
1497
+ const l = lines[i];
1498
+ const lab = l.slice(60).trim();
1499
+ if (lab === "EPOCH OF CURRENT MAP") {
1500
+ const f = l.slice(0, 60).trim().split(/\s+/).map(Number);
1501
+ epochMs = Date.UTC(f[0], f[1] - 1, f[2], f[3], f[4], f[5]);
1502
+ i++;
1503
+ } else if (lab === "LAT/LON1/LON2/DLON/H") {
1504
+ const lat = parseFloat(l.slice(2, 8));
1505
+ const latIdx = lats.findIndex((v) => Math.abs(v - lat) < 1e-6);
1506
+ i++;
1507
+ let lonIdx = 0;
1508
+ while (lonIdx < lons.length && i < lines.length) {
1509
+ const row = lines[i];
1510
+ for (let c = 0; c + 5 <= 80 && lonIdx < lons.length; c += 5) {
1511
+ const vStr = row.slice(c, c + 5).trim();
1512
+ if (vStr === "") continue;
1513
+ const v = parseInt(vStr, 10);
1514
+ if (latIdx >= 0) {
1515
+ map[latIdx * lons.length + lonIdx] = v === 9999 ? NaN : v * scale;
1516
+ }
1517
+ lonIdx++;
1518
+ }
1519
+ i++;
1520
+ }
1521
+ } else if (lab === "END OF TEC MAP") {
1522
+ i++;
1523
+ break;
1524
+ } else {
1525
+ i++;
1526
+ }
1527
+ }
1528
+ epochs.push(epochMs);
1529
+ maps.push(map);
1530
+ } else if (label === "START OF RMS MAP" || label === "END OF FILE") {
1531
+ break;
1532
+ } else {
1533
+ i++;
1534
+ }
1535
+ }
1536
+ return { epochs, lats, lons, maps };
1537
+ }
1452
1538
  // Annotate the CommonJS export names for ESM import in node:
1453
1539
  0 && (module.exports = {
1454
1540
  EMPTY_WARNINGS,
@@ -1461,6 +1547,7 @@ function writeRinexNav(nav) {
1461
1547
  padL,
1462
1548
  padR,
1463
1549
  parseCrxDataLine,
1550
+ parseIonex,
1464
1551
  parseNavFile,
1465
1552
  parseRinexStream,
1466
1553
  systemCmp,
package/dist/rinex.d.cts CHANGED
@@ -113,4 +113,26 @@ declare class WarningAccumulator {
113
113
  */
114
114
  declare function writeRinexNav(nav: NavResult): string;
115
115
 
116
- export { type CrxField, type DiffState, EMPTY_WARNINGS, NavResult, RinexHeader, type RinexWarning, type RinexWarnings, WarningAccumulator, type WarningSeverity, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, writeRinexNav };
116
+ /**
117
+ * IONEX 1.0 parser — global ionosphere maps (GIMs) as published by
118
+ * the IGS analysis centres (e.g. ESA0OPSRAP_*_GIM.INX).
119
+ *
120
+ * Returns the TEC maps in TECU on a regular lat/lon grid, one map per
121
+ * epoch. RMS maps and the DCB auxiliary block are skipped.
122
+ */
123
+ interface IonexGrid {
124
+ /** Map epochs (Unix ms, UTC). */
125
+ epochs: number[];
126
+ /** Grid latitudes (degrees), in file order (typically 87.5 → −87.5). */
127
+ lats: number[];
128
+ /** Grid longitudes (degrees), in file order (typically −180 → 180). */
129
+ lons: number[];
130
+ /**
131
+ * TEC maps in TECU: maps[epochIdx][latIdx * lons.length + lonIdx].
132
+ * NaN where the file marks no value (9999).
133
+ */
134
+ maps: Float32Array[];
135
+ }
136
+ declare function parseIonex(text: string): IonexGrid;
137
+
138
+ export { type CrxField, type DiffState, EMPTY_WARNINGS, type IonexGrid, NavResult, RinexHeader, type RinexWarning, type RinexWarnings, WarningAccumulator, type WarningSeverity, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, parseIonex, writeRinexNav };
package/dist/rinex.d.ts CHANGED
@@ -113,4 +113,26 @@ declare class WarningAccumulator {
113
113
  */
114
114
  declare function writeRinexNav(nav: NavResult): string;
115
115
 
116
- export { type CrxField, type DiffState, EMPTY_WARNINGS, NavResult, RinexHeader, type RinexWarning, type RinexWarnings, WarningAccumulator, type WarningSeverity, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, writeRinexNav };
116
+ /**
117
+ * IONEX 1.0 parser — global ionosphere maps (GIMs) as published by
118
+ * the IGS analysis centres (e.g. ESA0OPSRAP_*_GIM.INX).
119
+ *
120
+ * Returns the TEC maps in TECU on a regular lat/lon grid, one map per
121
+ * epoch. RMS maps and the DCB auxiliary block are skipped.
122
+ */
123
+ interface IonexGrid {
124
+ /** Map epochs (Unix ms, UTC). */
125
+ epochs: number[];
126
+ /** Grid latitudes (degrees), in file order (typically 87.5 → −87.5). */
127
+ lats: number[];
128
+ /** Grid longitudes (degrees), in file order (typically −180 → 180). */
129
+ lons: number[];
130
+ /**
131
+ * TEC maps in TECU: maps[epochIdx][latIdx * lons.length + lonIdx].
132
+ * NaN where the file marks no value (9999).
133
+ */
134
+ maps: Float32Array[];
135
+ }
136
+ declare function parseIonex(text: string): IonexGrid;
137
+
138
+ export { type CrxField, type DiffState, EMPTY_WARNINGS, type IonexGrid, NavResult, RinexHeader, type RinexWarning, type RinexWarnings, WarningAccumulator, type WarningSeverity, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, parseIonex, writeRinexNav };
package/dist/rinex.js CHANGED
@@ -5,9 +5,10 @@ import {
5
5
  hdrLine,
6
6
  padL,
7
7
  padR,
8
+ parseIonex,
8
9
  parseNavFile,
9
10
  writeRinexNav
10
- } from "./chunk-W4YMQKWH.js";
11
+ } from "./chunk-77VUSDNI.js";
11
12
  import {
12
13
  SYSTEM_ORDER,
13
14
  crxDecompress,
@@ -29,6 +30,7 @@ export {
29
30
  padL,
30
31
  padR,
31
32
  parseCrxDataLine,
33
+ parseIonex,
32
34
  parseNavFile,
33
35
  parseRinexStream,
34
36
  systemCmp,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "1.8.0",
3
+ "version": "1.10.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,