automatica 1.0.0

Automatic control systems library
Documentation
//! Collection of common continuous input signals.

use std::ops::{Mul, Sub};

use crate::{
    units::{RadiansPerSecond, Seconds},
    Sin, Zero,
};

/// Zero input function
///
/// # Arguments
///
/// * `size` - Output size
pub fn zero<T>(size: usize) -> impl Fn(Seconds<T>) -> Vec<T>
where
    T: Clone + Zero,
{
    move |_| vec![T::zero(); size]
}

/// Impulse function
///
/// # Arguments
///
/// * `k` - Impulse size
/// * `o` - Impulse time
/// * `size` - Output size
pub fn impulse<T>(k: T, o: Seconds<T>, size: usize) -> impl Fn(Seconds<T>) -> Vec<T>
where
    T: Clone + PartialEq + Zero,
{
    move |t| {
        if t == o {
            vec![k.clone(); size]
        } else {
            vec![T::zero(); size]
        }
    }
}

/// Step function
///
/// # Arguments
///
/// * `k` - Step size
/// * `size` - Output size
pub fn step<T>(k: T, size: usize) -> impl Fn(Seconds<T>) -> Vec<T>
where
    T: Clone,
{
    move |_| vec![k.clone(); size]
}

/// Sine input (single input single output).
///
/// `sin(omega*t - phase)`
///
/// # Arguments
///
/// * `a` - sine amplitude
/// * `omega` - sine pulse in radians per second
/// * `phi` - sine phase in radians
pub fn sin_siso<T>(a: T, omega: RadiansPerSecond<T>, phi: T) -> impl Fn(Seconds<T>) -> Vec<T>
where
    T: Clone + Mul<Output = T> + Sin + Sub<Output = T>,
{
    move |t| vec![a.clone() * (omega.0.clone() * t.0 - phi.clone()).sin()]
}

#[cfg(test)]
mod tests {
    use super::*;
    use proptest::prelude::*;
    use std::f64::consts::PI;

    proptest! {
        #[test]
        fn qc_zero_input(s: f64) {
            assert_relative_eq!(0., zero(1)(Seconds(s))[0]);
        }
    }

    #[test]
    fn impulse_input() {
        let imp = impulse(10., Seconds(1.), 1);
        assert_relative_eq!(0., imp(Seconds(0.5))[0]);
        assert_relative_eq!(10., imp(Seconds(1.))[0]);
    }

    proptest! {
        #[test]
        fn qc_step_input(s: f64) {
            assert_relative_eq!(3., step(3., 1)(Seconds(s))[0]);
        }
    }

    proptest! {
        #[test]
        fn qc_sin_input(t in (0.0..100.0)) {
            // Reduce the maximum input since sine may have convergence
            // issues with big numbers.
            let sine = sin_siso(1., RadiansPerSecond(0.5), 0.)(Seconds(t))[0];
            let traslated_sine = sin_siso(1., RadiansPerSecond(0.5), PI)(Seconds(t))[0];
            assert_relative_eq!(sine, -traslated_sine, max_relative = 1e-9);
        }
    }

    #[test]
    fn sin_input_regression() {
        // The following t value fails if the max_relative error is 1e-10.
        let t = -81.681_343_796_796_53;
        let sine = sin_siso(1., RadiansPerSecond(0.5), 0.)(Seconds(t))[0];
        let traslated_sine = sin_siso(1., RadiansPerSecond(0.5), PI)(Seconds(t))[0];
        assert_relative_eq!(sine, -traslated_sine, max_relative = 1e-9);
    }
}