lasp/src/lasp/dsp/lasp_avpowerspectra.h

169 lines
5.0 KiB
C++

#pragma once
#include "lasp_fft.h"
#include "lasp_mathtypes.h"
#include "lasp_timebuffer.h"
#include "lasp_window.h"
#include <memory>
#include <optional>
/** \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 Optionally, 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. It can be checked by operator bool().
*
*/
std::optional<ccube> compute(const dmat &timedata);
/**
* @brief Returns the latest estimate of cps (cross-power spectra.
*
* @return Pointer (reference, not owning!) to spectral estimate date, or
* nullptr, in case nothing could yet be computed.
*/
std::optional<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; }
};
/** @} */