Level 2 data level function implementations

This commit is contained in:
Dan Paulat 2024-01-05 23:38:17 -06:00
parent 36f8f73b0f
commit e96808d14d
3 changed files with 208 additions and 9 deletions

View file

@ -468,7 +468,6 @@ void Level2ProductView::ComputeSweep()
const uint32_t gates = momentData0->number_of_data_moment_gates(); const uint32_t gates = momentData0->number_of_data_moment_gates();
auto radialData0 = radarData0->radial_data_block();
auto volumeData0 = radarData0->volume_data_block(); auto volumeData0 = radarData0->volume_data_block();
p->latitude_ = volumeData0->latitude(); p->latitude_ = volumeData0->latitude();
p->longitude_ = volumeData0->longitude(); p->longitude_ = volumeData0->longitude();
@ -782,24 +781,209 @@ void Level2ProductViewImpl::ComputeCoordinates(
std::optional<std::uint16_t> std::optional<std::uint16_t>
Level2ProductView::GetBinLevel(const common::Coordinate& coordinate) const Level2ProductView::GetBinLevel(const common::Coordinate& coordinate) const
{ {
// TODO auto radarData = p->elevationScan_;
Q_UNUSED(coordinate); auto dataBlockType = p->dataBlockType_;
return std::nullopt;
if (radarData == nullptr)
{
return std::nullopt;
}
auto radarProductManager = radar_product_manager();
auto radarSite = radarProductManager->radar_site();
const double radarLatitude = radarSite->latitude();
const double radarLongitude = radarSite->longitude();
// Determine distance and azimuth of coordinate relative to radar location
double s12; // Distance (meters)
double azi1; // Azimuth (degrees)
double azi2; // Unused
util::GeographicLib::DefaultGeodesic().Inverse(radarLatitude,
radarLongitude,
coordinate.latitude_,
coordinate.longitude_,
s12,
azi1,
azi2);
if (std::isnan(azi1))
{
// If a problem occurred with the geodesic inverse calculation
return std::nullopt;
}
// Azimuth is returned as [-180, 180) from the geodesic inverse, we need a
// range of [0, 360)
while (azi1 < 0.0)
{
azi1 += 360.0;
}
// Find Radial
const std::uint16_t numRadials =
static_cast<std::uint16_t>(radarData->size());
auto radials = boost::irange<std::uint32_t>(0u, numRadials);
auto radial = std::find_if( //
std::execution::par_unseq,
radials.begin(),
radials.end(),
[&](std::uint32_t i)
{
bool found = false;
const float startAngle = (*radarData)[i]->azimuth_angle();
const float nextAngle =
(*radarData)[(i + 1) % numRadials]->azimuth_angle();
if (startAngle < nextAngle)
{
if (startAngle <= azi1 && azi1 < nextAngle)
{
found = true;
}
}
else
{
// If the bin crosses 0/360 degrees, special handling is needed
if (startAngle <= azi1 || azi1 < nextAngle)
{
found = true;
}
}
return found;
});
if (radial == radials.end())
{
// No radial was found (not likely to happen without a gap in data)
return std::nullopt;
}
// Compute gate interval
auto momentData = (*radarData)[*radial]->moment_data_block(dataBlockType);
const std::uint16_t dataMomentRange = momentData->data_moment_range_raw();
const std::uint16_t dataMomentInterval =
momentData->data_moment_range_sample_interval_raw();
const std::uint16_t dataMomentIntervalH = dataMomentInterval / 2;
// Compute gate size (number of base 250m gates per bin)
const std::uint16_t gateSizeMeters =
static_cast<std::uint16_t>(radarProductManager->gate_size());
// Compute gate range [startGate, endGate)
const std::uint16_t startGate =
(dataMomentRange - dataMomentIntervalH) / gateSizeMeters;
const std::uint16_t numberOfDataMomentGates =
momentData->number_of_data_moment_gates();
const std::uint16_t gate = s12 / dataMomentInterval - startGate;
if (gate > numberOfDataMomentGates || gate > common::MAX_DATA_MOMENT_GATES)
{
// Coordinate is beyond radar range
return std::nullopt;
}
// Compute threshold at which to display an individual bin (minimum of 2)
const std::uint16_t snrThreshold =
std::max<std::int16_t>(2, momentData->snr_threshold_raw());
std::uint16_t level;
if (momentData->data_word_size() == 8)
{
level =
reinterpret_cast<const uint8_t*>(momentData->data_moments())[gate];
}
else
{
level =
reinterpret_cast<const uint16_t*>(momentData->data_moments())[gate];
}
if (level < snrThreshold && level != RANGE_FOLDED)
{
return std::nullopt;
}
return level;
} }
std::optional<wsr88d::DataLevelCode> std::optional<wsr88d::DataLevelCode>
Level2ProductView::GetDataLevelCode(std::uint16_t level) const Level2ProductView::GetDataLevelCode(std::uint16_t level) const
{ {
// TODO switch (p->product_)
Q_UNUSED(level); {
case common::Level2Product::Reflectivity:
case common::Level2Product::Velocity:
case common::Level2Product::SpectrumWidth:
case common::Level2Product::DifferentialReflectivity:
case common::Level2Product::DifferentialPhase:
case common::Level2Product::CorrelationCoefficient:
if (level == RANGE_FOLDED)
{
return wsr88d::DataLevelCode::RangeFolded;
}
break;
case common::Level2Product::ClutterFilterPowerRemoved:
switch (level)
{
case 0:
return wsr88d::DataLevelCode::ClutterFilterNotApplied;
case 1:
return wsr88d::DataLevelCode::ClutterFilterApplied;
case 2:
return wsr88d::DataLevelCode::DualPolVariablesFiltered;
case 3:
case 4:
case 5:
case 6:
case 7:
return wsr88d::DataLevelCode::Reserved;
default:
break;
}
break;
default:
break;
}
return std::nullopt; return std::nullopt;
} }
std::optional<float> Level2ProductView::GetDataValue(std::uint16_t level) const std::optional<float> Level2ProductView::GetDataValue(std::uint16_t level) const
{ {
// TODO const float offset = p->momentDataBlock0_->offset();
Q_UNUSED(level); const float scale = p->momentDataBlock0_->scale();
return std::nullopt; std::uint16_t threshold = std::numeric_limits<std::uint16_t>::max();
switch (p->product_)
{
case common::Level2Product::Reflectivity:
case common::Level2Product::Velocity:
case common::Level2Product::SpectrumWidth:
case common::Level2Product::DifferentialReflectivity:
case common::Level2Product::DifferentialPhase:
case common::Level2Product::CorrelationCoefficient:
threshold = 2;
break;
case common::Level2Product::ClutterFilterPowerRemoved:
threshold = 8;
break;
default:
break;
}
if (level < threshold)
{
return std::nullopt;
}
return (level - offset) / scale;
} }
std::shared_ptr<Level2ProductView> Level2ProductView::Create( std::shared_ptr<Level2ProductView> Level2ProductView::Create(

View file

@ -50,6 +50,11 @@ enum class DataLevelCode
Z8, Z8,
SI, SI,
// Clutter Filter Power Removed
ClutterFilterNotApplied,
ClutterFilterApplied,
DualPolVariablesFiltered,
Unknown Unknown
}; };

View file

@ -50,6 +50,11 @@ static const std::unordered_map<DataLevelCode, std::string> dataLevelCodeName_ {
{DataLevelCode::Z8, "R(Z) * 0.8"}, {DataLevelCode::Z8, "R(Z) * 0.8"},
{DataLevelCode::SI, "R(Z) * multiplier"}, {DataLevelCode::SI, "R(Z) * multiplier"},
// Clutter Filter Power Removed
{DataLevelCode::ClutterFilterNotApplied, "Clutter Filter Not Applied"},
{DataLevelCode::ClutterFilterApplied, "Clutter Filter Applied"},
{DataLevelCode::DualPolVariablesFiltered, "Dual Pol Variables Filtered"},
{DataLevelCode::Unknown, "?"}}; {DataLevelCode::Unknown, "?"}};
static const std::unordered_map<DataLevelCode, std::string> static const std::unordered_map<DataLevelCode, std::string>
@ -95,6 +100,11 @@ static const std::unordered_map<DataLevelCode, std::string>
{DataLevelCode::Z8, "Z8"}, {DataLevelCode::Z8, "Z8"},
{DataLevelCode::SI, "SI"}, {DataLevelCode::SI, "SI"},
// Clutter Filter Power Removed
{DataLevelCode::ClutterFilterNotApplied, ""},
{DataLevelCode::ClutterFilterApplied, ""},
{DataLevelCode::DualPolVariablesFiltered, ""},
{DataLevelCode::Unknown, "?"}}; {DataLevelCode::Unknown, "?"}};
const std::string& GetDataLevelCodeName(DataLevelCode dataLevelCode) const std::string& GetDataLevelCodeName(DataLevelCode dataLevelCode)