v2.0.0
Loading...
Searching...
No Matches
welch_psd.cpp
Go to the documentation of this file.
1//=============================================================================================================
33
34//=============================================================================================================
35// INCLUDES
36//=============================================================================================================
37
38#include "welch_psd.h"
39
40//=============================================================================================================
41// EIGEN INCLUDES
42//=============================================================================================================
43
44#include <Eigen/Core>
45//#ifndef EIGEN_FFTW_DEFAULT
46//#define EIGEN_FFTW_DEFAULT
47//#endif
48#include <unsupported/Eigen/FFT>
49
50//=============================================================================================================
51// STD INCLUDES
52//=============================================================================================================
53
54#include <cmath>
55#include <vector>
56
57//=============================================================================================================
58// USED NAMESPACES
59//=============================================================================================================
60
61using namespace UTILSLIB;
62using namespace Eigen;
63
64namespace
65{
66constexpr double WELCH_PI = 3.14159265358979323846;
67}
68
69//=============================================================================================================
70// DEFINE MEMBER METHODS
71//=============================================================================================================
72
73VectorXd WelchPsd::buildWindow(int iN, WindowType window)
74{
75 VectorXd w(iN);
76 const double pi2 = 2.0 * WELCH_PI;
77
78 for (int n = 0; n < iN; ++n) {
79 const double t = static_cast<double>(n) / static_cast<double>(iN - 1);
80 switch (window) {
81 case Hann:
82 w[n] = 0.5 * (1.0 - std::cos(pi2 * t));
83 break;
84 case Hamming:
85 w[n] = 0.54 - 0.46 * std::cos(pi2 * t);
86 break;
87 case Blackman:
88 w[n] = 0.42
89 - 0.5 * std::cos( pi2 * t)
90 + 0.08 * std::cos(2.0 * pi2 * t);
91 break;
92 case FlatTop:
93 w[n] = 1.0
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);
98 break;
99 }
100 }
101 return w;
102}
103
104//=============================================================================================================
105
106RowVectorXd WelchPsd::freqAxis(int iNfft, double dSFreq)
107{
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);
112 return freqs;
113}
114
115//=============================================================================================================
116
117RowVectorXd WelchPsd::computeVector(const RowVectorXd& vecData,
118 double dSFreq,
119 int iNfft,
120 double dOverlap,
121 WindowType window)
122{
123 const int nIn = static_cast<int>(vecData.cols());
124 if (iNfft > nIn) iNfft = nIn;
125
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))));
129
130 const VectorXd w = buildWindow(iNfft, window);
131 const double dWinPow = w.squaredNorm(); // Σ w²
132
133 Eigen::FFT<double> fft;
134 RowVectorXd psd = RowVectorXd::Zero(iNFreqs);
135 int nSeg = 0;
136
137 for (int start = 0; start + iNfft <= nIn; start += iStep) {
138 // Apply window and forward FFT
139 VectorXd seg = vecData.segment(start, iNfft).transpose().array() * w.array();
140 VectorXcd spec;
141 fft.fwd(spec, seg);
142
143 for (int k = 0; k < iNFreqs; ++k)
144 psd[k] += std::norm(spec[k]); // accumulate |X[k]|²
145 ++nSeg;
146 }
147
148 if (nSeg == 0)
149 return RowVectorXd::Zero(iNFreqs);
150
151 // Normalise: divide by (nSeg × window_power × sfreq)
152 const double dNorm = 1.0 / (static_cast<double>(nSeg) * dWinPow * dSFreq);
153 psd *= dNorm;
154
155 // One-sided: double all non-DC, non-Nyquist bins
156 for (int k = 1; k < iNFreqs - 1; ++k)
157 psd[k] *= 2.0;
158
159 return psd;
160}
161
162//=============================================================================================================
163
164WelchPsdResult WelchPsd::compute(const MatrixXd& matData,
165 double dSFreq,
166 int iNfft,
167 double dOverlap,
168 WindowType window,
169 const RowVectorXi& vecPicks)
170{
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]);
176 } else {
177 picks.reserve(static_cast<std::size_t>(matData.rows()));
178 for (int i = 0; i < static_cast<int>(matData.rows()); ++i)
179 picks.push_back(i);
180 }
181
182 const int iNFreqs = iNfft / 2 + 1;
183 WelchPsdResult result;
184 result.matPsd.resize(static_cast<int>(picks.size()), iNFreqs);
185 result.vecFreqs = freqAxis(iNfft, dSFreq);
186
187 for (int ci = 0; ci < static_cast<int>(picks.size()); ++ci)
188 result.matPsd.row(ci) = computeVector(matData.row(picks[ci]),
189 dSFreq, iNfft, dOverlap, window);
190 return result;
191}
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.
Definition welch_psd.h:64
Eigen::MatrixXd matPsd
n_channels × (iNfft/2+1); one-sided PSD in unit²/Hz
Definition welch_psd.h:65
Eigen::RowVectorXd vecFreqs
Frequency axis in Hz, length iNfft/2+1.
Definition welch_psd.h:66
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)