tekhsi_rs 0.1.1

High-performance client for Tektronix TekHSI enabled oscilloscopes
Documentation
use crate::data::{AnalogWaveform, IqWaveform};
use crate::fft::core::{FftResult, ReusableFftStateComplex, ReusableFftStateReal};
use crate::fft::window::FftWindow;

/// A Waveform that can be converted to an FFT magnitude
pub trait FftWaveform {
    /// Computes the FFT magnitude of the waveform
    ///
    /// ```rust
    /// use tekhsi_rs::data::{AnalogSamples, AnalogWaveform};
    /// use tekhsi_rs::fft::{FftWaveform, FftWindow};
    /// use smol_str::SmolStr;
    ///
    /// let waveform = AnalogWaveform {
    ///     source_name: SmolStr::new("ch1"),
    ///     y_axis_values: AnalogSamples::I16(vec![1, 2, 3, 4]),
    ///     y_axis_spacing: 1.0,
    ///     y_axis_offset: 0.0,
    ///     y_axis_units: SmolStr::new("V"),
    ///     x_axis_spacing: 1.0,
    ///     x_axis_units: SmolStr::new("s"),
    ///     trigger_index: 0.0,
    /// };
    /// let fft = waveform.fft_mag(FftWindow::Rectangular, None);
    /// assert!(!fft.bins.is_empty());
    /// ```
    fn fft_mag(&self, window: FftWindow, fft_len: Option<usize>) -> FftResult;

    /// Computes the FFT magnitude of the waveform while reusing existing allocations.
    ///
    /// The returned result is stored in `state` and is overwritten on the next call
    /// that uses the same `ReusableFftStateReal`.
    fn fft_mag_cached<'a>(
        &self,
        state: &'a mut ReusableFftStateReal,
        window: FftWindow,
        fft_len: Option<usize>,
    ) -> &'a mut FftResult;
}

impl FftWaveform for AnalogWaveform {
    fn fft_mag(&self, window: FftWindow, fft_len: Option<usize>) -> FftResult {
        ReusableFftStateReal::new()
            .fft_points_mag(
                self.iter_normalized_values(),
                analog_sample_rate(self),
                fft_len,
                window,
            )
            .clone()
    }

    fn fft_mag_cached<'a>(
        &self,
        state: &'a mut ReusableFftStateReal,
        window: FftWindow,
        fft_len: Option<usize>,
    ) -> &'a mut FftResult {
        state.fft_points_mag(
            self.iter_normalized_values(),
            analog_sample_rate(self),
            fft_len,
            window,
        )
    }
}

impl FftWaveform for IqWaveform {
    /// Computes the complex FFT of the waveform
    fn fft_mag(&self, window: FftWindow, fft_len: Option<usize>) -> FftResult {
        ReusableFftStateReal::new()
            .fft_points_mag(
                self.iter_normalized_values()
                    .map(|value| (value.re * value.re + value.im * value.im).sqrt()),
                iq_sample_rate(self),
                fft_len,
                window,
            )
            .clone()
    }

    /// Computes the complex FFT of the waveform while reusing existing allocations
    fn fft_mag_cached<'a>(
        &self,
        state: &'a mut ReusableFftStateReal,
        window: FftWindow,
        fft_len: Option<usize>,
    ) -> &'a mut FftResult {
        state.fft_points_mag(
            self.iter_normalized_values()
                .map(|value| (value.re * value.re + value.im * value.im).sqrt()),
            iq_sample_rate(self),
            fft_len,
            window,
        )
    }
}

/// A Waveform that can be converted to complex FFT values
pub trait ComplexFftWaveform {
    fn fft_complex(&self, window: FftWindow, fft_len: Option<usize>) -> FftResult;

    /// Computes the complex FFT of the waveform while reusing existing allocations.
    ///
    /// The returned result is stored in `state` and is overwritten on the next call
    /// that uses the same `ReusableFftStateComplex`.
    fn fft_complex_cached<'a>(
        &self,
        state: &'a mut ReusableFftStateComplex,
        window: FftWindow,
        fft_len: Option<usize>,
    ) -> &'a mut FftResult;
}

impl ComplexFftWaveform for IqWaveform {
    fn fft_complex(&self, window: FftWindow, fft_len: Option<usize>) -> FftResult {
        ReusableFftStateComplex::new()
            .fft_points_complex(
                self.iter_normalized_values(),
                iq_sample_rate(self),
                self.meta_info.iq_center_frequency,
                fft_len,
                window,
            )
            .clone()
    }

    fn fft_complex_cached<'a>(
        &self,
        state: &'a mut ReusableFftStateComplex,
        window: FftWindow,
        fft_len: Option<usize>,
    ) -> &'a mut FftResult {
        state.fft_points_complex(
            self.iter_normalized_values(),
            iq_sample_rate(self),
            self.meta_info.iq_center_frequency,
            fft_len,
            window,
        )
    }
}

fn analog_sample_rate(analog: &AnalogWaveform) -> f64 {
    if analog.x_axis_spacing > 0.0 {
        1.0 / analog.x_axis_spacing
    } else {
        0.0
    }
}

fn iq_sample_rate(iq: &IqWaveform) -> f64 {
    if iq.meta_info.iq_sample_rate > 0.0 {
        iq.meta_info.iq_sample_rate
    } else if iq.x_axis_spacing > 0.0 {
        1.0 / iq.x_axis_spacing
    } else {
        0.0
    }
}