use crate::DType;
use numr::error::Result;
use numr::runtime::Runtime;
use numr::tensor::Tensor;
pub trait SignalAnalysisAlgorithms<R: Runtime<DType = DType>> {
fn hilbert(&self, x: &Tensor<R>) -> Result<HilbertResult<R>>;
fn resample(&self, x: &Tensor<R>, num: usize, den: usize) -> Result<Tensor<R>>;
fn decimate(&self, x: &Tensor<R>, q: usize, params: DecimateParams) -> Result<Tensor<R>>;
fn find_peaks(&self, x: &Tensor<R>, params: PeakParams) -> Result<PeakResult<R>>;
fn savgol_filter(
&self,
x: &Tensor<R>,
window_length: usize,
polyorder: usize,
deriv: usize,
) -> Result<Tensor<R>>;
}
#[derive(Debug, Clone)]
pub struct HilbertResult<R: Runtime<DType = DType>> {
pub real: Tensor<R>,
pub imag: Tensor<R>,
}
impl<R: Runtime<DType = DType>> HilbertResult<R> {
pub fn envelope(&self) -> Result<Tensor<R>> {
let re: Vec<f64> = self.real.to_vec();
let im: Vec<f64> = self.imag.to_vec();
let n = re.len();
let env: Vec<f64> = re
.iter()
.zip(im.iter())
.map(|(&r, &i)| (r * r + i * i).sqrt())
.collect();
let device = self.real.device();
Ok(Tensor::from_slice(&env, &[n], device))
}
pub fn instantaneous_phase(&self) -> Result<Tensor<R>> {
let re: Vec<f64> = self.real.to_vec();
let im: Vec<f64> = self.imag.to_vec();
let n = re.len();
let phase: Vec<f64> = re
.iter()
.zip(im.iter())
.map(|(&r, &i)| i.atan2(r))
.collect();
let device = self.real.device();
Ok(Tensor::from_slice(&phase, &[n], device))
}
pub fn instantaneous_frequency(&self) -> Result<Tensor<R>> {
let re: Vec<f64> = self.real.to_vec();
let im: Vec<f64> = self.imag.to_vec();
let n = re.len();
let phase: Vec<f64> = re
.iter()
.zip(im.iter())
.map(|(&r, &i)| i.atan2(r))
.collect();
let mut freq = Vec::with_capacity(n);
freq.push(0.0);
for i in 1..n {
let mut diff = phase[i] - phase[i - 1];
while diff > std::f64::consts::PI {
diff -= 2.0 * std::f64::consts::PI;
}
while diff < -std::f64::consts::PI {
diff += 2.0 * std::f64::consts::PI;
}
freq.push(diff);
}
let device = self.real.device();
Ok(Tensor::from_slice(&freq, &[n], device))
}
}
#[derive(Debug, Clone)]
pub struct DecimateParams {
pub n: usize,
pub ftype: DecimateFilterImpl,
pub zero_phase: bool,
}
impl Default for DecimateParams {
fn default() -> Self {
Self {
n: 8,
ftype: DecimateFilterImpl::Iir,
zero_phase: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DecimateFilterImpl {
#[default]
Iir,
Fir,
}
#[derive(Debug, Clone, Default)]
pub struct PeakParams {
pub height: Option<f64>,
pub threshold: Option<f64>,
pub distance: Option<usize>,
pub prominence: Option<f64>,
pub width: Option<f64>,
}
impl PeakParams {
pub fn new() -> Self {
Self::default()
}
pub fn with_height(mut self, height: f64) -> Self {
self.height = Some(height);
self
}
pub fn with_distance(mut self, distance: usize) -> Self {
self.distance = Some(distance);
self
}
pub fn with_prominence(mut self, prominence: f64) -> Self {
self.prominence = Some(prominence);
self
}
}
#[derive(Debug, Clone)]
pub struct PeakResult<R: Runtime<DType = DType>> {
pub indices: Vec<usize>,
pub heights: Tensor<R>,
pub prominences: Option<Tensor<R>>,
}