cloudnetpy 1.67.8__py3-none-any.whl → 1.68.1__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/__init__.py +1 -0
- cloudnetpy/instruments/cloudnet_instrument.py +31 -0
- cloudnetpy/instruments/rain_e_h3.py +168 -0
- cloudnetpy/instruments/weather_station.py +3 -29
- cloudnetpy/version.py +2 -2
- {cloudnetpy-1.67.8.dist-info → cloudnetpy-1.68.1.dist-info}/METADATA +1 -1
- {cloudnetpy-1.67.8.dist-info → cloudnetpy-1.68.1.dist-info}/RECORD +11 -10
- {cloudnetpy-1.67.8.dist-info → cloudnetpy-1.68.1.dist-info}/LICENSE +0 -0
- {cloudnetpy-1.67.8.dist-info → cloudnetpy-1.68.1.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.67.8.dist-info → cloudnetpy-1.68.1.dist-info}/entry_points.txt +0 -0
- {cloudnetpy-1.67.8.dist-info → cloudnetpy-1.68.1.dist-info}/top_level.txt +0 -0
@@ -112,3 +112,34 @@ class CloudnetInstrument:
|
|
112
112
|
if np.isnan(zenith_angle) or zenith_angle is ma.masked:
|
113
113
|
return None
|
114
114
|
return zenith_angle
|
115
|
+
|
116
|
+
|
117
|
+
class CSVFile(CloudnetInstrument):
|
118
|
+
def __init__(self, site_meta: dict):
|
119
|
+
super().__init__()
|
120
|
+
self.site_meta = site_meta
|
121
|
+
self._data: dict = {}
|
122
|
+
|
123
|
+
def add_date(self) -> None:
|
124
|
+
dt = self._data["time"][0]
|
125
|
+
self.date = dt.strftime("%Y %m %d").split()
|
126
|
+
|
127
|
+
def add_data(self) -> None:
|
128
|
+
for key, value in self._data.items():
|
129
|
+
parsed = (
|
130
|
+
utils.datetime2decimal_hours(value)
|
131
|
+
if key == "time"
|
132
|
+
else ma.array(value)
|
133
|
+
)
|
134
|
+
self.data[key] = CloudnetArray(parsed, key)
|
135
|
+
|
136
|
+
def normalize_rainfall_amount(self) -> None:
|
137
|
+
if "rainfall_amount" in self.data:
|
138
|
+
amount = self.data["rainfall_amount"][:]
|
139
|
+
offset = 0
|
140
|
+
for i in range(1, len(amount)):
|
141
|
+
if amount[i] + offset < amount[i - 1]:
|
142
|
+
offset += amount[i - 1]
|
143
|
+
amount[i] += offset
|
144
|
+
amount -= amount[0]
|
145
|
+
self.data["rainfall_amount"].data = amount
|
@@ -0,0 +1,168 @@
|
|
1
|
+
import csv
|
2
|
+
import datetime
|
3
|
+
from os import PathLike
|
4
|
+
|
5
|
+
import numpy as np
|
6
|
+
|
7
|
+
from cloudnetpy import output
|
8
|
+
from cloudnetpy.exceptions import ValidTimeStampError
|
9
|
+
from cloudnetpy.instruments import instruments
|
10
|
+
from cloudnetpy.instruments.cloudnet_instrument import CSVFile
|
11
|
+
|
12
|
+
|
13
|
+
def rain_e_h32nc(
|
14
|
+
input_file: str | PathLike,
|
15
|
+
output_file: str,
|
16
|
+
site_meta: dict,
|
17
|
+
uuid: str | None = None,
|
18
|
+
date: str | datetime.date | None = None,
|
19
|
+
):
|
20
|
+
"""Converts rain_e_h3 rain-gauge into Cloudnet Level 1b netCDF file.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
input_file: Filename of rain_e_h3 CSV file.
|
24
|
+
output_file: Output filename.
|
25
|
+
site_meta: Dictionary containing information about the site. Required key
|
26
|
+
is `name`.
|
27
|
+
uuid: Set specific UUID for the file.
|
28
|
+
date: Expected date of the measurements as YYYY-MM-DD or datetime.date object.
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
UUID of the generated file.
|
32
|
+
|
33
|
+
Raises:
|
34
|
+
WeatherStationDataError : Unable to read the file.
|
35
|
+
ValidTimeStampError: No valid timestamps found.
|
36
|
+
"""
|
37
|
+
rain = RainEH3(site_meta)
|
38
|
+
if isinstance(date, str):
|
39
|
+
date = datetime.date.fromisoformat(date)
|
40
|
+
rain.parse_input_file(input_file, date)
|
41
|
+
rain.add_data()
|
42
|
+
rain.add_date()
|
43
|
+
rain.convert_units()
|
44
|
+
rain.normalize_rainfall_amount()
|
45
|
+
rain.add_site_geolocation()
|
46
|
+
rain.sort_timestamps()
|
47
|
+
rain.remove_duplicate_timestamps()
|
48
|
+
attributes = output.add_time_attribute({}, rain.date)
|
49
|
+
output.update_attributes(rain.data, attributes)
|
50
|
+
return output.save_level1b(rain, output_file, uuid)
|
51
|
+
|
52
|
+
|
53
|
+
class RainEH3(CSVFile):
|
54
|
+
def __init__(self, site_meta: dict):
|
55
|
+
super().__init__(site_meta)
|
56
|
+
self.instrument = instruments.RAIN_E_H3
|
57
|
+
self._data = {
|
58
|
+
"time": [],
|
59
|
+
"rainfall_rate": [],
|
60
|
+
"rainfall_amount": [],
|
61
|
+
}
|
62
|
+
|
63
|
+
def parse_input_file(
|
64
|
+
self, filepath: str | PathLike, date: datetime.date | None = None
|
65
|
+
) -> None:
|
66
|
+
with open(filepath, encoding="latin1") as f:
|
67
|
+
data = list(csv.reader(f, delimiter=";"))
|
68
|
+
n_values = np.median([len(row) for row in data]).astype(int)
|
69
|
+
|
70
|
+
if n_values == 22:
|
71
|
+
self._read_talker_protocol_22_columns(data, date)
|
72
|
+
elif n_values == 16:
|
73
|
+
self._read_talker_protocol_16_columns(data, date)
|
74
|
+
else:
|
75
|
+
msg = "Only talker protocol with 16 or 22 columns is supported."
|
76
|
+
raise NotImplementedError(msg)
|
77
|
+
|
78
|
+
def _read_talker_protocol_16_columns(
|
79
|
+
self, data: list, date: datetime.date | None = None
|
80
|
+
) -> None:
|
81
|
+
"""Old Lindenberg data format.
|
82
|
+
|
83
|
+
0 date DD.MM.YYYY
|
84
|
+
1 time
|
85
|
+
2 precipitation intensity in mm/h
|
86
|
+
3 precipitation accumulation in mm
|
87
|
+
4 housing contact
|
88
|
+
5 top temperature
|
89
|
+
6 bottom temperature
|
90
|
+
7 heater status
|
91
|
+
8 error code
|
92
|
+
9 system status
|
93
|
+
10 talker interval in seconds
|
94
|
+
11 operating hours
|
95
|
+
12 device type
|
96
|
+
13 user data storage 1
|
97
|
+
14 user data storage 2
|
98
|
+
15 user data storage 3
|
99
|
+
|
100
|
+
"""
|
101
|
+
for row in data:
|
102
|
+
if len(row) != 16:
|
103
|
+
continue
|
104
|
+
try:
|
105
|
+
dt = datetime.datetime.strptime(
|
106
|
+
f"{row[0]} {row[1]}", "%d.%m.%Y %H:%M:%S"
|
107
|
+
)
|
108
|
+
except ValueError:
|
109
|
+
continue
|
110
|
+
if date and date != dt.date():
|
111
|
+
continue
|
112
|
+
self._data["time"].append(dt)
|
113
|
+
self._data["rainfall_rate"].append(float(row[2]))
|
114
|
+
self._data["rainfall_amount"].append(float(row[3]))
|
115
|
+
if not self._data["time"]:
|
116
|
+
raise ValidTimeStampError
|
117
|
+
|
118
|
+
def _read_talker_protocol_22_columns(
|
119
|
+
self, data: list, date: datetime.date | None = None
|
120
|
+
) -> None:
|
121
|
+
"""Columns according to header in Lindenberg data.
|
122
|
+
|
123
|
+
0 datetime utc
|
124
|
+
1 date
|
125
|
+
2 time
|
126
|
+
3 precipitation intensity in mm/h
|
127
|
+
4 precipitation accumulation in mm
|
128
|
+
5 housing contact
|
129
|
+
6 top temperature
|
130
|
+
7 bottom temperature
|
131
|
+
8 heater status
|
132
|
+
9 error code
|
133
|
+
10 system status
|
134
|
+
11 talker interval in seconds
|
135
|
+
12 operating hours
|
136
|
+
13 device type
|
137
|
+
14 user data storage 1
|
138
|
+
15 user data storage 2
|
139
|
+
16 user data storage 3
|
140
|
+
17 user data storage 4
|
141
|
+
18 serial number
|
142
|
+
19 hardware version
|
143
|
+
20 firmware version
|
144
|
+
21 external temperature * checksum
|
145
|
+
|
146
|
+
"""
|
147
|
+
for row in data:
|
148
|
+
if len(row) != 22:
|
149
|
+
continue
|
150
|
+
try:
|
151
|
+
dt = datetime.datetime.strptime(f"{row[0]}", "%Y-%m-%d %H:%M:%S")
|
152
|
+
except ValueError:
|
153
|
+
continue
|
154
|
+
if date and date != dt.date():
|
155
|
+
continue
|
156
|
+
self._data["time"].append(dt)
|
157
|
+
self._data["rainfall_rate"].append(float(row[3]))
|
158
|
+
self._data["rainfall_amount"].append(float(row[4]))
|
159
|
+
self.serial_number = row[18]
|
160
|
+
if not self._data["time"]:
|
161
|
+
raise ValidTimeStampError
|
162
|
+
|
163
|
+
def convert_units(self) -> None:
|
164
|
+
rainfall_rate = self.data["rainfall_rate"][:]
|
165
|
+
self.data["rainfall_rate"].data = rainfall_rate / 3600 / 1000 # mm/h -> m/s
|
166
|
+
self.data["rainfall_amount"].data = (
|
167
|
+
self.data["rainfall_amount"][:] / 1000
|
168
|
+
) # mm -> m
|
@@ -12,7 +12,7 @@ from cloudnetpy.cloudnetarray import CloudnetArray
|
|
12
12
|
from cloudnetpy.constants import HPA_TO_PA, MM_H_TO_M_S, SEC_IN_HOUR
|
13
13
|
from cloudnetpy.exceptions import ValidTimeStampError, WeatherStationDataError
|
14
14
|
from cloudnetpy.instruments import instruments
|
15
|
-
from cloudnetpy.instruments.cloudnet_instrument import
|
15
|
+
from cloudnetpy.instruments.cloudnet_instrument import CSVFile
|
16
16
|
from cloudnetpy.instruments.toa5 import read_toa5
|
17
17
|
from cloudnetpy.utils import datetime2decimal_hours
|
18
18
|
|
@@ -79,28 +79,13 @@ def ws2nc(
|
|
79
79
|
return output.save_level1b(ws, output_file, uuid)
|
80
80
|
|
81
81
|
|
82
|
-
class WS(
|
82
|
+
class WS(CSVFile):
|
83
83
|
def __init__(self, site_meta: dict):
|
84
|
-
super().__init__()
|
85
|
-
self._data: dict
|
86
|
-
self.site_meta = site_meta
|
84
|
+
super().__init__(site_meta)
|
87
85
|
self.instrument = instruments.GENERIC_WEATHER_STATION
|
88
86
|
|
89
87
|
date: list[str]
|
90
88
|
|
91
|
-
def add_date(self) -> None:
|
92
|
-
first_date = self._data["time"][0].date()
|
93
|
-
self.date = [
|
94
|
-
str(first_date.year),
|
95
|
-
str(first_date.month).zfill(2),
|
96
|
-
str(first_date.day).zfill(2),
|
97
|
-
]
|
98
|
-
|
99
|
-
def add_data(self) -> None:
|
100
|
-
for key, value in self._data.items():
|
101
|
-
parsed = datetime2decimal_hours(value) if key == "time" else ma.array(value)
|
102
|
-
self.data[key] = CloudnetArray(parsed, key)
|
103
|
-
|
104
89
|
def calculate_rainfall_amount(self) -> None:
|
105
90
|
if "rainfall_amount" in self.data:
|
106
91
|
return
|
@@ -137,17 +122,6 @@ class WS(CloudnetInstrument):
|
|
137
122
|
def convert_pressure(self) -> None:
|
138
123
|
self.data["air_pressure"].data = self.data["air_pressure"][:] * HPA_TO_PA
|
139
124
|
|
140
|
-
def normalize_rainfall_amount(self) -> None:
|
141
|
-
if "rainfall_amount" in self.data:
|
142
|
-
amount = self.data["rainfall_amount"][:]
|
143
|
-
offset = 0
|
144
|
-
for i in range(1, len(amount)):
|
145
|
-
if amount[i] + offset < amount[i - 1]:
|
146
|
-
offset += amount[i - 1]
|
147
|
-
amount[i] += offset
|
148
|
-
amount -= amount[0]
|
149
|
-
self.data["rainfall_amount"].data = amount
|
150
|
-
|
151
125
|
def convert_time(self) -> None:
|
152
126
|
pass
|
153
127
|
|
cloudnetpy/version.py
CHANGED
@@ -9,7 +9,7 @@ cloudnetpy/metadata.py,sha256=gEHwqEMY9gfaqVxx2_CobLg3i5gFXAumXLwnnW4BqtU,5840
|
|
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=U0iMIKPiKLrLVAfs_u9pPuoWYW1RJHcM8dbLF9a4yIA,29796
|
12
|
-
cloudnetpy/version.py,sha256=
|
12
|
+
cloudnetpy/version.py,sha256=1_v9fgJZISGL5Ze_UyTXKUAziVsozQnA_ChjfRY204w,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
|
@@ -32,13 +32,13 @@ cloudnetpy/categorize/attenuations/gas_attenuation.py,sha256=emr-RCxQT0i2N8k6eBN
|
|
32
32
|
cloudnetpy/categorize/attenuations/liquid_attenuation.py,sha256=0p0G79BPkw1itCXHMwbvkNHtJGBocJzow3gNHAirChI,3036
|
33
33
|
cloudnetpy/categorize/attenuations/melting_attenuation.py,sha256=9c9xoZHtGUbjFYJxkVc3UUDHLDy0UbNUZ32ITtnsj5w,2333
|
34
34
|
cloudnetpy/categorize/attenuations/rain_attenuation.py,sha256=qazJzRyXf9vbjJhh4yiFmABI4L57j5W_6YZ-6qjRiBI,2839
|
35
|
-
cloudnetpy/instruments/__init__.py,sha256=
|
35
|
+
cloudnetpy/instruments/__init__.py,sha256=2vAdceXCNxZhTujhArLf4NjYOfUdkhLpGM-NQa_LXdg,470
|
36
36
|
cloudnetpy/instruments/basta.py,sha256=_OTnySd36ktvxk_swWBzbv_H4AVGlkF_Ce3KtPGD1rE,3758
|
37
37
|
cloudnetpy/instruments/campbell_scientific.py,sha256=2WHfBKQjtRSl0AqvtPeX7G8Hdi3Dn0WbvoAppFOMbA8,5270
|
38
38
|
cloudnetpy/instruments/ceilo.py,sha256=xrI7iYNftKvGZf-3C_ESUNsu-QhXV43iWkDuKp3biZU,9552
|
39
39
|
cloudnetpy/instruments/ceilometer.py,sha256=pdmLVljsuciyKpaGxWxJ_f1IrJK-UrkBC0lSeuirLlU,12095
|
40
40
|
cloudnetpy/instruments/cl61d.py,sha256=g6DNBFju3wYhLFl32DKmC8pUup7y-EupXoUU0fuoGGA,1990
|
41
|
-
cloudnetpy/instruments/cloudnet_instrument.py,sha256=
|
41
|
+
cloudnetpy/instruments/cloudnet_instrument.py,sha256=086GJ6Nfp7sK9ZK8UygJOn-aiVPreez674_gbrOZj4I,5183
|
42
42
|
cloudnetpy/instruments/copernicus.py,sha256=he15ncH6SDCidBhr0BkOJc6Rg8cMvLWN0vGmpui6nGQ,6611
|
43
43
|
cloudnetpy/instruments/galileo.py,sha256=qABO3IUdRpGFDgqKsN0Pl4TQ-CWHQz8s13PxnwdIUnU,4828
|
44
44
|
cloudnetpy/instruments/hatpro.py,sha256=DzCWzTJxTc5BSOgoeyM8RjYkSXX6NDi3QXgKRp0uxlI,8759
|
@@ -50,11 +50,12 @@ cloudnetpy/instruments/nc_lidar.py,sha256=5gQG9PApnNPrHmS9_zanl8HEYIQuGRpbnzC3wf
|
|
50
50
|
cloudnetpy/instruments/nc_radar.py,sha256=AjPn3mkq5a1mE7YzKtZnxX5suNju9NhUq-TDvs7T_uU,6911
|
51
51
|
cloudnetpy/instruments/pollyxt.py,sha256=ra7sYQ_cmkC0T9TBYrMN6iiQEZimmWGdW9Ilv61JB-Q,10027
|
52
52
|
cloudnetpy/instruments/radiometrics.py,sha256=ySG4a042XkgjMTG8d20oAPNvFvw9bMwwiqS3zv-JF_w,11825
|
53
|
+
cloudnetpy/instruments/rain_e_h3.py,sha256=IY-ytMjQUV94qMQgIXMJsPXe6oF3yk-d9LIbN19ogv0,5376
|
53
54
|
cloudnetpy/instruments/rpg.py,sha256=vfs_eGahPOxFjOIBczNywRwtdutOsJpSNeXZm99SIOo,17438
|
54
55
|
cloudnetpy/instruments/rpg_reader.py,sha256=ThztFuVrWxhmWVAfZTfQDeUiKK1XMTbtv08IBe8GK98,11364
|
55
56
|
cloudnetpy/instruments/toa5.py,sha256=CfmmBMv5iMGaWHIGBK01Rw24cuXC1R1RMNTXkmsm340,1760
|
56
57
|
cloudnetpy/instruments/vaisala.py,sha256=GGuA_v4S7kR9yApSr1-d0ETzNj4ehEZ7-pD1-AdPYRE,14662
|
57
|
-
cloudnetpy/instruments/weather_station.py,sha256=
|
58
|
+
cloudnetpy/instruments/weather_station.py,sha256=x4IwG8loGD8FgAJgLAvRQyOy05glI6bI6sz5E7GByFo,17015
|
58
59
|
cloudnetpy/instruments/disdrometer/__init__.py,sha256=lyjwttWvFvuwYxEkusoAvgRcbBmglmOp5HJOpXUqLWo,93
|
59
60
|
cloudnetpy/instruments/disdrometer/common.py,sha256=g52iK2aNp3Z88kovUmGVpC54NZomPa9D871gzO0AmQ4,9267
|
60
61
|
cloudnetpy/instruments/disdrometer/parsivel.py,sha256=HJZrEysQkx9MiIVPDV25CYHpXi_SjgZlgO-otoaKK34,25640
|
@@ -115,9 +116,9 @@ cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe5
|
|
115
116
|
cloudnetpy/products/mwr_tools.py,sha256=rd7UC67O4fsIE5SaHVZ4qWvUJTj41ZGwgQWPwZzOM14,5377
|
116
117
|
cloudnetpy/products/product_tools.py,sha256=uu4l6reuGbPcW3TgttbaSrqIKbyYGhBVTdnC7opKvmg,11101
|
117
118
|
docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
|
118
|
-
cloudnetpy-1.
|
119
|
-
cloudnetpy-1.
|
120
|
-
cloudnetpy-1.
|
121
|
-
cloudnetpy-1.
|
122
|
-
cloudnetpy-1.
|
123
|
-
cloudnetpy-1.
|
119
|
+
cloudnetpy-1.68.1.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
120
|
+
cloudnetpy-1.68.1.dist-info/METADATA,sha256=gXDKeGrZz_D6nBeUJbvDDuWOv9fqiJmEw7S5M7DD6cA,5793
|
121
|
+
cloudnetpy-1.68.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
122
|
+
cloudnetpy-1.68.1.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
|
123
|
+
cloudnetpy-1.68.1.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
124
|
+
cloudnetpy-1.68.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|