Skip to content

Commit 521cbfd

Browse files
committed
fixes #31
1 parent 165f87f commit 521cbfd

2 files changed

Lines changed: 110 additions & 2 deletions

File tree

src/systems/transfer_function.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ where
4949
/// Creates a new transfer function from a given numerator and denominator.
5050
///
5151
/// # Arguments
52-
/// - `num`: A slice representing the numerator coefficients.
53-
/// - `den`: A slice representing the denominator coefficients.
52+
/// - `num`: A slice representing the numerator coefficients in ascending
53+
/// order, i.e. num = num[0] + num[1]*s + ... .
54+
/// - `den`: A slice representing the denominator coefficients in ascending
55+
/// order, i.e. den = den[0] + den[1]*s + ... .
5456
///
5557
/// # Returns
5658
/// Returns a new transfer function.

src/transformations.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use nalgebra::DMatrix;
22
use std::error::Error;
33

44
use crate::{
5+
Continuous,
56
slicot_wrapper::{tb01pd_, tb04ad_},
67
systems::{Ss, Tf},
78
utils::traits::Time,
@@ -489,6 +490,87 @@ impl<U: Time + 'static> Ss<U> {
489490
}
490491
}
491492

493+
impl Tf<f64, Continuous> {
494+
/// Returns a Pade approximation of a pure time delay `e^{-sT}` as a
495+
/// transfer function.
496+
///
497+
/// The Pade approximation is a rational function (ratio of two polynomials)
498+
/// that approximates the exponential delay term in the Laplace domain.
499+
///
500+
/// # Arguments
501+
///
502+
/// * `time_delay` - The delay time `T` (must be non-negative).
503+
/// * `order` - The order `n` of the approximation. Higher orders improve
504+
/// accuracy, especially at higher frequencies, but increase computational
505+
/// complexity.
506+
///
507+
/// # Returns
508+
///
509+
/// A `Tf<f64, Continuous>` representing the rational approximation of the
510+
/// delay.
511+
///
512+
/// # Panics
513+
///
514+
/// Panics if `time_delay` is negative.
515+
///
516+
/// # Examples
517+
///
518+
/// ```
519+
/// use control_systems_torbox::Tf;
520+
/// let delay = Tf::pade(0.5, 3);
521+
/// println!{"{}", delay};
522+
/// ```
523+
///
524+
/// # References
525+
/// - [RATIONAL APPROXIMATION OF TIME DELAY](https://www2.humusoft.cz/www/papers/tcp09/035_hanta.pdf)
526+
pub fn pade(time_delay: f64, order: usize) -> Tf<f64, Continuous> {
527+
pade_approx(time_delay, order)
528+
}
529+
}
530+
531+
fn pade_approx(time_delay: f64, order: usize) -> Tf<f64, Continuous> {
532+
assert!(time_delay >= 0.0);
533+
534+
if order == 0 || time_delay == 0.0 {
535+
return Tf::new_from_scalar(1.0);
536+
}
537+
538+
let mut num_coeffs = Vec::with_capacity(order + 1);
539+
let mut den_coeffs = Vec::with_capacity(order + 1);
540+
541+
num_coeffs.push(1.0);
542+
den_coeffs.push(1.0);
543+
544+
for i in 1..=order {
545+
// Not efficient calculation of coeffs, however likely fast enough.
546+
num_coeffs.push(time_delay.powi(i as i32) * pade_coeff_num(i, order));
547+
den_coeffs.push(time_delay.powi(i as i32) * pade_coeff_den(i, order));
548+
}
549+
Tf::new(&num_coeffs, &den_coeffs).normalize()
550+
}
551+
552+
fn pade_coeff_den(i: usize, n: usize) -> f64 {
553+
assert!(i <= n);
554+
555+
let num = factorial(2 * n - i) * factorial(n);
556+
let den = factorial(2 * n) * factorial(i) * factorial(n - i);
557+
num as f64 / den as f64
558+
}
559+
560+
fn pade_coeff_num(i: usize, n: usize) -> f64 {
561+
assert!(i <= n);
562+
563+
let mut abs_coeff = pade_coeff_den(i, n);
564+
if i % 2 != 0 {
565+
abs_coeff *= -1.;
566+
}
567+
abs_coeff
568+
}
569+
570+
fn factorial(n: usize) -> usize {
571+
(1..=n).product()
572+
}
573+
492574
#[cfg(test)]
493575
mod tests {
494576
use std::time::Instant;
@@ -521,6 +603,30 @@ mod tests {
521603

522604
Tf::<f64, Continuous>::new(&num, &den)
523605
}
606+
#[test]
607+
fn pade_approx_test() {
608+
let sys = Tf::pade(2.5, 0);
609+
assert_abs_diff_eq!(sys, Tf::new_from_scalar(1.0));
610+
let sys = Tf::pade(0.0, 3);
611+
assert_abs_diff_eq!(sys, Tf::new_from_scalar(1.0));
612+
613+
let sys = Tf::pade(2.5, 1);
614+
let sys_matlab = Tf::new(&[0.8, -1.0], &[0.8, 1.0]);
615+
assert_abs_diff_eq!(sys, sys_matlab, epsilon = 0.1);
616+
617+
let sys = Tf::pade(2.5, 4);
618+
let sys_matlab = Tf::new(
619+
&[43.01, -53.76, 28.8, -8.0, 1.0],
620+
&[43.01, 53.76, 28.8, 8.0, 1.0],
621+
);
622+
assert_abs_diff_eq!(sys, sys_matlab, epsilon = 0.1);
623+
}
624+
625+
#[test]
626+
#[should_panic]
627+
fn pade_approx_negative() {
628+
let _sys = Tf::pade(-1.0, 0);
629+
}
524630

525631
#[test]
526632
fn ss2tf_test() {

0 commit comments

Comments
 (0)