diff --git a/wxdata/include/scwx/wsr88d/rpg/cell_trend_data_packet.hpp b/wxdata/include/scwx/wsr88d/rpg/cell_trend_data_packet.hpp new file mode 100644 index 00000000..071cb045 --- /dev/null +++ b/wxdata/include/scwx/wsr88d/rpg/cell_trend_data_packet.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include + +#include +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rpg +{ + +class CellTrendDataPacketImpl; + +class CellTrendDataPacket : public Packet +{ +public: + explicit CellTrendDataPacket(); + ~CellTrendDataPacket(); + + CellTrendDataPacket(const CellTrendDataPacket&) = delete; + CellTrendDataPacket& operator=(const CellTrendDataPacket&) = delete; + + CellTrendDataPacket(CellTrendDataPacket&&) noexcept; + CellTrendDataPacket& operator=(CellTrendDataPacket&&) noexcept; + + uint16_t packet_code() const; + uint16_t length_of_block() const; + std::string cell_id() const; + int16_t i_position() const; + int16_t j_position() const; + uint16_t number_of_trends() const; + uint16_t trend_code(uint16_t t) const; + uint8_t number_of_volumes(uint16_t t) const; + uint8_t latest_volume_pointer(uint16_t t) const; + uint16_t trend_data(uint16_t t, uint8_t v) const; + + size_t data_size() const override; + + bool Parse(std::istream& is) override; + + static std::shared_ptr Create(std::istream& is); + +private: + std::unique_ptr p; +}; + +} // namespace rpg +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/source/scwx/wsr88d/rpg/cell_trend_data_packet.cpp b/wxdata/source/scwx/wsr88d/rpg/cell_trend_data_packet.cpp new file mode 100644 index 00000000..e3c5ad19 --- /dev/null +++ b/wxdata/source/scwx/wsr88d/rpg/cell_trend_data_packet.cpp @@ -0,0 +1,221 @@ +#include + +#include +#include + +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rpg +{ + +static const std::string logPrefix_ = + "[scwx::wsr88d::rpg::cell_trend_data_packet] "; + +struct CellTrendData +{ + uint16_t trendCode_; + uint8_t numberOfVolumes_; + uint8_t latestVolumePointer_; + std::vector trendData_; + + CellTrendData() : + trendCode_ {0}, + numberOfVolumes_ {0}, + latestVolumePointer_ {0}, + trendData_ {} + { + } +}; + +class CellTrendDataPacketImpl +{ +public: + explicit CellTrendDataPacketImpl() : + packetCode_ {0}, + lengthOfBlock_ {0}, + cellId_ {0}, + iPosition_ {0}, + jPosition_ {0}, + trendData_ {} + { + } + ~CellTrendDataPacketImpl() = default; + + uint16_t packetCode_; + uint16_t lengthOfBlock_; + std::string cellId_; + int16_t iPosition_; + int16_t jPosition_; + + std::vector trendData_; +}; + +CellTrendDataPacket::CellTrendDataPacket() : + p(std::make_unique()) +{ +} +CellTrendDataPacket::~CellTrendDataPacket() = default; + +CellTrendDataPacket::CellTrendDataPacket(CellTrendDataPacket&&) noexcept = + default; +CellTrendDataPacket& +CellTrendDataPacket::operator=(CellTrendDataPacket&&) noexcept = default; + +uint16_t CellTrendDataPacket::packet_code() const +{ + return p->packetCode_; +} + +uint16_t CellTrendDataPacket::length_of_block() const +{ + return p->lengthOfBlock_; +} + +std::string CellTrendDataPacket::cell_id() const +{ + return p->cellId_; +} + +int16_t CellTrendDataPacket::i_position() const +{ + return p->iPosition_; +} + +int16_t CellTrendDataPacket::j_position() const +{ + return p->iPosition_; +} + +uint16_t CellTrendDataPacket::number_of_trends() const +{ + return static_cast(p->trendData_.size()); +} + +uint16_t CellTrendDataPacket::trend_code(uint16_t t) const +{ + return p->trendData_[t].trendCode_; +} + +uint8_t CellTrendDataPacket::number_of_volumes(uint16_t t) const +{ + return p->trendData_[t].numberOfVolumes_; +} + +uint8_t CellTrendDataPacket::latest_volume_pointer(uint16_t t) const +{ + return p->trendData_[t].latestVolumePointer_; +} + +uint16_t CellTrendDataPacket::trend_data(uint16_t t, uint8_t v) const +{ + return p->trendData_[t].trendData_[v]; +} + +size_t CellTrendDataPacket::data_size() const +{ + return p->lengthOfBlock_ + 4u; +} + +bool CellTrendDataPacket::Parse(std::istream& is) +{ + bool blockValid = true; + + std::streampos isBegin = is.tellg(); + + is.read(reinterpret_cast(&p->packetCode_), 2); + is.read(reinterpret_cast(&p->lengthOfBlock_), 2); + + p->packetCode_ = ntohs(p->packetCode_); + p->lengthOfBlock_ = ntohs(p->lengthOfBlock_); + + if (is.eof()) + { + BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Reached end of file"; + blockValid = false; + } + else + { + if (p->packetCode_ != 21) + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Invalid packet code: " << p->packetCode_; + blockValid = false; + } + else if (p->lengthOfBlock_ < 12 || p->lengthOfBlock_ > 198) + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Invalid length of block: " << p->packetCode_; + blockValid = false; + } + } + + p->cellId_.resize(2); + + is.read(p->cellId_.data(), 2); + is.read(reinterpret_cast(&p->iPosition_), 2); + is.read(reinterpret_cast(&p->jPosition_), 2); + + p->iPosition_ = ntohs(p->iPosition_); + p->jPosition_ = ntohs(p->jPosition_); + + if (blockValid) + { + size_t trendSize = p->lengthOfBlock_ - 6; + size_t bytesRead = 0; + + while (bytesRead < trendSize) + { + CellTrendData trendData; + + is.read(reinterpret_cast(&trendData.trendCode_), 2); + is.read(reinterpret_cast(&trendData.numberOfVolumes_), 1); + is.read(reinterpret_cast(&trendData.latestVolumePointer_), 1); + + trendData.trendCode_ = ntohs(trendData.trendCode_); + + size_t trendDataSize = + static_cast(trendData.numberOfVolumes_) * 2u; + trendData.trendData_.resize(trendData.numberOfVolumes_); + + is.read(reinterpret_cast(trendData.trendData_.data()), + trendDataSize); + + SwapVector(trendData.trendData_); + + p->trendData_.push_back(std::move(trendData)); + + bytesRead += 4 + trendDataSize; + } + } + + std::streampos isEnd = is.tellg(); + + if (!ValidateMessage(is, isEnd - isBegin)) + { + blockValid = false; + } + + return blockValid; +} + +std::shared_ptr +CellTrendDataPacket::Create(std::istream& is) +{ + std::shared_ptr packet = + std::make_shared(); + + if (!packet->Parse(is)) + { + packet.reset(); + } + + return packet; +} + +} // namespace rpg +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/source/scwx/wsr88d/rpg/packet_factory.cpp b/wxdata/source/scwx/wsr88d/rpg/packet_factory.cpp index 074888c6..cdaa0e17 100644 --- a/wxdata/source/scwx/wsr88d/rpg/packet_factory.cpp +++ b/wxdata/source/scwx/wsr88d/rpg/packet_factory.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -59,6 +60,7 @@ static const std::unordered_map create_ { {18, PrecipitationRateDataArrayPacket::Create}, {19, HdaHailSymbolPacket::Create}, {20, PointFeatureSymbolPacket::Create}, + {21, CellTrendDataPacket::Create}, {23, ScitForecastDataPacket::Create}, {24, ScitForecastDataPacket::Create}, {25, StiCircleSymbolPacket::Create}, diff --git a/wxdata/wxdata.cmake b/wxdata/wxdata.cmake index aa1d2521..95263135 100644 --- a/wxdata/wxdata.cmake +++ b/wxdata/wxdata.cmake @@ -47,6 +47,7 @@ set(SRC_WSR88D_RDA source/scwx/wsr88d/rda/clutter_filter_map.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 + include/scwx/wsr88d/rpg/cell_trend_data_packet.hpp include/scwx/wsr88d/rpg/digital_precipitation_data_array_packet.hpp include/scwx/wsr88d/rpg/digital_radial_data_array_packet.hpp include/scwx/wsr88d/rpg/generic_data_packet.hpp @@ -76,6 +77,7 @@ set(HDR_WSR88D_RPG include/scwx/wsr88d/rpg/ccb_header.hpp include/scwx/wsr88d/rpg/wind_barb_data_packet.hpp include/scwx/wsr88d/rpg/wmo_header.hpp) set(SRC_WSR88D_RPG source/scwx/wsr88d/rpg/ccb_header.cpp + source/scwx/wsr88d/rpg/cell_trend_data_packet.cpp source/scwx/wsr88d/rpg/digital_precipitation_data_array_packet.cpp source/scwx/wsr88d/rpg/digital_radial_data_array_packet.cpp source/scwx/wsr88d/rpg/generic_data_packet.cpp