v2.0.0
Loading...
Searching...
No Matches
rtsensordatacontroller.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
18#include "rtsensordataworker.h"
20
21#include <QThread>
22#include <QTimer>
23#include <QDebug>
24
25//=============================================================================================================
26// DEFINE MEMBER METHODS
27//=============================================================================================================
28
30 : QObject(parent)
31{
32 // Create data worker
33 m_pWorker = new DISP3DLIB::RtSensorDataWorker();
34
35 // Create and configure data thread
36 m_pWorkerThread = new QThread(this);
37 m_pWorker->moveToThread(m_pWorkerThread);
38
39 // Clean up worker when thread finishes
40 connect(m_pWorkerThread, &QThread::finished, m_pWorker, &QObject::deleteLater);
41
42 // Forward worker signals to controller signals
47
48 // Create timer for driving the streaming cadence
49 m_pTimer = new QTimer(this);
50 m_pTimer->setTimerType(Qt::PreciseTimer);
51 connect(m_pTimer, &QTimer::timeout, m_pWorker, &DISP3DLIB::RtSensorDataWorker::streamData);
52
53 // Start the data worker thread
54 m_pWorkerThread->start();
55
56 // Create interpolation matrix worker
57 m_pInterpWorker = new DISP3DLIB::RtSensorInterpolationMatWorker();
58 m_pInterpThread = new QThread(this);
59 m_pInterpWorker->moveToThread(m_pInterpThread);
60
61 connect(m_pInterpThread, &QThread::finished, m_pInterpWorker, &QObject::deleteLater);
62
63 // Forward interpolation results: auto-apply to data worker + re-emit for external listeners
65 this, &RtSensorDataController::onNewMegMapping);
67 this, &RtSensorDataController::onNewEegMapping);
68
69 m_pInterpThread->start();
70
71 qDebug() << "RtSensorDataController: Initialized with" << m_iTimeInterval << "ms interval";
72}
73
74//=============================================================================================================
75
77{
78 // Stop timer
79 if (m_pTimer) {
80 m_pTimer->stop();
81 }
82
83 // Stop the data worker thread
84 if (m_pWorkerThread) {
85 m_pWorkerThread->quit();
86 m_pWorkerThread->wait();
87 }
88
89 // Stop the interpolation worker thread
90 if (m_pInterpThread) {
91 m_pInterpThread->quit();
92 m_pInterpThread->wait();
93 }
94}
95
96//=============================================================================================================
97
98void RtSensorDataController::addData(const Eigen::VectorXf &data)
99{
100 if (m_pWorker) {
101 // Direct call is thread-safe because RtSensorDataWorker uses a mutex
102 m_pWorker->addData(data);
103 }
104}
105
106//=============================================================================================================
107
108void RtSensorDataController::setMappingMatrix(std::shared_ptr<Eigen::MatrixXf> mat)
109{
110 if (m_pWorker) {
111 m_pWorker->setMappingMatrix(mat);
112 }
113}
114
115//=============================================================================================================
116
118{
119 m_bIsStreaming = state;
120
121 if (state) {
122 m_pTimer->start(m_iTimeInterval);
123 qDebug() << "RtSensorDataController: Streaming started at" << m_iTimeInterval << "ms interval";
124 } else {
125 m_pTimer->stop();
126 qDebug() << "RtSensorDataController: Streaming stopped";
127 }
128}
129
130//=============================================================================================================
131
133{
134 return m_bIsStreaming;
135}
136
137//=============================================================================================================
138
140{
141 m_iTimeInterval = qMax(1, msec);
142
143 if (m_bIsStreaming) {
144 m_pTimer->setInterval(m_iTimeInterval);
145 }
146
147 qDebug() << "RtSensorDataController: Time interval set to" << m_iTimeInterval << "ms";
148}
149
150//=============================================================================================================
151
153{
154 if (m_pWorker) {
155 m_pWorker->setNumberAverages(numAvr);
156 }
157}
158
159//=============================================================================================================
160
162{
163 if (m_pWorker) {
164 m_pWorker->setColormapType(name);
165 }
166}
167
168//=============================================================================================================
169
170void RtSensorDataController::setThresholds(double min, double max)
171{
172 if (m_pWorker) {
173 m_pWorker->setThresholds(min, max);
174 }
175}
176
177//=============================================================================================================
178
180{
181 if (m_pWorker) {
182 m_pWorker->setLoopState(enabled);
183 }
184}
185
186//=============================================================================================================
187
189{
190 if (m_pWorker) {
191 m_pWorker->setSFreq(sFreq);
192 }
193}
194
195//=============================================================================================================
196
198{
199 if (m_pWorker) {
200 m_pWorker->clear();
201 }
202}
203
204//=============================================================================================================
205
207{
208 if (m_pWorker) {
209 m_pWorker->setStreamSmoothedData(bStreamSmoothedData);
210 }
211}
212
213//=============================================================================================================
214// ── On-the-fly interpolation matrix computation ────────────────────────
215//=============================================================================================================
216
218{
219 if (m_pInterpWorker) {
220 m_pInterpWorker->setEvoked(evoked);
221 }
222}
223
224//=============================================================================================================
225
227 bool applySensorTrans)
228{
229 if (m_pInterpWorker) {
230 m_pInterpWorker->setTransform(trans, applySensorTrans);
231 }
232}
233
234//=============================================================================================================
235
237{
238 if (m_pInterpWorker) {
239 m_pInterpWorker->setMegFieldMapOnHead(onHead);
240 }
241}
242
243//=============================================================================================================
244
245void RtSensorDataController::setMegSurface(const QString &surfaceKey,
246 const Eigen::MatrixX3f &vertices,
247 const Eigen::MatrixX3f &normals,
248 const Eigen::MatrixX3i &triangles)
249{
250 if (m_pInterpWorker) {
251 m_pInterpWorker->setMegSurface(surfaceKey, vertices, normals, triangles);
252 }
253}
254
255//=============================================================================================================
256
257void RtSensorDataController::setEegSurface(const QString &surfaceKey,
258 const Eigen::MatrixX3f &vertices)
259{
260 if (m_pInterpWorker) {
261 m_pInterpWorker->setEegSurface(surfaceKey, vertices);
262 }
263}
264
265//=============================================================================================================
266
267void RtSensorDataController::setBadChannels(const QStringList &bads)
268{
269 if (m_pInterpWorker) {
270 m_pInterpWorker->setBadChannels(bads);
271 }
272}
273
274//=============================================================================================================
275
277{
278 if (m_pInterpWorker) {
279 // Use QMetaObject::invokeMethod with queued connection to ensure
280 // computeMapping() runs on the interpolation worker thread.
281 QMetaObject::invokeMethod(m_pInterpWorker, "computeMapping", Qt::QueuedConnection);
282 }
283}
284
285//=============================================================================================================
286
287void RtSensorDataController::onNewMegMapping(const QString &surfaceKey,
288 std::shared_ptr<Eigen::MatrixXf> mappingMat,
289 const QVector<int> &pick)
290{
291 // Auto-forward the new matrix to the data worker
292 if (m_pWorker && mappingMat) {
293 m_pWorker->setMappingMatrix(mappingMat);
294 }
295
296 // Re-emit for external listeners (e.g. BrainView)
297 emit newMegMappingAvailable(surfaceKey, mappingMat, pick);
298
299 qDebug() << "RtSensorDataController: New MEG mapping received and forwarded"
300 << "(" << mappingMat->rows() << "x" << mappingMat->cols() << ")";
301}
302
303//=============================================================================================================
304
305void RtSensorDataController::onNewEegMapping(const QString &surfaceKey,
306 std::shared_ptr<Eigen::MatrixXf> mappingMat,
307 const QVector<int> &pick)
308{
309 // Auto-forward the new matrix to the data worker
310 if (m_pWorker && mappingMat) {
311 m_pWorker->setMappingMatrix(mappingMat);
312 }
313
314 // Re-emit for external listeners (e.g. BrainView)
315 emit newEegMappingAvailable(surfaceKey, mappingMat, pick);
316
317 qDebug() << "RtSensorDataController: New EEG mapping received and forwarded"
318 << "(" << mappingMat->rows() << "x" << mappingMat->cols() << ")";
319}
Background worker that recomputes the dense MEG / EEG sensor-to-surface mapping matrix off the GUI th...
Real-time MEG / EEG sensor streaming controller that orchestrates the data and field-mapping workers.
Background worker that turns queued sensor packets into per-vertex ABGR colour buffers via a dense ma...
void newSensorColorsAvailable(const QString &surfaceKey, const QVector< uint32_t > &colors)
void setColormapType(const QString &name)
void setMegSurface(const QString &surfaceKey, const Eigen::MatrixX3f &vertices, const Eigen::MatrixX3f &normals, const Eigen::MatrixX3i &triangles)
void setBadChannels(const QStringList &bads)
void setTransform(const FIFFLIB::FiffCoordTrans &trans, bool applySensorTrans)
void setEegSurface(const QString &surfaceKey, const Eigen::MatrixX3f &vertices)
void newMegMappingAvailable(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mappingMat, const QVector< int > &pick)
void setMappingMatrix(std::shared_ptr< Eigen::MatrixXf > mat)
void newRawSensorDataAvailable(const Eigen::VectorXf &data)
void addData(const Eigen::VectorXf &data)
void setEvoked(const FIFFLIB::FiffEvoked &evoked)
void setStreamSmoothedData(bool bStreamSmoothedData)
void newEegMappingAvailable(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mappingMat, const QVector< int > &pick)
RtSensorDataController(QObject *parent=nullptr)
void setThresholds(double min, double max)
Background worker for real-time sensor data streaming.
void newRtSensorColors(const QString &surfaceKey, const QVector< uint32_t > &colors)
void setMappingMatrix(std::shared_ptr< Eigen::MatrixXf > mat)
void newRtRawSensorData(const Eigen::VectorXf &data)
Background worker for computing sensor field mapping matrices.
void newEegMappingAvailable(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mappingMat, const QVector< int > &pick)
void newMegMappingAvailable(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mappingMat, const QVector< int > &pick)
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
Definition fiff_evoked.h:75