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
disdrodb/__init__.py CHANGED
@@ -73,7 +73,7 @@ DIAMETER_COORDS = ["diameter_bin_center", "diameter_bin_width", "diameter_bin_lo
73
73
  VELOCITY_COORDS = ["velocity_bin_center", "velocity_bin_width", "velocity_bin_lower", "velocity_bin_upper"]
74
74
  VELOCITY_DIMENSION = "velocity_bin_center"
75
75
  DIAMETER_DIMENSION = "diameter_bin_center"
76
- OPTICAL_SENSORS = ["PARSIVEL", "PARSIVEL2", "LPM"]
76
+ OPTICAL_SENSORS = ["PARSIVEL", "PARSIVEL2", "LPM", "PWS100"]
77
77
  IMPACT_SENSORS = ["RD80"]
78
78
 
79
79
 
disdrodb/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.1.0'
21
- __version_tuple__ = version_tuple = (0, 1, 0)
20
+ __version__ = version = '0.1.2'
21
+ __version_tuple__ = version_tuple = (0, 1, 2)
disdrodb/api/io.py CHANGED
@@ -141,6 +141,7 @@ def open_dataset(
141
141
  product_kwargs=None,
142
142
  debugging_mode: bool = False,
143
143
  data_archive_dir: Optional[str] = None,
144
+ parallel=False,
144
145
  **open_kwargs,
145
146
  ):
146
147
  """Retrieve DISDRODB product files for a give station.
@@ -205,8 +206,17 @@ def open_dataset(
205
206
  # Open DISDRODB netCDF files using xarray
206
207
  # - TODO: parallel option and add closers !
207
208
  # - decode_timedelta -- > sample_interval not decoded to timedelta !
208
- list_ds = [xr.open_dataset(fpath, decode_timedelta=False, **open_kwargs) for fpath in filepaths]
209
- ds = xr.concat(list_ds, dim="time")
209
+ # list_ds = [xr.open_dataset(fpath, decode_timedelta=False, **open_kwargs) for fpath in filepaths]
210
+ # ds = xr.concat(list_ds, dim="time")
211
+ ds = xr.open_mfdataset(
212
+ filepaths,
213
+ engine="netcdf4",
214
+ combine="nested", # 'by_coords',
215
+ concat_dim="time",
216
+ decode_timedelta=False,
217
+ parallel=parallel,
218
+ **open_kwargs,
219
+ )
210
220
  return ds
211
221
 
212
222
 
@@ -21,6 +21,8 @@
21
21
  import logging
22
22
  import os
23
23
  import shutil
24
+ import subprocess
25
+ import urllib.parse
24
26
  from typing import Optional, Union
25
27
 
26
28
  import click
@@ -213,7 +215,7 @@ def download_station(
213
215
  check_exists=True,
214
216
  )
215
217
  # Download data
216
- _download_station_data(metadata_filepath, data_archive_dir=data_archive_dir, force=force)
218
+ download_station_data(metadata_filepath, data_archive_dir=data_archive_dir, force=force)
217
219
 
218
220
 
219
221
  def _is_valid_disdrodb_data_url(disdrodb_data_url):
@@ -228,13 +230,25 @@ def _extract_station_files(zip_filepath, station_dir):
228
230
  os.remove(zip_filepath)
229
231
 
230
232
 
231
- def _download_station_data(metadata_filepath: str, data_archive_dir: str, force: bool = False) -> None:
233
+ def check_consistent_station_name(metadata_filepath, station_name):
234
+ """Check consistent station_name between YAML file name and metadata key."""
235
+ # Check consistent station name
236
+ expected_station_name = os.path.basename(metadata_filepath).replace(".yml", "")
237
+ if station_name and str(station_name) != str(expected_station_name):
238
+ raise ValueError(f"Inconsistent station_name values in the {metadata_filepath} file. Download aborted.")
239
+ return station_name
240
+
241
+
242
+ def download_station_data(metadata_filepath: str, data_archive_dir: str, force: bool = False) -> None:
232
243
  """Download and unzip the station data .
233
244
 
234
245
  Parameters
235
246
  ----------
236
247
  metadata_filepaths : str
237
248
  Metadata file path.
249
+ data_archive_dir : str (optional)
250
+ DISDRODB Data Archive directory. Format: ``<...>/DISDRODB``.
251
+ If ``None`` (the default), the disdrodb config variable ``data_archive_dir`` is used.
238
252
  force : bool, optional
239
253
  If ``True``, delete existing files and redownload it. The default value is ``False``.
240
254
 
@@ -247,7 +261,7 @@ def _download_station_data(metadata_filepath: str, data_archive_dir: str, force:
247
261
  campaign_name = metadata_dict["campaign_name"]
248
262
  station_name = metadata_dict["station_name"]
249
263
  station_name = check_consistent_station_name(metadata_filepath, station_name)
250
- # Define the destination local filepath path
264
+ # Define the path to the station RAW data directory
251
265
  station_dir = define_station_dir(
252
266
  data_archive_dir=data_archive_dir,
253
267
  data_source=data_source,
@@ -259,19 +273,136 @@ def _download_station_data(metadata_filepath: str, data_archive_dir: str, force:
259
273
  disdrodb_data_url = metadata_dict.get("disdrodb_data_url", None)
260
274
  if not _is_valid_disdrodb_data_url(disdrodb_data_url):
261
275
  raise ValueError(f"Invalid disdrodb_data_url '{disdrodb_data_url}' for station {station_name}")
262
- # Download file
263
- zip_filepath = _download_file_from_url(disdrodb_data_url, dst_dir=station_dir, force=force)
264
- # Extract the stations files from the downloaded station.zip file
265
- _extract_station_files(zip_filepath, station_dir=station_dir)
266
276
 
277
+ # Download files
278
+ # - Option 1: Zip file from Zenodo containing all station raw data
279
+ if disdrodb_data_url.startswith("https://zenodo.org/"):
280
+ download_zenodo_zip_file(url=disdrodb_data_url, dst_dir=station_dir, force=force)
281
+ # - Option 2: Recursive download from a web server via HTTP or HTTPS.
282
+ elif disdrodb_data_url.startswith("http"):
283
+ download_web_server_data(url=disdrodb_data_url, dst_dir=station_dir, force=force, verbose=True)
284
+ else:
285
+ raise NotImplementedError(f"Open a GitHub Issue to enable the download of data from {disdrodb_data_url}.")
267
286
 
268
- def check_consistent_station_name(metadata_filepath, station_name):
269
- """Check consistent station_name between YAML file name and metadata key."""
270
- # Check consistent station name
271
- expected_station_name = os.path.basename(metadata_filepath).replace(".yml", "")
272
- if station_name and str(station_name) != str(expected_station_name):
273
- raise ValueError(f"Inconsistent station_name values in the {metadata_filepath} file. Download aborted.")
274
- return station_name
287
+
288
+ ####-----------------------------------------------------------------------------------------.
289
+ #### Download from Web Server via HTTP or HTTPS
290
+
291
+
292
+ def download_web_server_data(url: str, dst_dir: str, force=True, verbose=True) -> None:
293
+ """Download data from a web server via HTTP or HTTPS.
294
+
295
+ Use the system's wget command to recursively download all files and subdirectories
296
+ under the given HTTPS “directory” URL. Works on both Windows and Linux, provided
297
+ that wget is installed and on the PATH.
298
+
299
+ 1. Ensure wget is available.
300
+ 2. Normalize URL to end with '/'.
301
+ 3. Compute cut-dirs so that only the last segment of the path remains locally.
302
+ 4. Build and run the wget command.
303
+
304
+ Example:
305
+ download_with_wget("https://ruisdael.citg.tudelft.nl/parsivel/PAR001_Cabauw/2021/202101/")
306
+ # → Creates a local folder "202101/" with all files and subfolders.
307
+ """
308
+ # 1. Ensure wget exists
309
+ ensure_wget_available()
310
+
311
+ # 2. Normalize URL
312
+ url = ensure_trailing_slash(url)
313
+
314
+ # 3. Compute cut-dirs so that only the last URL segment remains locally
315
+ cut_dirs = compute_cut_dirs(url)
316
+
317
+ # 4. Create destination directory if needed
318
+ os.makedirs(dst_dir, exist_ok=True)
319
+
320
+ # 5. Build wget command
321
+ cmd = build_webserver_wget_command(url, cut_dirs=cut_dirs, dst_dir=dst_dir, force=force, verbose=verbose)
322
+
323
+ # 6. Run wget command
324
+ try:
325
+ subprocess.run(cmd, check=True)
326
+ except subprocess.CalledProcessError as e:
327
+ raise subprocess.CalledProcessError(
328
+ returncode=e.returncode,
329
+ cmd=e.cmd,
330
+ output=e.output,
331
+ stderr=e.stderr,
332
+ )
333
+
334
+
335
+ def ensure_wget_available() -> None:
336
+ """Raise FileNotFoundError if 'wget' is not on the system PATH."""
337
+ if shutil.which("wget") is None:
338
+ raise FileNotFoundError("The WGET software was not found. Please install WGET or add it to PATH.")
339
+
340
+
341
+ def ensure_trailing_slash(url: str) -> str:
342
+ """Return `url` guaranteed to end with a slash."""
343
+ return url if url.endswith("/") else url.rstrip("/") + "/"
344
+
345
+
346
+ def compute_cut_dirs(url: str) -> int:
347
+ """Compute the wget cut_dirs value to download directly in `dst_dir`.
348
+
349
+ Given a URL ending with '/', compute the total number of path segments.
350
+ By returning len(segments), we strip away all of them—so that files
351
+ within that final directory land directly in `dst_dir` without creating
352
+ an extra subfolder.
353
+ """
354
+ parsed = urllib.parse.urlparse(url)
355
+ path = parsed.path.strip("/") # remove leading/trailing '/'
356
+ segments = path.split("/") if path else []
357
+ return len(segments)
358
+
359
+
360
+ def build_webserver_wget_command(url: str, cut_dirs: int, dst_dir: str, force: bool, verbose: bool) -> list[str]:
361
+ """Construct the wget command list for subprocess.run.
362
+
363
+ Notes
364
+ -----
365
+ The following wget arguments are used
366
+ - -q : quiet mode (no detailed progress)
367
+ - -r : recursive
368
+ - -np : no parent
369
+ - -nH : no host directories
370
+ - --timestamping: download missing files or when remote version is newer
371
+ - --cut-dirs : strip all but the last path segment from the remote path
372
+ - -P dst_dir : download into `dst_dir`
373
+ - url
374
+ """
375
+ cmd = ["wget"]
376
+ if verbose:
377
+ cmd.append("-q")
378
+ cmd += [
379
+ "-r",
380
+ "-np",
381
+ "-nH",
382
+ f"--cut-dirs={cut_dirs}",
383
+ ]
384
+ if force:
385
+ cmd.append("--timestamping") # -N
386
+
387
+ # Define source and destination directory
388
+ cmd += [
389
+ "-P",
390
+ dst_dir,
391
+ url,
392
+ ]
393
+ return cmd
394
+
395
+
396
+ ####--------------------------------------------------------------------.
397
+ #### Download from Zenodo
398
+
399
+
400
+ def download_zenodo_zip_file(url, dst_dir, force):
401
+ """Download zip file from zenodo and extract station raw data."""
402
+ # Download zip file
403
+ zip_filepath = _download_file_from_url(url, dst_dir=dst_dir, force=force)
404
+ # Extract the stations files from the downloaded station.zip file
405
+ _extract_station_files(zip_filepath, station_dir=dst_dir)
275
406
 
276
407
 
277
408
  def _download_file_from_url(url: str, dst_dir: str, force: bool = False) -> str:
@@ -80,7 +80,12 @@ def _check_valid_values(df, dict_valid_values):
80
80
  raise ValueError(f"Columns {list_wrong_columns} have invalid values.")
81
81
 
82
82
 
83
- def _check_raw_fields_available(df: pd.DataFrame, sensor_name: str, logger=None, verbose: bool = False) -> None:
83
+ def _check_raw_fields_available(
84
+ df: pd.DataFrame,
85
+ sensor_name: str, # noqa: ARG001
86
+ logger=None, # noqa: ARG001
87
+ verbose: bool = False, # noqa: ARG001
88
+ ) -> None:
84
89
  """Check the presence of the raw spectrum data according to the type of sensor.
85
90
 
86
91
  Parameters
@@ -95,22 +100,22 @@ def _check_raw_fields_available(df: pd.DataFrame, sensor_name: str, logger=None,
95
100
  ValueError
96
101
  Error if the ``raw_drop_number`` field is missing.
97
102
  """
98
- from disdrodb.l0.standards import get_raw_array_nvalues
99
-
100
- # Retrieve raw arrays that could be available (based on sensor_name)
101
- n_bins_dict = get_raw_array_nvalues(sensor_name=sensor_name)
102
- raw_vars = np.array(list(n_bins_dict.keys()))
103
+ # from disdrodb.l0.standards import get_raw_array_nvalues
103
104
 
104
105
  # Check that raw_drop_number is present
105
106
  if "raw_drop_number" not in df.columns:
106
107
  msg = "The 'raw_drop_number' column is not present in the dataframe."
107
108
  raise ValueError(msg)
108
109
 
110
+ # Retrieve raw arrays that could be available (based on sensor_name)
111
+ # n_bins_dict = get_raw_array_nvalues(sensor_name=sensor_name)
112
+
109
113
  # Report additional raw arrays that are missing
110
- missing_vars = raw_vars[np.isin(raw_vars, list(df.columns), invert=True)]
111
- if len(missing_vars) > 0:
112
- msg = f"The following raw array variable are missing: {missing_vars}"
113
- log_info(logger=logger, msg=msg, verbose=verbose)
114
+ # raw_vars = np.array(list(n_bins_dict.keys()))
115
+ # missing_vars = raw_vars[np.isin(raw_vars, list(df.columns), invert=True)]
116
+ # if len(missing_vars) > 0:
117
+ # msg = f"The following raw array variable are missing: {missing_vars}"
118
+ # log_info(logger=logger, msg=msg, verbose=verbose)
114
119
 
115
120
 
116
121
  def check_l0a_column_names(df: pd.DataFrame, sensor_name: str) -> None:
@@ -20,7 +20,7 @@ center:
20
20
  18: 6.75
21
21
  19: 7.25
22
22
  20: 7.75
23
- 21: 54
23
+ 21: 9
24
24
  bounds:
25
25
  0:
26
26
  - 0.125
@@ -87,7 +87,7 @@ bounds:
87
87
  - 8.0
88
88
  21:
89
89
  - 8.0
90
- - 100
90
+ - 10.0
91
91
  width:
92
92
  0: 0.125
93
93
  1: 0.125
@@ -110,4 +110,4 @@ width:
110
110
  18: 0.5
111
111
  19: 0.5
112
112
  20: 0.5
113
- 21: 92
113
+ 21: 2
@@ -74,7 +74,7 @@ number_particles_class_8_internal_data: "float32"
74
74
  number_particles_class_9: "float32" # 'uint16'
75
75
  number_particles_class_9_internal_data: "float32"
76
76
  raw_drop_number: "str"
77
- # '521': air_temperature
78
- # '522': relative_humidity
79
- # '523': wind_speed
80
- # '524': wind_direction
77
+ air_temperature: "float32"
78
+ relative_humidity: "float32"
79
+ wind_speed: "float32"
80
+ wind_direction: "float32"
@@ -127,13 +127,13 @@ reserve_status:
127
127
  long_name: Reserve status
128
128
  units: ""
129
129
  temperature_interior:
130
- description: "Interior temperature [\xB0C] (NNN)"
130
+ description: "Interior temperature [C] (NNN)"
131
131
  long_name: Interior temperature
132
- units: "\xB0C"
132
+ units: "C"
133
133
  laser_temperature:
134
- description: "Temperature of laser driver 0-80\xB0C (NN)"
134
+ description: "Temperature of laser driver 0-80C (NN)"
135
135
  long_name: Temperature of laser driver
136
- units: "\xB0C"
136
+ units: "C"
137
137
  laser_current_average:
138
138
  description: Mean value laser current [1/100 mA] (NNNN)
139
139
  long_name: Mean value laser current
@@ -159,9 +159,9 @@ current_heating_pane_receiver_head:
159
159
  long_name: Current pane heating receiver head
160
160
  units: mA
161
161
  temperature_ambient:
162
- description: "Ambient temperature [\xB0C] (NNN.N)"
162
+ description: "Ambient temperature [C] (NNN.N)"
163
163
  long_name: Ambient temperature
164
- units: "\xB0C"
164
+ units: "C"
165
165
  current_heating_voltage_supply:
166
166
  description:
167
167
  Voltage Heating supply [1/10 V] (only 5.4110.x1.xxx, otherwise "999")
@@ -308,3 +308,19 @@ raw_drop_number:
308
308
  description: Precipitation spectrum
309
309
  long_name: Precipitation spectrum
310
310
  units: ""
311
+ air_temperature:
312
+ description: "Air temperature in degrees Celsius (C)"
313
+ long_name: Air temperature
314
+ units: "C"
315
+ relative_humidity:
316
+ description: "Relative humidity in percent (%)"
317
+ long_name: Relative humidity
318
+ units: "%"
319
+ wind_speed:
320
+ description: "Wind speed in m/s"
321
+ long_name: Wind speed
322
+ units: "m/s"
323
+ wind_direction:
324
+ description: "Wind direction in degrees (0-360)"
325
+ long_name: Wind direction
326
+ units: "degrees"
@@ -693,3 +693,44 @@ raw_drop_number:
693
693
  - 5000
694
694
  - 22 # diameter
695
695
  - 20 # velocity
696
+ air_temperature:
697
+ dtype: uint16
698
+ scale_factor: 0.1
699
+ add_offset: -99.9
700
+ zlib: true
701
+ complevel: 3
702
+ shuffle: true
703
+ fletcher32: false
704
+ contiguous: false
705
+ _FillValue: 65535
706
+ chunksizes: 5000
707
+ relative_humidity:
708
+ dtype: uint16
709
+ scale_factor: 0.01
710
+ zlib: true
711
+ complevel: 3
712
+ shuffle: true
713
+ fletcher32: false
714
+ contiguous: false
715
+ _FillValue: 65535
716
+ chunksizes: 5000
717
+ wind_speed:
718
+ dtype: uint16
719
+ scale_factor: 0.1
720
+ add_offset: -99.9
721
+ zlib: true
722
+ complevel: 3
723
+ shuffle: true
724
+ fletcher32: false
725
+ contiguous: false
726
+ _FillValue: 65535
727
+ chunksizes: 5000
728
+ wind_direction:
729
+ dtype: uint16
730
+ zlib: true
731
+ complevel: 3
732
+ shuffle: true
733
+ fletcher32: false
734
+ contiguous: false
735
+ _FillValue: 65535
736
+ chunksizes: 5000
@@ -828,3 +828,43 @@ raw_drop_number:
828
828
  - velocity_bin_center
829
829
  n_values: 440
830
830
  field_number: "81"
831
+ air_temperature:
832
+ n_digits: 4
833
+ n_characters: 5
834
+ n_decimals: 1
835
+ n_naturals: 2
836
+ data_range:
837
+ - -40
838
+ - 70
839
+ nan_flags: 99999
840
+ field_number: "521"
841
+ relative_humidity:
842
+ n_digits: 5
843
+ n_characters: 5
844
+ n_decimals: 0
845
+ n_naturals: 5
846
+ data_range:
847
+ - 0
848
+ - 99999
849
+ nan_flags: 99999
850
+ field_number: "522"
851
+ wind_speed:
852
+ n_digits: 3
853
+ n_characters: 4
854
+ n_decimals: 1
855
+ n_naturals: 2
856
+ data_range:
857
+ - 0
858
+ - 60
859
+ nan_flags: null
860
+ field_number: "523"
861
+ wind_direction:
862
+ n_digits: 3
863
+ n_characters: 3
864
+ n_decimals: 0
865
+ n_naturals: 3
866
+ data_range:
867
+ - 0
868
+ - 360
869
+ nan_flags: 999
870
+ field_number: "524"
@@ -47,7 +47,7 @@ number_particles:
47
47
  sensor_temperature:
48
48
  description: Temperature in sensor housing
49
49
  long_name: Temperature of the sensor
50
- units: "\xB0C"
50
+ units: "C"
51
51
  sensor_serial_number:
52
52
  description: Sensor serial number
53
53
  long_name: Serial number of the sensor
@@ -71,7 +71,7 @@ mor_visibility:
71
71
  n_naturals: 4
72
72
  data_range:
73
73
  - 0
74
- - 20000
74
+ - 9999
75
75
  nan_flags: null
76
76
  field_number: 08
77
77
  sample_interval:
@@ -37,3 +37,7 @@ list_particles: "str"
37
37
  raw_drop_concentration: "str"
38
38
  raw_drop_average_velocity: "str"
39
39
  raw_drop_number: "str"
40
+ air_temperature: "float32"
41
+ relative_humidity: "float32"
42
+ wind_speed: "float32"
43
+ wind_direction: "float32"
@@ -47,7 +47,7 @@ number_particles:
47
47
  sensor_temperature:
48
48
  description: Temperature in sensor housing
49
49
  long_name: Temperature of the sensor
50
- units: "\xB0C"
50
+ units: "C"
51
51
  sensor_serial_number:
52
52
  description: Sensor serial number
53
53
  long_name: Serial number of the sensor
@@ -105,15 +105,15 @@ error_code:
105
105
  sensor_temperature_pcb:
106
106
  description: Temperature in printed circuit board
107
107
  long_name: Sensor PCB temperature
108
- units: "\xB0C"
108
+ units: "C"
109
109
  sensor_temperature_receiver:
110
110
  description: Temperature in right sensor head
111
111
  long_name: Sensor receiver temperature
112
- units: "\xB0C"
112
+ units: "C"
113
113
  sensor_temperature_trasmitter:
114
114
  description: Temperature in left sensor head
115
115
  long_name: Sensor trasmitter temperature
116
- units: "\xB0C"
116
+ units: "C"
117
117
  rainfall_rate_16_bit_30:
118
118
  description: Rainfall rate
119
119
  long_name: Rainfall rate max 30 mm/h 16 bit
@@ -158,3 +158,19 @@ raw_drop_number:
158
158
  description: Drop counts per diameter and velocity class
159
159
  long_name: Raw drop number
160
160
  units: ""
161
+ air_temperature:
162
+ description: "Air temperature in degrees Celsius (C)"
163
+ long_name: Air temperature
164
+ units: "C"
165
+ relative_humidity:
166
+ description: "Relative humidity in percent (%)"
167
+ long_name: Relative humidity
168
+ units: "%"
169
+ wind_speed:
170
+ description: "Wind speed in m/s"
171
+ long_name: Wind speed
172
+ units: "m/s"
173
+ wind_direction:
174
+ description: "Wind direction in degrees (0-360)"
175
+ long_name: Wind direction
176
+ units: "degrees"
@@ -331,3 +331,44 @@ raw_drop_number:
331
331
  - 5000
332
332
  - 32
333
333
  - 32
334
+ air_temperature:
335
+ dtype: uint16
336
+ scale_factor: 0.1
337
+ add_offset: -99.9
338
+ zlib: true
339
+ complevel: 3
340
+ shuffle: true
341
+ fletcher32: false
342
+ contiguous: false
343
+ _FillValue: 65535
344
+ chunksizes: 5000
345
+ relative_humidity:
346
+ dtype: uint16
347
+ scale_factor: 0.01
348
+ zlib: true
349
+ complevel: 3
350
+ shuffle: true
351
+ fletcher32: false
352
+ contiguous: false
353
+ _FillValue: 65535
354
+ chunksizes: 5000
355
+ wind_speed:
356
+ dtype: uint16
357
+ scale_factor: 0.1
358
+ add_offset: -99.9
359
+ zlib: true
360
+ complevel: 3
361
+ shuffle: true
362
+ fletcher32: false
363
+ contiguous: false
364
+ _FillValue: 65535
365
+ chunksizes: 5000
366
+ wind_direction:
367
+ dtype: uint16
368
+ zlib: true
369
+ complevel: 3
370
+ shuffle: true
371
+ fletcher32: false
372
+ contiguous: false
373
+ _FillValue: 65535
374
+ chunksizes: 5000
@@ -65,10 +65,10 @@ reflectivity_32bit:
65
65
  nan_flags: null
66
66
  field_number: "07"
67
67
  mor_visibility:
68
- n_digits: 4
69
- n_characters: 4
68
+ n_digits: 5
69
+ n_characters: 5
70
70
  n_decimals: 0
71
- n_naturals: 4
71
+ n_naturals: 5
72
72
  data_range:
73
73
  - 0
74
74
  - 20000
@@ -270,8 +270,8 @@ sensor_temperature_trasmitter:
270
270
  rainfall_rate_16_bit_30:
271
271
  n_digits: 5
272
272
  n_characters: 6
273
- n_decimals: 2
274
- n_naturals: 3
273
+ n_decimals: 3
274
+ n_naturals: 2
275
275
  data_range:
276
276
  - 0
277
277
  - 30
@@ -280,8 +280,8 @@ rainfall_rate_16_bit_30:
280
280
  rainfall_rate_16_bit_1200:
281
281
  n_digits: 5
282
282
  n_characters: 6
283
- n_decimals: 4
284
- n_naturals: 1
283
+ n_decimals: 1
284
+ n_naturals: 4
285
285
  data_range:
286
286
  - 0
287
287
  - 1200
@@ -310,11 +310,11 @@ reflectivity_16bit:
310
310
  rain_kinetic_energy:
311
311
  n_digits: 6
312
312
  n_characters: 7
313
- n_decimals: 3
314
- n_naturals: 3
313
+ n_decimals: 2
314
+ n_naturals: 4
315
315
  data_range:
316
316
  - 0
317
- - 999.999
317
+ - 9999.99
318
318
  nan_flags: null
319
319
  field_number: "34"
320
320
  snowfall_rate:
@@ -379,3 +379,43 @@ raw_drop_number:
379
379
  - diameter_bin_center
380
380
  n_values: 1024
381
381
  field_number: "93"
382
+ air_temperature:
383
+ n_digits: 4
384
+ n_characters: 5
385
+ n_decimals: 1
386
+ n_naturals: 2
387
+ data_range:
388
+ - -40
389
+ - 70
390
+ nan_flags: 99999
391
+ field_number: "521"
392
+ relative_humidity:
393
+ n_digits: 5
394
+ n_characters: 5
395
+ n_decimals: 0
396
+ n_naturals: 5
397
+ data_range:
398
+ - 0
399
+ - 99999
400
+ nan_flags: 99999
401
+ field_number: "522"
402
+ wind_speed:
403
+ n_digits: 3
404
+ n_characters: 4
405
+ n_decimals: 1
406
+ n_naturals: 2
407
+ data_range:
408
+ - 0
409
+ - 60
410
+ nan_flags: null
411
+ field_number: "523"
412
+ wind_direction:
413
+ n_digits: 3
414
+ n_characters: 3
415
+ n_decimals: 0
416
+ n_naturals: 3
417
+ data_range:
418
+ - 0
419
+ - 360
420
+ nan_flags: 999
421
+ field_number: "524"