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);
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 - 0.5 * std::cos(pi2 * t) + 0.08 * std::cos(2.0 * pi2 * t);
68 break;
69 case FlatTop:
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);
71 break;
72 }
73 }
74 return w;
75}
76
77//=============================================================================================================
78
79RowVectorXd WelchPsd::freqAxis(int iNfft, double dSFreq)
80{
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);
85 return freqs;
86}
87
88//=============================================================================================================
89
90RowVectorXd WelchPsd::computeVector(const RowVectorXd& vecData,
91 double dSFreq,
92 int iNfft,
93 double dOverlap,
94 WindowType window,
95 bool bRemoveDc)
96{
97 const int nIn = static_cast<int>(vecData.cols());
98 if (iNfft > nIn)
99 iNfft = nIn;
100
101 const int iNFreqs = iNfft / 2 + 1;
102 const int iStep = std::max(1, static_cast<int>(std::round(static_cast<double>(iNfft) * (1.0 - dOverlap))));
103
104 const VectorXd w = buildWindow(iNfft, window);
105 const double dWinPow = w.squaredNorm(); // Σ w²
106
107 Eigen::FFT<double> fft;
108 RowVectorXd psd = RowVectorXd::Zero(iNFreqs);
109 int nSeg = 0;
110
111 for (int start = 0; start + iNfft <= nIn; start += iStep) {
112 // Apply window and forward FFT
113 VectorXd seg = vecData.segment(start, iNfft).transpose();
114 if (bRemoveDc)
115 seg.array() -= seg.mean();
116 seg.array() *= w.array();
117 VectorXcd spec;
118 fft.fwd(spec, seg);
119
120 for (int k = 0; k < iNFreqs; ++k)
121 psd[k] += std::norm(spec[k]); // accumulate |X[k]|²
122 ++nSeg;
123 }
124
125 if (nSeg == 0)
126 return RowVectorXd::Zero(iNFreqs);
127
128 // Normalise: divide by (nSeg × window_power × sfreq)
129 const double dNorm = 1.0 / (static_cast<double>(nSeg) * dWinPow * dSFreq);
130 psd *= dNorm;
131
132 // One-sided: double every bin with a mirror image (not DC, not an even-length Nyquist)
133 const int kLast = (iNfft % 2 == 0) ? iNFreqs - 1 : iNFreqs;
134 for (int k = 1; k < kLast; ++k)
135 psd[k] *= 2.0;
136
137 return psd;
138}
139
140//=============================================================================================================
141
142WelchPsdResult WelchPsd::compute(const MatrixXd& matData,
143 double dSFreq,
144 int iNfft,
145 double dOverlap,
146 WindowType window,
147 const RowVectorXi& vecPicks,
148 bool bRemoveDc)
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, bRemoveDc);
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:59
Eigen::MatrixXd matPsd
n_channels × (iNfft/2+1); one-sided PSD in unit²/Hz
Definition welch_psd.h:60
Eigen::RowVectorXd vecFreqs
Frequency axis in Hz, length iNfft/2+1.
Definition welch_psd.h:61
static Eigen::RowVectorXd freqAxis(int iNfft, double dSFreq)
Definition welch_psd.cpp:79
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(), bool bRemoveDc=true)
static Eigen::RowVectorXd computeVector(const Eigen::RowVectorXd &vecData, double dSFreq, int iNfft=256, double dOverlap=0.5, WindowType window=Hann, bool bRemoveDc=true)
Definition welch_psd.cpp:90