Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP [Fix,Enh]: Enable control over fitting window size and time between fits #892

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 87 additions & 15 deletions applications/mne_scan/plugins/hpi/hpi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@ using namespace INVERSELIB;
// DEFINE LOCAL CONSTANTS
//=============================================================================================================

constexpr const int defaultFittingWindowSize(300);
constexpr const int defaultFittingWindowSize(2000);

//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

Hpi::Hpi()
: m_iNumberBadChannels(0)
, m_iFittingWindowSize(defaultFittingWindowSize)
, m_iRepetitionTimeInSamples(defaultFittingWindowSize)
, m_dAllowedMeanErrorDist(10)
, m_dAllowedMovement(3)
, m_dAllowedRotation(5)
Expand Down Expand Up @@ -322,22 +322,25 @@ void Hpi::initPluginControlWidgets()
this, &Hpi::onAllowedMovementChanged);
connect(pHpiSettingsView, &HpiSettingsView::allowedRotationChanged,
this, &Hpi::onAllowedRotationChanged);
connect(pHpiSettingsView, &HpiSettingsView::fittingWindowSizeChanged,
connect(pHpiSettingsView, &HpiSettingsView::repetitionTimeChanged,
this, &Hpi::setTimeBetweenFits);
connect(pHpiSettingsView, &HpiSettingsView::fittingWindowTimeChanged,
this, &Hpi::setFittingWindowSize);
connect(this, &Hpi::errorsChanged,
pHpiSettingsView, &HpiSettingsView::setErrorLabels, Qt::BlockingQueuedConnection);
connect(this, &Hpi::movementResultsChanged,
pHpiSettingsView, &HpiSettingsView::setMovementResults, Qt::BlockingQueuedConnection);
connect(this, &Hpi::newDigitizerList,
pHpiSettingsView, &HpiSettingsView::newDigitizerList);

connect(this, &Hpi::minimumWindowSizeChanged,
pHpiSettingsView, &HpiSettingsView::setMinimumWindowSize);

onSspStatusChanged(pHpiSettingsView->getSspStatusChanged());
onCompStatusChanged(pHpiSettingsView->getCompStatusChanged());
onAllowedMeanErrorDistChanged(pHpiSettingsView->getAllowedMeanErrorDistChanged());
onAllowedMovementChanged(pHpiSettingsView->getAllowedMovementChanged());
onAllowedRotationChanged(pHpiSettingsView->getAllowedRotationChanged());
setFittingWindowSize(pHpiSettingsView->getFittingWindowSize());
setFittingWindowSize(pHpiSettingsView->getFittingRepetitionTime());
onContHpiStatusChanged(pHpiSettingsView->continuousHPIChecked());

plControlWidgets.append(pHpiSettingsView);
Expand Down Expand Up @@ -481,6 +484,12 @@ void Hpi::onCoilFrequenciesChanged(const QVector<int>& vCoilFreqs)
m_mutex.lock();
m_vCoilFreqs = vCoilFreqs;
m_mutex.unlock();

const int iMinimalWindowSize = computeMinimalWindowsize();

m_mutex.lock();
m_iFittingWindowSize = iMinimalWindowSize;
m_mutex.unlock();
}

//=============================================================================================================
Expand Down Expand Up @@ -515,10 +524,18 @@ void Hpi::onContHpiStatusChanged(bool bChecked)

//=============================================================================================================

void Hpi::setFittingWindowSize(int winSize)
void Hpi::setTimeBetweenFits(double dRepetitionTime)
{
QMutexLocker locker(&m_mutex);
m_iFittingWindowSize = winSize;
m_iRepetitionTimeInSamples = dRepetitionTime * m_pFiffInfo->sfreq;
}

//=============================================================================================================

void Hpi::setFittingWindowSize(double dFittingWindowSizeInMillisecons)
{
QMutexLocker locker(&m_mutex);
m_iFittingWindowSize = dFittingWindowSizeInMillisecons / 1000.0 * m_pFiffInfo->sfreq;
}

//=============================================================================================================
Expand All @@ -530,6 +547,45 @@ void Hpi::onDevHeadTransAvailable(const FIFFLIB::FiffCoordTrans& devHeadTrans)

//=============================================================================================================

int Hpi::computeMinimalWindowsize()
{
m_mutex.lock();
double dSFreq = m_pFiffInfo->sfreq;
double dLineFreq = m_pFiffInfo->linefreq;
QVector<int> vecFreqs = m_vCoilFreqs;
m_mutex.unlock();

// get number of harmonics to take into account
int iMaxHpiFreq = *std::max_element(vecFreqs.constBegin(), vecFreqs.constEnd());
int nHarmonics = ceil(iMaxHpiFreq/dLineFreq);

// append frequency vector
for(int i = 1; i <= nHarmonics; i++) {
vecFreqs << dLineFreq * i;
}

// sort vector in increasing order
std::sort(vecFreqs.begin(), vecFreqs.end());

// get minimimal required frequency difference
int iMinDeltaF = dSFreq;

for(int i = 0; i < vecFreqs.size() - 1; i++) {
int iSubsequentDeltaF = vecFreqs[i+1] - vecFreqs[i];
if(iSubsequentDeltaF< iMinDeltaF) {
iMinDeltaF = iSubsequentDeltaF;
}
}

// compute buffersize needed to provide this resolution in frequency space N = FS/df
int iWindowSize = ceil(dSFreq/iMinDeltaF);
emit minimumWindowSizeChanged(iWindowSize/dSFreq);

return iWindowSize;
}

//=============================================================================================================

