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