use crate::error::{StatsError, StatsResult};
use scirs2_core::ndarray::{Array1, Array2, Array3, Array4, ArrayView1, ArrayView2};
use scirs2_core::numeric::{Float, FloatConst, NumCast, One, Zero};
use scirs2_core::{simd_ops::SimdUnifiedOps, validation::*};
use scirs2_linalg::parallel_dispatch::ParallelConfig;
use std::collections::HashMap;
use std::marker::PhantomData;
pub struct AdvancedSpectralAnalyzer<F> {
config: AdvancedSpectralConfig<F>,
cache: SpectralCache<F>,
performance: SpectralPerformanceMetrics,
_phantom: PhantomData<F>,
}
pub struct AdvancedSpectralConfig<F> {
pub fs: F,
pub windows: Vec<WindowFunction>,
pub multitaper_config: MultiTaperConfig<F>,
pub wavelet_config: WaveletConfig<F>,
pub hos_config: HigherOrderSpectralConfig<F>,
pub coherence_config: CoherenceConfig<F>,
pub nonstationary_config: NonStationaryConfig<F>,
pub ml_config: MLSpectralConfig<F>,
pub parallel_config: ParallelConfig,
}
#[derive(Debug, Clone)]
pub struct MultiTaperConfig<F> {
pub nw: F,
pub k: usize,
pub adaptive: bool,
pub jackknife: bool,
pub f_test: bool,
}
#[derive(Debug, Clone)]
pub struct WaveletConfig<F> {
pub wavelet_type: WaveletType,
pub scales: usize,
pub f_min: F,
pub f_max: F,
pub q_factor: F,
pub continuous: bool,
pub packet_transform: bool,
}
#[derive(Debug, Clone)]
pub struct HigherOrderSpectralConfig<F> {
pub compute_bispectrum: bool,
pub compute_trispectrum: bool,
pub max_lag: usize,
pub overlap: F,
pub segment_length: usize,
}
#[derive(Debug, Clone)]
pub struct CoherenceConfig<F> {
pub magnitude_squared: bool,
pub complex_coherence: bool,
pub partial_coherence: bool,
pub multiple_coherence: bool,
pub frequency_resolution: F,
pub confidence_level: F,
}
#[derive(Debug, Clone)]
pub struct NonStationaryConfig<F> {
pub stft_windowsize: usize,
pub stft_overlap: F,
pub spectrogram_type: SpectrogramType,
pub time_varying: bool,
pub adaptive_window: bool,
}
#[derive(Debug, Clone)]
pub struct MLSpectralConfig<F> {
pub neural_enhancement: bool,
pub autoencoder_denoising: bool,
pub adversarial_sr: bool,
pub rl_adaptation: bool,
pub network_params: NetworkParams<F>,
}
#[derive(Debug, Clone)]
pub struct NetworkParams<F> {
pub hiddensizes: Vec<usize>,
pub activation: ActivationFunction,
pub learning_rate: F,
pub regularization: F,
pub epochs: usize,
}
#[derive(Debug, Clone, Copy)]
pub enum WindowFunction {
Rectangular,
Hann,
Hamming,
Blackman,
BlackmanHarris,
Kaiser(f64),
Tukey(f64),
Gaussian(f64),
DolphChebyshev(f64),
AdaptiveOptimal,
}
#[derive(Debug, Clone, Copy)]
pub enum WaveletType {
Morlet,
MexicanHat,
Daubechies(usize),
Biorthogonal(usize, usize),
Coiflets(usize),
ComplexMorlet,
Gabor,
Meyer,
Shannon,
}
#[derive(Debug, Clone, Copy)]
pub enum SpectrogramType {
PowerSpectralDensity,
CrossSpectralDensity,
Phase,
InstantaneousFrequency,
GroupDelay,
Reassigned,
Synchrosqueezed,
}
#[derive(Debug, Clone, Copy)]
pub enum ActivationFunction {
ReLU,
LeakyReLU(f64),
ELU(f64),
Swish,
GELU,
Tanh,
Sigmoid,
Softplus,
}
#[derive(Debug, Clone)]
pub struct AdvancedSpectralResults<F> {
pub psd: Array2<F>,
pub frequencies: Array1<F>,
pub times: Option<Array1<F>>,
pub confidence_intervals: Option<Array3<F>>,
pub coherence: Option<CoherenceResults<F>>,
pub higher_order: Option<HigherOrderResults<F>>,
pub wavelet: Option<WaveletResults<F>>,
pub ml_enhanced: Option<MLSpectralResults<F>>,
pub performance: SpectralPerformanceMetrics,
}
#[derive(Debug, Clone)]
pub struct CoherenceResults<F> {
pub magnitude_squared: Option<Array2<F>>,
pub complex_coherence: Option<Array2<scirs2_core::numeric::Complex<F>>>,
pub partial_coherence: Option<Array3<F>>,
pub multiple_coherence: Option<Array2<F>>,
pub significance: Option<Array2<F>>,
}
#[derive(Debug, Clone)]
pub struct HigherOrderResults<F> {
pub bispectrum: Option<Array3<scirs2_core::numeric::Complex<F>>>,
pub bicoherence: Option<Array3<F>>,
pub trispectrum: Option<Array4<scirs2_core::numeric::Complex<F>>>,
pub tricoherence: Option<Array4<F>>,
}
#[derive(Debug, Clone)]
pub struct WaveletResults<F> {
pub cwt_coefficients: Option<Array3<scirs2_core::numeric::Complex<F>>>,
pub dwt_coefficients: Option<Vec<Array1<F>>>,
pub packet_coefficients: Option<HashMap<String, Array1<F>>>,
pub ridges: Option<Array2<usize>>,
pub instantaneous_frequency: Option<Array2<F>>,
}
#[derive(Debug, Clone)]
pub struct MLSpectralResults<F> {
pub denoised_spectrum: Option<Array2<F>>,
pub super_resolution: Option<Array2<F>>,
pub learned_features: Option<Array2<F>>,
pub anomaly_scores: Option<Array1<F>>,
pub uncertainty: Option<Array2<F>>,
}
#[derive(Debug, Clone)]
pub struct SpectralPerformanceMetrics {
pub timing: HashMap<String, f64>,
pub memory_usage: MemoryUsageStats,
pub accuracy: AccuracyMetrics,
pub convergence: ConvergenceMetrics,
}
#[derive(Debug, Clone)]
pub struct MemoryUsageStats {
pub peak_usage: usize,
pub average_usage: usize,
pub cache_efficiency: f64,
pub allocation_count: usize,
}
#[derive(Debug, Clone)]
pub struct AccuracyMetrics {
pub relative_error: f64,
pub absolute_error: f64,
pub snr_improvement: f64,
pub frequency_resolution: f64,
}
#[derive(Debug, Clone)]
pub struct ConvergenceMetrics {
pub iterations: usize,
pub final_residual: f64,
pub convergence_rate: f64,
pub stability: f64,
}
struct SpectralCache<F> {
windows: HashMap<String, Array1<F>>,
fft_plans: HashMap<usize, Vec<u8>>, wavelets: HashMap<String, Array2<scirs2_core::numeric::Complex<F>>>,
tapers: HashMap<String, Array2<F>>,
}
impl<F> AdvancedSpectralAnalyzer<F>
where
F: Float
+ NumCast
+ FloatConst
+ SimdUnifiedOps
+ One
+ Zero
+ PartialOrd
+ Copy
+ Send
+ Sync
+ std::fmt::Display,
{
pub fn new(config: AdvancedSpectralConfig<F>) -> Self {
let cache = SpectralCache {
windows: HashMap::new(),
fft_plans: HashMap::new(),
wavelets: HashMap::new(),
tapers: HashMap::new(),
};
let performance = SpectralPerformanceMetrics {
timing: HashMap::new(),
memory_usage: MemoryUsageStats {
peak_usage: 0,
average_usage: 0,
cache_efficiency: 0.0,
allocation_count: 0,
},
accuracy: AccuracyMetrics {
relative_error: 0.0,
absolute_error: 0.0,
snr_improvement: 0.0,
frequency_resolution: 0.0,
},
convergence: ConvergenceMetrics {
iterations: 0,
final_residual: 0.0,
convergence_rate: 0.0,
stability: 0.0,
},
};
Self {
config,
cache,
performance: SpectralPerformanceMetrics {
timing: HashMap::new(),
memory_usage: MemoryUsageStats {
peak_usage: 0,
average_usage: 0,
cache_efficiency: 0.0,
allocation_count: 0,
},
accuracy: AccuracyMetrics {
relative_error: 0.0,
absolute_error: 0.0,
snr_improvement: 0.0,
frequency_resolution: 0.0,
},
convergence: ConvergenceMetrics {
iterations: 0,
final_residual: 0.0,
convergence_rate: 1.0,
stability: 1.0,
},
},
_phantom: PhantomData,
}
}
pub fn analyze_comprehensive(
&mut self,
signal: &ArrayView1<F>,
) -> StatsResult<AdvancedSpectralResults<F>> {
checkarray_finite(signal, "signal")?;
check_min_samples(signal, 2, "signal")?;
let start_time = std::time::Instant::now();
let mut results = AdvancedSpectralResults {
psd: Array2::zeros((0, 0)),
frequencies: Array1::zeros(0),
times: None,
confidence_intervals: None,
coherence: None,
higher_order: None,
wavelet: None,
ml_enhanced: None,
performance: self.performance.clone(),
};
let (psd, frequencies) = self.multitaper_psd(signal)?;
results.psd = psd;
results.frequencies = frequencies;
if self.config.multitaper_config.jackknife {
results.confidence_intervals = Some(self.compute_confidence_intervals(signal)?);
}
if self.config.wavelet_config.continuous || self.config.wavelet_config.packet_transform {
results.wavelet = Some(self.wavelet_analysis(signal)?);
}
if self.config.hos_config.compute_bispectrum || self.config.hos_config.compute_trispectrum {
results.higher_order = Some(self.higher_order_analysis(signal)?);
}
if self.config.ml_config.neural_enhancement {
results.ml_enhanced = Some(self.ml_spectral_enhancement(signal, &results.psd)?);
}
let elapsed = start_time.elapsed();
self.performance
.timing
.insert("total_analysis".to_string(), elapsed.as_secs_f64());
results.performance = self.performance.clone();
Ok(results)
}
pub fn analyze_coherence(
&mut self,
signals: &ArrayView2<F>,
) -> StatsResult<CoherenceResults<F>> {
checkarray_finite(signals, "signals")?;
let (_n_samples_, n_channels) = signals.dim();
if n_channels < 2 {
return Err(StatsError::InvalidArgument(
"Need at least 2 channels for coherence analysis".to_string(),
));
}
let mut coherence_results = CoherenceResults {
magnitude_squared: None,
complex_coherence: None,
partial_coherence: None,
multiple_coherence: None,
significance: None,
};
if self.config.coherence_config.magnitude_squared {
coherence_results.magnitude_squared =
Some(self.compute_magnitude_squared_coherence(signals)?);
}
if self.config.coherence_config.complex_coherence {
coherence_results.complex_coherence = Some(self.compute_complex_coherence(signals)?);
}
if self.config.coherence_config.partial_coherence {
coherence_results.partial_coherence = Some(self.compute_partial_coherence(signals)?);
}
if self.config.coherence_config.multiple_coherence {
coherence_results.multiple_coherence = Some(self.compute_multiple_coherence(signals)?);
}
Ok(coherence_results)
}
pub fn time_frequency_analysis(&mut self, signal: &ArrayView1<F>) -> StatsResult<Array3<F>> {
checkarray_finite(signal, "signal")?;
let n_samples_ = signal.len();
let windowsize = self.config.nonstationary_config.stft_windowsize;
let overlap = self.config.nonstationary_config.stft_overlap;
let hopsize = ((F::one() - overlap)
* F::from(windowsize).expect("Failed to convert to float"))
.to_usize()
.expect("Operation failed");
let n_windows = (n_samples_ - windowsize) / hopsize + 1;
let n_freqs = windowsize / 2 + 1;
let mut spectrogram = Array3::zeros((n_freqs, n_windows, 1));
let window = self.generate_window(WindowFunction::Hann, windowsize)?;
for (win_idx, window_start) in (0..n_samples_ - windowsize + 1)
.step_by(hopsize)
.enumerate()
{
if win_idx >= n_windows {
break;
}
let window_end = window_start + windowsize;
let windowed_signal = self.apply_window(
&signal.slice(scirs2_core::ndarray::s![window_start..window_end]),
&window.view(),
)?;
let spectrum = self.compute_fft(&windowed_signal)?;
for (freq_idx, &coeff) in spectrum.iter().enumerate().take(n_freqs) {
spectrogram[[freq_idx, win_idx, 0]] = coeff.norm_sqr();
}
}
Ok(spectrogram)
}
pub fn detect_spectral_peaks(
&self,
psd: &ArrayView1<F>,
frequencies: &ArrayView1<F>,
) -> StatsResult<Vec<SpectralPeak<F>>> {
checkarray_finite(psd, "psd")?;
checkarray_finite(frequencies, "frequencies")?;
if psd.len() != frequencies.len() {
return Err(StatsError::InvalidArgument(
"PSD and frequency arrays must have same length".to_string(),
));
}
let mut peaks = Vec::new();
let n = psd.len();
for i in 1..n - 1 {
if psd[i] > psd[i - 1] && psd[i] > psd[i + 1] {
let peak = SpectralPeak {
frequency: frequencies[i],
amplitude: psd[i],
phase: F::zero(), bandwidth: F::zero(), quality_factor: F::zero(), confidence: F::one(), };
peaks.push(peak);
}
}
peaks.sort_by(|a, b| {
b.amplitude
.partial_cmp(&a.amplitude)
.unwrap_or(std::cmp::Ordering::Equal)
});
Ok(peaks)
}
fn multitaper_psd(&mut self, signal: &ArrayView1<F>) -> StatsResult<(Array2<F>, Array1<F>)> {
let n = signal.len();
let nw = self.config.multitaper_config.nw;
let k = self.config.multitaper_config.k;
let tapers = self.generate_slepian_tapers(n, nw, k)?;
let n_freqs = n / 2 + 1;
let mut psd = Array2::zeros((n_freqs, 1));
let frequencies = self.generate_frequency_grid(n);
for taper_idx in 0..k {
let tapered_signal = self.apply_taper(signal, &tapers.column(taper_idx))?;
let spectrum = self.compute_fft(&tapered_signal)?;
for (freq_idx, &coeff) in spectrum.iter().enumerate().take(n_freqs) {
psd[[freq_idx, 0]] = psd[[freq_idx, 0]]
+ coeff.norm_sqr() / F::from(k).expect("Failed to convert to float");
}
}
Ok((psd, frequencies))
}
fn wavelet_analysis(&mut self, signal: &ArrayView1<F>) -> StatsResult<WaveletResults<F>> {
let mut results = WaveletResults {
cwt_coefficients: None,
dwt_coefficients: None,
packet_coefficients: None,
ridges: None,
instantaneous_frequency: None,
};
if self.config.wavelet_config.continuous {
results.cwt_coefficients = Some(self.compute_cwt(signal)?);
}
if !self.config.wavelet_config.continuous {
results.dwt_coefficients = Some(self.compute_dwt(signal)?);
}
if self.config.wavelet_config.packet_transform {
results.packet_coefficients = Some(self.compute_wavelet_packets(signal)?);
}
Ok(results)
}
fn higher_order_analysis(
&mut self,
signal: &ArrayView1<F>,
) -> StatsResult<HigherOrderResults<F>> {
let mut results = HigherOrderResults {
bispectrum: None,
bicoherence: None,
trispectrum: None,
tricoherence: None,
};
if self.config.hos_config.compute_bispectrum {
let (bispectrum, bicoherence) = self.compute_bispectrum(signal)?;
results.bispectrum = Some(bispectrum);
results.bicoherence = Some(bicoherence);
}
if self.config.hos_config.compute_trispectrum {
let (trispectrum, tricoherence) = self.compute_trispectrum(signal)?;
results.trispectrum = Some(trispectrum);
results.tricoherence = Some(tricoherence);
}
Ok(results)
}
fn ml_spectral_enhancement(
&self,
_signal: &ArrayView1<F>,
psd: &Array2<F>,
) -> StatsResult<MLSpectralResults<F>> {
let mut results = MLSpectralResults {
denoised_spectrum: None,
super_resolution: None,
learned_features: None,
anomaly_scores: None,
uncertainty: None,
};
if self.config.ml_config.autoencoder_denoising {
results.denoised_spectrum = Some(self.neural_denoising(psd)?);
}
if self.config.ml_config.adversarial_sr {
results.super_resolution = Some(self.spectral_super_resolution(psd)?);
}
Ok(results)
}
fn generate_window(&self, windowtype: WindowFunction, size: usize) -> StatsResult<Array1<F>> {
let mut window = Array1::zeros(size);
let n_f = F::from(size).expect("Failed to convert to float");
match windowtype {
WindowFunction::Hann => {
for i in 0..size {
let i_f = F::from(i).expect("Failed to convert to float");
let two_pi =
F::from(2.0).expect("Failed to convert constant to float") * F::PI();
window[i] = F::from(0.5).expect("Failed to convert constant to float")
* (F::one() - (two_pi * i_f / n_f).cos());
}
}
WindowFunction::Hamming => {
for i in 0..size {
let i_f = F::from(i).expect("Failed to convert to float");
let two_pi =
F::from(2.0).expect("Failed to convert constant to float") * F::PI();
window[i] = F::from(0.54).expect("Failed to convert constant to float")
- F::from(0.46).expect("Failed to convert constant to float")
* (two_pi * i_f / n_f).cos();
}
}
WindowFunction::Rectangular => {
window.fill(F::one());
}
_ => {
return Err(StatsError::InvalidArgument(
"Window function not implemented".to_string(),
));
}
}
Ok(window)
}
fn apply_window(
&self,
signal: &ArrayView1<F>,
window: &ArrayView1<F>,
) -> StatsResult<Array1<F>> {
if signal.len() != window.len() {
return Err(StatsError::InvalidArgument(
"Signal and window must have same length".to_string(),
));
}
let mut windowed = Array1::zeros(signal.len());
for i in 0..signal.len() {
windowed[i] = signal[i] * window[i];
}
Ok(windowed)
}
fn compute_fft(
&self,
signal: &Array1<F>,
) -> StatsResult<Vec<scirs2_core::numeric::Complex<F>>> {
let n = signal.len();
let mut spectrum = Vec::with_capacity(n);
for k in 0..n {
let mut sum = scirs2_core::numeric::Complex::new(F::zero(), F::zero());
for j in 0..n {
let angle = -F::from(2.0).expect("Failed to convert constant to float")
* F::PI()
* F::from(k).expect("Failed to convert to float")
* F::from(j).expect("Failed to convert to float")
/ F::from(n).expect("Failed to convert to float");
let complex_exp = scirs2_core::numeric::Complex::new(angle.cos(), angle.sin());
sum = sum + scirs2_core::numeric::Complex::new(signal[j], F::zero()) * complex_exp;
}
spectrum.push(sum);
}
Ok(spectrum)
}
fn generate_frequency_grid(&self, n: usize) -> Array1<F> {
let mut frequencies = Array1::zeros(n / 2 + 1);
let fs = self.config.fs;
let n_f = F::from(n).expect("Failed to convert to float");
for i in 0..frequencies.len() {
frequencies[i] = fs * F::from(i).expect("Failed to convert to float") / n_f;
}
frequencies
}
fn generate_slepian_tapers(&mut self, n: usize, nw: F, k: usize) -> StatsResult<Array2<F>> {
let mut tapers = Array2::zeros((n, k));
for taper_idx in 0..k {
for i in 0..n {
let i_f = F::from(i).expect("Failed to convert to float");
let n_f = F::from(n).expect("Failed to convert to float");
let phase = F::from(2.0).expect("Failed to convert constant to float")
* F::PI()
* F::from(taper_idx).expect("Failed to convert to float")
* i_f
/ n_f;
tapers[[i, taper_idx]] = phase.sin();
}
}
Ok(tapers)
}
fn apply_taper(&self, signal: &ArrayView1<F>, taper: &ArrayView1<F>) -> StatsResult<Array1<F>> {
self.apply_window(signal, taper)
}
fn compute_confidence_intervals(&self, signal: &ArrayView1<F>) -> StatsResult<Array3<F>> {
let n_freqs = signal.len() / 2 + 1;
Ok(Array3::zeros((n_freqs, 1, 2))) }
fn compute_magnitude_squared_coherence(
&self,
signals: &ArrayView2<F>,
) -> StatsResult<Array2<F>> {
let (_, n_channels) = signals.dim();
let n_freqs = signals.nrows() / 2 + 1;
Ok(Array2::zeros((n_freqs, n_channels * (n_channels - 1) / 2)))
}
fn compute_complex_coherence(
&self,
signals: &ArrayView2<F>,
) -> StatsResult<Array2<scirs2_core::numeric::Complex<F>>> {
let (_, n_channels) = signals.dim();
let n_freqs = signals.nrows() / 2 + 1;
let n_pairs = n_channels * (n_channels - 1) / 2;
Ok(Array2::from_elem(
(n_freqs, n_pairs),
scirs2_core::numeric::Complex::new(F::zero(), F::zero()),
))
}
fn compute_partial_coherence(&self, signals: &ArrayView2<F>) -> StatsResult<Array3<F>> {
let (_, n_channels) = signals.dim();
let n_freqs = signals.nrows() / 2 + 1;
Ok(Array3::zeros((n_freqs, n_channels, n_channels)))
}
fn compute_multiple_coherence(&self, signals: &ArrayView2<F>) -> StatsResult<Array2<F>> {
let (_, n_channels) = signals.dim();
let n_freqs = signals.nrows() / 2 + 1;
Ok(Array2::zeros((n_freqs, n_channels)))
}
fn compute_cwt(
&self,
signal: &ArrayView1<F>,
) -> StatsResult<Array3<scirs2_core::numeric::Complex<F>>> {
let n_samples_ = signal.len();
let n_scales = self.config.wavelet_config.scales;
Ok(Array3::from_elem(
(n_scales, n_samples_, 1),
scirs2_core::numeric::Complex::new(F::zero(), F::zero()),
))
}
fn compute_dwt(&self, signal: &ArrayView1<F>) -> StatsResult<Vec<Array1<F>>> {
let n_levels = (signal.len() as f64).log2().floor() as usize;
let mut coefficients = Vec::new();
for level in 0..n_levels {
let size = signal.len() >> level;
coefficients.push(Array1::zeros(size));
}
Ok(coefficients)
}
fn compute_wavelet_packets(
&self,
signal: &ArrayView1<F>,
) -> StatsResult<HashMap<String, Array1<F>>> {
let mut packets = HashMap::new();
packets.insert("root".to_string(), signal.to_owned());
Ok(packets)
}
fn compute_bispectrum(
&self,
signal: &ArrayView1<F>,
) -> StatsResult<(Array3<scirs2_core::numeric::Complex<F>>, Array3<F>)> {
let n = signal.len();
let n_freqs = n / 2 + 1;
let bispectrum = Array3::from_elem(
(n_freqs, n_freqs, 1),
scirs2_core::numeric::Complex::new(F::zero(), F::zero()),
);
let bicoherence = Array3::zeros((n_freqs, n_freqs, 1));
Ok((bispectrum, bicoherence))
}
fn compute_trispectrum(
&self,
signal: &ArrayView1<F>,
) -> StatsResult<(Array4<scirs2_core::numeric::Complex<F>>, Array4<F>)> {
let n = signal.len();
let n_freqs = n / 2 + 1;
let trispectrum = Array4::from_elem(
(n_freqs, n_freqs, n_freqs, 1),
scirs2_core::numeric::Complex::new(F::zero(), F::zero()),
);
let tricoherence = Array4::zeros((n_freqs, n_freqs, n_freqs, 1));
Ok((trispectrum, tricoherence))
}
fn neural_denoising(&self, psd: &Array2<F>) -> StatsResult<Array2<F>> {
Ok(psd.clone())
}
fn spectral_super_resolution(&self, psd: &Array2<F>) -> StatsResult<Array2<F>> {
let (n_freqs, n_channels) = psd.dim();
Ok(Array2::zeros((n_freqs * 2, n_channels))) }
}
#[derive(Debug, Clone)]
pub struct SpectralPeak<F> {
pub frequency: F,
pub amplitude: F,
pub phase: F,
pub bandwidth: F,
pub quality_factor: F,
pub confidence: F,
}
impl<F> Default for AdvancedSpectralConfig<F>
where
F: Float + NumCast + FloatConst + Copy + std::fmt::Display,
{
fn default() -> Self {
Self {
fs: F::one(),
windows: vec![WindowFunction::Hann],
multitaper_config: MultiTaperConfig {
nw: F::from(4.0).expect("Failed to convert constant to float"),
k: 7,
adaptive: true,
jackknife: true,
f_test: true,
},
wavelet_config: WaveletConfig {
wavelet_type: WaveletType::Morlet,
scales: 64,
f_min: F::from(0.1).expect("Failed to convert constant to float"),
f_max: F::from(0.5).expect("Failed to convert constant to float"),
q_factor: F::from(5.0).expect("Failed to convert constant to float"),
continuous: true,
packet_transform: false,
},
hos_config: HigherOrderSpectralConfig {
compute_bispectrum: false,
compute_trispectrum: false,
max_lag: 100,
overlap: F::from(0.5).expect("Failed to convert constant to float"),
segment_length: 512,
},
coherence_config: CoherenceConfig {
magnitude_squared: true,
complex_coherence: true,
partial_coherence: false,
multiple_coherence: false,
frequency_resolution: F::from(0.01).expect("Failed to convert constant to float"),
confidence_level: F::from(0.95).expect("Failed to convert constant to float"),
},
nonstationary_config: NonStationaryConfig {
stft_windowsize: 256,
stft_overlap: F::from(0.75).expect("Failed to convert constant to float"),
spectrogram_type: SpectrogramType::PowerSpectralDensity,
time_varying: true,
adaptive_window: false,
},
ml_config: MLSpectralConfig {
neural_enhancement: false,
autoencoder_denoising: false,
adversarial_sr: false,
rl_adaptation: false,
network_params: NetworkParams {
hiddensizes: vec![128, 64, 32],
activation: ActivationFunction::ReLU,
learning_rate: F::from(0.001).expect("Failed to convert constant to float"),
regularization: F::from(0.01).expect("Failed to convert constant to float"),
epochs: 100,
},
},
parallel_config: ParallelConfig::default(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::array;
#[test]
fn test_spectral_analyzer_creation() {
let config = AdvancedSpectralConfig::default();
let analyzer = AdvancedSpectralAnalyzer::<f64>::new(config);
assert_eq!(analyzer.config.fs, 1.0);
assert_eq!(analyzer.config.multitaper_config.k, 7);
}
#[test]
fn test_window_generation() {
let config = AdvancedSpectralConfig::default();
let analyzer = AdvancedSpectralAnalyzer::<f64>::new(config);
let window = analyzer
.generate_window(WindowFunction::Hann, 10)
.expect("Operation failed");
assert_eq!(window.len(), 10);
assert!(window[0] < window[5]); }
#[test]
fn test_frequency_grid() {
let mut config = AdvancedSpectralConfig::default();
config.fs = 100.0;
let analyzer = AdvancedSpectralAnalyzer::<f64>::new(config);
let freqs = analyzer.generate_frequency_grid(20);
assert_eq!(freqs.len(), 11); assert_eq!(freqs[0], 0.0);
assert!((freqs[freqs.len() - 1] - 50.0).abs() < 1e-10); }
#[test]
fn test_comprehensive_analysis() {
let config = AdvancedSpectralConfig::default();
let mut analyzer = AdvancedSpectralAnalyzer::<f64>::new(config);
let n = 128;
let mut signal = Array1::zeros(n);
for i in 0..n {
let t = i as f64 / 10.0;
signal[i] = (2.0 * std::f64::consts::PI * t).sin() + 0.1 * (i as f64).sin();
}
let result = analyzer
.analyze_comprehensive(&signal.view())
.expect("Operation failed");
assert!(result.frequencies.len() > 0);
assert!(result.psd.nrows() > 0);
assert!(result.performance.timing.contains_key("total_analysis"));
}
#[test]
fn test_time_frequency_analysis() {
let mut config = AdvancedSpectralConfig::default();
config.nonstationary_config.stft_windowsize = 32;
config.nonstationary_config.stft_overlap = 0.5;
let mut analyzer = AdvancedSpectralAnalyzer::<f64>::new(config);
let signal = array![
1.0, 2.0, 3.0, 4.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 4.0, 3.0,
2.0, 1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0, 1.0, 2.0, 3.0, 4.0,
5.0, 4.0, 3.0, 2.0, 1.0, 0.0
];
let result = analyzer
.time_frequency_analysis(&signal.view())
.expect("Operation failed");
assert!(result.ndim() == 3);
assert!(result.shape()[0] > 0); assert!(result.shape()[1] > 0); }
#[test]
fn test_spectral_peak_detection() {
let config = AdvancedSpectralConfig::default();
let analyzer = AdvancedSpectralAnalyzer::<f64>::new(config);
let psd = array![1.0, 2.0, 10.0, 2.0, 1.0, 3.0, 15.0, 3.0, 1.0, 2.0];
let freqs = array![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
let peaks = analyzer
.detect_spectral_peaks(&psd.view(), &freqs.view())
.expect("Operation failed");
assert!(peaks.len() >= 2); assert!(peaks[0].amplitude >= peaks[1].amplitude); }
}