From f709380a9714cb00b53a12330ec48a80dfeef621 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Tue, 13 May 2025 00:28:31 -0500 Subject: [PATCH] Add RdaPrfData (message type 32) --- wxdata/include/scwx/awips/message.hpp | 35 ++++- .../include/scwx/wsr88d/rda/rda_prf_data.hpp | 30 +++++ .../wsr88d/rda/level2_message_factory.cpp | 4 +- .../source/scwx/wsr88d/rda/rda_prf_data.cpp | 120 ++++++++++++++++++ wxdata/wxdata.cmake | 2 + 5 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 wxdata/include/scwx/wsr88d/rda/rda_prf_data.hpp create mode 100644 wxdata/source/scwx/wsr88d/rda/rda_prf_data.cpp diff --git a/wxdata/include/scwx/awips/message.hpp b/wxdata/include/scwx/awips/message.hpp index f13a2a90..486e7f06 100644 --- a/wxdata/include/scwx/awips/message.hpp +++ b/wxdata/include/scwx/awips/message.hpp @@ -121,13 +121,44 @@ public: [](auto& p) { p.second = SwapFloat(p.second); }); } - static void SwapVector(std::vector& v) + template + static void SwapVector(std::vector& v) { std::transform(std::execution::par_unseq, v.begin(), v.end(), v.begin(), - [](std::uint16_t u) { return ntohs(u); }); + [](T u) + { + if constexpr (std::is_same_v || + std::is_same_v) + { + return static_cast(ntohs(u)); + } + else if constexpr (std::is_same_v || + std::is_same_v) + { + return static_cast(ntohl(u)); + } + else if constexpr (std::is_same_v || + std::is_same_v) + { + return static_cast(ntohll(u)); + } + else if constexpr (std::is_same_v) + { + return SwapFloat(u); + } + else if constexpr (std::is_same_v) + { + return SwapDouble(u); + } + else + { + static_assert(std::is_same_v, + "Unsupported type for SwapVector"); + } + }); } private: diff --git a/wxdata/include/scwx/wsr88d/rda/rda_prf_data.hpp b/wxdata/include/scwx/wsr88d/rda/rda_prf_data.hpp new file mode 100644 index 00000000..1a04aacb --- /dev/null +++ b/wxdata/include/scwx/wsr88d/rda/rda_prf_data.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace scwx::wsr88d::rda +{ + +class RdaPrfData : public Level2Message +{ +public: + explicit RdaPrfData(); + ~RdaPrfData(); + + RdaPrfData(const RdaPrfData&) = delete; + RdaPrfData& operator=(const RdaPrfData&) = delete; + + RdaPrfData(RdaPrfData&&) noexcept; + RdaPrfData& operator=(RdaPrfData&&) noexcept; + + bool Parse(std::istream& is) override; + + static std::shared_ptr Create(Level2MessageHeader&& header, + std::istream& is); + +private: + class Impl; + std::unique_ptr p; +}; + +} // namespace scwx::wsr88d::rda diff --git a/wxdata/source/scwx/wsr88d/rda/level2_message_factory.cpp b/wxdata/source/scwx/wsr88d/rda/level2_message_factory.cpp index 2a478a2f..a659b734 100644 --- a/wxdata/source/scwx/wsr88d/rda/level2_message_factory.cpp +++ b/wxdata/source/scwx/wsr88d/rda/level2_message_factory.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -37,7 +38,8 @@ static const std::unordered_map {13, ClutterFilterBypassMap::Create}, {15, ClutterFilterMap::Create}, {18, RdaAdaptationData::Create}, - {31, DigitalRadarDataGeneric::Create}}; + {31, DigitalRadarDataGeneric::Create}, + {32, RdaPrfData::Create}}; struct Level2MessageFactory::Context { diff --git a/wxdata/source/scwx/wsr88d/rda/rda_prf_data.cpp b/wxdata/source/scwx/wsr88d/rda/rda_prf_data.cpp new file mode 100644 index 00000000..d516309b --- /dev/null +++ b/wxdata/source/scwx/wsr88d/rda/rda_prf_data.cpp @@ -0,0 +1,120 @@ +#include +#include + +namespace scwx::wsr88d::rda +{ + +static const std::string logPrefix_ = "scwx::wsr88d::rda::rda_prf_data"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); + +struct RdaPrfWaveformData +{ + std::uint16_t waveformType_ {0}; + std::uint16_t prfCount_ {0}; + std::vector prfValues_ {}; +}; + +class RdaPrfData::Impl +{ +public: + explicit Impl() = default; + ~Impl() = default; + + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + Impl(const Impl&&) = delete; + Impl& operator=(const Impl&&) = delete; + + std::uint16_t numberOfWaveforms_ {0}; + std::vector waveformData_ {}; +}; + +RdaPrfData::RdaPrfData() : p(std::make_unique()) {} +RdaPrfData::~RdaPrfData() = default; + +RdaPrfData::RdaPrfData(RdaPrfData&&) noexcept = default; +RdaPrfData& RdaPrfData::operator=(RdaPrfData&&) noexcept = default; + +bool RdaPrfData::Parse(std::istream& is) +{ + logger_->trace("Parsing RDA PRF Data (Message Type 32)"); + + bool messageValid = true; + std::size_t bytesRead = 0; + + std::streampos isBegin = is.tellg(); + + is.read(reinterpret_cast(&p->numberOfWaveforms_), 2); // 1 + is.seekg(2, std::ios_base::cur); // 2 + + bytesRead += 4; + + p->numberOfWaveforms_ = ntohs(p->numberOfWaveforms_); + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers): Readability + if (p->numberOfWaveforms_ < 1 || p->numberOfWaveforms_ > 5) + { + logger_->warn("Invalid number of waveforms: {}", p->numberOfWaveforms_); + p->numberOfWaveforms_ = 0; + messageValid = false; + } + + p->waveformData_.resize(p->numberOfWaveforms_); + + for (std::uint16_t i = 0; i < p->numberOfWaveforms_; ++i) + { + auto& w = p->waveformData_[i]; + + is.read(reinterpret_cast(&w.waveformType_), 2); // P1 + is.read(reinterpret_cast(&w.prfCount_), 2); // P2 + + w.waveformType_ = ntohs(w.waveformType_); + w.prfCount_ = ntohs(w.prfCount_); + + bytesRead += 4; + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers): Readability + if (w.prfCount_ > 255) + { + logger_->warn("Invalid PRF count: {} (waveform {})", w.prfCount_, i); + w.prfCount_ = 0; + messageValid = false; + break; + } + + w.prfValues_.resize(w.prfCount_); + + for (std::uint16_t j = 0; j < w.prfCount_; ++j) + { + is.read(reinterpret_cast(&w.prfValues_[j]), 4); + } + + bytesRead += static_cast(w.prfCount_) * 4; + + SwapVector(w.prfValues_); + } + + is.seekg(isBegin, std::ios_base::beg); + if (!ValidateMessage(is, bytesRead)) + { + messageValid = false; + } + + return messageValid; +} + +std::shared_ptr RdaPrfData::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 scwx::wsr88d::rda diff --git a/wxdata/wxdata.cmake b/wxdata/wxdata.cmake index 2c062f4b..6a02a265 100644 --- a/wxdata/wxdata.cmake +++ b/wxdata/wxdata.cmake @@ -125,6 +125,7 @@ set(HDR_WSR88D_RDA include/scwx/wsr88d/rda/clutter_filter_bypass_map.hpp include/scwx/wsr88d/rda/level2_message_header.hpp include/scwx/wsr88d/rda/performance_maintenance_data.hpp include/scwx/wsr88d/rda/rda_adaptation_data.hpp + include/scwx/wsr88d/rda/rda_prf_data.hpp include/scwx/wsr88d/rda/rda_status_data.hpp include/scwx/wsr88d/rda/rda_types.hpp include/scwx/wsr88d/rda/volume_coverage_pattern_data.hpp) @@ -138,6 +139,7 @@ set(SRC_WSR88D_RDA source/scwx/wsr88d/rda/clutter_filter_bypass_map.cpp source/scwx/wsr88d/rda/level2_message_header.cpp source/scwx/wsr88d/rda/performance_maintenance_data.cpp source/scwx/wsr88d/rda/rda_adaptation_data.cpp + source/scwx/wsr88d/rda/rda_prf_data.cpp source/scwx/wsr88d/rda/rda_status_data.cpp source/scwx/wsr88d/rda/volume_coverage_pattern_data.cpp) set(HDR_WSR88D_RPG include/scwx/wsr88d/rpg/ccb_header.hpp