use nalgebra::RealField;
use num_complex::Complex;
use num_traits::{Float, MulAdd};
use crate::transfer_function::continuous::Tf;
#[derive(Clone, Debug)]
pub struct RootLocus<T: Float> {
tf: Tf<T>,
min_k: T,
max_k: T,
step: T,
}
impl<T: Float> RootLocus<T> {
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: Float> {
tf: Tf<T>,
min_k: T,
step: T,
intervals: T,
index: T,
}
impl<T: Float + MulAdd<Output = T> + RealField> IntoIterator for RootLocus<T> {
type Item = Data<T>;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
let intervals = num_traits::Float::floor((self.max_k - self.min_k) / self.step);
Self::IntoIter {
tf: self.tf,
min_k: self.min_k,
step: self.step,
intervals,
index: T::zero(),
}
}
}
#[derive(Debug)]
pub struct Data<T> {
k: T,
output: Vec<Complex<T>>,
}
impl<T: Copy> Data<T> {
pub fn k(&self) -> T {
self.k
}
pub fn output(&self) -> &[Complex<T>] {
&self.output
}
}
impl<T: Float + MulAdd<Output = T> + RealField> Iterator for IntoIter<T> {
type Item = Data<T>;
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 += T::one();
Some(Self::Item {
k,
output: self.tf.root_locus(k),
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::poly;
#[test]
#[should_panic]
fn fail_new1() {
let tf = Tf::new(poly!(1.), poly!(0., 1.));
RootLocus::new(tf, 0.1, 0.2, 0.);
}
#[test]
#[should_panic]
fn fail_new2() {
let tf = Tf::new(poly!(1.), poly!(0., 1.));
RootLocus::new(tf, 0.9, 0.2, 0.1);
}
}