v2.0.0
Loading...
Searching...
No Matches
rt_noise.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "rt_noise.h"
18
19//=============================================================================================================
20// QT INCLUDES
21//=============================================================================================================
22
23#include <QDebug>
24
25//=============================================================================================================
26// EIGEN INCLUDES
27//=============================================================================================================
28
29#include <unsupported/Eigen/FFT>
30
31//=============================================================================================================
32// STD INCLUDES
33//=============================================================================================================
34
35#include <cmath>
36
37//=============================================================================================================
38// USED NAMESPACES
39//=============================================================================================================
40
41using namespace RTPROCESSINGLIB;
42using namespace FIFFLIB;
43using namespace Eigen;
44
45//=============================================================================================================
46// DEFINE MEMBER METHODS — RtNoiseWorker
47//=============================================================================================================
48
50 FiffInfo::SPtr pFiffInfo,
51 qint32 iDataLength)
52: m_iFftLength(iFftLength)
53, m_dFs(pFiffInfo->sfreq)
54, m_iDataLength(iDataLength < 1 ? 10 : iDataLength)
55{
56 m_fWin = hanning(m_iFftLength, 0);
57}
58
59//=============================================================================================================
60
61void RtNoiseWorker::doWork(const MatrixXd& matData)
62{
63 if (m_bFirstBlock) {
64 m_iNumOfBlocks = m_iDataLength;
65 m_iBlockSize = static_cast<int>(matData.cols());
66 m_iSensors = static_cast<int>(matData.rows());
67 m_matCircBuf.resize(m_iSensors, static_cast<Eigen::Index>(m_iNumOfBlocks) * m_iBlockSize);
68 m_iBlockIndex = 0;
69 m_bFirstBlock = false;
70 }
71
72 // Accumulate block into circular buffer
73 m_matCircBuf.block(0, m_iBlockIndex * m_iBlockSize, m_iSensors, m_iBlockSize) = matData;
74
75 m_iBlockIndex++;
76 if (m_iBlockIndex < m_iNumOfBlocks) {
77 return;
78 }
79
80 // Enough blocks accumulated — compute spectrum
81 m_iBlockIndex = 0;
82
83 const int iTotalSamples = m_iNumOfBlocks * m_iBlockSize;
84 const int iHalfSpec = m_iFftLength / 2 + 1;
85 const int nb = std::max(1, iTotalSamples / m_iFftLength); // complete segments only
86
87 double winPow = 0.0;
88 for (int k = 0; k < m_iFftLength; ++k) {
89 winPow += static_cast<double>(m_fWin[k]) * m_fWin[k];
90 }
91
92 MatrixXd sum_psdx = MatrixXd::Zero(m_iSensors, iHalfSpec);
93 RowVectorXd vecDataZeroPad = RowVectorXd::Zero(m_iFftLength);
94 RowVectorXcd vecFreqData(iHalfSpec);
95 Eigen::FFT<double> fft;
96 fft.SetFlag(fft.HalfSpectrum);
97
98 for (int n = 0; n < nb; ++n) {
99 const int iOffset = n * m_iFftLength;
100
101 for (int i = 0; i < m_iSensors; ++i) {
102 vecDataZeroPad.setZero();
103 const int iCopyLen = std::min(m_iFftLength, iTotalSamples - iOffset);
104 vecDataZeroPad.head(iCopyLen) = m_matCircBuf.block(i, iOffset, 1, iCopyLen);
105 for (int k = 0; k < m_iFftLength; ++k) {
106 vecDataZeroPad[k] *= m_fWin[k];
107 }
108 fft.fwd(vecFreqData, vecDataZeroPad);
109
110 // One-sided density |X|^2 / (fs * sum w^2), as scipy.signal.welch(scaling="density")
111 for (int j = 0; j < iHalfSpec; ++j) {
112 double spower = std::norm(vecFreqData(j)) / (m_dFs * winPow);
113 if (j > 0 && !(m_iFftLength % 2 == 0 && j == m_iFftLength / 2)) {
114 spower *= 2.0;
115 }
116 sum_psdx(i, j) += spower;
117 }
118 }
119 }
120
121 // Convert to dB
122 MatrixXd matResult(m_iSensors, iHalfSpec);
123 for (int i = 0; i < m_iSensors; ++i) {
124 for (int j = 0; j < iHalfSpec; ++j) {
125 matResult(i, j) = 10.0 * std::log10(sum_psdx(i, j) / nb);
126 }
127 }
128
129 emit resultReady(matResult);
130}
131
132//=============================================================================================================
133
134QVector<float> RtNoiseWorker::hanning(int N, short itype)
135{
136 QVector<float> w(N, 0.0f);
137
138 const int n = (itype == 1) ? N - 1 : N;
139
140 if (n % 2 == 0) {
141 const int half = n / 2;
142 for (int i = 0; i < half; ++i) {
143 w[i] = 0.5f * (1.0f - std::cos(2.0f * static_cast<float>(M_PI) * (i + 1) / (n + 1)));
144 }
145 int idx = half - 1;
146 for (int i = half; i < n; ++i) {
147 w[i] = w[idx--];
148 }
149 } else {
150 const int half = (n + 1) / 2;
151 for (int i = 0; i < half; ++i) {
152 w[i] = 0.5f * (1.0f - std::cos(2.0f * static_cast<float>(M_PI) * (i + 1) / (n + 1)));
153 }
154 int idx = half - 2;
155 for (int i = half; i < n; ++i) {
156 w[i] = w[idx--];
157 }
158 }
159
160 if (itype == 1) {
161 for (int i = N - 1; i >= 1; --i) {
162 w[i] = w[i - 1];
163 }
164 w[0] = 0.0f;
165 }
166
167 return w;
168}
169
170//=============================================================================================================
171// DEFINE MEMBER METHODS — RtNoise
172//=============================================================================================================
173
174RtNoise::RtNoise(qint32 iFftLength,
175 FiffInfo::SPtr pFiffInfo,
176 qint32 iDataLength,
177 QObject* parent)
178: QObject(parent)
179{
180 qRegisterMetaType<Eigen::MatrixXd>("Eigen::MatrixXd");
181
182 auto* worker = new RtNoiseWorker(iFftLength, pFiffInfo, iDataLength);
183 worker->moveToThread(&m_workerThread);
184
185 connect(&m_workerThread, &QThread::finished,
186 worker, &QObject::deleteLater);
187 connect(this, &RtNoise::operate,
188 worker, &RtNoiseWorker::doWork);
189 connect(worker, &RtNoiseWorker::resultReady,
191}
192
193//=============================================================================================================
194
196{
197 if (m_bIsRunning) {
198 stop();
199 }
200}
201
202//=============================================================================================================
203
204void RtNoise::append(const MatrixXd& matData)
205{
206 emit operate(matData);
207}
208
209//=============================================================================================================
210
212{
213 return m_bIsRunning;
214}
215
216//=============================================================================================================
217
219{
220 if (m_workerThread.isRunning()) {
221 m_workerThread.wait();
222 }
223
224 m_bIsRunning = true;
225 m_workerThread.start();
226 return true;
227}
228
229//=============================================================================================================
230
232{
233 m_bIsRunning = false;
234 m_workerThread.quit();
235 m_workerThread.wait();
236 return true;
237}
238
239//=============================================================================================================
240
241bool RtNoise::wait(unsigned long time)
242{
243 return m_workerThread.wait(time);
244}
#define M_PI
Real-time noise power-spectral-density estimation from streaming data blocks.
FIFF file I/O, in-memory data structures and high-level readers/writers.
Background worker that computes a noise power spectral density estimate from accumulated data blocks.
Definition rt_noise.h:66
void resultReady(const Eigen::MatrixXd &matSpecData)
RtNoiseWorker(qint32 iFftLength, FIFFLIB::FiffInfo::SPtr pFiffInfo, qint32 iDataLength)
Definition rt_noise.cpp:49
void doWork(const Eigen::MatrixXd &matData)
Definition rt_noise.cpp:61
bool wait(unsigned long time=ULONG_MAX)
Definition rt_noise.cpp:241
void operate(const Eigen::MatrixXd &matData)
void SpecCalculated(const Eigen::MatrixXd &matSpecData)
RtNoise(qint32 iFftLength, FIFFLIB::FiffInfo::SPtr pFiffInfo, qint32 iDataLength, QObject *parent=nullptr)
Definition rt_noise.cpp:174
void append(const Eigen::MatrixXd &matData)
Definition rt_noise.cpp:204
QSharedPointer< FiffInfo > SPtr
Definition fiff_info.h:92