cloudnetpy 1.66.12__py3-none-any.whl → 1.66.14__py3-none-any.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.
- cloudnetpy/instruments/radiometrics.py +89 -24
- cloudnetpy/metadata.py +5 -0
- cloudnetpy/plotting/plot_meta.py +19 -0
- cloudnetpy/plotting/plotting.py +3 -1
- cloudnetpy/version.py +1 -1
- {cloudnetpy-1.66.12.dist-info → cloudnetpy-1.66.14.dist-info}/METADATA +13 -13
- {cloudnetpy-1.66.12.dist-info → cloudnetpy-1.66.14.dist-info}/RECORD +11 -11
- {cloudnetpy-1.66.12.dist-info → cloudnetpy-1.66.14.dist-info}/LICENSE +0 -0
- {cloudnetpy-1.66.12.dist-info → cloudnetpy-1.66.14.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.66.12.dist-info → cloudnetpy-1.66.14.dist-info}/entry_points.txt +0 -0
- {cloudnetpy-1.66.12.dist-info → cloudnetpy-1.66.14.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ import csv
|
|
4
4
|
import datetime
|
5
5
|
import logging
|
6
6
|
import os
|
7
|
+
import re
|
7
8
|
from operator import attrgetter
|
8
9
|
from typing import Any, NamedTuple
|
9
10
|
|
@@ -11,8 +12,9 @@ import numpy as np
|
|
11
12
|
|
12
13
|
from cloudnetpy import output, utils
|
13
14
|
from cloudnetpy.cloudnetarray import CloudnetArray
|
14
|
-
from cloudnetpy.exceptions import ValidTimeStampError
|
15
|
+
from cloudnetpy.exceptions import InconsistentDataError, ValidTimeStampError
|
15
16
|
from cloudnetpy.instruments import instruments
|
17
|
+
from cloudnetpy.metadata import MetaData
|
16
18
|
|
17
19
|
|
18
20
|
def radiometrics2nc(
|
@@ -65,7 +67,7 @@ def radiometrics2nc(
|
|
65
67
|
if radiometrics.date is None:
|
66
68
|
msg = "Failed to find valid timestamps from Radiometrics file(s)."
|
67
69
|
raise ValidTimeStampError(msg)
|
68
|
-
attributes = output.add_time_attribute(
|
70
|
+
attributes = output.add_time_attribute(ATTRIBUTES, radiometrics.date)
|
69
71
|
output.update_attributes(radiometrics.data, attributes)
|
70
72
|
return output.save_level1b(radiometrics, output_file, uuid)
|
71
73
|
|
@@ -91,6 +93,7 @@ class Radiometrics:
|
|
91
93
|
self.raw_data: list[Record] = []
|
92
94
|
self.data: dict = {}
|
93
95
|
self.instrument = instruments.RADIOMETRICS
|
96
|
+
self.ranges: list[str] = []
|
94
97
|
|
95
98
|
def read_raw_data(self) -> None:
|
96
99
|
"""Reads Radiometrics raw data."""
|
@@ -98,14 +101,21 @@ class Radiometrics:
|
|
98
101
|
unknown_record_types = set()
|
99
102
|
rows = []
|
100
103
|
with open(self.filename, encoding="utf8") as infile:
|
101
|
-
reader = csv.reader(infile)
|
104
|
+
reader = csv.reader(infile, skipinitialspace=True)
|
102
105
|
for row in reader:
|
103
106
|
if row[0] == "Record":
|
104
107
|
if row[1] != "Date/Time":
|
105
108
|
msg = "Unexpected header in Radiometrics file"
|
106
109
|
raise RuntimeError(msg)
|
107
110
|
record_type = int(row[2])
|
108
|
-
|
111
|
+
columns = row[3:]
|
112
|
+
record_columns[record_type] = columns
|
113
|
+
if record_type in (10, 400):
|
114
|
+
self.ranges = [
|
115
|
+
column
|
116
|
+
for column in columns
|
117
|
+
if re.fullmatch(r"\d+\.\d+", column)
|
118
|
+
]
|
109
119
|
else:
|
110
120
|
record_type = int(row[2])
|
111
121
|
block_type = record_type // 10 * 10
|
@@ -125,33 +135,67 @@ class Radiometrics:
|
|
125
135
|
)
|
126
136
|
rows.append(record)
|
127
137
|
|
128
|
-
|
129
|
-
rows.sort(key=attrgetter("row_number"))
|
130
|
-
|
131
|
-
for data_row in rows:
|
132
|
-
# Use the first row of a block and skip the rest which should
|
133
|
-
# contain the same values in the first columns.
|
134
|
-
if data_row.block_index == 0:
|
135
|
-
self.raw_data.append(data_row)
|
138
|
+
self.raw_data = sorted(rows, key=attrgetter("row_number"))
|
136
139
|
|
137
140
|
def read_data(self) -> None:
|
138
141
|
"""Reads values."""
|
139
142
|
times = []
|
140
143
|
lwps = []
|
141
144
|
iwvs = []
|
145
|
+
irts = []
|
146
|
+
temps = []
|
147
|
+
rhs = []
|
148
|
+
ahs = []
|
149
|
+
block_titles = {}
|
142
150
|
for record in self.raw_data:
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
151
|
+
if record.block_type == 100:
|
152
|
+
block_type = int(record.values["Record Type"]) - 1
|
153
|
+
title = record.values["Title"]
|
154
|
+
block_titles[block_type] = title
|
155
|
+
if title := block_titles.get(record.block_type + record.block_index):
|
156
|
+
if title == "Temperature (K)":
|
157
|
+
temps.append(
|
158
|
+
[float(record.values[column]) for column in self.ranges]
|
159
|
+
)
|
160
|
+
elif title == "Relative Humidity (%)":
|
161
|
+
rhs.append([float(record.values[column]) for column in self.ranges])
|
162
|
+
elif title == "Vapor Density (g/m^3)":
|
163
|
+
ahs.append([float(record.values[column]) for column in self.ranges])
|
164
|
+
elif record.block_type == 10:
|
165
|
+
if record.block_index == 0:
|
166
|
+
lwp = record.values["Lqint(mm)"]
|
167
|
+
iwv = record.values["Vint(cm)"]
|
168
|
+
irt = record.values["Tir(K)"]
|
169
|
+
times.append(record.timestamp)
|
170
|
+
lwps.append(float(lwp))
|
171
|
+
iwvs.append(float(iwv))
|
172
|
+
irts.append([float(irt)])
|
173
|
+
temps.append(
|
174
|
+
[float(record.values[column]) for column in self.ranges]
|
175
|
+
)
|
176
|
+
elif record.block_index == 1:
|
177
|
+
ahs.append([float(record.values[column]) for column in self.ranges])
|
178
|
+
elif record.block_index == 2:
|
179
|
+
rhs.append([float(record.values[column]) for column in self.ranges])
|
180
|
+
elif record.block_type == 200:
|
181
|
+
irt = record.values["Tir(K)"]
|
182
|
+
irts.append([float(irt)])
|
183
|
+
elif record.block_type == 300:
|
184
|
+
lwp = record.values["Int. Liquid(mm)"]
|
185
|
+
iwv = record.values["Int. Vapor(cm)"]
|
186
|
+
times.append(record.timestamp)
|
187
|
+
lwps.append(float(lwp))
|
188
|
+
iwvs.append(float(iwv))
|
189
|
+
n_time = len(times)
|
152
190
|
self.data["time"] = np.array(times, dtype="datetime64[s]")
|
153
|
-
self.data["lwp"] = np.array(lwps
|
154
|
-
self.data["iwv"] = np.array(iwvs
|
191
|
+
self.data["lwp"] = np.array(lwps) # mm => kg m-2
|
192
|
+
self.data["iwv"] = np.array(iwvs) * 10 # cm => kg m-2
|
193
|
+
self.data["irt"] = np.array(irts[:n_time])
|
194
|
+
self.data["temperature"] = np.array(temps[:n_time])
|
195
|
+
self.data["relative_humidity"] = np.array(rhs[:n_time]) / 100 # % => 1
|
196
|
+
self.data["absolute_humidity"] = (
|
197
|
+
np.array(ahs[:n_time]) / 1000
|
198
|
+
) # g m-3 => kg m-3
|
155
199
|
|
156
200
|
|
157
201
|
class RadiometricsCombined:
|
@@ -165,8 +209,14 @@ class RadiometricsCombined:
|
|
165
209
|
self.data = {}
|
166
210
|
self.date = None
|
167
211
|
for obj in objs:
|
212
|
+
if obj.ranges != objs[0].ranges:
|
213
|
+
msg = "Inconsistent range between files"
|
214
|
+
raise InconsistentDataError(msg)
|
168
215
|
for key in obj.data:
|
169
216
|
self.data = utils.append_data(self.data, key, obj.data[key])
|
217
|
+
ranges = [float(x) for x in objs[0].ranges]
|
218
|
+
self.data["range"] = np.array(ranges) * 1000 # m => km
|
219
|
+
self.data["height"] = self.data["range"] + self.site_meta["altitude"]
|
170
220
|
self.instrument = instruments.RADIOMETRICS
|
171
221
|
|
172
222
|
def screen_time(self, expected_date: datetime.date | None) -> None:
|
@@ -179,6 +229,8 @@ class RadiometricsCombined:
|
|
179
229
|
if np.count_nonzero(valid_mask) == 0:
|
180
230
|
raise ValidTimeStampError
|
181
231
|
for key in self.data:
|
232
|
+
if key in ("range", "height"):
|
233
|
+
continue
|
182
234
|
self.data[key] = self.data[key][valid_mask]
|
183
235
|
|
184
236
|
def time_to_fractional_hours(self) -> None:
|
@@ -188,7 +240,12 @@ class RadiometricsCombined:
|
|
188
240
|
def data_to_cloudnet_arrays(self) -> None:
|
189
241
|
"""Converts arrays to CloudnetArrays."""
|
190
242
|
for key, array in self.data.items():
|
191
|
-
|
243
|
+
dimensions = (
|
244
|
+
("time", "range")
|
245
|
+
if key in ("temperature", "relative_humidity", "absolute_humidity")
|
246
|
+
else None
|
247
|
+
)
|
248
|
+
self.data[key] = CloudnetArray(array, key, dimensions=dimensions)
|
192
249
|
|
193
250
|
def add_meta(self) -> None:
|
194
251
|
"""Adds some metadata."""
|
@@ -213,3 +270,11 @@ def _parse_datetime(text: str) -> datetime.datetime:
|
|
213
270
|
minute,
|
214
271
|
second,
|
215
272
|
)
|
273
|
+
|
274
|
+
|
275
|
+
ATTRIBUTES = {
|
276
|
+
"irt": MetaData(
|
277
|
+
long_name="Infrared brightness temperatures",
|
278
|
+
units="K",
|
279
|
+
),
|
280
|
+
}
|
cloudnetpy/metadata.py
CHANGED
@@ -144,6 +144,11 @@ COMMON_ATTRIBUTES = {
|
|
144
144
|
standard_name="relative_humidity",
|
145
145
|
units="1",
|
146
146
|
),
|
147
|
+
"absolute_humidity": MetaData(
|
148
|
+
long_name="Absolute humidity",
|
149
|
+
standard_name="mass_concentration_of_water_vapor_in_air",
|
150
|
+
units="kg m-3",
|
151
|
+
),
|
147
152
|
"wind_speed": MetaData(
|
148
153
|
long_name="Wind speed",
|
149
154
|
standard_name="wind_speed",
|
cloudnetpy/plotting/plot_meta.py
CHANGED
@@ -137,6 +137,25 @@ ATTRIBUTES = {
|
|
137
137
|
plot_range=(0, 50 / 3600000),
|
138
138
|
)
|
139
139
|
},
|
140
|
+
"mwr": {
|
141
|
+
"temperature": PlotMeta(
|
142
|
+
cmap="coolwarm",
|
143
|
+
plot_range=(223.15, 323.15),
|
144
|
+
contour=True,
|
145
|
+
time_smoothing_duration=_MWR_SINGLE_SMOOTHING,
|
146
|
+
),
|
147
|
+
"relative_humidity": PlotMeta(
|
148
|
+
plot_range=(0, 120),
|
149
|
+
contour=True,
|
150
|
+
time_smoothing_duration=_MWR_SINGLE_SMOOTHING,
|
151
|
+
),
|
152
|
+
"absolute_humidity": PlotMeta(
|
153
|
+
plot_range=(1e-4, 1e-2),
|
154
|
+
log_scale=True,
|
155
|
+
contour=True,
|
156
|
+
time_smoothing_duration=_MWR_SINGLE_SMOOTHING,
|
157
|
+
),
|
158
|
+
},
|
140
159
|
"mwr-single": {
|
141
160
|
"temperature": PlotMeta(
|
142
161
|
cmap="coolwarm",
|
cloudnetpy/plotting/plotting.py
CHANGED
@@ -627,13 +627,15 @@ class Plot1D(Plot):
|
|
627
627
|
label = "Freq"
|
628
628
|
value = figure_data.file.variables["frequency"][freq_ind]
|
629
629
|
unit = "GHz"
|
630
|
-
|
630
|
+
elif "ir_wavelength" in figure_data.file.variables:
|
631
631
|
label = "WL"
|
632
632
|
variable = figure_data.file.variables["ir_wavelength"]
|
633
633
|
# `ir_wavelength` is scalar in old files
|
634
634
|
value = variable[:] if len(variable.shape) == 0 else variable[freq_ind]
|
635
635
|
value /= 1e-6 # m to μm
|
636
636
|
unit = "μm"
|
637
|
+
else:
|
638
|
+
return
|
637
639
|
self._ax.text(
|
638
640
|
0.0,
|
639
641
|
-0.13,
|
cloudnetpy/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: cloudnetpy
|
3
|
-
Version: 1.66.
|
3
|
+
Version: 1.66.14
|
4
4
|
Summary: Python package for Cloudnet processing
|
5
5
|
Author: Simo Tukiainen
|
6
6
|
License: MIT License
|
@@ -75,7 +75,7 @@ classified as ice, liquid, aerosol, insects, and so on.
|
|
75
75
|
Subsequently, geophysical products such as ice water content can be
|
76
76
|
retrieved in further processing steps. See [Illingworth et al. (2007)](https://doi.org/10.1175/BAMS-88-6-883) for more details about the concept.
|
77
77
|
|
78
|
-
CloudnetPy is a rewritten version of the original Cloudnet
|
78
|
+
CloudnetPy is a rewritten version of the original Cloudnet MATLAB code. It features several revised methods, extensive documentation, and more.
|
79
79
|
|
80
80
|
- CloudnetPy documentation: <https://actris-cloudnet.github.io/cloudnetpy/>
|
81
81
|
- Cloudnet data portal: <https://cloudnet.fmi.fi>
|
@@ -84,13 +84,13 @@ CloudnetPy is a rewritten version of the original Cloudnet Matlab code. It featu
|
|
84
84
|
|
85
85
|
## Installation
|
86
86
|
|
87
|
-
### From PyPI
|
87
|
+
### Option 1: From PyPI
|
88
88
|
|
89
89
|
```
|
90
90
|
python3 -m pip install cloudnetpy
|
91
91
|
```
|
92
92
|
|
93
|
-
### From the source
|
93
|
+
### Option 2: From the source
|
94
94
|
|
95
95
|
```sh
|
96
96
|
git clone https://github.com/actris-cloudnet/cloudnetpy
|
@@ -101,6 +101,14 @@ python3 -m pip install --upgrade pip
|
|
101
101
|
python3 -m pip install .
|
102
102
|
```
|
103
103
|
|
104
|
+
### Verification
|
105
|
+
|
106
|
+
To verify the installation:
|
107
|
+
|
108
|
+
```sh
|
109
|
+
cloudnetpy --help
|
110
|
+
```
|
111
|
+
|
104
112
|
## Citing
|
105
113
|
|
106
114
|
If you wish to acknowledge CloudnetPy in your publication, please cite:
|
@@ -123,12 +131,7 @@ pre-commit install
|
|
123
131
|
Run unit tests:
|
124
132
|
|
125
133
|
```sh
|
126
|
-
python3 -m pytest
|
127
|
-
```
|
128
|
-
|
129
|
-
Run single unit test:
|
130
|
-
|
131
|
-
```sh
|
134
|
+
python3 -m pytest
|
132
135
|
python3 -m pytest tests/unit/test_hatpro.py
|
133
136
|
```
|
134
137
|
|
@@ -136,9 +139,6 @@ Run end-to-end tests:
|
|
136
139
|
|
137
140
|
```sh
|
138
141
|
python3 tests/e2e_test.py
|
139
|
-
```
|
140
|
-
|
141
|
-
```sh
|
142
142
|
for f in cloudnetpy/model_evaluation/tests/e2e/*/main.py; do $f; done
|
143
143
|
```
|
144
144
|
|
@@ -5,11 +5,11 @@ cloudnetpy/concat_lib.py,sha256=RiL6fgaKjX2YyXl0BonbCjLXV2voIKPcQcdR9ZkQ8QA,1188
|
|
5
5
|
cloudnetpy/constants.py,sha256=RDB9aqpBRztk3QVCFgsmi9fwhtLuit_0WJrt0D6sDcc,736
|
6
6
|
cloudnetpy/datasource.py,sha256=FcWS77jz56gIzwnbafDLdj-HjAyu0P_VtY7gkeVZThU,7952
|
7
7
|
cloudnetpy/exceptions.py,sha256=ns48useL9RN3mPh7CqIiLA19VI9OmVbyRsKTkwbThF8,1760
|
8
|
-
cloudnetpy/metadata.py,sha256=
|
8
|
+
cloudnetpy/metadata.py,sha256=DOGt7EQLS-AVJEszrUrpXr3gHVQv655FzeCzKrOPvoU,5477
|
9
9
|
cloudnetpy/output.py,sha256=lq4YSeMT_d-j4rlQkKm9KIZ8boupTBBBKV1eUawpmCI,15672
|
10
10
|
cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
cloudnetpy/utils.py,sha256=JksYOwf9ORiR_QpoKrTe1JJwXpPYJj-wlwaZKCHoh3o,29744
|
12
|
-
cloudnetpy/version.py,sha256=
|
12
|
+
cloudnetpy/version.py,sha256=2QGNMZni3ixostxBEdUnnkW51Mi01KeZjZ44M5gaAgU,73
|
13
13
|
cloudnetpy/categorize/__init__.py,sha256=s-SJaysvVpVVo5kidiruWQO6p3gv2TXwY1wEHYO5D6I,44
|
14
14
|
cloudnetpy/categorize/atmos_utils.py,sha256=9-ymI6i1xASf-XAFyO87FaTfvq6bF89N1i_27OkUp-M,10104
|
15
15
|
cloudnetpy/categorize/attenuation.py,sha256=Y_-fzmQTltWTqIZTulJhovC7a6ifpMcaAazDJcnMIOc,990
|
@@ -49,7 +49,7 @@ cloudnetpy/instruments/mrr.py,sha256=eeAzCp3CiHGauywjwvMUAFwZ4vBOZMcd3IlF8KsrLQo
|
|
49
49
|
cloudnetpy/instruments/nc_lidar.py,sha256=5gQG9PApnNPrHmS9_zanl8HEYIQuGRpbnzC3wfTcOyQ,1705
|
50
50
|
cloudnetpy/instruments/nc_radar.py,sha256=ctmb0tJHkRoz-Ic8UAw4_v4VfS27r22_4X_1s4mUAas,6990
|
51
51
|
cloudnetpy/instruments/pollyxt.py,sha256=YuVEHr-BX31rtVOFsWGU-SQFAmcxpXL26eyCVMz_9hw,8933
|
52
|
-
cloudnetpy/instruments/radiometrics.py,sha256=
|
52
|
+
cloudnetpy/instruments/radiometrics.py,sha256=troxDHj7DDx04dIj0Qe8pRDWGz7W_qxmq7H620_jE-s,10481
|
53
53
|
cloudnetpy/instruments/rpg.py,sha256=IozvBJ8_qXTPqtp58FQwRsoI5_aI3-kycpXugZkS0d4,17462
|
54
54
|
cloudnetpy/instruments/rpg_reader.py,sha256=ThztFuVrWxhmWVAfZTfQDeUiKK1XMTbtv08IBe8GK98,11364
|
55
55
|
cloudnetpy/instruments/toa5.py,sha256=CfmmBMv5iMGaWHIGBK01Rw24cuXC1R1RMNTXkmsm340,1760
|
@@ -100,8 +100,8 @@ cloudnetpy/model_evaluation/tests/unit/test_plotting.py,sha256=h9V8JKmrO4v9bOvv-
|
|
100
100
|
cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py,sha256=Ra3r4V0qbqkpDuaTYvEIbaasl0nZ5gmTLR4eGC0glBQ,9724
|
101
101
|
cloudnetpy/model_evaluation/tests/unit/test_tools.py,sha256=Ia_VrLdV2NstX5gbx_3AZTOAlrgLAy_xFZ8fHYVX0xI,3817
|
102
102
|
cloudnetpy/plotting/__init__.py,sha256=lg9Smn4BI0dVBgnDLC3JVJ4GmwoSnO-qoSd4ApvwV6Y,107
|
103
|
-
cloudnetpy/plotting/plot_meta.py,sha256=
|
104
|
-
cloudnetpy/plotting/plotting.py,sha256=
|
103
|
+
cloudnetpy/plotting/plot_meta.py,sha256=d7CIT4ltUsjKw0HNnOXPkaLdZbkSmTuq6AbKdbODfZE,16730
|
104
|
+
cloudnetpy/plotting/plotting.py,sha256=ufFKChNK9Z6uRRBtMLT5AvZiyWiakPLGgC9OHWaC8Z0,35504
|
105
105
|
cloudnetpy/products/__init__.py,sha256=2hRb5HG9hNrxH1if5laJkLeFeaZCd5W1q3hh4ewsX0E,273
|
106
106
|
cloudnetpy/products/classification.py,sha256=KwAiBSgFwDqhM114NIgYiUjj8HoYc7gAlc8E1QgcSig,8207
|
107
107
|
cloudnetpy/products/der.py,sha256=soypE7uSEP4uHUCCQVEhyXsKY6e9mzV9B_2S5GUizqk,12729
|
@@ -115,9 +115,9 @@ cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe5
|
|
115
115
|
cloudnetpy/products/mwr_tools.py,sha256=rd7UC67O4fsIE5SaHVZ4qWvUJTj41ZGwgQWPwZzOM14,5377
|
116
116
|
cloudnetpy/products/product_tools.py,sha256=01Zc6xV8CSuYcIcLpchFf5POL3_c629-YMNDZJ51udA,10853
|
117
117
|
docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
|
118
|
-
cloudnetpy-1.66.
|
119
|
-
cloudnetpy-1.66.
|
120
|
-
cloudnetpy-1.66.
|
121
|
-
cloudnetpy-1.66.
|
122
|
-
cloudnetpy-1.66.
|
123
|
-
cloudnetpy-1.66.
|
118
|
+
cloudnetpy-1.66.14.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
119
|
+
cloudnetpy-1.66.14.dist-info/METADATA,sha256=EBvDEO2ZIXKdZkYX856_rst7Cy6S4QRThZSh9F75aMI,5806
|
120
|
+
cloudnetpy-1.66.14.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
121
|
+
cloudnetpy-1.66.14.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
|
122
|
+
cloudnetpy-1.66.14.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
123
|
+
cloudnetpy-1.66.14.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|