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/radial_data_packet.hpp>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <boost/range/irange.hpp>
|
||||
#include <boost/timer/timer.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
|
@ -240,15 +242,22 @@ void Level3ProductView::UpdateColorTable()
|
|||
return;
|
||||
}
|
||||
|
||||
int16_t productCode = descriptionBlock->product_code();
|
||||
float offset = descriptionBlock->offset();
|
||||
float scale = descriptionBlock->scale();
|
||||
uint16_t threshold = descriptionBlock->threshold();
|
||||
std::int16_t productCode = descriptionBlock->product_code();
|
||||
float offset = descriptionBlock->offset();
|
||||
float scale = descriptionBlock->scale();
|
||||
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
|
||||
uint16_t rangeMin = std::min<uint16_t>(1, threshold);
|
||||
uint16_t numberOfLevels = descriptionBlock->number_of_levels();
|
||||
uint16_t rangeMax = (numberOfLevels > 0) ? numberOfLevels - 1 : 0;
|
||||
// If the threshold is 2, the range min should be set to 1 for range
|
||||
// folding
|
||||
std::uint8_t rangeMin = std::min<std::uint8_t>(1, threshold);
|
||||
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_ && //
|
||||
p->savedOffset_ == offset && //
|
||||
|
|
@ -274,7 +283,8 @@ void Level3ProductView::UpdateColorTable()
|
|||
[&](uint16_t i)
|
||||
{
|
||||
const size_t lutIndex = i - *dataRange.begin();
|
||||
float f;
|
||||
|
||||
std::optional<float> f = descriptionBlock->data_value(i);
|
||||
|
||||
// Different products use different scale/offset formulas
|
||||
if (numberOfLevels > 16 || productCode == 34)
|
||||
|
|
@ -285,28 +295,14 @@ void Level3ProductView::UpdateColorTable()
|
|||
}
|
||||
else
|
||||
{
|
||||
switch (descriptionBlock->product_code())
|
||||
if (f.has_value())
|
||||
{
|
||||
case 159:
|
||||
case 161:
|
||||
case 163:
|
||||
case 167:
|
||||
case 168:
|
||||
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.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
lut[lutIndex] = boost::gil::rgba8_pixel_t {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
lut[lutIndex] = p->colorTable_->Color(f);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -314,29 +310,15 @@ void Level3ProductView::UpdateColorTable()
|
|||
uint16_t th = descriptionBlock->data_level_threshold(i);
|
||||
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;
|
||||
|
||||
lut[lutIndex] = p->colorTable_->Color(f);
|
||||
if (f.has_value())
|
||||
{
|
||||
lut[lutIndex] = p->colorTable_->Color(f.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
lut[lutIndex] = boost::gil::rgba8_pixel_t {0, 0, 0, 0};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <scwx/awips/message.hpp>
|
||||
#include <scwx/wsr88d/rpg/rpg_types.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <units/angle.h>
|
||||
|
||||
|
|
@ -61,6 +63,9 @@ public:
|
|||
float scale() 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_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;
|
||||
}
|
||||
|
||||
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 wsr88d
|
||||
} // 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/radial_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/set_color_level_packet.hpp
|
||||
include/scwx/wsr88d/rpg/special_graphic_symbol_packet.hpp
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue