48#include <QWriteLocker>
65 constexpr float kScaleMEGGrad = 400e-13f;
66 constexpr float kScaleMEGMag = 1.2e-12f;
67 constexpr float kScaleEEG = 30e-6f;
68 constexpr float kScaleEOG = 150e-6f;
69 constexpr float kScaleEMG = 1e-3f;
70 constexpr float kScaleECG = 1e-3f;
71 constexpr float kScaleSTIM = 5.0f;
72 constexpr float kScaleMISC = 1.0f;
73 constexpr float kScaleFallback = 1.0f;
76 constexpr qint32 kMEGGradKind = -1;
77 constexpr qint32 kMEGMagKind = -2;
101 QWriteLocker lk(&m_lock);
102 m_pFiffInfo = pFiffInfo;
104 int nCh = (pFiffInfo ? pFiffInfo->nchan : 0) + m_virtualDisplayInfo.size();
105 m_channelData.resize(nCh);
106 for (
auto &ch : m_channelData)
111 rebuildDisplayInfo();
119 if (data.rows() == 0 || data.cols() == 0)
122 QWriteLocker lk(&m_lock);
123 int nCh =
static_cast<int>(data.rows());
124 m_channelData.resize(nCh);
125 for (
int ch = 0; ch < nCh; ++ch) {
126 m_channelData[ch].resize(
static_cast<int>(data.cols()));
127 for (
int s = 0; s < static_cast<int>(data.cols()); ++s)
128 m_channelData[ch][s] =
static_cast<float>(data(ch, s));
140 if (data.rows() == 0 || data.cols() == 0)
143 QWriteLocker lk(&m_lock);
144 int nCh =
static_cast<int>(data.rows());
145 int nNew =
static_cast<int>(data.cols());
147 if (m_channelData.size() != nCh)
148 m_channelData.resize(nCh);
150 for (
int ch = 0; ch < nCh; ++ch) {
151 auto &buf = m_channelData[ch];
152 int oldSize = buf.size();
153 buf.resize(oldSize + nNew);
154 for (
int s = 0; s < nNew; ++s)
155 buf[oldSize + s] =
static_cast<float>(data(ch, s));
158 if (m_maxStoredSamples > 0 && buf.size() > m_maxStoredSamples) {
159 int drop = buf.size() - m_maxStoredSamples;
162 m_firstSample += drop;
175 QWriteLocker lk(&m_lock);
176 for (
auto &ch : m_channelData)
188 QWriteLocker lk(&m_lock);
189 m_scaleMap = scaleMap;
191 rebuildDisplayInfo();
199 QMap<qint32, float> intMap;
200 if (scaleMap.contains(QStringLiteral(
"MEG_grad")))
201 intMap[kMEGGradKind] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_grad")));
202 if (scaleMap.contains(QStringLiteral(
"MEG_mag")))
203 intMap[kMEGMagKind] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_mag")));
204 if (scaleMap.contains(QStringLiteral(
"MEG_EEG")))
205 intMap[
FIFFV_EEG_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_EEG")));
206 if (scaleMap.contains(QStringLiteral(
"MEG_EOG")))
207 intMap[
FIFFV_EOG_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_EOG")));
208 if (scaleMap.contains(QStringLiteral(
"MEG_EMG")))
209 intMap[
FIFFV_EMG_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_EMG")));
210 if (scaleMap.contains(QStringLiteral(
"MEG_ECG")))
211 intMap[
FIFFV_ECG_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_ECG")));
212 if (scaleMap.contains(QStringLiteral(
"MEG_MISC")))
213 intMap[
FIFFV_MISC_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_MISC")));
214 if (scaleMap.contains(QStringLiteral(
"MEG_STIM")))
215 intMap[
FIFFV_STIM_CH] =
static_cast<float>(scaleMap.value(QStringLiteral(
"MEG_STIM")));
224 QWriteLocker lk(&m_lock);
225 m_virtualDisplayInfo = virtualChannels;
227 const int realChannelCount = m_pFiffInfo ? m_pFiffInfo->nchan : 0;
228 const int totalChannelCount = realChannelCount + m_virtualDisplayInfo.size();
229 m_channelData.resize(totalChannelCount);
232 rebuildDisplayInfo();
242 QWriteLocker lk(&m_lock);
243 m_signalColor = color;
245 rebuildDisplayInfo();
253 QWriteLocker lk(&m_lock);
254 m_maxStoredSamples = (n > 0) ? n : 0;
269 QWriteLocker lk(&m_lock);
270 m_detrendMode = mode;
279 QWriteLocker lk(&m_lock);
280 if (channelIdx >= 0 && channelIdx < m_displayInfo.size())
281 m_displayInfo[channelIdx].bad = bad;
283 if (m_pFiffInfo && channelIdx >= 0 && channelIdx < m_pFiffInfo->nchan) {
284 const QString channelName = m_pFiffInfo->ch_names.value(channelIdx);
285 const int badIndex = m_pFiffInfo->bads.indexOf(channelName);
289 m_pFiffInfo->bads.append(channelName);
290 }
else if (badIndex >= 0) {
291 m_pFiffInfo->bads.removeAt(badIndex);
303 QReadLocker lk(&m_lock);
304 const int realChannelCount = m_pFiffInfo ? m_pFiffInfo->nchan : 0;
305 return qMax(m_channelData.size(), realChannelCount + m_virtualDisplayInfo.size());
312 QReadLocker lk(&m_lock);
313 return m_firstSample;
320 QReadLocker lk(&m_lock);
321 return m_channelData.isEmpty() ? 0 : m_channelData[0].size();
328 QReadLocker lk(&m_lock);
329 if (channelIdx >= 0 && channelIdx < m_displayInfo.size())
330 return m_displayInfo[channelIdx];
338 QReadLocker lk(&m_lock);
339 if (channelIdx < 0 || channelIdx >= m_channelData.size())
341 const QVector<float> &src = m_channelData[channelIdx];
342 int bufFirst = qBound(0, first - m_firstSample, src.size());
343 int bufLast = qBound(0, last - m_firstSample, src.size());
344 if (bufLast <= bufFirst)
347 constexpr int kMax = 1000;
348 if (bufLast - bufFirst > kMax)
349 bufFirst = bufLast - kMax;
351 for (
int i = bufFirst; i < bufLast; ++i)
352 sum +=
static_cast<double>(src[i]) * src[i];
353 return static_cast<float>(qSqrt(sum / (bufLast - bufFirst)));
360 QReadLocker lk(&m_lock);
361 if (channelIdx < 0 || channelIdx >= m_channelData.size())
363 const QVector<float> &src = m_channelData[channelIdx];
364 int bufIdx = sample - m_firstSample;
365 if (bufIdx < 0 || bufIdx >= src.size())
376 int &vboFirstSample)
const
378 QReadLocker lk(&m_lock);
382 if (channelIdx < 0 || channelIdx >= m_channelData.size()
383 || pixelWidth <= 0 || firstSample >= lastSample) {
387 const QVector<float> &src = m_channelData[channelIdx];
390 int bufLast = lastSample - m_firstSample;
393 bufFirst = qBound(0, bufFirst, src.size());
394 bufLast = qBound(0, bufLast, src.size());
400 vboFirstSample = m_firstSample + bufFirst;
402 if (bufLast <= bufFirst)
405 int nSamples = bufLast - bufFirst;
408 float dcOffset = 0.f;
409 float linearSlope = 0.f;
410 float linearIntercept = 0.f;
416 for (
int i = bufFirst; i < bufLast; ++i)
418 dcOffset =
static_cast<float>(sum / nSamples);
419 }
else if (useLinear) {
421 double sumX = 0.0, sumY = 0.0, sumXX = 0.0, sumXY = 0.0;
422 for (
int i = 0; i < nSamples; ++i) {
423 double x =
static_cast<double>(i);
424 double y =
static_cast<double>(src[bufFirst + i]);
430 double denom = nSamples * sumXX - sumX * sumX;
431 if (qAbs(denom) > 1e-30) {
432 linearSlope =
static_cast<float>((nSamples * sumXY - sumX * sumY) / denom);
433 linearIntercept =
static_cast<float>((sumY - linearSlope * sumX) / nSamples);
437 if (nSamples <= pixelWidth * 2) {
439 QVector<float> result;
440 result.reserve(nSamples * 2);
441 for (
int i = 0; i < nSamples; ++i) {
442 float trend = useMean ? dcOffset
443 : useLinear ? (linearSlope * i + linearIntercept)
445 result.append(
static_cast<float>(i));
446 result.append(src[bufFirst + i] - trend);
456 QVector<float> result;
457 result.reserve(pixelWidth * 4);
459 float spp =
static_cast<float>(nSamples) / pixelWidth;
461 for (
int px = 0; px < pixelWidth; ++px) {
462 int sBegin = bufFirst +
static_cast<int>(px * spp);
463 int sEnd = bufFirst +
static_cast<int>((px + 1) * spp);
464 sEnd = qMin(sEnd, bufLast);
465 if (sBegin >= sEnd) sBegin = qMax(sEnd - 1, bufFirst);
467 float minV = src[sBegin];
468 float maxV = src[sBegin];
469 for (
int s = sBegin + 1; s < sEnd; ++s) {
470 if (src[s] < minV) minV = src[s];
471 if (src[s] > maxV) maxV = src[s];
475 float tCenter =
static_cast<float>(sBegin - bufFirst) + (sEnd - sBegin) * 0.5f;
476 float trend = useMean ? dcOffset
477 : useLinear ? (linearSlope * tCenter + linearIntercept)
482 float xOffset = px * spp;
485 result.append(xOffset); result.append(maxV);
486 result.append(xOffset); result.append(minV);
496void ChannelDataModel::rebuildDisplayInfo()
498 QWriteLocker lk(&m_lock);
499 const int realChannelCount = m_pFiffInfo ? m_pFiffInfo->nchan : 0;
500 const int nCh = qMax(m_channelData.size(), realChannelCount + m_virtualDisplayInfo.size());
501 m_displayInfo.resize(nCh);
502 for (
int ch = 0; ch < nCh; ++ch) {
503 if (ch >= realChannelCount && ch - realChannelCount < m_virtualDisplayInfo.size()) {
505 if (info.
name.isEmpty())
506 info.
name = QString(
"Virtual %1").arg(ch - realChannelCount + 1);
509 if (!info.
color.isValid())
510 info.
color = m_signalColor;
513 m_displayInfo[ch] = info;
514 m_displayInfo[ch].isVirtualChannel =
true;
518 m_displayInfo[ch].amplitudeMax = amplitudeMaxForChannel(ch);
519 m_displayInfo[ch].color = colorForChannel(ch);
520 if (m_pFiffInfo && ch < realChannelCount)
521 m_displayInfo[ch].name = m_pFiffInfo->ch_names[ch];
523 m_displayInfo[ch].name = QString(
"CH %1").arg(ch + 1);
524 m_displayInfo[ch].typeLabel = typeLabelForChannel(ch);
525 m_displayInfo[ch].bad = (m_pFiffInfo && ch < realChannelCount)
526 ? m_pFiffInfo->bads.contains(m_displayInfo[ch].name)
528 m_displayInfo[ch].isVirtualChannel =
false;
534float ChannelDataModel::amplitudeMaxForChannel(
int ch)
const
536 if (!m_pFiffInfo || ch >= m_pFiffInfo->nchan)
537 return kScaleFallback;
539 const auto &info = m_pFiffInfo->chs[ch];
540 qint32 kind = info.kind;
545 return m_scaleMap.value(kMEGGradKind,
548 return m_scaleMap.value(kMEGMagKind,
551 if (m_scaleMap.contains(kind))
552 return m_scaleMap.value(kind);
553 return kScaleFallback;
558QColor ChannelDataModel::colorForChannel(
int ch)
const
560 if (!m_pFiffInfo || ch >= m_pFiffInfo->nchan)
561 return m_signalColor;
564 switch (m_pFiffInfo->chs[ch].kind) {
571 default:
return m_signalColor;
577QString ChannelDataModel::typeLabelForChannel(
int ch)
const
579 if (!m_pFiffInfo || ch >= m_pFiffInfo->nchan)
580 return QStringLiteral(
"MISC");
581 switch (m_pFiffInfo->chs[ch].kind) {
584 return QStringLiteral(
"MEG grad");
585 return QStringLiteral(
"MEG mag");
591 default:
return QStringLiteral(
"MISC");
599 QReadLocker lk(&m_lock);
600 return m_pFiffInfo ?
static_cast<float>(m_pFiffInfo->sfreq) : 0.f;
Declaration of the ChannelDataModel class.
FiffInfo class declaration.
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
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)