48#include <unsupported/Eigen/FFT>
66constexpr double WELCH_PI = 3.14159265358979323846;
73VectorXd WelchPsd::buildWindow(
int iN, WindowType window)
76 const double pi2 = 2.0 * WELCH_PI;
78 for (
int n = 0; n < iN; ++n) {
79 const double t =
static_cast<double>(n) /
static_cast<double>(iN - 1);
82 w[n] = 0.5 * (1.0 - std::cos(pi2 * t));
85 w[n] = 0.54 - 0.46 * std::cos(pi2 * t);
89 - 0.5 * std::cos( pi2 * t)
90 + 0.08 * std::cos(2.0 * pi2 * t);
94 - 1.93293488969 * std::cos( pi2 * t)
95 + 1.28349769674 * std::cos(2.0 * pi2 * t)
96 - 0.38763473916 * std::cos(3.0 * pi2 * t)
97 + 0.03279543650 * std::cos(4.0 * pi2 * t);
108 const int iNFreqs = iNfft / 2 + 1;
109 RowVectorXd freqs(iNFreqs);
110 for (
int k = 0; k < iNFreqs; ++k)
111 freqs[k] =
static_cast<double>(k) * dSFreq /
static_cast<double>(iNfft);
123 const int nIn =
static_cast<int>(vecData.cols());
124 if (iNfft > nIn) iNfft = nIn;
126 const int iNFreqs = iNfft / 2 + 1;
127 const int iStep = std::max(1,
static_cast<int>(std::round(
128 static_cast<double>(iNfft) * (1.0 - dOverlap))));
130 const VectorXd w = buildWindow(iNfft, window);
131 const double dWinPow = w.squaredNorm();
133 Eigen::FFT<double> fft;
134 RowVectorXd psd = RowVectorXd::Zero(iNFreqs);
137 for (
int start = 0; start + iNfft <= nIn; start += iStep) {
139 VectorXd seg = vecData.segment(start, iNfft).transpose().array() * w.array();
143 for (
int k = 0; k < iNFreqs; ++k)
144 psd[k] += std::norm(spec[k]);
149 return RowVectorXd::Zero(iNFreqs);
152 const double dNorm = 1.0 / (
static_cast<double>(nSeg) * dWinPow * dSFreq);
156 for (
int k = 1; k < iNFreqs - 1; ++k)
169 const RowVectorXi& vecPicks)
171 std::vector<int> picks;
172 if (vecPicks.size() > 0) {
173 picks.reserve(
static_cast<std::size_t
>(vecPicks.size()));
174 for (
int i = 0; i < vecPicks.size(); ++i)
175 picks.push_back(vecPicks[i]);
177 picks.reserve(
static_cast<std::size_t
>(matData.rows()));
178 for (
int i = 0; i < static_cast<int>(matData.rows()); ++i)
182 const int iNFreqs = iNfft / 2 + 1;
184 result.
matPsd.resize(
static_cast<int>(picks.size()), iNFreqs);
187 for (
int ci = 0; ci < static_cast<int>(picks.size()); ++ci)
189 dSFreq, iNfft, dOverlap, window);
WelchPsd class declaration — Welch's averaged periodogram PSD 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)