use crate::messages::digital_radar_data::{ControlFlags, DataBlockId, ScaledMomentValue};
use crate::messages::primitive_aliases::{
Code1, Integer1, Integer2, Integer4, Real4, ScaledInteger2,
};
use serde::Deserialize;
use std::fmt::Debug;
#[cfg(feature = "uom")]
use uom::si::f64::{Information, Length};
#[cfg(feature = "uom")]
use uom::si::information::byte;
#[cfg(feature = "uom")]
use uom::si::length::kilometer;
#[derive(Clone, PartialEq)]
pub struct GenericDataBlock {
pub header: GenericDataBlockHeader,
pub encoded_data: Vec<u8>,
}
impl GenericDataBlock {
pub(crate) fn new(header: GenericDataBlockHeader) -> Self {
let word_size_bytes = header.data_word_size as usize / 8;
let encoded_data_size = header.number_of_data_moment_gates as usize * word_size_bytes;
Self {
encoded_data: vec![0; encoded_data_size],
header,
}
}
pub fn encoded_values(&self) -> &[u8] {
&self.encoded_data
}
pub fn decoded_values(&self) -> Vec<ScaledMomentValue> {
self.encoded_data
.iter()
.copied()
.map(|raw_value| {
if self.header.scale == 0.0 {
return ScaledMomentValue::Value(raw_value as f32);
}
match raw_value {
0 => ScaledMomentValue::BelowThreshold,
1 => ScaledMomentValue::RangeFolded,
_ => ScaledMomentValue::Value(
(raw_value as f32 - self.header.offset) / self.header.scale,
),
}
})
.collect()
}
#[cfg(feature = "nexrad-model")]
pub fn moment_data(&self) -> nexrad_model::data::MomentData {
nexrad_model::data::MomentData::from_fixed_point(
self.header.scale,
self.header.offset,
self.encoded_data.clone(),
)
}
#[cfg(feature = "nexrad-model")]
pub fn into_moment_data(self) -> nexrad_model::data::MomentData {
nexrad_model::data::MomentData::from_fixed_point(
self.header.scale,
self.header.offset,
self.encoded_data,
)
}
}
impl Debug for GenericDataBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GenericDataBlock")
.field("header", &self.header)
.field("data", &self.encoded_data.len())
.finish()
}
}
#[derive(Clone, PartialEq, Deserialize)]
pub struct GenericDataBlockHeader {
pub data_block_id: DataBlockId,
pub reserved: Integer4,
pub number_of_data_moment_gates: Integer2,
pub data_moment_range: ScaledInteger2,
pub data_moment_range_sample_interval: ScaledInteger2,
pub tover: ScaledInteger2,
pub snr_threshold: ScaledInteger2,
pub control_flags: Code1,
pub data_word_size: Integer1,
pub scale: Real4,
pub offset: Real4,
}
impl GenericDataBlockHeader {
#[cfg(feature = "uom")]
pub fn data_moment_range(&self) -> Length {
Length::new::<kilometer>(self.data_moment_range as f64 * 0.001)
}
#[cfg(feature = "uom")]
pub fn data_moment_range_sample_interval(&self) -> Length {
Length::new::<kilometer>(self.data_moment_range_sample_interval as f64 * 0.001)
}
pub fn control_flags(&self) -> ControlFlags {
match self.control_flags {
0 => ControlFlags::None,
1 => ControlFlags::RecombinedAzimuthalRadials,
2 => ControlFlags::RecombinedRangeGates,
3 => ControlFlags::RecombinedRadialsAndRangeGatesToLegacyResolution,
_ => panic!("Invalid control flag value: {}", self.control_flags),
}
}
#[cfg(feature = "uom")]
pub fn moment_size(&self) -> Information {
Information::new::<byte>(
self.number_of_data_moment_gates as f64 * self.data_word_size as f64 / 8.0,
)
}
}
#[cfg(not(feature = "uom"))]
impl Debug for GenericDataBlockHeader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GenericDataBlockHeader")
.field("data_block_id", &self.data_block_id)
.field("reserved", &self.reserved)
.field(
"number_of_data_moment_gates",
&self.number_of_data_moment_gates,
)
.field("data_moment_range", &self.data_moment_range)
.field(
"data_moment_range_sample_interval",
&self.data_moment_range_sample_interval,
)
.field("tover", &self.tover)
.field("snr_threshold", &self.snr_threshold)
.field("control_flags", &self.control_flags())
.field("data_word_size", &self.data_word_size)
.field("scale", &self.scale)
.field("offset", &self.offset)
.finish()
}
}
#[cfg(feature = "uom")]
impl Debug for GenericDataBlockHeader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GenericDataBlockHeader")
.field("data_block_id", &self.data_block_id)
.field("reserved", &self.reserved)
.field(
"number_of_data_moment_gates",
&self.number_of_data_moment_gates,
)
.field("data_moment_range", &self.data_moment_range())
.field(
"data_moment_range_sample_interval",
&self.data_moment_range_sample_interval(),
)
.field("tover", &self.tover)
.field("snr_threshold", &self.snr_threshold)
.field("control_flags", &self.control_flags())
.field("data_word_size", &self.data_word_size)
.field("scale", &self.scale)
.field("offset", &self.offset)
.finish()
}
}