cloudnetpy 1.47.0__py3-none-any.whl → 1.47.2__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/disdrometer/parsivel.py +33 -27
- cloudnetpy/instruments/lufft.py +13 -7
- cloudnetpy/version.py +1 -1
- {cloudnetpy-1.47.0.dist-info → cloudnetpy-1.47.2.dist-info}/METADATA +1 -1
- {cloudnetpy-1.47.0.dist-info → cloudnetpy-1.47.2.dist-info}/RECORD +8 -8
- {cloudnetpy-1.47.0.dist-info → cloudnetpy-1.47.2.dist-info}/LICENSE +0 -0
- {cloudnetpy-1.47.0.dist-info → cloudnetpy-1.47.2.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.47.0.dist-info → cloudnetpy-1.47.2.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
import datetime
|
2
2
|
import logging
|
3
|
-
from collections.abc import Callable, Iterator
|
3
|
+
from collections.abc import Callable, Iterator, Sequence
|
4
4
|
from pathlib import Path
|
5
5
|
from typing import Any, Literal, TypeVar
|
6
6
|
|
@@ -21,7 +21,7 @@ def parsivel2nc(
|
|
21
21
|
site_meta: dict,
|
22
22
|
uuid: str | None = None,
|
23
23
|
date: str | datetime.date | None = None,
|
24
|
-
telegram:
|
24
|
+
telegram: Sequence[int | None] | None = None,
|
25
25
|
) -> str:
|
26
26
|
"""Converts OTT Parsivel-2 disdrometer data into Cloudnet Level 1b netCDF
|
27
27
|
file.
|
@@ -34,8 +34,9 @@ def parsivel2nc(
|
|
34
34
|
uuid: Set specific UUID for the file.
|
35
35
|
date: Expected date of the measurements as YYYY-MM-DD.
|
36
36
|
telegram: List of measured value numbers as specified in section 11.2 of
|
37
|
-
the instrument's operating instructions.
|
38
|
-
input file doesn't contain a
|
37
|
+
the instrument's operating instructions. Unknown values are indicated
|
38
|
+
with None. Telegram is required if the input file doesn't contain a
|
39
|
+
header.
|
39
40
|
|
40
41
|
Returns:
|
41
42
|
UUID of the generated file.
|
@@ -69,7 +70,7 @@ class Parsivel(CloudnetInstrument):
|
|
69
70
|
self,
|
70
71
|
filename: Path | str | bytes,
|
71
72
|
site_meta: dict,
|
72
|
-
telegram:
|
73
|
+
telegram: Sequence[int | None] | None = None,
|
73
74
|
expected_date: datetime.date | None = None,
|
74
75
|
):
|
75
76
|
super().__init__()
|
@@ -204,6 +205,9 @@ TELEGRAM = {
|
|
204
205
|
16: "I_heating",
|
205
206
|
17: "V_power_supply",
|
206
207
|
18: "state_sensor",
|
208
|
+
19: "_datetime",
|
209
|
+
20: "_time",
|
210
|
+
21: "_date",
|
207
211
|
22: "_station_name",
|
208
212
|
24: "_rain_amount_absolute",
|
209
213
|
25: "error_code",
|
@@ -231,12 +235,19 @@ def _parse_int(tokens: Iterator[str]) -> int:
|
|
231
235
|
|
232
236
|
|
233
237
|
def _parse_float(tokens: Iterator[str]) -> float:
|
234
|
-
|
238
|
+
token = next(tokens)
|
239
|
+
token = token.replace(",", ".")
|
240
|
+
return float(token)
|
235
241
|
|
236
242
|
|
237
243
|
def _parse_date(tokens: Iterator[str]) -> datetime.date:
|
238
244
|
token = next(tokens)
|
239
|
-
|
245
|
+
if "/" in token:
|
246
|
+
year, month, day = token.split("/")
|
247
|
+
elif "." in token:
|
248
|
+
day, month, year = token.split(".")
|
249
|
+
else:
|
250
|
+
raise ValueError(f"Unsupported date: '{input}'")
|
240
251
|
if len(year) != 4:
|
241
252
|
raise ValueError(f"Unsupported date: '{input}'")
|
242
253
|
return datetime.date(int(year), int(month), int(day))
|
@@ -260,8 +271,7 @@ def _parse_datetime(tokens: Iterator[str]) -> datetime.datetime:
|
|
260
271
|
|
261
272
|
|
262
273
|
def _parse_vector(tokens: Iterator[str]) -> np.ndarray:
|
263
|
-
|
264
|
-
return np.array(values)
|
274
|
+
return np.array([_parse_float(tokens) for _i in range(32)])
|
265
275
|
|
266
276
|
|
267
277
|
def _parse_spectrum(tokens: Iterator[str]) -> np.ndarray:
|
@@ -285,14 +295,8 @@ PARSERS: dict[str, Callable[[Iterator[str]], Any]] = {
|
|
285
295
|
"T_sensor": _parse_int,
|
286
296
|
"V_power_supply": _parse_float,
|
287
297
|
"_date": _parse_date,
|
288
|
-
"_dsp_firmware_version": next,
|
289
|
-
"_iop_firmware_version": next,
|
290
|
-
"_metar": next,
|
291
|
-
"_nws": next,
|
292
298
|
"_rain_accum": _parse_float,
|
293
299
|
"_rain_amount_absolute": _parse_float,
|
294
|
-
"_sensor_id": next,
|
295
|
-
"_station_name": next,
|
296
300
|
"_time": _parse_time,
|
297
301
|
"error_code": _parse_int,
|
298
302
|
"fall_velocity": _parse_vector,
|
@@ -300,7 +304,7 @@ PARSERS: dict[str, Callable[[Iterator[str]], Any]] = {
|
|
300
304
|
"kinetic_energy": _parse_float,
|
301
305
|
"n_particles": _parse_int,
|
302
306
|
"number_concentration": _parse_vector,
|
303
|
-
"
|
307
|
+
"_datetime": _parse_datetime,
|
304
308
|
"radar_reflectivity": _parse_float,
|
305
309
|
"rainfall_rate": _parse_float,
|
306
310
|
"sig_laser": _parse_int,
|
@@ -316,8 +320,11 @@ def _parse_headers(line: str) -> list[str]:
|
|
316
320
|
return [HEADERS[header.strip()] for header in line.split(";")]
|
317
321
|
|
318
322
|
|
319
|
-
def _parse_telegram(telegram:
|
320
|
-
return [
|
323
|
+
def _parse_telegram(telegram: Sequence[int | None]) -> list[str]:
|
324
|
+
return [
|
325
|
+
TELEGRAM[num] if num is not None else f"_unknown_{i}"
|
326
|
+
for i, num in enumerate(telegram)
|
327
|
+
]
|
321
328
|
|
322
329
|
|
323
330
|
def _read_rows(headers: list[str], rows: list[str]) -> dict[str, list]:
|
@@ -328,7 +335,7 @@ def _read_rows(headers: list[str], rows: list[str]) -> dict[str, list]:
|
|
328
335
|
continue
|
329
336
|
try:
|
330
337
|
tokens = iter(row.removesuffix(";").split(";"))
|
331
|
-
parsed = [PARSERS
|
338
|
+
parsed = [PARSERS.get(header, next)(tokens) for header in headers]
|
332
339
|
unread_tokens = list(tokens)
|
333
340
|
if unread_tokens:
|
334
341
|
raise ValueError("More values than expected")
|
@@ -345,7 +352,7 @@ def _read_rows(headers: list[str], rows: list[str]) -> dict[str, list]:
|
|
345
352
|
|
346
353
|
|
347
354
|
def _read_parsivel(
|
348
|
-
filename: Path | str | bytes, telegram:
|
355
|
+
filename: Path | str | bytes, telegram: Sequence[int | None] | None = None
|
349
356
|
) -> dict[str, np.ndarray]:
|
350
357
|
with open(filename, encoding="latin1", errors="ignore") as file:
|
351
358
|
lines = file.read().splitlines()
|
@@ -354,17 +361,16 @@ def _read_parsivel(
|
|
354
361
|
if "Date" in lines[0]:
|
355
362
|
headers = _parse_headers(lines[0])
|
356
363
|
data = _read_rows(headers, lines[1:])
|
357
|
-
data["time"] = [
|
358
|
-
datetime.datetime.combine(date, time)
|
359
|
-
for date, time in zip(data["_date"], data["_time"])
|
360
|
-
]
|
361
|
-
del data["_date"]
|
362
|
-
del data["_time"]
|
363
364
|
elif telegram is not None:
|
364
365
|
headers = _parse_telegram(telegram)
|
365
366
|
data = _read_rows(headers, lines)
|
366
367
|
else:
|
367
368
|
raise ValueError("telegram must be specified for files without header")
|
369
|
+
if "_datetime" not in data:
|
370
|
+
data["_datetime"] = [
|
371
|
+
datetime.datetime.combine(date, time)
|
372
|
+
for date, time in zip(data["_date"], data["_time"])
|
373
|
+
]
|
368
374
|
result = {key: np.array(value) for key, value in data.items()}
|
369
|
-
result["time"] = result["
|
375
|
+
result["time"] = result["_datetime"].astype("datetime64[s]")
|
370
376
|
return result
|
cloudnetpy/instruments/lufft.py
CHANGED
@@ -12,6 +12,8 @@ from cloudnetpy.instruments.nc_lidar import NcLidar
|
|
12
12
|
class LufftCeilo(NcLidar):
|
13
13
|
"""Class for Lufft chm15k ceilometer."""
|
14
14
|
|
15
|
+
serial_number: str | None
|
16
|
+
|
15
17
|
def __init__(
|
16
18
|
self, file_name: str, site_meta: dict, expected_date: str | None = None
|
17
19
|
):
|
@@ -19,12 +21,13 @@ class LufftCeilo(NcLidar):
|
|
19
21
|
self.file_name = file_name
|
20
22
|
self.site_meta = site_meta
|
21
23
|
self.expected_date = expected_date
|
22
|
-
self.
|
24
|
+
self.serial_number = None
|
23
25
|
|
24
26
|
def read_ceilometer_file(self, calibration_factor: float | None = None) -> None:
|
25
27
|
"""Reads data and metadata from Jenoptik netCDF file."""
|
26
28
|
with netCDF4.Dataset(self.file_name) as dataset:
|
27
29
|
self.dataset = dataset
|
30
|
+
self._fetch_attributes()
|
28
31
|
self._fetch_range(reference="upper")
|
29
32
|
self._fetch_beta_raw(calibration_factor)
|
30
33
|
self._fetch_time_and_date()
|
@@ -77,9 +80,12 @@ class LufftCeilo(NcLidar):
|
|
77
80
|
return var[0] if utils.isscalar(var) else var[:]
|
78
81
|
raise ValueError("Unknown variable")
|
79
82
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
def _fetch_attributes(self):
|
84
|
+
self.serial_number = getattr(self.dataset, "device_name", None)
|
85
|
+
if self.serial_number is None:
|
86
|
+
self.serial_number = getattr(self.dataset, "source")
|
87
|
+
self.instrument = (
|
88
|
+
instruments.CHM15KX
|
89
|
+
if self.serial_number.startswith("CHX")
|
90
|
+
else instruments.CHM15K
|
91
|
+
)
|
cloudnetpy/version.py
CHANGED
@@ -8,7 +8,7 @@ cloudnetpy/metadata.py,sha256=4stvQT5oxNdxu6Bh7NbT5LH65CIpzhfGA_Cey5Ai6m4,5022
|
|
8
8
|
cloudnetpy/output.py,sha256=Oaj5w_C8rIFQrt2i9WZtvOjliBZgHo9Rm-HO1n6wazQ,13071
|
9
9
|
cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
cloudnetpy/utils.py,sha256=Rsh_ojLNMH5ps7fWLKvF74Xd9B1yC9JqL507eRljyDQ,26973
|
11
|
-
cloudnetpy/version.py,sha256=
|
11
|
+
cloudnetpy/version.py,sha256=a1VwkoLwSdAt59hSaL7yzIODKVJxnFcS6tSnZws2z64,72
|
12
12
|
cloudnetpy/categorize/__init__.py,sha256=gP5q3Vis1y9u9OWgA_idlbjfWXYN_S0IBSWdwBhL_uU,69
|
13
13
|
cloudnetpy/categorize/atmos.py,sha256=-Z1y1Eaju7pG2yOIgUeIf-xH21HRxzWfJ9AOIrVL4A8,12389
|
14
14
|
cloudnetpy/categorize/atmos_utils.py,sha256=6WdfGqzOvnaDW7vlMMrZBJIxW_eHQdjH-Xl_iPv1TTI,3716
|
@@ -34,7 +34,7 @@ cloudnetpy/instruments/copernicus.py,sha256=FDS7Rsunp4ieTPFh_T_LXvreNi5_HTv4ZzR3
|
|
34
34
|
cloudnetpy/instruments/galileo.py,sha256=F_XyoAb9PR-ifGhqhXziKnv0KfyOh-yEBaE1NgRMzNg,4318
|
35
35
|
cloudnetpy/instruments/hatpro.py,sha256=ftXVbYx2xtQcRZ2zVF82jUP7R96YV5Wdgcz_PUNZTXM,4824
|
36
36
|
cloudnetpy/instruments/instruments.py,sha256=FGiN0369--1z8uYzgDP9_1A7DFLL_Cl4-Ca-OMsjBdI,2824
|
37
|
-
cloudnetpy/instruments/lufft.py,sha256=
|
37
|
+
cloudnetpy/instruments/lufft.py,sha256=E2qzvya206gNiML7BSM6vm1lzeOMBePrIuPT81NHPvw,3397
|
38
38
|
cloudnetpy/instruments/mira.py,sha256=wtcgTR_nqL_BPZ5yqvBYXpI9Qtwdvc_1v6N_r3hCEPc,4888
|
39
39
|
cloudnetpy/instruments/nc_lidar.py,sha256=9FSsInEM8fdyyhgNXb2INBjb5OEGFE5ZaVSowHjzoCA,1386
|
40
40
|
cloudnetpy/instruments/nc_radar.py,sha256=hjmtgLuBPnfsyRInOZeKzAw3oZ82WSumrlPpycqBbjk,5530
|
@@ -46,7 +46,7 @@ cloudnetpy/instruments/vaisala.py,sha256=wOziQs_NuPTcZm8fg0UH9HCslhBCWromyxulQgS
|
|
46
46
|
cloudnetpy/instruments/weather_station.py,sha256=gwPh4opv8SDf_vi4yBq5oHe8IFxGUF7ktB5yOBerd1g,5829
|
47
47
|
cloudnetpy/instruments/disdrometer/__init__.py,sha256=lyjwttWvFvuwYxEkusoAvgRcbBmglmOp5HJOpXUqLWo,93
|
48
48
|
cloudnetpy/instruments/disdrometer/common.py,sha256=_IWuhRtFIiYdIUjnS9R715C4eyr0dGd96ZDvQWJYetc,15006
|
49
|
-
cloudnetpy/instruments/disdrometer/parsivel.py,sha256=
|
49
|
+
cloudnetpy/instruments/disdrometer/parsivel.py,sha256=_AGqOl2_vC927MJ2vn9xArHZDw0kWhUKxxxusCyv4CY,12821
|
50
50
|
cloudnetpy/instruments/disdrometer/thies.py,sha256=YB328052yqlkOW7sGZSfUUCwLS3GPzYrWDiSg8C3C-s,5022
|
51
51
|
cloudnetpy/model_evaluation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
52
|
cloudnetpy/model_evaluation/file_handler.py,sha256=xYEkCsgwpF1kQNkG3ydxGM6DjFd-tryZ9ULlA9HUIAE,6321
|
@@ -103,8 +103,8 @@ cloudnetpy/products/lwc.py,sha256=CqvV7iqxUbk2YekaocBActmZuhF1BhWkfc-y6MDej-0,18
|
|
103
103
|
cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe55y9ob58,16637951
|
104
104
|
cloudnetpy/products/product_tools.py,sha256=9eKvr-zGEK2ARN2DmJN--KbynKdT08DhfT1GB8Rfoe4,9616
|
105
105
|
docs/source/conf.py,sha256=baQlgkkUGJi4952W6NRhLkIBbRtwFgqrIOBuEeSCLfk,1488
|
106
|
-
cloudnetpy-1.47.
|
107
|
-
cloudnetpy-1.47.
|
108
|
-
cloudnetpy-1.47.
|
109
|
-
cloudnetpy-1.47.
|
110
|
-
cloudnetpy-1.47.
|
106
|
+
cloudnetpy-1.47.2.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
107
|
+
cloudnetpy-1.47.2.dist-info/METADATA,sha256=TdlQti8TZ1wKHHG1coPydiB7fUkj2MrRIDFfSGjXE44,5638
|
108
|
+
cloudnetpy-1.47.2.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
109
|
+
cloudnetpy-1.47.2.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
110
|
+
cloudnetpy-1.47.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|