supercell-wx/wxdata/source/scwx/wsr88d/rda/volume_coverage_pattern_data.cpp
2022-04-16 08:12:49 -05:00

544 lines
16 KiB
C++

#include <scwx/wsr88d/rda/volume_coverage_pattern_data.hpp>
#include <scwx/util/logger.hpp>
namespace scwx
{
namespace wsr88d
{
namespace rda
{
static const std::string logPrefix_ =
"scwx::wsr88d::rda::volume_coverage_pattern_data";
static const auto logger_ = util::Logger::Create(logPrefix_);
struct Sector;
static void ReadSector(std::istream& is, Sector& s);
static void SwapSector(Sector& s);
struct Sector
{
uint16_t edgeAngle_;
uint16_t dopplerPrfNumber_;
uint16_t dopplerPrfPulseCountRadial_;
Sector() :
edgeAngle_ {0}, dopplerPrfNumber_ {0}, dopplerPrfPulseCountRadial_ {0}
{
}
};
struct ElevationCut
{
uint16_t elevationAngle_;
uint8_t channelConfiguration_;
uint8_t waveformType_;
uint8_t superResolutionControl_;
uint8_t surveillancePrfNumber_;
uint16_t surveillancePrfPulseCountRadial_;
int16_t azimuthRate_;
uint16_t reflectivityThreshold_;
uint16_t velocityThreshold_;
uint16_t spectrumWidthThreshold_;
uint16_t differentialReflectivityThreshold_;
uint16_t differentialPhaseThreshold_;
uint16_t correlationCoefficientThreshold_;
std::array<Sector, 3> sector_;
uint16_t supplementalData_;
uint16_t ebcAngle_;
ElevationCut() :
elevationAngle_ {0},
channelConfiguration_ {0},
waveformType_ {0},
superResolutionControl_ {0},
surveillancePrfNumber_ {0},
surveillancePrfPulseCountRadial_ {0},
azimuthRate_ {0},
reflectivityThreshold_ {0},
velocityThreshold_ {0},
spectrumWidthThreshold_ {0},
differentialReflectivityThreshold_ {0},
differentialPhaseThreshold_ {0},
correlationCoefficientThreshold_ {0},
sector_(),
supplementalData_ {0},
ebcAngle_ {0}
{
}
};
class VolumeCoveragePatternDataImpl
{
public:
explicit VolumeCoveragePatternDataImpl() :
patternType_ {0},
patternNumber_ {0},
version_ {0},
clutterMapGroupNumber_ {0},
dopplerVelocityResolution_ {0},
pulseWidth_ {0},
vcpSequencing_ {0},
vcpSupplementalData_ {0},
elevationCuts_() {};
~VolumeCoveragePatternDataImpl() = default;
uint16_t patternType_;
uint16_t patternNumber_;
uint8_t version_;
uint8_t clutterMapGroupNumber_;
uint8_t dopplerVelocityResolution_;
uint8_t pulseWidth_;
uint16_t vcpSequencing_;
uint16_t vcpSupplementalData_;
std::vector<ElevationCut> elevationCuts_;
};
VolumeCoveragePatternData::VolumeCoveragePatternData() :
Level2Message(), p(std::make_unique<VolumeCoveragePatternDataImpl>())
{
}
VolumeCoveragePatternData::~VolumeCoveragePatternData() = default;
VolumeCoveragePatternData::VolumeCoveragePatternData(
VolumeCoveragePatternData&&) noexcept = default;
VolumeCoveragePatternData& VolumeCoveragePatternData::operator=(
VolumeCoveragePatternData&&) noexcept = default;
uint16_t VolumeCoveragePatternData::pattern_type() const
{
return p->patternType_;
}
uint16_t VolumeCoveragePatternData::pattern_number() const
{
return p->patternNumber_;
}
uint16_t VolumeCoveragePatternData::number_of_elevation_cuts() const
{
return static_cast<uint16_t>(p->elevationCuts_.size());
}
uint8_t VolumeCoveragePatternData::version() const
{
return p->version_;
}
uint8_t VolumeCoveragePatternData::clutter_map_group_number() const
{
return p->clutterMapGroupNumber_;
}
float VolumeCoveragePatternData::doppler_velocity_resolution() const
{
float resolution = 0.0f;
switch (p->dopplerVelocityResolution_)
{
case 2:
resolution = 0.5f;
break;
case 4:
resolution = 1.0f;
break;
}
return resolution;
}
uint8_t VolumeCoveragePatternData::pulse_width() const
{
return p->pulseWidth_;
}
uint16_t VolumeCoveragePatternData::vcp_sequencing() const
{
return p->vcpSequencing_;
}
uint16_t VolumeCoveragePatternData::number_of_elevations() const
{
return p->vcpSequencing_ & 0x001f;
}
uint16_t VolumeCoveragePatternData::maximum_sails_cuts() const
{
return (p->vcpSequencing_ & 0x0060) >> 5;
}
bool VolumeCoveragePatternData::sequence_active() const
{
return p->vcpSequencing_ & 0x2000;
}
bool VolumeCoveragePatternData::truncated_vcp() const
{
return p->vcpSequencing_ & 0x4000;
}
uint16_t VolumeCoveragePatternData::vcp_supplemental_data() const
{
return p->vcpSupplementalData_;
}
bool VolumeCoveragePatternData::sails_vcp() const
{
return p->vcpSupplementalData_ & 0x0001;
}
uint16_t VolumeCoveragePatternData::number_of_sails_cuts() const
{
return (p->vcpSupplementalData_ & 0x000E) >> 1;
}
bool VolumeCoveragePatternData::mrle_vcp() const
{
return p->vcpSupplementalData_ & 0x0010;
}
uint16_t VolumeCoveragePatternData::number_of_mrle_cuts() const
{
return (p->vcpSupplementalData_ & 0x00E0) >> 5;
}
bool VolumeCoveragePatternData::mpda_vcp() const
{
return p->vcpSupplementalData_ & 0x0800;
}
bool VolumeCoveragePatternData::base_tilt_vcp() const
{
return p->vcpSupplementalData_ & 0x1000;
}
uint16_t VolumeCoveragePatternData::number_of_base_tilts() const
{
return (p->vcpSupplementalData_ & 0xE000) >> 13;
}
double VolumeCoveragePatternData::elevation_angle(uint16_t e) const
{
return p->elevationCuts_[e].elevationAngle_ * ANGLE_DATA_SCALE;
}
uint16_t VolumeCoveragePatternData::elevation_angle_raw(uint16_t e) const
{
return p->elevationCuts_[e].elevationAngle_;
}
uint8_t VolumeCoveragePatternData::channel_configuration(uint16_t e) const
{
return p->elevationCuts_[e].channelConfiguration_;
}
WaveformType VolumeCoveragePatternData::waveform_type(uint16_t e) const
{
switch (p->elevationCuts_[e].waveformType_)
{
case 1:
return WaveformType::ContiguousSurveillance;
case 2:
return WaveformType::ContiguousDopplerWithAmbiguityResolution;
case 3:
return WaveformType::ContiguousDopplerWithoutAmbiguityResolution;
case 4:
return WaveformType::Batch;
case 5:
return WaveformType::StaggeredPulsePair;
default:
return WaveformType::Unknown;
}
}
uint8_t VolumeCoveragePatternData::waveform_type_raw(uint16_t e) const
{
return p->elevationCuts_[e].waveformType_;
}
uint8_t VolumeCoveragePatternData::super_resolution_control(uint16_t e) const
{
return p->elevationCuts_[e].superResolutionControl_;
}
bool VolumeCoveragePatternData::half_degree_azimuth(uint16_t e) const
{
return p->elevationCuts_[e].superResolutionControl_ & 0x01;
}
bool VolumeCoveragePatternData::quarter_km_reflectivity(uint16_t e) const
{
return p->elevationCuts_[e].superResolutionControl_ & 0x02;
}
bool VolumeCoveragePatternData::doppler_to_300km(uint16_t e) const
{
return p->elevationCuts_[e].superResolutionControl_ & 0x04;
}
bool VolumeCoveragePatternData::dual_polarization_to_300km(uint16_t e) const
{
return p->elevationCuts_[e].superResolutionControl_ & 0x08;
}
uint8_t VolumeCoveragePatternData::surveillance_prf_number(uint16_t e) const
{
return p->elevationCuts_[e].surveillancePrfNumber_;
}
uint16_t
VolumeCoveragePatternData::surveillance_prf_pulse_count_radial(uint16_t e) const
{
return p->elevationCuts_[e].surveillancePrfPulseCountRadial_;
}
double VolumeCoveragePatternData::azimuth_rate(uint16_t e) const
{
return p->elevationCuts_[e].azimuthRate_ * AZ_EL_RATE_DATA_SCALE;
}
float VolumeCoveragePatternData::reflectivity_threshold(uint16_t e) const
{
return p->elevationCuts_[e].reflectivityThreshold_ * 0.125f;
}
float VolumeCoveragePatternData::velocity_threshold(uint16_t e) const
{
return p->elevationCuts_[e].velocityThreshold_ * 0.125f;
}
float VolumeCoveragePatternData::spectrum_width_threshold(uint16_t e) const
{
return p->elevationCuts_[e].spectrumWidthThreshold_ * 0.125f;
}
float VolumeCoveragePatternData::differential_reflectivity_threshold(
uint16_t e) const
{
return p->elevationCuts_[e].differentialReflectivityThreshold_ * 0.125f;
}
float VolumeCoveragePatternData::differential_phase_threshold(uint16_t e) const
{
return p->elevationCuts_[e].differentialPhaseThreshold_ * 0.125f;
}
float VolumeCoveragePatternData::correlation_coefficient_threshold(
uint16_t e) const
{
return p->elevationCuts_[e].correlationCoefficientThreshold_ * 0.125f;
}
uint16_t VolumeCoveragePatternData::supplemental_data(uint16_t e) const
{
return p->elevationCuts_[e].supplementalData_;
}
bool VolumeCoveragePatternData::sails_cut(uint16_t e) const
{
return p->elevationCuts_[e].supplementalData_ & 0x0001;
}
uint16_t VolumeCoveragePatternData::sails_sequence_number(uint16_t e) const
{
return (p->elevationCuts_[e].supplementalData_ & 0x000E) >> 1;
}
bool VolumeCoveragePatternData::mrle_cut(uint16_t e) const
{
return p->elevationCuts_[e].supplementalData_ & 0x0010;
}
uint16_t VolumeCoveragePatternData::mrle_sequence_number(uint16_t e) const
{
return (p->elevationCuts_[e].supplementalData_ & 0x00E0) >> 5;
}
bool VolumeCoveragePatternData::mpda_cut(uint16_t e) const
{
return p->elevationCuts_[e].supplementalData_ & 0x0200;
}
bool VolumeCoveragePatternData::base_tilt_cut(uint16_t e) const
{
return p->elevationCuts_[e].supplementalData_ & 0x0400;
}
double VolumeCoveragePatternData::ebc_angle(uint16_t e) const
{
return p->elevationCuts_[e].ebcAngle_ * ANGLE_DATA_SCALE;
}
double VolumeCoveragePatternData::edge_angle(uint16_t e, uint16_t s) const
{
return p->elevationCuts_[e].sector_[s].edgeAngle_ * ANGLE_DATA_SCALE;
}
uint16_t VolumeCoveragePatternData::doppler_prf_number(uint16_t e,
uint16_t s) const
{
return p->elevationCuts_[e].sector_[s].dopplerPrfNumber_;
}
uint16_t
VolumeCoveragePatternData::doppler_prf_pulse_count_radial(uint16_t e,
uint16_t s) const
{
return p->elevationCuts_[e].sector_[s].dopplerPrfPulseCountRadial_;
}
bool VolumeCoveragePatternData::Parse(std::istream& is)
{
logger_->trace("Parsing Volume Coverage Pattern Data (Message Type 5)");
bool messageValid = true;
size_t bytesRead = 0;
uint16_t messageSize = 0;
uint16_t numberOfElevationCuts = 0;
is.read(reinterpret_cast<char*>(&messageSize), 2); // 1
is.read(reinterpret_cast<char*>(&p->patternType_), 2); // 2
is.read(reinterpret_cast<char*>(&p->patternNumber_), 2); // 3
is.read(reinterpret_cast<char*>(&numberOfElevationCuts), 2); // 4
is.read(reinterpret_cast<char*>(&p->version_), 1); // 5
is.read(reinterpret_cast<char*>(&p->clutterMapGroupNumber_), 1); // 5
is.read(reinterpret_cast<char*>(&p->dopplerVelocityResolution_), 1); // 6
is.read(reinterpret_cast<char*>(&p->pulseWidth_), 1); // 6
is.seekg(4, std::ios_base::cur); // 7-8
is.read(reinterpret_cast<char*>(&p->vcpSequencing_), 2); // 9
is.read(reinterpret_cast<char*>(&p->vcpSupplementalData_), 2); // 10
is.seekg(2, std::ios_base::cur); // 11
bytesRead += 22;
messageSize = ntohs(messageSize);
p->patternType_ = ntohs(p->patternType_);
p->patternNumber_ = ntohs(p->patternNumber_);
numberOfElevationCuts = ntohs(numberOfElevationCuts);
p->vcpSequencing_ = ntohs(p->vcpSequencing_);
p->vcpSupplementalData_ = ntohs(p->vcpSupplementalData_);
if (messageSize < 34 || messageSize > 747)
{
logger_->warn("Invalid message size: {}", messageSize);
messageValid = false;
}
if (numberOfElevationCuts < 1 || numberOfElevationCuts > 32)
{
logger_->warn("Invalid number of elevation cuts: {}",
numberOfElevationCuts);
messageValid = false;
}
if (!messageValid)
{
numberOfElevationCuts = 0;
}
p->elevationCuts_.resize(numberOfElevationCuts);
for (uint16_t e = 0; e < numberOfElevationCuts; ++e)
{
ElevationCut& c = p->elevationCuts_[e];
is.read(reinterpret_cast<char*>(&c.elevationAngle_), 2); // E1
is.read(reinterpret_cast<char*>(&c.channelConfiguration_), 1); // E2
is.read(reinterpret_cast<char*>(&c.waveformType_), 1); // E2
is.read(reinterpret_cast<char*>(&c.superResolutionControl_), 1); // E3
is.read(reinterpret_cast<char*>(&c.surveillancePrfNumber_), 1); // E3
is.read(reinterpret_cast<char*>(&c.surveillancePrfPulseCountRadial_),
2); // E4
is.read(reinterpret_cast<char*>(&c.azimuthRate_), 2); // E5
is.read(reinterpret_cast<char*>(&c.reflectivityThreshold_), 2); // E6
is.read(reinterpret_cast<char*>(&c.velocityThreshold_), 2); // E7
is.read(reinterpret_cast<char*>(&c.spectrumWidthThreshold_), 2); // E8
is.read(reinterpret_cast<char*>(&c.differentialReflectivityThreshold_),
2); // E9
is.read(reinterpret_cast<char*>(&c.differentialPhaseThreshold_),
2); // E10
is.read(reinterpret_cast<char*>(&c.correlationCoefficientThreshold_),
2); // E11
ReadSector(is, c.sector_[0]); // E12-E14
is.read(reinterpret_cast<char*>(&c.supplementalData_), 2); // E15
ReadSector(is, c.sector_[1]); // E16-E18
is.read(reinterpret_cast<char*>(&c.ebcAngle_), 2); // E19
ReadSector(is, c.sector_[2]); // E20-E22
is.seekg(2, std::ios_base::cur); // E23
bytesRead += 46;
c.elevationAngle_ = ntohs(c.elevationAngle_);
c.surveillancePrfPulseCountRadial_ =
ntohs(c.surveillancePrfPulseCountRadial_);
c.azimuthRate_ = ntohs(c.azimuthRate_);
c.reflectivityThreshold_ = ntohs(c.reflectivityThreshold_);
c.velocityThreshold_ = ntohs(c.velocityThreshold_);
c.spectrumWidthThreshold_ = ntohs(c.spectrumWidthThreshold_);
c.differentialReflectivityThreshold_ =
ntohs(c.differentialReflectivityThreshold_);
c.differentialPhaseThreshold_ = ntohs(c.differentialPhaseThreshold_);
c.correlationCoefficientThreshold_ =
ntohs(c.correlationCoefficientThreshold_);
c.supplementalData_ = ntohs(c.supplementalData_);
c.ebcAngle_ = ntohs(c.ebcAngle_);
for (size_t s = 0; s < c.sector_.size(); s++)
{
SwapSector(c.sector_[s]);
}
}
if (messageValid && bytesRead != messageSize * 2)
{
logger_->warn("Bytes read ({}) not equal to message size ({})",
bytesRead,
messageSize * 2);
}
if (!ValidateMessage(is, bytesRead))
{
messageValid = false;
}
if (!messageValid)
{
p->elevationCuts_.resize(0);
p->elevationCuts_.shrink_to_fit();
}
return messageValid;
}
std::shared_ptr<VolumeCoveragePatternData>
VolumeCoveragePatternData::Create(Level2MessageHeader&& header,
std::istream& is)
{
std::shared_ptr<VolumeCoveragePatternData> message =
std::make_shared<VolumeCoveragePatternData>();
message->set_header(std::move(header));
if (!message->Parse(is))
{
message.reset();
}
return message;
}
static void ReadSector(std::istream& is, Sector& s)
{
is.read(reinterpret_cast<char*>(&s.edgeAngle_), 2); // S1
is.read(reinterpret_cast<char*>(&s.dopplerPrfNumber_), 2); // S2
is.read(reinterpret_cast<char*>(&s.dopplerPrfPulseCountRadial_), 2); // S3
}
static void SwapSector(Sector& s)
{
s.edgeAngle_ = ntohs(s.edgeAngle_);
s.dopplerPrfNumber_ = ntohs(s.dopplerPrfNumber_);
s.dopplerPrfPulseCountRadial_ = ntohs(s.dopplerPrfPulseCountRadial_);
}
} // namespace rda
} // namespace wsr88d
} // namespace scwx