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