diff --git a/wxdata/include/scwx/wsr88d/rda/digital_radar_data.hpp b/wxdata/include/scwx/wsr88d/rda/digital_radar_data.hpp new file mode 100644 index 00000000..42acb84b --- /dev/null +++ b/wxdata/include/scwx/wsr88d/rda/digital_radar_data.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rda +{ + +class DigitalRadarData : public Level2Message +{ +public: + explicit DigitalRadarData(); + ~DigitalRadarData(); + + DigitalRadarData(const DigitalRadarData&) = delete; + DigitalRadarData& operator=(const DigitalRadarData&) = delete; + + DigitalRadarData(DigitalRadarData&&) noexcept; + DigitalRadarData& operator=(DigitalRadarData&&) noexcept; + + std::uint32_t collection_time() const; + std::uint16_t modified_julian_date() const; + std::uint16_t unambiguous_range() const; + std::uint16_t azimuth_angle() const; + std::uint16_t azimuth_number() const; + std::uint16_t radial_status() const; + std::uint16_t elevation_angle() const; + std::uint16_t elevation_number() const; + std::uint16_t surveillance_range() const; + std::uint16_t doppler_range() const; + std::uint16_t surveillance_range_sample_interval() const; + std::uint16_t doppler_range_sample_interval() const; + std::uint16_t number_of_surveillance_bins() const; + std::uint16_t number_of_doppler_bins() const; + std::uint16_t cut_sector_number() const; + float calibration_constant() const; + std::uint16_t surveillance_pointer() const; + std::uint16_t velocity_pointer() const; + std::uint16_t spectral_width_pointer() const; + std::uint16_t doppler_velocity_resolution() const; + std::uint16_t volume_coverage_pattern_number() const; + std::uint16_t nyquist_velocity() const; + std::uint16_t atmos() const; + std::uint16_t tover() const; + std::uint16_t radial_spot_blanking_status() const; + + bool Parse(std::istream& is); + + static std::shared_ptr Create(Level2MessageHeader&& header, + std::istream& is); + +private: + class Impl; + std::unique_ptr p; +}; + +} // namespace rda +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/source/scwx/wsr88d/rda/digital_radar_data.cpp b/wxdata/source/scwx/wsr88d/rda/digital_radar_data.cpp new file mode 100644 index 00000000..f894458e --- /dev/null +++ b/wxdata/source/scwx/wsr88d/rda/digital_radar_data.cpp @@ -0,0 +1,355 @@ +#include +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rda +{ + +static const std::string logPrefix_ = "scwx::wsr88d::rda::digital_radar_data"; +static const auto logger_ = util::Logger::Create(logPrefix_); + +class DigitalRadarData::Impl +{ +public: + explicit Impl() {}; + ~Impl() = default; + + std::uint32_t collectionTime_ {}; + std::uint16_t modifiedJulianDate_ {}; + std::uint16_t unambiguousRange_ {}; + std::uint16_t azimuthAngle_ {}; + std::uint16_t azimuthNumber_ {}; + std::uint16_t radialStatus_ {}; + std::uint16_t elevationAngle_ {}; + std::uint16_t elevationNumber_ {}; + std::uint16_t surveillanceRange_ {}; + std::uint16_t dopplerRange_ {}; + std::uint16_t surveillanceRangeSampleInterval_ {}; + std::uint16_t dopplerRangeSampleInterval_ {}; + std::uint16_t numberOfSurveillanceBins_ {}; + std::uint16_t numberOfDopplerBins_ {}; + std::uint16_t cutSectorNumber_ {}; + float calibrationConstant_ {}; + std::uint16_t surveillancePointer_ {}; + std::uint16_t velocityPointer_ {}; + std::uint16_t spectralWidthPointer_ {}; + std::uint16_t dopplerVelocityResolution_ {}; + std::uint16_t vcpNumber_ {}; + std::uint16_t nyquistVelocity_ {}; + std::uint16_t atmos_ {}; + std::uint16_t tover_ {}; + std::uint16_t radialSpotBlankingStatus_ {}; + + std::vector reflectivity_ {}; + std::vector dopplerVelocity_ {}; + std::vector dopplerSpectrumWidth_ {}; +}; + +DigitalRadarData::DigitalRadarData() : + Level2Message(), p(std::make_unique()) +{ +} +DigitalRadarData::~DigitalRadarData() = default; + +DigitalRadarData::DigitalRadarData(DigitalRadarData&&) noexcept = default; +DigitalRadarData& +DigitalRadarData::operator=(DigitalRadarData&&) noexcept = default; + +std::uint32_t DigitalRadarData::collection_time() const +{ + return p->collectionTime_; +} + +std::uint16_t DigitalRadarData::modified_julian_date() const +{ + return p->modifiedJulianDate_; +} + +std::uint16_t DigitalRadarData::unambiguous_range() const +{ + return p->unambiguousRange_; +} + +std::uint16_t DigitalRadarData::azimuth_angle() const +{ + return p->azimuthAngle_; +} + +std::uint16_t DigitalRadarData::azimuth_number() const +{ + return p->azimuthNumber_; +} + +std::uint16_t DigitalRadarData::radial_status() const +{ + return p->radialStatus_; +} + +std::uint16_t DigitalRadarData::elevation_angle() const +{ + return p->elevationAngle_; +} + +std::uint16_t DigitalRadarData::elevation_number() const +{ + return p->elevationNumber_; +} + +std::uint16_t DigitalRadarData::surveillance_range() const +{ + return p->surveillanceRange_; +} + +std::uint16_t DigitalRadarData::doppler_range() const +{ + return p->dopplerRange_; +} + +std::uint16_t DigitalRadarData::surveillance_range_sample_interval() const +{ + return p->surveillanceRangeSampleInterval_; +} + +std::uint16_t DigitalRadarData::doppler_range_sample_interval() const +{ + return p->dopplerRangeSampleInterval_; +} + +std::uint16_t DigitalRadarData::number_of_surveillance_bins() const +{ + return p->numberOfSurveillanceBins_; +} + +std::uint16_t DigitalRadarData::number_of_doppler_bins() const +{ + return p->numberOfDopplerBins_; +} + +std::uint16_t DigitalRadarData::cut_sector_number() const +{ + return p->cutSectorNumber_; +} + +float DigitalRadarData::calibration_constant() const +{ + return p->calibrationConstant_; +} + +std::uint16_t DigitalRadarData::surveillance_pointer() const +{ + return p->surveillancePointer_; +} + +std::uint16_t DigitalRadarData::velocity_pointer() const +{ + return p->velocityPointer_; +} + +std::uint16_t DigitalRadarData::spectral_width_pointer() const +{ + return p->spectralWidthPointer_; +} + +std::uint16_t DigitalRadarData::doppler_velocity_resolution() const +{ + return p->dopplerVelocityResolution_; +} + +std::uint16_t DigitalRadarData::volume_coverage_pattern_number() const +{ + return p->vcpNumber_; +} + +std::uint16_t DigitalRadarData::nyquist_velocity() const +{ + return p->nyquistVelocity_; +} + +std::uint16_t DigitalRadarData::atmos() const +{ + return p->atmos_; +} + +std::uint16_t DigitalRadarData::tover() const +{ + return p->tover_; +} + +std::uint16_t DigitalRadarData::radial_spot_blanking_status() const +{ + return p->radialSpotBlankingStatus_; +} + +bool DigitalRadarData::Parse(std::istream& is) +{ + logger_->trace("Parsing Digital Radar Data (Message Type 1)"); + + bool messageValid = true; + std::size_t bytesRead = 0; + + std::streampos isBegin = is.tellg(); + + is.read(reinterpret_cast(&p->collectionTime_), 4); // 0-3 + is.read(reinterpret_cast(&p->modifiedJulianDate_), 2); // 4-5 + is.read(reinterpret_cast(&p->unambiguousRange_), 2); // 6-7 + is.read(reinterpret_cast(&p->azimuthAngle_), 2); // 8-9 + is.read(reinterpret_cast(&p->azimuthNumber_), 2); // 10-11 + is.read(reinterpret_cast(&p->radialStatus_), 2); // 12-13 + is.read(reinterpret_cast(&p->elevationAngle_), 2); // 14-15 + is.read(reinterpret_cast(&p->elevationNumber_), 2); // 16-17 + is.read(reinterpret_cast(&p->surveillanceRange_), 2); // 18-19 + is.read(reinterpret_cast(&p->dopplerRange_), 2); // 20-21 + + is.read(reinterpret_cast(&p->surveillanceRangeSampleInterval_), + 2); // 22-23 + is.read(reinterpret_cast(&p->dopplerRangeSampleInterval_), + 2); // 24-25 + + is.read(reinterpret_cast(&p->numberOfSurveillanceBins_), 2); // 26-27 + is.read(reinterpret_cast(&p->numberOfDopplerBins_), 2); // 28-29 + is.read(reinterpret_cast(&p->cutSectorNumber_), 2); // 30-31 + is.read(reinterpret_cast(&p->calibrationConstant_), 4); // 32-35 + is.read(reinterpret_cast(&p->surveillancePointer_), 2); // 36-37 + is.read(reinterpret_cast(&p->velocityPointer_), 2); // 38-39 + is.read(reinterpret_cast(&p->spectralWidthPointer_), 2); // 40-41 + is.read(reinterpret_cast(&p->dopplerVelocityResolution_), 2); // 42-43 + is.read(reinterpret_cast(&p->vcpNumber_), 2); // 44-45 + is.seekg(14, std::ios_base::cur); // 46-59 + is.read(reinterpret_cast(&p->nyquistVelocity_), 2); // 60-61 + is.read(reinterpret_cast(&p->atmos_), 2); // 62-63 + is.read(reinterpret_cast(&p->tover_), 2); // 64-65 + is.read(reinterpret_cast(&p->radialSpotBlankingStatus_), 2); // 66-67 + is.seekg(32, std::ios_base::cur); // 68-99 + + p->collectionTime_ = ntohl(p->collectionTime_); + p->modifiedJulianDate_ = ntohs(p->modifiedJulianDate_); + p->unambiguousRange_ = ntohs(p->unambiguousRange_); + p->azimuthAngle_ = ntohs(p->azimuthAngle_); + p->azimuthNumber_ = ntohs(p->azimuthNumber_); + p->radialStatus_ = ntohs(p->radialStatus_); + p->elevationAngle_ = ntohs(p->elevationAngle_); + p->elevationNumber_ = ntohs(p->elevationNumber_); + p->surveillanceRange_ = ntohs(p->surveillanceRange_); + p->dopplerRange_ = ntohs(p->dopplerRange_); + + p->surveillanceRangeSampleInterval_ = + ntohs(p->surveillanceRangeSampleInterval_); + + p->dopplerRangeSampleInterval_ = ntohs(p->dopplerRangeSampleInterval_); + p->numberOfSurveillanceBins_ = ntohs(p->numberOfSurveillanceBins_); + p->numberOfDopplerBins_ = ntohs(p->numberOfDopplerBins_); + p->cutSectorNumber_ = ntohs(p->cutSectorNumber_); + p->calibrationConstant_ = SwapFloat(p->calibrationConstant_); + p->surveillancePointer_ = ntohs(p->surveillancePointer_); + p->velocityPointer_ = ntohs(p->velocityPointer_); + p->spectralWidthPointer_ = ntohs(p->spectralWidthPointer_); + p->dopplerVelocityResolution_ = ntohs(p->dopplerVelocityResolution_); + p->vcpNumber_ = ntohs(p->vcpNumber_); + p->nyquistVelocity_ = ntohs(p->nyquistVelocity_); + p->atmos_ = ntohs(p->atmos_); + p->tover_ = ntohs(p->tover_); + p->radialSpotBlankingStatus_ = ntohs(p->radialSpotBlankingStatus_); + + if (p->azimuthNumber_ < 1 || p->azimuthNumber_ > 400) + { + logger_->warn("Invalid azimuth number: {}", p->azimuthNumber_); + messageValid = false; + } + if (p->elevationNumber_ < 1 || p->elevationNumber_ > 25) + { + logger_->warn("Invalid elevation number: {}", p->elevationNumber_); + messageValid = false; + } + if (p->numberOfSurveillanceBins_ > 460) + { + logger_->warn("Invalid number of surveillance bins: {}", + p->numberOfSurveillanceBins_); + messageValid = false; + } + if (p->numberOfDopplerBins_ > 920) + { + logger_->warn("Invalid number of doppler bins: {}", + p->numberOfDopplerBins_); + messageValid = false; + } + if (p->surveillancePointer_ != 0 && p->surveillancePointer_ != 100) + { + logger_->warn("Invalid surveillance pointer: {}", + p->surveillancePointer_); + messageValid = false; + } + if (p->velocityPointer_ != 0 && + (p->velocityPointer_ < 100 || p->velocityPointer_ > 560)) + { + logger_->warn("Invalid velocity pointer: {}", p->velocityPointer_); + messageValid = false; + } + if (p->spectralWidthPointer_ != 0 && + (p->spectralWidthPointer_ < 100 || p->spectralWidthPointer_ > 1480 || + p->spectralWidthPointer_ > data_size())) + { + logger_->warn("Invalid spectral width pointer: {}", + p->spectralWidthPointer_); + messageValid = false; + } + + if (messageValid && p->surveillancePointer_ != 0) + { + is.seekg(isBegin + std::streamoff(p->surveillancePointer_), + std::ios_base::beg); + + p->reflectivity_.resize(p->numberOfSurveillanceBins_); + is.read(reinterpret_cast(p->reflectivity_.data()), + p->numberOfSurveillanceBins_); + } + + if (messageValid && p->velocityPointer_ != 0) + { + is.seekg(isBegin + std::streamoff(p->velocityPointer_), + std::ios_base::beg); + + p->dopplerVelocity_.resize(p->numberOfDopplerBins_); + is.read(reinterpret_cast(p->dopplerVelocity_.data()), + p->numberOfDopplerBins_); + } + + if (messageValid && p->spectralWidthPointer_ != 0) + { + is.seekg(isBegin + std::streamoff(p->spectralWidthPointer_), + std::ios_base::beg); + + p->dopplerSpectrumWidth_.resize(p->numberOfDopplerBins_); + is.read(reinterpret_cast(p->dopplerSpectrumWidth_.data()), + p->numberOfDopplerBins_); + } + + is.seekg(isBegin, std::ios_base::beg); + if (!ValidateMessage(is, bytesRead)) + { + messageValid = false; + } + + return messageValid; +} + +std::shared_ptr +DigitalRadarData::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 db76fafb..04c45676 100644 --- a/wxdata/source/scwx/wsr88d/rda/level2_message_factory.cpp +++ b/wxdata/source/scwx/wsr88d/rda/level2_message_factory.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,8 @@ typedef std::function(Level2MessageHeader&&, CreateLevel2MessageFunction; static const std::unordered_map - create_ {{2, RdaStatusData::Create}, + create_ {{1, DigitalRadarData::Create}, + {2, RdaStatusData::Create}, {3, PerformanceMaintenanceData::Create}, {5, VolumeCoveragePatternData::Create}, {13, ClutterFilterBypassMap::Create}, diff --git a/wxdata/wxdata.cmake b/wxdata/wxdata.cmake index e2cdec59..c151b046 100644 --- a/wxdata/wxdata.cmake +++ b/wxdata/wxdata.cmake @@ -102,6 +102,7 @@ set(SRC_WSR88D source/scwx/wsr88d/ar2v_file.cpp source/scwx/wsr88d/wsr88d_types.cpp) 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/digital_radar_data_generic.hpp include/scwx/wsr88d/rda/level2_message.hpp include/scwx/wsr88d/rda/level2_message_factory.hpp @@ -113,6 +114,7 @@ set(HDR_WSR88D_RDA include/scwx/wsr88d/rda/clutter_filter_bypass_map.hpp include/scwx/wsr88d/rda/volume_coverage_pattern_data.hpp) 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/digital_radar_data_generic.cpp source/scwx/wsr88d/rda/level2_message.cpp source/scwx/wsr88d/rda/level2_message_factory.cpp