diff --git a/wxdata/include/scwx/wsr88d/rda/clutter_filter_bypass_map.hpp b/wxdata/include/scwx/wsr88d/rda/clutter_filter_bypass_map.hpp new file mode 100644 index 00000000..abb28401 --- /dev/null +++ b/wxdata/include/scwx/wsr88d/rda/clutter_filter_bypass_map.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rda +{ + +class ClutterFilterBypassMapImpl; + +class ClutterFilterBypassMap : public Level2Message +{ +public: + explicit ClutterFilterBypassMap(); + ~ClutterFilterBypassMap(); + + ClutterFilterBypassMap(const ClutterFilterBypassMap&) = delete; + ClutterFilterBypassMap& operator=(const ClutterFilterBypassMap&) = delete; + + ClutterFilterBypassMap(ClutterFilterBypassMap&&) noexcept; + ClutterFilterBypassMap& operator=(ClutterFilterBypassMap&&) noexcept; + + uint16_t map_generation_date() const; + uint16_t map_generation_time() const; + uint16_t number_of_elevation_segments() const; + uint16_t range_bin(uint16_t e, uint16_t r, uint16_t b) const; + + bool Parse(std::istream& is); + + static std::shared_ptr + Create(Level2MessageHeader&& header, std::istream& is); + + static constexpr size_t NUM_RADIALS = 360u; + static constexpr size_t NUM_RANGE_BINS = 512u; + static constexpr size_t NUM_CODED_RANGE_BINS = NUM_RANGE_BINS / 16u; + +private: + std::unique_ptr p; +}; + +} // namespace rda +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/source/scwx/wsr88d/rda/clutter_filter_bypass_map.cpp b/wxdata/source/scwx/wsr88d/rda/clutter_filter_bypass_map.cpp new file mode 100644 index 00000000..cfc1c5aa --- /dev/null +++ b/wxdata/source/scwx/wsr88d/rda/clutter_filter_bypass_map.cpp @@ -0,0 +1,156 @@ +#include + +#include + +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rda +{ + +static const std::string logPrefix_ = + "[scwx::wsr88d::rda::clutter_filter_bypass_map] "; + +class ClutterFilterBypassMapImpl +{ +public: + explicit ClutterFilterBypassMapImpl() : + mapGenerationDate_(), mapGenerationTime_(), rangeBins_() {}; + ~ClutterFilterBypassMapImpl() = default; + + uint16_t mapGenerationDate_; + uint16_t mapGenerationTime_; + + std::vector>> rangeBins_; +}; + +ClutterFilterBypassMap::ClutterFilterBypassMap() : + Level2Message(), p(std::make_unique()) +{ +} +ClutterFilterBypassMap::~ClutterFilterBypassMap() = default; + +ClutterFilterBypassMap::ClutterFilterBypassMap( + ClutterFilterBypassMap&&) noexcept = default; +ClutterFilterBypassMap& +ClutterFilterBypassMap::operator=(ClutterFilterBypassMap&&) noexcept = default; + +uint16_t ClutterFilterBypassMap::map_generation_date() const +{ + return p->mapGenerationDate_; +} + +uint16_t ClutterFilterBypassMap::map_generation_time() const +{ + return p->mapGenerationTime_; +} + +uint16_t ClutterFilterBypassMap::number_of_elevation_segments() const +{ + return static_cast(p->rangeBins_.size()); +} + +uint16_t +ClutterFilterBypassMap::range_bin(uint16_t e, uint16_t r, uint16_t b) const +{ + return p->rangeBins_[e][r][b]; +} + +bool ClutterFilterBypassMap::Parse(std::istream& is) +{ + BOOST_LOG_TRIVIAL(trace) + << logPrefix_ << "Parsing Clutter Filter Bypass Map (Message Type 13)"; + + bool messageValid = true; + size_t bytesRead = 0; + uint16_t numElevationSegments = 0; + + is.read(reinterpret_cast(&p->mapGenerationDate_), 2); + is.read(reinterpret_cast(&p->mapGenerationTime_), 2); + is.read(reinterpret_cast(&numElevationSegments), 2); + bytesRead += 6; + + p->mapGenerationDate_ = ntohs(p->mapGenerationDate_); + p->mapGenerationTime_ = ntohs(p->mapGenerationTime_); + numElevationSegments = ntohs(numElevationSegments); + + if (p->mapGenerationDate_ < 1) + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Invalid date: " << p->mapGenerationDate_; + messageValid = false; + } + if (p->mapGenerationTime_ > 1440) + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Invalid time: " << p->mapGenerationTime_; + messageValid = false; + } + if (numElevationSegments < 1 || numElevationSegments > 5) + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ + << "Invalid number of elevation segments: " << numElevationSegments; + messageValid = false; + } + + if (!messageValid) + { + numElevationSegments = 0; + } + + p->rangeBins_.resize(numElevationSegments); + + for (uint16_t e = 0; e < numElevationSegments && messageValid; e++) + { + p->rangeBins_[e].resize(NUM_RADIALS); + + is.seekg(2, std::ios_base::cur); // Segment number (redundant) + + for (uint16_t r = 0; r < NUM_RADIALS && messageValid; r++) + { + p->rangeBins_[e][r].resize(NUM_CODED_RANGE_BINS); + + is.read(reinterpret_cast(p->rangeBins_[e][r].data()), + NUM_RANGE_BINS / 8u); + bytesRead += NUM_RANGE_BINS / 8u; + + SwapVector(p->rangeBins_[e][r]); + } + } + + if (!ValidateMessage(is, bytesRead)) + { + messageValid = false; + } + + if (!messageValid) + { + p->rangeBins_.resize(0); + p->rangeBins_.shrink_to_fit(); + } + + return messageValid; +} + +std::shared_ptr +ClutterFilterBypassMap::Create(Level2MessageHeader&& header, std::istream& is) +{ + std::shared_ptr message = + std::make_shared(); + message->set_header(std::move(header)); + + if (!message->Parse(is)) + { + message.reset(); + } + + return message; +} + +} // namespace rda +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/source/scwx/wsr88d/rda/level2_message_factory.cpp b/wxdata/source/scwx/wsr88d/rda/level2_message_factory.cpp index b2b5e606..595593ca 100644 --- a/wxdata/source/scwx/wsr88d/rda/level2_message_factory.cpp +++ b/wxdata/source/scwx/wsr88d/rda/level2_message_factory.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,7 @@ static const std::unordered_map create_ { {2, RdaStatusData::Create}, {3, PerformanceMaintenanceData::Create}, {5, VolumeCoveragePatternData::Create}, + {13, ClutterFilterBypassMap::Create}, {15, ClutterFilterMap::Create}, {18, RdaAdaptationData::Create}, {31, DigitalRadarData::Create}}; diff --git a/wxdata/wxdata.cmake b/wxdata/wxdata.cmake index 1fe871db..905f1e9f 100644 --- a/wxdata/wxdata.cmake +++ b/wxdata/wxdata.cmake @@ -48,7 +48,8 @@ set(SRC_WSR88D source/scwx/wsr88d/ar2v_file.cpp source/scwx/wsr88d/level3_file.cpp source/scwx/wsr88d/nexrad_file.cpp source/scwx/wsr88d/nexrad_file_factory.cpp) -set(HDR_WSR88D_RDA include/scwx/wsr88d/rda/clutter_filter_map.hpp +set(HDR_WSR88D_RDA include/scwx/wsr88d/rda/clutter_filter_bypass_map.hpp + include/scwx/wsr88d/rda/clutter_filter_map.hpp include/scwx/wsr88d/rda/digital_radar_data.hpp include/scwx/wsr88d/rda/level2_message.hpp include/scwx/wsr88d/rda/level2_message_factory.hpp @@ -58,7 +59,8 @@ set(HDR_WSR88D_RDA include/scwx/wsr88d/rda/clutter_filter_map.hpp include/scwx/wsr88d/rda/rda_status_data.hpp include/scwx/wsr88d/rda/types.hpp include/scwx/wsr88d/rda/volume_coverage_pattern_data.hpp) -set(SRC_WSR88D_RDA source/scwx/wsr88d/rda/clutter_filter_map.cpp +set(SRC_WSR88D_RDA source/scwx/wsr88d/rda/clutter_filter_bypass_map.cpp + source/scwx/wsr88d/rda/clutter_filter_map.cpp source/scwx/wsr88d/rda/digital_radar_data.cpp source/scwx/wsr88d/rda/level2_message.cpp source/scwx/wsr88d/rda/level2_message_factory.cpp