gnss-js 1.7.0 → 1.9.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.
@@ -437,13 +437,33 @@ 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 = {};
464
+ const azimuth = {};
465
+ const subLat = {};
466
+ const subLon = {};
447
467
  const visibleCount = new Array(times.length).fill(0);
448
468
  const pdop = new Array(times.length).fill(null);
449
469
  const gdop = new Array(times.length).fill(null);
@@ -453,9 +473,12 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
453
473
  for (const prn of all.prns) {
454
474
  const series = all.positions[prn];
455
475
  elevation[prn] = series.map((p) => p ? p.el : null);
476
+ azimuth[prn] = series.map((p) => p ? p.az : null);
477
+ subLat[prn] = series.map((p) => p ? p.lat : null);
478
+ subLon[prn] = series.map((p) => p ? p.lon : null);
456
479
  for (let i = 0; i < series.length; i++) {
457
480
  const p = series[i];
458
- if (p && p.el >= maskRad) {
481
+ if (p && p.el >= maskRadFor(p.az)) {
459
482
  visibleCount[i]++;
460
483
  visiblePerEpoch[i].push({ az: p.az, el: p.el });
461
484
  }
@@ -476,7 +499,8 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
476
499
  let peakTime = 0;
477
500
  for (let i = 0; i <= els.length; i++) {
478
501
  const el = i < els.length ? els[i] : null;
479
- const above = el !== null && el !== void 0 && el >= maskRad;
502
+ const az = all.positions[prn][i]?.az ?? 0;
503
+ const above = el !== null && el !== void 0 && el >= maskRadFor(az);
480
504
  if (above) {
481
505
  if (start === -1) {
482
506
  start = i;
@@ -500,7 +524,19 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
500
524
  }
501
525
  }
502
526
  passes.sort((a, b) => a.rise - b.rise);
503
- return { times, elevation, visibleCount, pdop, gdop, hdop, vdop, passes };
527
+ return {
528
+ times,
529
+ elevation,
530
+ azimuth,
531
+ subLat,
532
+ subLon,
533
+ visibleCount,
534
+ pdop,
535
+ gdop,
536
+ hdop,
537
+ vdop,
538
+ passes
539
+ };
504
540
  }
505
541
 
506
542
  export {
@@ -514,5 +550,6 @@ export {
514
550
  computeAllPositions,
515
551
  ephInfoToEphemeris,
516
552
  computeLiveSkyPositions,
553
+ maskRadForAzimuth,
517
554
  computeVisibility
518
555
  };
@@ -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
  };
@@ -2,7 +2,7 @@ import {
2
2
  computeDop,
3
3
  computeSatPosition,
4
4
  ecefToAzEl
5
- } from "./chunk-TBRZDWDE.js";
5
+ } from "./chunk-647YMBJV.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,13 +4514,33 @@ 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 = {};
4541
+ const azimuth = {};
4542
+ const subLat = {};
4543
+ const subLon = {};
4437
4544
  const visibleCount = new Array(times.length).fill(0);
4438
4545
  const pdop = new Array(times.length).fill(null);
4439
4546
  const gdop = new Array(times.length).fill(null);
@@ -4443,9 +4550,12 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
4443
4550
  for (const prn of all.prns) {
4444
4551
  const series = all.positions[prn];
4445
4552
  elevation[prn] = series.map((p) => p ? p.el : null);
4553
+ azimuth[prn] = series.map((p) => p ? p.az : null);
4554
+ subLat[prn] = series.map((p) => p ? p.lat : null);
4555
+ subLon[prn] = series.map((p) => p ? p.lon : null);
4446
4556
  for (let i = 0; i < series.length; i++) {
4447
4557
  const p = series[i];
4448
- if (p && p.el >= maskRad) {
4558
+ if (p && p.el >= maskRadFor(p.az)) {
4449
4559
  visibleCount[i]++;
4450
4560
  visiblePerEpoch[i].push({ az: p.az, el: p.el });
4451
4561
  }
@@ -4466,7 +4576,8 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
4466
4576
  let peakTime = 0;
4467
4577
  for (let i = 0; i <= els.length; i++) {
4468
4578
  const el = i < els.length ? els[i] : null;
4469
- const above = el !== null && el !== void 0 && el >= maskRad;
4579
+ const az = all.positions[prn][i]?.az ?? 0;
4580
+ const above = el !== null && el !== void 0 && el >= maskRadFor(az);
4470
4581
  if (above) {
4471
4582
  if (start === -1) {
4472
4583
  start = i;
@@ -4490,7 +4601,19 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
4490
4601
  }
4491
4602
  }
4492
4603
  passes.sort((a, b) => a.rise - b.rise);
4493
- return { times, elevation, visibleCount, pdop, gdop, hdop, vdop, passes };
4604
+ return {
4605
+ times,
4606
+ elevation,
4607
+ azimuth,
4608
+ subLat,
4609
+ subLon,
4610
+ visibleCount,
4611
+ pdop,
4612
+ gdop,
4613
+ hdop,
4614
+ vdop,
4615
+ passes
4616
+ };
4494
4617
  }
4495
4618
 
4496
4619
  // src/frames/index.ts
@@ -7341,6 +7464,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
7341
7464
  horizonDistance,
7342
7465
  ionoFree,
7343
7466
  keplerPosition,
7467
+ maskRadForAzimuth,
7344
7468
  msmEpochToDate,
7345
7469
  navTimesFromEph,
7346
7470
  nmeaCoordToDecimal,
@@ -7348,6 +7472,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
7348
7472
  padR,
7349
7473
  parseAntex,
7350
7474
  parseCrxDataLine,
7475
+ parseIonex,
7351
7476
  parseNavFile,
7352
7477
  parseNmeaFile,
7353
7478
  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-7M7UJJIA.js";
105
+ } from "./chunk-ZRBRVNHK.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-TBRZDWDE.js";
119
+ } from "./chunk-647YMBJV.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,13 +591,33 @@ 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 = {};
618
+ const azimuth = {};
619
+ const subLat = {};
620
+ const subLon = {};
600
621
  const visibleCount = new Array(times.length).fill(0);
601
622
  const pdop = new Array(times.length).fill(null);
602
623
  const gdop = new Array(times.length).fill(null);
@@ -606,9 +627,12 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
606
627
  for (const prn of all.prns) {
607
628
  const series = all.positions[prn];
608
629
  elevation[prn] = series.map((p) => p ? p.el : null);
630
+ azimuth[prn] = series.map((p) => p ? p.az : null);
631
+ subLat[prn] = series.map((p) => p ? p.lat : null);
632
+ subLon[prn] = series.map((p) => p ? p.lon : null);
609
633
  for (let i = 0; i < series.length; i++) {
610
634
  const p = series[i];
611
- if (p && p.el >= maskRad) {
635
+ if (p && p.el >= maskRadFor(p.az)) {
612
636
  visibleCount[i]++;
613
637
  visiblePerEpoch[i].push({ az: p.az, el: p.el });
614
638
  }
@@ -629,7 +653,8 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
629
653
  let peakTime = 0;
630
654
  for (let i = 0; i <= els.length; i++) {
631
655
  const el = i < els.length ? els[i] : null;
632
- const above = el !== null && el !== void 0 && el >= maskRad;
656
+ const az = all.positions[prn][i]?.az ?? 0;
657
+ const above = el !== null && el !== void 0 && el >= maskRadFor(az);
633
658
  if (above) {
634
659
  if (start === -1) {
635
660
  start = i;
@@ -653,7 +678,19 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
653
678
  }
654
679
  }
655
680
  passes.sort((a, b) => a.rise - b.rise);
656
- return { times, elevation, visibleCount, pdop, gdop, hdop, vdop, passes };
681
+ return {
682
+ times,
683
+ elevation,
684
+ azimuth,
685
+ subLat,
686
+ subLon,
687
+ visibleCount,
688
+ pdop,
689
+ gdop,
690
+ hdop,
691
+ vdop,
692
+ passes
693
+ };
657
694
  }
658
695
  // Annotate the CommonJS export names for ESM import in node:
659
696
  0 && (module.exports = {
@@ -668,6 +705,7 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
668
705
  geodeticToEcef,
669
706
  glonassPosition,
670
707
  keplerPosition,
708
+ maskRadForAzimuth,
671
709
  navTimesFromEph,
672
710
  selectEphemeris
673
711
  });
package/dist/orbit.d.cts CHANGED
@@ -133,6 +133,12 @@ interface VisibilityResult {
133
133
  times: number[];
134
134
  /** Elevation (radians) per PRN per epoch; null when no valid ephemeris. */
135
135
  elevation: Record<string, (number | null)[]>;
136
+ /** Azimuth (radians) per PRN per epoch; null when no valid ephemeris. */
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)[]>;
136
142
  /** Number of satellites at or above the mask, per epoch. */
137
143
  visibleCount: number[];
138
144
  /** PDOP per epoch (null when < 4 satellites above the mask). */
@@ -146,6 +152,12 @@ interface VisibilityResult {
146
152
  /** Discrete above-mask passes, sorted by rise time. */
147
153
  passes: VisibilityPass[];
148
154
  }
155
+ /**
156
+ * Build a mask lookup (radians) from a scalar or per-azimuth array of
157
+ * mask values in degrees. Array entries span 0–360° uniformly and are
158
+ * linearly interpolated.
159
+ */
160
+ declare function maskRadForAzimuth(maskDeg: number | number[]): (azRad: number) => number;
149
161
  /**
150
162
  * Predict satellite visibility and DOP over a time window for a fixed
151
163
  * receiver location.
@@ -155,8 +167,11 @@ interface VisibilityResult {
155
167
  * @param startMs Window start (Unix ms).
156
168
  * @param endMs Window end (Unix ms).
157
169
  * @param stepSec Sample spacing in seconds (default 300).
158
- * @param elevationMaskDeg Elevation mask in degrees (default 10).
170
+ * @param elevationMaskDeg Elevation mask in degrees (default 10) — a
171
+ * scalar, or an array of per-azimuth mask values in degrees
172
+ * (uniformly spanning 0–360°, e.g. 36 sectors of 10°) describing a
173
+ * local horizon/obstruction profile.
159
174
  */
160
- declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, number, number], startMs: number, endMs: number, stepSec?: number, elevationMaskDeg?: number): VisibilityResult;
175
+ declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, number, number], startMs: number, endMs: number, stepSec?: number, elevationMaskDeg?: number | number[]): VisibilityResult;
161
176
 
162
- 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 };
177
+ 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
@@ -133,6 +133,12 @@ interface VisibilityResult {
133
133
  times: number[];
134
134
  /** Elevation (radians) per PRN per epoch; null when no valid ephemeris. */
135
135
  elevation: Record<string, (number | null)[]>;
136
+ /** Azimuth (radians) per PRN per epoch; null when no valid ephemeris. */
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)[]>;
136
142
  /** Number of satellites at or above the mask, per epoch. */
137
143
  visibleCount: number[];
138
144
  /** PDOP per epoch (null when < 4 satellites above the mask). */
@@ -146,6 +152,12 @@ interface VisibilityResult {
146
152
  /** Discrete above-mask passes, sorted by rise time. */
147
153
  passes: VisibilityPass[];
148
154
  }
155
+ /**
156
+ * Build a mask lookup (radians) from a scalar or per-azimuth array of
157
+ * mask values in degrees. Array entries span 0–360° uniformly and are
158
+ * linearly interpolated.
159
+ */
160
+ declare function maskRadForAzimuth(maskDeg: number | number[]): (azRad: number) => number;
149
161
  /**
150
162
  * Predict satellite visibility and DOP over a time window for a fixed
151
163
  * receiver location.
@@ -155,8 +167,11 @@ interface VisibilityResult {
155
167
  * @param startMs Window start (Unix ms).
156
168
  * @param endMs Window end (Unix ms).
157
169
  * @param stepSec Sample spacing in seconds (default 300).
158
- * @param elevationMaskDeg Elevation mask in degrees (default 10).
170
+ * @param elevationMaskDeg Elevation mask in degrees (default 10) — a
171
+ * scalar, or an array of per-azimuth mask values in degrees
172
+ * (uniformly spanning 0–360°, e.g. 36 sectors of 10°) describing a
173
+ * local horizon/obstruction profile.
159
174
  */
160
- declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, number, number], startMs: number, endMs: number, stepSec?: number, elevationMaskDeg?: number): VisibilityResult;
175
+ declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, number, number], startMs: number, endMs: number, stepSec?: number, elevationMaskDeg?: number | number[]): VisibilityResult;
161
176
 
162
- 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 };
177
+ 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-TBRZDWDE.js";
14
+ } from "./chunk-647YMBJV.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-7M7UJJIA.js";
6
- import "./chunk-TBRZDWDE.js";
5
+ } from "./chunk-ZRBRVNHK.js";
6
+ import "./chunk-647YMBJV.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.7.0",
3
+ "version": "1.9.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,