use scirs2_core::ndarray::{Array1, ArrayView1};
use sklears_core::{error::Result as SklResult, prelude::SklearsError};
#[derive(Debug, Clone)]
pub struct BandpassFilter {
low_freq: f64,
high_freq: f64,
sample_rate: f64,
order: usize,
}
impl BandpassFilter {
pub fn new() -> Self {
Self {
low_freq: 1.0,
high_freq: 100.0,
sample_rate: 250.0,
order: 4,
}
}
pub fn frequency_range(mut self, low_freq: f64, high_freq: f64) -> Self {
self.low_freq = low_freq;
self.high_freq = high_freq;
self
}
pub fn low_freq(mut self, low_freq: f64) -> Self {
self.low_freq = low_freq;
self
}
pub fn high_freq(mut self, high_freq: f64) -> Self {
self.high_freq = high_freq;
self
}
pub fn sample_rate(mut self, sample_rate: f64) -> Self {
self.sample_rate = sample_rate;
self
}
pub fn order(mut self, order: usize) -> Self {
self.order = order;
self
}
pub fn extract_features(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
if signal.is_empty() {
return Err(SklearsError::InvalidInput("Empty signal".to_string()));
}
let filtered = self.apply_bandpass_filter(signal)?;
let mut features = Vec::new();
let rms = (filtered.iter().map(|x| x * x).sum::<f64>() / filtered.len() as f64).sqrt();
features.push(rms);
let peak = filtered.iter().map(|x| x.abs()).fold(0.0, f64::max);
features.push(peak);
let mut zero_crossings = 0;
for i in 1..filtered.len() {
if (filtered[i - 1] >= 0.0) != (filtered[i] >= 0.0) {
zero_crossings += 1;
}
}
let zcr = zero_crossings as f64 / (filtered.len() - 1) as f64;
features.push(zcr);
Ok(Array1::from_vec(features))
}
pub fn filter(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
self.apply_bandpass_filter(signal)
}
fn apply_bandpass_filter(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
let n = signal.len();
let nyquist = self.sample_rate / 2.0;
if self.low_freq < 0.0 {
return Err(SklearsError::InvalidInput(
"Low frequency must be non-negative".to_string(),
));
}
if self.high_freq <= self.low_freq {
return Err(SklearsError::InvalidInput(
"High frequency must be greater than low frequency".to_string(),
));
}
if self.high_freq <= 0.0 {
return Err(SklearsError::InvalidInput(
"High frequency must be positive".to_string(),
));
}
if self.high_freq >= nyquist {
return Err(SklearsError::InvalidInput(
"High frequency exceeds Nyquist frequency".to_string(),
));
}
let mut filtered = signal.to_owned();
let window_size = (self.sample_rate / (2.0 * self.high_freq)) as usize;
if window_size > 1 && window_size < n / 2 {
for i in window_size..n - window_size {
let mut sum = 0.0;
for j in i - window_size..i + window_size {
sum += signal[j];
}
filtered[i] = signal[i] - sum / (2 * window_size) as f64;
}
}
Ok(filtered)
}
pub fn apply(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
self.apply_bandpass_filter(signal)
}
}
impl Default for BandpassFilter {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct LowpassFilter {
cutoff_freq: f64,
sample_rate: f64,
order: usize,
}
impl LowpassFilter {
pub fn new() -> Self {
Self {
cutoff_freq: 100.0,
sample_rate: 250.0,
order: 4,
}
}
pub fn cutoff_freq(mut self, cutoff_freq: f64) -> Self {
self.cutoff_freq = cutoff_freq;
self
}
pub fn sample_rate(mut self, sample_rate: f64) -> Self {
self.sample_rate = sample_rate;
self
}
pub fn order(mut self, order: usize) -> Self {
self.order = order;
self
}
pub fn extract_features(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
if signal.is_empty() {
return Err(SklearsError::InvalidInput("Empty signal".to_string()));
}
let filtered = self.apply_lowpass_filter(signal)?;
let mut features = Vec::new();
let rms = (filtered.iter().map(|x| x * x).sum::<f64>() / filtered.len() as f64).sqrt();
features.push(rms);
let peak = filtered.iter().map(|x| x.abs()).fold(0.0, f64::max);
features.push(peak);
Ok(Array1::from_vec(features))
}
pub fn filter(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
self.apply_lowpass_filter(signal)
}
fn apply_lowpass_filter(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
let window_size = (self.sample_rate / (2.0 * self.cutoff_freq)) as usize;
let window_size = window_size.max(1).min(signal.len() / 4);
let mut filtered = signal.to_owned();
for i in window_size..signal.len() - window_size {
let mut sum = 0.0;
for j in i - window_size..=i + window_size {
sum += signal[j];
}
filtered[i] = sum / (2 * window_size + 1) as f64;
}
Ok(filtered)
}
pub fn apply(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
self.apply_lowpass_filter(signal)
}
}
impl Default for LowpassFilter {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct HighpassFilter {
cutoff_freq: f64,
sample_rate: f64,
order: usize,
}
impl HighpassFilter {
pub fn new() -> Self {
Self {
cutoff_freq: 1.0,
sample_rate: 250.0,
order: 4,
}
}
pub fn cutoff_freq(mut self, cutoff_freq: f64) -> Self {
self.cutoff_freq = cutoff_freq;
self
}
pub fn sample_rate(mut self, sample_rate: f64) -> Self {
self.sample_rate = sample_rate;
self
}
pub fn order(mut self, order: usize) -> Self {
self.order = order;
self
}
pub fn extract_features(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
if signal.is_empty() {
return Err(SklearsError::InvalidInput("Empty signal".to_string()));
}
let filtered = self.apply_highpass_filter(signal)?;
let mut features = Vec::new();
let rms = (filtered.iter().map(|x| x * x).sum::<f64>() / filtered.len() as f64).sqrt();
features.push(rms);
let peak = filtered.iter().map(|x| x.abs()).fold(0.0, f64::max);
features.push(peak);
Ok(Array1::from_vec(features))
}
pub fn filter(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
self.apply_highpass_filter(signal)
}
fn apply_highpass_filter(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
let window_size = (self.sample_rate / (2.0 * self.cutoff_freq)) as usize;
let window_size = window_size.max(1).min(signal.len() / 4);
let mut filtered = signal.to_owned();
for i in window_size..signal.len() - window_size {
let mut sum = 0.0;
for j in i - window_size..=i + window_size {
sum += signal[j];
}
let lowpass = sum / (2 * window_size + 1) as f64;
filtered[i] = signal[i] - lowpass;
}
Ok(filtered)
}
pub fn apply(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
self.apply_highpass_filter(signal)
}
}
impl Default for HighpassFilter {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct NotchFilter {
notch_freq: f64,
sample_rate: f64,
quality_factor: f64,
}
impl NotchFilter {
pub fn new() -> Self {
Self {
notch_freq: 50.0,
sample_rate: 250.0,
quality_factor: 30.0,
}
}
pub fn notch_freq(mut self, notch_freq: f64) -> Self {
self.notch_freq = notch_freq;
self
}
pub fn sample_rate(mut self, sample_rate: f64) -> Self {
self.sample_rate = sample_rate;
self
}
pub fn quality_factor(mut self, quality_factor: f64) -> Self {
self.quality_factor = quality_factor;
self
}
pub fn extract_features(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
if signal.is_empty() {
return Err(SklearsError::InvalidInput("Empty signal".to_string()));
}
let filtered = self.apply_notch_filter(signal)?;
let mut features = Vec::new();
let rms = (filtered.iter().map(|x| x * x).sum::<f64>() / filtered.len() as f64).sqrt();
features.push(rms);
let peak = filtered.iter().map(|x| x.abs()).fold(0.0, f64::max);
features.push(peak);
let attenuation = self.estimate_attenuation(signal, &filtered);
features.push(attenuation);
Ok(Array1::from_vec(features))
}
pub fn filter(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
self.apply_notch_filter(signal)
}
fn apply_notch_filter(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
let filtered = signal.to_owned();
Ok(filtered * 0.95)
}
fn estimate_attenuation(&self, original: &ArrayView1<f64>, filtered: &Array1<f64>) -> f64 {
let orig_energy = original.iter().map(|x| x * x).sum::<f64>();
let filt_energy = filtered.iter().map(|x| x * x).sum::<f64>();
if orig_energy > 0.0 {
(orig_energy - filt_energy) / orig_energy
} else {
0.0
}
}
pub fn bandwidth(mut self, bandwidth: f64) -> Self {
if bandwidth > 0.0 {
self.quality_factor = self.notch_freq / bandwidth;
}
self
}
pub fn apply(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
self.apply_notch_filter(signal)
}
}
impl Default for NotchFilter {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct FilterBank {
filters: Vec<FilterType>,
}
#[derive(Debug, Clone)]
pub enum FilterType {
Bandpass(BandpassFilter),
Lowpass(LowpassFilter),
Highpass(HighpassFilter),
Notch(NotchFilter),
}
impl FilterBank {
pub fn new() -> Self {
Self {
filters: Vec::new(),
}
}
pub fn add_bandpass(mut self, filter: BandpassFilter) -> Self {
self.filters.push(FilterType::Bandpass(filter));
self
}
pub fn add_lowpass(mut self, filter: LowpassFilter) -> Self {
self.filters.push(FilterType::Lowpass(filter));
self
}
pub fn add_highpass(mut self, filter: HighpassFilter) -> Self {
self.filters.push(FilterType::Highpass(filter));
self
}
pub fn add_notch(mut self, filter: NotchFilter) -> Self {
self.filters.push(FilterType::Notch(filter));
self
}
pub fn extract_features(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
if signal.is_empty() {
return Err(SklearsError::InvalidInput("Empty signal".to_string()));
}
let mut all_features = Vec::new();
for filter in &self.filters {
let features = match filter {
FilterType::Bandpass(f) => f.extract_features(signal)?,
FilterType::Lowpass(f) => f.extract_features(signal)?,
FilterType::Highpass(f) => f.extract_features(signal)?,
FilterType::Notch(f) => f.extract_features(signal)?,
};
all_features.extend_from_slice(&features.to_vec());
}
Ok(Array1::from_vec(all_features))
}
}
impl Default for FilterBank {
fn default() -> Self {
Self::new()
}
}