27#include <unsupported/Eigen/FFT>
45constexpr double WELCH_PI = 3.14159265358979323846;
52VectorXd WelchPsd::buildWindow(
int iN, WindowType window)
55 const double pi2 = 2.0 * WELCH_PI;
57 for (
int n = 0; n < iN; ++n) {
58 const double t =
static_cast<double>(n) /
static_cast<double>(iN - 1);
61 w[n] = 0.5 * (1.0 - std::cos(pi2 * t));
64 w[n] = 0.54 - 0.46 * std::cos(pi2 * t);
67 w[n] = 0.42 - 0.5 * std::cos(pi2 * t) + 0.08 * std::cos(2.0 * pi2 * t);
70 w[n] = 1.0 - 1.93293488969 * std::cos(pi2 * t) + 1.28349769674 * std::cos(2.0 * pi2 * t) - 0.38763473916 * std::cos(3.0 * pi2 * t) + 0.03279543650 * std::cos(4.0 * pi2 * t);
81 const int iNFreqs = iNfft / 2 + 1;
82 RowVectorXd freqs(iNFreqs);
83 for (
int k = 0; k < iNFreqs; ++k)
84 freqs[k] =
static_cast<double>(k) * dSFreq /
static_cast<double>(iNfft);
96 const int nIn =
static_cast<int>(vecData.cols());
100 const int iNFreqs = iNfft / 2 + 1;
101 const int iStep = std::max(1,
static_cast<int>(std::round(
static_cast<double>(iNfft) * (1.0 - dOverlap))));
103 const VectorXd w = buildWindow(iNfft, window);
104 const double dWinPow = w.squaredNorm();
106 Eigen::FFT<double> fft;
107 RowVectorXd psd = RowVectorXd::Zero(iNFreqs);
110 for (
int start = 0; start + iNfft <= nIn; start += iStep) {
112 VectorXd seg = vecData.segment(start, iNfft).transpose().array() * w.array();
116 for (
int k = 0; k < iNFreqs; ++k)
117 psd[k] += std::norm(spec[k]);
122 return RowVectorXd::Zero(iNFreqs);
125 const double dNorm = 1.0 / (
static_cast<double>(nSeg) * dWinPow * dSFreq);
129 for (
int k = 1; k < iNFreqs - 1; ++k)
142 const RowVectorXi& vecPicks)
144 std::vector<int> picks;
145 if (vecPicks.size() > 0) {
146 picks.reserve(
static_cast<std::size_t
>(vecPicks.size()));
147 for (
int i = 0; i < vecPicks.size(); ++i)
148 picks.push_back(vecPicks[i]);
150 picks.reserve(
static_cast<std::size_t
>(matData.rows()));
151 for (
int i = 0; i < static_cast<int>(matData.rows()); ++i)
155 const int iNFreqs = iNfft / 2 + 1;
157 result.
matPsd.resize(
static_cast<int>(picks.size()), iNFreqs);
160 for (
int ci = 0; ci < static_cast<int>(picks.size()); ++ci)
162 dSFreq, iNfft, dOverlap, window);
Welch's averaged-periodogram power-spectral-density estimator.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Result of a Welch PSD computation.
Eigen::MatrixXd matPsd
n_channels × (iNfft/2+1); one-sided PSD in unit²/Hz
Eigen::RowVectorXd vecFreqs
Frequency axis in Hz, length iNfft/2+1.
static Eigen::RowVectorXd freqAxis(int iNfft, double dSFreq)
static WelchPsdResult compute(const Eigen::MatrixXd &matData, double dSFreq, int iNfft=256, double dOverlap=0.5, WindowType window=Hann, const Eigen::RowVectorXi &vecPicks=Eigen::RowVectorXi())
static Eigen::RowVectorXd computeVector(const Eigen::RowVectorXd &vecData, double dSFreq, int iNfft=256, double dOverlap=0.5, WindowType window=Hann)