use crate::system::{fmt_num, push_term};
use crate::{Continuous, PartialFraction, PoleTerm, TransferFunction};
use num_complex::Complex;
use num_traits::{Float, Zero};
use std::ops::AddAssign;
impl<T: Float + AddAssign> TransferFunction<T, Continuous> {
pub fn inverse_laplace(&self) -> impl Fn(T) -> T + use<T>
where
T: 'static,
{
self.partial_fraction().time_function()
}
}
impl<T: Float> PartialFraction<T> {
pub fn time_response(&self, t: T) -> T {
if t < T::zero() {
return T::zero();
}
eval_terms(&self.terms, t)
}
pub fn time_function(&self) -> impl Fn(T) -> T + use<T>
where
T: 'static,
{
let pf = self.clone();
move |t| pf.time_response(t)
}
pub fn time_domain(&self) -> TimeDomain<'_, T> {
TimeDomain(self)
}
}
pub struct TimeDomain<'a, T>(&'a PartialFraction<T>);
pub(super) fn eval_terms<T: Float>(terms: &[PoleTerm<T>], t: T) -> T {
let mut sum = Complex::zero();
for term in terms {
let exp_pt = (term.pole * t).exp();
let mut basis = T::one();
for (k, &r) in term.residues.iter().enumerate() {
if k > 0 {
basis = basis * t / T::from(k).unwrap();
}
sum = sum + r * exp_pt * basis;
}
}
sum.re
}
impl<T: Float + std::fmt::Display> std::fmt::Display for TimeDomain<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "x(t) = {}", format_time_terms(self.0, f.precision()))
}
}
pub(super) fn format_time_terms<T: Float + std::fmt::Display>(pf: &PartialFraction<T>, prec: Option<usize>) -> String {
let mut out = String::new();
let degree = pf.direct.len().saturating_sub(1);
for (i, &c) in pf.direct.iter().enumerate() {
let body = match degree - i {
0 => "δ(t)".to_string(),
n => format!("δ^({})(t)", n),
};
push_term(&mut out, c, &body, prec);
}
for term in &pf.terms {
let p = term.pole;
if p.im < T::zero() {
continue;
}
let exp = if p.re.is_zero() { None } else { Some(format!("exp({}t)", fmt_num(p.re, prec))) };
let mut factorial = T::one();
for (k, &r) in term.residues.iter().enumerate() {
if k > 0 {
factorial = factorial * T::from(k).unwrap();
}
let r = r / factorial;
let mut factors: Vec<String> = Vec::new();
match k {
0 => {}
1 => factors.push("t".to_string()),
_ => factors.push(format!("t^{}", k)),
}
factors.extend(exp.clone());
if p.im.is_zero() {
push_term(&mut out, r.re, &factors.join(" * "), prec);
} else {
let two = T::from(2.0).unwrap();
let (a, b) = (two * r.re, -two * r.im);
if a.is_zero() && b.is_zero() {
continue;
}
let w = fmt_num(p.im, prec);
let mut trig = String::new();
push_term(&mut trig, a, &format!("cos({}t)", w), prec);
push_term(&mut trig, b, &format!("sin({}t)", w), prec);
factors.push(format!("({})", trig));
push_term(&mut out, T::one(), &factors.join(" * "), prec);
}
}
}
if out.is_empty() {
out = fmt_num(T::zero(), prec);
}
out
}