pygnss 0.3.0__cp313-cp313-musllinux_1_2_i686.whl → 0.5.0__cp313-cp313-musllinux_1_2_i686.whl

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.

Potentially problematic release.


This version of pygnss might be problematic. Click here for more details.

pygnss/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.3.0"
1
+ __version__ = "0.5.0"
pygnss/decorator.py CHANGED
@@ -1,3 +1,6 @@
1
+ import gzip
2
+ from functools import wraps
3
+ import subprocess
1
4
  import warnings
2
5
 
3
6
 
@@ -12,3 +15,33 @@ def deprecated(alternative):
12
15
  return func(*args, **kwargs)
13
16
  return new_func
14
17
  return decorator
18
+
19
+
20
+ def read_contents(func):
21
+ """
22
+ Decorator to handle gzip compression based on filename and pass its contents
23
+ to the function
24
+ """
25
+
26
+ @wraps(func)
27
+ def wrapper(filename, *args, **kwargs):
28
+
29
+ doc = None
30
+
31
+ if filename.endswith('.gz'):
32
+ with gzip.open(filename, 'rt', encoding='utf-8') as fh:
33
+ doc = fh.read()
34
+ elif filename.endswith('.Z'):
35
+ result = subprocess.run(['uncompress', '-c', filename],
36
+ stdout=subprocess.PIPE,
37
+ stderr=subprocess.PIPE,
38
+ check=True,
39
+ text=True)
40
+ doc = result.stdout
41
+ else:
42
+ with open(filename, 'rt', encoding='utf-8') as fh:
43
+ doc = fh.read()
44
+
45
+ return func(doc, *args, **kwargs)
46
+
47
+ return wrapper
pygnss/ionex.py ADDED
@@ -0,0 +1,259 @@
1
+ import datetime
2
+ import math
3
+ from typing import List
4
+
5
+ import numpy as np
6
+
7
+ from .iono import gim
8
+ from .decorator import read_contents
9
+
10
+
11
+ def load(filename: str, gim_handler: gim.GimHandler):
12
+ """
13
+ Load an IONEX file and process its contents using the provided GIM handler.
14
+
15
+ :param filename: The path to the IONEX file to load.
16
+ :param gim_handler: An instance of a GIM handler to process the GIMs read from the file.
17
+ :return: The result of processing the IONEX file.
18
+ """
19
+
20
+ return _load(filename, gim_handler)
21
+
22
+
23
+ def write(filename: str, gims: List[gim.Gim], gim_type: gim.GimType,
24
+ pgm: str = "pygnss", runby: str = "pygnss", comment_lines: List[str] = []) -> None:
25
+ """
26
+ Write a list of GIMs to an IONEX file.
27
+
28
+ :param filename: The path to the IONEX file to write.
29
+ :param gims: A list of GIM objects to write to the file.
30
+ """
31
+
32
+ EXPONENT = -1
33
+ FACTOR = math.pow(10, EXPONENT)
34
+
35
+ if not gims:
36
+ raise ValueError("The list of GIMs is empty. Cannot write to the IONEX file.")
37
+
38
+ # Extract latitudes and longitudes from the first GIM
39
+ latitudes = gims[0].latitudes
40
+ longitudes = gims[0].longitudes
41
+
42
+ lon1 = longitudes[0]
43
+ lon2 = longitudes[-1]
44
+ dlon = longitudes[1] - longitudes[0]
45
+
46
+ # Ensure all GIMs have the same latitudes and longitudes
47
+ for gim_obj in gims:
48
+ if np.array_equal(gim_obj.latitudes, latitudes) == False or \
49
+ np.array_equal(gim_obj.longitudes, longitudes) == False:
50
+ raise ValueError("All GIMs must have the same latitudes and longitudes.")
51
+
52
+ # Sort the IONEX files by epoch
53
+ gims.sort(key=lambda gim: gim.epoch)
54
+
55
+ first_epoch = gims[0].epoch
56
+ last_epoch = gims[-1].epoch
57
+ n_maps = len(gims)
58
+
59
+ lat_0 = gims[0].latitudes[0]
60
+ lat_1 = gims[0].latitudes[-1]
61
+ dlat = gims[0].latitudes[1] - gims[0].latitudes[0]
62
+
63
+ # We will print the map from North to South, therefore check if the
64
+ # latitudes need to be reversed
65
+ latitude_reversal = lat_0 < lat_1
66
+ if latitude_reversal:
67
+ lat_0 = gims[0].latitudes[-1]
68
+ lat_1 = gims[0].latitudes[0]
69
+ dlat = gims[0].latitudes[0] - gims[0].latitudes[1]
70
+
71
+ lon_0 = gims[0].longitudes[0]
72
+ lon_1 = gims[0].longitudes[-1]
73
+ dlon = gims[0].longitudes[1] - gims[0].longitudes[0]
74
+
75
+ doc = ""
76
+
77
+ # Header
78
+ today = datetime.datetime.now()
79
+ epoch_str = today.strftime('%d-%b-%y %H:%M')
80
+
81
+ doc +=" 1.0 IONOSPHERE MAPS NEQUICK IONEX VERSION / TYPE\n"
82
+ doc +=f"{pgm[:20]:<20}{runby[:20]:<20}{epoch_str[:20]:<20}PGM / RUN BY / DATE\n"
83
+
84
+ for comment_line in comment_lines:
85
+ doc += f"{comment_line[:60]:<60}COMMENT\n"
86
+
87
+ doc += first_epoch.strftime(" %Y %m %d %H %M %S EPOCH OF FIRST MAP\n")
88
+ doc += last_epoch.strftime(" %Y %m %d %H %M %S EPOCH OF LAST MAP\n")
89
+ doc += " 0 INTERVAL\n"
90
+ doc += f"{n_maps:>6} # OF MAPS IN FILE\n"
91
+ doc += " NONE MAPPING FUNCTION\n"
92
+ doc += " 0.0 ELEVATION CUTOFF\n"
93
+ doc += " OBSERVABLES USED\n"
94
+ doc += " 6371.0 BASE RADIUS\n"
95
+ doc += " 2 MAP DIMENSION\n"
96
+ doc += " 450.0 450.0 0.0 HGT1 / HGT2 / DHGT\n"
97
+ doc += f" {lat_0:6.1f}{lat_1:6.1f}{dlat:6.1f} LAT1 / LAT2 / DLAT\n"
98
+ doc += f" {lon_0:6.1f}{lon_1:6.1f}{dlon:6.1f} LON1 / LON2 / DLON\n"
99
+ doc += f"{EXPONENT:>6} EXPONENT\n"
100
+ doc += " END OF HEADER\n"
101
+
102
+ # Write each GIM
103
+ for i_map, gim_obj in enumerate(gims):
104
+
105
+ doc += f"{i_map+1:>6} START OF {gim_type.name} MAP\n"
106
+
107
+ # Write the epoch
108
+ epoch = gim_obj.epoch
109
+ doc += epoch.strftime(" %Y %m %d %H %M %S EPOCH OF CURRENT MAP\n")
110
+
111
+
112
+ for i, _ in enumerate(latitudes):
113
+
114
+ if latitude_reversal:
115
+ i = len(latitudes) - 1 - i
116
+
117
+ lat = latitudes[i]
118
+
119
+ doc += f" {lat:6.1f}{lon1:6.1f}{lon2:6.1f}{dlon:6.1f} 450.0 LAT/LON1/LON2/DLON/H\n"
120
+
121
+ lat_row = gim_obj.vtec_values[i]
122
+ for j in range(0, len(longitudes), 16):
123
+ doc += "".join(f"{round(vtec / FACTOR):5d}" for vtec in lat_row[j:j+16]) + "\n"
124
+
125
+ doc += f"{i_map+1:>6} END OF {gim_type.name} MAP\n"
126
+
127
+ # Tail
128
+ doc += " END OF FILE\n"
129
+
130
+ with open(filename, "wt") as fh:
131
+ fh.write(doc)
132
+
133
+
134
+
135
+ def diff(filename_lhs: str, filename_rhs: str, output_file: str) -> None:
136
+ """
137
+ Compute the difference between two IONEX files and write the result in IONEX format
138
+ """
139
+
140
+ gim_handler_lhs = gim.GimHandlerArray()
141
+ gim_handler_rhs = gim.GimHandlerArray()
142
+
143
+ load(filename_lhs, gim_handler=gim_handler_lhs)
144
+ load(filename_rhs, gim_handler=gim_handler_rhs)
145
+
146
+ gim_diffs = gim.subtract_gims(gim_handler_lhs.vtec_gims, gim_handler_rhs.vtec_gims)
147
+
148
+ write(output_file, gim_diffs, gim.GimType.TEC)
149
+
150
+
151
+ @read_contents
152
+ def _load(doc: str, gim_handler: gim.GimHandler):
153
+ """
154
+ Parse the contents of an IONEX file and process each GIM using the provided handler.
155
+
156
+ :param doc: The contents of the IONEX file as a string.
157
+ :param gim_handler: An instance of a GIM handler to process the GIMs read from the file.
158
+ :raises ValueError: If the file is not a valid IONEX file or contains unsupported features.
159
+ """
160
+
161
+ lines = doc.splitlines()
162
+ n_lines = len(lines)
163
+ i_body = 0
164
+
165
+ latitudes_deg: List[float] = []
166
+ longitudes_deg: List[float] = []
167
+
168
+ header_mark_found = False
169
+
170
+ # Header
171
+ for i in range(n_lines):
172
+
173
+ line = lines[i]
174
+
175
+ if line[60:].startswith('IONEX VERSION / TYPE'):
176
+ header_mark_found = True
177
+
178
+ elif line[60:].startswith('HGT1 / HGT2 / DHGT'):
179
+ _hgt1, _hgt2, _dhgt = [float(v) for v in line.split()[:3]]
180
+ if _dhgt != 0.0:
181
+ raise ValueError('Multi-layer Ionex files not supported')
182
+
183
+ elif line[60:].startswith('LAT1 / LAT2 / DLAT'):
184
+ _lat1, _lat2, _dlat = [float(v) for v in line.split()[:3]]
185
+ latitudes_deg = np.arange(_lat1, _lat2 + _dlat/2, _dlat)
186
+
187
+ elif line[60:].startswith('LON1 / LON2 / DLON'):
188
+ _lon1, _lon2, _dlon = [float(v) for v in line.split()[:3]]
189
+ longitudes_deg = np.arange(_lon1, _lon2 + _dlon/2, _dlon)
190
+
191
+ elif line[60:].startswith('EXPONENT'):
192
+ exponent: float = float(line[:6])
193
+
194
+ elif line[60:].startswith('END OF HEADER'):
195
+ i_body = i + 1
196
+ break
197
+
198
+ if header_mark_found is False:
199
+ raise ValueError(f'The input does not seem to be a IONEX file [ {doc[:10]} ]')
200
+
201
+ n_lines_lat_row = int(np.ceil(len(longitudes_deg) / 16))
202
+
203
+ current_gim = None
204
+ gim_type = None
205
+
206
+ # Body
207
+ for i in range(i_body, n_lines):
208
+
209
+ line = lines[i]
210
+
211
+ if line[60:].startswith('START OF TEC MAP'):
212
+
213
+ i_lat_row = 0
214
+
215
+ gim_type = gim.GimType.TEC
216
+
217
+ elif line[60:].startswith('START OF RMS MAP'):
218
+
219
+ i_lat_row = 0
220
+
221
+ gim_type = gim.GimType.RMS
222
+
223
+ elif line[60:].startswith('EPOCH OF CURRENT MAP'):
224
+
225
+ # Initialize map
226
+ current_gim = gim.Gim(_parse_ionex_epoch(line),
227
+ longitudes_deg, latitudes_deg,
228
+ [[0] * len(longitudes_deg)] * len(latitudes_deg))
229
+
230
+ elif line[60:].startswith('LAT/LON1/LON2/DLON/H'):
231
+
232
+ lat_row = ''.join([lines[i + 1 + j] for j in range(n_lines_lat_row)])
233
+
234
+ values = np.array([float(v) for v in lat_row.split()])
235
+
236
+ i += n_lines_lat_row
237
+
238
+ current_gim.vtec_values[i_lat_row] = (values * np.power(10, exponent)).tolist()
239
+
240
+ i_lat_row = i_lat_row + 1
241
+
242
+ # If end of map reached, send them to appropriate processor
243
+ elif line[60:].startswith('END OF TEC MAP') or line[60:].startswith('END OF RMS MAP'):
244
+ gim_handler.process(current_gim, gim_type)
245
+
246
+
247
+ def _parse_ionex_epoch(ionex_line: str) -> datetime.datetime:
248
+ """
249
+ Parse the epoch from a IONEX line
250
+
251
+ >>> _parse_ionex_epoch(" 2024 12 11 0 0 14 EPOCH OF FIRST MAP")
252
+ datetime.datetime(2024, 12, 11, 0, 0, 14)
253
+ >>> _parse_ionex_epoch(" 2024 12 11 0 0 0 EPOCH OF CURRENT MAP")
254
+ datetime.datetime(2024, 12, 11, 0, 0)
255
+ """
256
+
257
+ _HEADER_EPOCH_FORMAT = " %Y %m %d %H %M %S"
258
+
259
+ return datetime.datetime.strptime(ionex_line[:36], _HEADER_EPOCH_FORMAT)
File without changes
pygnss/iono/gim.py ADDED
@@ -0,0 +1,131 @@
1
+ from abc import ABC, abstractmethod
2
+ from dataclasses import dataclass
3
+ import datetime
4
+ import enum
5
+ from typing import List
6
+
7
+ import numpy as np
8
+
9
+
10
+ class GimType(enum.Enum):
11
+ """
12
+ Type of Global Ionospheric Map (VTEC, RMS)
13
+ """
14
+
15
+ TEC = enum.auto()
16
+ RMS = enum.auto()
17
+
18
+
19
+ @dataclass
20
+ class Gim():
21
+ epoch: datetime.datetime
22
+ longitudes: List[float]
23
+ latitudes: List[float]
24
+ vtec_values: List[List[float]] # Grid of VTEC values n_latitudes (rows) x n_longitudes (columns)
25
+
26
+ def __sub__(self, other: 'Gim') -> 'Gim':
27
+ """
28
+ Subtract the VTEC values of another Gim from this Gim
29
+
30
+ :param other: The Gim to subtract.
31
+
32
+ :return: A new Gim with the resulting VTEC values.
33
+ """
34
+
35
+ return subtract(self, other)
36
+
37
+
38
+ class GimHandler(ABC):
39
+
40
+ @abstractmethod
41
+ def process(self, gim: Gim, type: GimType):
42
+ """
43
+ Process a GIM file
44
+ """
45
+ pass
46
+
47
+ class GimHandlerArray(GimHandler):
48
+ """
49
+ Handler to store the incoming GIMs in arrays
50
+ """
51
+
52
+ def __init__(self):
53
+ self.vtec_gims = []
54
+ self.rms_gims = []
55
+
56
+ def process(self, gim: Gim, type: GimType):
57
+ """
58
+ Process a GIM file
59
+ """
60
+
61
+ if type == GimType.TEC:
62
+ self.vtec_gims.append(gim)
63
+
64
+ elif type == GimType.RMS:
65
+ self.rms_gims.append(gim)
66
+
67
+ else:
68
+ raise ValueError(f'Gim Type [ {type} ] not supported')
69
+
70
+
71
+ def subtract(lhs: Gim, rhs: Gim) -> Gim:
72
+ """
73
+ Subtract the VTEC values of two GIMs (lhs - rhs)
74
+
75
+ :param lhs: Left-hand operand
76
+ :param rhs: Right-hand operand
77
+
78
+ :return: A new Gim with the resulting difference of VTEC values.
79
+
80
+ :raises ValueError: If the dimensions of the GIMs do not match.
81
+ """
82
+
83
+ if lhs.epoch != rhs.epoch:
84
+ raise ValueError(f"Epochs of both GIMs differ: {lhs.epoch} != {rhs.epoch}")
85
+
86
+ if np.array_equal(lhs.latitudes, rhs.latitudes) == False:
87
+ raise ValueError("Latitudes do not match between the two GIMs.")
88
+
89
+ if np.array_equal(lhs.longitudes, rhs.longitudes) == False:
90
+ raise ValueError("Longitude do not match between the two GIMs.")
91
+
92
+ vtec_diff = np.subtract(lhs.vtec_values, rhs.vtec_values)
93
+
94
+ return Gim(
95
+ epoch=lhs.epoch, # Keep the epoch of the first Gim
96
+ longitudes=lhs.longitudes,
97
+ latitudes=lhs.latitudes,
98
+ vtec_values=vtec_diff.tolist(),
99
+ )
100
+
101
+
102
+ def subtract_gims(lhs: List[Gim], rhs: List[Gim]) -> List[Gim]:
103
+ """
104
+ Subtract the VTEC values of two lists of GIMs (lhs - rhs).
105
+
106
+ The subtraction is performed only for GIMs with matching epochs, latitudes, and longitudes.
107
+ If a GIM in one list does not have a matching epoch in the other list, it is ignored.
108
+
109
+ :param lhs: The first list of GIMs (left-hand operand).
110
+ :param rhs: The second list of GIMs (right-hand operand).
111
+ :return: A list of GIMs resulting from the subtraction.
112
+ :raises ValueError: If latitudes or longitudes do not match for matching epochs.
113
+ """
114
+ result = []
115
+
116
+ # Create a dictionary for quick lookup of GIMs in the rhs list by epoch
117
+ rhs_dict = {gim.epoch: gim for gim in rhs}
118
+
119
+ for gim_lhs in lhs:
120
+
121
+ # Check if there is a matching epoch in the rhs list
122
+ if gim_lhs.epoch in rhs_dict:
123
+ gim_rhs = rhs_dict[gim_lhs.epoch]
124
+
125
+ try:
126
+ result.append(gim_lhs - gim_rhs)
127
+ except ValueError as e:
128
+ raise ValueError(f"Error subtracting GIMs for epoch {gim_lhs.epoch}: {e}")
129
+
130
+ return result
131
+
pygnss/nequick.py CHANGED
@@ -6,6 +6,9 @@ import numpy as np
6
6
 
