use super::{ppmdropspeed::FromToUsize, ClipState, PPMDropSpeed, ALMOST_CLIPPED_REL_AMP};
use crate::config::*;
use crate::daq::StreamMetaData;
use crate::math::{max, maxabs, min};
use crate::rt::ppm::{
CLIP_INDICATOR_WAIT_S, LEVEL_THRESHOLD_FOR_HIGH_LEVEL, LEVEL_THRESHOLD_FOR_LOW_LEVEL,
PEAK_LEVEL_HOLD_TIME_S,
};
use ndarray::ArrayView1;
use std::time::Instant;
#[inline]
fn level(pwr: Flt) -> Flt {
10. * pwr.log10()
}
#[derive(Copy, Clone, Debug)]
pub struct PPMChannelStatus {
block_ctr: usize,
alpha: Flt,
range: (Flt, Flt),
abs_range_sq: Flt,
signal_pwr: Flt,
signal_peak_pwr: Flt,
pub signal_level: Flt,
pub peak_level: Flt,
pub clipstate: ClipState,
peak_hold_blocks: usize,
clip_hold_blocks: usize,
clip_moment: Option<usize>,
signal_peak_moment: usize,
}
impl PPMChannelStatus {
pub fn new(meta: &StreamMetaData, chno: usize, dropSpeed: PPMDropSpeed) -> Self {
let chmeta = &meta.channelInfo[chno];
let range = chmeta.range;
let dropspeed_val = dropSpeed.to_usize() as Flt; let pole = -dropspeed_val * Flt::log(10., 10.) / 10.;
let alpha = Flt::exp(meta.framesPerBlock as Flt * pole / meta.samplerate);
let abs_range = if range.0.abs() > range.1.abs() {
range.0.abs()
} else {
range.1.abs()
};
let clip_hold_blocks =
(CLIP_INDICATOR_WAIT_S * meta.samplerate / meta.framesPerBlock as Flt) as usize;
let peak_hold_blocks =
(PEAK_LEVEL_HOLD_TIME_S * meta.samplerate / meta.framesPerBlock as Flt) as usize;
Self {
alpha,
range,
abs_range_sq: abs_range.powi(2),
clip_hold_blocks,
peak_hold_blocks,
signal_level: -Flt::INFINITY,
peak_level: -Flt::INFINITY,
block_ctr: 0,
signal_pwr: 0.,
signal_peak_pwr: 0.,
clipstate: ClipState::LowLevel,
clip_moment: None,
signal_peak_moment: 0,
}
}
pub fn process<'a, T>(&mut self, chdata: T)
where
T: Into<ArrayView1<'a, Flt>>,
{
let chdata = chdata.into();
self.signal_pwr *= self.alpha;
if self.block_ctr > self.peak_hold_blocks + self.signal_peak_moment {
self.signal_peak_pwr *= self.alpha;
}
let min_val = min(chdata);
let max_val = max(chdata);
let this_block_pwr = {
let abs_min_pwr = min_val.powi(2);
let abs_max_pwr = max_val.powi(2);
if abs_max_pwr > abs_min_pwr {
abs_max_pwr
} else {
abs_min_pwr
}
};
if this_block_pwr > self.signal_pwr {
self.signal_pwr = this_block_pwr;
}
if this_block_pwr > self.signal_peak_pwr {
self.signal_peak_pwr = this_block_pwr;
self.signal_peak_moment = self.block_ctr;
}
self.peak_level = level(self.signal_peak_pwr / self.abs_range_sq);
self.signal_level = level(self.signal_pwr / self.abs_range_sq);
let hasclipped = min_val <= ALMOST_CLIPPED_REL_AMP * self.range.0
|| max_val >= ALMOST_CLIPPED_REL_AMP * self.range.1;
self.clipstate = if hasclipped {
self.clip_moment = Some(self.block_ctr);
ClipState::Clipped
} else if let Some(clip_moment) = self.clip_moment {
if self.block_ctr > self.clip_hold_blocks + clip_moment {
self.clip_moment = None;
}
ClipState::Clipped
} else {
let high_level = self.signal_level > LEVEL_THRESHOLD_FOR_HIGH_LEVEL;
let low_level = self.signal_level < LEVEL_THRESHOLD_FOR_LOW_LEVEL;
if high_level {
ClipState::HighLevel
} else if low_level {
ClipState::LowLevel
} else {
ClipState::LevelFine
}
};
self.block_ctr += 1;
}
}