27#include <QWriteLocker>
44 constexpr float kScaleMEGGrad = 400e-13f;
45 constexpr float kScaleMEGMag = 1.2e-12f;
46 constexpr float kScaleEEG = 30e-6f;
47 constexpr float kScaleEOG = 150e-6f;
48 constexpr float kScaleEMG = 1e-3f;
49 constexpr float kScaleECG = 1e-3f;
50 constexpr float kScaleSTIM = 5.0f;
51 constexpr float kScaleMISC = 1.0f;
52 constexpr float kScaleFallback = 1.0f;
55 constexpr qint32 kMEGGradKind = -1;
56 constexpr qint32 kMEGMagKind = -2;
80 QWriteLocker lk(&m_lock);
81 m_pFiffInfo = pFiffInfo;
83 int nCh = (pFiffInfo ? pFiffInfo->nchan : 0) + m_virtualDisplayInfo.size();
84 m_channelData.resize(nCh);
85 for (
auto &ch : m_channelData)
98 if (data.rows() == 0 || data.cols() == 0)
101 QWriteLocker lk(&m_lock);
102 int nCh =
static_cast<int>(data.rows());
103 m_channelData.resize(nCh);
104 for (
int ch = 0; ch < nCh; ++ch) {
105 m_channelData[ch].resize(
static_cast<int>(data.cols()));
106 for (
int s = 0; s < static_cast<int>(data.cols()); ++s)
107 m_channelData[ch][s] =
static_cast<float>(data(ch, s));
119 if (data.rows() == 0 || data.cols() == 0)
122 QWriteLocker lk(&m_lock);
123 int nCh =
static_cast<int>(data.rows());
124 int nNew =
static_cast<int>(data.cols());
126 if (m_channelData.size() != nCh)
127 m_channelData.resize(nCh);
129 for (
int ch = 0; ch < nCh; ++ch) {
130 auto &buf = m_channelData[ch];
131 int oldSize = buf.size();
132 buf.resize(oldSize + nNew);
133 for (
int s = 0; s < nNew; ++s)
134 buf[oldSize + s] =
static_cast<float>(data(ch, s));
137 if (m_maxStoredSamples > 0 && buf.size() > m_maxStoredSamples) {
138 int drop = buf.size() - m_maxStoredSamples;
141 m_firstSample += drop;
154 QWriteLocker lk(&m_lock);
155 for (
auto &ch : m_channelData)
167 QWriteLocker lk(&m_lock);
168 m_scaleMap = scaleMap;
170 rebuildDisplayInfo();
178 QMap<qint32, float> intMap;
179 if (scaleMap.contains(QStringLiteral(
"MEG_grad")))
180 intMap[kMEGGradKind] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_grad")));
181 if (scaleMap.contains(QStringLiteral(
"MEG_mag")))
182 intMap[kMEGMagKind] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_mag")));
183 if (scaleMap.contains(QStringLiteral(
"MEG_EEG")))
184 intMap[
FIFFV_EEG_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_EEG")));
185 if (scaleMap.contains(QStringLiteral(
"MEG_EOG")))
186 intMap[
FIFFV_EOG_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_EOG")));
187 if (scaleMap.contains(QStringLiteral(
"MEG_EMG")))
188 intMap[
FIFFV_EMG_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_EMG")));
189 if (scaleMap.contains(QStringLiteral(
"MEG_ECG")))
190 intMap[
FIFFV_ECG_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_ECG")));
191 if (scaleMap.contains(QStringLiteral(
"MEG_MISC")))
192 intMap[
FIFFV_MISC_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_MISC")));
193 if (scaleMap.contains(QStringLiteral(
"MEG_STIM")))
194 intMap[
FIFFV_STIM_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_STIM")));
203 QWriteLocker lk(&m_lock);
204 m_virtualDisplayInfo = virtualChannels;
206 const int realChannelCount = m_pFiffInfo ? m_pFiffInfo->nchan : 0;
207 const int totalChannelCount = realChannelCount + m_virtualDisplayInfo.size();
208 m_channelData.resize(totalChannelCount);
211 rebuildDisplayInfo();
221 QWriteLocker lk(&m_lock);
222 m_signalColor = color;
224 rebuildDisplayInfo();
232 QWriteLocker lk(&m_lock);
233 m_maxStoredSamples = (n > 0) ? n : 0;
248 QWriteLocker lk(&m_lock);
249 m_detrendMode = mode;
258 QWriteLocker lk(&m_lock);
259 if (channelIdx >= 0 && channelIdx < m_displayInfo.size())
260 m_displayInfo[channelIdx].bad = bad;
262 if (m_pFiffInfo && channelIdx >= 0 && channelIdx < m_pFiffInfo->nchan) {
263 const QString channelName = m_pFiffInfo->ch_names.value(channelIdx);
264 const int badIndex = m_pFiffInfo->bads.indexOf(channelName);
268 m_pFiffInfo->bads.append(channelName);
269 }
else if (badIndex >= 0) {
270 m_pFiffInfo->bads.removeAt(badIndex);
282 QReadLocker lk(&m_lock);
283 const int realChannelCount = m_pFiffInfo ? m_pFiffInfo->nchan : 0;
284 return qMax(m_channelData.size(), realChannelCount + m_virtualDisplayInfo.size());
291 QReadLocker lk(&m_lock);
292 return m_firstSample;
299 QReadLocker lk(&m_lock);
300 return m_channelData.isEmpty() ? 0 : m_channelData[0].size();
307 QReadLocker lk(&m_lock);
308 if (channelIdx >= 0 && channelIdx < m_displayInfo.size())
309 return m_displayInfo[channelIdx];
317 QReadLocker lk(&m_lock);
318 if (channelIdx < 0 || channelIdx >= m_channelData.size())
320 const QVector<float> &src = m_channelData[channelIdx];
321 int bufFirst = qBound(0, first - m_firstSample, src.size());
322 int bufLast = qBound(0, last - m_firstSample, src.size());
323 if (bufLast <= bufFirst)
326 constexpr int kMax = 1000;
327 if (bufLast - bufFirst > kMax)
328 bufFirst = bufLast - kMax;
330 for (
int i = bufFirst; i < bufLast; ++i)
331 sum +=
static_cast<double>(src[i]) * src[i];
332 return static_cast<float>(qSqrt(sum / (bufLast - bufFirst)));
339 QReadLocker lk(&m_lock);
340 if (channelIdx < 0 || channelIdx >= m_channelData.size())
342 const QVector<float> &src = m_channelData[channelIdx];
343 int bufIdx = sample - m_firstSample;
344 if (bufIdx < 0 || bufIdx >= src.size())
355 int &vboFirstSample)
const
357 QReadLocker lk(&m_lock);
361 if (channelIdx < 0 || channelIdx >= m_channelData.size()
362 || pixelWidth <= 0 || firstSample >= lastSample) {
366 const QVector<float> &src = m_channelData[channelIdx];
369 int bufLast = lastSample - m_firstSample;
372 bufFirst = qBound(0, bufFirst, src.size());
373 bufLast = qBound(0, bufLast, src.size());
379 vboFirstSample = m_firstSample + bufFirst;
381 if (bufLast <= bufFirst)
384 int nSamples = bufLast - bufFirst;
387 float dcOffset = 0.f;
388 float linearSlope = 0.f;
389 float linearIntercept = 0.f;
395 for (
int i = bufFirst; i < bufLast; ++i)
397 dcOffset =
static_cast<float>(sum / nSamples);
398 }
else if (useLinear) {
400 double sumX = 0.0, sumY = 0.0, sumXX = 0.0, sumXY = 0.0;
401 for (
int i = 0; i < nSamples; ++i) {
402 double x =
static_cast<double>(i);
403 double y =
static_cast<double>(src[bufFirst + i]);
409 double denom = nSamples * sumXX - sumX * sumX;
410 if (qAbs(denom) > 1e-30) {
411 linearSlope =
static_cast<float>((nSamples * sumXY - sumX * sumY) / denom);
412 linearIntercept =
static_cast<float>((sumY - linearSlope * sumX) / nSamples);
416 if (nSamples <= pixelWidth * 2) {
418 QVector<float> result;
419 result.reserve(nSamples * 2);
420 for (
int i = 0; i < nSamples; ++i) {
421 float trend = useMean ? dcOffset
422 : useLinear ? (linearSlope * i + linearIntercept)
424 result.append(
static_cast<float>(i));
425 result.append(src[bufFirst + i] - trend);
435 QVector<float> result;
436 result.reserve(pixelWidth * 4);
438 float spp =
static_cast<float>(nSamples) / pixelWidth;
440 for (
int px = 0; px < pixelWidth; ++px) {
441 int sBegin = bufFirst +
static_cast<int>(px * spp);
442 int sEnd = bufFirst +
static_cast<int>((px + 1) * spp);
443 sEnd = qMin(sEnd, bufLast);
444 if (sBegin >= sEnd) sBegin = qMax(sEnd - 1, bufFirst);
446 float minV = src[sBegin];
447 float maxV = src[sBegin];
448 for (
int s = sBegin + 1; s < sEnd; ++s) {
449 if (src[s] < minV) minV = src[s];
450 if (src[s] > maxV) maxV = src[s];
454 float tCenter =
static_cast<float>(sBegin - bufFirst) + (sEnd - sBegin) * 0.5f;
455 float trend = useMean ? dcOffset
456 : useLinear ? (linearSlope * tCenter + linearIntercept)
461 float xOffset = px * spp;
464 result.append(xOffset); result.append(maxV);
465 result.append(xOffset); result.append(minV);
475void ChannelDataModel::rebuildDisplayInfo()
477 QWriteLocker lk(&m_lock);
478 const int realChannelCount = m_pFiffInfo ? m_pFiffInfo->nchan : 0;
479 const int nCh = qMax(m_channelData.size(), realChannelCount + m_virtualDisplayInfo.size());
480 m_displayInfo.resize(nCh);
481 for (
int ch = 0; ch < nCh; ++ch) {
482 if (ch >= realChannelCount && ch - realChannelCount < m_virtualDisplayInfo.size()) {
484 if (info.
name.isEmpty())
485 info.
name = QString(
"Virtual %1").arg(ch - realChannelCount + 1);
488 if (!info.
color.isValid())
489 info.
color = m_signalColor;
492 m_displayInfo[ch] = info;
493 m_displayInfo[ch].isVirtualChannel =
true;
497 m_displayInfo[ch].amplitudeMax = amplitudeMaxForChannel(ch);
498 m_displayInfo[ch].color = colorForChannel(ch);
499 if (m_pFiffInfo && ch < realChannelCount)
500 m_displayInfo[ch].name = m_pFiffInfo->ch_names[ch];
502 m_displayInfo[ch].name = QString(
"CH %1").arg(ch + 1);
503 m_displayInfo[ch].typeLabel = typeLabelForChannel(ch);
504 m_displayInfo[ch].bad = (m_pFiffInfo && ch < realChannelCount)
505 ? m_pFiffInfo->bads.contains(m_displayInfo[ch].name)
507 m_displayInfo[ch].isVirtualChannel =
false;
513float ChannelDataModel::amplitudeMaxForChannel(
int ch)
const
515 if (!m_pFiffInfo || ch >= m_pFiffInfo->nchan)
516 return kScaleFallback;
518 const auto &info = m_pFiffInfo->chs[ch];
519 qint32 kind = info.kind;
524 return m_scaleMap.value(kMEGGradKind,
527 return m_scaleMap.value(kMEGMagKind,
530 if (m_scaleMap.contains(kind))
531 return m_scaleMap.value(kind);
532 return kScaleFallback;
537QColor ChannelDataModel::colorForChannel(
int ch)
const
539 if (!m_pFiffInfo || ch >= m_pFiffInfo->nchan)
540 return m_signalColor;
543 switch (m_pFiffInfo->chs[ch].kind) {
550 default:
return m_signalColor;
556QString ChannelDataModel::typeLabelForChannel(
int ch)
const
558 if (!m_pFiffInfo || ch >= m_pFiffInfo->nchan)
559 return QStringLiteral(
"MISC");
560 switch (m_pFiffInfo->chs[ch].kind) {
563 return QStringLiteral(
"MEG grad");
564 return QStringLiteral(
"MEG mag");
570 default:
return QStringLiteral(
"MISC");
578 QReadLocker lk(&m_lock);
579 return m_pFiffInfo ?
static_cast<float>(m_pFiffInfo->sfreq) : 0.f;
Circular-buffer Qt model exposing a rolling window of the live FIFF stream as a table.
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
FIFF file I/O, in-memory data structures and high-level readers/writers.
2-D display widgets and visualisation helpers (charts, topography, colour maps).
DetrendMode
Channel display metadata (read-only from the renderer's perspective).
Channel display metadata (read-only from the renderer's perspective).
void appendData(const Eigen::MatrixXd &data)
void setDetrendMode(DetrendMode mode)
void setScaleMap(const QMap< qint32, float > &scaleMap)
float channelRms(int channelIdx, int firstSample, int lastSample) const
float sampleValueAt(int channelIdx, int sample) const
void setScaleMapFromStrings(const QMap< QString, double > &scaleMap)
void setVirtualChannels(const QVector< ChannelDisplayInfo > &virtualChannels)
void setChannelBad(int channelIdx, bool bad)
void setRemoveDC(bool remove)
void init(QSharedPointer< FIFFLIB::FiffInfo > pFiffInfo)
void setSignalColor(const QColor &color)
void setData(const Eigen::MatrixXd &data, int firstSample=0)
ChannelDisplayInfo channelInfo(int channelIdx) const
void setMaxStoredSamples(int n)
QVector< float > decimatedVertices(int channelIdx, int firstSample, int lastSample, int pixelWidth, int &vboFirstSample) const
ChannelDataModel(QObject *parent=nullptr)