#include #include namespace scwx { namespace wsr88d { namespace rda { static const std::string logPrefix_ = "scwx::wsr88d::rda::digital_radar_data_generic"; static const auto logger_ = util::Logger::Create(logPrefix_); static const std::unordered_map strToDataBlock_ { {"VOL", DataBlockType::Volume}, {"ELV", DataBlockType::Elevation}, {"RAD", DataBlockType::Radial}, {"REF", DataBlockType::MomentRef}, {"VEL", DataBlockType::MomentVel}, {"SW ", DataBlockType::MomentSw}, {"ZDR", DataBlockType::MomentZdr}, {"PHI", DataBlockType::MomentPhi}, {"RHO", DataBlockType::MomentRho}, {"CFP", DataBlockType::MomentCfp}}; class DigitalRadarDataGeneric::DataBlock::Impl { public: explicit Impl(const std::string& dataBlockType, const std::string& dataName) : dataBlockType_ {dataBlockType}, dataName_ {dataName} { } std::string dataBlockType_; std::string dataName_; }; DigitalRadarDataGeneric::DataBlock::DataBlock(const std::string& dataBlockType, const std::string& dataName) : p(std::make_unique(dataBlockType, dataName)) { } DigitalRadarDataGeneric::DataBlock::~DataBlock() = default; DigitalRadarDataGeneric::DataBlock::DataBlock(DataBlock&&) noexcept = default; DigitalRadarDataGeneric::DataBlock& DigitalRadarDataGeneric::DataBlock::operator=(DataBlock&&) noexcept = default; class DigitalRadarDataGeneric::MomentDataBlock::Impl { public: explicit Impl() {} std::uint16_t numberOfDataMomentGates_ {0}; std::uint16_t dataMomentRange_ {0}; std::uint16_t dataMomentRangeSampleInterval_ {0}; std::uint16_t tover_ {0}; std::int16_t snrThreshold_ {0}; std::uint8_t controlFlags_ {0}; std::uint8_t dataWordSize_ {0}; float scale_ {0.0f}; float offset_ {0.0f}; std::vector momentGates8_ {}; std::vector momentGates16_ {}; }; DigitalRadarDataGeneric::MomentDataBlock::MomentDataBlock( const std::string& dataBlockType, const std::string& dataName) : DataBlock(dataBlockType, dataName), p(std::make_unique()) { } DigitalRadarDataGeneric::MomentDataBlock::~MomentDataBlock() = default; DigitalRadarDataGeneric::MomentDataBlock::MomentDataBlock( MomentDataBlock&&) noexcept = default; DigitalRadarDataGeneric::MomentDataBlock& DigitalRadarDataGeneric::MomentDataBlock::operator=( MomentDataBlock&&) noexcept = default; std::uint16_t DigitalRadarDataGeneric::MomentDataBlock::number_of_data_moment_gates() const { return p->numberOfDataMomentGates_; } units::kilometers DigitalRadarDataGeneric::MomentDataBlock::data_moment_range() const { return units::kilometers {p->dataMomentRange_ * 0.001f}; } std::uint16_t DigitalRadarDataGeneric::MomentDataBlock::data_moment_range_raw() const { return p->dataMomentRange_; } units::kilometers DigitalRadarDataGeneric::MomentDataBlock::data_moment_range_sample_interval() const { return units::kilometers {p->dataMomentRangeSampleInterval_ * 0.001f}; } std::uint16_t DigitalRadarDataGeneric::MomentDataBlock:: data_moment_range_sample_interval_raw() const { return p->dataMomentRangeSampleInterval_; } float DigitalRadarDataGeneric::MomentDataBlock::snr_threshold() const { return p->snrThreshold_ * 0.1f; } std::int16_t DigitalRadarDataGeneric::MomentDataBlock::snr_threshold_raw() const { return p->snrThreshold_; } std::uint8_t DigitalRadarDataGeneric::MomentDataBlock::data_word_size() const { return p->dataWordSize_; } float DigitalRadarDataGeneric::MomentDataBlock::scale() const { return p->scale_; } float DigitalRadarDataGeneric::MomentDataBlock::offset() const { return p->offset_; } const void* DigitalRadarDataGeneric::MomentDataBlock::data_moments() const { const void* dataMoments; switch (p->dataWordSize_) { case 8: dataMoments = p->momentGates8_.data(); break; case 16: dataMoments = p->momentGates16_.data(); break; default: dataMoments = nullptr; break; } return dataMoments; } std::shared_ptr DigitalRadarDataGeneric::MomentDataBlock::Create( const std::string& dataBlockType, const std::string& dataName, std::istream& is) { std::shared_ptr p = std::make_shared(dataBlockType, dataName); if (!p->Parse(is)) { p.reset(); } return p; } bool DigitalRadarDataGeneric::MomentDataBlock::Parse(std::istream& is) { bool dataBlockValid = true; is.seekg(4, std::ios_base::cur); // 4-7 is.read(reinterpret_cast(&p->numberOfDataMomentGates_), 2); // 8-9 is.read(reinterpret_cast(&p->dataMomentRange_), 2); // 10-11 is.read(reinterpret_cast(&p->dataMomentRangeSampleInterval_), 2); // 12-13 is.read(reinterpret_cast(&p->tover_), 2); // 14-15 is.read(reinterpret_cast(&p->snrThreshold_), 2); // 16-17 is.read(reinterpret_cast(&p->controlFlags_), 1); // 18 is.read(reinterpret_cast(&p->dataWordSize_), 1); // 19 is.read(reinterpret_cast(&p->scale_), 4); // 20-23 is.read(reinterpret_cast(&p->offset_), 4); // 24-27 p->numberOfDataMomentGates_ = ntohs(p->numberOfDataMomentGates_); p->dataMomentRange_ = ntohs(p->dataMomentRange_); p->dataMomentRangeSampleInterval_ = ntohs(p->dataMomentRangeSampleInterval_); p->tover_ = ntohs(p->tover_); p->snrThreshold_ = ntohs(p->snrThreshold_); p->scale_ = awips::Message::SwapFloat(p->scale_); p->offset_ = awips::Message::SwapFloat(p->offset_); if (p->numberOfDataMomentGates_ <= 1840) { if (p->dataWordSize_ == 8) { p->momentGates8_.resize(p->numberOfDataMomentGates_); is.read(reinterpret_cast(p->momentGates8_.data()), p->numberOfDataMomentGates_); } else if (p->dataWordSize_ == 16) { p->momentGates16_.resize(p->numberOfDataMomentGates_); is.read(reinterpret_cast(p->momentGates16_.data()), p->numberOfDataMomentGates_ * 2); awips::Message::SwapVector(p->momentGates16_); } else { logger_->warn("Invalid data word size: {}", p->dataWordSize_); dataBlockValid = false; } } else { logger_->warn("Invalid number of data moment gates: {}", p->numberOfDataMomentGates_); dataBlockValid = false; } return dataBlockValid; } class DigitalRadarDataGeneric::VolumeDataBlock::Impl { public: explicit Impl() {} std::uint16_t lrtup_ {0}; std::uint8_t versionNumberMajor_ {0}; std::uint8_t versionNumberMinor_ {0}; float latitude_ {0.0f}; float longitude_ {0.0f}; std::int16_t siteHeight_ {0}; std::uint16_t feedhornHeight_ {0}; float calibrationConstant_ {0.0f}; float horizontaShvTxPower_ {0.0f}; float verticalShvTxPower_ {0.0f}; float systemDifferentialReflectivity_ {0.0f}; float initialSystemDifferentialPhase_ {0.0f}; std::uint16_t volumeCoveragePatternNumber_ {0}; std::uint16_t processingStatus_ {0}; }; DigitalRadarDataGeneric::VolumeDataBlock::VolumeDataBlock( const std::string& dataBlockType, const std::string& dataName) : DataBlock(dataBlockType, dataName), p(std::make_unique()) { } DigitalRadarDataGeneric::VolumeDataBlock::~VolumeDataBlock() = default; DigitalRadarDataGeneric::VolumeDataBlock::VolumeDataBlock( VolumeDataBlock&&) noexcept = default; DigitalRadarDataGeneric::VolumeDataBlock& DigitalRadarDataGeneric::VolumeDataBlock::operator=( VolumeDataBlock&&) noexcept = default; float DigitalRadarDataGeneric::VolumeDataBlock::latitude() const { return p->latitude_; } float DigitalRadarDataGeneric::VolumeDataBlock::longitude() const { return p->longitude_; } std::uint16_t DigitalRadarDataGeneric::VolumeDataBlock::volume_coverage_pattern_number() const { return p->volumeCoveragePatternNumber_; } std::shared_ptr DigitalRadarDataGeneric::VolumeDataBlock::Create( const std::string& dataBlockType, const std::string& dataName, std::istream& is) { std::shared_ptr p = std::make_shared(dataBlockType, dataName); if (!p->Parse(is)) { p.reset(); } return p; } bool DigitalRadarDataGeneric::VolumeDataBlock::Parse(std::istream& is) { bool dataBlockValid = true; is.read(reinterpret_cast(&p->lrtup_), 2); // 4-5 is.read(reinterpret_cast(&p->versionNumberMajor_), 1); // 6 is.read(reinterpret_cast(&p->versionNumberMinor_), 1); // 7 is.read(reinterpret_cast(&p->latitude_), 4); // 8-11 is.read(reinterpret_cast(&p->longitude_), 4); // 12-15 is.read(reinterpret_cast(&p->siteHeight_), 2); // 16-17 is.read(reinterpret_cast(&p->feedhornHeight_), 2); // 18-19 is.read(reinterpret_cast(&p->calibrationConstant_), 4); // 20-23 is.read(reinterpret_cast(&p->horizontaShvTxPower_), 4); // 24-27 is.read(reinterpret_cast(&p->verticalShvTxPower_), 4); // 28-31 is.read(reinterpret_cast(&p->systemDifferentialReflectivity_), 4); // 32-35 is.read(reinterpret_cast(&p->initialSystemDifferentialPhase_), 4); // 36-39 is.read(reinterpret_cast(&p->volumeCoveragePatternNumber_), 2); // 40-41 is.read(reinterpret_cast(&p->processingStatus_), 2); // 42-43 p->lrtup_ = ntohs(p->lrtup_); p->latitude_ = awips::Message::SwapFloat(p->latitude_); p->longitude_ = awips::Message::SwapFloat(p->longitude_); p->siteHeight_ = ntohs(p->siteHeight_); p->feedhornHeight_ = ntohs(p->feedhornHeight_); p->calibrationConstant_ = awips::Message::SwapFloat(p->calibrationConstant_); p->horizontaShvTxPower_ = awips::Message::SwapFloat(p->horizontaShvTxPower_); p->verticalShvTxPower_ = awips::Message::SwapFloat(p->verticalShvTxPower_); p->systemDifferentialReflectivity_ = awips::Message::SwapFloat(p->systemDifferentialReflectivity_); p->initialSystemDifferentialPhase_ = awips::Message::SwapFloat(p->initialSystemDifferentialPhase_); p->volumeCoveragePatternNumber_ = ntohs(p->volumeCoveragePatternNumber_); p->processingStatus_ = ntohs(p->processingStatus_); return dataBlockValid; } class DigitalRadarDataGeneric::ElevationDataBlock::Impl { public: explicit Impl() {} std::uint16_t lrtup_ {0}; std::int16_t atmos_ {0}; float calibrationConstant_ {0.0f}; }; DigitalRadarDataGeneric::ElevationDataBlock::ElevationDataBlock( const std::string& dataBlockType, const std::string& dataName) : DataBlock(dataBlockType, dataName), p(std::make_unique()) { } DigitalRadarDataGeneric::ElevationDataBlock::~ElevationDataBlock() = default; DigitalRadarDataGeneric::ElevationDataBlock::ElevationDataBlock( ElevationDataBlock&&) noexcept = default; DigitalRadarDataGeneric::ElevationDataBlock& DigitalRadarDataGeneric::ElevationDataBlock::operator=( ElevationDataBlock&&) noexcept = default; std::shared_ptr DigitalRadarDataGeneric::ElevationDataBlock::Create( const std::string& dataBlockType, const std::string& dataName, std::istream& is) { std::shared_ptr p = std::make_shared(dataBlockType, dataName); if (!p->Parse(is)) { p.reset(); } return p; } bool DigitalRadarDataGeneric::ElevationDataBlock::Parse(std::istream& is) { bool dataBlockValid = true; is.read(reinterpret_cast(&p->lrtup_), 2); // 4-5 is.read(reinterpret_cast(&p->atmos_), 2); // 6-7 is.read(reinterpret_cast(&p->calibrationConstant_), 4); // 8-11 p->lrtup_ = ntohs(p->lrtup_); p->atmos_ = ntohs(p->atmos_); p->calibrationConstant_ = awips::Message::SwapFloat(p->calibrationConstant_); return dataBlockValid; } class DigitalRadarDataGeneric::RadialDataBlock::Impl { public: explicit Impl() {} std::uint16_t lrtup_ {0}; std::uint16_t unambigiousRange_ {0}; float noiseLevelHorizontal_ {0.0f}; float noiseLevelVertical_ {0.0f}; std::uint16_t nyquistVelocity_ {0}; std::uint16_t radialFlags_ {0}; float calibrationConstantHorizontal_ {0.0f}; float calibrationConstantVertical_ {0.0f}; }; DigitalRadarDataGeneric::RadialDataBlock::RadialDataBlock( const std::string& dataBlockType, const std::string& dataName) : DataBlock(dataBlockType, dataName), p(std::make_unique()) { } DigitalRadarDataGeneric::RadialDataBlock::~RadialDataBlock() = default; DigitalRadarDataGeneric::RadialDataBlock::RadialDataBlock( RadialDataBlock&&) noexcept = default; DigitalRadarDataGeneric::RadialDataBlock& DigitalRadarDataGeneric::RadialDataBlock::operator=( RadialDataBlock&&) noexcept = default; float DigitalRadarDataGeneric::RadialDataBlock::unambiguous_range() const { return p->unambigiousRange_ / 10.0f; } std::shared_ptr DigitalRadarDataGeneric::RadialDataBlock::Create( const std::string& dataBlockType, const std::string& dataName, std::istream& is) { std::shared_ptr p = std::make_shared(dataBlockType, dataName); if (!p->Parse(is)) { p.reset(); } return p; } bool DigitalRadarDataGeneric::RadialDataBlock::Parse(std::istream& is) { bool dataBlockValid = true; is.read(reinterpret_cast(&p->lrtup_), 2); // 4-5 is.read(reinterpret_cast(&p->unambigiousRange_), 2); // 6-7 is.read(reinterpret_cast(&p->noiseLevelHorizontal_), 4); // 8-11 is.read(reinterpret_cast(&p->noiseLevelVertical_), 4); // 12-15 is.read(reinterpret_cast(&p->nyquistVelocity_), 2); // 16-17 is.read(reinterpret_cast(&p->radialFlags_), 2); // 18-19 is.read(reinterpret_cast(&p->calibrationConstantHorizontal_), 4); // 20-23 is.read(reinterpret_cast(&p->calibrationConstantVertical_), 4); // 24-27 p->lrtup_ = ntohs(p->lrtup_); p->unambigiousRange_ = ntohs(p->unambigiousRange_); p->noiseLevelHorizontal_ = awips::Message::SwapFloat(p->noiseLevelHorizontal_); p->noiseLevelVertical_ = awips::Message::SwapFloat(p->noiseLevelVertical_); p->nyquistVelocity_ = ntohs(p->nyquistVelocity_); p->radialFlags_ = ntohs(p->radialFlags_); p->calibrationConstantHorizontal_ = awips::Message::SwapFloat(p->calibrationConstantHorizontal_); p->calibrationConstantVertical_ = awips::Message::SwapFloat(p->calibrationConstantVertical_); return dataBlockValid; } class DigitalRadarDataGeneric::Impl { public: explicit Impl() {}; ~Impl() = default; std::string radarIdentifier_ {}; std::uint32_t collectionTime_ {0}; std::uint16_t modifiedJulianDate_ {0}; std::uint16_t azimuthNumber_ {0}; float azimuthAngle_ {0.0f}; std::uint8_t compressionIndicator_ {0}; std::uint16_t radialLength_ {0}; std::uint8_t azimuthResolutionSpacing_ {0}; std::uint8_t radialStatus_ {0}; std::uint8_t elevationNumber_ {0}; std::uint8_t cutSectorNumber_ {0}; float elevationAngle_ {0.0f}; std::uint8_t radialSpotBlankingStatus_ {0}; std::uint8_t azimuthIndexingMode_ {0}; std::uint16_t dataBlockCount_ {0}; std::array dataBlockPointer_ {0}; std::shared_ptr volumeDataBlock_ {nullptr}; std::shared_ptr elevationDataBlock_ {nullptr}; std::shared_ptr radialDataBlock_ {nullptr}; std::unordered_map> momentDataBlock_ {}; }; DigitalRadarDataGeneric::DigitalRadarDataGeneric() : GenericRadarData(), p(std::make_unique()) { } DigitalRadarDataGeneric::~DigitalRadarDataGeneric() = default; DigitalRadarDataGeneric::DigitalRadarDataGeneric( DigitalRadarDataGeneric&&) noexcept = default; DigitalRadarDataGeneric& DigitalRadarDataGeneric::operator=( DigitalRadarDataGeneric&&) noexcept = default; std::string DigitalRadarDataGeneric::radar_identifier() const { return p->radarIdentifier_; } std::uint32_t DigitalRadarDataGeneric::collection_time() const { return p->collectionTime_; } std::uint16_t DigitalRadarDataGeneric::modified_julian_date() const { return p->modifiedJulianDate_; } std::uint16_t DigitalRadarDataGeneric::azimuth_number() const { return p->azimuthNumber_; } units::degrees DigitalRadarDataGeneric::azimuth_angle() const { return units::degrees {p->azimuthAngle_}; } std::uint8_t DigitalRadarDataGeneric::compression_indicator() const { return p->compressionIndicator_; } std::uint16_t DigitalRadarDataGeneric::radial_length() const { return p->radialLength_; } std::uint8_t DigitalRadarDataGeneric::azimuth_resolution_spacing() const { return p->azimuthResolutionSpacing_; } std::uint8_t DigitalRadarDataGeneric::radial_status() const { return p->radialStatus_; } std::uint16_t DigitalRadarDataGeneric::elevation_number() const { return p->elevationNumber_; } std::uint8_t DigitalRadarDataGeneric::cut_sector_number() const { return p->cutSectorNumber_; } units::degrees DigitalRadarDataGeneric::elevation_angle() const { return units::degrees {p->elevationAngle_}; } std::uint8_t DigitalRadarDataGeneric::radial_spot_blanking_status() const { return p->radialSpotBlankingStatus_; } std::uint8_t DigitalRadarDataGeneric::azimuth_indexing_mode() const { return p->azimuthIndexingMode_; } std::uint16_t DigitalRadarDataGeneric::data_block_count() const { return p->dataBlockCount_; } std::uint16_t DigitalRadarDataGeneric::volume_coverage_pattern_number() const { std::uint16_t vcpNumber = 0; if (p->volumeDataBlock_ != nullptr) { vcpNumber = p->volumeDataBlock_->volume_coverage_pattern_number(); } return vcpNumber; } std::shared_ptr DigitalRadarDataGeneric::elevation_data_block() const { return p->elevationDataBlock_; } std::shared_ptr DigitalRadarDataGeneric::radial_data_block() const { return p->radialDataBlock_; } std::shared_ptr DigitalRadarDataGeneric::volume_data_block() const { return p->volumeDataBlock_; } std::shared_ptr DigitalRadarDataGeneric::moment_data_block(DataBlockType type) const { std::shared_ptr momentDataBlock = nullptr; auto it = p->momentDataBlock_.find(type); if (it != p->momentDataBlock_.end()) { momentDataBlock = it->second; } return momentDataBlock; } bool DigitalRadarDataGeneric::Parse(std::istream& is) { logger_->trace("Parsing Digital Radar Data (Message Type 31)"); bool messageValid = true; std::size_t bytesRead = 0; std::streampos isBegin = is.tellg(); p->radarIdentifier_.resize(4); is.read(&p->radarIdentifier_[0], 4); // 0-3 is.read(reinterpret_cast(&p->collectionTime_), 4); // 4-7 is.read(reinterpret_cast(&p->modifiedJulianDate_), 2); // 8-9 is.read(reinterpret_cast(&p->azimuthNumber_), 2); // 10-11 is.read(reinterpret_cast(&p->azimuthAngle_), 4); // 12-15 is.read(reinterpret_cast(&p->compressionIndicator_), 1); // 16 is.seekg(1, std::ios_base::cur); // 17 is.read(reinterpret_cast(&p->radialLength_), 2); // 18-19 is.read(reinterpret_cast(&p->azimuthResolutionSpacing_), 1); // 20 is.read(reinterpret_cast(&p->radialStatus_), 1); // 21 is.read(reinterpret_cast(&p->elevationNumber_), 1); // 22 is.read(reinterpret_cast(&p->cutSectorNumber_), 1); // 23 is.read(reinterpret_cast(&p->elevationAngle_), 4); // 24-27 is.read(reinterpret_cast(&p->radialSpotBlankingStatus_), 1); // 28 is.read(reinterpret_cast(&p->azimuthIndexingMode_), 1); // 29 is.read(reinterpret_cast(&p->dataBlockCount_), 2); // 30-31 p->collectionTime_ = ntohl(p->collectionTime_); p->modifiedJulianDate_ = ntohs(p->modifiedJulianDate_); p->azimuthNumber_ = ntohs(p->azimuthNumber_); p->azimuthAngle_ = SwapFloat(p->azimuthAngle_); p->radialLength_ = ntohs(p->radialLength_); p->elevationAngle_ = SwapFloat(p->elevationAngle_); p->dataBlockCount_ = ntohs(p->dataBlockCount_); if (p->azimuthNumber_ < 1 || p->azimuthNumber_ > 720) { logger_->warn("Invalid azimuth number: {}", p->azimuthNumber_); messageValid = false; } if (p->elevationNumber_ < 1 || p->elevationNumber_ > 32) { logger_->warn("Invalid elevation number: {}", p->elevationNumber_); messageValid = false; } if (p->dataBlockCount_ < 4 || p->dataBlockCount_ > 10) { logger_->warn("Invalid number of data blocks: {}", p->dataBlockCount_); messageValid = false; } if (p->compressionIndicator_ != 0) { logger_->warn("Compression not supported"); messageValid = false; } if (!messageValid) { p->dataBlockCount_ = 0; } is.read(reinterpret_cast(&p->dataBlockPointer_), p->dataBlockCount_ * 4); SwapArray(p->dataBlockPointer_, p->dataBlockCount_); for (uint16_t b = 0; b < p->dataBlockCount_; ++b) { is.seekg(isBegin + std::streamoff(p->dataBlockPointer_[b]), std::ios_base::beg); std::string dataBlockType(1, 0); std::string dataName(3, 0); is.read(&dataBlockType[0], 1); is.read(&dataName[0], 3); DataBlockType dataBlock = DataBlockType::Unknown; try { dataBlock = strToDataBlock_.at(dataName); } catch (const std::exception&) { } switch (dataBlock) { case DataBlockType::Volume: p->volumeDataBlock_ = std::move(VolumeDataBlock::Create(dataBlockType, dataName, is)); break; case DataBlockType::Elevation: p->elevationDataBlock_ = std::move(ElevationDataBlock::Create(dataBlockType, dataName, is)); break; case DataBlockType::Radial: p->radialDataBlock_ = std::move(RadialDataBlock::Create(dataBlockType, dataName, is)); break; case DataBlockType::MomentRef: case DataBlockType::MomentVel: case DataBlockType::MomentSw: case DataBlockType::MomentZdr: case DataBlockType::MomentPhi: case DataBlockType::MomentRho: case DataBlockType::MomentCfp: p->momentDataBlock_[dataBlock] = std::move(MomentDataBlock::Create(dataBlockType, dataName, is)); break; default: logger_->warn("Unknown data name: {}", dataName); break; } } is.seekg(isBegin, std::ios_base::beg); if (!ValidateMessage(is, bytesRead)) { messageValid = false; } return messageValid; } std::shared_ptr DigitalRadarDataGeneric::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