27#include <unsupported/Eigen/FFT>
46constexpr double MORLET_PI = 3.14159265358979323846;
53int MorletTfr::nextPow2(
int n)
56 while (p < n) p <<= 1;
62VectorXcd MorletTfr::buildWavelet(
double dFreq,
double dSFreq,
double dNCycles,
int& halfLen)
65 const double sigma_t = dNCycles / (2.0 * MORLET_PI * dFreq);
68 halfLen =
static_cast<int>(std::round(4.0 * sigma_t * dSFreq));
70 const int nWav = 2 * halfLen + 1;
71 VectorXcd wavelet(nWav);
74 const double A = std::pow(sigma_t * std::sqrt(2.0 * MORLET_PI), -0.5);
76 for (
int i = 0; i < nWav; ++i) {
77 const double t =
static_cast<double>(i - halfLen) / dSFreq;
78 const double gauss = std::exp(-t * t / (2.0 * sigma_t * sigma_t));
79 const double phase = 2.0 * MORLET_PI * dFreq * t;
80 wavelet[i] = std::complex<double>(A * gauss * std::cos(phase),
81 A * gauss * std::sin(phase));
90 const RowVectorXd& vecFreqs,
93 const int nTimes =
static_cast<int>(vecData.cols());
94 const int nFreqs =
static_cast<int>(vecFreqs.cols());
97 result.
matPower.resize(nFreqs, nTimes);
102 Eigen::FFT<double> fft;
104 for (
int fi = 0; fi < nFreqs; ++fi) {
106 const VectorXcd wavelet = buildWavelet(vecFreqs[fi], dSFreq, dNCycles, halfLen);
108 const int nWav =
static_cast<int>(wavelet.size());
109 const int nConv = nextPow2(nTimes + nWav - 1);
112 VectorXd sigPad = VectorXd::Zero(nConv);
113 sigPad.head(nTimes) = vecData.transpose();
115 fft.fwd(sigSpec, sigPad);
118 VectorXd wavReal = VectorXd::Zero(nConv);
119 VectorXd wavImag = VectorXd::Zero(nConv);
120 for (
int k = 0; k < nWav; ++k) {
121 wavReal[k] = wavelet[k].real();
122 wavImag[k] = wavelet[k].imag();
124 VectorXcd wavSpecR, wavSpecI;
125 fft.fwd(wavSpecR, wavReal);
126 fft.fwd(wavSpecI, wavImag);
128 VectorXcd wavSpec = wavSpecR + std::complex<double>(0.0, 1.0) * wavSpecI;
131 VectorXcd product = sigSpec.array() * wavSpec.array();
133 fft.inv(conv, product);
136 for (
int t = 0; t < nTimes; ++t)
137 result.
matPower(fi, t) = std::norm(conv[t + halfLen]);
147 const RowVectorXd& vecFreqs,
149 const RowVectorXi& vecPicks)
151 std::vector<int> picks;
152 if (vecPicks.size() > 0) {
153 picks.reserve(
static_cast<std::size_t
>(vecPicks.size()));
154 for (
int i = 0; i < vecPicks.size(); ++i)
155 picks.push_back(vecPicks[i]);
157 picks.reserve(
static_cast<std::size_t
>(matData.rows()));
158 for (
int i = 0; i < static_cast<int>(matData.rows()); ++i)
162 QVector<MorletTfrResult> results;
163 results.reserve(
static_cast<int>(picks.size()));
165 results.append(
compute(matData.row(ch), dSFreq, vecFreqs, dNCycles));
Complex Morlet wavelet time-frequency representation (TFR).
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Result of a Morlet TFR computation for one channel.
Eigen::RowVectorXd vecFreqs
Centre frequencies in Hz, length n_freqs.
Eigen::MatrixXd matPower
n_freqs × n_times, instantaneous power (amplitude²)
static MorletTfrResult compute(const Eigen::RowVectorXd &vecData, double dSFreq, const Eigen::RowVectorXd &vecFreqs, double dNCycles=7.0)
static QVector< MorletTfrResult > computeMultiChannel(const Eigen::MatrixXd &matData, double dSFreq, const Eigen::RowVectorXd &vecFreqs, double dNCycles=7.0, const Eigen::RowVectorXi &vecPicks=Eigen::RowVectorXi())