#pragma once #include "lasp_fft.h" #include "lasp_mathtypes.h" #include "lasp_timebuffer.h" #include "lasp_window.h" #include #include /** \defgroup dsp Digital Signal Processing utilities * These are classes and functions used for processing raw signal data, to * obtain statistics. * * * @{ */ /** * @brief Computes single-sided cross-power spectra for a group of channels. * Only a single block of length fft, no averaging. This class should normally * not be used. Please refer to AvPowerSpectra instead. */ class PowerSpectra { public: /** * @brief The FFT length */ us nfft; private: /** * @brief Instance to compute actual FFT's */ Fft _fft; vd _window; d _scale_fac; public: /** * @brief Initalize power spectra computer * * @param nfft The fft length * @param w The window type */ PowerSpectra(const us nfft, const Window::WindowType w); /** * @brief Initalize power spectra computer * * @param window Uses the window length to compute fft length, and uses the * window shape as the actual window. */ PowerSpectra(const vd &window); /** * @brief Computes the spectra. Data is normalized by the (spectral) power of * the window, to compensate for the effect of the window. * * @param input Input data, first axis is assumed the sample, second axis the * channel. Input first dimension should be equal to nfft, otherwise a * runtime error is thrown. * * @return Cross-power-spectra. Cubic array with size indices C_fij, where * f is the frequency index, i is the row and j is the column. An element * can be accessed by: C(f, i, j). Storage is such that frequency components * are contiguous. */ ccube compute(const dmat &input); }; /** * @brief Estimate cross-power spectra using Welch' method of spectral * estimation. The exact amount of overlap in Welch' method is rounded up to a * certain amount of samples. */ class AvPowerSpectra { enum class Mode { Averaging = 0, // Averaging all time date Leaking = 1, // Exponential weighting of an "instantaneous cps" Spectrogram = 2 // Instantenous spectrum, no averaging }; Mode _mode; d _alpha; // Only valid in case of 'Leaking' us n_averages = 0; // Only valid in case of 'Averaging' PowerSpectra _ps; /** * @brief Current estimate of cross-spectral density */ ccube _est; /** * @brief Buffer of storage of time data. */ TimeBuffer _timeBuf; /** * @brief The amount of samples to keep in the overlap */ us _overlap_keep; public: /** * @brief Initalize averaged power spectra computer. If a time constant is * given > 0, it is used in a kind of exponential weighting. * * @param nfft The fft length * @param w The window type. * @param overlap_percentage A number 0 < overlap_percentage <= 100. It * determines the amount of overlap used in Welch' method. A typical value is * 50 %, i.e. 50. * @param fs_tau Value should either be < 0, indicating that the * estimate is averages over all time data. * For a value = 0 the instantaneous power spectrum is returned, which can be * interpreted as the spectrogram. For a value > 0 a exponential forgetting is * used, where the value is used as the time constant such that the decay * follows approximately the trend exp(-n/fs_tau), where n is the * sample number in the power spectra. To choose 'fast' time weighting, set * time_constant to the value of fs*0.125, where fs denotes the sampling * frequency. **/ AvPowerSpectra(const us nfft = 2048, const Window::WindowType w = Window::WindowType::Hann, const d overlap_percentage = 50., const d fs_tau = -1); AvPowerSpectra(const AvPowerSpectra &) = delete; AvPowerSpectra &operator=(const AvPowerSpectra &) = delete; /** * @brief Reset to empty state. Clears the time buffer and sets estimator to * empty. */ void reset(); /** * @brief Compute an update of the power spectra based on given time data. * Note that the number of channels is determined from the first time this * function is called. If a later call has an incompatible number of * channels, a runtime error is thrown. * * @param timedata * * @return a copy of the latest estimate of the power spectra. an * update is only given if the amount of new time data is enough to compute a * new estimate. if no new estimate is available, it returns an empty ccube. * Note that the latest available estimate can be obtained using get_est(). * */ ccube compute(const dmat &timedata); /** * @brief Returns the latest estimate of cps (cross-power spectra. * * @return a copy of the latest estimate of the power spectra. an * update is only given if the amount of new time data is enough to compute a * new estimate. If no estimate is available, it returns an empty ccube. * */ ccube get_est() const; /** * @brief The overlap is rounded to a certain amount of time samples. This * function returns that value. * * @return The amount of samples in overlapping. */ us exactOverlapSamples() const { return _ps.nfft - _overlap_keep; } }; /** @} */