hilbert_spectrum

Function hilbert_spectrum 

pub fn hilbert_spectrum(
    emd_result: &EMDResult,
    sample_rate: f64,
    config: &HilbertSpectrumConfig,
) -> Result<HilbertSpectrum>
Expand description

Build the HilbertSpectrum for emd_result at the given sample_rate.

IMF::frequency / IMF::amplitude are not consulted — the instantaneous attributes are recomputed fresh from each imf.data via hilbert_transform, so two calls with the same EMDResult always yield the same spectrum regardless of prior mutation.

§Band selection

Samples whose instantaneous frequency falls outside HilbertSpectrumConfig::freq_range are silently dropped — this is by design so callers can zoom into a band of interest, but a too-narrow band will yield a near-empty density without raising an error. Use (0.0, sample_rate / 2.0) if you want every IMF accounted for.

§Errors

Returns WaveletError::InvalidParameter when:

  • emd_result.imfs is empty,
  • any IMF has fewer than 4 samples (the minimum for Hilbert + central- difference frequency),
  • sample_rate is non-positive or non-finite,
  • num_freq_bins < 2,
  • freq_range.0 >= freq_range.1,
  • freq_range.0 < 0 (or ≤ 0 when log_freq = true),
  • freq_range.1 > sample_rate / 2 (above Nyquist),
  • time_smoothing = Some(0) (use None to disable smoothing).

§Examples

use ferro_wave::transform::{emd, hilbert_spectrum, EMDConfig, HilbertSpectrumConfig};
use ferro_wave::Signal;
use std::f64::consts::PI;

let sample_rate = 256.0;
let n = 1024;
// Pure 40 Hz tone — single IMF, single ridge.
let data: Vec<f64> = (0..n)
    .map(|i| (2.0 * PI * 40.0 * i as f64 / sample_rate).cos())
    .collect();
let signal = Signal::<f64>::new(data);
let emd_result = emd(&signal, &EMDConfig::default()).unwrap();

let cfg = HilbertSpectrumConfig::new(0.0, sample_rate / 2.0);
let spectrum = hilbert_spectrum(&emd_result, sample_rate, &cfg).unwrap();

assert_eq!(spectrum.density.len(), n);
assert_eq!(spectrum.frequencies.len(), cfg.num_freq_bins);
assert!(spectrum.marginal().iter().any(|&v| v > 0.0));