use crate::{transfer_function::continuous::Tf, Complex, MulAdd, One, Zero};
#[derive(Clone, Debug)]
pub struct RootLocus<T> {
tf: Tf<T>,
min_k: T,
max_k: T,
step: T,
}
impl<T> RootLocus<T>
where
T: Clone + PartialOrd + Zero,
{
pub(crate) fn new(tf: Tf<T>, min_k: T, max_k: T, step: T) -> Self {
assert!(step > T::zero(), "Step value must be strictly positive.");
assert!(
min_k < max_k,
"Maximum transfer constant must be greater than the minimum transfer constant."
);
Self {
tf,
min_k,
max_k,
step,
}
}
}
#[derive(Clone, Debug)]
pub struct IntoIter<T> {
tf: Tf<T>,
min_k: T,
step: T,
intervals: T,
index: T,
}
#[derive(Clone, Debug)]
pub struct Data<T> {
k: T,
output: Vec<Complex<T>>,
}
impl<T: Clone> Data<T> {
pub fn k(&self) -> T {
self.k.clone()
}
pub fn output(&self) -> &[Complex<T>] {
&self.output
}
}
macro_rules! root_locus_iter {
($ty:ty) => {
impl IntoIterator for RootLocus<$ty> {
type Item = Data<$ty>;
type IntoIter = IntoIter<$ty>;
fn into_iter(self) -> Self::IntoIter {
let intervals = ((self.max_k - self.min_k) / self.step).floor();
Self::IntoIter {
tf: self.tf,
min_k: self.min_k,
step: self.step,
intervals,
index: <$ty as Zero>::zero(),
}
}
}
impl Iterator for IntoIter<$ty> {
type Item = Data<$ty>;
fn next(&mut self) -> Option<Self::Item> {
if self.index > self.intervals {
None
} else {
let k = MulAdd::mul_add(self.step, self.index, self.min_k);
self.index += <$ty>::one();
Some(Self::Item {
k,
output: self.tf.root_locus(k),
})
}
}
}
};
}
root_locus_iter!(f32);
root_locus_iter!(f64);
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn fail_new1() {
let tf = Tf::new([1.], [0., 1.]);
RootLocus::new(tf, 0.1, 0.2, 0.);
}
#[test]
#[should_panic]
fn fail_new2() {
let tf = Tf::new([1.], [0., 1.]);
RootLocus::new(tf, 0.9, 0.2, 0.1);
}
}