mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 21:00:05 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			834 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			834 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include <scwx/wsr88d/rda/digital_radar_data_generic.hpp>
 | |
| #include <scwx/util/logger.hpp>
 | |
| 
 | |
| namespace scwx::wsr88d::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<std::string, DataBlockType> 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(std::string dataBlockType, std::string dataName) :
 | |
|        dataBlockType_ {std::move(dataBlockType)},
 | |
|        dataName_ {std::move(dataName)}
 | |
|    {
 | |
|    }
 | |
| 
 | |
|    std::string dataBlockType_;
 | |
|    std::string dataName_;
 | |
| };
 | |
| 
 | |
| DigitalRadarDataGeneric::DataBlock::DataBlock(const std::string& dataBlockType,
 | |
|                                               const std::string& dataName) :
 | |
|     p(std::make_unique<Impl>(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() = default;
 | |
|    ~Impl()         = default;
 | |
| 
 | |
|    Impl(const Impl&)             = delete;
 | |
|    Impl& operator=(const Impl&)  = delete;
 | |
|    Impl(const Impl&&)            = delete;
 | |
|    Impl& operator=(const Impl&&) = delete;
 | |
| 
 | |
|    std::uint16_t numberOfDataMomentGates_ {0};
 | |
|    std::int16_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<std::uint8_t>  momentGates8_ {};
 | |
|    std::vector<std::uint16_t> momentGates16_ {};
 | |
| };
 | |
| 
 | |
| DigitalRadarDataGeneric::MomentDataBlock::MomentDataBlock(
 | |
|    const std::string& dataBlockType, const std::string& dataName) :
 | |
|     DataBlock(dataBlockType, dataName), p(std::make_unique<Impl>())
 | |
| {
 | |
| }
 | |
| 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<float>
 | |
| DigitalRadarDataGeneric::MomentDataBlock::data_moment_range() const
 | |
| {
 | |
|    static constexpr float kScale_ = 0.001f;
 | |
|    return units::kilometers<float> {static_cast<float>(p->dataMomentRange_) *
 | |
|                                     kScale_};
 | |
| }
 | |
| 
 | |
| std::int16_t
 | |
| DigitalRadarDataGeneric::MomentDataBlock::data_moment_range_raw() const
 | |
| {
 | |
|    return p->dataMomentRange_;
 | |
| }
 | |
| 
 | |
| units::kilometers<float>
 | |
| DigitalRadarDataGeneric::MomentDataBlock::data_moment_range_sample_interval()
 | |
|    const
 | |
| {
 | |
|    static constexpr float kScale_ = 0.001f;
 | |
|    return units::kilometers<float> {
 | |
|       static_cast<float>(p->dataMomentRangeSampleInterval_) * kScale_};
 | |
| }
 | |
| 
 | |
| std::uint16_t DigitalRadarDataGeneric::MomentDataBlock::
 | |
|    data_moment_range_sample_interval_raw() const
 | |
| {
 | |
|    return p->dataMomentRangeSampleInterval_;
 | |
| }
 | |
| 
 | |
| float DigitalRadarDataGeneric::MomentDataBlock::snr_threshold() const
 | |
| {
 | |
|    static constexpr float kScale_ = 0.1f;
 | |
|    return static_cast<float>(p->snrThreshold_) * kScale_;
 | |
| }
 | |
| 
 | |
| 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 = nullptr;
 | |
| 
 | |
|    switch (p->dataWordSize_)
 | |
|    {
 | |
|    case 8: // NOLINT(cppcoreguidelines-avoid-magic-numbers)
 | |
|       dataMoments = p->momentGates8_.data();
 | |
|       break;
 | |
|    case 16: // NOLINT(cppcoreguidelines-avoid-magic-numbers)
 | |
|       dataMoments = p->momentGates16_.data();
 | |
|       break;
 | |
|    default:
 | |
|       dataMoments = nullptr;
 | |
|       break;
 | |
|    }
 | |
| 
 | |
|    return dataMoments;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<DigitalRadarDataGeneric::MomentDataBlock>
 | |
| DigitalRadarDataGeneric::MomentDataBlock::Create(
 | |
|    const std::string& dataBlockType,
 | |
|    const std::string& dataName,
 | |
|    std::istream&      is)
 | |
| {
 | |
|    std::shared_ptr<MomentDataBlock> p =
 | |
|       std::make_shared<MomentDataBlock>(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<char*>(&p->numberOfDataMomentGates_), 2); // 8-9
 | |
|    is.read(reinterpret_cast<char*>(&p->dataMomentRange_), 2);         // 10-11
 | |
|    is.read(reinterpret_cast<char*>(&p->dataMomentRangeSampleInterval_),
 | |
|            2);                                             // 12-13
 | |
|    is.read(reinterpret_cast<char*>(&p->tover_), 2);        // 14-15
 | |
|    is.read(reinterpret_cast<char*>(&p->snrThreshold_), 2); // 16-17
 | |
|    is.read(reinterpret_cast<char*>(&p->controlFlags_), 1); // 18
 | |
|    is.read(reinterpret_cast<char*>(&p->dataWordSize_), 1); // 19
 | |
|    is.read(reinterpret_cast<char*>(&p->scale_), 4);        // 20-23
 | |
|    is.read(reinterpret_cast<char*>(&p->offset_), 4);       // 24-27
 | |
| 
 | |
|    p->numberOfDataMomentGates_ = ntohs(p->numberOfDataMomentGates_);
 | |
|    p->dataMomentRange_ = static_cast<std::int16_t>(ntohs(p->dataMomentRange_));
 | |
|    p->dataMomentRangeSampleInterval_ = ntohs(p->dataMomentRangeSampleInterval_);
 | |
|    p->tover_                         = ntohs(p->tover_);
 | |
|    p->snrThreshold_ = static_cast<std::int16_t>(ntohs(p->snrThreshold_));
 | |
|    p->scale_        = awips::Message::SwapFloat(p->scale_);
 | |
|    p->offset_       = awips::Message::SwapFloat(p->offset_);
 | |
| 
 | |
|    // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
 | |
| 
 | |
|    if (p->numberOfDataMomentGates_ <= 1840)
 | |
|    {
 | |
|       if (p->dataWordSize_ == 8)
 | |
|       {
 | |
|          p->momentGates8_.resize(p->numberOfDataMomentGates_);
 | |
|          is.read(reinterpret_cast<char*>(p->momentGates8_.data()),
 | |
|                  p->numberOfDataMomentGates_);
 | |
|       }
 | |
|       else if (p->dataWordSize_ == 16)
 | |
|       {
 | |
|          p->momentGates16_.resize(p->numberOfDataMomentGates_);
 | |
|          is.read(reinterpret_cast<char*>(p->momentGates16_.data()),
 | |
|                  static_cast<std::streamsize>(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;
 | |
|    }
 | |
| 
 | |
|    // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
 | |
| 
 | |
|    return dataBlockValid;
 | |
| }
 | |
| 
 | |
| class DigitalRadarDataGeneric::VolumeDataBlock::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 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};
 | |
|    std::uint16_t zdrBiasEstimateWeightedMean_ {0};
 | |
| };
 | |
| 
 | |
| DigitalRadarDataGeneric::VolumeDataBlock::VolumeDataBlock(
 | |
|    const std::string& dataBlockType, const std::string& dataName) :
 | |
|     DataBlock(dataBlockType, dataName), p(std::make_unique<Impl>())
 | |
| {
 | |
| }
 | |
| 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>
 | |
| DigitalRadarDataGeneric::VolumeDataBlock::Create(
 | |
|    const std::string& dataBlockType,
 | |
|    const std::string& dataName,
 | |
|    std::istream&      is)
 | |
| {
 | |
|    std::shared_ptr<VolumeDataBlock> p =
 | |
|       std::make_shared<VolumeDataBlock>(dataBlockType, dataName);
 | |
| 
 | |
|    if (!p->Parse(is))
 | |
|    {
 | |
|       p.reset();
 | |
|    }
 | |
| 
 | |
|    return p;
 | |
| }
 | |
| 
 | |
| bool DigitalRadarDataGeneric::VolumeDataBlock::Parse(std::istream& is)
 | |
| {
 | |
|    bool dataBlockValid = true;
 | |
| 
 | |
|    is.read(reinterpret_cast<char*>(&p->lrtup_), 2);               // 4-5
 | |
|    is.read(reinterpret_cast<char*>(&p->versionNumberMajor_), 1);  // 6
 | |
|    is.read(reinterpret_cast<char*>(&p->versionNumberMinor_), 1);  // 7
 | |
|    is.read(reinterpret_cast<char*>(&p->latitude_), 4);            // 8-11
 | |
|    is.read(reinterpret_cast<char*>(&p->longitude_), 4);           // 12-15
 | |
|    is.read(reinterpret_cast<char*>(&p->siteHeight_), 2);          // 16-17
 | |
|    is.read(reinterpret_cast<char*>(&p->feedhornHeight_), 2);      // 18-19
 | |
|    is.read(reinterpret_cast<char*>(&p->calibrationConstant_), 4); // 20-23
 | |
|    is.read(reinterpret_cast<char*>(&p->horizontaShvTxPower_), 4); // 24-27
 | |
|    is.read(reinterpret_cast<char*>(&p->verticalShvTxPower_), 4);  // 28-31
 | |
|    is.read(reinterpret_cast<char*>(&p->systemDifferentialReflectivity_),
 | |
|            4); // 32-35
 | |
|    is.read(reinterpret_cast<char*>(&p->initialSystemDifferentialPhase_),
 | |
|            4); // 36-39
 | |
|    is.read(reinterpret_cast<char*>(&p->volumeCoveragePatternNumber_),
 | |
|            2);                                                 // 40-41
 | |
|    is.read(reinterpret_cast<char*>(&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_          = static_cast<std::int16_t>(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_);
 | |
| 
 | |
|    // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
 | |
| 
 | |
|    if (p->lrtup_ >= 46)
 | |
|    {
 | |
|       is.read(reinterpret_cast<char*>(&p->zdrBiasEstimateWeightedMean_),
 | |
|               2); // 44-45
 | |
|       p->zdrBiasEstimateWeightedMean_ = ntohs(p->zdrBiasEstimateWeightedMean_);
 | |
|    }
 | |
| 
 | |
|    if (p->lrtup_ >= 52)
 | |
|    {
 | |
|       is.seekg(6, std::ios_base::cur); // 46-51
 | |
|    }
 | |
| 
 | |
|    // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
 | |
| 
 | |
|    return dataBlockValid;
 | |
| }
 | |
| 
 | |
| class DigitalRadarDataGeneric::ElevationDataBlock::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 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<Impl>())
 | |
| {
 | |
| }
 | |
| DigitalRadarDataGeneric::ElevationDataBlock::~ElevationDataBlock() = default;
 | |
| 
 | |
| DigitalRadarDataGeneric::ElevationDataBlock::ElevationDataBlock(
 | |
|    ElevationDataBlock&&) noexcept = default;
 | |
| DigitalRadarDataGeneric::ElevationDataBlock&
 | |
| DigitalRadarDataGeneric::ElevationDataBlock::operator=(
 | |
|    ElevationDataBlock&&) noexcept = default;
 | |
| 
 | |
| std::shared_ptr<DigitalRadarDataGeneric::ElevationDataBlock>
 | |
| DigitalRadarDataGeneric::ElevationDataBlock::Create(
 | |
|    const std::string& dataBlockType,
 | |
|    const std::string& dataName,
 | |
|    std::istream&      is)
 | |
| {
 | |
|    std::shared_ptr<ElevationDataBlock> p =
 | |
|       std::make_shared<ElevationDataBlock>(dataBlockType, dataName);
 | |
| 
 | |
|    if (!p->Parse(is))
 | |
|    {
 | |
|       p.reset();
 | |
|    }
 | |
| 
 | |
|    return p;
 | |
| }
 | |
| 
 | |
| bool DigitalRadarDataGeneric::ElevationDataBlock::Parse(std::istream& is)
 | |
| {
 | |
|    bool dataBlockValid = true;
 | |
| 
 | |
|    is.read(reinterpret_cast<char*>(&p->lrtup_), 2);               // 4-5
 | |
|    is.read(reinterpret_cast<char*>(&p->atmos_), 2);               // 6-7
 | |
|    is.read(reinterpret_cast<char*>(&p->calibrationConstant_), 4); // 8-11
 | |
| 
 | |
|    p->lrtup_               = ntohs(p->lrtup_);
 | |
|    p->atmos_               = static_cast<std::int16_t>(ntohs(p->atmos_));
 | |
|    p->calibrationConstant_ = awips::Message::SwapFloat(p->calibrationConstant_);
 | |
| 
 | |
|    return dataBlockValid;
 | |
| }
 | |
| 
 | |
| class DigitalRadarDataGeneric::RadialDataBlock::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 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<Impl>())
 | |
| {
 | |
| }
 | |
| DigitalRadarDataGeneric::RadialDataBlock::~RadialDataBlock() = default;
 | |
| 
 | |
| DigitalRadarDataGeneric::RadialDataBlock::RadialDataBlock(
 | |
|    RadialDataBlock&&) noexcept = default;
 | |
| DigitalRadarDataGeneric::RadialDataBlock&
 | |
| DigitalRadarDataGeneric::RadialDataBlock::operator=(
 | |
|    RadialDataBlock&&) noexcept = default;
 | |
| 
 | |
| float DigitalRadarDataGeneric::RadialDataBlock::unambiguous_range() const
 | |
| {
 | |
|    static constexpr float kScale_ = 0.1f;
 | |
|    return static_cast<float>(p->unambigiousRange_) * kScale_;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<DigitalRadarDataGeneric::RadialDataBlock>
 | |
| DigitalRadarDataGeneric::RadialDataBlock::Create(
 | |
|    const std::string& dataBlockType,
 | |
|    const std::string& dataName,
 | |
|    std::istream&      is)
 | |
| {
 | |
|    std::shared_ptr<RadialDataBlock> p =
 | |
|       std::make_shared<RadialDataBlock>(dataBlockType, dataName);
 | |
| 
 | |
|    if (!p->Parse(is))
 | |
|    {
 | |
|       p.reset();
 | |
|    }
 | |
| 
 | |
|    return p;
 | |
| }
 | |
| 
 | |
| bool DigitalRadarDataGeneric::RadialDataBlock::Parse(std::istream& is)
 | |
| {
 | |
|    bool dataBlockValid = true;
 | |
| 
 | |
|    is.read(reinterpret_cast<char*>(&p->lrtup_), 2);                // 4-5
 | |
|    is.read(reinterpret_cast<char*>(&p->unambigiousRange_), 2);     // 6-7
 | |
|    is.read(reinterpret_cast<char*>(&p->noiseLevelHorizontal_), 4); // 8-11
 | |
|    is.read(reinterpret_cast<char*>(&p->noiseLevelVertical_), 4);   // 12-15
 | |
|    is.read(reinterpret_cast<char*>(&p->nyquistVelocity_), 2);      // 16-17
 | |
|    is.read(reinterpret_cast<char*>(&p->radialFlags_), 2);          // 18-19
 | |
|    is.read(reinterpret_cast<char*>(&p->calibrationConstantHorizontal_),
 | |
|            4); // 20-23
 | |
|    is.read(reinterpret_cast<char*>(&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() = default;
 | |
|    ~Impl()         = default;
 | |
| 
 | |
|    Impl(const Impl&)             = delete;
 | |
|    Impl& operator=(const Impl&)  = delete;
 | |
|    Impl(const Impl&&)            = delete;
 | |
|    Impl& operator=(const Impl&&) = delete;
 | |
| 
 | |
|    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};
 | |
| 
 | |
|    // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
 | |
|    std::array<std::uint32_t, 10> dataBlockPointer_ {0};
 | |
| 
 | |
|    std::shared_ptr<VolumeDataBlock>    volumeDataBlock_ {nullptr};
 | |
|    std::shared_ptr<ElevationDataBlock> elevationDataBlock_ {nullptr};
 | |
|    std::shared_ptr<RadialDataBlock>    radialDataBlock_ {nullptr};
 | |
|    std::unordered_map<DataBlockType, std::shared_ptr<MomentDataBlock>>
 | |
|       momentDataBlock_ {};
 | |
| };
 | |
| 
 | |
| DigitalRadarDataGeneric::DigitalRadarDataGeneric() :
 | |
|     GenericRadarData(), p(std::make_unique<Impl>())
 | |
| {
 | |
| }
 | |
| 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<float> DigitalRadarDataGeneric::azimuth_angle() const
 | |
| {
 | |
|    return units::degrees<float> {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<float> DigitalRadarDataGeneric::elevation_angle() const
 | |
| {
 | |
|    return units::degrees<float> {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::ElevationDataBlock>
 | |
| DigitalRadarDataGeneric::elevation_data_block() const
 | |
| {
 | |
|    return p->elevationDataBlock_;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<DigitalRadarDataGeneric::RadialDataBlock>
 | |
| DigitalRadarDataGeneric::radial_data_block() const
 | |
| {
 | |
|    return p->radialDataBlock_;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<DigitalRadarDataGeneric::VolumeDataBlock>
 | |
| DigitalRadarDataGeneric::volume_data_block() const
 | |
| {
 | |
|    return p->volumeDataBlock_;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<GenericRadarData::MomentDataBlock>
 | |
| DigitalRadarDataGeneric::moment_data_block(DataBlockType type) const
 | |
| {
 | |
|    std::shared_ptr<MomentDataBlock> 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<char*>(&p->collectionTime_), 4);           // 4-7
 | |
|    is.read(reinterpret_cast<char*>(&p->modifiedJulianDate_), 2);       // 8-9
 | |
|    is.read(reinterpret_cast<char*>(&p->azimuthNumber_), 2);            // 10-11
 | |
|    is.read(reinterpret_cast<char*>(&p->azimuthAngle_), 4);             // 12-15
 | |
|    is.read(reinterpret_cast<char*>(&p->compressionIndicator_), 1);     // 16
 | |
|    is.seekg(1, std::ios_base::cur);                                    // 17
 | |
|    is.read(reinterpret_cast<char*>(&p->radialLength_), 2);             // 18-19
 | |
|    is.read(reinterpret_cast<char*>(&p->azimuthResolutionSpacing_), 1); // 20
 | |
|    is.read(reinterpret_cast<char*>(&p->radialStatus_), 1);             // 21
 | |
|    is.read(reinterpret_cast<char*>(&p->elevationNumber_), 1);          // 22
 | |
|    is.read(reinterpret_cast<char*>(&p->cutSectorNumber_), 1);          // 23
 | |
|    is.read(reinterpret_cast<char*>(&p->elevationAngle_), 4);           // 24-27
 | |
|    is.read(reinterpret_cast<char*>(&p->radialSpotBlankingStatus_), 1); // 28
 | |
|    is.read(reinterpret_cast<char*>(&p->azimuthIndexingMode_), 1);      // 29
 | |
|    is.read(reinterpret_cast<char*>(&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_);
 | |
| 
 | |
|    // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
 | |
| 
 | |
|    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;
 | |
|    }
 | |
| 
 | |
|    // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
 | |
| 
 | |
|    if (!messageValid)
 | |
|    {
 | |
|       p->dataBlockCount_ = 0;
 | |
|    }
 | |
| 
 | |
|    is.read(reinterpret_cast<char*>(&p->dataBlockPointer_),
 | |
|            static_cast<std::streamsize>(p->dataBlockCount_) * 4);
 | |
| 
 | |
|    SwapArray(p->dataBlockPointer_, p->dataBlockCount_);
 | |
| 
 | |
|    for (uint16_t b = 0; b < p->dataBlockCount_; ++b)
 | |
|    {
 | |
|       // Index already has bounds check
 | |
|       // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
 | |
|       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_ =
 | |
|             VolumeDataBlock::Create(dataBlockType, dataName, is);
 | |
|          break;
 | |
|       case DataBlockType::Elevation:
 | |
|          p->elevationDataBlock_ =
 | |
|             ElevationDataBlock::Create(dataBlockType, dataName, is);
 | |
|          break;
 | |
|       case DataBlockType::Radial:
 | |
|          p->radialDataBlock_ =
 | |
|             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] =
 | |
|             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>
 | |
| DigitalRadarDataGeneric::Create(Level2MessageHeader&& header, std::istream& is)
 | |
| {
 | |
|    std::shared_ptr<DigitalRadarDataGeneric> message =
 | |
|       std::make_shared<DigitalRadarDataGeneric>();
 | |
|    message->set_header(std::move(header));
 | |
| 
 | |
|    if (!message->Parse(is))
 | |
|    {
 | |
|       message.reset();
 | |
|    }
 | |
| 
 | |
|    return message;
 | |
| }
 | |
| 
 | |
| } // namespace scwx::wsr88d::rda
 | 
