1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! # Signal Processing
//! The signal processing toolbox currently contains some filtering functions, a limited set of filter design tools,<br/>
//! and a few B-spline interpolation algorithms for 1- and 2-D data. While the B-spline algorithms could<br/>
//! technically be placed under the interpolation category, they are included here because they only work with<br/>
//! equally-spaced data and make heavy use of filter-theory and transfer-function formalism to provide a fast B-spline transform.<br/>
//! To understand this section, you will need to understand that a signal in sciport-rs is an array of real or complex numbers.
//!
//! ## Filter Design
//!
//! Time-discrete filters can be classified into finite response (FIR) filters and infinite response (IIR) filters.<br/>
//! FIR filters can provide a linear phase response, whereas IIR filters cannot.<br/>
//! sciport-rs provides functions for designing both types of filters.
//!
//! ### IIR Filter
//!
//! sciport-rs provides two functions to directly design IIR iirdesign and iirfilter, where the filter type (e.g., elliptic)<br/>
//! is passed as an argument and several more filter design functions for specific filter types, e.g., ellip.
//! ### Filter coefficients
//!
//! Filter coefficients can be stored in several different formats:
//!  - [`Ba`](`crate::signal::output_type::Ba`)
//!  - [`Zpk`](`crate::signal::output_type::Zpk`)
//!  - [`Sos`](`crate::signal::output_type::Sos`) (currently unsupported)
//!
//! # References:
//!
//! The documentation on this page is largely been copied from the [SciPy](https://docs.scipy.org/doc/scipy/tutorial/signal.html) documentation
use self::{
    band_filter::{lp2bf_zpk, BandFilter},
    output_type::{DesiredFilterOutput, FilterOutput, Zpk},
    tools::bilinear_zpk,
};

mod convolution;
mod filter_design;

//pub use convolution::*;
pub use filter_design::*;

pub mod band_filter;
pub mod output_type;
pub mod tools;

#[derive(Debug, Clone, Copy)]
pub enum Analog {
    True,
    False { fs: f64 },
}

impl Analog {
    pub fn is_analog(&self) -> bool {
        match self {
            Self::True => true,
            Self::False { .. } => false,
        }
    }
}

/// Generic iir_filter
///
/// Takes a filter prototype and returns the final filter in the desired output
pub fn iir_filter(
    proto: Zpk,
    _order: u32,
    mut band_filter: BandFilter,
    mut analog: Analog,
    desired_output: DesiredFilterOutput,
) -> FilterOutput {
    let mut warped = band_filter;
    match &mut analog {
        Analog::True => {}
        Analog::False { fs } => {
            band_filter = (band_filter * 2.0) / *fs;
            *fs = 2.0;
            warped = ((band_filter * std::f64::consts::PI) / *fs).tan() * 2.0 * *fs;
        }
    }

    let mut result = lp2bf_zpk(proto, warped);

    if let Analog::False { fs } = &analog {
        result = bilinear_zpk(result, *fs);
    }

    FilterOutput::get_output(result, desired_output)
}

pub trait IIRFilter<T: ToOwned> {
    fn design_filter(&self) -> T;
    fn cache(&self) -> &Option<T>;
    fn cache_mut(&mut self) -> &mut Option<T>;

    /// Computes and returns the filter coefficients
    fn get_filter(&mut self) -> &T {
        if self.cache().is_some() {
            return self.cache().as_ref().unwrap();
        } else {
            let filter = self.design_filter();
            self.cache_mut().insert(filter)
        }
    }
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_iir {
    ($f:ty, $T:ty, $sel:ident, $design:expr) => {
        impl $crate::signal::IIRFilter<$T> for $f {
            fn cache(&self) -> &Option<$T> {
                &self.cache
            }

            fn cache_mut(&mut self) -> &mut Option<$T> {
                &mut self.cache
            }

            fn design_filter(&$sel) -> $T {
                $design
            }
        }
    };
}