use crate::DType;
use crate::signal::traits::analysis::HilbertResult;
use numr::algorithm::fft::{FftAlgorithms, FftDirection, FftNormalization};
use numr::error::{Error, Result};
use numr::ops::{ComplexOps, ScalarOps, ShapeOps, TensorOps};
use numr::runtime::{Runtime, RuntimeClient};
use numr::tensor::Tensor;
pub fn hilbert_impl<R, C>(client: &C, x: &Tensor<R>) -> Result<HilbertResult<R>>
where
R: Runtime<DType = DType>,
C: FftAlgorithms<R>
+ ComplexOps<R>
+ ScalarOps<R>
+ TensorOps<R>
+ ShapeOps<R>
+ RuntimeClient<R>,
{
let n = x.shape()[0];
let device = x.device();
if n == 0 {
return Err(Error::InvalidArgument {
arg: "x",
reason: "Input signal cannot be empty".to_string(),
});
}
let zeros = Tensor::zeros(x.shape(), x.dtype(), device);
let x_complex = client.make_complex(x, &zeros)?;
let fft = client.fft(&x_complex, FftDirection::Forward, FftNormalization::None)?;
let mut h_data = vec![0.0f64; n];
h_data[0] = 1.0;
let half = n / 2;
for hi in h_data.iter_mut().take(half).skip(1) {
*hi = 2.0; }
if n.is_multiple_of(2) {
h_data[half] = 1.0; } else {
h_data[half] = 2.0; }
let h = Tensor::from_slice(&h_data, &[n], device);
let fft_re = client.real(&fft)?;
let fft_im = client.imag(&fft)?;
let scaled_re = client.mul(&fft_re, &h)?;
let scaled_im = client.mul(&fft_im, &h)?;
let scaled_fft = client.make_complex(&scaled_re, &scaled_im)?;
let analytic = client.fft(
&scaled_fft,
FftDirection::Inverse,
FftNormalization::Backward,
)?;
let real = client.real(&analytic)?;
let imag = client.imag(&analytic)?;
Ok(HilbertResult { real, imag })
}