7
7
  import nequick
8
8
 
9
+ from pygnss import ionex
10
+ import pygnss.iono.gim
11
+
9
12
 
10
13
  class GimIonexHandler(nequick.GimHandler):
11
14
  """
@@ -32,102 +35,23 @@ class GimIonexHandler(nequick.GimHandler):
32
35
 
33
36
  self._gims.append(gim)
34
37
 
35
- def to_ionex(self, pgm: str = "pygnss", runby: str = "pygnss") -> str:
36
-
37
- EXPONENT = -1
38
-
39
- # Sort the IONEX files by epoch
40
- self._gims.sort(key=lambda gim: gim.epoch)
41
-
42
- first_epoch = self._gims[0].epoch
43
- last_epoch = self._gims[-1].epoch
44
- n_maps = len(self._gims)
45
-
46
- lat_0 = self._gims[0].latitudes[0]
47
- lat_1 = self._gims[0].latitudes[-1]
48
- dlat = self._gims[0].latitudes[1] - self._gims[0].latitudes[0]
49
-
50
- # We will print the map from North to South, therefore check if the
51
- # latitudes need to be reversed
52
- latitude_reversal = lat_0 < lat_1
53
- if latitude_reversal:
54
- lat_0 = self._gims[0].latitudes[-1]
55
- lat_1 = self._gims[0].latitudes[0]
56
- dlat = self._gims[0].latitudes[0] - self._gims[0].latitudes[1]
57
-
58
- lon_0 = self._gims[0].longitudes[0]
59
- lon_1 = self._gims[0].longitudes[-1]
60
- dlon = self._gims[0].longitudes[1] - self._gims[0].longitudes[0]
61
-
62
- doc = ""
63
-
64
- # Header
65
- today = datetime.datetime.now()
66
- epoch_str = today.strftime('%d-%b-%y %H:%M')
67
-
68
- doc +=" 1.0 IONOSPHERE MAPS NEQUICK IONEX VERSION / TYPE\n"
69
- doc +=f"{pgm[:20]:<20}{runby[:20]:<20}{epoch_str[:20]:<20}PGM / RUN BY / DATE\n"
70
- doc +="Maps computed using the NeQuick model with the following COMMENT\n"
71
- doc +="coefficients: COMMENT\n"
72
- doc += f"{EXPONENT:>6} EXPONENT\n"
73
- doc +=f"a0={self._coeffs.a0:<17.6f}a1={self._coeffs.a1:<17.8f}a2={self._coeffs.a2:<17.11f}COMMENT\n"
74
- doc += first_epoch.strftime(" %Y %m %d %H %M %S EPOCH OF FIRST MAP\n")
75
- doc += last_epoch.strftime(" %Y %m %d %H %M %S EPOCH OF LAST MAP\n")
76
- doc += " 0 INTERVAL\n"
77
- doc += f"{n_maps:>6} # OF MAPS IN FILE\n"
78
- doc += " NONE MAPPING FUNCTION\n"
79
- doc += " 0.0 ELEVATION CUTOFF\n"
80
- doc += " OBSERVABLES USED\n"
81
- doc += " 6371.0 BASE RADIUS\n"
82
- doc += " 2 MAP DIMENSION\n"
83
- doc += " 450.0 450.0 0.0 HGT1 / HGT2 / DHGT\n"
84
- doc += f" {lat_0:6.1f}{lat_1:6.1f}{dlat:6.1f} LAT1 / LAT2 / DLAT\n"
85
- doc += f" {lon_0:6.1f}{lon_1:6.1f}{dlon:6.1f} LON1 / LON2 / DLON\n"
86
- doc += " END OF HEADER\n"
87
-
88
- # Body: For each GIM file, write the VTEC values
89
- for i, gim in enumerate(self._gims):
38
+ def to_ionex(self, filename: str, pgm: str = "pygnss", runby: str = "pygnss") -> None:
90
39
 
91
- doc += f"{i+1:>6} START OF TEC MAP\n"
92
- doc += gim.epoch.strftime(" %Y %m %d %H %M %S EPOCH OF CURRENT MAP\n")
40
+ comment_lines = [
41
+ "Maps computed using the NeQuick model with the following",
42
+ "coefficients:",
43
+ f"a0={self._coeffs.a0:<17.6f}a1={self._coeffs.a1:<17.8f}a2={self._coeffs.a2:<17.11f}"
44
+ ]
93
45
 
94
- n_latitudes = len(gim.latitudes)
46
+ ionex.write(filename, self._gims, pygnss.iono.gim.GimType.TEC, pgm, runby,
47
+ comment_lines=comment_lines)
95
48
 
96
- for i_lat, lat in enumerate(gim.latitudes):
97
49
 
98
- if latitude_reversal:
99
- i_lat = n_latitudes - 1 - i_lat
100
-
101
- lat = gim.latitudes[i_lat]
102
- doc += f" {lat:6.1f}{lon_0:6.1f}{lon_1:6.1f}{dlon:6.1f} 450.0 LAT/LON1/LON2/DLON/H"
103
-
104
- lat_row = gim.vtec_values[i_lat]
105
- for i_lon, _ in enumerate(gim.longitudes):
106
-
107
- if i_lon % 16 == 0:
108
- doc += "\n"
109
-
110
- vtec = lat_row[i_lon] / math.pow(10, EXPONENT)
111
- doc += f"{int(vtec):>5d}"
112
-
113
- doc += "\n"
114
-
115
- doc += f"{i+1:>6} END OF TEC MAP\n"
116
-
117
- # Tail
118
- doc += " END OF FILE\n"
119
-
120
- return doc
121
-
122
- def to_ionex(coeffs: nequick.Coefficients, dates: List[datetime.datetime]) -> str:
123
-
124
- doc = None
50
+ def to_ionex(filename: str, coeffs: nequick.Coefficients, dates: List[datetime.datetime]):
125
51
 
126
52
  gim_handler = GimIonexHandler(coeffs)
127
53
 
128
54
  for date in dates:
129
55
  nequick.to_gim(coeffs, date, gim_handler=gim_handler)
130
56
 
131
- doc = gim_handler.to_ionex()
132
-
133
- return doc
57
+ gim_handler.to_ionex(filename)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygnss
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: Package with utilities and tools for GNSS data processing
5
5
  Author-email: Miquel Garcia-Fernandez <miquel@mgfernan.com>
6
6
  License: MIT
@@ -1,13 +1,14 @@
1
- pygnss/__init__.py,sha256=VrXpHDu3erkzwl_WXrqINBm9xWkcyUy53IQOj042dOs,22
1
+ pygnss/__init__.py,sha256=LBK46heutvn3KmsCrKIYu8RQikbfnjZaj2xFrXaeCzQ,22
2
2
  pygnss/_c_ext.cpython-313-i386-linux-musl.so,sha256=9CwtpxkeyS2ajcDuwlLpG1R9QsgwtBkl6wMFDeK4LNw,81152
3
3
  pygnss/cl.py,sha256=ISmd2RjikUMmj3nLPN0VSjvQLG5rLizp2X2ajeBkoDE,4509
4
4
  pygnss/constants.py,sha256=1hF6K92X6E6Ofo0rAuCBCgrwln9jxio26RV2a6vyURk,133
5
- pygnss/decorator.py,sha256=ldlZuvwuIlJf2pkoWteyXyp5tLds8KRkphrPsrURw9U,491
5
+ pygnss/decorator.py,sha256=qB-0jl2GTEHJdvmDruNll5X3RrdySssv94u9Hok5-lA,1438
6
6
  pygnss/file.py,sha256=kkMBWjoTPkxJD1UgH0mXJT2fxnhU8u7_l2Ph5Xz2-hY,933
7
7
  pygnss/geodetic.py,sha256=3q8Rpl4b5CxGlhdn1nQRBHHSW1v-0PBFz54zOeVyO74,33633
8
8
  pygnss/hatanaka.py,sha256=P9XG6bZwUzfAPYn--6-DXfFQIEefeimE7fMJm_DF5zE,1951
9
+ pygnss/ionex.py,sha256=n_5IADZcTg1nCKApT19fvnAh5823QgbRkX8oPYDdpSI,9244
9
10
  pygnss/logger.py,sha256=4kvcTWXPoiG-MlyP6B330l4Fu7MfCuDjuIlIiLA8f1Y,1479
10
- pygnss/nequick.py,sha256=ewtccKH1tdh8z6iuM6yEvDCakOKbzgkMnEWh3jWg6R4,5490
11
+ pygnss/nequick.py,sha256=tSsUw3FoRWS-TBKKVb0_c90DFz_2jUTkUvNPsGQOLA8,1722
11
12
  pygnss/rinex.py,sha256=LsOOh3Fc263kkM8KOUBNeMeIAmbOn2ASSBO4rAUJWj8,68783
12
13
  pygnss/sinex.py,sha256=nErOmGCFFmGSnmWGNTJhaj3yZ6IIB8GgtW5WPypJc6U,3057
13
14
  pygnss/stats.py,sha256=GYZfcyDvbM9xamWIyVlqyN5-DPJzTLJrybRrcNV6Z6o,1912
@@ -25,13 +26,15 @@ pygnss/gnss/edit.py,sha256=T1r0WbJmt8tLJpG_IIsy4Atej6cy0IStBaSGxw0S5ho,1884
25
26
  pygnss/gnss/observables.py,sha256=0x0NLkTjxf8cO9F_f_Q1b-1hEeoNjWB2x-53ecUEv0M,1656
26
27
  pygnss/gnss/residuals.py,sha256=8qKGNOYkrqxHGOSjIfH21K82PAqEh2068kf78j5usL8,1244
27
28
  pygnss/gnss/types.py,sha256=lmL15KRckRiTwVkYvGzF4c1BrojnQlegrYCXSz1hGaI,10377
29
+ pygnss/iono/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ pygnss/iono/gim.py,sha256=bJ-1jRIrkZj790fuCk3np72NrPaG_Qvx-EVZsvWMfsk,3521
28
31
  pygnss/orbit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
32
  pygnss/orbit/kepler.py,sha256=QORTgg5yBtsQXxLWSzoZ1pmh-CwPiZlFdIYqhQhv1a0,1745
30
33
  pygnss/orbit/tle.py,sha256=6CIEielPgui3DXNv46XxOGlig31ROIwjH42xLGaeE5M,5905
31
34
  pygnss/parsers/rtklib/stats.py,sha256=YV6yadxMeQMQYZvsUCaSf4ZTpK8Bbv3f2xgu0l4PekA,5449
32
- pygnss-0.3.0.dist-info/METADATA,sha256=kDz5U4Y1CzGww42HgE5Fkj05KGNXZOs1SkYhuiHmPL8,1659
33
- pygnss-0.3.0.dist-info/WHEEL,sha256=qeaIuaJB-6C86fEu4NQSDId-fDkNNB1_xWNvSM-4W7I,110
34
- pygnss-0.3.0.dist-info/entry_points.txt,sha256=mCuKrljB_wh9ZQVROiId9m68EDbTiY1oef_L1N3IDDA,262
35
- pygnss-0.3.0.dist-info/top_level.txt,sha256=oZRSR-qOv98VW2PRRMGCVNCJmewcJjyJYmxzxfeimtg,7
36
- pygnss-0.3.0.dist-info/RECORD,,
37
- pygnss-0.3.0.dist-info/licenses/LICENSE,sha256=Wwany6RAAZ9vVHjFLA9KBJ0HE77d52s2NOUA1CPAEug,1067
35
+ pygnss-0.5.0.dist-info/METADATA,sha256=9HBJeG5a0H-MIxtMwwkPP3DqulH3GIoX_8l5O0E77pk,1659
36
+ pygnss-0.5.0.dist-info/WHEEL,sha256=qeaIuaJB-6C86fEu4NQSDId-fDkNNB1_xWNvSM-4W7I,110
37
+ pygnss-0.5.0.dist-info/entry_points.txt,sha256=mCuKrljB_wh9ZQVROiId9m68EDbTiY1oef_L1N3IDDA,262
38
+ pygnss-0.5.0.dist-info/top_level.txt,sha256=oZRSR-qOv98VW2PRRMGCVNCJmewcJjyJYmxzxfeimtg,7
39
+ pygnss-0.5.0.dist-info/RECORD,,
40
+ pygnss-0.5.0.dist-info/licenses/LICENSE,sha256=Wwany6RAAZ9vVHjFLA9KBJ0HE77d52s2NOUA1CPAEug,1067
File without changes