cloudnetpy 1.75.1__py3-none-any.whl → 1.76.0__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 +94 -20
- cloudnetpy/version.py +2 -2
- {cloudnetpy-1.75.1.dist-info → cloudnetpy-1.76.0.dist-info}/METADATA +1 -1
- {cloudnetpy-1.75.1.dist-info → cloudnetpy-1.76.0.dist-info}/RECORD +8 -8
- {cloudnetpy-1.75.1.dist-info → cloudnetpy-1.76.0.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.75.1.dist-info → cloudnetpy-1.76.0.dist-info}/entry_points.txt +0 -0
- {cloudnetpy-1.75.1.dist-info → cloudnetpy-1.76.0.dist-info}/licenses/LICENSE +0 -0
- {cloudnetpy-1.75.1.dist-info → cloudnetpy-1.76.0.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@ import logging
|
|
6
6
|
import os
|
7
7
|
import re
|
8
8
|
from operator import attrgetter
|
9
|
+
from pathlib import Path
|
9
10
|
from typing import Any, NamedTuple
|
10
11
|
|
11
12
|
import numpy as np
|
@@ -18,8 +19,8 @@ from cloudnetpy.metadata import MetaData
|
|
18
19
|
|
19
20
|
|
20
21
|
def radiometrics2nc(
|
21
|
-
full_path: str,
|
22
|
-
output_file: str,
|
22
|
+
full_path: str | os.PathLike,
|
23
|
+
output_file: str | os.PathLike,
|
23
24
|
site_meta: dict,
|
24
25
|
uuid: str | None = None,
|
25
26
|
date: str | datetime.date | None = None,
|
@@ -46,19 +47,11 @@ def radiometrics2nc(
|
|
46
47
|
"""
|
47
48
|
if isinstance(date, str):
|
48
49
|
date = datetime.date.fromisoformat(date)
|
49
|
-
|
50
50
|
if os.path.isdir(full_path):
|
51
|
-
valid_filenames =
|
51
|
+
valid_filenames = list(Path(full_path).iterdir())
|
52
52
|
else:
|
53
|
-
valid_filenames = [full_path]
|
54
|
-
|
55
|
-
objs = []
|
56
|
-
for filename in valid_filenames:
|
57
|
-
obj = Radiometrics(filename)
|
58
|
-
obj.read_raw_data()
|
59
|
-
obj.read_data()
|
60
|
-
objs.append(obj)
|
61
|
-
|
53
|
+
valid_filenames = [Path(full_path)]
|
54
|
+
objs = [_read_file(filename) for filename in valid_filenames]
|
62
55
|
radiometrics = RadiometricsCombined(objs, site_meta)
|
63
56
|
radiometrics.screen_time(date)
|
64
57
|
radiometrics.sort_timestamps()
|
@@ -81,15 +74,16 @@ class Record(NamedTuple):
|
|
81
74
|
values: dict[str, Any]
|
82
75
|
|
83
76
|
|
84
|
-
class
|
85
|
-
"""Reader for level 2 files
|
77
|
+
class RadiometricsMP:
|
78
|
+
"""Reader for level 2 files (*.csv) from Radiometrics MP-3000A and similar
|
79
|
+
microwave radiometers.
|
86
80
|
|
87
81
|
References:
|
88
82
|
Radiometrics (2008). Profiler Operator's Manual: MP-3000A, MP-2500A,
|
89
83
|
MP-1500A, MP-183A.
|
90
84
|
"""
|
91
85
|
|
92
|
-
def __init__(self, filename:
|
86
|
+
def __init__(self, filename: Path):
|
93
87
|
self.filename = filename
|
94
88
|
self.raw_data: list[Record] = []
|
95
89
|
self.data: dict = {}
|
@@ -152,12 +146,19 @@ class Radiometrics:
|
|
152
146
|
ahs = []
|
153
147
|
ah_times = []
|
154
148
|
block_titles = {}
|
149
|
+
skip_procs = set()
|
155
150
|
for record in self.raw_data:
|
156
151
|
if record.block_type == 100:
|
157
152
|
block_type = int(record.values["Record Type"]) - 1
|
158
153
|
title = record.values["Title"]
|
159
154
|
block_titles[block_type] = title
|
160
155
|
if title := block_titles.get(record.block_type + record.block_index):
|
156
|
+
# "LV2 Processor" values "Zenith" and "0.00:90.00" should be OK
|
157
|
+
# but "Angle20(N)" and similar should be skipped.
|
158
|
+
proc = record.values["LV2 Processor"]
|
159
|
+
if proc.startswith("Angle"):
|
160
|
+
skip_procs.add(proc)
|
161
|
+
continue
|
161
162
|
if title == "Temperature (K)":
|
162
163
|
temp_times.append(record.timestamp)
|
163
164
|
temps.append(
|
@@ -194,6 +195,9 @@ class Radiometrics:
|
|
194
195
|
irt = record.values["Tir(K)"]
|
195
196
|
irts.append([float(irt)])
|
196
197
|
elif record.block_type == 300:
|
198
|
+
if skip_procs:
|
199
|
+
skip_procs.pop()
|
200
|
+
continue
|
197
201
|
lwp = record.values["Int. Liquid(mm)"]
|
198
202
|
iwv = record.values["Int. Vapor(cm)"]
|
199
203
|
times.append(record.timestamp)
|
@@ -224,13 +228,69 @@ class Radiometrics:
|
|
224
228
|
)
|
225
229
|
|
226
230
|
|
231
|
+
class RadiometricsWVR:
|
232
|
+
"""Reader for *.los files from Radiometrics WVR-1100 microwave
|
233
|
+
radiometer.
|
234
|
+
"""
|
235
|
+
|
236
|
+
def __init__(self, filename: Path):
|
237
|
+
self.filename = filename
|
238
|
+
self.raw_data: dict = {}
|
239
|
+
self.data: dict = {}
|
240
|
+
self.instrument = instruments.RADIOMETRICS
|
241
|
+
self.ranges: list[str] = []
|
242
|
+
|
243
|
+
def read_raw_data(self) -> None:
|
244
|
+
with open(self.filename, encoding="utf8") as infile:
|
245
|
+
for line in infile:
|
246
|
+
columns = line.split()
|
247
|
+
if columns[:2] == ["date", "time"]:
|
248
|
+
headers = columns
|
249
|
+
break
|
250
|
+
else:
|
251
|
+
msg = "No headers found"
|
252
|
+
raise RuntimeError(msg)
|
253
|
+
for key in headers:
|
254
|
+
self.raw_data[key] = []
|
255
|
+
for line in infile:
|
256
|
+
for key, value in zip(headers, line.split(), strict=False):
|
257
|
+
parsed_value: Any
|
258
|
+
if key == "date":
|
259
|
+
month, day, year = map(int, value.split("/"))
|
260
|
+
if year < 100:
|
261
|
+
year += 2000
|
262
|
+
parsed_value = datetime.date(year, month, day)
|
263
|
+
elif key == "time":
|
264
|
+
hour, minute, second = map(int, value.split(":"))
|
265
|
+
parsed_value = datetime.time(hour, minute, second)
|
266
|
+
else:
|
267
|
+
parsed_value = float(value)
|
268
|
+
self.raw_data[key].append(parsed_value)
|
269
|
+
|
270
|
+
def read_data(self) -> None:
|
271
|
+
self.data["time"] = np.array(
|
272
|
+
[
|
273
|
+
datetime.datetime.combine(date, time)
|
274
|
+
for date, time in zip(
|
275
|
+
self.raw_data["date"], self.raw_data["time"], strict=False
|
276
|
+
)
|
277
|
+
],
|
278
|
+
dtype="datetime64[s]",
|
279
|
+
)
|
280
|
+
self.data["lwp"] = np.array(self.raw_data["LiqCM"]) * 10 # cm => kg m-2
|
281
|
+
self.data["iwv"] = np.array(self.raw_data["VapCM"]) * 10 # cm => kg m-2
|
282
|
+
is_zenith = np.abs(np.array(self.raw_data["ELact"]) - 90.0) < 1.0
|
283
|
+
for key in self.data:
|
284
|
+
self.data[key] = self.data[key][is_zenith]
|
285
|
+
|
286
|
+
|
227
287
|
class RadiometricsCombined:
|
228
288
|
site_meta: dict
|
229
289
|
data: dict
|
230
290
|
date: datetime.date | None
|
231
291
|
instrument: instruments.Instrument
|
232
292
|
|
233
|
-
def __init__(self, objs: list[
|
293
|
+
def __init__(self, objs: list[RadiometricsMP | RadiometricsWVR], site_meta: dict):
|
234
294
|
self.site_meta = site_meta
|
235
295
|
self.data = {}
|
236
296
|
self.date = None
|
@@ -240,9 +300,10 @@ class RadiometricsCombined:
|
|
240
300
|
raise InconsistentDataError(msg)
|
241
301
|
for key in obj.data:
|
242
302
|
self.data = utils.append_data(self.data, key, obj.data[key])
|
243
|
-
|
244
|
-
|
245
|
-
|
303
|
+
if objs[0].ranges:
|
304
|
+
ranges = [float(x) for x in objs[0].ranges]
|
305
|
+
self.data["range"] = np.array(ranges) * 1000 # m => km
|
306
|
+
self.data["height"] = self.data["range"] + self.site_meta["altitude"]
|
246
307
|
self.instrument = instruments.RADIOMETRICS
|
247
308
|
|
248
309
|
def screen_time(self, expected_date: datetime.date | None) -> None:
|
@@ -289,6 +350,19 @@ class RadiometricsCombined:
|
|
289
350
|
self.data[name] = CloudnetArray(float(value), key)
|
290
351
|
|
291
352
|
|
353
|
+
def _read_file(filename: Path) -> RadiometricsMP | RadiometricsWVR:
|
354
|
+
with open(filename) as f:
|
355
|
+
first_line = f.readline()
|
356
|
+
obj = (
|
357
|
+
RadiometricsWVR(filename)
|
358
|
+
if "RETRIEVAL COEFFICIENTS" in first_line
|
359
|
+
else RadiometricsMP(filename)
|
360
|
+
)
|
361
|
+
obj.read_raw_data()
|
362
|
+
obj.read_data()
|
363
|
+
return obj
|
364
|
+
|
365
|
+
|
292
366
|
def _parse_datetime(text: str) -> datetime.datetime:
|
293
367
|
date, time = text.split()
|
294
368
|
month, day, year = map(int, date.split("/"))
|
cloudnetpy/version.py
CHANGED
@@ -9,7 +9,7 @@ cloudnetpy/metadata.py,sha256=lO7BCbVAzFoH3Nq-VuezYX0f7MnbG1Zp11g5GSiuQwM,6189
|
|
9
9
|
cloudnetpy/output.py,sha256=l0LoOhcGCBrg2EJ4NT1xZ7-UKWdV7X7yQ0fJmhkwJVc,15829
|
10
10
|
cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
cloudnetpy/utils.py,sha256=SSZWk82c4nkAiTcLdOKGVvxt5ovETdAMn_TLxVeYpBY,33473
|
12
|
-
cloudnetpy/version.py,sha256=
|
12
|
+
cloudnetpy/version.py,sha256=vHHlyjedUCAOK4akFXnc6Xk6GWb-5gg4KD85wtoljYA,72
|
13
13
|
cloudnetpy/categorize/__init__.py,sha256=s-SJaysvVpVVo5kidiruWQO6p3gv2TXwY1wEHYO5D6I,44
|
14
14
|
cloudnetpy/categorize/atmos_utils.py,sha256=RcmbKxm2COkE7WEya0mK3yX5rzUbrewRVh3ekm01RtM,10598
|
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=HlaZeH5939R86ukF8K-P4Kfzb5-CpLB15LU2u94C5eI,7330
|
51
51
|
cloudnetpy/instruments/pollyxt.py,sha256=U3g-ttmcs02LuLwVOydP3GjeNcmDyoYQroB-leIGdHY,10060
|
52
|
-
cloudnetpy/instruments/radiometrics.py,sha256=
|
52
|
+
cloudnetpy/instruments/radiometrics.py,sha256=__H7KFBcXT0FP8lis3P25MuMa5-S8GrTzKtazMKO678,14822
|
53
53
|
cloudnetpy/instruments/rain_e_h3.py,sha256=9TdpP4UzMBNIt2iE2GL6K9dFldzTHPLOrU8Q3tcosCU,5317
|
54
54
|
cloudnetpy/instruments/rpg.py,sha256=m3-xLJ-w2T7Ip7jBveWsGrts4tmNvdc-Lb4HebvHQjQ,17319
|
55
55
|
cloudnetpy/instruments/rpg_reader.py,sha256=ThztFuVrWxhmWVAfZTfQDeUiKK1XMTbtv08IBe8GK98,11364
|
@@ -116,10 +116,10 @@ cloudnetpy/products/lwc.py,sha256=sl6Al2tuH3KkCBrPbWTmuz3jlD5UQJ4D6qBsn1tt2CQ,18
|
|
116
116
|
cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe55y9ob58,16637951
|
117
117
|
cloudnetpy/products/mwr_tools.py,sha256=8HPZpQMTojKZP1JS1S83IE0sxmbDE9bxlaWoqmGnUZE,6199
|
118
118
|
cloudnetpy/products/product_tools.py,sha256=uu4l6reuGbPcW3TgttbaSrqIKbyYGhBVTdnC7opKvmg,11101
|
119
|
-
cloudnetpy-1.
|
119
|
+
cloudnetpy-1.76.0.dist-info/licenses/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
120
120
|
docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
|
121
|
-
cloudnetpy-1.
|
122
|
-
cloudnetpy-1.
|
123
|
-
cloudnetpy-1.
|
124
|
-
cloudnetpy-1.
|
125
|
-
cloudnetpy-1.
|
121
|
+
cloudnetpy-1.76.0.dist-info/METADATA,sha256=sefr7DyZ7S4JNZcT_TvGj_b9hxEZOXJp887yXKW0tkg,5796
|
122
|
+
cloudnetpy-1.76.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
123
|
+
cloudnetpy-1.76.0.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
|
124
|
+
cloudnetpy-1.76.0.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
125
|
+
cloudnetpy-1.76.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|