v2.0.0
Loading...
Searching...
No Matches
rtsensordataworker.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "rtsensordataworker.h"
18#include "core/rendertypes.h"
19
21#include <QMutexLocker>
22#include <QDebug>
23#include <cmath>
24
25using namespace DISP3DLIB;
26
27//=============================================================================================================
28// DEFINE MEMBER METHODS
29//=============================================================================================================
30
32: QObject(parent)
33{
34}
35
36//=============================================================================================================
37
38void RtSensorDataWorker::addData(const Eigen::VectorXf& data)
39{
40 QMutexLocker locker(&m_mutex);
41
42 // Cap queue size at sampling frequency (1 second of data)
43 if (m_lDataQ.size() < static_cast<int>(m_dSFreq)) {
44 m_lDataQ.append(data);
45 }
46
47 // Also store for looping
48 if (m_bIsLooping) {
49 m_lDataLoopQ.append(data);
50 // Cap loop queue at 10 seconds of data
51 if (m_lDataLoopQ.size() > static_cast<int>(m_dSFreq) * 10) {
52 m_lDataLoopQ.removeFirst();
53 }
54 }
55}
56
57//=============================================================================================================
58
60{
61 QMutexLocker locker(&m_mutex);
62 m_lDataQ.clear();
63 m_lDataLoopQ.clear();
64 m_vecAverage = Eigen::VectorXf();
65 m_iSampleCtr = 0;
66 m_iCurrentSample = 0;
67}
68
69//=============================================================================================================
70
71void RtSensorDataWorker::setMappingMatrix(const QString& surfaceKey, std::shared_ptr<Eigen::MatrixXf> mat)
72{
73 QMutexLocker locker(&m_mutex);
74 m_sSurfaceKey = surfaceKey;
75 m_mappingMat = mat;
76}
77
78//=============================================================================================================
79
81{
82 QMutexLocker locker(&m_mutex);
83 m_iNumAverages = qMax(1, numAvr);
84}
85
86//=============================================================================================================
87
88void RtSensorDataWorker::setColormapType(const QString& name)
89{
90 QMutexLocker locker(&m_mutex);
91 m_sColormapType = name;
92}
93
94//=============================================================================================================
95
96void RtSensorDataWorker::setThresholds(double min, double max)
97{
98 QMutexLocker locker(&m_mutex);
99 if (min == 0.0 && max == 0.0) {
100 m_bUseAutoNorm = true;
101 m_dThreshMin = 0.0;
102 m_dThreshMax = 0.0;
103 } else {
104 m_bUseAutoNorm = false;
105 m_dThreshMin = min;
106 m_dThreshMax = max;
107 }
108}
109
110//=============================================================================================================
111
113{
114 QMutexLocker locker(&m_mutex);
115 m_bIsLooping = enabled;
116}
117
118//=============================================================================================================
119
121{
122 QMutexLocker locker(&m_mutex);
123 m_dSFreq = qMax(1.0, sFreq);
124}
125
126//=============================================================================================================
127
128void RtSensorDataWorker::setStreamSmoothedData(bool bStreamSmoothedData)
129{
130 QMutexLocker locker(&m_mutex);
131 m_bStreamSmoothedData = bStreamSmoothedData;
132}
133
134//=============================================================================================================
135
137{
138 QMutexLocker locker(&m_mutex);
139
140 // Try to get data from queue
141 Eigen::VectorXf vecCurrentData;
142
143 if (!m_lDataQ.isEmpty()) {
144 vecCurrentData = m_lDataQ.takeFirst();
145 } else if (m_bIsLooping && !m_lDataLoopQ.isEmpty()) {
146 // Loop: replay from stored data
147 vecCurrentData = m_lDataLoopQ[m_iCurrentSample % m_lDataLoopQ.size()];
148 m_iCurrentSample++;
149 } else {
150 // No data available
151 return;
152 }
153
154 // Averaging
155 if (m_iNumAverages > 1) {
156 if (m_vecAverage.size() != vecCurrentData.size()) {
157 m_vecAverage = vecCurrentData;
158 m_iSampleCtr = 1;
159 } else {
160 m_vecAverage += vecCurrentData;
161 m_iSampleCtr++;
162 }
163
164 if (m_iSampleCtr < m_iNumAverages) {
165 return; // Accumulate more samples
166 }
167
168 vecCurrentData = m_vecAverage / static_cast<float>(m_iSampleCtr);
169 m_vecAverage = Eigen::VectorXf();
170 m_iSampleCtr = 0;
171 }
172
173 // Check streaming mode
174 if (!m_bStreamSmoothedData) {
175 // Raw mode: emit measurement vector without mapping
176 Eigen::VectorXf rawData = vecCurrentData;
177 locker.unlock();
178 emit newRtRawSensorData(rawData);
179 return;
180 }
181
182 // Compute per-vertex colors using the dense mapping matrix
183 QVector<uint32_t> colors = computeSurfaceColors(vecCurrentData);
184 const QString surfaceKey = m_sSurfaceKey;
185
186 // Unlock before emitting (avoid deadlock if slot is direct connection)
187 locker.unlock();
188
189 if (!colors.isEmpty()) {
190 emit newRtSensorColors(surfaceKey, colors);
191 }
192}
193
194//=============================================================================================================
195
196QVector<uint32_t> RtSensorDataWorker::computeSurfaceColors(const Eigen::VectorXf& sensorData) const
197{
198 if (sensorData.size() == 0 || !m_mappingMat || m_mappingMat->rows() == 0) {
199 return QVector<uint32_t>();
200 }
201
202 // Validate dimensions
203 if (m_mappingMat->cols() != sensorData.size()) {
204 qWarning() << "RtSensorDataWorker: Mapping matrix cols" << m_mappingMat->cols()
205 << "!= sensor data size" << sensorData.size();
206 return QVector<uint32_t>();
207 }
208
209 // Map sensor data to surface vertices: mapped = M * meas
210 Eigen::VectorXf mapped = (*m_mappingMat) * sensorData;
211
212 int nVertices = mapped.size();
213
214 // Determine normalization range
215 float normMin, normMax;
216 if (m_bUseAutoNorm) {
217 // Symmetric auto-normalization: ±maxAbs
218 float maxAbs = 0.0f;
219 for (int i = 0; i < nVertices; ++i) {
220 maxAbs = std::max(maxAbs, std::abs(mapped(i)));
221 }
222 if (maxAbs <= 0.0f)
223 maxAbs = 1.0f;
224 normMin = -maxAbs;
225 normMax = maxAbs;
226 } else {
227 // Explicit thresholds
228 normMin = static_cast<float>(m_dThreshMin);
229 normMax = static_cast<float>(m_dThreshMax);
230 if (normMax <= normMin)
231 normMax = normMin + 1.0f;
232 }
233
234 float range = normMax - normMin;
235
236 // Convert to per-vertex ABGR colours
237 QVector<uint32_t> colors(nVertices);
238
239 for (int i = 0; i < nVertices; ++i) {
240 // Normalisation: map [normMin, normMax] → [0, 1]
241 double norm = static_cast<double>(mapped(i) - normMin) / static_cast<double>(range);
242 norm = qBound(0.0, norm, 1.0);
243
244 QRgb rgb = DISPLIB::ColorMap::valueToColor(norm, m_sColormapType);
245
246 uint32_t r = qRed(rgb);
247 uint32_t g = qGreen(rgb);
248 uint32_t b = qBlue(rgb);
249
250 // Pack as ABGR (same format as BrainSurface uses)
251 colors[i] = packABGR(r, g, b);
252 }
253
254 return colors;
255}
Static scalar-to-colour lookup helpers (Jet, Hot, Bone, Viridis, Cool, RedBlue, MNE) used by every pl...
Background worker that turns queued sensor packets into per-vertex ABGR colour buffers via a dense ma...
Lightweight render-related enums (ShaderMode, VisualizationMode) shared across disp3D.
3-D brain visualisation using the Qt RHI rendering backend.
uint32_t packABGR(uint32_t r, uint32_t g, uint32_t b, uint32_t a=0xFF)
Definition rendertypes.h:51
static QRgb valueToColor(double v, const QString &sMap)
Definition colormap.h:683
void setMappingMatrix(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mat)
void newRtSensorColors(const QString &surfaceKey, const QVector< uint32_t > &colors)
void setThresholds(double min, double max)
void addData(const Eigen::VectorXf &data)
RtSensorDataWorker(QObject *parent=nullptr)
void setColormapType(const QString &name)
void setStreamSmoothedData(bool bStreamSmoothedData)
void newRtRawSensorData(const Eigen::VectorXf &data)