From 5a5f2b1697dddfe4d169f3ff83c1ff3746b6ed50 Mon Sep 17 00:00:00 2001 From: Aaron Cuevas Lopez <aacuelo@teleco.upv.es> Date: Fri, 23 Jan 2015 03:31:30 +0100 Subject: [PATCH] Add proper support for RHD2164 --- .../Processors/DataThreads/RHD2000Editor.cpp | 8 +- .../Processors/DataThreads/RHD2000Thread.cpp | 315 +++++++++++------- Source/Processors/DataThreads/RHD2000Thread.h | 39 ++- 3 files changed, 235 insertions(+), 127 deletions(-) diff --git a/Source/Processors/DataThreads/RHD2000Editor.cpp b/Source/Processors/DataThreads/RHD2000Editor.cpp index b2c3fea9d..8769ec79e 100644 --- a/Source/Processors/DataThreads/RHD2000Editor.cpp +++ b/Source/Processors/DataThreads/RHD2000Editor.cpp @@ -1199,7 +1199,7 @@ void HeadstageOptionsInterface::checkEnabledState() if (board->isHeadstageEnabled(hsNumber1)) { - channelsOnHs1 = 32; + channelsOnHs1 = board->getChannelsInHeadstage(hsNumber1); hsButton1->setLabel(String(channelsOnHs1)); hsButton1->setEnabledState(true); } @@ -1212,7 +1212,7 @@ void HeadstageOptionsInterface::checkEnabledState() if (board->isHeadstageEnabled(hsNumber2)) { - channelsOnHs2 = 32; + channelsOnHs2 = board->getChannelsInHeadstage(hsNumber2); hsButton2->setLabel(String(channelsOnHs2)); hsButton2->setEnabledState(true); } @@ -1230,7 +1230,7 @@ void HeadstageOptionsInterface::checkEnabledState() void HeadstageOptionsInterface::buttonClicked(Button* button) { - if (!(editor->acquisitionIsActive) && board->foundInputSource()) + /* if (!(editor->acquisitionIsActive) && board->foundInputSource()) { //std::cout << "Acquisition is not active" << std::endl; @@ -1265,7 +1265,7 @@ void HeadstageOptionsInterface::buttonClicked(Button* button) editor->getEditorViewport()->makeEditorVisible(editor, false, true); - } + }*/ } diff --git a/Source/Processors/DataThreads/RHD2000Thread.cpp b/Source/Processors/DataThreads/RHD2000Thread.cpp index 8ebb1cc23..de8fe09d4 100644 --- a/Source/Processors/DataThreads/RHD2000Thread.cpp +++ b/Source/Processors/DataThreads/RHD2000Thread.cpp @@ -81,6 +81,10 @@ RHD2000Thread::RHD2000Thread(SourceNode* sn) : DataThread(sn), cableLengthPortA(0.914f), cableLengthPortB(0.914f), cableLengthPortC(0.914f), cableLengthPortD(0.914f), // default is 3 feet (0.914 m), audioOutputL(-1), audioOutputR(-1) ,numberingScheme(1) { + + for (int i=0; i < MAX_NUM_HEADSTAGES; i++) + headstagesArray.add(new RHDHeadstage(static_cast<Rhd2000EvalBoard::BoardDataSource>(i))); + evalBoard = new Rhd2000EvalBoard; dataBlock = new Rhd2000DataBlock(1); dataBuffer = new DataBuffer(2, 10000); // start with 2 channels and automatically resize @@ -408,9 +412,13 @@ void RHD2000Thread::scanPorts() { return; } + + //Clear previous known streams + enabledStreams.clear(); + // Scan SPI ports - int delay, stream, id; + int delay, hs, id; int register59Value; //int numChannelsOnPort[4] = {0, 0, 0, 0}; Rhd2000EvalBoard::BoardDataSource initStreamPorts[8] = @@ -438,6 +446,7 @@ void RHD2000Thread::scanPorts() }; chipId.insertMultiple(0,-1,8); + Array<int> tmpChipId(chipId); setSampleRate(Rhd2000EvalBoard::SampleRate30000Hz, true); // set to 30 kHz temporarily @@ -517,28 +526,28 @@ void RHD2000Thread::scanPorts() // Read the Intan chip ID number from each RHD2000 chip found. // Record delay settings that yield good communication with the chip. - for (stream = 0; stream < MAX_NUM_DATA_STREAMS; ++stream)//MAX_NUM_DATA_STREAMS; ++stream) + for (hs = 0; hs < MAX_NUM_HEADSTAGES; ++hs)//MAX_NUM_DATA_STREAMS; ++stream) { // std::cout << "Stream number " << stream << ", delay = " << delay << std::endl; - id = deviceId(dataBlock, stream, register59Value); + id = deviceId(dataBlock, hs, register59Value); if (id == CHIP_ID_RHD2132 || id == CHIP_ID_RHD2216 || (id == CHIP_ID_RHD2164 && register59Value == REGISTER_59_MISO_A)) { // std::cout << "Device ID found: " << id << std::endl; - sumGoodDelays.set(stream,sumGoodDelays[stream] + 1); + sumGoodDelays.set(hs,sumGoodDelays[hs] + 1); - if (indexFirstGoodDelay[stream] == -1) + if (indexFirstGoodDelay[hs] == -1) { - indexFirstGoodDelay.set(stream, delay); - chipId.set(stream,id); + indexFirstGoodDelay.set(hs, delay); + tmpChipId.set(hs,id); } - else if (indexSecondGoodDelay[stream] == -1) + else if (indexSecondGoodDelay[hs] == -1) { - indexSecondGoodDelay.set(stream,delay); - chipId.set(stream,id); + indexSecondGoodDelay.set(hs,delay); + tmpChipId.set(hs,id); } } } @@ -554,7 +563,7 @@ void RHD2000Thread::scanPorts() #if DEBUG_EMULATE_HEADSTAGES > 0 for (int nd = 0; nd < MAX_NUM_DATA_STREAMS; ++nd) { - if ((nd < DEBUG_EMULATE_HEADSTAGES) &&(chipId[0] > 0)) + if ((nd < DEBUG_EMULATE_HEADSTAGES) &&(tmpChipId[0] > 0)) { evalBoard->setDataSource(nd,initStreamPorts[0]); enableHeadstage(nd,true); @@ -566,29 +575,36 @@ void RHD2000Thread::scanPorts() } #else // Now, disable data streams where we did not find chips present. - for (stream = 0; stream < MAX_NUM_DATA_STREAMS; ++stream) + int chipIdx = 0; + for (hs = 0; hs < MAX_NUM_HEADSTAGES; ++hs) { - if (chipId[stream] > 0) + if ((tmpChipId[hs] > 0) && (enabledStreams.size() < MAX_NUM_DATA_STREAMS)) { + chipId.set(chipIdx++,chipId[hs]); //std::cout << "Enabling headstage on stream " << stream << std::endl; - if (chipId[stream] == CHIP_ID_RHD2164) //RHD2164 + if (tmpChipId[hs] == CHIP_ID_RHD2164) //RHD2164 { - //We just add it like a second headstage, allowing only one RHD2164 per channel - //This would need to change - evalBoard->setDataSource(stream+1,initStreamDdrPorts[stream]); - enableHeadstage(stream,true); - enableHeadstage(stream+1,true); - chipId.set(stream+1,CHIP_ID_RHD2164_B); - stream++; + if (enabledStreams.size() < MAX_NUM_DATA_STREAMS - 1) + { + enableHeadstage(hs,true,2,32); + chipId.set(chipIdx++,CHIP_ID_RHD2164_B); + } + else //just one stream left + { + enableHeadstage(hs,true,1,32); + } } else - enableHeadstage(stream, true); + { + enableHeadstage(hs, true,1,tmpChipId[hs] == 1 ? 32:16); + } } else { - enableHeadstage(stream, false); + enableHeadstage(hs, false); } } + updateBoardStreams(); #endif @@ -600,15 +616,15 @@ void RHD2000Thread::scanPorts() Array<int> optimumDelay; optimumDelay.insertMultiple(0,0,8); - for (stream = 0; stream < MAX_NUM_DATA_STREAMS; ++stream) + for (hs = 0; hs < MAX_NUM_HEADSTAGES; ++hs) { - if (sumGoodDelays[stream] == 1 || sumGoodDelays[stream] == 2) + if (sumGoodDelays[hs] == 1 || sumGoodDelays[hs] == 2) { - optimumDelay.set(stream,indexFirstGoodDelay[stream]); + optimumDelay.set(hs,indexFirstGoodDelay[hs]); } - else if (sumGoodDelays[stream] > 2) + else if (sumGoodDelays[hs] > 2) { - optimumDelay.set(stream,indexSecondGoodDelay[stream]); + optimumDelay.set(hs,indexSecondGoodDelay[hs]); } } @@ -669,11 +685,11 @@ bool RHD2000Thread::isAcquisitionActive() return isTransmitting; } -void RHD2000Thread::setNumChannels(int hsNum, int numChannels) +/*void RHD2000Thread::setNumChannels(int hsNum, int numChannels) { if (numChannelsPerDataStream[hsNum] > 0) numChannelsPerDataStream.set(hsNum, numChannels); -} +}*/ void RHD2000Thread::getEventChannelNames(StringArray& Names) @@ -836,11 +852,11 @@ void RHD2000Thread::setDefaultChannelNamesAndType() stream_prefix.add("D1"); stream_prefix.add("D2"); - for (int i = 0; i < MAX_NUM_DATA_STREAMS; i++) + for (int i = 0; i < MAX_NUM_HEADSTAGES; i++) { - if (numChannelsPerDataStream[i] > 0) + if (headstagesArray[i]->isPlugged()) { - for (int k = 0; k < numChannelsPerDataStream[i]; k++) + for (int k = 0; k < headstagesArray[i]->getNumChannels(); k++) { type.add(HEADSTAGE_CHANNEL); @@ -867,16 +883,16 @@ void RHD2000Thread::setDefaultChannelNamesAndType() } } //Aux channels - for (int i = 0; i < MAX_NUM_DATA_STREAMS; i++) + for (int i = 0; i < MAX_NUM_HEADSTAGES; i++) { - if (numChannelsPerDataStream[i] > 0) + if (headstagesArray[i]->isPlugged()) { for (int k = 0; k < 3; k++) { type.add(AUX_CHANNEL); - if (channelModified(AUX_CHANNEL,i,numChannelsPerDataStream[i]+k, oldName,oldGain, dummy)) + if (channelModified(AUX_CHANNEL,i,headstagesArray[i]->getNumChannels()+k, oldName,oldGain, dummy)) { Names.add(oldName); gains.add(oldGain); @@ -895,7 +911,7 @@ void RHD2000Thread::setDefaultChannelNamesAndType() } stream.add(i); - originalChannelNumber.add(numChannelsPerDataStream[i]+k); + originalChannelNumber.add(headstagesArray[i]->getNumChannels()+k); } } } @@ -908,7 +924,7 @@ void RHD2000Thread::setDefaultChannelNamesAndType() channelNumber++; type.add(ADC_CHANNEL); - if (channelModified(ADC_CHANNEL,MAX_NUM_DATA_STREAMS,k, oldName,oldGain,dummy)) + if (channelModified(ADC_CHANNEL,MAX_NUM_HEADSTAGES,k, oldName,oldGain,dummy)) { Names.add(oldName); gains.add(oldGain); @@ -919,7 +935,7 @@ void RHD2000Thread::setDefaultChannelNamesAndType() gains.add(getAdcBitVolts(k)); } - stream.add(MAX_NUM_DATA_STREAMS); + stream.add(MAX_NUM_HEADSTAGES); originalChannelNumber.add(k); } @@ -935,12 +951,12 @@ int RHD2000Thread::getNumChannels() int RHD2000Thread::getNumHeadstageOutputs() { numChannels = 0; - for (int i = 0; i < MAX_NUM_DATA_STREAMS; i++) + for (int i = 0; i < MAX_NUM_HEADSTAGES; i++) { - if (numChannelsPerDataStream[i] > 0) + if (headstagesArray[i]->isPlugged()) { - numChannels += numChannelsPerDataStream[i]; + numChannels += headstagesArray[i]->getNumChannels(); } } @@ -954,9 +970,9 @@ int RHD2000Thread::getNumAuxOutputs() { int numAuxOutputs = 0; - for (int i = 0; i < MAX_NUM_DATA_STREAMS; i++) + for (int i = 0; i < MAX_NUM_HEADSTAGES; i++) { - if (numChannelsPerDataStream[i] > 0) + if (headstagesArray[i]->isPlugged() > 0) { numAuxOutputs += 3; } @@ -1089,45 +1105,82 @@ bool RHD2000Thread::foundInputSource() } -bool RHD2000Thread::enableHeadstage(int hsNum, bool enabled) +bool RHD2000Thread::enableHeadstage(int hsNum, bool enabled, int nStr, int strChans) { - evalBoard->enableDataStream(hsNum, enabled); + /* evalBoard->enableDataStream(hsNum, enabled);*/ - if (enabled) - { - numChannelsPerDataStream.set(hsNum, 32); - } - else - { - numChannelsPerDataStream.set(hsNum, 0); - } + if (enabled) + { + headstagesArray[hsNum]->setNumStreams(nStr); + headstagesArray[hsNum]->setChannelsPerStream(strChans); + enabledStreams.add(headstagesArray[hsNum]->getDataStream(0)); + numChannelsPerDataStream.add(strChans); + if (nStr > 1) + { + enabledStreams.add(headstagesArray[hsNum]->getDataStream(1)); + numChannelsPerDataStream.add(strChans); + } + } + else + { + int idx = enabledStreams.indexOf(headstagesArray[hsNum]->getDataStream(0)); + if (idx >= 0) + { + enabledStreams.remove(idx); + numChannelsPerDataStream.remove(idx); + } + if (headstagesArray[hsNum]->getNumStreams() > 1) + { + idx = enabledStreams.indexOf(headstagesArray[hsNum]->getDataStream(1)); + if (idx >= 0) + { + enabledStreams.remove(idx); + numChannelsPerDataStream.remove(idx); + } + } + headstagesArray[hsNum]->setNumStreams(0); + } - std::cout << "Enabled channels: "; + /* + std::cout << "Enabled channels: "; for (int i = 0; i < MAX_NUM_DATA_STREAMS; i++) { std::cout << numChannelsPerDataStream[i] << " "; - } + }*/ - std:: cout << std::endl; + dataBuffer->resize(getNumChannels(), 10000); + return true; +} - dataBuffer->resize(getNumChannels(), 10000); - - return true; +void RHD2000Thread::updateBoardStreams() +{ + for (int i=0; i < MAX_NUM_DATA_STREAMS; i++) + { + if (i < enabledStreams.size()) + { + evalBoard->enableDataStream(i,true); + evalBoard->setDataSource(i,enabledStreams[i]); + } + else + { + evalBoard->enableDataStream(i,false); + } + } } bool RHD2000Thread::isHeadstageEnabled(int hsNum) { - if (numChannelsPerDataStream[hsNum] > 0) - { - return true; - } + return headstagesArray[hsNum]->isPlugged(); - return false; +} +int RHD2000Thread::getChannelsInHeadstage(int hsNum) +{ + return headstagesArray[hsNum]->getNumChannels(); } void RHD2000Thread::assignAudioOut(int dacChannel, int dataChannel) @@ -1497,82 +1550,73 @@ bool RHD2000Thread::updateBuffer() for (int samp = 0; samp < dataBlock->getSamplesPerDataBlock(); samp++) { - int streamNumber = -1; int channel = -1; // do the neural data channels first - for (int dataStream = 0; dataStream < MAX_NUM_DATA_STREAMS; dataStream++) - { - if (numChannelsPerDataStream[dataStream] > 0) - { - streamNumber++; + for (int dataStream = 0; dataStream < enabledStreams.size(); dataStream++) + { - for (int chan = 0; chan < numChannelsPerDataStream[dataStream]; chan++) - { + for (int chan = 0; chan < numChannelsPerDataStream[dataStream]; chan++) + { - // std::cout << "reading sample stream " << streamNumber << " chan " << chan << " sample "<< samp << std::endl; + // std::cout << "reading sample stream " << streamNumber << " chan " << chan << " sample "<< samp << std::endl; - channel++; + channel++; - int value = dataBlock->amplifierData[streamNumber][chan][samp]; + int value = dataBlock->amplifierData[dataStream][chan][samp]; - thisSample[channel] = float(value-32768)*0.195f; - } + thisSample[channel] = float(value-32768)*0.195f; + } - } - } + } - streamNumber = -1; // then do the Intan AUX channels - for (int dataStream = 0; dataStream < MAX_NUM_DATA_STREAMS; dataStream++) + for (int dataStream = 0; dataStream < enabledStreams.size(); dataStream++) { - if (numChannelsPerDataStream[dataStream] > 0) - { - streamNumber++; - if (samp % 4 == 1) // every 4th sample should have auxiliary input data - { + if (samp % 4 == 1) // every 4th sample should have auxiliary input data + { - // std::cout << "reading sample stream " << streamNumber << " aux ADCs " << std::endl; + // std::cout << "reading sample stream " << streamNumber << " aux ADCs " << std::endl; - channel++; - thisSample[channel] = 0.0374 * - float(dataBlock->auxiliaryData[streamNumber][1][samp+0] - 45000.0f) ; - // constant offset keeps the values visible in the LFP Viewer + channel++; + thisSample[channel] = 0.0374 * + float(dataBlock->auxiliaryData[dataStream][1][samp+0] - 45000.0f) ; + // constant offset keeps the values visible in the LFP Viewer - auxBuffer[channel] = thisSample[channel]; + auxBuffer[channel] = thisSample[channel]; - channel++; - thisSample[channel] = 0.0374 * - float(dataBlock->auxiliaryData[streamNumber][1][samp+1] - 45000.0f) ; - // constant offset keeps the values visible in the LFP Viewer + channel++; + thisSample[channel] = 0.0374 * + float(dataBlock->auxiliaryData[dataStream][1][samp+1] - 45000.0f) ; + // constant offset keeps the values visible in the LFP Viewer - auxBuffer[channel] = thisSample[channel]; + auxBuffer[channel] = thisSample[channel]; - channel++; - thisSample[channel] = 0.0374 * - float(dataBlock->auxiliaryData[streamNumber][1][samp+2] - 45000.0f) ; - // constant offset keeps the values visible in the LFP Viewer + channel++; + thisSample[channel] = 0.0374 * + float(dataBlock->auxiliaryData[dataStream][1][samp+2] - 45000.0f) ; + // constant offset keeps the values visible in the LFP Viewer - auxBuffer[channel] = thisSample[channel]; + auxBuffer[channel] = thisSample[channel]; - } - else // repeat last values from buffer - { + } + else // repeat last values from buffer + { - //std::cout << "reading sample stream " << streamNumber << " aux ADCs " << std::endl; + //std::cout << "reading sample stream " << streamNumber << " aux ADCs " << std::endl; + + channel++; + thisSample[channel] = auxBuffer[channel]; + channel++; + thisSample[channel] = auxBuffer[channel]; + channel++; + thisSample[channel] = auxBuffer[channel]; + } - channel++; - thisSample[channel] = auxBuffer[channel]; - channel++; - thisSample[channel] = auxBuffer[channel]; - channel++; - thisSample[channel] = auxBuffer[channel]; - } - } } @@ -2130,3 +2174,44 @@ void RHD2000Thread::runImpedanceTest(Array<int>& streams, Array<int>& channels, evalBoard->enableExternalFastSettle(true); } } + + +RHDHeadstage::RHDHeadstage(Rhd2000EvalBoard::BoardDataSource stream) : + numStreams(0), channelsPerStream(32), dataStream(stream) +{ +} + +RHDHeadstage::~RHDHeadstage() +{ +} + +void RHDHeadstage::setNumStreams(int num) +{ + numStreams = num; +} + +void RHDHeadstage::setChannelsPerStream(int nchan) +{ + channelsPerStream = nchan; +} + +int RHDHeadstage::getNumChannels() +{ + return channelsPerStream*numStreams; +} + +int RHDHeadstage::getNumStreams() +{ + return numStreams; +} + +Rhd2000EvalBoard::BoardDataSource RHDHeadstage::getDataStream(int index) +{ + if (index < 0 || index > 1) index = 0; + return static_cast<Rhd2000EvalBoard::BoardDataSource>(dataStream+MAX_NUM_HEADSTAGES*index); +} + +bool RHDHeadstage::isPlugged() +{ + return (numStreams > 0); +} \ No newline at end of file diff --git a/Source/Processors/DataThreads/RHD2000Thread.h b/Source/Processors/DataThreads/RHD2000Thread.h index 6eb6c031d..70a5edc96 100644 --- a/Source/Processors/DataThreads/RHD2000Thread.h +++ b/Source/Processors/DataThreads/RHD2000Thread.h @@ -40,9 +40,10 @@ #include "../GenericProcessor/GenericProcessor.h" #define MAX_NUM_DATA_STREAMS 8 +#define MAX_NUM_HEADSTAGES 8 class SourceNode; - +class RHDHeadstage; /** Communicates with the RHD2000 Evaluation Board from Intan Technologies @@ -68,12 +69,8 @@ public: float getBitVolts(Channel* chan); float getAdcBitVolts(int channelNum); - // for internal use: - bool isHeadstageEnabled(int hsNum); - - bool enableHeadstage(int hsNum, bool enabled); - void setCableLength(int hsNum, float length); - void setNumChannels(int hsNum, int nChannels); + bool isHeadstageEnabled(int hsNum); + int getChannelsInHeadstage(int hsNum); void setSampleRate(int index, bool temporary = false); @@ -122,6 +119,12 @@ public: String getChannelName(ChannelType t, int str, int ch); private: + + bool enableHeadstage(int hsNum, bool enabled, int nStr = 1, int strChans = 32); + void updateBoardStreams(); + void setCableLength(int hsNum, float length); + // void setNumChannels(int hsNum, int nChannels); + void setDefaultChannelNamesAndType(); bool channelModified(ChannelType t, int str, int k, String &oldName, float &oldGain, int &index); @@ -130,7 +133,6 @@ private: Rhd2000DataBlock* dataBlock; std::vector<std::vector<std::vector<double>>> amplifierPreFilter; - Array<int> numChannelsPerDataStream; void factorOutParallelCapacitance(double &impedanceMagnitude, double &impedancePhase, double frequency, double parasiticCapacitance); void empiricalResistanceCorrection(double &impedanceMagnitude, double &impedancePhase, @@ -188,6 +190,9 @@ private: float *dacThresholds; bool *dacChannelsToUpdate; Array<int> chipId; + OwnedArray<RHDHeadstage> headstagesArray; + Array<Rhd2000EvalBoard::BoardDataSource> enabledStreams; + Array<int> numChannelsPerDataStream; // used for data stream names... int numberingScheme ; @@ -203,4 +208,22 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RHD2000Thread); }; +class RHDHeadstage +{ +public: + RHDHeadstage(Rhd2000EvalBoard::BoardDataSource stream); + ~RHDHeadstage(); + void setNumStreams(int num); + void setChannelsPerStream(int nchan); + int getNumChannels(); + int getNumStreams(); + Rhd2000EvalBoard::BoardDataSource getDataStream(int index); + bool isPlugged(); +private: + Rhd2000EvalBoard::BoardDataSource dataStream; + int numStreams; + int channelsPerStream; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RHDHeadstage); +}; + #endif // __RHD2000THREAD_H_2C4CBD67__ -- GitLab