direct_neural_biasing/processing/filters/
bandpass.rs1use super::FilterInstance;
2use crate::processing::signal_processor::SignalProcessorConfig;
3
4use std::collections::HashMap;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Serialize, Deserialize, Clone)]
8pub struct BandPassFilterConfig {
9 pub id: String,
10 pub f_low: f64,
11 pub f_high: f64,
12}
13
14pub struct BandPassFilter {
15 config: BandPassFilterConfig,
16 high_pass: SecondOrderFilter,
17 low_pass: SecondOrderFilter,
18 keys: Keys,
19}
20
21struct SecondOrderFilter {
22 a: [f64; 3],
23 b: [f64; 3],
24 x: [f64; 2],
25 y: [f64; 2],
26}
27
28impl SecondOrderFilter {
29 pub fn new(f0: f64, fs: f64, filter_type: &str) -> Self {
30 let q = (2.0f64).sqrt() / 2.0; let omega = 2.0 * std::f64::consts::PI * f0 / fs;
32 let alpha = f64::sin(omega) / (2.0 * q);
33
34 let (b0, b1, b2, a0, a1, a2) = match filter_type {
35 "high" => (
36 (1.0 + f64::cos(omega)) / 2.0,
37 -(1.0 + f64::cos(omega)),
38 (1.0 + f64::cos(omega)) / 2.0,
39 1.0 + alpha,
40 -2.0 * f64::cos(omega),
41 1.0 - alpha,
42 ),
43 "low" => (
44 (1.0 - f64::cos(omega)) / 2.0,
45 1.0 - f64::cos(omega),
46 (1.0 - f64::cos(omega)) / 2.0,
47 1.0 + alpha,
48 -2.0 * f64::cos(omega),
49 1.0 - alpha,
50 ),
51 _ => panic!("Unsupported filter type"),
52 };
53
54 SecondOrderFilter {
55 a: [a0, a1, a2],
56 b: [b0, b1, b2],
57 x: [0.0, 0.0],
58 y: [0.0, 0.0],
59 }
60 }
61
62 fn calculate_output(&mut self, input: f64) -> f64 {
63 let output = (self.b[0] / self.a[0]) * input
64 + (self.b[1] / self.a[0]) * self.x[0]
65 + (self.b[2] / self.a[0]) * self.x[1]
66 - (self.a[1] / self.a[0]) * self.y[0]
67 - (self.a[2] / self.a[0]) * self.y[1];
68
69 self.x[1] = self.x[0];
71 self.x[0] = input;
72 self.y[1] = self.y[0];
73 self.y[0] = output;
74
75 output
76 }
77}
78
79pub struct Keys {
80 filtered_sample: &'static str,
81}
82
83impl BandPassFilter {
84 pub fn new(config: BandPassFilterConfig, fs: f64) -> Self {
86 let high_pass = SecondOrderFilter::new(config.f_low, fs, "high");
87 let low_pass = SecondOrderFilter::new(config.f_high, fs, "low");
88 let keys = Keys {
89 filtered_sample: Box::leak(
90 format!("filters:{}:filtered_sample", config.id).into_boxed_str(),
91 ),
92 };
93
94 BandPassFilter {
95 config,
96 high_pass,
97 low_pass,
98 keys,
99 }
100 }
101}
102
103impl FilterInstance for BandPassFilter {
104 fn id(&self) -> &str {
105 &self.config.id
106 }
107
108 fn process_sample(
109 &mut self,
110 _global_config: &SignalProcessorConfig,
111 results: &mut HashMap<&'static str, f64>,
112 ) {
113 if let Some(&raw_sample) = results.get("global:raw_sample") {
114 let high_pass_output = self.high_pass.calculate_output(raw_sample);
116 let filtered_sample = self.low_pass.calculate_output(high_pass_output);
118
119 results.insert(self.keys.filtered_sample, filtered_sample);
120 }
121 }
122}