use std::borrow::Borrow;
use std::marker::PhantomData;
use num_traits::{float::FloatConst, Float, ToPrimitive};
use crate::{Continuous, Discrete, TransferFunction};
use super::{FrequencyCharacteristics, FrequencyTransferFunction, FrequencyUnit, Hz};
pub struct BodeDiagramPlotter<T, U = Hz> {
freq_from: T,
dfreq: T,
data_len: usize,
log_scale: bool,
unwrap_phase: bool,
_unit: PhantomData<U>,
}
impl <T, U> BodeDiagramPlotter<T, U>
where
T: Float + FloatConst + ToPrimitive + 'static,
U: FrequencyUnit,
{
pub fn new(freq_from: T, freq_to: T, dfreq: T, log_scale: bool) -> Self {
Self {
freq_from,
dfreq,
data_len: ((freq_to - freq_from) / dfreq).ceil().to_usize().unwrap_or(0),
log_scale,
unwrap_phase: true,
_unit: PhantomData,
}
}
pub fn unwrap_phase(mut self, unwrap: bool) -> Self {
self.unwrap_phase = unwrap;
self
}
pub fn plot<G: Into<FrequencyTransferFunction<T>>>(&self, g: G) -> Vec<FrequencyCharacteristics<T, U>> {
let g = g.into();
let mut points: Vec<FrequencyCharacteristics<T, U>> = (0..self.data_len)
.map(|i| g.characteristics(self.freq_from + T::from(i).unwrap() * self.dfreq, self.log_scale))
.collect();
if self.unwrap_phase {
unwrap_phase(&mut points);
}
points
}
pub fn frequency_response_s<S: Borrow<TransferFunction<T, Continuous>>>(&self, tf: S) -> Vec<FrequencyCharacteristics<T, U>> {
self.plot(tf.borrow().frequency_transfer_function())
}
pub fn frequency_response_z<S: Borrow<TransferFunction<T, Discrete>>>(&self, tf: S, ts: T) -> Vec<FrequencyCharacteristics<T, U>> {
self.plot(tf.borrow().frequency_transfer_function(ts))
}
}
fn unwrap_phase<T: Float + FloatConst, U>(points: &mut [FrequencyCharacteristics<T, U>]) {
let two_pi = T::TAU();
for i in 1..points.len() {
let step = points[i].phase - points[i - 1].phase;
let turns = ((step + T::PI()) / two_pi).ceil() - T::one();
points[i].phase = points[i].phase - turns * two_pi;
}
}