"""Module for reading raw Galileo cloud radar data."""
import os
from tempfile import NamedTemporaryFile, TemporaryDirectory
import numpy as np
from cloudnetpy import concat_lib, output, utils
from cloudnetpy.instruments.instruments import GALILEO
from cloudnetpy.instruments.nc_radar import ChilboltonRadar
from cloudnetpy.metadata import MetaData
[docs]
def galileo2nc(
raw_files: str,
output_file: str,
site_meta: dict,
uuid: str | None = None,
date: str | None = None,
) -> str:
"""Converts 'Galileo' cloud radar data into Cloudnet Level 1b netCDF file.
Args:
raw_files: Input file name or folder containing multiple input files.
output_file: Output filename.
site_meta: Dictionary containing information about the site. Required key
value pair is `name`. Optional are `latitude`, `longitude`, `altitude` and
`snr_limit` (default = 3).
uuid: Set specific UUID for the file.
date: Expected date as YYYY-MM-DD of all profiles in the file.
Returns:
UUID of the generated file.
Raises:
ValidTimeStampError: No valid timestamps found.
Examples:
>>> from cloudnetpy.instruments import galileo2nc
>>> site_meta = {'name': 'Chilbolton'}
>>> galileo2nc('raw_radar.nc', 'radar.nc', site_meta)
>>> galileo2nc('/one/day/of/galileo/files/', 'radar.nc', site_meta)
"""
keymap = {
"ZED_HC": "Zh",
"VEL_HC": "v",
"SPW_HC": "width",
"LDR_HC": "ldr",
"SNR_HC": "SNR",
"elevation": "elevation",
"azimuth": "azimuth_angle",
"height": "altitude",
"antenna_diameter": "antenna_diameter",
"beamwidthV": "beamwidthV",
"beamwidthH": "beamwidthH",
}
with TemporaryDirectory() as temp_dir:
if os.path.isdir(raw_files):
with NamedTemporaryFile(
dir=temp_dir,
suffix=".nc",
delete=False,
) as temp_file:
nc_filename = temp_file.name
valid_filenames = utils.get_sorted_filenames(raw_files, ".nc")
valid_filenames = utils.get_files_with_variables(
valid_filenames, ["time", "ZED_HC"]
)
valid_filenames = utils.get_files_with_common_range(valid_filenames)
variables = list(keymap.keys())
concat_lib.concatenate_files(
valid_filenames,
nc_filename,
variables=variables,
)
else:
nc_filename = raw_files
with Galileo(nc_filename, site_meta) as galileo:
galileo.init_data(keymap)
galileo.add_time_and_range()
if date is not None:
galileo.check_date(date)
galileo.sort_timestamps()
galileo.remove_duplicate_timestamps()
snr_limit = site_meta.get("snr_limit", 3)
galileo.screen_by_snr(snr_limit=snr_limit)
galileo.mask_clutter()
galileo.mask_invalid_data()
galileo.add_time_and_range()
galileo.add_radar_specific_variables()
galileo.add_nyquist_velocity(keymap)
galileo.add_site_geolocation()
valid_indices = galileo.add_zenith_and_azimuth_angles()
galileo.screen_time_indices(valid_indices)
galileo.add_height()
attributes = output.add_time_attribute(ATTRIBUTES, galileo.date)
output.update_attributes(galileo.data, attributes)
return output.save_level1b(galileo, output_file, uuid)
class Galileo(ChilboltonRadar):
"""Class for Galileo raw radar data. Child of ChilboltonRadar().
Args:
full_path: Filename of a daily Galileo .nc NetCDF file.
site_meta: Site properties in a dictionary. Required keys are: `name`.
"""
def __init__(self, full_path: str, site_meta: dict):
super().__init__(full_path, site_meta)
self.date = self._init_date()
self.instrument = GALILEO
def mask_clutter(self) -> None:
"""Masks clutter."""
# Only strong Z values are valid
n_low_gates = 15
ind = np.where(self.data["Zh"][:, :n_low_gates] < -15) and np.where(
self.data["ldr"][:, :n_low_gates] > -5,
)
self.data["v"].mask_indices(ind)
ATTRIBUTES = {
"antenna_diameter": MetaData(long_name="Antenna diameter", units="m"),
"beamwidthV": MetaData(long_name="Vertical angular beamwidth", units="degree"),
"beamwidthH": MetaData(long_name="Horizontal angular beamwidth", units="degree"),
}