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
25namespace DISP3DLIB
26{
27
28//=============================================================================================================
29// DEFINE MEMBER METHODS
30//=============================================================================================================
31
33: QObject(parent)
34{
35 // Create data worker
36 m_pWorker = new DISP3DLIB::RtSensorDataWorker();
37
38 // Create and configure data thread
39 m_pWorkerThread = new QThread(this);
40 m_pWorker->moveToThread(m_pWorkerThread);
41
42 // Clean up worker when thread finishes
43 connect(m_pWorkerThread, &QThread::finished, m_pWorker, &QObject::deleteLater);
44
45 // Forward worker signals to controller signals
50
51 // Create timer for driving the streaming cadence
52 m_pTimer = new QTimer(this);
53 m_pTimer->setTimerType(Qt::PreciseTimer);
54 connect(m_pTimer, &QTimer::timeout, m_pWorker, &DISP3DLIB::RtSensorDataWorker::streamData);
55
56 // Start the data worker thread
57 m_pWorkerThread->start();
58
59 // Create interpolation matrix worker
60 m_pInterpWorker = new DISP3DLIB::RtSensorInterpolationMatWorker();
61 m_pInterpThread = new QThread(this);
62 m_pInterpWorker->moveToThread(m_pInterpThread);
63
64 connect(m_pInterpThread, &QThread::finished, m_pInterpWorker, &QObject::deleteLater);
65
66 // Forward interpolation results: auto-apply to data worker + re-emit for external listeners
68 this, &RtSensorDataController::onNewMegMapping);
70 this, &RtSensorDataController::onNewEegMapping);
71
72 m_pInterpThread->start();
73
74 qDebug() << "RtSensorDataController: Initialized with" << m_iTimeInterval << "ms interval";
75}
76
77//=============================================================================================================
78
80{
81 // Stop timer
82 if (m_pTimer) {
83 m_pTimer->stop();
84 }
85
86 // Stop the data worker thread
87 if (m_pWorkerThread) {
88 m_pWorkerThread->quit();
89 m_pWorkerThread->wait();
90 }
91
92 // Stop the interpolation worker thread
93 if (m_pInterpThread) {
94 m_pInterpThread->quit();
95 m_pInterpThread->wait();
96 }
97}
98
99//=============================================================================================================
100
101void RtSensorDataController::addData(const Eigen::VectorXf& data)
102{
103 if (m_pWorker) {
104 // Direct call is thread-safe because RtSensorDataWorker uses a mutex
105 m_pWorker->addData(data);
106 }
107}
108
109//=============================================================================================================
110
111void RtSensorDataController::setMappingMatrix(const QString& surfaceKey, std::shared_ptr<Eigen::MatrixXf> mat)
112{
113 if (m_pWorker) {
114 m_pWorker->setMappingMatrix(surfaceKey, mat);
115 }
116}
117
118//=============================================================================================================
119
121{
122 m_bIsStreaming = state;
123
124 if (state) {
125 m_pTimer->start(m_iTimeInterval);
126 qDebug() << "RtSensorDataController: Streaming started at" << m_iTimeInterval << "ms interval";
127 } else {
128 m_pTimer->stop();
129 qDebug() << "RtSensorDataController: Streaming stopped";
130 }
131}
132
133//=============================================================================================================
134
136{
137 return m_bIsStreaming;
138}
139
140//=============================================================================================================
141
143{
144 m_iTimeInterval = qMax(1, msec);
145
146 if (m_bIsStreaming) {
147 m_pTimer->setInterval(m_iTimeInterval);
148 }
149
150 qDebug() << "RtSensorDataController: Time interval set to" << m_iTimeInterval << "ms";
151}
152
153//=============================================================================================================
154
156{
157 if (m_pWorker) {
158 m_pWorker->setNumberAverages(numAvr);
159 }
160}
161
162//=============================================================================================================
163
165{
166 if (m_pWorker) {
167 m_pWorker->setColormapType(name);
168 }
169}
170
171//=============================================================================================================
172
173void RtSensorDataController::setThresholds(double min, double max)
174{
175 if (m_pWorker) {
176 m_pWorker->setThresholds(min, max);
177 }
178}
179
180//=============================================================================================================
181
183{
184 if (m_pWorker) {
185 m_pWorker->setLoopState(enabled);
186 }
187}
188
189//=============================================================================================================
190
192{
193 if (m_pWorker) {
194 m_pWorker->setSFreq(sFreq);
195 }
196}
197
198//=============================================================================================================
199
201{
202 if (m_pWorker) {
203 m_pWorker->clear();
204 }
205}
206
207//=============================================================================================================
208
210{
211 if (m_pWorker) {
212 m_pWorker->setStreamSmoothedData(bStreamSmoothedData);
213 }
214}
215
216//=============================================================================================================
217// ── On-the-fly interpolation matrix computation ────────────────────────
218//=============================================================================================================
219
221{
222 if (m_pInterpWorker) {
223 m_pInterpWorker->setEvoked(evoked);
224 }
225}
226
227//=============================================================================================================
228
230 bool applySensorTrans)
231{
232 if (m_pInterpWorker) {
233 m_pInterpWorker->setTransform(trans, applySensorTrans);
234 }
235}
236
237//=============================================================================================================
238
240{
241 if (m_pInterpWorker) {
242 m_pInterpWorker->setMegFieldMapOnHead(onHead);
243 }
244}
245
246//=============================================================================================================
247
248void RtSensorDataController::setMegSurface(const QString& surfaceKey,
249 const Eigen::MatrixX3f& vertices,
250 const Eigen::MatrixX3f& normals,
251 const Eigen::MatrixX3i& triangles)
252{
253 if (m_pInterpWorker) {
254 m_pInterpWorker->setMegSurface(surfaceKey, vertices, normals, triangles);
255 }
256}
257
258//=============================================================================================================
259
260void RtSensorDataController::setEegSurface(const QString& surfaceKey,
261 const Eigen::MatrixX3f& vertices)
262{
263 if (m_pInterpWorker) {
264 m_pInterpWorker->setEegSurface(surfaceKey, vertices);
265 }
266}
267
268//=============================================================================================================
269
270void RtSensorDataController::setBadChannels(const QStringList& bads)
271{
272 if (m_pInterpWorker) {
273 m_pInterpWorker->setBadChannels(bads);
274 }
275}
276
277//=============================================================================================================
278
280{
281 if (m_pInterpWorker) {
282 // Use QMetaObject::invokeMethod with queued connection to ensure
283 // computeMapping() runs on the interpolation worker thread.
284 QMetaObject::invokeMethod(m_pInterpWorker, "computeMapping", Qt::QueuedConnection);
285 }
286}
287
288//=============================================================================================================
289
290void RtSensorDataController::onNewMegMapping(const QString& surfaceKey,
291 std::shared_ptr<Eigen::MatrixXf> mappingMat,
292 const QVector<int>& pick)
293{
294 // Auto-forward the new matrix to the data worker
295 if (m_pWorker && mappingMat) {
296 m_pWorker->setMappingMatrix(surfaceKey, mappingMat);
297 }
298
299 // Re-emit for external listeners (e.g. BrainView)
300 emit newMegMappingAvailable(surfaceKey, mappingMat, pick);
301
302 qDebug() << "RtSensorDataController: New MEG mapping received and forwarded"
303 << "(" << mappingMat->rows() << "x" << mappingMat->cols() << ")";
304}
305
306//=============================================================================================================
307
308void RtSensorDataController::onNewEegMapping(const QString& surfaceKey,
309 std::shared_ptr<Eigen::MatrixXf> mappingMat,
310 const QVector<int>& pick)
311{
312 // Auto-forward the new matrix to the data worker
313 if (m_pWorker && mappingMat) {
314 m_pWorker->setMappingMatrix(surfaceKey, mappingMat);
315 }
316
317 // Re-emit for external listeners (e.g. BrainView)
318 emit newEegMappingAvailable(surfaceKey, mappingMat, pick);
319
320 qDebug() << "RtSensorDataController: New EEG mapping received and forwarded"
321 << "(" << mappingMat->rows() << "x" << mappingMat->cols() << ")";
322}
323
324} // namespace DISP3DLIB
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...
3-D brain visualisation using the Qt RHI rendering backend.
void newSensorColorsAvailable(const QString &surfaceKey, const QVector< uint32_t > &colors)
void setTransform(const FIFFLIB::FiffCoordTrans &trans, bool applySensorTrans)
void setEegSurface(const QString &surfaceKey, const Eigen::MatrixX3f &vertices)
void setEvoked(const FIFFLIB::FiffEvoked &evoked)
void newEegMappingAvailable(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mappingMat, const QVector< int > &pick)
void newRawSensorDataAvailable(const Eigen::VectorXf &data)
void setMappingMatrix(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mat)
void setStreamSmoothedData(bool bStreamSmoothedData)
RtSensorDataController(QObject *parent=nullptr)
void newMegMappingAvailable(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mappingMat, const QVector< int > &pick)
void addData(const Eigen::VectorXf &data)
void setMegSurface(const QString &surfaceKey, const Eigen::MatrixX3f &vertices, const Eigen::MatrixX3f &normals, const Eigen::MatrixX3i &triangles)
void setBadChannels(const QStringList &bads)
void setThresholds(double min, double max)
Background worker for real-time sensor data streaming.
void setMappingMatrix(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mat)
void newRtSensorColors(const QString &surfaceKey, const QVector< uint32_t > &colors)
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:77