@@ -2,6 +2,7 @@ use nalgebra::DMatrix;
22use std:: error:: Error ;
33
44use 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) ]
493575mod 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