disdrodb 0.1.4__py3-none-any.whl → 0.1.5__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 (85) hide show
  1. disdrodb/_version.py +2 -2
  2. disdrodb/api/create_directories.py +0 -2
  3. disdrodb/cli/disdrodb_create_summary.py +10 -0
  4. disdrodb/cli/disdrodb_create_summary_station.py +10 -0
  5. disdrodb/constants.py +1 -1
  6. disdrodb/etc/products/L1/global.yaml +1 -1
  7. disdrodb/etc/products/L2E/5MIN.yaml +1 -0
  8. disdrodb/etc/products/L2E/global.yaml +1 -1
  9. disdrodb/etc/products/L2M/GAMMA_GS_ND_MAE.yaml +6 -0
  10. disdrodb/etc/products/L2M/GAMMA_ML.yaml +1 -1
  11. disdrodb/etc/products/L2M/LOGNORMAL_GS_LOG_ND_MAE.yaml +6 -0
  12. disdrodb/etc/products/L2M/LOGNORMAL_GS_ND_MAE.yaml +6 -0
  13. disdrodb/etc/products/L2M/LOGNORMAL_ML.yaml +8 -0
  14. disdrodb/etc/products/L2M/global.yaml +11 -3
  15. disdrodb/l0/check_configs.py +49 -16
  16. disdrodb/l0/configs/LPM/l0a_encodings.yml +2 -2
  17. disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +2 -2
  18. disdrodb/l0/configs/LPM/l0b_encodings.yml +2 -2
  19. disdrodb/l0/configs/LPM/raw_data_format.yml +2 -2
  20. disdrodb/l0/configs/PWS100/l0b_encodings.yml +1 -0
  21. disdrodb/l0/configs/SWS250/bins_diameter.yml +108 -0
  22. disdrodb/l0/configs/SWS250/bins_velocity.yml +83 -0
  23. disdrodb/l0/configs/SWS250/l0a_encodings.yml +18 -0
  24. disdrodb/l0/configs/SWS250/l0b_cf_attrs.yml +72 -0
  25. disdrodb/l0/configs/SWS250/l0b_encodings.yml +155 -0
  26. disdrodb/l0/configs/SWS250/raw_data_format.yml +148 -0
  27. disdrodb/l0/l0b_processing.py +70 -15
  28. disdrodb/l0/readers/LPM/ARM/ARM_LPM.py +1 -1
  29. disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +2 -2
  30. disdrodb/l0/readers/LPM/BELGIUM/ULIEGE.py +256 -0
  31. disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +2 -2
  32. disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +2 -2
  33. disdrodb/l0/readers/LPM/GERMANY/DWD.py +491 -0
  34. disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +2 -2
  35. disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +2 -2
  36. disdrodb/l0/readers/LPM/KIT/CHWALA.py +2 -2
  37. disdrodb/l0/readers/LPM/SLOVENIA/ARSO.py +107 -12
  38. disdrodb/l0/readers/LPM/SLOVENIA/UL.py +3 -3
  39. disdrodb/l0/readers/LPM/SWITZERLAND/INNERERIZ_LPM.py +2 -2
  40. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +5 -14
  41. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +5 -14
  42. disdrodb/l0/readers/PARSIVEL/SLOVENIA/UL.py +117 -8
  43. disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +10 -14
  44. disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +10 -14
  45. disdrodb/l0/readers/PARSIVEL2/DENMARK/DTU.py +8 -14
  46. disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_raw.py +382 -0
  47. disdrodb/l0/readers/PARSIVEL2/FINLAND/FMI_PARSIVEL2.py +4 -0
  48. disdrodb/l0/readers/PARSIVEL2/FRANCE/OSUG.py +1 -1
  49. disdrodb/l0/readers/PARSIVEL2/GREECE/NOA.py +127 -0
  50. disdrodb/l0/readers/PARSIVEL2/ITALY/HYDROX.py +239 -0
  51. disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +5 -11
  52. disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_MIPS.py +4 -17
  53. disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +5 -14
  54. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +10 -13
  55. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +10 -13
  56. disdrodb/l0/readers/PARSIVEL2/PHILIPPINES/PANGASA.py +232 -0
  57. disdrodb/l0/readers/PARSIVEL2/SPAIN/CENER.py +6 -18
  58. disdrodb/l0/readers/PARSIVEL2/SPAIN/GRANADA.py +120 -0
  59. disdrodb/l0/readers/PARSIVEL2/USA/C3WE.py +7 -25
  60. disdrodb/l0/readers/PWS100/AUSTRIA/HOAL.py +321 -0
  61. disdrodb/l0/readers/SW250/BELGIUM/KMI.py +239 -0
  62. disdrodb/l1/beard_model.py +31 -129
  63. disdrodb/l1/fall_velocity.py +136 -83
  64. disdrodb/l1/filters.py +25 -28
  65. disdrodb/l1/processing.py +11 -13
  66. disdrodb/l1_env/routines.py +46 -17
  67. disdrodb/l2/empirical_dsd.py +6 -0
  68. disdrodb/l2/processing.py +2 -2
  69. disdrodb/metadata/geolocation.py +0 -2
  70. disdrodb/psd/fitting.py +16 -13
  71. disdrodb/routines/l2.py +35 -23
  72. disdrodb/routines/wrappers.py +5 -0
  73. disdrodb/scattering/axis_ratio.py +90 -84
  74. disdrodb/scattering/permittivity.py +6 -0
  75. disdrodb/summary/routines.py +38 -12
  76. disdrodb/utils/attrs.py +2 -0
  77. disdrodb/utils/encoding.py +5 -0
  78. disdrodb/utils/time.py +2 -2
  79. disdrodb/viz/plots.py +24 -1
  80. {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/METADATA +2 -1
  81. {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/RECORD +85 -65
  82. {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/WHEEL +0 -0
  83. {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/entry_points.txt +0 -0
  84. {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/licenses/LICENSE +0 -0
  85. {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,491 @@
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 DWD stations."""
20
+ import os
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
+
28
+ # Assign column names
29
+ COLUMNS = [
30
+ "weather_code_synop_4677_5min",
31
+ "weather_code_synop_4680_5min",
32
+ "weather_code_metar_4678_5min",
33
+ "precipitation_rate_5min",
34
+ "weather_code_synop_4677",
35
+ "weather_code_synop_4680",
36
+ "weather_code_metar_4678",
37
+ "precipitation_rate",
38
+ "rainfall_rate",
39
+ "snowfall_rate",
40
+ "precipitation_accumulated",
41
+ "mor_visibility",
42
+ "reflectivity",
43
+ "quality_index",
44
+ "max_hail_diameter",
45
+ "laser_status",
46
+ "static_signal_status",
47
+ "laser_temperature_analog_status",
48
+ "laser_temperature_digital_status",
49
+ "laser_current_analog_status",
50
+ "laser_current_digital_status",
51
+ "sensor_voltage_supply_status",
52
+ "current_heating_pane_transmitter_head_status",
53
+ "current_heating_pane_receiver_head_status",
54
+ "temperature_sensor_status",
55
+ "current_heating_voltage_supply_status",
56
+ "current_heating_house_status",
57
+ "current_heating_heads_status",
58
+ "current_heating_carriers_status",
59
+ "control_output_laser_power_status",
60
+ "reserved_status",
61
+ "temperature_interior",
62
+ "laser_temperature",
63
+ "laser_current_average",
64
+ "control_voltage",
65
+ "optical_control_voltage_output",
66
+ "sensor_voltage_supply",
67
+ "current_heating_pane_transmitter_head",
68
+ "current_heating_pane_receiver_head",
69
+ "temperature_ambient",
70
+ "current_heating_voltage_supply",
71
+ "current_heating_house",
72
+ "current_heating_heads",
73
+ "current_heating_carriers",
74
+ "number_particles",
75
+ "number_particles_internal_data",
76
+ "number_particles_min_speed",
77
+ "number_particles_min_speed_internal_data",
78
+ "number_particles_max_speed",
79
+ "number_particles_max_speed_internal_data",
80
+ "number_particles_min_diameter",
81
+ "number_particles_min_diameter_internal_data",
82
+ "number_particles_no_hydrometeor",
83
+ "number_particles_no_hydrometeor_internal_data",
84
+ "number_particles_unknown_classification",
85
+ "number_particles_unknown_classification_internal_data",
86
+ "number_particles_class_1",
87
+ "number_particles_class_1_internal_data",
88
+ "number_particles_class_2",
89
+ "number_particles_class_2_internal_data",
90
+ "number_particles_class_3",
91
+ "number_particles_class_3_internal_data",
92
+ "number_particles_class_4",
93
+ "number_particles_class_4_internal_data",
94
+ "number_particles_class_5",
95
+ "number_particles_class_5_internal_data",
96
+ "number_particles_class_6",
97
+ "number_particles_class_6_internal_data",
98
+ "number_particles_class_7",
99
+ "number_particles_class_7_internal_data",
100
+ "number_particles_class_8",
101
+ "number_particles_class_8_internal_data",
102
+ "number_particles_class_9",
103
+ "number_particles_class_9_internal_data",
104
+ "raw_drop_number",
105
+ ]
106
+
107
+
108
+ def read_synop_file(filepath, logger):
109
+ """Read SYNOP 10 min file."""
110
+ ##------------------------------------------------------------------------.
111
+ #### Define column names
112
+ column_names = [
113
+ "time",
114
+ "temperature_2m",
115
+ "relative_humidity",
116
+ "precipitation_accumulated_10min",
117
+ "total_cloud_cover",
118
+ "wind_speed",
119
+ "wind_direction",
120
+ ]
121
+ ##------------------------------------------------------------------------.
122
+ #### Define reader options
123
+ # - For more info: https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html
124
+ reader_kwargs = {}
125
+
126
+ # - Define delimiter
127
+ reader_kwargs["delimiter"] = r"\s+"
128
+
129
+ # - Avoid first column to become df index !!!
130
+ reader_kwargs["index_col"] = False
131
+
132
+ # Since column names are expected to be passed explicitly, header is set to None
133
+ reader_kwargs["header"] = None
134
+
135
+ # - Number of rows to be skipped at the beginning of the file
136
+ reader_kwargs["skiprows"] = 6
137
+
138
+ # - Define behaviour when encountering bad lines
139
+ reader_kwargs["on_bad_lines"] = "skip"
140
+
141
+ # - Define reader engine
142
+ # - C engine is faster
143
+ # - Python engine is more feature-complete
144
+ reader_kwargs["engine"] = "python"
145
+
146
+ # - Define on-the-fly decompression of on-disk data
147
+ # - Available: gzip, bz2, zip
148
+ reader_kwargs["compression"] = "infer"
149
+
150
+ # - Strings to recognize as NA/NaN and replace with standard NA flags
151
+ # - Already included: '#N/A', '#N/A N/A', '#NA', '-1.#IND', '-1.#QNAN',
152
+ # '-NaN', '-nan', '1.#IND', '1.#QNAN', '<NA>', 'N/A',
153
+ # 'NA', 'NULL', 'NaN', 'n/a', 'nan', 'null'
154
+ reader_kwargs["na_values"] = ["na", "", "error"]
155
+
156
+ ##------------------------------------------------------------------------.
157
+ #### Read the data
158
+ df = read_raw_text_file(
159
+ filepath=filepath,
160
+ column_names=column_names,
161
+ reader_kwargs=reader_kwargs,
162
+ logger=logger,
163
+ )
164
+
165
+ # Define datetime "time" column
166
+ df["time"] = pd.to_datetime(df["time"], format="%Y%m%d%H%M%S", errors="coerce")
167
+
168
+ # Return SYNOP dataframe
169
+ return df
170
+
171
+
172
+ def parse_format_v1(df):
173
+ """Parse DWD format v1."""
174
+ raise NotImplementedError
175
+
176
+
177
+ def parse_format_v2(df):
178
+ """Parse DWD format v2."""
179
+ # Count number of delimiters to identify valid rows
180
+ df = df[df["TO_PARSE"].str.count(";") == 520]
181
+
182
+ # Split by ; delimiter (before raw drop number)
183
+ df = df["TO_PARSE"].str.split(";", expand=True, n=81)
184
+
185
+ # Assign column names
186
+ column_names = [
187
+ "dummy_1",
188
+ "dummy_2",
189
+ "start_identifier",
190
+ "dummy_3",
191
+ "device_address",
192
+ "sensor_date",
193
+ "sensor_time",
194
+ "weather_code_synop_4677_5min",
195
+ "weather_code_synop_4680_5min",
196
+ "weather_code_metar_4678_5min",
197
+ "precipitation_rate_5min",
198
+ "weather_code_synop_4677",
199
+ "weather_code_synop_4680",
200
+ "weather_code_metar_4678",
201
+ "precipitation_rate",
202
+ "rainfall_rate",
203
+ "snowfall_rate",
204
+ "precipitation_accumulated",
205
+ "mor_visibility",
206
+ "reflectivity",
207
+ "quality_index",
208
+ "max_hail_diameter",
209
+ "laser_status",
210
+ "static_signal_status",
211
+ "laser_temperature_analog_status",
212
+ "laser_temperature_digital_status",
213
+ "laser_current_analog_status",
214
+ "laser_current_digital_status",
215
+ "sensor_voltage_supply_status",
216
+ "current_heating_pane_transmitter_head_status",
217
+ "current_heating_pane_receiver_head_status",
218
+ "temperature_sensor_status",
219
+ "current_heating_voltage_supply_status",
220
+ "current_heating_house_status",
221
+ "current_heating_heads_status",
222
+ "current_heating_carriers_status",
223
+ "control_output_laser_power_status",
224
+ "reserved_status",
225
+ "temperature_interior",
226
+ "laser_temperature",
227
+ "laser_current_average",
228
+ "control_voltage",
229
+ "optical_control_voltage_output",
230
+ "sensor_voltage_supply",
231
+ "current_heating_pane_transmitter_head",
232
+ "current_heating_pane_receiver_head",
233
+ "temperature_ambient",
234
+ "current_heating_voltage_supply",
235
+ "current_heating_house",
236
+ "current_heating_heads",
237
+ "current_heating_carriers",
238
+ "number_particles",
239
+ "number_particles_internal_data",
240
+ "number_particles_min_speed",
241
+ "number_particles_min_speed_internal_data",
242
+ "number_particles_max_speed",
243
+ "number_particles_max_speed_internal_data",
244
+ "number_particles_min_diameter",
245
+ "number_particles_min_diameter_internal_data",
246
+ "number_particles_no_hydrometeor",
247
+ "number_particles_no_hydrometeor_internal_data",
248
+ "number_particles_unknown_classification",
249
+ "number_particles_unknown_classification_internal_data",
250
+ "number_particles_class_1",
251
+ "number_particles_class_1_internal_data",
252
+ "number_particles_class_2",
253
+ "number_particles_class_2_internal_data",
254
+ "number_particles_class_3",
255
+ "number_particles_class_3_internal_data",
256
+ "number_particles_class_4",
257
+ "number_particles_class_4_internal_data",
258
+ "number_particles_class_5",
259
+ "number_particles_class_5_internal_data",
260
+ "number_particles_class_6",
261
+ "number_particles_class_6_internal_data",
262
+ "number_particles_class_7",
263
+ "number_particles_class_7_internal_data",
264
+ "number_particles_class_8",
265
+ "number_particles_class_8_internal_data",
266
+ "number_particles_class_9",
267
+ "number_particles_class_9_internal_data",
268
+ "raw_drop_number",
269
+ ]
270
+ df.columns = column_names
271
+
272
+ # Define datetime "time" column
273
+ df["time"] = df["sensor_date"] + "-" + df["sensor_time"]
274
+ df["time"] = pd.to_datetime(df["time"], format="%d.%m.%y-%H:%M:%S", errors="coerce")
275
+
276
+ # Drop rows with invalid raw_drop_number
277
+ df = df[df["raw_drop_number"].astype(str).str.len() == 1759]
278
+
279
+ # Drop columns not agreeing with DISDRODB L0 standards
280
+ columns_to_drop = [
281
+ "device_address",
282
+ "start_identifier",
283
+ "sensor_date",
284
+ "sensor_time",
285
+ "dummy_1",
286
+ "dummy_2",
287
+ "dummy_3",
288
+ ]
289
+ df = df.drop(columns=columns_to_drop)
290
+ return df
291
+
292
+
293
+ def parse_format_v3(df):
294
+ """Parse DWD format v3."""
295
+ # Count number of delimiters to identify valid rows
296
+ df = df[df["TO_PARSE"].str.count(";") == 498]
297
+
298
+ # Split by ; delimiter (before raw drop number)
299
+ df = df["TO_PARSE"].str.split(";", expand=True, n=59)
300
+
301
+ # Assign column names
302
+ column_names = [
303
+ "dummy_1",
304
+ "dummy_2",
305
+ "dummy_3",
306
+ "dummy_4",
307
+ "device_address",
308
+ "sensor_serial_number",
309
+ "software_version",
310
+ "dummy_5",
311
+ "dummy_6",
312
+ "sensor_date",
313
+ "sensor_time",
314
+ "weather_code_synop_4677_5min",
315
+ "weather_code_synop_4680_5min",
316
+ "weather_code_metar_4678_5min",
317
+ "precipitation_rate_5min",
318
+ "weather_code_synop_4677",
319
+ "weather_code_synop_4680",
320
+ "weather_code_metar_4678",
321
+ "precipitation_rate",
322
+ "rainfall_rate",
323
+ "snowfall_rate",
324
+ "precipitation_accumulated",
325
+ "mor_visibility",
326
+ "reflectivity",
327
+ "quality_index",
328
+ "max_hail_diameter",
329
+ "laser_status",
330
+ "static_signal_status",
331
+ "laser_temperature_analog_status",
332
+ "laser_temperature_digital_status",
333
+ "laser_current_analog_status",
334
+ "laser_current_digital_status",
335
+ "sensor_voltage_supply_status",
336
+ "current_heating_pane_transmitter_head_status",
337
+ "current_heating_pane_receiver_head_status",
338
+ "temperature_sensor_status",
339
+ "current_heating_voltage_supply_status",
340
+ "current_heating_house_status",
341
+ "current_heating_heads_status",
342
+ "current_heating_carriers_status",
343
+ "control_output_laser_power_status",
344
+ "reserved_status",
345
+ "temperature_interior",
346
+ "laser_temperature",
347
+ "laser_current_average",
348
+ "control_voltage",
349
+ "optical_control_voltage_output",
350
+ "sensor_voltage_supply",
351
+ "current_heating_pane_transmitter_head",
352
+ "current_heating_pane_receiver_head",
353
+ "temperature_ambient",
354
+ "current_heating_voltage_supply",
355
+ "current_heating_house",
356
+ "current_heating_heads",
357
+ "current_heating_carriers",
358
+ "number_particles",
359
+ # "number_particles_internal_data",
360
+ "number_particles_min_speed",
361
+ # "number_particles_min_speed_internal_data",
362
+ "number_particles_max_speed",
363
+ # "number_particles_max_speed_internal_data",
364
+ "number_particles_min_diameter",
365
+ # "number_particles_min_diameter_internal_data",
366
+ # "number_particles_no_hydrometeor",
367
+ # "number_particles_no_hydrometeor_internal_data",
368
+ # "number_particles_unknown_classification",
369
+ # "number_particles_unknown_classification_internal_data",
370
+ # "number_particles_class_1",
371
+ # "number_particles_class_1_internal_data",
372
+ # "number_particles_class_2",
373
+ # "number_particles_class_2_internal_data",
374
+ # "number_particles_class_3",
375
+ # "number_particles_class_3_internal_data",
376
+ # "number_particles_class_4",
377
+ # "number_particles_class_4_internal_data",
378
+ # "number_particles_class_5",
379
+ # "number_particles_class_5_internal_data",
380
+ # "number_particles_class_6",
381
+ # "number_particles_class_6_internal_data",
382
+ # "number_particles_class_7",
383
+ # "number_particles_class_7_internal_data",
384
+ # "number_particles_class_8",
385
+ # "number_particles_class_8_internal_data",
386
+ # "number_particles_class_9",
387
+ # "number_particles_class_9_internal_data",
388
+ "raw_drop_number",
389
+ ]
390
+ df.columns = column_names
391
+
392
+ # Sanitize columns
393
+ df["current_heating_voltage_supply"] = df["current_heating_voltage_supply"].str.replace("///", "NaN")
394
+ df["current_heating_house"] = df["current_heating_house"].str.replace("////", "NaN")
395
+ df["current_heating_heads"] = df["current_heating_heads"].str.replace("////", "NaN")
396
+ df["current_heating_carriers"] = df["current_heating_carriers"].str.replace("////", "NaN")
397
+
398
+ # Define datetime "time" column
399
+ df["time"] = df["sensor_date"] + "-" + df["sensor_time"]
400
+ df["time"] = pd.to_datetime(df["time"], format="%d.%m.%y-%H:%M:%S", errors="coerce")
401
+
402
+ # Drop rows with invalid raw_drop_number
403
+ df = df[df["raw_drop_number"].astype(str).str.len() == 1759]
404
+
405
+ # Identify missing columns and add NaN
406
+ missing_columns = np.array(COLUMNS)[np.isin(COLUMNS, df.columns, invert=True)].tolist()
407
+ if len(missing_columns) > 0:
408
+ for column in missing_columns:
409
+ df[column] = "NaN"
410
+
411
+ # Drop columns not agreeing with DISDRODB L0 standards
412
+ columns_to_drop = [
413
+ "device_address",
414
+ "sensor_serial_number",
415
+ "software_version",
416
+ "sensor_date",
417
+ "sensor_time",
418
+ "dummy_1",
419
+ "dummy_2",
420
+ "dummy_3",
421
+ "dummy_4",
422
+ "dummy_5",
423
+ "dummy_6",
424
+ ]
425
+ df = df.drop(columns=columns_to_drop)
426
+ return df
427
+
428
+
429
+ @is_documented_by(reader_generic_docstring)
430
+ def reader(
431
+ filepath,
432
+ logger=None,
433
+ ):
434
+ """Reader."""
435
+ ##------------------------------------------------------------------------.
436
+ #### - Define raw data headers
437
+ column_names = ["TO_PARSE"]
438
+
439
+ ##------------------------------------------------------------------------.
440
+ #### Define reader options
441
+ # - For more info: https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html
442
+ reader_kwargs = {}
443
+
444
+ # - Define delimiter
445
+ reader_kwargs["delimiter"] = "\\n"
446
+
447
+ # - Avoid first column to become df index !!!
448
+ reader_kwargs["index_col"] = False
449
+
450
+ # Since column names are expected to be passed explicitly, header is set to None
451
+ reader_kwargs["header"] = None
452
+
453
+ # - Number of rows to be skipped at the beginning of the file
454
+ reader_kwargs["skiprows"] = None
455
+
456
+ # - Define behaviour when encountering bad lines
457
+ reader_kwargs["on_bad_lines"] = "skip"
458
+
459
+ # - Define reader engine
460
+ # - C engine is faster
461
+ # - Python engine is more feature-complete
462
+ reader_kwargs["engine"] = "python"
463
+
464
+ # - Define on-the-fly decompression of on-disk data
465
+ # - Available: gzip, bz2, zip
466
+ reader_kwargs["compression"] = "infer"
467
+
468
+ # - Strings to recognize as NA/NaN and replace with standard NA flags
469
+ # - Already included: '#N/A', '#N/A N/A', '#NA', '-1.#IND', '-1.#QNAN',
470
+ # '-NaN', '-nan', '1.#IND', '1.#QNAN', '<NA>', 'N/A',
471
+ # 'NA', 'NULL', 'NaN', 'n/a', 'nan', 'null'
472
+ reader_kwargs["na_values"] = ["na", "", "error"]
473
+
474
+ ##------------------------------------------------------------------------.
475
+ #### Read the data
476
+ df = read_raw_text_file(
477
+ filepath=filepath,
478
+ column_names=column_names,
479
+ reader_kwargs=reader_kwargs,
480
+ logger=logger,
481
+ )
482
+ ##------------------------------------------------------------------------.
483
+ #### Adapt the dataframe to adhere to DISDRODB L0 standards
484
+ filename = os.path.basename(filepath)
485
+ if filename.startswith("3_"):
486
+ return parse_format_v3(df)
487
+ if filename.startswith("2_"):
488
+ return parse_format_v2(df)
489
+ if filename.startswith("1_"):
490
+ return parse_format_v1(df)
491
+ raise ValueError(f"Not implemented parser for DWD {filepath} data format.")
@@ -108,7 +108,7 @@ def reader(
108
108
  "quality_index",
109
109
  "max_hail_diameter",
110
110
  "laser_status",
111
- "static_signal",
111
+ "static_signal_status",
112
112
  "laser_temperature_analog_status",
113
113
  "laser_temperature_digital_status",
114
114
  "laser_current_analog_status",
@@ -122,7 +122,7 @@ def reader(
122
122
  "current_heating_heads_status",
123
123
  "current_heating_carriers_status",
124
124
  "control_output_laser_power_status",
125
- "reserve_status",
125
+ "reserved_status",
126
126
  "temperature_interior",
127
127
  "laser_temperature",
128
128
  "laser_current_average",
@@ -108,7 +108,7 @@ def reader(
108
108
  "quality_index",
109
109
  "max_hail_diameter",
110
110
  "laser_status",
111
- "static_signal",
111
+ "static_signal_status",
112
112
  "laser_temperature_analog_status",
113
113
  "laser_temperature_digital_status",
114
114
  "laser_current_analog_status",
@@ -122,7 +122,7 @@ def reader(
122
122
  "current_heating_heads_status",
123
123
  "current_heating_carriers_status",
124
124
  "control_output_laser_power_status",
125
- "reserve_status",
125
+ "reserved_status",
126
126
  "temperature_interior",
127
127
  "laser_temperature",
128
128
  "laser_current_average",
@@ -112,7 +112,7 @@ def reader(
112
112
  "quality_index",
113
113
  "max_hail_diameter",
114
114
  "laser_status",
115
- "static_signal",
115
+ "static_signal_status",
116
116
  "laser_temperature_analog_status",
117
117
  "laser_temperature_digital_status",
118
118
  "laser_current_analog_status",
@@ -126,7 +126,7 @@ def reader(
126
126
  "current_heating_heads_status",
127
127
  "current_heating_carriers_status",
128
128
  "control_output_laser_power_status",
129
- "reserve_status",
129
+ "reserved_status",
130
130
  "temperature_interior",
131
131
  "laser_temperature",
132
132
  "laser_current_average",