disdrodb 0.1.0__py3-none-any.whl → 0.1.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.
Files changed (77) hide show
  1. disdrodb/__init__.py +1 -1
  2. disdrodb/_version.py +2 -2
  3. disdrodb/api/io.py +12 -2
  4. disdrodb/data_transfer/download_data.py +145 -14
  5. disdrodb/l0/check_standards.py +15 -10
  6. disdrodb/l0/configs/LPM/bins_diameter.yml +3 -3
  7. disdrodb/l0/configs/LPM/l0a_encodings.yml +4 -4
  8. disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +22 -6
  9. disdrodb/l0/configs/LPM/l0b_encodings.yml +41 -0
  10. disdrodb/l0/configs/LPM/raw_data_format.yml +40 -0
  11. disdrodb/l0/configs/PARSIVEL/l0b_cf_attrs.yml +1 -1
  12. disdrodb/l0/configs/PARSIVEL/raw_data_format.yml +1 -1
  13. disdrodb/l0/configs/PARSIVEL2/l0a_encodings.yml +4 -0
  14. disdrodb/l0/configs/PARSIVEL2/l0b_cf_attrs.yml +20 -4
  15. disdrodb/l0/configs/PARSIVEL2/l0b_encodings.yml +41 -0
  16. disdrodb/l0/configs/PARSIVEL2/raw_data_format.yml +50 -10
  17. disdrodb/l0/configs/PWS100/bins_diameter.yml +173 -0
  18. disdrodb/l0/configs/PWS100/bins_velocity.yml +173 -0
  19. disdrodb/l0/configs/PWS100/l0a_encodings.yml +19 -0
  20. disdrodb/l0/configs/PWS100/l0b_cf_attrs.yml +76 -0
  21. disdrodb/l0/configs/PWS100/l0b_encodings.yml +176 -0
  22. disdrodb/l0/configs/PWS100/raw_data_format.yml +182 -0
  23. disdrodb/l0/configs/RD80/raw_data_format.yml +2 -6
  24. disdrodb/l0/l0b_nc_processing.py +1 -1
  25. disdrodb/l0/l0b_processing.py +12 -10
  26. disdrodb/l0/manuals/SWS250.pdf +0 -0
  27. disdrodb/l0/manuals/VPF730.pdf +0 -0
  28. disdrodb/l0/manuals/VPF750.pdf +0 -0
  29. disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +23 -13
  30. disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +3 -3
  31. disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +5 -3
  32. disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +36 -20
  33. disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +210 -0
  34. disdrodb/l0/readers/LPM/KIT/CHWALA.py +225 -0
  35. disdrodb/l0/readers/LPM/SLOVENIA/ARSO.py +197 -0
  36. disdrodb/l0/readers/LPM/SLOVENIA/CRNI_VRH.py +197 -0
  37. disdrodb/l0/readers/PARSIVEL/GPM/PIERS.py +107 -0
  38. disdrodb/l0/readers/PARSIVEL/JAPAN/JMA.py +125 -0
  39. disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +1 -1
  40. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +1 -1
  41. disdrodb/l0/readers/PARSIVEL/SLOVENIA/UL_FGG.py +121 -0
  42. disdrodb/l0/readers/PARSIVEL2/FRANCE/ENPC_PARSIVEL2.py +189 -0
  43. disdrodb/l0/readers/PARSIVEL2/KIT/BURKINA_FASO.py +133 -0
  44. disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +138 -0
  45. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +1 -1
  46. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +1 -1
  47. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +9 -0
  48. disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT_NC.py +67 -0
  49. disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100.py +150 -0
  50. disdrodb/l0/readers/RD80/NOAA/PSL_RD80.py +291 -0
  51. disdrodb/l0/readers/template_reader_raw_netcdf_data.py +1 -1
  52. disdrodb/l0/standards.py +7 -4
  53. disdrodb/l0/template_tools.py +2 -2
  54. disdrodb/l1/encoding_attrs.py +30 -8
  55. disdrodb/l1/processing.py +6 -4
  56. disdrodb/l1/resampling.py +1 -1
  57. disdrodb/l1/routines.py +9 -7
  58. disdrodb/l2/empirical_dsd.py +100 -2
  59. disdrodb/l2/event.py +3 -3
  60. disdrodb/l2/processing.py +21 -12
  61. disdrodb/l2/processing_options.py +7 -7
  62. disdrodb/l2/routines.py +3 -3
  63. disdrodb/metadata/checks.py +15 -6
  64. disdrodb/metadata/manipulation.py +2 -2
  65. disdrodb/metadata/standards.py +83 -79
  66. disdrodb/metadata/writer.py +2 -2
  67. disdrodb/routines.py +246 -10
  68. disdrodb/scattering/routines.py +1 -1
  69. disdrodb/utils/dataframe.py +342 -0
  70. disdrodb/utils/directories.py +14 -2
  71. disdrodb/utils/xarray.py +83 -0
  72. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/METADATA +34 -61
  73. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/RECORD +77 -54
  74. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/WHEEL +1 -1
  75. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/entry_points.txt +3 -3
  76. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/licenses/LICENSE +0 -0
  77. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # -----------------------------------------------------------------------------.
