use std::collections::HashMap;
use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::Complex64;
use super::*;
use crate::DeviceResult;
impl SignalProcessor {
pub fn new(config: &SignalProcessingConfig) -> Self {
Self {
config: config.clone(),
filter_bank: FilterBank::new(&config.filtering_config),
spectral_analyzer: SpectralAnalyzer::new(&config.spectral_config),
timefreq_analyzer: TimeFrequencyAnalyzer::new(&config.timefreq_config),
wavelet_analyzer: WaveletAnalyzer::new(&config.wavelet_config),
}
}
pub fn process_signals(&mut self, characterization: &CrosstalkCharacterization) -> DeviceResult<SignalProcessingResult> {
Ok(SignalProcessingResult {
filtered_signals: HashMap::new(),
spectral_analysis: SpectralAnalysisResult {
power_spectral_density: HashMap::new(),
cross_spectral_density: HashMap::new(),
coherence: HashMap::new(),
transfer_functions: HashMap::new(),
spectral_peaks: HashMap::new(),
},
timefreq_analysis: TimeFrequencyAnalysisResult {
stft_results: HashMap::new(),
cwt_results: HashMap::new(),
hht_results: None,
instantaneous_frequency: HashMap::new(),
instantaneous_amplitude: HashMap::new(),
},
wavelet_analysis: WaveletAnalysisResult {
coefficients: HashMap::new(),
reconstructed_signals: HashMap::new(),
energy_distribution: HashMap::new(),
denoising_results: HashMap::new(),
},
noise_characteristics: NoiseCharacteristicsResult {
noise_power: HashMap::new(),
snr: HashMap::new(),
noise_color: HashMap::new(),
stationarity: HashMap::new(),
},
})
}
pub fn apply_filtering(&mut self, signals: &HashMap<String, Array1<f64>>) -> DeviceResult<HashMap<String, Array1<f64>>> {
let mut filtered_signals = HashMap::new();
for (signal_name, signal_data) in signals {
let filtered = self.filter_bank.apply_filters(signal_data)?;
filtered_signals.insert(signal_name.clone(), filtered);
}
Ok(filtered_signals)
}
pub fn analyze_spectrum(&mut self, signals: &HashMap<String, Array1<f64>>) -> DeviceResult<SpectralAnalysisResult> {
self.spectral_analyzer.analyze_signals(signals)
}
pub fn analyze_time_frequency(&mut self, signals: &HashMap<String, Array1<f64>>) -> DeviceResult<TimeFrequencyAnalysisResult> {
self.timefreq_analyzer.analyze_signals(signals)
}
pub fn analyze_wavelets(&mut self, signals: &HashMap<String, Array1<f64>>) -> DeviceResult<WaveletAnalysisResult> {
self.wavelet_analyzer.analyze_signals(signals)
}
pub fn analyze_noise(&self, signals: &HashMap<String, Array1<f64>>) -> DeviceResult<NoiseCharacteristicsResult> {
let mut noise_power = HashMap::new();
let mut snr = HashMap::new();
let mut noise_color = HashMap::new();
let mut stationarity = HashMap::new();
for (signal_name, signal_data) in signals {
let power = self.estimate_noise_power(signal_data)?;
noise_power.insert(signal_name.clone(), power);
let signal_power = self.estimate_signal_power(signal_data)?;
let snr_value = 10.0 * (signal_power / power).log10();
snr.insert(signal_name.clone(), snr_value);
let color = self.analyze_noise_color(signal_data)?;
noise_color.insert(signal_name.clone(), color);
let stationarity_result = self.test_stationarity(signal_data)?;
stationarity.insert(signal_name.clone(), stationarity_result);
}
Ok(NoiseCharacteristicsResult {
noise_power,
snr,
noise_color,
stationarity,
})
}
fn estimate_noise_power(&self, signal: &Array1<f64>) -> DeviceResult<f64> {
let variance = signal.var(0.0);
Ok(variance * 0.1) }
fn estimate_signal_power(&self, signal: &Array1<f64>) -> DeviceResult<f64> {
let variance = signal.var(0.0);
Ok(variance * 0.9) }
fn analyze_noise_color(&self, signal: &Array1<f64>) -> DeviceResult<NoiseColor> {
Ok(NoiseColor::White) }
fn test_stationarity(&self, signal: &Array1<f64>) -> DeviceResult<StationarityResult> {
Ok(StationarityResult {
test_statistic: -3.5,
p_value: 0.01,
is_stationary: true,
confidence: 0.99,
})
}
}
impl FilterBank {
pub fn new(config: &FilteringConfig) -> Self {
Self {
filters: HashMap::new(),
adaptive_filters: HashMap::new(),
noise_reducer: NoiseReducer::new(&config.noise_reduction),
}
}
pub fn apply_filters(&mut self, signal: &Array1<f64>) -> DeviceResult<Array1<f64>> {
let mut filtered_signal = signal.clone();
for (_, filter) in &mut self.filters {
filtered_signal = filter.apply(&filtered_signal)?;
}
for (_, adaptive_filter) in &mut self.adaptive_filters {
filtered_signal = adaptive_filter.apply(&filtered_signal)?;
}
filtered_signal = self.noise_reducer.reduce_noise(&filtered_signal)?;
Ok(filtered_signal)
}
pub fn add_filter(&mut self, name: String, filter_type: FilterType, params: FilterParameters) {
let filter = DigitalFilter::new(filter_type, params);
self.filters.insert(name, filter);
}
pub fn add_adaptive_filter(&mut self, name: String, algorithm: LearningAlgorithm, length: usize) {
let filter = AdaptiveFilter::new(algorithm, length);
self.adaptive_filters.insert(name, filter);
}
pub fn update_adaptive_filters(&mut self, desired_signal: &Array1<f64>, input_signal: &Array1<f64>) -> DeviceResult<()> {
for (_, adaptive_filter) in &mut self.adaptive_filters {
adaptive_filter.update(desired_signal, input_signal)?;
}
Ok(())
}
}
impl DigitalFilter {
pub fn new(filter_type: FilterType, parameters: FilterParameters) -> Self {
let coefficients = Self::design_filter(&filter_type, ¶meters);
let state = Array1::zeros(coefficients.len());
Self {
filter_type,
coefficients,
state,
parameters,
}
}
pub fn apply(&mut self, input: &Array1<f64>) -> DeviceResult<Array1<f64>> {
let mut output = Array1::zeros(input.len());
for (i, &x) in input.iter().enumerate() {
output[i] = self.filter_sample(x);
}
Ok(output)
}
fn filter_sample(&mut self, input: f64) -> f64 {
for i in (1..self.state.len()).rev() {
self.state[i] = self.state[i - 1];
}
self.state[0] = input;
let mut output = 0.0;
for (i, &coeff) in self.coefficients.iter().enumerate() {
if i < self.state.len() {
output += coeff * self.state[i];
}
}
output
}
fn design_filter(filter_type: &FilterType, params: &FilterParameters) -> Array1<f64> {
match filter_type {
FilterType::Butterworth { order, cutoff } => {
Self::design_butterworth(*order, *cutoff, params.sampling_frequency)
},
FilterType::Chebyshev1 { order, rp, cutoff } => {
Self::design_chebyshev1(*order, *rp, *cutoff, params.sampling_frequency)
},
FilterType::Chebyshev2 { order, rs, cutoff } => {
Self::design_chebyshev2(*order, *rs, *cutoff, params.sampling_frequency)
},
FilterType::Elliptic { order, rp, rs, cutoff } => {
Self::design_elliptic(*order, *rp, *rs, *cutoff, params.sampling_frequency)
},
FilterType::Kalman { process_noise, measurement_noise } => {
Self::design_kalman(*process_noise, *measurement_noise)
},
FilterType::Wiener { noise_estimate } => {
Self::design_wiener(*noise_estimate)
},
}
}
fn design_butterworth(order: usize, cutoff: f64, fs: f64) -> Array1<f64> {
Array1::from_vec(vec![0.1, 0.2, 0.4, 0.2, 0.1]) }
fn design_chebyshev1(order: usize, rp: f64, cutoff: f64, fs: f64) -> Array1<f64> {
Array1::from_vec(vec![0.05, 0.2, 0.5, 0.2, 0.05])
}
fn design_chebyshev2(order: usize, rs: f64, cutoff: f64, fs: f64) -> Array1<f64> {
Array1::from_vec(vec![0.08, 0.18, 0.48, 0.18, 0.08])
}
fn design_elliptic(order: usize, rp: f64, rs: f64, cutoff: f64, fs: f64) -> Array1<f64> {
Array1::from_vec(vec![0.06, 0.19, 0.5, 0.19, 0.06])
}
fn design_kalman(process_noise: f64, measurement_noise: f64) -> Array1<f64> {
Array1::from_vec(vec![0.2, 0.6, 0.2])
}
fn design_wiener(noise_estimate: f64) -> Array1<f64> {
Array1::from_vec(vec![0.15, 0.7, 0.15])
}
}
impl AdaptiveFilter {
pub fn new(algorithm: LearningAlgorithm, filter_length: usize) -> Self {
Self {
algorithm,
filter_length,
weights: Array1::zeros(filter_length),
learning_curve: VecDeque::with_capacity(1000),
}
}
pub fn apply(&self, input: &Array1<f64>) -> DeviceResult<Array1<f64>> {
let mut output = Array1::zeros(input.len());
for (i, &x) in input.iter().enumerate() {
let mut y = 0.0;
let start_idx = if i >= self.filter_length { i - self.filter_length + 1 } else { 0 };
for (j, &w) in self.weights.iter().enumerate() {
let input_idx = start_idx + j;
if input_idx < input.len() {
y += w * input[input_idx];
}
}
output[i] = y;
}
Ok(output)
}
pub fn update(&mut self, desired: &Array1<f64>, input: &Array1<f64>) -> DeviceResult<()> {
match &self.algorithm {
LearningAlgorithm::LMS { step_size } => {
self.lms_update(desired, input, *step_size)
},
LearningAlgorithm::RLS { forgetting_factor } => {
self.rls_update(desired, input, *forgetting_factor)
},
LearningAlgorithm::GradientDescent { momentum } => {
self.gradient_descent_update(desired, input, *momentum)
},
LearningAlgorithm::Adam { beta1, beta2, epsilon } => {
self.adam_update(desired, input, *beta1, *beta2, *epsilon)
},
LearningAlgorithm::KalmanFilter { process_noise, measurement_noise } => {
self.kalman_update(desired, input, *process_noise, *measurement_noise)
},
}
}
fn lms_update(&mut self, desired: &Array1<f64>, input: &Array1<f64>, step_size: f64) -> DeviceResult<()> {
let output = self.apply(input)?;
let error = desired - &output;
for i in 0..self.weights.len() {
self.weights[i] += step_size * error.mean().unwrap_or(0.0);
}
self.learning_curve.push_back(error.mapv(|x| x * x).mean().unwrap_or(0.0));
Ok(())
}
fn rls_update(&mut self, desired: &Array1<f64>, input: &Array1<f64>, forgetting_factor: f64) -> DeviceResult<()> {
Ok(())
}
fn gradient_descent_update(&mut self, desired: &Array1<f64>, input: &Array1<f64>, momentum: f64) -> DeviceResult<()> {
Ok(())
}
fn adam_update(&mut self, desired: &Array1<f64>, input: &Array1<f64>, beta1: f64, beta2: f64, epsilon: f64) -> DeviceResult<()> {
Ok(())
}
fn kalman_update(&mut self, desired: &Array1<f64>, input: &Array1<f64>, process_noise: f64, measurement_noise: f64) -> DeviceResult<()> {
Ok(())
}
}
impl NoiseReducer {
pub fn new(config: &NoiseReductionConfig) -> Self {
Self {
method: config.method.clone(),
noise_estimator: NoiseEstimator::new(&config.noise_estimation),
reduction_history: VecDeque::with_capacity(1000),
}
}
pub fn reduce_noise(&mut self, signal: &Array1<f64>) -> DeviceResult<Array1<f64>> {
if !self.method.is_enabled() {
return Ok(signal.clone());
}
match &self.method {
NoiseReductionMethod::SpectralSubtraction { over_subtraction_factor } => {
self.spectral_subtraction(signal, *over_subtraction_factor)
},
NoiseReductionMethod::WienerFiltering { noise_estimate } => {
self.wiener_filtering(signal, *noise_estimate)
},
NoiseReductionMethod::WaveletDenoising { wavelet, threshold_method } => {
self.wavelet_denoising(signal, wavelet, threshold_method)
},
NoiseReductionMethod::AdaptiveFiltering { step_size, filter_length } => {
self.adaptive_filtering(signal, *step_size, *filter_length)
},
}
}
fn spectral_subtraction(&mut self, signal: &Array1<f64>, over_subtraction_factor: f64) -> DeviceResult<Array1<f64>> {
let noise_estimate = self.noise_estimator.estimate_noise(signal)?;
let reduced_signal = signal.mapv(|x| x * (1.0 - over_subtraction_factor * noise_estimate));
self.reduction_history.push_back(noise_estimate);
Ok(reduced_signal)
}
fn wiener_filtering(&mut self, signal: &Array1<f64>, noise_estimate: f64) -> DeviceResult<Array1<f64>> {
let signal_power = signal.var(0.0);
let wiener_gain = signal_power / (signal_power + noise_estimate);
let filtered_signal = signal.mapv(|x| x * wiener_gain);
self.reduction_history.push_back(noise_estimate);
Ok(filtered_signal)
}
fn wavelet_denoising(&mut self, signal: &Array1<f64>, wavelet: &str, threshold_method: &str) -> DeviceResult<Array1<f64>> {
let threshold = signal.std(0.0) * 0.1;
let denoised_signal = signal.mapv(|x| {
if x.abs() > threshold {
x - threshold * x.signum()
} else {
0.0
}
});
Ok(denoised_signal)
}
fn adaptive_filtering(&mut self, signal: &Array1<f64>, step_size: f64, filter_length: usize) -> DeviceResult<Array1<f64>> {
let mut adaptive_filter = AdaptiveFilter::new(
LearningAlgorithm::LMS { step_size },
filter_length,
);
let delayed_signal = if signal.len() > 1 {
let mut delayed = Array1::zeros(signal.len());
delayed.slice_mut(scirs2_core::ndarray::s![1..]).assign(&signal.slice(scirs2_core::ndarray::s![..signal.len()-1]));
delayed
} else {
signal.clone()
};
adaptive_filter.apply(signal)
}
}
impl NoiseEstimator {
pub fn new(method: &NoiseEstimationMethod) -> Self {
Self {
method: method.clone(),
noise_estimate: 0.01,
adaptation_rate: 0.1,
}
}
pub fn estimate_noise(&mut self, signal: &Array1<f64>) -> DeviceResult<f64> {
let new_estimate = match &self.method {
NoiseEstimationMethod::VoiceActivityDetection => {
self.vad_noise_estimation(signal)
},
NoiseEstimationMethod::MinimumStatistics => {
self.minimum_statistics_estimation(signal)
},
NoiseEstimationMethod::MCRA { alpha } => {
self.mcra_estimation(signal, *alpha)
},
NoiseEstimationMethod::IMCRA { alpha_s, alpha_d } => {
self.imcra_estimation(signal, *alpha_s, *alpha_d)
},
}?;
self.noise_estimate = (1.0 - self.adaptation_rate) * self.noise_estimate
+ self.adaptation_rate * new_estimate;
Ok(self.noise_estimate)
}
fn vad_noise_estimation(&self, signal: &Array1<f64>) -> DeviceResult<f64> {
let noise_samples = signal.len() / 10;
let noise_portion = signal.slice(scirs2_core::ndarray::s![..noise_samples]);
Ok(noise_portion.var(0.0))
}
fn minimum_statistics_estimation(&self, signal: &Array1<f64>) -> DeviceResult<f64> {
let window_size = 100;
let mut min_powers = Vec::new();
for i in (0..signal.len()).step_by(window_size) {
let end_idx = std::cmp::min(i + window_size, signal.len());
let window = signal.slice(scirs2_core::ndarray::s![i..end_idx]);
let power = window.mapv(|x| x * x).mean().unwrap_or(0.0);
min_powers.push(power);
}
let min_power = min_powers.iter().cloned().fold(f64::INFINITY, f64::min);
Ok(min_power)
}
fn mcra_estimation(&self, signal: &Array1<f64>, alpha: f64) -> DeviceResult<f64> {
let current_power = signal.mapv(|x| x * x).mean().unwrap_or(0.0);
Ok(alpha * self.noise_estimate + (1.0 - alpha) * current_power)
}
fn imcra_estimation(&self, signal: &Array1<f64>, alpha_s: f64, alpha_d: f64) -> DeviceResult<f64> {
let current_power = signal.mapv(|x| x * x).mean().unwrap_or(0.0);
let alpha = if current_power > self.noise_estimate { alpha_s } else { alpha_d };
Ok(alpha * self.noise_estimate + (1.0 - alpha) * current_power)
}
}
impl SpectralAnalyzer {
pub fn new(config: &SpectralAnalysisConfig) -> Self {
Self {
config: config.clone(),
window_function: config.window_function.clone(),
spectral_cache: HashMap::new(),
}
}
pub fn analyze_signals(&mut self, signals: &HashMap<String, Array1<f64>>) -> DeviceResult<SpectralAnalysisResult> {
let mut power_spectral_density = HashMap::new();
let mut cross_spectral_density = HashMap::new();
let mut coherence = HashMap::new();
let mut transfer_functions = HashMap::new();
let mut spectral_peaks = HashMap::new();
for (signal_name, signal_data) in signals {
let psd = self.compute_psd(signal_data)?;
power_spectral_density.insert(signal_name.clone(), psd);
let peaks = self.find_spectral_peaks(signal_data)?;
spectral_peaks.insert(signal_name.clone(), peaks);
}
let signal_names: Vec<_> = signals.keys().collect();
for i in 0..signal_names.len() {
for j in (i + 1)..signal_names.len() {
let signal1_name = signal_names[i];
let signal2_name = signal_names[j];
let signal1_data = &signals[signal1_name];
let signal2_data = &signals[signal2_name];
let csd = self.compute_cross_spectral_density(signal1_data, signal2_data)?;
cross_spectral_density.insert((signal1_name.clone(), signal2_name.clone()), csd);
let coh = self.compute_coherence(signal1_data, signal2_data)?;
coherence.insert((signal1_name.clone(), signal2_name.clone()), coh);
let tf = self.compute_transfer_function(signal1_data, signal2_data)?;
transfer_functions.insert((signal1_name.clone(), signal2_name.clone()), tf);
}
}
Ok(SpectralAnalysisResult {
power_spectral_density,
cross_spectral_density,
coherence,
transfer_functions,
spectral_peaks,
})
}
fn compute_psd(&self, signal: &Array1<f64>) -> DeviceResult<Array1<f64>> {
match &self.config.estimation_method {
SpectralEstimationMethod::Periodogram => self.periodogram(signal),
SpectralEstimationMethod::Welch { nperseg, noverlap } => {
self.welch_method(signal, *nperseg, *noverlap)
},
SpectralEstimationMethod::Bartlett => self.bartlett_method(signal),
SpectralEstimationMethod::Multitaper { nw, k } => {
self.multitaper_method(signal, *nw, *k)
},
}
}
fn periodogram(&self, signal: &Array1<f64>) -> DeviceResult<Array1<f64>> {
let windowed_signal = self.apply_window(signal)?;
let fft_result = self.compute_fft(&windowed_signal)?;
let psd = fft_result.mapv(|x| x.norm_sqr() / signal.len() as f64);
Ok(psd)
}
fn welch_method(&self, signal: &Array1<f64>, nperseg: usize, noverlap: usize) -> DeviceResult<Array1<f64>> {
let step_size = nperseg - noverlap;
let mut psd_accumulator = Array1::zeros(nperseg / 2 + 1);
let mut num_segments = 0;
for start in (0..signal.len()).step_by(step_size) {
let end = std::cmp::min(start + nperseg, signal.len());
if end - start < nperseg {
break;
}
let segment = signal.slice(scirs2_core::ndarray::s![start..end]).to_owned();
let windowed_segment = self.apply_window(&segment)?;
let fft_result = self.compute_fft(&windowed_segment)?;
let segment_psd = fft_result.mapv(|x| x.norm_sqr());
let half_length = segment_psd.len() / 2 + 1;
psd_accumulator = psd_accumulator + segment_psd.slice(scirs2_core::ndarray::s![..half_length]);
num_segments += 1;
}
if num_segments > 0 {
psd_accumulator = psd_accumulator / num_segments as f64;
}
Ok(psd_accumulator)
}
fn bartlett_method(&self, signal: &Array1<f64>) -> DeviceResult<Array1<f64>> {
self.welch_method(signal, signal.len() / 8, 0)
}
fn multitaper_method(&self, signal: &Array1<f64>, nw: f64, k: usize) -> DeviceResult<Array1<f64>> {
self.periodogram(signal)
}
fn apply_window(&self, signal: &Array1<f64>) -> DeviceResult<Array1<f64>> {
let window = self.generate_window(signal.len())?;
Ok(signal * &window)
}
fn generate_window(&self, length: usize) -> DeviceResult<Array1<f64>> {
let mut window = Array1::zeros(length);
match &self.window_function {
WindowFunction::Rectangular => {
window.fill(1.0);
},
WindowFunction::Hanning => {
for (i, w) in window.iter_mut().enumerate() {
*w = 0.5 * (1.0 - (2.0 * std::f64::consts::PI * i as f64 / (length - 1) as f64).cos());
}
},
WindowFunction::Hamming => {
for (i, w) in window.iter_mut().enumerate() {
*w = 0.54 - 0.46 * (2.0 * std::f64::consts::PI * i as f64 / (length - 1) as f64).cos();
}
},
WindowFunction::Blackman => {
for (i, w) in window.iter_mut().enumerate() {
let n = i as f64 / (length - 1) as f64;
*w = 0.42 - 0.5 * (2.0 * std::f64::consts::PI * n).cos()
+ 0.08 * (4.0 * std::f64::consts::PI * n).cos();
}
},
WindowFunction::Kaiser { beta } => {
for (i, w) in window.iter_mut().enumerate() {
let n = i as f64 - (length - 1) as f64 / 2.0;
let alpha = (length - 1) as f64 / 2.0;
*w = self.modified_bessel_i0(*beta * (1.0 - (n / alpha).powi(2)).sqrt())
/ self.modified_bessel_i0(*beta);
}
},
WindowFunction::Tukey { alpha } => {
let transition_samples = (*alpha * length as f64 / 2.0) as usize;
for (i, w) in window.iter_mut().enumerate() {
if i < transition_samples {
*w = 0.5 * (1.0 + (std::f64::consts::PI * i as f64 / transition_samples as f64 - std::f64::consts::PI).cos());
} else if i < length - transition_samples {
*w = 1.0;
} else {
let idx = i - (length - transition_samples);
*w = 0.5 * (1.0 + (std::f64::consts::PI * idx as f64 / transition_samples as f64).cos());
}
}
},
}
Ok(window)
}
fn modified_bessel_i0(&self, x: f64) -> f64 {
let t = x / 3.75;
if x.abs() < 3.75 {
let t2 = t * t;
1.0 + 3.5156229 * t2 + 3.0899424 * t2 * t2 + 1.2067492 * t2 * t2 * t2
+ 0.2659732 * t2 * t2 * t2 * t2 + 0.0360768 * t2 * t2 * t2 * t2 * t2
+ 0.0045813 * t2 * t2 * t2 * t2 * t2 * t2
} else {
let t_inv = 1.0 / t;
(x.exp() / x.sqrt()) * (0.39894228 + 0.01328592 * t_inv + 0.00225319 * t_inv * t_inv
- 0.00157565 * t_inv * t_inv * t_inv + 0.00916281 * t_inv * t_inv * t_inv * t_inv
- 0.02057706 * t_inv * t_inv * t_inv * t_inv * t_inv)
}
}
fn compute_fft(&self, signal: &Array1<f64>) -> DeviceResult<Array1<Complex64>> {
let n = signal.len();
let mut result = Array1::zeros(n);
for k in 0..n {
let mut sum = Complex64::new(0.0, 0.0);
for j in 0..n {
let angle = -2.0 * std::f64::consts::PI * k as f64 * j as f64 / n as f64;
sum += signal[j] * Complex64::new(angle.cos(), angle.sin());
}
result[k] = sum;
}
Ok(result)
}
fn compute_cross_spectral_density(&self, signal1: &Array1<f64>, signal2: &Array1<f64>) -> DeviceResult<Array1<Complex64>> {
let fft1 = self.compute_fft(signal1)?;
let fft2 = self.compute_fft(signal2)?;
let csd = Array1::from_iter(fft1.iter().zip(fft2.iter()).map(|(x1, x2)| x1 * x2.conj()));
Ok(csd)
}
fn compute_coherence(&self, signal1: &Array1<f64>, signal2: &Array1<f64>) -> DeviceResult<Array1<f64>> {
let csd = self.compute_cross_spectral_density(signal1, signal2)?;
let psd1 = self.compute_psd(signal1)?;
let psd2 = self.compute_psd(signal2)?;
let coherence = Array1::from_iter(
csd.iter().zip(psd1.iter().zip(psd2.iter()))
.map(|(csd_val, (psd1_val, psd2_val))| {
csd_val.norm_sqr() / (psd1_val * psd2_val)
})
);
Ok(coherence)
}
fn compute_transfer_function(&self, input: &Array1<f64>, output: &Array1<f64>) -> DeviceResult<Array1<Complex64>> {
let csd = self.compute_cross_spectral_density(input, output)?;
let psd_input = self.compute_psd(input)?;
let transfer_function = Array1::from_iter(
csd.iter().zip(psd_input.iter())
.map(|(csd_val, psd_val)| {
if psd_val.abs() > 1e-12 {
csd_val / psd_val
} else {
Complex64::new(0.0, 0.0)
}
})
);
Ok(transfer_function)
}
fn find_spectral_peaks(&self, signal: &Array1<f64>) -> DeviceResult<Vec<SpectralPeak>> {
let psd = self.compute_psd(signal)?;
let mut peaks = Vec::new();
for i in 1..(psd.len() - 1) {
if psd[i] > psd[i - 1] && psd[i] > psd[i + 1] && psd[i] > 0.1 {
let frequency = i as f64 * self.config.sampling_frequency / (2.0 * psd.len() as f64);
peaks.push(SpectralPeak {
frequency,
amplitude: psd[i],
width: 1.0, significance: 0.9, q_factor: frequency / 1.0, });
}
}
Ok(peaks)
}
}
impl NoiseReductionMethod {
fn is_enabled(&self) -> bool {
true }
}