diff --git a/scwx-qt/source/scwx/qt/view/level3_product_view.cpp b/scwx-qt/source/scwx/qt/view/level3_product_view.cpp index 099e3fac..a749ec18 100644 --- a/scwx-qt/source/scwx/qt/view/level3_product_view.cpp +++ b/scwx-qt/source/scwx/qt/view/level3_product_view.cpp @@ -56,8 +56,11 @@ public: uint16_t colorTableMax_; std::shared_ptr savedColorTable_; - float savedScale_; - float savedOffset_; + float savedScale_ {1.0f}; + float savedOffset_ {0.0f}; + std::uint16_t savedLogStart_ {20u}; + float savedLogScale_ {1.0f}; + float savedLogOffset_ {0.0f}; }; Level3ProductView::Level3ProductView( @@ -247,9 +250,12 @@ void Level3ProductView::UpdateColorTableLut() return; } - float offset = descriptionBlock->offset(); - float scale = descriptionBlock->scale(); - std::uint8_t threshold = static_cast( + float offset = descriptionBlock->offset(); + float scale = descriptionBlock->scale(); + float logOffset = descriptionBlock->log_offset(); + float logScale = descriptionBlock->log_scale(); + std::uint16_t logStart = descriptionBlock->log_start(); + std::uint8_t threshold = static_cast( std::clamp(descriptionBlock->threshold(), std::numeric_limits::min(), std::numeric_limits::max())); @@ -266,6 +272,9 @@ void Level3ProductView::UpdateColorTableLut() if (p->savedColorTable_ == p->colorTable_ && // p->savedOffset_ == offset && // p->savedScale_ == scale && // + p->savedLogOffset_ == logOffset && // + p->savedLogScale_ == logScale && // + p->savedLogStart_ == logStart && // numberOfLevels > 16) { // The color table LUT does not need updated @@ -335,6 +344,9 @@ void Level3ProductView::UpdateColorTableLut() p->savedColorTable_ = p->colorTable_; p->savedOffset_ = offset; p->savedScale_ = scale; + p->savedLogOffset_ = logOffset; + p->savedLogScale_ = logScale; + p->savedLogStart_ = logStart; Q_EMIT ColorTableLutUpdated(); } diff --git a/test/source/scwx/util/float.test.cpp b/test/source/scwx/util/float.test.cpp index f91fe4bf..aa4bfe7e 100644 --- a/test/source/scwx/util/float.test.cpp +++ b/test/source/scwx/util/float.test.cpp @@ -7,6 +7,11 @@ namespace scwx namespace util { +class Float16Test : + public testing ::TestWithParam> +{ +}; + TEST(FloatTest, Decode32Positive1) { uint16_t msw = 0x3f80; @@ -37,14 +42,26 @@ TEST(FloatTest, Decode32Positive12345678) EXPECT_FLOAT_EQ(x, 12345678.0f); } -TEST(FloatTest, Decode16h0x5bb4) +TEST_P(Float16Test, DecodeFloat16) { - uint16_t hex = 0x5bb4; + auto param = GetParam(); + + std::uint16_t hex = param.first; float x = DecodeFloat16(hex); - EXPECT_FLOAT_EQ(x, 123.25f); + EXPECT_FLOAT_EQ(x, param.second); } +INSTANTIATE_TEST_SUITE_P( + FloatTest, + Float16Test, + testing::Values(std::pair {0x4400, 2.0f}, + std::pair {0x59ab, 90.6875f}, + std::pair {0x593e, 83.875f}, + std::pair {0x54dc, 38.875f}, + std::pair {0xc82a, -4.1640625f}, + std::pair {0x5bb4, 123.25f})); + } // namespace util } // namespace scwx diff --git a/wxdata/include/scwx/common/products.hpp b/wxdata/include/scwx/common/products.hpp index ce1b12cb..b4e57a36 100644 --- a/wxdata/include/scwx/common/products.hpp +++ b/wxdata/include/scwx/common/products.hpp @@ -47,6 +47,7 @@ enum class Level3ProductCategory DifferentialReflectivity, SpecificDifferentialPhase, CorrelationCoefficient, + VerticallyIntegratedLiquid, HydrometeorClassification, Unknown }; diff --git a/wxdata/include/scwx/wsr88d/rpg/product_description_block.hpp b/wxdata/include/scwx/wsr88d/rpg/product_description_block.hpp index c67ccbf2..d7973026 100644 --- a/wxdata/include/scwx/wsr88d/rpg/product_description_block.hpp +++ b/wxdata/include/scwx/wsr88d/rpg/product_description_block.hpp @@ -66,8 +66,9 @@ public: std::optional data_level_code(std::uint8_t level) const; std::optional data_value(std::uint8_t level) const; - float log_offset() const; - float log_scale() const; + std::uint16_t log_start() const; + float log_offset() const; + float log_scale() const; units::angle::degrees elevation() const; diff --git a/wxdata/source/scwx/common/products.cpp b/wxdata/source/scwx/common/products.cpp index 87144c8d..cd472e4d 100644 --- a/wxdata/source/scwx/common/products.cpp +++ b/wxdata/source/scwx/common/products.cpp @@ -45,8 +45,10 @@ static const std::unordered_map level2Palette_ { static const std::unordered_map level3ProductCodeMap_ { {37, "NCR"}, {56, "SRM"}, + {57, "NVL"}, {94, "DR"}, {99, "DV"}, + {134, "DVL"}, {153, "SDR"}, {154, "SDV"}, {159, "DZD"}, @@ -68,12 +70,14 @@ static const std::unordered_map {"DZD", "Digital Differential Reflectivity"}, {"DCC", "Digital Correlation Coefficient"}, {"DKD", "Digital Specific Differential Phase"}, + {"DVL", "Digital Vertically Integrated Liquid"}, {"DHC", "Digital Hydrometeor Classification"}, {"HHC", "Hybrid Hydrometeor Classification"}, {"ML", "Melting Layer"}, {"SW", "Spectrum Width"}, {"TDR", "Digital Reflectivity"}, {"TDV", "Digital Velocity"}, + {"VIL", "Vertically Integrated Liquid"}, {"?", "Unknown"}}; static const std::unordered_map> @@ -104,6 +108,10 @@ static const std::unordered_map> // Specific Differential Phase {"DKD", {"NXK", "NYK", "NZK", "N0K", "NAK", "N1K", "NBK", "N2K", "N3K"}}, + // Vertically Integrated Liquid + {"DVL", {"DVL"}}, + {"VIL", {"NVL"}}, + // Hydrometeor Classification {"DHC", {"NXH", "NYH", "NZH", "N0H", "NAH", "N1H", "NBH", "N2H", "N3H"}}, {"HHC", {"HHC"}}, @@ -123,6 +131,7 @@ static const std::unordered_map {Level3ProductCategory::DifferentialReflectivity, "ZDR"}, {Level3ProductCategory::SpecificDifferentialPhase, "KDP"}, {Level3ProductCategory::CorrelationCoefficient, "CC"}, + {Level3ProductCategory::VerticallyIntegratedLiquid, "VIL"}, {Level3ProductCategory::HydrometeorClassification, "HC"}, {Level3ProductCategory::Unknown, "?"}}; @@ -138,6 +147,8 @@ static const std::unordered_map "Specific Differential Phase"}, {Level3ProductCategory::CorrelationCoefficient, "Correlation Coefficient"}, + {Level3ProductCategory::VerticallyIntegratedLiquid, + "Vertically Integrated Liquid"}, {Level3ProductCategory::HydrometeorClassification, "Hydrometeor Classification"}, {Level3ProductCategory::Unknown, "?"}}; @@ -151,6 +162,7 @@ static const std::unordered_map> {Level3ProductCategory::DifferentialReflectivity, {"DZD"}}, {Level3ProductCategory::SpecificDifferentialPhase, {"DKD"}}, {Level3ProductCategory::CorrelationCoefficient, {"DCC"}}, + {Level3ProductCategory::VerticallyIntegratedLiquid, {"DVL", "VIL"}}, {Level3ProductCategory::HydrometeorClassification, {"DHC", "HHC"}}, {Level3ProductCategory::Unknown, {}}}; @@ -163,6 +175,7 @@ static const std::unordered_map {Level3ProductCategory::DifferentialReflectivity, "N0X"}, {Level3ProductCategory::SpecificDifferentialPhase, "N0K"}, {Level3ProductCategory::CorrelationCoefficient, "N0C"}, + {Level3ProductCategory::VerticallyIntegratedLiquid, "DVL"}, {Level3ProductCategory::HydrometeorClassification, "N0H"}}; static const std::unordered_map level3Palette_ { diff --git a/wxdata/source/scwx/util/float.cpp b/wxdata/source/scwx/util/float.cpp index cee8ae66..3aea2dd6 100644 --- a/wxdata/source/scwx/util/float.cpp +++ b/wxdata/source/scwx/util/float.cpp @@ -19,7 +19,7 @@ float DecodeFloat16(std::uint16_t hex) static constexpr std::uint16_t S_MASK = 0x8000; static constexpr std::uint16_t S_LSB = 0; static constexpr std::uint16_t S_SHIFT = 15 - S_LSB; - static constexpr std::uint16_t E_MASK = 0x7a00; + static constexpr std::uint16_t E_MASK = 0x7c00; static constexpr std::uint16_t E_LSB = 5; static constexpr std::uint16_t E_SHIFT = 15 - E_LSB; static constexpr std::uint16_t F_MASK = 0x03ff; diff --git a/wxdata/source/scwx/wsr88d/rpg/product_description_block.cpp b/wxdata/source/scwx/wsr88d/rpg/product_description_block.cpp index 90385e88..4bcd24fb 100644 --- a/wxdata/source/scwx/wsr88d/rpg/product_description_block.cpp +++ b/wxdata/source/scwx/wsr88d/rpg/product_description_block.cpp @@ -591,6 +591,20 @@ uint16_t ProductDescriptionBlock::number_of_levels() const return numberOfLevels; } +std::uint16_t ProductDescriptionBlock::log_start() const +{ + std::uint16_t logStart = std::numeric_limits::max(); + + switch (p->productCode_) + { + case 134: + logStart = p->halfword(33); + break; + } + + return logStart; +} + float ProductDescriptionBlock::log_offset() const { float logOffset = 0.0f; @@ -598,7 +612,7 @@ float ProductDescriptionBlock::log_offset() const switch (p->productCode_) { case 134: - logOffset = util ::DecodeFloat16(p->halfword(35)); + logOffset = util::DecodeFloat16(p->halfword(35)); break; } @@ -612,7 +626,7 @@ float ProductDescriptionBlock::log_scale() const switch (p->productCode_) { case 134: - logScale = util ::DecodeFloat16(p->halfword(34)); + logScale = util::DecodeFloat16(p->halfword(34)); break; } @@ -1007,6 +1021,17 @@ ProductDescriptionBlock::data_value(std::uint8_t level) const f = (level - dataOffset) / dataScale; break; + case 134: + if (level < log_start()) + { + f = (level - dataOffset) / dataScale; + } + else + { + f = expf((level - log_offset()) / log_scale()); + } + break; + default: f = level * dataScale + dataOffset; break;