4
+ # Copyright (c) 2021-2023 DISDRODB developers
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ # -----------------------------------------------------------------------------.
19
+ """DISDRODB reader for ENPC PARSIVEL2 raw text data."""
20
+ import zipfile
21
+
22
+ import numpy as np
23
+ import pandas as pd
24
+
25
+ from disdrodb.l0.l0_reader import is_documented_by, reader_generic_docstring
26
+ from disdrodb.l0.l0a_processing import read_raw_text_file
27
+ from disdrodb.utils.logger import log_error
28
+
29
+
30
+ @is_documented_by(reader_generic_docstring)
31
+ def reader(
32
+ filepath,
33
+ logger=None,
34
+ ):
35
+ """Reader."""
36
+
37
+ ##------------------------------------------------------------------------.
38
+ #### Define function to read each txt file inside each daily zip file
39
+ def read_txt_file(file, filename):
40
+ """Parse a single txt file within the daily zip file."""
41
+ ##------------------------------------------------------------------------.
42
+ #### Define column names
43
+ column_names = ["TO_PARSE"]
44
+
45
+ ##------------------------------------------------------------------------.
46
+ #### Define reader options
47
+ reader_kwargs = {}
48
+ # - Define delimiter
49
+ reader_kwargs["delimiter"] = "\\n"
50
+ # - Skip first row as columns names
51
+ # - Define encoding
52
+ reader_kwargs["encoding"] = "latin" # "ISO-8859-1"
53
+ # - Avoid first column to become df index !!!
54
+ reader_kwargs["index_col"] = False
55
+ # - Define behaviour when encountering bad lines
56
+ reader_kwargs["on_bad_lines"] = "skip"
57
+ # - Define reader engine
58
+ # - C engine is faster
59
+ # - Python engine is more feature-complete
60
+ reader_kwargs["engine"] = "python"
61
+ # - Define on-the-fly decompression of on-disk data
62
+ # - Available: gzip, bz2, zip
63
+ reader_kwargs["compression"] = "infer"
64
+ # - Strings to recognize as NA/NaN and replace with standard NA flags
65
+ # - Already included: '#N/A', '#N/A N/A', '#NA', '-1.#IND', '-1.#QNAN',
66
+ # '-NaN', '-nan', '1.#IND', '1.#QNAN', '<NA>', 'N/A',
67
+ # 'NA', 'NULL', 'NaN', 'n/a', 'nan', 'null'
68
+ reader_kwargs["na_values"] = ["na", "", "error"]
69
+
70
+ ##------------------------------------------------------------------------.
71
+ #### Read the data
72
+ df = read_raw_text_file(
73
+ filepath=file,
74
+ column_names=column_names,
75
+ reader_kwargs=reader_kwargs,
76
+ logger=logger,
77
+ )
78
+
79
+ ##------------------------------------------------------------------------.
80
+ #### Adapt the dataframe to adhere to DISDRODB L0 standards
81
+ # Create ID and Value columns
82
+ df = df["TO_PARSE"].str.split(":", expand=True, n=1)
83
+ df.columns = ["ID", "Value"]
84
+
85
+ # Select only rows with values
86
+ df = df[df["Value"].apply(lambda x: x is not None)]
87
+
88
+ # Drop rows with invalid IDs
89
+ valid_id_str = np.char.rjust(np.arange(0, 94).astype(str), width=2, fillchar="0")
90
+ df = df[df["ID"].astype(str).isin(valid_id_str)]
91
+
92
+ # Create the dataframe with each row corresponding to a timestep
93
+ # - Group rows based on when ID values restart
94
+ groups = df.groupby((df["ID"].astype(int).diff() <= 0).cumsum())
95
+
96
+ # Reshape the dataframe
97
+ group_dfs = []
98
+ for _, group in groups:
99
+ group_df = group.set_index("ID").T
100
+ group_dfs.append(group_df)
101
+
102
+ # Merge each timestep dataframe
103
+ # --> Missing columns are infilled by NaN
104
+ df = pd.concat(group_dfs, axis=0)
105
+
106
+ # Assign column names
107
+ column_dict = {
108
+ "01": "rainfall_rate_32bit",
109
+ "02": "rainfall_accumulated_32bit",
110
+ "03": "weather_code_synop_4680",
111
+ "04": "weather_code_synop_4677",
112
+ "05": "weather_code_metar_4678",
113
+ "06": "weather_code_nws",
114
+ "07": "reflectivity_32bit",
115
+ "08": "mor_visibility",
116
+ "09": "sample_interval",
117
+ "10": "laser_amplitude",
118
+ "11": "number_particles",
119
+ "12": "sensor_temperature",
120
+ # "13": "sensor_serial_number",
121
+ # "14": "firmware_iop",
122
+ # "15": "firmware_dsp",
123
+ "16": "sensor_heating_current",
124
+ "17": "sensor_battery_voltage",
125
+ "18": "sensor_status",
126
+ # "19": "start_time",
127
+ # "20": "sensor_time",
128
+ # "21": "sensor_date",
129
+ # "22": "station_name",
130
+ # "23": "station_number",
131
+ "24": "rainfall_amount_absolute_32bit",
132
+ "25": "error_code",
133
+ "26": "sensor_temperature_pcb",
134
+ "27": "sensor_temperature_receiver",
135
+ "28": "sensor_temperature_trasmitter",
136
+ "30": "rainfall_rate_16_bit_30",
137
+ "31": "rainfall_rate_16_bit_1200",
138
+ "32": "rainfall_accumulated_16bit",
139
+ "34": "rain_kinetic_energy",
140
+ "35": "snowfall_rate",
141
+ "90": "raw_drop_concentration",
142
+ "91": "raw_drop_average_velocity",
143
+ "93": "raw_drop_number",
144
+ }
145
+
146
+ df = df.rename(column_dict, axis=1)
147
+
148
+ # Keep only columns defined in the dictionary
149
+ df = df[list(column_dict.values())]
150
+
151
+ # Define datetime "time" column from filename
152
+ datetime_str = " ".join(filename.replace(".txt", "").split("_")[-6:])
153
+ df["time"] = pd.to_datetime(datetime_str, format="%Y %m %d %H %M %S")
154
+
155
+ # # Drop columns not agreeing with DISDRODB L0 standards
156
+ # columns_to_drop = [
157
+ # "sensor_date",
158
+ # "sensor_time",
159
+ # "firmware_iop",
160
+ # "firmware_dsp",
161
+ # "sensor_serial_number",
162
+ # "station_name",
163
+ # "station_number",
164
+ # ]
165
+ # df = df.drop(columns=columns_to_drop)
166
+ return df
167
+
168
+ # ---------------------------------------------------------------------.
169
+ #### Iterate over all files (aka timesteps) in the daily zip archive
170
+ # - Each file contain a single timestep !
171
+ list_df = []
172
+ with zipfile.ZipFile(filepath, "r") as zip_ref:
173
+ filenames = sorted(zip_ref.namelist())
174
+ for filename in filenames:
175
+ if filename.endswith(".txt"):
176
+ # Open file
177
+ with zip_ref.open(filename) as file:
178
+ try:
179
+ df = read_txt_file(file=file, filename=filename)
180
+ list_df.append(df)
181
+ except Exception as e:
182
+ msg = f"An error occurred while reading {filename}. The error is: {e}."
183
+ log_error(logger=logger, msg=msg, verbose=True)
184
+
185
+ # Concatenate all dataframes into a single one
186
+ df = pd.concat(list_df)
187
+
188
+ # ---------------------------------------------------------------------.
189
+ return df
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python3
2
+ # -----------------------------------------------------------------------------.
3
+ # Copyright (c) 2021-2023 DISDRODB developers
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ # -----------------------------------------------------------------------------.
18
+ import pandas as pd
19
+
20
+ from disdrodb.l0.l0_reader import is_documented_by, reader_generic_docstring
21
+ from disdrodb.l0.l0a_processing import read_raw_text_file
22
+
23
+
24
+ @is_documented_by(reader_generic_docstring)
25
+ def reader(
26
+ filepath,
27
+ logger=None,
28
+ ):
29
+ """Reader."""
30
+ ##------------------------------------------------------------------------.
31
+ #### Define column names
32
+ column_names = ["TO_SPLIT"]
33
+
34
+ ##------------------------------------------------------------------------.
35
+ #### Define reader options
36
+ reader_kwargs = {}
37
+
38
+ # - Define delimiter
39
+ reader_kwargs["delimiter"] = "\\n"
40
+
41
+ # - Skip first row as columns names
42
+ reader_kwargs["header"] = None
43
+
44
+ # - Skip header
45
+ reader_kwargs["skiprows"] = 7
46
+
47
+ # - Define encoding
48
+ reader_kwargs["encoding"] = "ISO-8859-1"
49
+
50
+ # - Avoid first column to become df index !!!
51
+ reader_kwargs["index_col"] = False
52
+
53
+ # - Define behaviour when encountering bad lines
54
+ reader_kwargs["on_bad_lines"] = "skip"
55
+
56
+ # - Define reader engine
57
+ # - C engine is faster
58
+ # - Python engine is more feature-complete
59
+ reader_kwargs["engine"] = "python"
60
+
61
+ # - Define on-the-fly decompression of on-disk data
62
+ # - Available: gzip, bz2, zip
63
+ # reader_kwargs['compression'] = 'xz'
64
+
65
+ # - Strings to recognize as NA/NaN and replace with standard NA flags
66
+ # - Already included: '#N/A', '#N/A N/A', '#NA', '-1.#IND', '-1.#QNAN',
67
+ # '-NaN', '-nan', '1.#IND', '1.#QNAN', '<NA>', 'N/A',
68
+ # 'NA', 'NULL', 'NaN', 'n/a', 'nan', 'null'
69
+ reader_kwargs["na_values"] = ["na", "error", "-.-", " NA"]
70
+
71
+ ##------------------------------------------------------------------------.
72
+ #### Read the data
73
+ df = read_raw_text_file(
74
+ filepath=filepath,
75
+ column_names=column_names,
76
+ reader_kwargs=reader_kwargs,
77
+ logger=logger,
78
+ )
79
+
80
+ ##------------------------------------------------------------------------.
81
+ #### Adapt the dataframe to adhere to DISDRODB L0 standards
82
+ # Remove repeated headers (with length 301)
83
+ # df = df[df["TO_SPLIT"].str.len() != 301]
84
+
85
+ # Split into columns and assign name
86
+ df = df["TO_SPLIT"].str.split(";", expand=True, n=16)
87
+ columns = [
88
+ "date",
89
+ "time",
90
+ "rainfall_rate_32bit",
91
+ "rainfall_accumulated_32bit",
92
+ "weather_code_synop_4680",
93
+ "weather_code_metar_4678",
94
+ "weather_code_nws",
95
+ "reflectivity_32bit",
96
+ "mor_visibility",
97
+ "laser_amplitude",
98
+ "number_particles",
99
+ "sensor_temperature",
100
+ "sensor_status",
101
+ "sensor_battery_voltage",
102
+ "unknown", # "rainfall_amount_absolute_32bit", ?
103
+ "error_code",
104
+ "raw_drop_number",
105
+ ]
106
+ df.columns = columns
107
+
108
+ # Add datetime time column
109
+ df["time"] = df["date"] + "-" + df["time"]
110
+ df["time"] = pd.to_datetime(df["time"], format="%d.%m.%Y-%H:%M:%S", errors="coerce")
111
+ df = df.drop(columns=["date"])
112
+
113
+ # Preprocess the raw spectrum
114
+ # - The '<SPECTRUM>ZERO</SPECTRUM>' indicates no drops detected
115
+ # --> "" generates an array of zeros in L0B processing
116
+ df["raw_drop_number"] = df["raw_drop_number"].str.replace("<SPECTRUM>ZERO</SPECTRUM>", "")
117
+
118
+ # Remove <SPECTRUM> and </SPECTRUM>" acronyms from the raw_drop_number field
119
+ df["raw_drop_number"] = df["raw_drop_number"].str.replace("<SPECTRUM>", "")
120
+ df["raw_drop_number"] = df["raw_drop_number"].str.replace("</SPECTRUM>", "")
121
+
122
+ # Add 0 before every , if , not preceded by a digit
123
+ # Example: ',,1,,' --> '0,0,1,0,'
124
+ df["raw_drop_number"] = df["raw_drop_number"].str.replace(r"(?<!\d);", "0;", regex=True)
125
+
126
+ # Drop columns not agreeing with DISDRODB L0 standards
127
+ columns_to_drop = [
128
+ "unknown",
129
+ ]
130
+ df = df.drop(columns=columns_to_drop)
131
+
132
+ # Return the dataframe adhering to DISDRODB L0 standards
133
+ return df
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env python3
2
+ # -----------------------------------------------------------------------------.
3
+ # Copyright (c) 2021-2023 DISDRODB developers
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ # -----------------------------------------------------------------------------.
18
+ """Reader for CSWR FARM disdrometer data (used in PERILS and RELAMPAGO campaign)."""
19
+ import pandas as pd
20
+
21
+ from disdrodb.l0.l0_reader import is_documented_by, reader_generic_docstring
22
+ from disdrodb.l0.l0a_processing import read_raw_text_file
23
+
24
+
25
+ @is_documented_by(reader_generic_docstring)
26
+ def reader(
27
+ filepath,
28
+ logger=None,
29
+ ):
30
+ """Reader."""
31
+ ##------------------------------------------------------------------------.
32
+ #### Define column names
33
+ column_names = ["TO_PARSE"]
34
+
35
+ ##------------------------------------------------------------------------.
36
+ #### Define reader options
37
+ reader_kwargs = {}
38
+
39
+ # - Define delimiter
40
+ reader_kwargs["delimiter"] = "\\n"
41
+
42
+ # - Define encoding
43
+ reader_kwargs["encoding"] = "ISO-8859-1"
44
+
45
+ # Skip first row as columns names
46
+ reader_kwargs["header"] = None
47
+ reader_kwargs["skiprows"] = 2
48
+
49
+ # - Avoid first column to become df index !!!
50
+ reader_kwargs["index_col"] = False
51
+
52
+ # - Define behaviour when encountering bad lines
53
+ reader_kwargs["on_bad_lines"] = "skip"
54
+
55
+ # - Define reader engine
56
+ # - C engine is faster
57
+ # - Python engine is more feature-complete
58
+ reader_kwargs["engine"] = "python"
59
+
60
+ # - Define on-the-fly decompression of on-disk data
61
+ # - Available: gzip, bz2, zip
62
+ reader_kwargs["compression"] = "infer"
63
+
64
+ # - Strings to recognize as NA/NaN and replace with standard NA flags
65
+ # - Already included: '#N/A', '#N/A N/A', '#NA', '-1.#IND', '-1.#QNAN',
66
+ # '-NaN', '-nan', '1.#IND', '1.#QNAN', '<NA>', 'N/A',
67
+ # 'NA', 'NULL', 'NaN', 'n/a', 'nan', 'null'
68
+ reader_kwargs["na_values"] = ["na", "", "error"]
69
+
70
+ ##------------------------------------------------------------------------.
71
+ #### Read the data
72
+ df = read_raw_text_file(
73
+ filepath=filepath,
74
+ column_names=column_names,
75
+ reader_kwargs=reader_kwargs,
76
+ logger=logger,
77
+ )
78
+
79
+ ##------------------------------------------------------------------------.
80
+ #### Adapt the dataframe to adhere to DISDRODB L0 standards
81
+ # Split and assign integrated variables names
82
+ df = df["TO_PARSE"].str.split(",", expand=True, n=22)
83
+
84
+ names = [
85
+ "time",
86
+ "station_name",
87
+ "station_number",
88
+ "rainfall_rate_32bit",
89
+ "rainfall_accumulated_32bit",
90
+ "weather_code_synop_4680",
91
+ "weather_code_synop_4677",
92
+ "weather_code_metar_4678",
93
+ "weather_code_nws",
94
+ "reflectivity_32bit",
95
+ "mor_visibility",
96
+ "sample_interval",
97
+ "laser_amplitude",
98
+ "number_particles",
99
+ "sensor_temperature",
100
+ "sensor_serial_number",
101
+ "firmware_iop",
102
+ "firmware_dsp",
103
+ "sensor_heating_current",
104
+ "sensor_battery_voltage",
105
+ "sensor_status",
106
+ "rain_kinetic_energy",
107
+ "TO_SPLIT",
108
+ ]
109
+ df.columns = names
110
+
111
+ # Derive raw drop arrays
112
+ def split_string(s):
113
+ vals = [v.strip() for v in s.split(",")]
114
+ c1 = ", ".join(vals[:32])
115
+ c2 = ", ".join(vals[32:64])
116
+ c3 = ", ".join(vals[64:])
117
+ return pd.Series({"raw_drop_concentration": c1, "raw_drop_average_velocity": c2, "raw_drop_number": c3})
118
+
119
+ splitted_string = df["TO_SPLIT"].apply(split_string)
120
+ df["raw_drop_concentration"] = splitted_string["raw_drop_concentration"]
121
+ df["raw_drop_average_velocity"] = splitted_string["raw_drop_average_velocity"]
122
+ df["raw_drop_number"] = splitted_string["raw_drop_number"]
123
+
124
+ # Define datetime "time" column
125
+ df["time"] = pd.to_datetime(df["time"], format="%Y-%m-%d %H:%M:%S", errors="coerce")
126
+
127
+ # Drop columns not agreeing with DISDRODB L0 standards
128
+ columns_to_drop = [
129
+ "station_name",
130
+ "station_number",
131
+ "firmware_iop",
132
+ "firmware_dsp",
133
+ "TO_SPLIT",
134
+ ]
135
+ df = df.drop(columns=columns_to_drop)
136
+
137
+ # Return the dataframe adhering to DISDRODB L0 standards
138
+ return df
@@ -106,7 +106,7 @@ def reader(
106
106
  # Preprocess the raw spectrum
107
107
  # - The '<SPECTRUM>ZERO</SPECTRUM>' indicates no drops detected
108
108
  # --> "" generates an array of zeros in L0B processing
109
- df["raw_drop_number"] = df["raw_drop_number"].replace("<SPECTRUM>ZERO</SPECTRUM>", "")
109
+ df["raw_drop_number"] = df["raw_drop_number"].str.replace("<SPECTRUM>ZERO</SPECTRUM>", "")
110
110
 
111
111
  # Remove <SPECTRUM> and </SPECTRUM>" acronyms from the raw_drop_number field
112
112
  df["raw_drop_number"] = df["raw_drop_number"].str.replace("<SPECTRUM>", "")
@@ -100,7 +100,7 @@ def reader(
100
100
  # --> "" generates an array of zeros in L0B processing
101
101
  df["raw_drop_number"] = df["raw_drop_number"].astype("string")
102
102
  df["raw_drop_number"] = df["raw_drop_number"].str.strip()
103
- df["raw_drop_number"] = df["raw_drop_number"].replace("<SPECTRUM>ZERO</SPECTRUM>", "")
103
+ df["raw_drop_number"] = df["raw_drop_number"].str.replace("<SPECTRUM>ZERO</SPECTRUM>", "")
104
104
 
105
105
  # Remove <SPECTRUM> and </SPECTRUM>" acronyms from the raw_drop_number field
106
106
  df["raw_drop_number"] = df["raw_drop_number"].str.replace("<SPECTRUM>", "")
@@ -126,6 +126,15 @@ def reader(
126
126
  ]
127
127
  df_data.columns = column_names
128
128
 
129
+ # Add weather information
130
+ df_data["air_temperature"] = df["fast_temperature"]
131
+ df_data["relative_humidity"] = df["relative_humidity"]
132
+ df_data["wind_direction"] = df["wind_direction"]
133
+ df_data["wind_speed"] = df["wind_speed"]
134
+
135
+ # df_data["dew_point"] = df["dew_point"]
136
+ # df_data["air_pressure"] = df["pressure"]
137
+
129
138
  # Retrieve time and coordinates information
130
139
  # --> Latitude in degrees_north
131
140
  # --> Longitude in degrees_east
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env python3
2
+ # -----------------------------------------------------------------------------.
3
+ # Copyright (c) 2021-2023 DISDRODB developers
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ # -----------------------------------------------------------------------------.
18
+ """Reader for DELFT OTT PARSIVEL2 sensor in netCDF format."""
19
+
20
+ from disdrodb.l0.l0_reader import is_documented_by, reader_generic_docstring
21
+ from disdrodb.l0.l0b_nc_processing import open_raw_netcdf_file, standardize_raw_dataset
22
+
23
+
24
+ @is_documented_by(reader_generic_docstring)
25
+ def reader(
26
+ filepath,
27
+ logger=None,
28
+ ):
29
+ """Reader."""
30
+ ##------------------------------------------------------------------------.
31
+ #### Open the netCDF
32
+ ds = open_raw_netcdf_file(filepath=filepath, logger=logger)
33
+
34
+ ##------------------------------------------------------------------------.
35
+ #### Adapt the dataframe to adhere to DISDRODB L0 standards
36
+ # Add time coordinate
37
+ ds["time"] = ds["time_as_string"].astype("M8[s]")
38
+ ds = ds.set_coords("time")
39
+
40
+ # Define dictionary mapping dataset variables to select and rename
41
+ dict_names = {
42
+ ### Dimensions
43
+ "diameter_classes": "diameter_bin_center",
44
+ "velocity_classes": "velocity_bin_center",
45
+ ### Variables
46
+ "rainfall_rate_32bit": "rainfall_rate_32bit",
47
+ "weather_code_synop_4680": "weather_code_synop_4680",
48
+ "weather_code_synop_4677": "weather_code_synop_4677",
49
+ "weather_code_metar_4678": "weather_code_metar_4678",
50
+ "weather_code_nws": "weather_code_nws",
51
+ "reflectivity_32bit": "reflectivity_32bit",
52
+ "mor_visibility": "mor_visibility",
53
+ "laser_amplitude": "laser_amplitude",
54
+ "number_particles_validated": "number_particles",
55
+ "sensor_temperature": "sensor_temperature",
56
+ "error_code": "error_code",
57
+ "kinetic_energy": "rain_kinetic_energy",
58
+ "fieldV": "raw_drop_average_velocity",
59
+ "fieldN": "raw_drop_concentration",
60
+ "raw_data": "raw_drop_number",
61
+ }
62
+
63
+ # Rename dataset variables and columns and infill missing variables
64
+ ds = standardize_raw_dataset(ds=ds, dict_names=dict_names, sensor_name="PARSIVEL2")
65
+
66
+ # Return the dataset adhering to DISDRODB L0B standards
67
+ return ds