mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 13:00:06 +00:00
Add data level codes, refactor data value determination to product description block
This commit is contained in:
parent
b67f546774
commit
2f06076bb5
5 changed files with 407 additions and 51 deletions
|
|
@ -7,6 +7,8 @@
|
||||||
#include <scwx/wsr88d/rpg/graphic_product_message.hpp>
|
#include <scwx/wsr88d/rpg/graphic_product_message.hpp>
|
||||||
#include <scwx/wsr88d/rpg/radial_data_packet.hpp>
|
#include <scwx/wsr88d/rpg/radial_data_packet.hpp>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include <boost/range/irange.hpp>
|
#include <boost/range/irange.hpp>
|
||||||
#include <boost/timer/timer.hpp>
|
#include <boost/timer/timer.hpp>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
@ -240,15 +242,22 @@ void Level3ProductView::UpdateColorTable()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t productCode = descriptionBlock->product_code();
|
std::int16_t productCode = descriptionBlock->product_code();
|
||||||
float offset = descriptionBlock->offset();
|
float offset = descriptionBlock->offset();
|
||||||
float scale = descriptionBlock->scale();
|
float scale = descriptionBlock->scale();
|
||||||
uint16_t threshold = descriptionBlock->threshold();
|
std::uint8_t threshold = static_cast<std::uint8_t>(
|
||||||
|
std::clamp<std::uint16_t>(descriptionBlock->threshold(),
|
||||||
|
std::numeric_limits<std::uint8_t>::min(),
|
||||||
|
std::numeric_limits<std::uint8_t>::max()));
|
||||||
|
|
||||||
// If the threshold is 2, the range min should be set to 1 for range folding
|
// If the threshold is 2, the range min should be set to 1 for range
|
||||||
uint16_t rangeMin = std::min<uint16_t>(1, threshold);
|
// folding
|
||||||
uint16_t numberOfLevels = descriptionBlock->number_of_levels();
|
std::uint8_t rangeMin = std::min<std::uint8_t>(1, threshold);
|
||||||
uint16_t rangeMax = (numberOfLevels > 0) ? numberOfLevels - 1 : 0;
|
std::uint16_t numberOfLevels = descriptionBlock->number_of_levels();
|
||||||
|
std::uint8_t rangeMax = static_cast<std::uint8_t>(
|
||||||
|
std::clamp<std::uint16_t>((numberOfLevels > 0) ? numberOfLevels - 1 : 0,
|
||||||
|
std::numeric_limits<std::uint8_t>::min(),
|
||||||
|
std::numeric_limits<std::uint8_t>::max()));
|
||||||
|
|
||||||
if (p->savedColorTable_ == p->colorTable_ && //
|
if (p->savedColorTable_ == p->colorTable_ && //
|
||||||
p->savedOffset_ == offset && //
|
p->savedOffset_ == offset && //
|
||||||
|
|
@ -274,7 +283,8 @@ void Level3ProductView::UpdateColorTable()
|
||||||
[&](uint16_t i)
|
[&](uint16_t i)
|
||||||
{
|
{
|
||||||
const size_t lutIndex = i - *dataRange.begin();
|
const size_t lutIndex = i - *dataRange.begin();
|
||||||
float f;
|
|
||||||
|
std::optional<float> f = descriptionBlock->data_value(i);
|
||||||
|
|
||||||
// Different products use different scale/offset formulas
|
// Different products use different scale/offset formulas
|
||||||
if (numberOfLevels > 16 || productCode == 34)
|
if (numberOfLevels > 16 || productCode == 34)
|
||||||
|
|
@ -285,28 +295,14 @@ void Level3ProductView::UpdateColorTable()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (descriptionBlock->product_code())
|
if (f.has_value())
|
||||||
{
|
{
|
||||||
case 159:
|
lut[lutIndex] = p->colorTable_->Color(f.value());
|
||||||
case 161:
|
}
|
||||||
case 163:
|
else
|
||||||
case 167:
|
{
|
||||||
case 168:
|
lut[lutIndex] = boost::gil::rgba8_pixel_t {0, 0, 0, 0};
|
||||||
case 170:
|
|
||||||
case 172:
|
|
||||||
case 173:
|
|
||||||
case 174:
|
|
||||||
case 175:
|
|
||||||
case 176:
|
|
||||||
f = (i - offset) / scale;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
f = i * scale + offset;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lut[lutIndex] = p->colorTable_->Color(f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -314,29 +310,15 @@ void Level3ProductView::UpdateColorTable()
|
||||||
uint16_t th = descriptionBlock->data_level_threshold(i);
|
uint16_t th = descriptionBlock->data_level_threshold(i);
|
||||||
if ((th & 0x8000u) == 0)
|
if ((th & 0x8000u) == 0)
|
||||||
{
|
{
|
||||||
float scaleFactor = 1.0f;
|
|
||||||
|
|
||||||
if (th & 0x4000u)
|
|
||||||
{
|
|
||||||
scaleFactor *= 0.01f;
|
|
||||||
}
|
|
||||||
if (th & 0x2000u)
|
|
||||||
{
|
|
||||||
scaleFactor *= 0.05f;
|
|
||||||
}
|
|
||||||
if (th & 0x1000u)
|
|
||||||
{
|
|
||||||
scaleFactor *= 0.1f;
|
|
||||||
}
|
|
||||||
if (th & 0x0100u)
|
|
||||||
{
|
|
||||||
scaleFactor *= -1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If bit 0 is zero, then the LSB is numeric
|
// If bit 0 is zero, then the LSB is numeric
|
||||||
f = static_cast<float>(th & 0x00ffu) * scaleFactor;
|
if (f.has_value())
|
||||||
|
{
|
||||||
lut[lutIndex] = p->colorTable_->Color(f);
|
lut[lutIndex] = p->colorTable_->Color(f.value());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lut[lutIndex] = boost::gil::rgba8_pixel_t {0, 0, 0, 0};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <scwx/awips/message.hpp>
|
#include <scwx/awips/message.hpp>
|
||||||
|
#include <scwx/wsr88d/rpg/rpg_types.hpp>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <units/angle.h>
|
#include <units/angle.h>
|
||||||
|
|
||||||
|
|
@ -61,6 +63,9 @@ public:
|
||||||
float scale() const;
|
float scale() const;
|
||||||
uint16_t number_of_levels() const;
|
uint16_t number_of_levels() const;
|
||||||
|
|
||||||
|
std::optional<DataLevelCode> data_level_code(std::uint8_t level) const;
|
||||||
|
std::optional<float> data_value(std::uint8_t level) const;
|
||||||
|
|
||||||
float log_offset() const;
|
float log_offset() const;
|
||||||
float log_scale() const;
|
float log_scale() const;
|
||||||
|
|
||||||
|
|
|
||||||
58
wxdata/include/scwx/wsr88d/rpg/rpg_types.hpp
Normal file
58
wxdata/include/scwx/wsr88d/rpg/rpg_types.hpp
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace wsr88d
|
||||||
|
{
|
||||||
|
namespace rpg
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class DataLevelCode
|
||||||
|
{
|
||||||
|
BadData,
|
||||||
|
BelowThreshold,
|
||||||
|
Blank,
|
||||||
|
ChaffDetection,
|
||||||
|
EditRemove,
|
||||||
|
FlaggedData,
|
||||||
|
Missing,
|
||||||
|
NoData,
|
||||||
|
OutsideCoverageArea,
|
||||||
|
NoAccumulation,
|
||||||
|
RangeFolded,
|
||||||
|
Reserved,
|
||||||
|
|
||||||
|
// Hydrometeor Classification
|
||||||
|
Biological,
|
||||||
|
AnomalousPropagationGroundClutter,
|
||||||
|
IceCrystals,
|
||||||
|
DrySnow,
|
||||||
|
WetSnow,
|
||||||
|
LightAndOrModerateRain,
|
||||||
|
HeavyRain,
|
||||||
|
BigDrops,
|
||||||
|
Graupel,
|
||||||
|
SmallHail,
|
||||||
|
LargeHail,
|
||||||
|
GiantHail,
|
||||||
|
UnknownClassification,
|
||||||
|
|
||||||
|
// Rainfall Rate Classification
|
||||||
|
NoPrecipitation,
|
||||||
|
Unfilled,
|
||||||
|
Convective,
|
||||||
|
Tropical,
|
||||||
|
SpecificAttenuation,
|
||||||
|
KL,
|
||||||
|
KH,
|
||||||
|
Z1,
|
||||||
|
Z6,
|
||||||
|
Z8,
|
||||||
|
SI,
|
||||||
|
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rpg
|
||||||
|
} // namespace wsr88d
|
||||||
|
} // namespace scwx
|
||||||
|
|
@ -705,6 +705,316 @@ bool ProductDescriptionBlock::Parse(std::istream& is)
|
||||||
return blockValid;
|
return blockValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<DataLevelCode>
|
||||||
|
ProductDescriptionBlock::data_level_code(std::uint8_t level) const
|
||||||
|
{
|
||||||
|
switch (p->productCode_)
|
||||||
|
{
|
||||||
|
case 32:
|
||||||
|
case 93:
|
||||||
|
case 94:
|
||||||
|
case 99:
|
||||||
|
case 153:
|
||||||
|
case 154:
|
||||||
|
case 155:
|
||||||
|
case 159:
|
||||||
|
case 161:
|
||||||
|
case 163:
|
||||||
|
case 167:
|
||||||
|
case 168:
|
||||||
|
case 195:
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return DataLevelCode::BelowThreshold;
|
||||||
|
case 1:
|
||||||
|
return DataLevelCode::RangeFolded;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 81:
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return DataLevelCode::NoAccumulation;
|
||||||
|
case 255:
|
||||||
|
return DataLevelCode::OutsideCoverageArea;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 134:
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return DataLevelCode::BelowThreshold;
|
||||||
|
case 1:
|
||||||
|
return DataLevelCode::FlaggedData;
|
||||||
|
case 255:
|
||||||
|
return DataLevelCode::Reserved;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 135:
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return DataLevelCode::BelowThreshold;
|
||||||
|
case 1:
|
||||||
|
return DataLevelCode::BadData;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 138:
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return DataLevelCode::NoAccumulation;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 165:
|
||||||
|
case 177:
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return DataLevelCode::BelowThreshold;
|
||||||
|
case 10:
|
||||||
|
return DataLevelCode::Biological;
|
||||||
|
case 20:
|
||||||
|
return DataLevelCode::AnomalousPropagationGroundClutter;
|
||||||
|
case 30:
|
||||||
|
return DataLevelCode::IceCrystals;
|
||||||
|
case 40:
|
||||||
|
return DataLevelCode::DrySnow;
|
||||||
|
case 50:
|
||||||
|
return DataLevelCode::WetSnow;
|
||||||
|
case 60:
|
||||||
|
return DataLevelCode::LightAndOrModerateRain;
|
||||||
|
case 70:
|
||||||
|
return DataLevelCode::HeavyRain;
|
||||||
|
case 80:
|
||||||
|
return DataLevelCode::BigDrops;
|
||||||
|
case 90:
|
||||||
|
return DataLevelCode::Graupel;
|
||||||
|
case 100:
|
||||||
|
return DataLevelCode::SmallHail;
|
||||||
|
case 110:
|
||||||
|
return DataLevelCode::LargeHail;
|
||||||
|
case 120:
|
||||||
|
return DataLevelCode::GiantHail;
|
||||||
|
case 140:
|
||||||
|
return DataLevelCode::UnknownClassification;
|
||||||
|
case 150:
|
||||||
|
return DataLevelCode::RangeFolded;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 170:
|
||||||
|
case 172:
|
||||||
|
case 173:
|
||||||
|
case 174:
|
||||||
|
case 175:
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return DataLevelCode::NoData;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 193:
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return DataLevelCode::BelowThreshold;
|
||||||
|
case 1:
|
||||||
|
return DataLevelCode::RangeFolded;
|
||||||
|
case 2:
|
||||||
|
return DataLevelCode::EditRemove;
|
||||||
|
case 254:
|
||||||
|
return DataLevelCode::ChaffDetection;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 197:
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return DataLevelCode::NoPrecipitation;
|
||||||
|
case 10:
|
||||||
|
return DataLevelCode::Unfilled;
|
||||||
|
case 20:
|
||||||
|
return DataLevelCode::Convective;
|
||||||
|
case 30:
|
||||||
|
return DataLevelCode::Tropical;
|
||||||
|
case 40:
|
||||||
|
return DataLevelCode::SpecificAttenuation;
|
||||||
|
case 50:
|
||||||
|
return DataLevelCode::KL;
|
||||||
|
case 60:
|
||||||
|
return DataLevelCode::KH;
|
||||||
|
case 70:
|
||||||
|
return DataLevelCode::Z1;
|
||||||
|
case 80:
|
||||||
|
return DataLevelCode::Z6;
|
||||||
|
case 90:
|
||||||
|
return DataLevelCode::Z8;
|
||||||
|
case 100:
|
||||||
|
return DataLevelCode::SI;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Different products use different scale/offset formulas
|
||||||
|
if (number_of_levels() <= 16 && p->productCode_ != 34)
|
||||||
|
{
|
||||||
|
uint16_t th = data_level_threshold(level);
|
||||||
|
if ((th & 0x8000u))
|
||||||
|
{
|
||||||
|
// If bit 0 is one, then the LSB is coded
|
||||||
|
uint16_t lsb = th & 0x00ffu;
|
||||||
|
|
||||||
|
switch (lsb)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return DataLevelCode::Blank;
|
||||||
|
case 1:
|
||||||
|
return DataLevelCode::BelowThreshold;
|
||||||
|
case 2:
|
||||||
|
return DataLevelCode::NoData;
|
||||||
|
case 3:
|
||||||
|
return DataLevelCode::RangeFolded;
|
||||||
|
case 4:
|
||||||
|
return DataLevelCode::Biological;
|
||||||
|
case 5:
|
||||||
|
return DataLevelCode::AnomalousPropagationGroundClutter;
|
||||||
|
case 6:
|
||||||
|
return DataLevelCode::IceCrystals;
|
||||||
|
case 7:
|
||||||
|
return DataLevelCode::Graupel;
|
||||||
|
case 8:
|
||||||
|
return DataLevelCode::WetSnow;
|
||||||
|
case 9:
|
||||||
|
return DataLevelCode::DrySnow;
|
||||||
|
case 10:
|
||||||
|
return DataLevelCode::LightAndOrModerateRain;
|
||||||
|
case 11:
|
||||||
|
return DataLevelCode::HeavyRain;
|
||||||
|
case 12:
|
||||||
|
return DataLevelCode::BigDrops;
|
||||||
|
case 13:
|
||||||
|
return DataLevelCode::SmallHail;
|
||||||
|
case 14:
|
||||||
|
return DataLevelCode::UnknownClassification;
|
||||||
|
case 15:
|
||||||
|
return DataLevelCode::LargeHail;
|
||||||
|
case 16:
|
||||||
|
return DataLevelCode::GiantHail;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<float>
|
||||||
|
ProductDescriptionBlock::data_value(std::uint8_t level) const
|
||||||
|
{
|
||||||
|
float dataOffset = offset();
|
||||||
|
float dataScale = scale();
|
||||||
|
std::uint16_t dataThreshold = threshold();
|
||||||
|
std::uint16_t numberOfLevels = number_of_levels();
|
||||||
|
|
||||||
|
if (level < dataThreshold)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
float f;
|
||||||
|
|
||||||
|
// Different products use different scale/offset formulas
|
||||||
|
if (numberOfLevels > 16 || p->productCode_ == 34)
|
||||||
|
{
|
||||||
|
switch (p->productCode_)
|
||||||
|
{
|
||||||
|
case 159:
|
||||||
|
case 161:
|
||||||
|
case 163:
|
||||||
|
case 167:
|
||||||
|
case 168:
|
||||||
|
case 170:
|
||||||
|
case 172:
|
||||||
|
case 173:
|
||||||
|
case 174:
|
||||||
|
case 175:
|
||||||
|
case 176:
|
||||||
|
f = (level - dataOffset) / dataScale;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
f = level * dataScale + dataOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::uint16_t th = data_level_threshold(level);
|
||||||
|
if ((th & 0x8000u) == 0)
|
||||||
|
{
|
||||||
|
float scaleFactor = 1.0f;
|
||||||
|
|
||||||
|
if (th & 0x4000u)
|
||||||
|
{
|
||||||
|
scaleFactor *= 0.01f;
|
||||||
|
}
|
||||||
|
if (th & 0x2000u)
|
||||||
|
{
|
||||||
|
scaleFactor *= 0.05f;
|
||||||
|
}
|
||||||
|
if (th & 0x1000u)
|
||||||
|
{
|
||||||
|
scaleFactor *= 0.1f;
|
||||||
|
}
|
||||||
|
if (th & 0x0100u)
|
||||||
|
{
|
||||||
|
scaleFactor *= -1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If bit 0 is zero, then the LSB is numeric
|
||||||
|
f = static_cast<float>(th & 0x00ffu) * scaleFactor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If bit 0 is one, then the LSB is coded
|
||||||
|
std::uint16_t lsb = th & 0x00ffu;
|
||||||
|
|
||||||
|
f = static_cast<float>(lsb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace rpg
|
} // namespace rpg
|
||||||
} // namespace wsr88d
|
} // namespace wsr88d
|
||||||
} // namespace scwx
|
} // namespace scwx
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@ set(HDR_WSR88D_RPG include/scwx/wsr88d/rpg/ccb_header.hpp
|
||||||
include/scwx/wsr88d/rpg/radar_coded_message.hpp
|
include/scwx/wsr88d/rpg/radar_coded_message.hpp
|
||||||
include/scwx/wsr88d/rpg/radial_data_packet.hpp
|
include/scwx/wsr88d/rpg/radial_data_packet.hpp
|
||||||
include/scwx/wsr88d/rpg/raster_data_packet.hpp
|
include/scwx/wsr88d/rpg/raster_data_packet.hpp
|
||||||
|
include/scwx/wsr88d/rpg/rpg_types.hpp
|
||||||
include/scwx/wsr88d/rpg/scit_forecast_data_packet.hpp
|
include/scwx/wsr88d/rpg/scit_forecast_data_packet.hpp
|
||||||
include/scwx/wsr88d/rpg/set_color_level_packet.hpp
|
include/scwx/wsr88d/rpg/set_color_level_packet.hpp
|
||||||
include/scwx/wsr88d/rpg/special_graphic_symbol_packet.hpp
|
include/scwx/wsr88d/rpg/special_graphic_symbol_packet.hpp
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue