diff --git a/Source/Processors/DataThreads/RHD2000Editor.cpp b/Source/Processors/DataThreads/RHD2000Editor.cpp index 081e47007eb4ad09bd04264c4111cac1f1f1a997..7a0a3589743c2e4e178952a0afba758b9b43dd5a 100644 --- a/Source/Processors/DataThreads/RHD2000Editor.cpp +++ b/Source/Processors/DataThreads/RHD2000Editor.cpp @@ -541,6 +541,9 @@ RHD2000Editor::RHD2000Editor(GenericProcessor* parentNode, measureWhenRecording = false; saveImpedances = false; + impedanceData = new ImpedanceData(); + impedanceData->valid = false; + // add headstage-specific controls (currently just an enable/disable button) for (int i = 0; i < 4; i++) { @@ -689,14 +692,18 @@ void RHD2000Editor::scanPorts() void RHD2000Editor::measureImpedance() { - Array<int> stream, channel; - Array<float> magnitude, phase; - board->runImpedanceTest(stream,channel,magnitude,phase); + impedanceData->valid = false; + board->runImpedanceTest(impedanceData); +} +void RHD2000Editor::handleAsyncUpdate() +{ + if (!impedanceData->valid) + return; if (canvas == nullptr) VisualizerEditor::canvas = createNewCanvas(); // update components... - canvas->updateImpedance(stream,channel,magnitude,phase); + canvas->updateImpedance(impedanceData->streams, impedanceData->channels, impedanceData->magnitudes, impedanceData->phases); if (saveImpedances) { getProcessorGraph()->getRecordNode()->createNewDirectory(); @@ -711,14 +718,14 @@ void RHD2000Editor::measureImpedance() XmlDocument doc(file); ScopedPointer<XmlElement> xml = new XmlElement("CHANNEL_IMPEDANCES"); - for (int i = 0; i < channel.size(); i++) + for (int i = 0; i < impedanceData->channels.size(); i++) { XmlElement* chan = new XmlElement("CHANNEL"); chan->setAttribute("name",board->getChannelName(i)); - chan->setAttribute("stream",stream[i]); - chan->setAttribute("channel_number",channel[i]); - chan->setAttribute("magnitude",magnitude[i]); - chan->setAttribute("phase",phase[i]); + chan->setAttribute("stream", impedanceData->streams[i]); + chan->setAttribute("channel_number", impedanceData->channels[i]); + chan->setAttribute("magnitude", impedanceData->magnitudes[i]); + chan->setAttribute("phase", impedanceData->phases[i]); xml->addChildElement(chan); } xml->writeToFile(file,String::empty); diff --git a/Source/Processors/DataThreads/RHD2000Editor.h b/Source/Processors/DataThreads/RHD2000Editor.h index 66da62c90901051a6320d305533828e844d52067..b20b7b6a79c97b1674f999b275688cb22b0a8e58 100644 --- a/Source/Processors/DataThreads/RHD2000Editor.h +++ b/Source/Processors/DataThreads/RHD2000Editor.h @@ -38,6 +38,7 @@ class AudioInterface; class RHD2000Thread; class UtilityButton; +struct ImpedanceData; /** @@ -157,7 +158,7 @@ public: ScopedPointer<FPGAchannelList> channelList; }; -class RHD2000Editor : public VisualizerEditor, public ComboBox::Listener +class RHD2000Editor : public VisualizerEditor, public ComboBox::Listener, public AsyncUpdater { public: @@ -184,6 +185,8 @@ public: bool getSaveImpedance(); bool getAutoMeasureImpedance(); + void handleAsyncUpdate(); + private: OwnedArray<HeadstageOptionsInterface> headstageOptionsInterfaces; @@ -208,6 +211,9 @@ private: RHD2000Thread* board; FPGAcanvas* canvas; + + ScopedPointer<ImpedanceData> impedanceData; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RHD2000Editor); }; diff --git a/Source/Processors/DataThreads/RHD2000Thread.cpp b/Source/Processors/DataThreads/RHD2000Thread.cpp index 7bc9ce5e0a5e0f27b24938ed02ff489d24991d43..d210a6e028e9ffad383f82a172f57400d2993ac0 100644 --- a/Source/Processors/DataThreads/RHD2000Thread.cpp +++ b/Source/Processors/DataThreads/RHD2000Thread.cpp @@ -22,6 +22,7 @@ */ #include "RHD2000Thread.h" +#include "RHD2000Editor.h" #include "../SourceNode/SourceNode.h" #if defined(_WIN32) @@ -82,6 +83,7 @@ RHD2000Thread::RHD2000Thread(SourceNode* sn) : DataThread(sn), audioOutputL(-1), audioOutputR(-1) ,numberingScheme(1), newScan(true) { + impedanceThread = new RHDImpedanceMeasure(this); for (int i=0; i < MAX_NUM_HEADSTAGES; i++) headstagesArray.add(new RHDHeadstage(static_cast<Rhd2000EvalBoard::BoardDataSource>(i))); @@ -427,6 +429,7 @@ void RHD2000Thread::scanPorts() return; } + impedanceThread->stopThreadSafely(); //Clear previous known streams enabledStreams.clear(); @@ -930,7 +933,7 @@ float RHD2000Thread::getAdcBitVolts(int chan) double RHD2000Thread::setUpperBandwidth(double upper) { - + impedanceThread->stopThreadSafely(); desiredUpperBandwidth = upper; updateRegisters(); @@ -941,6 +944,7 @@ double RHD2000Thread::setUpperBandwidth(double upper) double RHD2000Thread::setLowerBandwidth(double lower) { + impedanceThread->stopThreadSafely(); desiredLowerBandwidth = lower; updateRegisters(); @@ -950,7 +954,7 @@ double RHD2000Thread::setLowerBandwidth(double lower) double RHD2000Thread::setDspCutoffFreq(double freq) { - + impedanceThread->stopThreadSafely(); desiredDspCutoffFreq = freq; updateRegisters(); @@ -966,6 +970,7 @@ double RHD2000Thread::getDspCutoffFreq() void RHD2000Thread::setDSPOffset(bool state) { + impedanceThread->stopThreadSafely(); dspEnabled = state; updateRegisters(); } @@ -1130,7 +1135,7 @@ void RHD2000Thread::enableAdcs(bool t) void RHD2000Thread::setSampleRate(int sampleRateIndex, bool isTemporary) { - + impedanceThread->stopThreadSafely(); if (!isTemporary) { savedSampleRateIndex = sampleRateIndex; @@ -1360,7 +1365,7 @@ void RHD2000Thread::setCableLength(int hsNum, float length) bool RHD2000Thread::startAcquisition() { - + impedanceThread->waitSafely(); dataBlock = new Rhd2000DataBlock(evalBoard->getNumEnabledDataStreams()); std::cout << "Expecting " << getNumChannels() << " channels." << std::endl; @@ -1690,6 +1695,13 @@ int RHD2000Thread::getHeadstageChannel(int& hs, int ch) return -1; } +void RHD2000Thread::runImpedanceTest(ImpedanceData* data) +{ + impedanceThread->stopThreadSafely(); + impedanceThread->prepareData(data); + impedanceThread->startThread(); +} + RHDHeadstage::RHDHeadstage(Rhd2000EvalBoard::BoardDataSource stream) : numStreams(0), channelsPerStream(32), dataStream(stream), halfChannels(false) @@ -1744,7 +1756,7 @@ bool RHDHeadstage::isPlugged() /***********************************/ /* Below is code for impedance measurements */ -RHDImpedanceMeasure::RHDImpedanceMeasure(RHD2000Thread* b, ImpedanceData* d) : Thread(""), data(d), board(b) +RHDImpedanceMeasure::RHDImpedanceMeasure(RHD2000Thread* b) : Thread(""), data(nullptr), board(b) { // to perform electrode impedance measurements at very low frequencies. const int maxNumBlocks = 120; @@ -1752,7 +1764,40 @@ RHDImpedanceMeasure::RHDImpedanceMeasure(RHD2000Thread* b, ImpedanceData* d) : T allocateDoubleArray3D(amplifierPreFilter, numStreams, 32, SAMPLES_PER_DATA_BLOCK * maxNumBlocks); } +RHDImpedanceMeasure::~RHDImpedanceMeasure() +{ + stopThreadSafely(); +} +void RHDImpedanceMeasure::stopThreadSafely() +{ + if (isThreadRunning()) + { + sendActionMessage("Impedance measure in progress. Stopping it."); + if (!stopThread(3000)) //wait three seconds max for it to exit gracefully + { + std::cerr << "ERROR: Impedance measurement thread did not exit. Force killed it. This might led to crashes." << std::endl; + } + } +} + +void RHDImpedanceMeasure::waitSafely() +{ + if (!waitForThreadToExit(120000)) //two minutes should be enough for completing a scan + { + sendActionMessage("Impedance measurement took too much. Aborting."); + if (!stopThread(3000)) //wait three seconds max for it to exit gracefully + { + std::cerr << "ERROR: Impedance measurement thread did not exit. Force killed it. This might led to crashes." << std::endl; + } + } +} + +void RHDImpedanceMeasure::prepareData(ImpedanceData* d) +{ + addActionListener(board->sn->getMessageCenter()); + data = d; +} // Update electrode impedance measurement frequency, after checking that @@ -1955,6 +2000,20 @@ void RHDImpedanceMeasure::empiricalResistanceCorrection(double& impedanceMagnitu } void RHDImpedanceMeasure::run() +{ + RHD2000Editor* ed; + ed = (RHD2000Editor*)board->sn->editor.get(); + if (data == nullptr) + return; + runImpedanceMeasurement(); + restoreFPGA(); + ed->triggerAsyncUpdate(); + data = nullptr; +} + +#define CHECK_EXIT if (threadShouldExit()) return + +void RHDImpedanceMeasure::runImpedanceMeasurement() { int commandSequenceLength, stream, channel, capRange; double cSeries; @@ -1967,7 +2026,7 @@ void RHDImpedanceMeasure::run() Array<int> enabledStreams; for (stream = 0; stream < MAX_NUM_DATA_STREAMS; ++stream) { - + CHECK_EXIT; if (board->evalBoard->isStreamEnabled(stream)) { enabledStreams.add(stream); @@ -1987,6 +2046,7 @@ void RHDImpedanceMeasure::run() } // Create a command list for the AuxCmd1 slot. commandSequenceLength = board->chipRegisters.createCommandListZcheckDac(commandList, actualImpedanceFreq, 128.0); + CHECK_EXIT; board->evalBoard->uploadCommandList(commandList, Rhd2000EvalBoard::AuxCmd1, 1); board->evalBoard->selectAuxCommandLength(Rhd2000EvalBoard::AuxCmd1, 0, commandSequenceLength - 1); @@ -1994,7 +2054,7 @@ void RHDImpedanceMeasure::run() { board->evalBoard->enableExternalFastSettle(false); } - + CHECK_EXIT; board->evalBoard->selectAuxCommandBank(Rhd2000EvalBoard::PortA, Rhd2000EvalBoard::AuxCmd1, 1); board->evalBoard->selectAuxCommandBank(Rhd2000EvalBoard::PortB, @@ -2011,12 +2071,14 @@ void RHDImpedanceMeasure::run() int numBlocks = ceil((numPeriods + 2.0) * period / 60.0); // + 2 periods to give time to settle initially if (numBlocks < 2) numBlocks = 2; // need first block for command to switch channels to take effect. + CHECK_EXIT; board->actualDspCutoffFreq = board->chipRegisters.setDspCutoffFreq(board->desiredDspCutoffFreq); board->actualLowerBandwidth = board->chipRegisters.setLowerBandwidth(board->desiredLowerBandwidth); board->actualUpperBandwidth = board->chipRegisters.setUpperBandwidth(board->desiredUpperBandwidth); board->chipRegisters.enableDsp(board->dspEnabled); board->chipRegisters.enableZcheck(true); commandSequenceLength = board->chipRegisters.createCommandListRegisterConfig(commandList, false); + CHECK_EXIT; // Upload version with no ADC calibration to AuxCmd3 RAM Bank 1. board->evalBoard->uploadCommandList(commandList, Rhd2000EvalBoard::AuxCmd3, 3); board->evalBoard->selectAuxCommandLength(Rhd2000EvalBoard::AuxCmd3, 0, commandSequenceLength - 1); @@ -2025,6 +2087,7 @@ void RHDImpedanceMeasure::run() board->evalBoard->selectAuxCommandBank(Rhd2000EvalBoard::PortC, Rhd2000EvalBoard::AuxCmd3, 3); board->evalBoard->selectAuxCommandBank(Rhd2000EvalBoard::PortD, Rhd2000EvalBoard::AuxCmd3, 3); + CHECK_EXIT; board->evalBoard->setContinuousRunMode(false); board->evalBoard->setMaxTimeStep(SAMPLES_PER_DATA_BLOCK * numBlocks); @@ -2066,7 +2129,6 @@ void RHDImpedanceMeasure::run() for (capRange = 0; capRange < 3; ++capRange) { - switch (capRange) { case 0: @@ -2089,6 +2151,7 @@ void RHDImpedanceMeasure::run() // Check all 32 channels across all active data streams. for (channel = 0; channel < 32; ++channel) { + CHECK_EXIT; cout << "running impedance on channel " << channel << endl; board->chipRegisters.setZcheckChannel(channel); @@ -2119,6 +2182,7 @@ void RHDImpedanceMeasure::run() // and repeat the previous steps. if (rhd2164ChipPresent) { + CHECK_EXIT; board->chipRegisters.setZcheckChannel(channel + 32); // address channels 32-63 commandSequenceLength = board->chipRegisters.createCommandListRegisterConfig(commandList, false); @@ -2214,7 +2278,12 @@ void RHDImpedanceMeasure::run() } } } + data->valid = true; + +} +void RHDImpedanceMeasure::restoreFPGA() +{ board->evalBoard->setContinuousRunMode(false); board->evalBoard->setMaxTimeStep(0); board->evalBoard->flush(); diff --git a/Source/Processors/DataThreads/RHD2000Thread.h b/Source/Processors/DataThreads/RHD2000Thread.h index 3cba97cd8e94ec4a2c066628e119526a8df74c99..ad02e086a6f9295ae6ff3251c3f2390e7d011f6a 100644 --- a/Source/Processors/DataThreads/RHD2000Thread.h +++ b/Source/Processors/DataThreads/RHD2000Thread.h @@ -52,6 +52,7 @@ struct ImpedanceData Array<int> channels; Array<float> magnitudes; Array<float> phases; + bool valid; }; /** @@ -123,6 +124,8 @@ public: /*Gets the headstage relative channel index from the absolute channel index*/ int getHeadstageChannel(int& hs, int ch); + void runImpedanceTest(ImpedanceData* data); + private: bool enableHeadstage(int hsNum, bool enabled, int nStr = 1, int strChans = 32); @@ -196,6 +199,7 @@ private: int numberingScheme ; Array<float> adcBitVolts; bool newScan; + ScopedPointer<RHDImpedanceMeasure> impedanceThread; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RHD2000Thread); }; @@ -221,12 +225,18 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RHDHeadstage); }; -class RHDImpedanceMeasure : public Thread +class RHDImpedanceMeasure : public Thread, public ActionBroadcaster { public: - RHDImpedanceMeasure(RHD2000Thread* b, ImpedanceData* d); + RHDImpedanceMeasure(RHD2000Thread* b); + ~RHDImpedanceMeasure(); + void prepareData(ImpedanceData* d); + void stopThreadSafely(); + void waitSafely(); void run(); private: + void runImpedanceMeasurement(); + void restoreFPGA(); void measureComplexAmplitude(std::vector<std::vector<std::vector<double>>>& measuredMagnitude, std::vector<std::vector<std::vector<double>>>& measuredPhase, int capIndex, int stream, int chipChannel, int numBlocks, @@ -246,6 +256,8 @@ private: ImpedanceData* data; RHD2000Thread* board; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RHDImpedanceMeasure); }; #endif // __RHD2000THREAD_H_2C4CBD67__