Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cloudnetpy/instruments/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .basta import basta2nc
from .bowtie import bowtie2nc
from .ceilo import ceilo2nc
from .copernicus import copernicus2nc
from .disdrometer import parsivel2nc, thies2nc
Expand Down
95 changes: 95 additions & 0 deletions cloudnetpy/instruments/bowtie.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from os import PathLike

from cloudnetpy import output
from cloudnetpy.constants import G_TO_KG, MM_H_TO_M_S
from cloudnetpy.exceptions import ValidTimeStampError
from cloudnetpy.instruments.instruments import FMCW94
from cloudnetpy.instruments.nc_radar import NcRadar
from cloudnetpy.metadata import MetaData


def bowtie2nc(
bowtie_file: str | PathLike,
output_file: str,
site_meta: dict,
uuid: str | None = None,
date: str | None = None,
) -> str:
"""Converts data from 'BOW-TIE' campaign cloud radar on RV-Meteor into
Cloudnet Level 1b netCDF file.

Args:
bowtie_file: Input filename.
output_file: Output filename.
site_meta: Dictionary containing information about the site. Required key
value pair is `name`. Optional are `latitude`, `longitude`, `altitude`.
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.

"""
keymap = {
"Zh": "Zh",
"v": "v",
"width": "width",
"ldr": "ldr",
"kurt": "kurtosis",
"Skew": "skewness",
"SNR": "SNR",
"time": "time",
"range": "range",
"lwp": "lwp",
"SurfRelHum": "relative_humidity",
"rain": "rainfall_rate",
"Nyquist_velocity": "nyquist_velocity",
"range_offsets": "chirp_start_indices",
}

with Bowtie(bowtie_file, site_meta) as bowtie:
bowtie.init_data(keymap)
bowtie.add_time_and_range()
if date is not None:
bowtie.check_date(date)
bowtie.add_radar_specific_variables()
bowtie.add_site_geolocation()
bowtie.add_height()
bowtie.convert_units()
bowtie.test_if_all_masked()
attributes = output.add_time_attribute(ATTRIBUTES, bowtie.date)
output.update_attributes(bowtie.data, attributes)
return output.save_level1b(bowtie, output_file, uuid)


class Bowtie(NcRadar):
def __init__(self, full_path: str | PathLike, site_meta: dict):
super().__init__(full_path, site_meta)
self.instrument = FMCW94
self.date = self.get_date()

def convert_units(self):
self.data["lwp"].data *= G_TO_KG
self.data["rainfall_rate"].data *= MM_H_TO_M_S
self.data["relative_humidity"].data /= 100

def check_date(self, date: str):
if "-".join(self.date) != date:
raise ValidTimeStampError


ATTRIBUTES: dict = {
"v": MetaData(
long_name="Doppler velocity",
units="m s-1",
comment=(
"This parameter is the radial component of the velocity, with positive\n"
"velocities are away from the radar. It was corrected for the heave\n"
"motion of the ship. A rolling average over 3 time steps has been\n"
"applied to it."
),
),
}
8 changes: 0 additions & 8 deletions cloudnetpy/instruments/rpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,10 +445,6 @@ def _filter_zenith_angle(zenith: ma.MaskedArray) -> np.ndarray:
long_name="Number of spectral samples in each chirp sequence",
units="1",
),
"chirp_start_indices": MetaData(
long_name="Chirp sequences start indices",
units="1",
),
"number_of_averaged_chirps": MetaData(
long_name="Number of averaged chirps in sequence",
units="1",
Expand Down Expand Up @@ -517,10 +513,6 @@ def _filter_zenith_angle(zenith: ma.MaskedArray) -> np.ndarray:
long_name="PC temperature",
units="K",
),
"skewness": MetaData(
long_name="Skewness of spectra",
units="1",
),
"kurtosis": MetaData(
long_name="Kurtosis of spectra",
units="1",
Expand Down
8 changes: 8 additions & 0 deletions cloudnetpy/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ class MetaData(NamedTuple):
long_name="Kurtosis of spectra",
units="1",
),
"skewness": MetaData(
long_name="Skewness of spectra",
units="1",
),
"nyquist_velocity": MetaData(long_name="Nyquist velocity", units="m s-1"),
"radar_frequency": MetaData(long_name="Radar transmit frequency", units="GHz"),
"beta": MetaData(
Expand Down Expand Up @@ -190,4 +194,8 @@ class MetaData(NamedTuple):
units="dB",
comment="SNR threshold used in data screening.",
),
"chirp_start_indices": MetaData(
long_name="Chirp sequences start indices",
units="1",
),
}
Binary file added tests/unit/data/bowtie/bowtie-trunc.nc
Binary file not shown.
6 changes: 3 additions & 3 deletions tests/unit/radar_fun.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ def test_variable_names(self):
"v",
"radar_frequency",
"range",
"zenith_angle",
"height",
}
for key in keys:
assert key in self.nc.variables
assert key in self.nc.variables, key

def test_axis(self):
assert self.nc.variables["range"].axis == "Z"
Expand All @@ -30,7 +29,8 @@ def test_axis(self):
assert hasattr(self.nc.variables[key], "axis") is False

def test_variable_values(self):
assert 0 <= np.all(self.nc.variables["zenith_angle"][:]) < 10
if "zenith_angle" in self.nc.variables:
assert 0 <= np.all(self.nc.variables["zenith_angle"][:]) < 10
assert np.all(
(
self.nc.variables["height"][:]
Expand Down
60 changes: 60 additions & 0 deletions tests/unit/test_bowtie.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from os import path
from tempfile import TemporaryDirectory

import numpy as np
import pytest

from cloudnetpy.exceptions import ValidTimeStampError
from cloudnetpy.instruments import bowtie2nc
from tests.unit.all_products_fun import Check
from tests.unit.radar_fun import RadarFun

SCRIPT_PATH = path.dirname(path.realpath(__file__))
FILEPATH = f"{SCRIPT_PATH}/data/bowtie/bowtie-trunc.nc"


class TestBowtie2nc(Check):
site_meta = {
"altitude": 16,
"latitude": 6.1,
"longitude": -25.9,
"name": "RV Meteor",
}
temp_dir = TemporaryDirectory()
temp_path = temp_dir.name + "/bowtie.nc"
uuid = bowtie2nc(FILEPATH, temp_path, site_meta)
date = "2024-08-22"

def test_variables(self):
assert np.isclose(
self.nc.variables["radar_frequency"][:].data,
94,
)

def test_common_radar(self):
radar_fun = RadarFun(self.nc, self.site_meta, self.date, self.uuid)
for name, method in RadarFun.__dict__.items():
if "test_" in name:
getattr(radar_fun, name)()

def test_global_attributes(self):
assert self.nc.source == "RPG-Radiometer Physics RPG-FMCW-94"
assert self.nc.title == f'RPG-FMCW-94 cloud radar from {self.site_meta["name"]}'

def test_range(self):
for key in ("range", "height"):
assert np.all(self.nc.variables[key][:] > 0)

def test_correct_date_validation(self, tmp_path):
test_path = tmp_path / "date.nc"
bowtie2nc(FILEPATH, test_path, self.site_meta, date=self.date)

def test_wrong_date_validation(self, tmp_path):
test_path = tmp_path / "invalid.nc"
with pytest.raises(ValidTimeStampError):
bowtie2nc(
FILEPATH,
test_path,
self.site_meta,
date="2021-01-03",
)