void Hpi::run()
{
// Wait for fiff info
Expand Down Expand Up @@ -571,31 +627,46 @@ void Hpi::run()
double dMovement = 0.0;
double dRotation = 0.0;

int iRepetitionIndexCounter = 0;
int iDataIndexCounter = 0;
MatrixXd matData;

m_mutex.lock();
int fittingWindowSize = m_iFittingWindowSize;
int iFittingWindowSize = m_iFittingWindowSize;
int iRepetitionTimeInSamples = m_iRepetitionTimeInSamples;

m_mutex.unlock();

MatrixXd matDataMerged(m_pFiffInfo->chs.size(), fittingWindowSize);
MatrixXd matDataMerged(m_pFiffInfo->chs.size(), iFittingWindowSize);
bool bOrder = false;
QElapsedTimer timer;
timer.start();

while(!isInterruptionRequested()) {
m_mutex.lock();
if(fittingWindowSize != m_iFittingWindowSize) {
fittingWindowSize = m_iFittingWindowSize;
std::cout << "Fitting window size: " << fittingWindowSize << "\n";
matDataMerged.resize(m_pFiffInfo->chs.size(), fittingWindowSize);
iDataIndexCounter = 0;
// check if fitting window size has changed and resize matData if necessary
if(iFittingWindowSize != m_iFittingWindowSize) {
iFittingWindowSize = m_iFittingWindowSize;
matDataMerged.resize(m_pFiffInfo->chs.size(), iFittingWindowSize);
iRepetitionIndexCounter = 0;
}

// check if time between fits has changed
if(iRepetitionTimeInSamples != m_iRepetitionTimeInSamples) {
iRepetitionTimeInSamples = m_iRepetitionTimeInSamples;
iRepetitionIndexCounter = 0;
}
m_mutex.unlock();

//pop matrix
if(m_pCircularBuffer->pop(matData)) {
if(iDataIndexCounter + matData.cols() < matDataMerged.cols()) {
if(iRepetitionIndexCounter + matData.cols() < iRepetitionTimeInSamples) {
iRepetitionIndexCounter += matData.cols();

} else if(iDataIndexCounter + matData.cols() < iFittingWindowSize) {
matDataMerged.block(0, iDataIndexCounter, matData.rows(), matData.cols()) = matData;
iDataIndexCounter += matData.cols();

} else {
m_mutex.lock();
if(m_bDoSingleHpi) {
Expand Down Expand Up @@ -671,6 +742,7 @@ void Hpi::run()
}
}

iRepetitionIndexCounter = 0;
iDataIndexCounter = 0;
}
}
Expand Down
34 changes: 29 additions & 5 deletions applications/mne_scan/plugins/hpi/hpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,22 @@ class HPISHARED_EXPORT Hpi : public SCSHAREDLIB::AbstractAlgorithm
*/
void onDevHeadTransAvailable(const FIFFLIB::FiffCoordTrans& devHeadTrans);


//=========================================================================================================
/**
* Set repetition time between hpi fits.
*
* @param[in] dRepetitionTime Repetition time in seconds
*/
void setTimeBetweenFits(double dRepetitionTimeInSeconds);

//=========================================================================================================
/**
* Set fitting window size when doing continuous hpi.
* Set hpi fitting window size.
*
* @param[in] winSize window size in samples
* @param[in] dFittingWindowSizeInSeconds Fitting window size in seconds
*/
void setFittingWindowSize(int winSize);
void setFittingWindowSize(double dFittingWindowSizeInSeconds);

//=========================================================================================================
/**
Expand Down Expand Up @@ -289,6 +298,19 @@ class HPISHARED_EXPORT Hpi : public SCSHAREDLIB::AbstractAlgorithm
*/
void updateDigitizerInfo();

//=========================================================================================================
/**
* Returns the optimal window size for hpi computation
*
* Compute the optimal window size based on minimal detectable frequency difference between HPI
* freqeuencies and line frequency (+ harmonics).
*
* @return optimal Windowsize
*/
int computeMinimalWindowsize();



void resetState();

QMutex m_mutex; /**< The threads mutex.*/
Expand All @@ -298,8 +320,8 @@ class HPISHARED_EXPORT Hpi : public SCSHAREDLIB::AbstractAlgorithm
QString m_sFilePathDigitzers; /**< The file path to the current digitzers. */

qint16 m_iNumberBadChannels; /**< The number of bad channels.*/
qint16 m_iFittingWindowSize; /**< The number of samples in each fitting window.*/

qint16 m_iRepetitionTimeInSamples; /**< The number of samples to wait between fits.*/
int m_iFittingWindowSize; /**< The number of samples in each fitting window.*/
double m_dAllowedMeanErrorDist; /**< The allowed error distance in order for the last fit to be counted as a good fit.*/
double m_dAllowedMovement; /**< The allowed head movement regarding reference head position.*/
double m_dAllowedRotation; /**< The allowed head rotation regarding reference head position in degree.*/
Expand All @@ -321,6 +343,8 @@ class HPISHARED_EXPORT Hpi : public SCSHAREDLIB::AbstractAlgorithm
SCSHAREDLIB::PluginOutputData<SCMEASLIB::RealTimeHpiResult>::SPtr m_pHpiOutput; /**< The RealTimeHpiResult of the Hpi output.*/

signals:
void minimumWindowSizeChanged(const double dWindowSizeInSeconds);

void errorsChanged(const QVector<double>& vErrors,
double dMeanErrorDist);
void movementResultsChanged(double dMovement,
Expand Down
Loading