diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..43cdbfe --- /dev/null +++ b/.clang-format @@ -0,0 +1,60 @@ +# Note: Uses clang-format v17 +--- +Language: Cpp +# BasedOnStyle: JUCE (Custom) +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Allman +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList : BeforeColon +BreakStringLiterals: false +ColumnLimit: 0 +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtEOF: true +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: Inner +PackConstructorInitializers: CurrentLine +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: NonEmptyParentheses +SpaceInEmptyParentheses: false +SpacesInAngles: Never +SpacesInParens: Never +SpacesInContainerLiterals: true +Standard: "c++17" +TabWidth: 4 +UseTab: Never \ No newline at end of file diff --git a/Source/Devices/AnalogIO.cpp b/Source/Devices/AnalogIO.cpp index c59d7e0..5c0cc0d 100644 --- a/Source/Devices/AnalogIO.cpp +++ b/Source/Devices/AnalogIO.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,235 +24,238 @@ using namespace OnixSourcePlugin; -AnalogIO::AnalogIO(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) - : OnixDevice(name, hubName, AnalogIO::getDeviceType(), deviceIdx_, oni_ctx) +AnalogIO::AnalogIO (std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) + : OnixDevice (name, hubName, AnalogIO::getDeviceType(), deviceIdx_, oni_ctx) { - StreamInfo analogInputStream = StreamInfo( - OnixDevice::createStreamName({ getHubName(), name, "AnalogInput" }), - "Analog Input data", - getStreamIdentifier(), - numChannels, - getSampleRate(), - "AnalogInput", - ContinuousChannel::Type::ADC, - getVoltsPerDivision(AnalogIOVoltageRange::TenVolts), // NB: +/- 10 Volts - "V", - {}, - { "input" }); - streamInfos.add(analogInputStream); - - for (int i = 0; i < numFrames; i++) - eventCodes[i] = 0; - - for (int i = 0; i < numChannels; i++) - { - channelDirection[i] = AnalogIODirection::Input; - channelVoltageRange[i] = AnalogIOVoltageRange::TenVolts; - } - - dataType = AnalogIODataType::Volts; + StreamInfo analogInputStream = StreamInfo ( + OnixDevice::createStreamName ({ getHubName(), name, "AnalogInput" }), + "Analog Input data", + getStreamIdentifier(), + numChannels, + getSampleRate(), + "AnalogInput", + ContinuousChannel::Type::ADC, + getVoltsPerDivision (AnalogIOVoltageRange::TenVolts), // NB: +/- 10 Volts + "V", + {}, + { "input" }); + streamInfos.add (analogInputStream); + + for (int i = 0; i < numFrames; i++) + eventCodes[i] = 0; + + for (int i = 0; i < numChannels; i++) + { + channelDirection[i] = AnalogIODirection::Input; + channelVoltageRange[i] = AnalogIOVoltageRange::TenVolts; + } + + dataType = AnalogIODataType::Volts; } int AnalogIO::getSampleRate() { - return std::floor(AnalogIOFrequencyHz / framesToAverage); + return std::floor (AnalogIOFrequencyHz / framesToAverage); } OnixDeviceType AnalogIO::getDeviceType() { - return OnixDeviceType::ANALOGIO; + return OnixDeviceType::ANALOGIO; } int AnalogIO::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + throw error_str ("Device context is not initialized properly for " + getName()); - return deviceContext->writeRegister(deviceIdx, (uint32_t)AnalogIORegisters::ENABLE, (oni_reg_val_t)(isEnabled() ? 1 : 0)); + return deviceContext->writeRegister (deviceIdx, (uint32_t) AnalogIORegisters::ENABLE, (oni_reg_val_t) (isEnabled() ? 1 : 0)); } bool AnalogIO::updateSettings() { - int rc = 0; + int rc = 0; - for (int i = 0; i < numChannels; i++) - { - rc = deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)AnalogIORegisters::CH00_IN_RANGE + i, (oni_reg_val_t)channelVoltageRange[i]); - if (rc != ONI_ESUCCESS) return false; - } + for (int i = 0; i < numChannels; i++) + { + rc = deviceContext->writeRegister (deviceIdx, (oni_reg_addr_t) AnalogIORegisters::CH00_IN_RANGE + i, (oni_reg_val_t) channelVoltageRange[i]); + if (rc != ONI_ESUCCESS) + return false; + } - uint32_t ioReg = 0; + uint32_t ioReg = 0; - for (int i = 0; i < numChannels; i++) - { - ioReg = (ioReg & ~((uint32_t)1 << i)) | ((uint32_t)(channelDirection[i]) << i); - } + for (int i = 0; i < numChannels; i++) + { + ioReg = (ioReg & ~((uint32_t) 1 << i)) | ((uint32_t) (channelDirection[i]) << i); + } - rc = deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)AnalogIORegisters::CHDIR, ioReg); - if (rc != ONI_ESUCCESS) return false; + rc = deviceContext->writeRegister (deviceIdx, (oni_reg_addr_t) AnalogIORegisters::CHDIR, ioReg); + if (rc != ONI_ESUCCESS) + return false; - for (int i = 0; i < numChannels; i++) - { - voltsPerDivision[i] = getVoltsPerDivision(channelVoltageRange[i]); - } + for (int i = 0; i < numChannels; i++) + { + voltsPerDivision[i] = getVoltsPerDivision (channelVoltageRange[i]); + } - return true; + return true; } -float AnalogIO::getVoltsPerDivision(AnalogIOVoltageRange voltageRange) +float AnalogIO::getVoltsPerDivision (AnalogIOVoltageRange voltageRange) { - switch (voltageRange) - { - case AnalogIOVoltageRange::TenVolts: - return 20.0f / numberOfDivisions; - case AnalogIOVoltageRange::TwoPointFiveVolts: - return 5.0f / numberOfDivisions; - case AnalogIOVoltageRange::FiveVolts: - return 10.0f / numberOfDivisions; - default: - return 0.0f; - } + switch (voltageRange) + { + case AnalogIOVoltageRange::TenVolts: + return 20.0f / numberOfDivisions; + case AnalogIOVoltageRange::TwoPointFiveVolts: + return 5.0f / numberOfDivisions; + case AnalogIOVoltageRange::FiveVolts: + return 10.0f / numberOfDivisions; + default: + return 0.0f; + } } -AnalogIODirection AnalogIO::getChannelDirection(int channelNumber) +AnalogIODirection AnalogIO::getChannelDirection (int channelNumber) { - if (channelNumber > numChannels || channelNumber < 0) - { - LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); - return AnalogIODirection::Input; - } + if (channelNumber > numChannels || channelNumber < 0) + { + LOGE ("Channel number must be between 0 and " + std::to_string (channelNumber)); + return AnalogIODirection::Input; + } - return channelDirection[channelNumber]; + return channelDirection[channelNumber]; } -std::string AnalogIO::getChannelDirection(AnalogIODirection direction) +std::string AnalogIO::getChannelDirection (AnalogIODirection direction) { - switch (direction) - { - case AnalogIODirection::Input: - return "Input"; - case AnalogIODirection::Output: - return "Output"; - default: - return ""; - } + switch (direction) + { + case AnalogIODirection::Input: + return "Input"; + case AnalogIODirection::Output: + return "Output"; + default: + return ""; + } } -void AnalogIO::setChannelDirection(int channelNumber, AnalogIODirection direction) +void AnalogIO::setChannelDirection (int channelNumber, AnalogIODirection direction) { - if (channelNumber > numChannels || channelNumber < 0) - { - LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); - return; - } + if (channelNumber > numChannels || channelNumber < 0) + { + LOGE ("Channel number must be between 0 and " + std::to_string (channelNumber)); + return; + } - channelDirection[channelNumber] = direction; + channelDirection[channelNumber] = direction; } -AnalogIOVoltageRange AnalogIO::getChannelVoltageRange(int channelNumber) +AnalogIOVoltageRange AnalogIO::getChannelVoltageRange (int channelNumber) { - if (channelNumber > numChannels || channelNumber < 0) - { - LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); - return AnalogIOVoltageRange::FiveVolts; - } + if (channelNumber > numChannels || channelNumber < 0) + { + LOGE ("Channel number must be between 0 and " + std::to_string (channelNumber)); + return AnalogIOVoltageRange::FiveVolts; + } - return channelVoltageRange[channelNumber]; + return channelVoltageRange[channelNumber]; } -void AnalogIO::setChannelVoltageRange(int channelNumber, AnalogIOVoltageRange direction) +void AnalogIO::setChannelVoltageRange (int channelNumber, AnalogIOVoltageRange direction) { - if (channelNumber > numChannels || channelNumber < 0) - { - LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); - return; - } + if (channelNumber > numChannels || channelNumber < 0) + { + LOGE ("Channel number must be between 0 and " + std::to_string (channelNumber)); + return; + } - channelVoltageRange[channelNumber] = direction; + channelVoltageRange[channelNumber] = direction; } AnalogIODataType AnalogIO::getDataType() const { - return dataType; + return dataType; } -void AnalogIO::setDataType(AnalogIODataType type) +void AnalogIO::setDataType (AnalogIODataType type) { - dataType = type; + dataType = type; } void AnalogIO::startAcquisition() { - currentFrame = 0; - currentAverageFrame = 0; - sampleNumber = 0; + currentFrame = 0; + currentAverageFrame = 0; + sampleNumber = 0; - analogInputSamples.fill(0); + analogInputSamples.fill (0); } int AnalogIO::getNumberOfFrames() { - return frameQueue.size_approx(); + return frameQueue.size_approx(); } -void AnalogIO::addSourceBuffers(OwnedArray& sourceBuffers) +void AnalogIO::addSourceBuffers (OwnedArray& sourceBuffers) { - sourceBuffers.add(new DataBuffer(streamInfos.getFirst().getNumChannels(), (int)streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); - analogInputBuffer = sourceBuffers.getLast(); + sourceBuffers.add (new DataBuffer (streamInfos.getFirst().getNumChannels(), (int) streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); + analogInputBuffer = sourceBuffers.getLast(); } -void AnalogIO::processFrame(uint64_t eventWord) +void AnalogIO::processFrame (uint64_t eventWord) { - oni_frame_t* frame; - if (!frameQueue.try_dequeue(frame)) { //NB: This method should never be called unless a frame is sure to be there - jassertfalse; - } + oni_frame_t* frame; + if (! frameQueue.try_dequeue (frame)) + { // NB: This method should never be called unless a frame is sure to be there + jassertfalse; + } - int16_t* dataPtr = (int16_t*)frame->data; + int16_t* dataPtr = (int16_t*) frame->data; - int dataOffset = 4; + int dataOffset = 4; - for (size_t i = 0; i < numChannels; i++) - { - if (dataType == AnalogIODataType::S16) - analogInputSamples[currentFrame + i * numFrames] += *(dataPtr + dataOffset + i); - else - analogInputSamples[currentFrame + i * numFrames] += *(dataPtr + dataOffset + i) * voltsPerDivision[i]; - } + for (size_t i = 0; i < numChannels; i++) + { + if (dataType == AnalogIODataType::S16) + analogInputSamples[currentFrame + i * numFrames] += *(dataPtr + dataOffset + i); + else + analogInputSamples[currentFrame + i * numFrames] += *(dataPtr + dataOffset + i) * voltsPerDivision[i]; + } - currentAverageFrame++; + currentAverageFrame++; - if (currentAverageFrame >= framesToAverage) - { - for (size_t i = 0; i < numChannels; i++) - { - analogInputSamples[currentFrame + i * numFrames] /= framesToAverage; - } + if (currentAverageFrame >= framesToAverage) + { + for (size_t i = 0; i < numChannels; i++) + { + analogInputSamples[currentFrame + i * numFrames] /= framesToAverage; + } - currentAverageFrame = 0; + currentAverageFrame = 0; - timestamps[currentFrame] = deviceContext->convertTimestampToSeconds(frame->time); - sampleNumbers[currentFrame] = sampleNumber++; - eventCodes[currentFrame] = eventWord; + timestamps[currentFrame] = deviceContext->convertTimestampToSeconds (frame->time); + sampleNumbers[currentFrame] = sampleNumber++; + eventCodes[currentFrame] = eventWord; - currentFrame++; - } + currentFrame++; + } - oni_destroy_frame(frame); + oni_destroy_frame (frame); - if (currentFrame >= numFrames) - { - analogInputBuffer->addToBuffer(analogInputSamples.data(), sampleNumbers, timestamps, eventCodes, numFrames); + if (currentFrame >= numFrames) + { + analogInputBuffer->addToBuffer (analogInputSamples.data(), sampleNumbers, timestamps, eventCodes, numFrames); - analogInputSamples.fill(0); - currentFrame = 0; - } + analogInputSamples.fill (0); + currentFrame = 0; + } } void AnalogIO::processFrames() { - while (frameQueue.peek() != nullptr) - { - processFrame(); - } + while (frameQueue.peek() != nullptr) + { + processFrame(); + } } diff --git a/Source/Devices/AnalogIO.h b/Source/Devices/AnalogIO.h index b395a0b..f6bc15b 100644 --- a/Source/Devices/AnalogIO.h +++ b/Source/Devices/AnalogIO.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -26,107 +26,106 @@ namespace OnixSourcePlugin { - enum class AnalogIORegisters : uint32_t - { - ENABLE = 0, - CHDIR = 1, - CH00_IN_RANGE = 2, - CH01_IN_RANGE = 3, - CH02_IN_RANGE = 4, - CH03_IN_RANGE = 5, - CH04_IN_RANGE = 6, - CH05_IN_RANGE = 7, - CH06_IN_RANGE = 8, - CH07_IN_RANGE = 9, - CH08_IN_RANGE = 10, - CH09_IN_RANGE = 11, - CH10_IN_RANGE = 12, - CH11_IN_RANGE = 13, - }; - - enum class AnalogIOVoltageRange : uint32_t - { - TenVolts = 0, - TwoPointFiveVolts = 1, - FiveVolts = 2 - }; - - enum class AnalogIODirection : uint32_t - { - Input = 0, - Output = 1 - }; - - enum class AnalogIODataType : uint32_t - { - S16 = 0, - Volts = 1 - }; - - /* - Configures and streams data from an AnalogIO device on a Breakout Board - */ - class AnalogIO : public OnixDevice - { - public: - AnalogIO(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); - - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; - void processFrames() override; - - void processFrame(uint64_t eventWord = 0); - - AnalogIODirection getChannelDirection(int channelNumber); - static std::string getChannelDirection(AnalogIODirection direction); - void setChannelDirection(int channelNumber, AnalogIODirection direction); - - AnalogIOVoltageRange getChannelVoltageRange(int channelNumber); - void setChannelVoltageRange(int channelNumber, AnalogIOVoltageRange direction); - - AnalogIODataType getDataType() const; - void setDataType(AnalogIODataType type); - - static OnixDeviceType getDeviceType(); - - static int getSampleRate(); - - int getNumberOfFrames(); - - private: - - DataBuffer* analogInputBuffer = nullptr; - - static constexpr int AnalogIOFrequencyHz = 100000; - static constexpr int framesToAverage = 4; // NB: Downsampling from 100 kHz to 25 kHz - - static constexpr int numFrames = 25; - static constexpr int numChannels = 12; - - static constexpr int numberOfDivisions = 1 << 16; - static constexpr int dacMidScale = 1 << 15; - - std::array channelDirection; - std::array channelVoltageRange; - - AnalogIODataType dataType = AnalogIODataType::Volts; - - unsigned short currentFrame = 0; - unsigned short currentAverageFrame = 0; - int sampleNumber = 0; - - std::array analogInputSamples; - - double timestamps[numFrames]; - int64 sampleNumbers[numFrames]; - uint64 eventCodes[numFrames]; - - std::array voltsPerDivision; - - static float getVoltsPerDivision(AnalogIOVoltageRange voltageRange); - - JUCE_LEAK_DETECTOR(AnalogIO); - }; -} +enum class AnalogIORegisters : uint32_t +{ + ENABLE = 0, + CHDIR = 1, + CH00_IN_RANGE = 2, + CH01_IN_RANGE = 3, + CH02_IN_RANGE = 4, + CH03_IN_RANGE = 5, + CH04_IN_RANGE = 6, + CH05_IN_RANGE = 7, + CH06_IN_RANGE = 8, + CH07_IN_RANGE = 9, + CH08_IN_RANGE = 10, + CH09_IN_RANGE = 11, + CH10_IN_RANGE = 12, + CH11_IN_RANGE = 13, +}; + +enum class AnalogIOVoltageRange : uint32_t +{ + TenVolts = 0, + TwoPointFiveVolts = 1, + FiveVolts = 2 +}; + +enum class AnalogIODirection : uint32_t +{ + Input = 0, + Output = 1 +}; + +enum class AnalogIODataType : uint32_t +{ + S16 = 0, + Volts = 1 +}; + +/* + Configures and streams data from an AnalogIO device on a Breakout Board +*/ +class AnalogIO : public OnixDevice +{ +public: + AnalogIO (std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); + + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void addSourceBuffers (OwnedArray& sourceBuffers) override; + void processFrames() override; + + void processFrame (uint64_t eventWord = 0); + + AnalogIODirection getChannelDirection (int channelNumber); + static std::string getChannelDirection (AnalogIODirection direction); + void setChannelDirection (int channelNumber, AnalogIODirection direction); + + AnalogIOVoltageRange getChannelVoltageRange (int channelNumber); + void setChannelVoltageRange (int channelNumber, AnalogIOVoltageRange direction); + + AnalogIODataType getDataType() const; + void setDataType (AnalogIODataType type); + + static OnixDeviceType getDeviceType(); + + static int getSampleRate(); + + int getNumberOfFrames(); + +private: + DataBuffer* analogInputBuffer = nullptr; + + static constexpr int AnalogIOFrequencyHz = 100000; + static constexpr int framesToAverage = 4; // NB: Downsampling from 100 kHz to 25 kHz + + static constexpr int numFrames = 25; + static constexpr int numChannels = 12; + + static constexpr int numberOfDivisions = 1 << 16; + static constexpr int dacMidScale = 1 << 15; + + std::array channelDirection; + std::array channelVoltageRange; + + AnalogIODataType dataType = AnalogIODataType::Volts; + + unsigned short currentFrame = 0; + unsigned short currentAverageFrame = 0; + int sampleNumber = 0; + + std::array analogInputSamples; + + double timestamps[numFrames]; + int64 sampleNumbers[numFrames]; + uint64 eventCodes[numFrames]; + + std::array voltsPerDivision; + + static float getVoltsPerDivision (AnalogIOVoltageRange voltageRange); + + JUCE_LEAK_DETECTOR (AnalogIO); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/Bno055.cpp b/Source/Devices/Bno055.cpp index 21fac20..14547ef 100644 --- a/Source/Devices/Bno055.cpp +++ b/Source/Devices/Bno055.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,209 +24,203 @@ using namespace OnixSourcePlugin; -Bno055::Bno055(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx) - : OnixDevice(name, hubName, Bno055::getDeviceType(), deviceIdx_, ctx) +Bno055::Bno055 (std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx) + : OnixDevice (name, hubName, Bno055::getDeviceType(), deviceIdx_, ctx) { - auto streamIdentifier = getStreamIdentifier(); - - std::string port = getPortName(deviceIdx); - StreamInfo eulerAngleStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Euler" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Euler angle", - streamIdentifier, - 3, - sampleRate, - "Eul", - ContinuousChannel::Type::AUX, - eulerAngleScale, - "Degrees", - { "Y", "R", "P" }, - "euler", - { "y", "r", "p" } - ); - streamInfos.add(eulerAngleStream); - - StreamInfo quaternionStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Quaternion" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Quaternion", - streamIdentifier, - 4, - sampleRate, - "Quat", - ContinuousChannel::Type::AUX, - quaternionScale, - "u", // NB: Quaternion data is unitless by definition - { "W", "X", "Y", "Z" }, - "quaternion", - { "w", "x", "y", "z" } - ); - streamInfos.add(quaternionStream); - - StreamInfo accelerationStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Acceleration" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Acceleration", - streamIdentifier, - 3, - sampleRate, - "Acc", - ContinuousChannel::Type::AUX, - accelerationScale, - "m/s^2", - { "X", "Y", "Z" }, - "acceleration", - { "x","y","z" } - ); - streamInfos.add(accelerationStream); - - StreamInfo gravityStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Gravity" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Gravity", - streamIdentifier, - 3, - sampleRate, - "Grav", - ContinuousChannel::Type::AUX, - accelerationScale, - "m/s^2", - { "X", "Y", "Z" }, - "gravity", - { "x", "y", "z" } - ); - streamInfos.add(gravityStream); - - StreamInfo temperatureStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Temperature" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Temperature", - streamIdentifier, - 1, - sampleRate, - "Temp", - ContinuousChannel::Type::AUX, - 1.0f, - "Celsius", - { "" }, - "temperature" - ); - streamInfos.add(temperatureStream); - - StreamInfo calibrationStatusStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Calibration" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Calibration status", - streamIdentifier, - 4, - sampleRate, - "Cal", - ContinuousChannel::Type::AUX, - 1.0f, - "", - { "Mag", "Acc", "Gyr", "Sys" }, - "calibration", - { "magnetometer", "acceleration", "gyroscope", "system" } - ); - streamInfos.add(calibrationStatusStream); - - for (int i = 0; i < numFrames; i++) - eventCodes[i] = 0; + auto streamIdentifier = getStreamIdentifier(); + + std::string port = getPortName (deviceIdx); + StreamInfo eulerAngleStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Euler" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Euler angle", + streamIdentifier, + 3, + sampleRate, + "Eul", + ContinuousChannel::Type::AUX, + eulerAngleScale, + "Degrees", + { "Y", "R", "P" }, + "euler", + { "y", "r", "p" }); + streamInfos.add (eulerAngleStream); + + StreamInfo quaternionStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Quaternion" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Quaternion", + streamIdentifier, + 4, + sampleRate, + "Quat", + ContinuousChannel::Type::AUX, + quaternionScale, + "u", // NB: Quaternion data is unitless by definition + { "W", "X", "Y", "Z" }, + "quaternion", + { "w", "x", "y", "z" }); + streamInfos.add (quaternionStream); + + StreamInfo accelerationStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Acceleration" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Acceleration", + streamIdentifier, + 3, + sampleRate, + "Acc", + ContinuousChannel::Type::AUX, + accelerationScale, + "m/s^2", + { "X", "Y", "Z" }, + "acceleration", + { "x", "y", "z" }); + streamInfos.add (accelerationStream); + + StreamInfo gravityStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Gravity" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Gravity", + streamIdentifier, + 3, + sampleRate, + "Grav", + ContinuousChannel::Type::AUX, + accelerationScale, + "m/s^2", + { "X", "Y", "Z" }, + "gravity", + { "x", "y", "z" }); + streamInfos.add (gravityStream); + + StreamInfo temperatureStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Temperature" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Temperature", + streamIdentifier, + 1, + sampleRate, + "Temp", + ContinuousChannel::Type::AUX, + 1.0f, + "Celsius", + { "" }, + "temperature"); + streamInfos.add (temperatureStream); + + StreamInfo calibrationStatusStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Calibration" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Calibration status", + streamIdentifier, + 4, + sampleRate, + "Cal", + ContinuousChannel::Type::AUX, + 1.0f, + "", + { "Mag", "Acc", "Gyr", "Sys" }, + "calibration", + { "magnetometer", "acceleration", "gyroscope", "system" }); + streamInfos.add (calibrationStatusStream); + + for (int i = 0; i < numFrames; i++) + eventCodes[i] = 0; } OnixDeviceType Bno055::getDeviceType() { - return OnixDeviceType::BNO; + return OnixDeviceType::BNO; } int Bno055::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + throw error_str ("Device context is not initialized properly for " + getName()); - return deviceContext->writeRegister(deviceIdx, (uint32_t)Bno055Registers::ENABLE, isEnabled() ? 1 : 0); + return deviceContext->writeRegister (deviceIdx, (uint32_t) Bno055Registers::ENABLE, isEnabled() ? 1 : 0); } bool Bno055::updateSettings() { - return true; + return true; } void Bno055::startAcquisition() { - currentFrame = 0; - sampleNumber = 0; + currentFrame = 0; + sampleNumber = 0; } -void Bno055::addSourceBuffers(OwnedArray& sourceBuffers) +void Bno055::addSourceBuffers (OwnedArray& sourceBuffers) { - sourceBuffers.add(new DataBuffer(numberOfChannels, (int)sampleRate * bufferSizeInSeconds)); - bnoBuffer = sourceBuffers.getLast(); + sourceBuffers.add (new DataBuffer (numberOfChannels, (int) sampleRate * bufferSizeInSeconds)); + bnoBuffer = sourceBuffers.getLast(); } void Bno055::processFrames() { - oni_frame_t* frame; - while (frameQueue.try_dequeue(frame)) - { - int16_t* dataPtr = ((int16_t*)frame->data) + 4; - - bnoTimestamps[currentFrame] = deviceContext->convertTimestampToSeconds(frame->time); - - size_t offset = 0; - - // Euler - for (int i = 0; i < 3; i++) - { - bnoSamples[currentFrame + offset * numFrames] = float(*(dataPtr + offset)) * eulerAngleScale; - offset++; - } - - // Quaternion - for (int i = 0; i < 4; i++) - { - bnoSamples[currentFrame + offset * numFrames] = float(*(dataPtr + offset)) * quaternionScale; - offset++; - } - - // Acceleration - for (int i = 0; i < 3; i++) - { - bnoSamples[currentFrame + offset * numFrames] = float(*(dataPtr + offset)) * accelerationScale; - offset++; - } - - // Gravity - for (int i = 0; i < 3; i++) - { - bnoSamples[currentFrame + offset * numFrames] = float(*(dataPtr + offset)) * accelerationScale; - offset++; - } - - // Temperature - bnoSamples[currentFrame + offset * numFrames] = *((uint8_t*)(dataPtr + offset)); - - // Calibration - auto calibrationStatus = *((uint8_t*)(dataPtr + offset) + 1); - - constexpr uint8_t statusMask = 0b11; - - for (int i = 0; i < 4; i++) - { - bnoSamples[currentFrame + (offset + i + 1) * numFrames] = (calibrationStatus & (statusMask << (2 * i))) >> (2 * i); - } - - oni_destroy_frame(frame); - - sampleNumbers[currentFrame] = sampleNumber++; - - currentFrame++; - - if (currentFrame >= numFrames) - { - shouldAddToBuffer = true; - currentFrame = 0; - } - - if (shouldAddToBuffer) - { - shouldAddToBuffer = false; - bnoBuffer->addToBuffer(bnoSamples.data(), sampleNumbers, bnoTimestamps, eventCodes, numFrames); - } - } + oni_frame_t* frame; + while (frameQueue.try_dequeue (frame)) + { + int16_t* dataPtr = ((int16_t*) frame->data) + 4; + + bnoTimestamps[currentFrame] = deviceContext->convertTimestampToSeconds (frame->time); + + size_t offset = 0; + + // Euler + for (int i = 0; i < 3; i++) + { + bnoSamples[currentFrame + offset * numFrames] = float (*(dataPtr + offset)) * eulerAngleScale; + offset++; + } + + // Quaternion + for (int i = 0; i < 4; i++) + { + bnoSamples[currentFrame + offset * numFrames] = float (*(dataPtr + offset)) * quaternionScale; + offset++; + } + + // Acceleration + for (int i = 0; i < 3; i++) + { + bnoSamples[currentFrame + offset * numFrames] = float (*(dataPtr + offset)) * accelerationScale; + offset++; + } + + // Gravity + for (int i = 0; i < 3; i++) + { + bnoSamples[currentFrame + offset * numFrames] = float (*(dataPtr + offset)) * accelerationScale; + offset++; + } + + // Temperature + bnoSamples[currentFrame + offset * numFrames] = *((uint8_t*) (dataPtr + offset)); + + // Calibration + auto calibrationStatus = *((uint8_t*) (dataPtr + offset) + 1); + + constexpr uint8_t statusMask = 0b11; + + for (int i = 0; i < 4; i++) + { + bnoSamples[currentFrame + (offset + i + 1) * numFrames] = (calibrationStatus & (statusMask << (2 * i))) >> (2 * i); + } + + oni_destroy_frame (frame); + + sampleNumbers[currentFrame] = sampleNumber++; + + currentFrame++; + + if (currentFrame >= numFrames) + { + shouldAddToBuffer = true; + currentFrame = 0; + } + + if (shouldAddToBuffer) + { + shouldAddToBuffer = false; + bnoBuffer->addToBuffer (bnoSamples.data(), sampleNumbers, bnoTimestamps, eventCodes, numFrames); + } + } } diff --git a/Source/Devices/Bno055.h b/Source/Devices/Bno055.h index 8830a3e..0623595 100644 --- a/Source/Devices/Bno055.h +++ b/Source/Devices/Bno055.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -26,53 +26,51 @@ namespace OnixSourcePlugin { - enum class Bno055Registers - { - ENABLE = 0x00 - }; - - /* - Configures and streams data from a BNO055 device - */ - class Bno055 : public OnixDevice - { - public: - - /** Constructor */ - Bno055(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr ctx); +enum class Bno055Registers +{ + ENABLE = 0x00 +}; - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void processFrames() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; +/* + Configures and streams data from a BNO055 device +*/ +class Bno055 : public OnixDevice +{ +public: + /** Constructor */ + Bno055 (std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr ctx); - static OnixDeviceType getDeviceType(); + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void processFrames() override; + void addSourceBuffers (OwnedArray& sourceBuffers) override; - private: + static OnixDeviceType getDeviceType(); - DataBuffer* bnoBuffer; +private: + DataBuffer* bnoBuffer; - const float eulerAngleScale = 1.0f / 16; // 1 degree = 16 LSB - const float quaternionScale = 1.0f / (1 << 14); // 1 = 2^14 LSB - const float accelerationScale = 1.0f / 100; // 1m / s^2 = 100 LSB + const float eulerAngleScale = 1.0f / 16; // 1 degree = 16 LSB + const float quaternionScale = 1.0f / (1 << 14); // 1 = 2^14 LSB + const float accelerationScale = 1.0f / 100; // 1m / s^2 = 100 LSB - static const int numFrames = 2; + static const int numFrames = 2; - bool shouldAddToBuffer = false; + bool shouldAddToBuffer = false; - static const int numberOfChannels = 3 + 3 + 4 + 3 + 1 + 4; - static constexpr float sampleRate = 100.0f; + static const int numberOfChannels = 3 + 3 + 4 + 3 + 1 + 4; + static constexpr float sampleRate = 100.0f; - std::array bnoSamples; + std::array bnoSamples; - double bnoTimestamps[numFrames]; - int64 sampleNumbers[numFrames]; - uint64 eventCodes[numFrames]; + double bnoTimestamps[numFrames]; + int64 sampleNumbers[numFrames]; + uint64 eventCodes[numFrames]; - unsigned short currentFrame = 0; - int sampleNumber = 0; + unsigned short currentFrame = 0; + int sampleNumber = 0; - JUCE_LEAK_DETECTOR(Bno055); - }; -} + JUCE_LEAK_DETECTOR (Bno055); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/DS90UB9x.h b/Source/Devices/DS90UB9x.h index d15e3d3..400f979 100644 --- a/Source/Devices/DS90UB9x.h +++ b/Source/Devices/DS90UB9x.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -26,128 +26,128 @@ namespace OnixSourcePlugin { - static class DS90UB9x - { - public: - // managed registers - static const uint32_t ENABLE = 0x8000; - static const uint32_t READSZ = 0x8001; - static const uint32_t TRIGGER = 0x8002; - static const uint32_t TRIGGEROFF = 0x8003; - static const uint32_t DATAGATE = 0x8004; - static const uint32_t SYNCBITS = 0x8005; - static const uint32_t MARK = 0x8006; - static const uint32_t MAGIC_MASK = 0x8007; - static const uint32_t MAGIC = 0x8008; - static const uint32_t MAGIC_WAIT = 0x8009; - static const uint32_t DATAMODE = 0x800A; - static const uint32_t DATALINES0 = 0x800B; - static const uint32_t DATALINES1 = 0x800C; - - // reserved registers - static const uint32_t GPIO_DIR = 0x8010; - static const uint32_t GPIO_VAL = 0x8011; - static const uint32_t LINKSTATUS = 0x8012; - static const uint32_t LASTI2CL = 0x8013; - static const uint32_t LASTI2CH = 0x8014; - - // unmanaged default serializer / deserializer I2C addresses - static const uint32_t DES_ADDR = 0x30; - static const uint32_t SER_ADDR = 0x58; - - enum class DS90UB9xTriggerMode : uint32_t - { - Continuous = 0, - HsyncEdgePositive = 0b0001, - HsyncEdgeNegative = 0b1001, - HsyncLevelPositive = 0b0101, - HsyncLevelNegative = 0b1101, - VsyncEdgePositive = 0b0011, - VsyncEdgeNegative = 0b1011, - VsyncLevelPositive = 0b0111, - VsyncLevelNegative = 0b1111, - }; - - enum class DS90UB9xDataGate : uint32_t - { - Disabled = 0, - HsyncPositive = 0b001, - HsyncNegative = 0b101, - VsyncPositive = 0b011, - VsyncNegative = 0b111, - }; - - enum class DS90UB9xMarkMode : uint32_t - { - Disabled = 0, - HsyncRising = 0b001, - HsyncFalling = 0b101, - VsyncRising = 0b011, - VsyncFalling = 0b111, - }; - - enum class DS90UB9xDeserializerI2CRegister : uint32_t - { - PortMode = 0x6D, - PortSel = 0x4C, - I2CConfig = 0x58, - GpioCtrl0 = 0x6E, - GpioCtrl1 = 0x6F, - - SerAlias = 0x5C, - - SlaveID1 = 0x5E, - SlaveID2 = 0x5F, - SlaveID3 = 0x60, - SlaveID4 = 0x61, - SlaveID5 = 0x62, - SlaveID6 = 0x63, - SlaveID7 = 0x64, - - SlaveAlias1 = 0x66, - SlaveAlias2 = 0x67, - SlaveAlias3 = 0x68, - SlaveAlias4 = 0x69, - SlaveAlias5 = 0x6A, - SlaveAlias6 = 0x6B, - SlaveAlias7 = 0x6C, - }; - - enum class DS90UB9xSerializerI2CRegister : uint32_t - { - GPIO10 = 0x0D, - GPIO32 = 0x0E, - SCLHIGH = 0x0A, - SCLLOW = 0x0B - }; - - enum class DS90UB933SerializerI2CRegister : uint32_t - { - Gpio10 = 0x0D, - Gpio32 = 0x0E, - SclHigh = 0x11, - SclLow = 0x12 - }; - - enum class DS90UB953SerializerI2CRegister : uint32_t - { - GpioData = 0x0D, - GpioIOControl = 0x0E, - SclHigh = 0x0B, - SclLow = 0x0C - }; - - enum class DS90UB9xMode : uint32_t - { - Raw12BitLowFrequency = 1, - Raw12BitHighFrequency = 2, - Raw10Bit = 3, - }; - - enum class DS90UB9xDirection : uint32_t - { - Input = 0, - Output = 1 - }; - }; -} +static class DS90UB9x +{ +public: + // managed registers + static const uint32_t ENABLE = 0x8000; + static const uint32_t READSZ = 0x8001; + static const uint32_t TRIGGER = 0x8002; + static const uint32_t TRIGGEROFF = 0x8003; + static const uint32_t DATAGATE = 0x8004; + static const uint32_t SYNCBITS = 0x8005; + static const uint32_t MARK = 0x8006; + static const uint32_t MAGIC_MASK = 0x8007; + static const uint32_t MAGIC = 0x8008; + static const uint32_t MAGIC_WAIT = 0x8009; + static const uint32_t DATAMODE = 0x800A; + static const uint32_t DATALINES0 = 0x800B; + static const uint32_t DATALINES1 = 0x800C; + + // reserved registers + static const uint32_t GPIO_DIR = 0x8010; + static const uint32_t GPIO_VAL = 0x8011; + static const uint32_t LINKSTATUS = 0x8012; + static const uint32_t LASTI2CL = 0x8013; + static const uint32_t LASTI2CH = 0x8014; + + // unmanaged default serializer / deserializer I2C addresses + static const uint32_t DES_ADDR = 0x30; + static const uint32_t SER_ADDR = 0x58; + + enum class DS90UB9xTriggerMode : uint32_t + { + Continuous = 0, + HsyncEdgePositive = 0b0001, + HsyncEdgeNegative = 0b1001, + HsyncLevelPositive = 0b0101, + HsyncLevelNegative = 0b1101, + VsyncEdgePositive = 0b0011, + VsyncEdgeNegative = 0b1011, + VsyncLevelPositive = 0b0111, + VsyncLevelNegative = 0b1111, + }; + + enum class DS90UB9xDataGate : uint32_t + { + Disabled = 0, + HsyncPositive = 0b001, + HsyncNegative = 0b101, + VsyncPositive = 0b011, + VsyncNegative = 0b111, + }; + + enum class DS90UB9xMarkMode : uint32_t + { + Disabled = 0, + HsyncRising = 0b001, + HsyncFalling = 0b101, + VsyncRising = 0b011, + VsyncFalling = 0b111, + }; + + enum class DS90UB9xDeserializerI2CRegister : uint32_t + { + PortMode = 0x6D, + PortSel = 0x4C, + I2CConfig = 0x58, + GpioCtrl0 = 0x6E, + GpioCtrl1 = 0x6F, + + SerAlias = 0x5C, + + SlaveID1 = 0x5E, + SlaveID2 = 0x5F, + SlaveID3 = 0x60, + SlaveID4 = 0x61, + SlaveID5 = 0x62, + SlaveID6 = 0x63, + SlaveID7 = 0x64, + + SlaveAlias1 = 0x66, + SlaveAlias2 = 0x67, + SlaveAlias3 = 0x68, + SlaveAlias4 = 0x69, + SlaveAlias5 = 0x6A, + SlaveAlias6 = 0x6B, + SlaveAlias7 = 0x6C, + }; + + enum class DS90UB9xSerializerI2CRegister : uint32_t + { + GPIO10 = 0x0D, + GPIO32 = 0x0E, + SCLHIGH = 0x0A, + SCLLOW = 0x0B + }; + + enum class DS90UB933SerializerI2CRegister : uint32_t + { + Gpio10 = 0x0D, + Gpio32 = 0x0E, + SclHigh = 0x11, + SclLow = 0x12 + }; + + enum class DS90UB953SerializerI2CRegister : uint32_t + { + GpioData = 0x0D, + GpioIOControl = 0x0E, + SclHigh = 0x0B, + SclLow = 0x0C + }; + + enum class DS90UB9xMode : uint32_t + { + Raw12BitLowFrequency = 1, + Raw12BitHighFrequency = 2, + Raw10Bit = 3, + }; + + enum class DS90UB9xDirection : uint32_t + { + Input = 0, + Output = 1 + }; +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/DeviceList.h b/Source/Devices/DeviceList.h index 0cf5851..a99ca82 100644 --- a/Source/Devices/DeviceList.h +++ b/Source/Devices/DeviceList.h @@ -1,29 +1,29 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #include "AnalogIO.h" #include "Bno055.h" -#include "DigitalIO.h" #include "DS90UB9x.h" +#include "DigitalIO.h" #include "HarpSyncInput.h" #include "HeadStageEEPROM.h" #include "MemoryMonitor.h" diff --git a/Source/Devices/DigitalIO.cpp b/Source/Devices/DigitalIO.cpp index 1d6eae9..acd6de9 100644 --- a/Source/Devices/DigitalIO.cpp +++ b/Source/Devices/DigitalIO.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -25,144 +25,144 @@ using namespace OnixSourcePlugin; -DigitalIO::DigitalIO(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) - : OnixDevice(name, hubName, DigitalIO::getDeviceType(), deviceIdx_, oni_ctx) +DigitalIO::DigitalIO (std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) + : OnixDevice (name, hubName, DigitalIO::getDeviceType(), deviceIdx_, oni_ctx) { - StreamInfo digitalInputStream = StreamInfo( - OnixDevice::createStreamName({ getHubName(), name, "DigitalInputs" }), - "Digital Inputs data", - getStreamIdentifier(), - NumDigitalInputs, - AnalogIO::getSampleRate(), - "CH", - ContinuousChannel::Type::AUX, - 1.0, - "u", // NB: Digital data is unitless by definition - {}, - { "input" }); - streamInfos.add(digitalInputStream); - - StreamInfo digitalButtonStream = StreamInfo( - OnixDevice::createStreamName({ getHubName(), name, "DigitalButtons" }), - "Digital Buttons data", - getStreamIdentifier(), - NumButtons, - AnalogIO::getSampleRate(), - "", - ContinuousChannel::Type::AUX, - 1.0, - "u", // NB: Digital data is unitless by definition - { "Moon", "Triangle", "X", "Check", "Circle", "Square" }, - { "input" }); - streamInfos.add(digitalButtonStream); - - eventCodes.fill(0); + StreamInfo digitalInputStream = StreamInfo ( + OnixDevice::createStreamName ({ getHubName(), name, "DigitalInputs" }), + "Digital Inputs data", + getStreamIdentifier(), + NumDigitalInputs, + AnalogIO::getSampleRate(), + "CH", + ContinuousChannel::Type::AUX, + 1.0, + "u", // NB: Digital data is unitless by definition + {}, + { "input" }); + streamInfos.add (digitalInputStream); + + StreamInfo digitalButtonStream = StreamInfo ( + OnixDevice::createStreamName ({ getHubName(), name, "DigitalButtons" }), + "Digital Buttons data", + getStreamIdentifier(), + NumButtons, + AnalogIO::getSampleRate(), + "", + ContinuousChannel::Type::AUX, + 1.0, + "u", // NB: Digital data is unitless by definition + { "Moon", "Triangle", "X", "Check", "Circle", "Square" }, + { "input" }); + streamInfos.add (digitalButtonStream); + + eventCodes.fill (0); } OnixDeviceType DigitalIO::getDeviceType() { - return OnixDeviceType::DIGITALIO; + return OnixDeviceType::DIGITALIO; } int DigitalIO::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); - - int rc = deviceContext->writeRegister(deviceIdx, (uint32_t)DigitalIORegisters::ENABLE, (oni_reg_val_t)(isEnabled() ? 1 : 0)); - if (rc != ONI_ESUCCESS) - throw error_str("Failed to enable the DigitalIO device."); - - oni_reg_val_t baseFreqHz; - rc = deviceContext->readRegister(deviceIdx, (uint32_t)DigitalIORegisters::BASE_FREQ_HZ, &baseFreqHz); - if (rc != ONI_ESUCCESS) - throw error_str("Could not read the base frequency register on the DigitalIO device."); - - // NB: Two states are not accounted for when comparing clock ticks on the hardware, - // therefore the periodTicks variable must be decreased by 2 to get the correct sample rate. - uint32_t periodTicks = (baseFreqHz / (uint32_t)AnalogIO::getSampleRate()) - 2u; - rc = deviceContext->writeRegister(deviceIdx, (uint32_t)DigitalIORegisters::SAMPLE_PERIOD, periodTicks); - if (rc != ONI_ESUCCESS) - throw error_str("Could not write the sample rate for polling to the DigitalIO device."); - - return rc; + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + throw error_str ("Device context is not initialized properly for " + getName()); + + int rc = deviceContext->writeRegister (deviceIdx, (uint32_t) DigitalIORegisters::ENABLE, (oni_reg_val_t) (isEnabled() ? 1 : 0)); + if (rc != ONI_ESUCCESS) + throw error_str ("Failed to enable the DigitalIO device."); + + oni_reg_val_t baseFreqHz; + rc = deviceContext->readRegister (deviceIdx, (uint32_t) DigitalIORegisters::BASE_FREQ_HZ, &baseFreqHz); + if (rc != ONI_ESUCCESS) + throw error_str ("Could not read the base frequency register on the DigitalIO device."); + + // NB: Two states are not accounted for when comparing clock ticks on the hardware, + // therefore the periodTicks variable must be decreased by 2 to get the correct sample rate. + uint32_t periodTicks = (baseFreqHz / (uint32_t) AnalogIO::getSampleRate()) - 2u; + rc = deviceContext->writeRegister (deviceIdx, (uint32_t) DigitalIORegisters::SAMPLE_PERIOD, periodTicks); + if (rc != ONI_ESUCCESS) + throw error_str ("Could not write the sample rate for polling to the DigitalIO device."); + + return rc; } bool DigitalIO::updateSettings() { - return true; + return true; } void DigitalIO::startAcquisition() { - currentFrame = 0; - sampleNumber = 0; + currentFrame = 0; + sampleNumber = 0; - digitalSamples.fill(0); + digitalSamples.fill (0); } -void DigitalIO::addSourceBuffers(OwnedArray& sourceBuffers) +void DigitalIO::addSourceBuffers (OwnedArray& sourceBuffers) { - sourceBuffers.add(new DataBuffer(NumChannels, (int)streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); - digitalBuffer = sourceBuffers.getLast(); + sourceBuffers.add (new DataBuffer (NumChannels, (int) streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); + digitalBuffer = sourceBuffers.getLast(); } -EventChannel::Settings DigitalIO::getEventChannelSettings(DataStream* stream) +EventChannel::Settings DigitalIO::getEventChannelSettings (DataStream* stream) { - EventChannel::Settings settings{ - EventChannel::Type::TTL, - OnixDevice::createStreamName({getHubName(), getName(), "Events"}), - "Digital inputs and breakout button states coming from a DigitalIO device", - getStreamIdentifier() + ".event.digital", - stream, - NumChannels - }; - - return settings; + EventChannel::Settings settings { + EventChannel::Type::TTL, + OnixDevice::createStreamName ({ getHubName(), getName(), "Events" }), + "Digital inputs and breakout button states coming from a DigitalIO device", + getStreamIdentifier() + ".event.digital", + stream, + NumChannels + }; + + return settings; } -float DigitalIO::getChannelState(uint8_t state, int channel) +float DigitalIO::getChannelState (uint8_t state, int channel) { - return (state & (1 << channel)) >> channel; // NB: Return the state of the specified channel + return (state & (1 << channel)) >> channel; // NB: Return the state of the specified channel }; void DigitalIO::processFrames() { - oni_frame_t* frame; - while (frameQueue.try_dequeue(frame)) - { - size_t offset = 0; + oni_frame_t* frame; + while (frameQueue.try_dequeue (frame)) + { + size_t offset = 0; - uint16_t* dataPtr = (uint16_t*)frame->data; + uint16_t* dataPtr = (uint16_t*) frame->data; - timestamps[currentFrame] = deviceContext->convertTimestampToSeconds(frame->time); - sampleNumbers[currentFrame] = sampleNumber++; + timestamps[currentFrame] = deviceContext->convertTimestampToSeconds (frame->time); + sampleNumbers[currentFrame] = sampleNumber++; - constexpr int inputDataOffset = 4; - constexpr int buttonDataOffset = inputDataOffset + 1; + constexpr int inputDataOffset = 4; + constexpr int buttonDataOffset = inputDataOffset + 1; - uint64_t inputState = *(dataPtr + inputDataOffset); - uint64_t buttonState = *(dataPtr + buttonDataOffset); + uint64_t inputState = *(dataPtr + inputDataOffset); + uint64_t buttonState = *(dataPtr + buttonDataOffset); - for (int i = 0; i < NumDigitalInputs; i++) - { - digitalSamples[currentFrame + offset++ * NumFrames] = getChannelState(inputState, i); - } + for (int i = 0; i < NumDigitalInputs; i++) + { + digitalSamples[currentFrame + offset++ * NumFrames] = getChannelState (inputState, i); + } - for (int i = 0; i < NumButtons; i++) - { - digitalSamples[currentFrame + offset++ * NumFrames] = getChannelState(buttonState, i); - } + for (int i = 0; i < NumButtons; i++) + { + digitalSamples[currentFrame + offset++ * NumFrames] = getChannelState (buttonState, i); + } - eventCodes[currentFrame] = (buttonState & 0x3F) << 8 | (inputState & 0xFF); + eventCodes[currentFrame] = (buttonState & 0x3F) << 8 | (inputState & 0xFF); - oni_destroy_frame(frame); + oni_destroy_frame (frame); - if (++currentFrame >= NumFrames) - { - digitalBuffer->addToBuffer(digitalSamples.data(), sampleNumbers.data(), timestamps.data(), eventCodes.data(), NumFrames); + if (++currentFrame >= NumFrames) + { + digitalBuffer->addToBuffer (digitalSamples.data(), sampleNumbers.data(), timestamps.data(), eventCodes.data(), NumFrames); - currentFrame = 0; - } - } + currentFrame = 0; + } + } } diff --git a/Source/Devices/DigitalIO.h b/Source/Devices/DigitalIO.h index 6589f1f..7551261 100644 --- a/Source/Devices/DigitalIO.h +++ b/Source/Devices/DigitalIO.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -26,84 +26,83 @@ namespace OnixSourcePlugin { - enum class DigitalIORegisters : uint32_t - { - ENABLE = 0x0, - BASE_FREQ_HZ = 0x5, - DEAD_TICKS = 0x6, - SAMPLE_PERIOD = 0x7, - }; - - enum class DigitalPortState : uint16_t - { - Pin0 = 0x1, - Pin1 = 0x2, - Pin2 = 0x4, - Pin3 = 0x8, - Pin4 = 0x10, - Pin5 = 0x20, - Pin6 = 0x40, - Pin7 = 0x80 - }; - - enum class BreakoutButtonState : uint16_t - { - None = 0x0, - Moon = 0x1, - Triangle = 0x2, - X = 0x4, - Check = 0x8, - Circle = 0x10, - Square = 0x20, - Reserved0 = 0x40, - Reserved1 = 0x80, - PortDOn = 0x100, - PortCOn = 0x200, - PortBOn = 0x400, - PortAOn = 0x800 - }; - - /* - Configures and streams data from a DigitalIO device on a Breakout Board - */ - class DigitalIO : public OnixDevice - { - public: - DigitalIO(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); - - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; - void processFrames() override; - - EventChannel::Settings getEventChannelSettings(DataStream* stream); - - static OnixDeviceType getDeviceType(); - - private: - - DataBuffer* digitalBuffer = nullptr; - - static float getChannelState(uint8_t state, int channel); - - static constexpr int NumFrames = 25; - - static constexpr int NumDigitalInputs = 8; - static constexpr int NumButtons = 6; - static constexpr int NumChannels = NumDigitalInputs + NumButtons; - - static constexpr int NumSamples = NumFrames * NumChannels; - - unsigned short currentFrame = 0; - int64_t sampleNumber = 0; - - std::array digitalSamples; - - std::array timestamps; - std::array sampleNumbers; - std::array eventCodes; - - JUCE_LEAK_DETECTOR(DigitalIO); - }; -} +enum class DigitalIORegisters : uint32_t +{ + ENABLE = 0x0, + BASE_FREQ_HZ = 0x5, + DEAD_TICKS = 0x6, + SAMPLE_PERIOD = 0x7, +}; + +enum class DigitalPortState : uint16_t +{ + Pin0 = 0x1, + Pin1 = 0x2, + Pin2 = 0x4, + Pin3 = 0x8, + Pin4 = 0x10, + Pin5 = 0x20, + Pin6 = 0x40, + Pin7 = 0x80 +}; + +enum class BreakoutButtonState : uint16_t +{ + None = 0x0, + Moon = 0x1, + Triangle = 0x2, + X = 0x4, + Check = 0x8, + Circle = 0x10, + Square = 0x20, + Reserved0 = 0x40, + Reserved1 = 0x80, + PortDOn = 0x100, + PortCOn = 0x200, + PortBOn = 0x400, + PortAOn = 0x800 +}; + +/* + Configures and streams data from a DigitalIO device on a Breakout Board +*/ +class DigitalIO : public OnixDevice +{ +public: + DigitalIO (std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); + + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void addSourceBuffers (OwnedArray& sourceBuffers) override; + void processFrames() override; + + EventChannel::Settings getEventChannelSettings (DataStream* stream); + + static OnixDeviceType getDeviceType(); + +private: + DataBuffer* digitalBuffer = nullptr; + + static float getChannelState (uint8_t state, int channel); + + static constexpr int NumFrames = 25; + + static constexpr int NumDigitalInputs = 8; + static constexpr int NumButtons = 6; + static constexpr int NumChannels = NumDigitalInputs + NumButtons; + + static constexpr int NumSamples = NumFrames * NumChannels; + + unsigned short currentFrame = 0; + int64_t sampleNumber = 0; + + std::array digitalSamples; + + std::array timestamps; + std::array sampleNumbers; + std::array eventCodes; + + JUCE_LEAK_DETECTOR (DigitalIO); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/HarpSyncInput.cpp b/Source/Devices/HarpSyncInput.cpp index 8aee5cb..9e24cc8 100644 --- a/Source/Devices/HarpSyncInput.cpp +++ b/Source/Devices/HarpSyncInput.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,88 +24,86 @@ using namespace OnixSourcePlugin; -HarpSyncInput::HarpSyncInput(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) - : OnixDevice(name, hubName, HarpSyncInput::getDeviceType(), deviceIdx_, oni_ctx) +HarpSyncInput::HarpSyncInput (std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) + : OnixDevice (name, hubName, HarpSyncInput::getDeviceType(), deviceIdx_, oni_ctx) { - setEnabled(false); - - StreamInfo harpTimeStream = StreamInfo( - OnixDevice::createStreamName({ getHubName(), getName(), "HarpTime" }), - "Harp clock time corresponding to the local acquisition ONIX clock count", - getStreamIdentifier(), - 1, - 1, - "HarpTime", - ContinuousChannel::Type::AUX, - 1.0f, - "s", - { "" }, - "harptime" - ); - streamInfos.add(harpTimeStream); - - for (int i = 0; i < numFrames; i++) - eventCodes[i] = 0; + setEnabled (false); + + StreamInfo harpTimeStream = StreamInfo ( + OnixDevice::createStreamName ({ getHubName(), getName(), "HarpTime" }), + "Harp clock time corresponding to the local acquisition ONIX clock count", + getStreamIdentifier(), + 1, + 1, + "HarpTime", + ContinuousChannel::Type::AUX, + 1.0f, + "s", + { "" }, + "harptime"); + streamInfos.add (harpTimeStream); + + for (int i = 0; i < numFrames; i++) + eventCodes[i] = 0; } OnixDeviceType HarpSyncInput::getDeviceType() { - return OnixDeviceType::HARPSYNCINPUT; + return OnixDeviceType::HARPSYNCINPUT; } int HarpSyncInput::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + throw error_str ("Device context is not initialized properly for " + getName()); - return deviceContext->writeRegister(deviceIdx, (uint32_t)HarpSyncInputRegisters::ENABLE, (oni_reg_val_t)(isEnabled() ? 1 : 0)); + return deviceContext->writeRegister (deviceIdx, (uint32_t) HarpSyncInputRegisters::ENABLE, (oni_reg_val_t) (isEnabled() ? 1 : 0)); } bool HarpSyncInput::updateSettings() { - return deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)HarpSyncInputRegisters::SOURCE, (oni_reg_val_t)HarpSyncSource::Breakout) == ONI_ESUCCESS; + return deviceContext->writeRegister (deviceIdx, (oni_reg_addr_t) HarpSyncInputRegisters::SOURCE, (oni_reg_val_t) HarpSyncSource::Breakout) == ONI_ESUCCESS; } void HarpSyncInput::startAcquisition() { - currentFrame = 0; - sampleNumber = 0; + currentFrame = 0; + sampleNumber = 0; } -void HarpSyncInput::addSourceBuffers(OwnedArray& sourceBuffers) +void HarpSyncInput::addSourceBuffers (OwnedArray& sourceBuffers) { - sourceBuffers.add(new DataBuffer(streamInfos.getFirst().getNumChannels(), (int)streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); - harpTimeBuffer = sourceBuffers.getLast(); + sourceBuffers.add (new DataBuffer (streamInfos.getFirst().getNumChannels(), (int) streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); + harpTimeBuffer = sourceBuffers.getLast(); } void HarpSyncInput::processFrames() { - oni_frame_t* frame; - while (frameQueue.try_dequeue(frame)) - { + oni_frame_t* frame; + while (frameQueue.try_dequeue (frame)) + { + uint32_t* dataPtr = (uint32_t*) frame->data; - uint32_t* dataPtr = (uint32_t*)frame->data; + timestamps[currentFrame] = deviceContext->convertTimestampToSeconds (frame->time); - timestamps[currentFrame] = deviceContext->convertTimestampToSeconds(frame->time); + harpTimeSamples[currentFrame] = *(dataPtr + 2) + 1; - harpTimeSamples[currentFrame] = *(dataPtr + 2) + 1; + oni_destroy_frame (frame); - oni_destroy_frame(frame); + sampleNumbers[currentFrame] = sampleNumber++; - sampleNumbers[currentFrame] = sampleNumber++; + currentFrame++; - currentFrame++; + if (currentFrame >= numFrames) + { + shouldAddToBuffer = true; + currentFrame = 0; + } - if (currentFrame >= numFrames) - { - shouldAddToBuffer = true; - currentFrame = 0; - } - - if (shouldAddToBuffer) - { - shouldAddToBuffer = false; - harpTimeBuffer->addToBuffer(harpTimeSamples, sampleNumbers, timestamps, eventCodes, numFrames); - } - } + if (shouldAddToBuffer) + { + shouldAddToBuffer = false; + harpTimeBuffer->addToBuffer (harpTimeSamples, sampleNumbers, timestamps, eventCodes, numFrames); + } + } } diff --git a/Source/Devices/HarpSyncInput.h b/Source/Devices/HarpSyncInput.h index 41ed645..2d28ec1 100644 --- a/Source/Devices/HarpSyncInput.h +++ b/Source/Devices/HarpSyncInput.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -26,51 +26,50 @@ namespace OnixSourcePlugin { - enum class HarpSyncInputRegisters : uint32_t - { - ENABLE = 0, - SOURCE = 1 - }; - - enum class HarpSyncSource : uint32_t - { - Breakout = 0, - ClockAdapter = 1 - }; +enum class HarpSyncInputRegisters : uint32_t +{ + ENABLE = 0, + SOURCE = 1 +}; - /* - Configures and streams data from a HarpSyncInput device on a Breakout Board - */ - class HarpSyncInput : public OnixDevice - { - public: - HarpSyncInput(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); +enum class HarpSyncSource : uint32_t +{ + Breakout = 0, + ClockAdapter = 1 +}; - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; - void processFrames() override; +/* + Configures and streams data from a HarpSyncInput device on a Breakout Board +*/ +class HarpSyncInput : public OnixDevice +{ +public: + HarpSyncInput (std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); - static OnixDeviceType getDeviceType(); + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void addSourceBuffers (OwnedArray& sourceBuffers) override; + void processFrames() override; - private: + static OnixDeviceType getDeviceType(); - DataBuffer* harpTimeBuffer; +private: + DataBuffer* harpTimeBuffer; - static const int numFrames = 2; + static const int numFrames = 2; - unsigned short currentFrame = 0; - int sampleNumber = 0; + unsigned short currentFrame = 0; + int sampleNumber = 0; - bool shouldAddToBuffer = false; + bool shouldAddToBuffer = false; - float harpTimeSamples[numFrames]; + float harpTimeSamples[numFrames]; - double timestamps[numFrames]; - int64 sampleNumbers[numFrames]; - uint64 eventCodes[numFrames]; + double timestamps[numFrames]; + int64 sampleNumbers[numFrames]; + uint64 eventCodes[numFrames]; - JUCE_LEAK_DETECTOR(HarpSyncInput); - }; -} + JUCE_LEAK_DETECTOR (HarpSyncInput); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/HeadStageEEPROM.cpp b/Source/Devices/HeadStageEEPROM.cpp index f6ec487..c365bd5 100644 --- a/Source/Devices/HeadStageEEPROM.cpp +++ b/Source/Devices/HeadStageEEPROM.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -26,28 +26,31 @@ using namespace OnixSourcePlugin; -HeadStageEEPROM::HeadStageEEPROM(const oni_dev_idx_t dev_id, std::shared_ptr ctx) - : I2CRegisterContext(HeadStageEEPROM::EEPROM_ADDRESS, dev_id, ctx) +HeadStageEEPROM::HeadStageEEPROM (const oni_dev_idx_t dev_id, std::shared_ptr ctx) + : I2CRegisterContext (HeadStageEEPROM::EEPROM_ADDRESS, dev_id, ctx) { - auto deserializer = std::make_unique(DS90UB9x::DES_ADDR, dev_id, ctx); - uint32_t alias = HeadStageEEPROM::EEPROM_ADDRESS << 1; - int rc = deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID7, alias); - if (rc != ONI_ESUCCESS) return; - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias7, alias); - if (rc != ONI_ESUCCESS) return; + auto deserializer = std::make_unique (DS90UB9x::DES_ADDR, dev_id, ctx); + uint32_t alias = HeadStageEEPROM::EEPROM_ADDRESS << 1; + int rc = deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID7, alias); + if (rc != ONI_ESUCCESS) + return; + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias7, alias); + if (rc != ONI_ESUCCESS) + return; } uint32_t HeadStageEEPROM::GetHeadStageID() { - uint32_t data = 0; - int rc = 0; - for (unsigned int i = 0; i < sizeof(uint32_t); i++) - { - oni_reg_val_t val; - rc = ReadByte(DEVID_START_ADDR + i, &val, true); - if (rc != ONI_ESUCCESS) return data; - data += (val & 0xFF) << (8 * i); - } - return data; + uint32_t data = 0; + int rc = 0; + for (unsigned int i = 0; i < sizeof (uint32_t); i++) + { + oni_reg_val_t val; + rc = ReadByte (DEVID_START_ADDR + i, &val, true); + if (rc != ONI_ESUCCESS) + return data; + data += (val & 0xFF) << (8 * i); + } + return data; } diff --git a/Source/Devices/HeadStageEEPROM.h b/Source/Devices/HeadStageEEPROM.h index 0c93714..f9ce657 100644 --- a/Source/Devices/HeadStageEEPROM.h +++ b/Source/Devices/HeadStageEEPROM.h @@ -1,26 +1,25 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ - #pragma once #include "../I2CRegisterContext.h" @@ -30,18 +29,17 @@ namespace OnixSourcePlugin { - class HeadStageEEPROM : - public I2CRegisterContext - { - public: - HeadStageEEPROM(const oni_dev_idx_t, std::shared_ptr); +class HeadStageEEPROM : public I2CRegisterContext +{ +public: + HeadStageEEPROM (const oni_dev_idx_t, std::shared_ptr); - uint32_t GetHeadStageID(); + uint32_t GetHeadStageID(); - private: - static const uint32_t EEPROM_ADDRESS = 0x51; - static const uint32_t DEVID_START_ADDR = 18; +private: + static const uint32_t EEPROM_ADDRESS = 0x51; + static const uint32_t DEVID_START_ADDR = 18; - JUCE_LEAK_DETECTOR(HeadStageEEPROM); - }; -} + JUCE_LEAK_DETECTOR (HeadStageEEPROM); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/MemoryMonitor.cpp b/Source/Devices/MemoryMonitor.cpp index ec60757..8843852 100644 --- a/Source/Devices/MemoryMonitor.cpp +++ b/Source/Devices/MemoryMonitor.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -25,140 +25,139 @@ using namespace OnixSourcePlugin; -MemoryMonitorUsage::MemoryMonitorUsage(GenericProcessor* p) - : LevelMonitor(p) +MemoryMonitorUsage::MemoryMonitorUsage (GenericProcessor* p) + : LevelMonitor (p) { - device = nullptr; - setPassiveTooltip(); + device = nullptr; + setPassiveTooltip(); } void MemoryMonitorUsage::timerCallback() { - if (device != nullptr) - { - auto memoryUsedPercent = device->getLastPercentUsedValue(); - auto logMemoryUsed = std::log(memoryUsedPercent + 1) / maxLogarithmicValue; - setFillPercentage(logMemoryUsed); - setTooltip(getNewTooltip(memoryUsedPercent)); - repaint(); - } + if (device != nullptr) + { + auto memoryUsedPercent = device->getLastPercentUsedValue(); + auto logMemoryUsed = std::log (memoryUsedPercent + 1) / maxLogarithmicValue; + setFillPercentage (logMemoryUsed); + setTooltip (getNewTooltip (memoryUsedPercent)); + repaint(); + } } -std::string MemoryMonitorUsage::getNewTooltip(float memoryUsage) +std::string MemoryMonitorUsage::getNewTooltip (float memoryUsage) { - std::stringstream ss; - ss << "Memory Used: " << std::setprecision(3) << memoryUsage << "%"; - return ss.str(); + std::stringstream ss; + ss << "Memory Used: " << std::setprecision (3) << memoryUsage << "%"; + return ss.str(); } void MemoryMonitorUsage::setPassiveTooltip() { - setTooltip("Monitors the percent of the hardware memory buffer used."); + setTooltip ("Monitors the percent of the hardware memory buffer used."); } -void MemoryMonitorUsage::setMemoryMonitor(std::shared_ptr memoryMonitor) +void MemoryMonitorUsage::setMemoryMonitor (std::shared_ptr memoryMonitor) { - device = memoryMonitor; + device = memoryMonitor; } void MemoryMonitorUsage::startAcquisition() { - startTimerHz(TimerFrequencyHz); + startTimerHz (TimerFrequencyHz); } void MemoryMonitorUsage::stopAcquisition() { - stopTimer(); - setFillPercentage(0.0f); - setPassiveTooltip(); - repaint(); + stopTimer(); + setFillPercentage (0.0f); + setPassiveTooltip(); + repaint(); } -MemoryMonitor::MemoryMonitor(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) - : OnixDevice(name, hubName, MemoryMonitor::getDeviceType(), deviceIdx_, oni_ctx) +MemoryMonitor::MemoryMonitor (std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) + : OnixDevice (name, hubName, MemoryMonitor::getDeviceType(), deviceIdx_, oni_ctx) { - StreamInfo percentUsedStream = StreamInfo( - OnixDevice::createStreamName({ getHubName(), getName(), "PercentUsed" }), - "Percent of available memory that is currently used", - getStreamIdentifier(), - 1, - samplesPerSecond, - "Percent", - ContinuousChannel::Type::AUX, - 1.0f, - "%", - { "" }, - "percent" - ); - streamInfos.add(percentUsedStream); + StreamInfo percentUsedStream = StreamInfo ( + OnixDevice::createStreamName ({ getHubName(), getName(), "PercentUsed" }), + "Percent of available memory that is currently used", + getStreamIdentifier(), + 1, + samplesPerSecond, + "Percent", + ContinuousChannel::Type::AUX, + 1.0f, + "%", + { "" }, + "percent"); + streamInfos.add (percentUsedStream); } OnixDeviceType MemoryMonitor::getDeviceType() { - return OnixDeviceType::MEMORYMONITOR; + return OnixDeviceType::MEMORYMONITOR; } int MemoryMonitor::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + throw error_str ("Device context is not initialized properly for " + getName()); - setEnabled(true); + setEnabled (true); - int rc = deviceContext->writeRegister(deviceIdx, (uint32_t)MemoryMonitorRegisters::ENABLE, 1); - if (rc != ONI_ESUCCESS) - throw error_str("Unable to enable " + getName()); + int rc = deviceContext->writeRegister (deviceIdx, (uint32_t) MemoryMonitorRegisters::ENABLE, 1); + if (rc != ONI_ESUCCESS) + throw error_str ("Unable to enable " + getName()); - rc = deviceContext->readRegister(deviceIdx, (oni_reg_addr_t)MemoryMonitorRegisters::TOTAL_MEM, &totalMemory); + rc = deviceContext->readRegister (deviceIdx, (oni_reg_addr_t) MemoryMonitorRegisters::TOTAL_MEM, &totalMemory); - if (rc != ONI_ESUCCESS) - throw error_str("Unable to find the total memory used for " + getName()); + if (rc != ONI_ESUCCESS) + throw error_str ("Unable to find the total memory used for " + getName()); - return rc; + return rc; } bool MemoryMonitor::updateSettings() { - oni_reg_val_t clkHz; - int rc = deviceContext->readRegister(deviceIdx, (oni_reg_addr_t)MemoryMonitorRegisters::CLK_HZ, &clkHz); - if (rc != ONI_ESUCCESS) return rc; + oni_reg_val_t clkHz; + int rc = deviceContext->readRegister (deviceIdx, (oni_reg_addr_t) MemoryMonitorRegisters::CLK_HZ, &clkHz); + if (rc != ONI_ESUCCESS) + return rc; - rc = deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)MemoryMonitorRegisters::CLK_DIV, clkHz / samplesPerSecond); + rc = deviceContext->writeRegister (deviceIdx, (oni_reg_addr_t) MemoryMonitorRegisters::CLK_DIV, clkHz / samplesPerSecond); - return rc == ONI_ESUCCESS; + return rc == ONI_ESUCCESS; } void MemoryMonitor::startAcquisition() { - - sampleNumber = 0; - lastPercentUsedValue = 0.0f; + sampleNumber = 0; + lastPercentUsedValue = 0.0f; } -void MemoryMonitor::addSourceBuffers(OwnedArray& sourceBuffers) +void MemoryMonitor::addSourceBuffers (OwnedArray& sourceBuffers) { - sourceBuffers.add(new DataBuffer(streamInfos.getFirst().getNumChannels(), (int)streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); - percentUsedBuffer = sourceBuffers.getLast(); + sourceBuffers.add (new DataBuffer (streamInfos.getFirst().getNumChannels(), (int) streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); + percentUsedBuffer = sourceBuffers.getLast(); } float MemoryMonitor::getLastPercentUsedValue() { - return lastPercentUsedValue; + return lastPercentUsedValue; } void MemoryMonitor::processFrames() { - static uint64_t ec = 0; - oni_frame_t* frame; - - while (frameQueue.try_dequeue(frame)) - { - uint32_t* dataPtr = (uint32_t*)frame->data; - auto t = deviceContext->convertTimestampToSeconds(frame->time); - auto p = 100.0f * float(*(dataPtr + 2)) / totalMemory; - lastPercentUsedValue = p; - oni_destroy_frame(frame); - auto sn = sampleNumber++; - percentUsedBuffer->addToBuffer(&p, &sn, &t, &ec, 1); - } + static uint64_t ec = 0; + oni_frame_t* frame; + + while (frameQueue.try_dequeue (frame)) + { + uint32_t* dataPtr = (uint32_t*) frame->data; + auto t = deviceContext->convertTimestampToSeconds (frame->time); + auto p = 100.0f * float (*(dataPtr + 2)) / totalMemory; + lastPercentUsedValue = p; + oni_destroy_frame (frame); + auto sn = sampleNumber++; + percentUsedBuffer->addToBuffer (&p, &sn, &t, &ec, 1); + } } diff --git a/Source/Devices/MemoryMonitor.h b/Source/Devices/MemoryMonitor.h index 127ce9d..a053670 100644 --- a/Source/Devices/MemoryMonitor.h +++ b/Source/Devices/MemoryMonitor.h @@ -1,104 +1,102 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #pragma once #include + #include "../OnixDevice.h" namespace OnixSourcePlugin { - enum class MemoryMonitorRegisters : uint32_t - { - ENABLE = 0, - CLK_DIV = 1, - CLK_HZ = 2, - TOTAL_MEM = 3 - }; - - /* - Configures and streams data from a MemoryMonitor device on a Breakout Board - */ - class MemoryMonitor : public OnixDevice - { - public: - MemoryMonitor(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); - - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; - void processFrames() override; - - float getLastPercentUsedValue(); +enum class MemoryMonitorRegisters : uint32_t +{ + ENABLE = 0, + CLK_DIV = 1, + CLK_HZ = 2, + TOTAL_MEM = 3 +}; - static OnixDeviceType getDeviceType(); +/* + Configures and streams data from a MemoryMonitor device on a Breakout Board +*/ +class MemoryMonitor : public OnixDevice +{ +public: + MemoryMonitor (std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); - private: + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void addSourceBuffers (OwnedArray& sourceBuffers) override; + void processFrames() override; - DataBuffer* percentUsedBuffer; + float getLastPercentUsedValue(); - int64_t sampleNumber = 0; + static OnixDeviceType getDeviceType(); - /** The frequency at which memory use is recorded in Hz. */ - const uint32_t samplesPerSecond = 100; +private: + DataBuffer* percentUsedBuffer; - bool shouldAddToBuffer = false; + int64_t sampleNumber = 0; + /** The frequency at which memory use is recorded in Hz. */ + const uint32_t samplesPerSecond = 100; - /** The total amount of memory, in 32-bit words, on the hardware that is available for data buffering*/ - uint32_t totalMemory; + bool shouldAddToBuffer = false; - std::atomic lastPercentUsedValue = 0.0f; + /** The total amount of memory, in 32-bit words, on the hardware that is available for data buffering*/ + uint32_t totalMemory; - JUCE_LEAK_DETECTOR(MemoryMonitor); - }; + std::atomic lastPercentUsedValue = 0.0f; - /* - Tracks the MemoryMonitor usage while data acquisition is running - */ - class MemoryMonitorUsage : public LevelMonitor - { - public: - MemoryMonitorUsage(GenericProcessor*); + JUCE_LEAK_DETECTOR (MemoryMonitor); +}; - void timerCallback() override; +/* + Tracks the MemoryMonitor usage while data acquisition is running +*/ +class MemoryMonitorUsage : public LevelMonitor +{ +public: + MemoryMonitorUsage (GenericProcessor*); - void setMemoryMonitor(std::shared_ptr memoryMonitor); - void startAcquisition(); - void stopAcquisition(); - void setPassiveTooltip(); - static std::string getNewTooltip(float memoryUsage); + void timerCallback() override; - private: + void setMemoryMonitor (std::shared_ptr memoryMonitor); + void startAcquisition(); + void stopAcquisition(); + void setPassiveTooltip(); + static std::string getNewTooltip (float memoryUsage); - std::shared_ptr device; +private: + std::shared_ptr device; - // NB: Calculate the maximum logarithmic value to convert from linear scale (x: 0-100) to logarithmic scale (y: 0-1) - // using the following equation: y = log_e(x + 1) / log_e(x_max + 1); - const float maxLogarithmicValue = std::log(101); + // NB: Calculate the maximum logarithmic value to convert from linear scale (x: 0-100) to logarithmic scale (y: 0-1) + // using the following equation: y = log_e(x + 1) / log_e(x_max + 1); + const float maxLogarithmicValue = std::log (101); - const int TimerFrequencyHz = 10; + const int TimerFrequencyHz = 10; - JUCE_LEAK_DETECTOR(MemoryMonitorUsage); - }; -} + JUCE_LEAK_DETECTOR (MemoryMonitorUsage); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/Neuropixels1.cpp b/Source/Devices/Neuropixels1.cpp index 3cd85f0..fca20cf 100644 --- a/Source/Devices/Neuropixels1.cpp +++ b/Source/Devices/Neuropixels1.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,492 +24,493 @@ using namespace OnixSourcePlugin; -NeuropixelsV1BackgroundUpdater::NeuropixelsV1BackgroundUpdater(Neuropixels1* d) - : ThreadWithProgressWindow("Writing calibration files to Neuropixels Probe: " + d->getName(), true, false) +NeuropixelsV1BackgroundUpdater::NeuropixelsV1BackgroundUpdater (Neuropixels1* d) + : ThreadWithProgressWindow ("Writing calibration files to Neuropixels Probe: " + d->getName(), true, false) { - device = d; + device = d; } bool NeuropixelsV1BackgroundUpdater::updateSettings() { - if (device->isEnabled()) - runThread(); - else - return false; + if (device->isEnabled()) + runThread(); + else + return false; - return result; + return result; } -Neuropixels1::Neuropixels1(std::string name, std::string hubName, OnixDeviceType deviceType, const oni_dev_idx_t deviceIndex, std::shared_ptr context) : - OnixDevice(name, hubName, deviceType, deviceIndex, context, deviceType == OnixDeviceType::NEUROPIXELSV1E), - I2CRegisterContext(ProbeI2CAddress, deviceIndex, context), - INeuropixel(NeuropixelsV1Values::numberOfSettings, NeuropixelsV1Values::numberOfShanks) +Neuropixels1::Neuropixels1 (std::string name, std::string hubName, OnixDeviceType deviceType, const oni_dev_idx_t deviceIndex, std::shared_ptr context) + : OnixDevice (name, hubName, deviceType, deviceIndex, context, deviceType == OnixDeviceType::NEUROPIXELSV1E), + I2CRegisterContext (ProbeI2CAddress, deviceIndex, context), + INeuropixel (NeuropixelsV1Values::numberOfSettings, NeuropixelsV1Values::numberOfShanks) { } -void Neuropixels1::setSettings(ProbeSettings* settings_, int index) +void Neuropixels1::setSettings (ProbeSettings* settings_, int index) { - if (index >= settings.size()) - { - LOGE("Invalid index given when trying to update settings."); - return; - } + if (index >= settings.size()) + { + LOGE ("Invalid index given when trying to update settings."); + return; + } - settings[index]->updateProbeSettings(settings_); + settings[index]->updateProbeSettings (settings_); } -std::vector Neuropixels1::selectElectrodeConfiguration(int electrodeConfigurationIndex) +std::vector Neuropixels1::selectElectrodeConfiguration (int electrodeConfigurationIndex) { - std::vector selection; - - if (electrodeConfigurationIndex == (int32_t)ElectrodeConfiguration::BankA) - { - for (int i = 0; i < 384; i++) - selection.emplace_back(i); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfiguration::BankB) - { - for (int i = 384; i < 768; i++) - selection.emplace_back(i); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfiguration::BankC) - { - for (int i = 576; i < 960; i++) - selection.emplace_back(i); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfiguration::SingleColumn) - { - for (int i = 0; i < 384; i += 2) - selection.emplace_back(i); - - for (int i = 385; i < 768; i += 2) - selection.emplace_back(i); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfiguration::Tetrodes) - { - for (int i = 0; i < 384; i += 8) - { - for (int j = 0; j < 4; j++) - selection.emplace_back(i + j); - } - - for (int i = 388; i < 768; i += 8) - { - for (int j = 0; j < 4; j++) - selection.emplace_back(i + j); - } - } - - assert(selection.size() == numberOfChannels && "Invalid number of selected channels."); - - return selection; + std::vector selection; + + if (electrodeConfigurationIndex == (int32_t) ElectrodeConfiguration::BankA) + { + for (int i = 0; i < 384; i++) + selection.emplace_back (i); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfiguration::BankB) + { + for (int i = 384; i < 768; i++) + selection.emplace_back (i); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfiguration::BankC) + { + for (int i = 576; i < 960; i++) + selection.emplace_back (i); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfiguration::SingleColumn) + { + for (int i = 0; i < 384; i += 2) + selection.emplace_back (i); + + for (int i = 385; i < 768; i += 2) + selection.emplace_back (i); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfiguration::Tetrodes) + { + for (int i = 0; i < 384; i += 8) + { + for (int j = 0; j < 4; j++) + selection.emplace_back (i + j); + } + + for (int i = 388; i < 768; i += 8) + { + for (int j = 0; j < 4; j++) + selection.emplace_back (i + j); + } + } + + assert (selection.size() == numberOfChannels && "Invalid number of selected channels."); + + return selection; } -void Neuropixels1::updateApOffsets(std::array& samples, int64 sampleNumber) +void Neuropixels1::updateApOffsets (std::array& samples, int64 sampleNumber) { - if (sampleNumber > apSampleRate * secondsToSettle) - { - uint32_t counter = 0; - - while (apOffsetValues[0].size() < samplesToAverage) - { - if (counter >= superFramesPerUltraFrame * numUltraFrames) break; - - for (size_t i = 0; i < numberOfChannels; i++) - { - apOffsetValues[i].emplace_back(samples[i * superFramesPerUltraFrame * numUltraFrames + counter]); - } - - counter++; - } - - if (apOffsetValues[0].size() >= samplesToAverage) - { - for (int i = 0; i < numberOfChannels; i++) - { - apOffsets[i] = std::reduce(apOffsetValues.at(i).begin(), apOffsetValues.at(i).end()) / apOffsetValues.at(i).size(); - } - - apOffsetCalculated = true; - apOffsetValues.clear(); - } - } + if (sampleNumber > apSampleRate * secondsToSettle) + { + uint32_t counter = 0; + + while (apOffsetValues[0].size() < samplesToAverage) + { + if (counter >= superFramesPerUltraFrame * numUltraFrames) + break; + + for (size_t i = 0; i < numberOfChannels; i++) + { + apOffsetValues[i].emplace_back (samples[i * superFramesPerUltraFrame * numUltraFrames + counter]); + } + + counter++; + } + + if (apOffsetValues[0].size() >= samplesToAverage) + { + for (int i = 0; i < numberOfChannels; i++) + { + apOffsets[i] = std::reduce (apOffsetValues.at (i).begin(), apOffsetValues.at (i).end()) / apOffsetValues.at (i).size(); + } + + apOffsetCalculated = true; + apOffsetValues.clear(); + } + } } -void Neuropixels1::updateLfpOffsets(std::array& samples, int64 sampleNumber) +void Neuropixels1::updateLfpOffsets (std::array& samples, int64 sampleNumber) { - if (sampleNumber > lfpSampleRate * secondsToSettle) - { - uint32_t counter = 0; - - while (lfpOffsetValues[0].size() < samplesToAverage) - { - if (counter >= numUltraFrames) break; - - for (size_t i = 0; i < numberOfChannels; i++) - { - lfpOffsetValues[i].emplace_back(samples[i * numUltraFrames + counter]); - } - - counter++; - } - - if (lfpOffsetValues[0].size() >= samplesToAverage) - { - for (int i = 0; i < numberOfChannels; i++) - { - lfpOffsets[i] = std::reduce(lfpOffsetValues.at(i).begin(), lfpOffsetValues.at(i).end()) / lfpOffsetValues.at(i).size(); - } - - lfpOffsetCalculated = true; - lfpOffsetValues.clear(); - } - } + if (sampleNumber > lfpSampleRate * secondsToSettle) + { + uint32_t counter = 0; + + while (lfpOffsetValues[0].size() < samplesToAverage) + { + if (counter >= numUltraFrames) + break; + + for (size_t i = 0; i < numberOfChannels; i++) + { + lfpOffsetValues[i].emplace_back (samples[i * numUltraFrames + counter]); + } + + counter++; + } + + if (lfpOffsetValues[0].size() >= samplesToAverage) + { + for (int i = 0; i < numberOfChannels; i++) + { + lfpOffsets[i] = std::reduce (lfpOffsetValues.at (i).begin(), lfpOffsetValues.at (i).end()) / lfpOffsetValues.at (i).size(); + } + + lfpOffsetCalculated = true; + lfpOffsetValues.clear(); + } + } } -void Neuropixels1::defineMetadata(ProbeSettings* settings) +void Neuropixels1::defineMetadata (ProbeSettings* settings) { - settings->probeType = ProbeType::NPX_V1; - settings->probeMetadata.name = "Neuropixels 1.0"; - - std::vector> shankOutline{ - {27, 31}, - {27, 514}, - {27 + 5, 522}, - {27 + 10, 514}, - {27 + 10, 31} - }; - - std::vector> probeContour{ - {0, 155}, - {35, 0}, - {70, 155}, - {70, 9770}, - {0, 9770}, - {0, 155} - }; - - settings->probeMetadata.shank_count = 1; - settings->probeMetadata.electrodes_per_shank = numberOfElectrodes; - settings->probeMetadata.rows_per_shank = numberOfElectrodes / 2; - settings->probeMetadata.columns_per_shank = 2; - settings->probeMetadata.shankOutline = shankOutline; - settings->probeMetadata.probeContour = probeContour; - settings->probeMetadata.num_adcs = 32; // NB: Is this right for 1.0e? - settings->probeMetadata.adc_bits = 10; // NB: Is this right for 1.0e? - - settings->availableBanks = { - Bank::A, - Bank::B, - Bank::C, - Bank::NONE //disconnected - }; - - Array xpositions = { 27.0f, 59.0f, 11.0f, 43.0f }; - - for (int i = 0; i < numberOfElectrodes; i++) - { - ElectrodeMetadata metadata; - - metadata.shank = 0; - metadata.shank_local_index = i % settings->probeMetadata.electrodes_per_shank; - metadata.global_index = i; - metadata.xpos = xpositions[i % 4]; - metadata.ypos = (i - (i % 2)) * 10.0f; - metadata.site_width = 12; - metadata.column_index = i % 2; - metadata.row_index = i / 2; - metadata.isSelected = false; - metadata.colour = Colours::lightgrey; - - if (i < 384) - { - metadata.bank = Bank::A; - metadata.channel = i; - metadata.status = ElectrodeStatus::CONNECTED; - } - else if (i >= 384 && i < 768) - { - metadata.bank = Bank::B; - metadata.channel = i - 384; - metadata.status = ElectrodeStatus::DISCONNECTED; - } - else - { - metadata.bank = Bank::C; - metadata.channel = i - 768; - metadata.status = ElectrodeStatus::DISCONNECTED; - } - - if (i == 191 || i == 575 || i == 959) - { - metadata.type = ElectrodeType::REFERENCE; - } - else - { - metadata.type = ElectrodeType::ELECTRODE; - } - - settings->electrodeMetadata[i] = metadata; - } - - settings->apGainIndex = 4; // NB: AP Gain Index of 4 = Gain1000 - settings->lfpGainIndex = 0; // NB: LFP Gain Index of 0 = Gain50 - settings->referenceIndex = 0; - settings->apFilterState = true; - - for (int i = 0; i < numberOfChannels; i++) - { - settings->selectedBank[i] = Bank::A; - settings->selectedShank[i] = 0; - settings->selectedElectrode[i] = i; - } - - settings->availableApGains.add(50.0f); - settings->availableApGains.add(125.0f); - settings->availableApGains.add(250.0f); - settings->availableApGains.add(500.0f); - settings->availableApGains.add(1000.0f); - settings->availableApGains.add(1500.0f); - settings->availableApGains.add(2000.0f); - settings->availableApGains.add(3000.0f); - - settings->availableLfpGains.add(50.0f); - settings->availableLfpGains.add(125.0f); - settings->availableLfpGains.add(250.0f); - settings->availableLfpGains.add(500.0f); - settings->availableLfpGains.add(1000.0f); - settings->availableLfpGains.add(1500.0f); - settings->availableLfpGains.add(2000.0f); - settings->availableLfpGains.add(3000.0f); - - settings->availableReferences.add("Ext"); - settings->availableReferences.add("Tip"); - - for (const auto& [_, config] : electrodeConfiguration) - { - settings->availableElectrodeConfigurations.add(config); - } - - settings->electrodeConfigurationIndex = (int32_t)ElectrodeConfiguration::BankA; - auto selection = selectElectrodeConfiguration(settings->electrodeConfigurationIndex); - settings->selectElectrodes(selection); - - settings->isValid = true; + settings->probeType = ProbeType::NPX_V1; + settings->probeMetadata.name = "Neuropixels 1.0"; + + std::vector> shankOutline { + { 27, 31 }, + { 27, 514 }, + { 27 + 5, 522 }, + { 27 + 10, 514 }, + { 27 + 10, 31 } + }; + + std::vector> probeContour { + { 0, 155 }, + { 35, 0 }, + { 70, 155 }, + { 70, 9770 }, + { 0, 9770 }, + { 0, 155 } + }; + + settings->probeMetadata.shank_count = 1; + settings->probeMetadata.electrodes_per_shank = numberOfElectrodes; + settings->probeMetadata.rows_per_shank = numberOfElectrodes / 2; + settings->probeMetadata.columns_per_shank = 2; + settings->probeMetadata.shankOutline = shankOutline; + settings->probeMetadata.probeContour = probeContour; + settings->probeMetadata.num_adcs = 32; // NB: Is this right for 1.0e? + settings->probeMetadata.adc_bits = 10; // NB: Is this right for 1.0e? + + settings->availableBanks = { + Bank::A, + Bank::B, + Bank::C, + Bank::NONE // disconnected + }; + + Array xpositions = { 27.0f, 59.0f, 11.0f, 43.0f }; + + for (int i = 0; i < numberOfElectrodes; i++) + { + ElectrodeMetadata metadata; + + metadata.shank = 0; + metadata.shank_local_index = i % settings->probeMetadata.electrodes_per_shank; + metadata.global_index = i; + metadata.xpos = xpositions[i % 4]; + metadata.ypos = (i - (i % 2)) * 10.0f; + metadata.site_width = 12; + metadata.column_index = i % 2; + metadata.row_index = i / 2; + metadata.isSelected = false; + metadata.colour = Colours::lightgrey; + + if (i < 384) + { + metadata.bank = Bank::A; + metadata.channel = i; + metadata.status = ElectrodeStatus::CONNECTED; + } + else if (i >= 384 && i < 768) + { + metadata.bank = Bank::B; + metadata.channel = i - 384; + metadata.status = ElectrodeStatus::DISCONNECTED; + } + else + { + metadata.bank = Bank::C; + metadata.channel = i - 768; + metadata.status = ElectrodeStatus::DISCONNECTED; + } + + if (i == 191 || i == 575 || i == 959) + { + metadata.type = ElectrodeType::REFERENCE; + } + else + { + metadata.type = ElectrodeType::ELECTRODE; + } + + settings->electrodeMetadata[i] = metadata; + } + + settings->apGainIndex = 4; // NB: AP Gain Index of 4 = Gain1000 + settings->lfpGainIndex = 0; // NB: LFP Gain Index of 0 = Gain50 + settings->referenceIndex = 0; + settings->apFilterState = true; + + for (int i = 0; i < numberOfChannels; i++) + { + settings->selectedBank[i] = Bank::A; + settings->selectedShank[i] = 0; + settings->selectedElectrode[i] = i; + } + + settings->availableApGains.add (50.0f); + settings->availableApGains.add (125.0f); + settings->availableApGains.add (250.0f); + settings->availableApGains.add (500.0f); + settings->availableApGains.add (1000.0f); + settings->availableApGains.add (1500.0f); + settings->availableApGains.add (2000.0f); + settings->availableApGains.add (3000.0f); + + settings->availableLfpGains.add (50.0f); + settings->availableLfpGains.add (125.0f); + settings->availableLfpGains.add (250.0f); + settings->availableLfpGains.add (500.0f); + settings->availableLfpGains.add (1000.0f); + settings->availableLfpGains.add (1500.0f); + settings->availableLfpGains.add (2000.0f); + settings->availableLfpGains.add (3000.0f); + + settings->availableReferences.add ("Ext"); + settings->availableReferences.add ("Tip"); + + for (const auto& [_, config] : electrodeConfiguration) + { + settings->availableElectrodeConfigurations.add (config); + } + + settings->electrodeConfigurationIndex = (int32_t) ElectrodeConfiguration::BankA; + auto selection = selectElectrodeConfiguration (settings->electrodeConfigurationIndex); + settings->selectElectrodes (selection); + + settings->isValid = true; } -uint64_t Neuropixels1::getProbeSerialNumber(int index) +uint64_t Neuropixels1::getProbeSerialNumber (int index) { - return probeNumber; + return probeNumber; } bool Neuropixels1::parseGainCalibrationFile() { - if (gainCalibrationFilePath == "None" || gainCalibrationFilePath == "") - { - Onix1::showWarningMessageBoxAsync("Missing File", "Missing gain calibration file for probe " + std::to_string(probeNumber)); - return false; - } + if (gainCalibrationFilePath == "None" || gainCalibrationFilePath == "") + { + Onix1::showWarningMessageBoxAsync ("Missing File", "Missing gain calibration file for probe " + std::to_string (probeNumber)); + return false; + } - File gainFile = File(gainCalibrationFilePath); + File gainFile = File (gainCalibrationFilePath); - if (!gainFile.existsAsFile()) - { - Onix1::showWarningMessageBoxAsync("Invalid File", "Invalid gain calibration file for probe " + std::to_string(probeNumber)); - return false; - } + if (! gainFile.existsAsFile()) + { + Onix1::showWarningMessageBoxAsync ("Invalid File", "Invalid gain calibration file for probe " + std::to_string (probeNumber)); + return false; + } - StringArray gainFileLines; - gainFile.readLines(gainFileLines); + StringArray gainFileLines; + gainFile.readLines (gainFileLines); - auto gainSN = std::stoull(gainFileLines[0].toStdString()); + auto gainSN = std::stoull (gainFileLines[0].toStdString()); - LOGD("Gain calibration file SN = ", gainSN); + LOGD ("Gain calibration file SN = ", gainSN); - if (gainSN != probeNumber) - { - Onix1::showWarningMessageBoxAsync("Serial Number Mismatch", "Gain calibration file serial number (" + std::to_string(gainSN) + ") does not match probe serial number (" + std::to_string(probeNumber) + ")."); - return false; - } + if (gainSN != probeNumber) + { + Onix1::showWarningMessageBoxAsync ("Serial Number Mismatch", "Gain calibration file serial number (" + std::to_string (gainSN) + ") does not match probe serial number (" + std::to_string (probeNumber) + ")."); + return false; + } - if (gainFileLines.size() != numberOfElectrodes + 2) - { - Onix1::showWarningMessageBoxAsync("Invalid Gain Calibration File", "Expected to find " + std::to_string(numberOfElectrodes + 1) + " lines, but found " + std::to_string(gainFileLines.size()) + " instead."); - return false; - } + if (gainFileLines.size() != numberOfElectrodes + 2) + { + Onix1::showWarningMessageBoxAsync ("Invalid Gain Calibration File", "Expected to find " + std::to_string (numberOfElectrodes + 1) + " lines, but found " + std::to_string (gainFileLines.size()) + " instead."); + return false; + } - StringRef gainCalLine = gainFileLines[1]; - StringRef breakCharacters = ","; - StringRef noQuote = ""; + StringRef gainCalLine = gainFileLines[1]; + StringRef breakCharacters = ","; + StringRef noQuote = ""; - StringArray calibrationValues = StringArray::fromTokens(gainCalLine, breakCharacters, noQuote); + StringArray calibrationValues = StringArray::fromTokens (gainCalLine, breakCharacters, noQuote); - static constexpr int NumberOfGainFactors = 8; + static constexpr int NumberOfGainFactors = 8; - if (calibrationValues.size() != NumberOfGainFactors * 2 + 1) - { - Onix1::showWarningMessageBoxAsync("Gain Calibration File Error", "Expected to find " + std::to_string(NumberOfGainFactors * 2 + 1) + " elements per line, but found " + std::to_string(calibrationValues.size()) + " instead."); - return false; - } + if (calibrationValues.size() != NumberOfGainFactors * 2 + 1) + { + Onix1::showWarningMessageBoxAsync ("Gain Calibration File Error", "Expected to find " + std::to_string (NumberOfGainFactors * 2 + 1) + " elements per line, but found " + std::to_string (calibrationValues.size()) + " instead."); + return false; + } - apGainCorrection = std::stod(calibrationValues[settings[0]->apGainIndex + 1].toStdString()); - lfpGainCorrection = std::stod(calibrationValues[settings[0]->lfpGainIndex + NumberOfGainFactors + 1].toStdString()); + apGainCorrection = std::stod (calibrationValues[settings[0]->apGainIndex + 1].toStdString()); + lfpGainCorrection = std::stod (calibrationValues[settings[0]->lfpGainIndex + NumberOfGainFactors + 1].toStdString()); - LOGD("AP gain correction = ", apGainCorrection, ", LFP gain correction = ", lfpGainCorrection); + LOGD ("AP gain correction = ", apGainCorrection, ", LFP gain correction = ", lfpGainCorrection); - return true; + return true; } bool Neuropixels1::parseAdcCalibrationFile() { - if (adcCalibrationFilePath == "None" || adcCalibrationFilePath == "") - { - Onix1::showWarningMessageBoxAsync("Missing File", "Missing ADC calibration file for probe " + std::to_string(probeNumber)); - return false; - } - - File adcFile = File(adcCalibrationFilePath); - - if (!adcFile.existsAsFile()) - { - Onix1::showWarningMessageBoxAsync("Invalid File", "Invalid ADC calibration file for probe " + std::to_string(probeNumber)); - return false; - } - - StringArray adcFileLines; - adcFile.readLines(adcFileLines); - - auto adcSN = std::stoull(adcFileLines[0].toStdString()); - - LOGD("ADC calibration file SN = ", adcSN); - - if (adcSN != probeNumber) - { - Onix1::showWarningMessageBoxAsync("Serial Number Mismatch", "ADC calibration serial number (" + std::to_string(adcSN) + ") does not match probe serial number (" + std::to_string(probeNumber)+")."); - return false; - } - - if (adcFileLines.size() != NeuropixelsV1Values::AdcCount + 2) - { - Onix1::showWarningMessageBoxAsync("ADC Calibration File Error", "ADC calibration file does not have the correct number of lines. Expected " + std::to_string(NeuropixelsV1Values::AdcCount + 1) + " lines, found " + std::to_string(adcFileLines.size()) + " instead."); - return false; - } - - static constexpr int NumAdcValues = 9; // NB: ADC number + 8 values - - StringRef gainCalLine = adcFileLines[1]; - StringRef breakCharacters = ","; - StringRef noQuote = ""; - - adcValues.clear(); - - for (int i = 1; i < adcFileLines.size() - 1; i++) - { - auto adcLine = StringArray::fromTokens(adcFileLines[i], breakCharacters, noQuote); - - if (adcLine.size() != NumAdcValues) - { - Onix1::showWarningMessageBoxAsync("ADC Calibration File Error", "ADC Calibration file line " + std::to_string(i) + " is invalid. Expected " + std::to_string(NumAdcValues) + " values, found " + std::to_string(adcLine.size()) + " instead."); - return false; - } - - adcValues.emplace_back( - NeuropixelsV1Adc( - std::stoi(adcLine[1].toStdString()), - std::stoi(adcLine[2].toStdString()), - std::stoi(adcLine[3].toStdString()), - std::stoi(adcLine[4].toStdString()), - std::stoi(adcLine[5].toStdString()), - std::stoi(adcLine[6].toStdString()), - std::stoi(adcLine[7].toStdString()), - std::stoi(adcLine[8].toStdString()) - )); - } - - return true; + if (adcCalibrationFilePath == "None" || adcCalibrationFilePath == "") + { + Onix1::showWarningMessageBoxAsync ("Missing File", "Missing ADC calibration file for probe " + std::to_string (probeNumber)); + return false; + } + + File adcFile = File (adcCalibrationFilePath); + + if (! adcFile.existsAsFile()) + { + Onix1::showWarningMessageBoxAsync ("Invalid File", "Invalid ADC calibration file for probe " + std::to_string (probeNumber)); + return false; + } + + StringArray adcFileLines; + adcFile.readLines (adcFileLines); + + auto adcSN = std::stoull (adcFileLines[0].toStdString()); + + LOGD ("ADC calibration file SN = ", adcSN); + + if (adcSN != probeNumber) + { + Onix1::showWarningMessageBoxAsync ("Serial Number Mismatch", "ADC calibration serial number (" + std::to_string (adcSN) + ") does not match probe serial number (" + std::to_string (probeNumber) + ")."); + return false; + } + + if (adcFileLines.size() != NeuropixelsV1Values::AdcCount + 2) + { + Onix1::showWarningMessageBoxAsync ("ADC Calibration File Error", "ADC calibration file does not have the correct number of lines. Expected " + std::to_string (NeuropixelsV1Values::AdcCount + 1) + " lines, found " + std::to_string (adcFileLines.size()) + " instead."); + return false; + } + + static constexpr int NumAdcValues = 9; // NB: ADC number + 8 values + + StringRef gainCalLine = adcFileLines[1]; + StringRef breakCharacters = ","; + StringRef noQuote = ""; + + adcValues.clear(); + + for (int i = 1; i < adcFileLines.size() - 1; i++) + { + auto adcLine = StringArray::fromTokens (adcFileLines[i], breakCharacters, noQuote); + + if (adcLine.size() != NumAdcValues) + { + Onix1::showWarningMessageBoxAsync ("ADC Calibration File Error", "ADC Calibration file line " + std::to_string (i) + " is invalid. Expected " + std::to_string (NumAdcValues) + " values, found " + std::to_string (adcLine.size()) + " instead."); + return false; + } + + adcValues.emplace_back ( + NeuropixelsV1Adc ( + std::stoi (adcLine[1].toStdString()), + std::stoi (adcLine[2].toStdString()), + std::stoi (adcLine[3].toStdString()), + std::stoi (adcLine[4].toStdString()), + std::stoi (adcLine[5].toStdString()), + std::stoi (adcLine[6].toStdString()), + std::stoi (adcLine[7].toStdString()), + std::stoi (adcLine[8].toStdString()))); + } + + return true; } -NeuropixelsV1Gain Neuropixels1::getGainEnum(int index) +NeuropixelsV1Gain Neuropixels1::getGainEnum (int index) { - switch (index) - { - case 0: - return NeuropixelsV1Gain::Gain50; - case 1: - return NeuropixelsV1Gain::Gain125; - case 2: - return NeuropixelsV1Gain::Gain250; - case 3: - return NeuropixelsV1Gain::Gain500; - case 4: - return NeuropixelsV1Gain::Gain1000; - case 5: - return NeuropixelsV1Gain::Gain1500; - case 6: - return NeuropixelsV1Gain::Gain2000; - case 7: - return NeuropixelsV1Gain::Gain3000; - default: - return NeuropixelsV1Gain::Gain50; - } + switch (index) + { + case 0: + return NeuropixelsV1Gain::Gain50; + case 1: + return NeuropixelsV1Gain::Gain125; + case 2: + return NeuropixelsV1Gain::Gain250; + case 3: + return NeuropixelsV1Gain::Gain500; + case 4: + return NeuropixelsV1Gain::Gain1000; + case 5: + return NeuropixelsV1Gain::Gain1500; + case 6: + return NeuropixelsV1Gain::Gain2000; + case 7: + return NeuropixelsV1Gain::Gain3000; + default: + return NeuropixelsV1Gain::Gain50; + } } -int Neuropixels1::getGainValue(NeuropixelsV1Gain gain) +int Neuropixels1::getGainValue (NeuropixelsV1Gain gain) { - switch (gain) - { - case NeuropixelsV1Gain::Gain50: - return 50; - case NeuropixelsV1Gain::Gain125: - return 125; - case NeuropixelsV1Gain::Gain250: - return 250; - case NeuropixelsV1Gain::Gain500: - return 500; - case NeuropixelsV1Gain::Gain1000: - return 1000; - case NeuropixelsV1Gain::Gain1500: - return 1500; - case NeuropixelsV1Gain::Gain2000: - return 2000; - case NeuropixelsV1Gain::Gain3000: - return 3000; - default: - return 50; - } + switch (gain) + { + case NeuropixelsV1Gain::Gain50: + return 50; + case NeuropixelsV1Gain::Gain125: + return 125; + case NeuropixelsV1Gain::Gain250: + return 250; + case NeuropixelsV1Gain::Gain500: + return 500; + case NeuropixelsV1Gain::Gain1000: + return 1000; + case NeuropixelsV1Gain::Gain1500: + return 1500; + case NeuropixelsV1Gain::Gain2000: + return 2000; + case NeuropixelsV1Gain::Gain3000: + return 3000; + default: + return 50; + } } -NeuropixelsV1Reference Neuropixels1::getReference(int index) +NeuropixelsV1Reference Neuropixels1::getReference (int index) { - switch (index) - { - case 0: - return NeuropixelsV1Reference::External; - case 1: - return NeuropixelsV1Reference::Tip; - default: - break; - } - - return NeuropixelsV1Reference::External; + switch (index) + { + case 0: + return NeuropixelsV1Reference::External; + case 1: + return NeuropixelsV1Reference::Tip; + default: + break; + } + + return NeuropixelsV1Reference::External; } std::string Neuropixels1::getAdcCalibrationFilePath() { - return adcCalibrationFilePath; + return adcCalibrationFilePath; } -void Neuropixels1::setAdcCalibrationFilePath(std::string filepath) +void Neuropixels1::setAdcCalibrationFilePath (std::string filepath) { - adcCalibrationFilePath = filepath; + adcCalibrationFilePath = filepath; } std::string Neuropixels1::getGainCalibrationFilePath() { - return gainCalibrationFilePath; + return gainCalibrationFilePath; } -void Neuropixels1::setGainCalibrationFilePath(std::string filepath) +void Neuropixels1::setGainCalibrationFilePath (std::string filepath) { - gainCalibrationFilePath = filepath; + gainCalibrationFilePath = filepath; } diff --git a/Source/Devices/Neuropixels1.h b/Source/Devices/Neuropixels1.h index 94eb030..3d5e5d4 100644 --- a/Source/Devices/Neuropixels1.h +++ b/Source/Devices/Neuropixels1.h @@ -1,172 +1,168 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #pragma once -#include "../OnixDevice.h" -#include "../NeuropixelsComponents.h" #include "../I2CRegisterContext.h" +#include "../NeuropixelsComponents.h" +#include "../OnixDevice.h" namespace OnixSourcePlugin { - class Neuropixels1 : public INeuropixel, - public OnixDevice, - public I2CRegisterContext - { - public: - - Neuropixels1(std::string, std::string, OnixDeviceType, const oni_dev_idx_t, std::shared_ptr); - - NeuropixelsV1Gain getGainEnum(int index); - - int getGainValue(NeuropixelsV1Gain); - - NeuropixelsV1Reference getReference(int index); +class Neuropixels1 : public INeuropixel, + public OnixDevice, + public I2CRegisterContext +{ +public: + Neuropixels1 (std::string, std::string, OnixDeviceType, const oni_dev_idx_t, std::shared_ptr); - std::string getAdcCalibrationFilePath(); - void setAdcCalibrationFilePath(std::string filepath); - std::string getGainCalibrationFilePath(); - void setGainCalibrationFilePath(std::string filepath); + NeuropixelsV1Gain getGainEnum (int index); - // INeuropixels methods - void defineMetadata(ProbeSettings* settings) override; + int getGainValue (NeuropixelsV1Gain); - /** Select a preset electrode configuration, based on the index of the given enum */ - std::vector selectElectrodeConfiguration(int electrodeConfigurationIndex) override; + NeuropixelsV1Reference getReference (int index); - uint64_t getProbeSerialNumber(int index = 0) override; + std::string getAdcCalibrationFilePath(); + void setAdcCalibrationFilePath (std::string filepath); + std::string getGainCalibrationFilePath(); + void setGainCalibrationFilePath (std::string filepath); - void setSettings(ProbeSettings* settings_, int index = 0) override; + // INeuropixels methods + void defineMetadata (ProbeSettings* settings) override; - bool parseGainCalibrationFile(); - bool parseAdcCalibrationFile(); + /** Select a preset electrode configuration, based on the index of the given enum */ + std::vector selectElectrodeConfiguration (int electrodeConfigurationIndex) override; - protected: + uint64_t getProbeSerialNumber (int index = 0) override; - DataBuffer* apBuffer; - DataBuffer* lfpBuffer; + void setSettings (ProbeSettings* settings_, int index = 0) override; - std::string adcCalibrationFilePath; - std::string gainCalibrationFilePath; + bool parseGainCalibrationFile(); + bool parseAdcCalibrationFile(); - double apGainCorrection = 0; - double lfpGainCorrection = 0; +protected: + DataBuffer* apBuffer; + DataBuffer* lfpBuffer; - uint64_t probeNumber = 0; + std::string adcCalibrationFilePath; + std::string gainCalibrationFilePath; - const uint32_t ENABLE = 0x8000; + double apGainCorrection = 0; + double lfpGainCorrection = 0; - static constexpr int ProbeI2CAddress = 0x70; + uint64_t probeNumber = 0; - static constexpr int superFramesPerUltraFrame = 12; - static constexpr int framesPerSuperFrame = 13; - static constexpr int framesPerUltraFrame = superFramesPerUltraFrame * framesPerSuperFrame; - static constexpr int numUltraFrames = 12; - static constexpr int dataOffset = 4 + 1; // NB: 4 bytes [hubClock] + 1 byte [probeIndex] + const uint32_t ENABLE = 0x8000; - static constexpr uint16_t NumberOfAdcBins = 1024; - static constexpr float DataMidpoint = NumberOfAdcBins / 2; + static constexpr int ProbeI2CAddress = 0x70; - static constexpr int secondsToSettle = 5; - static constexpr int samplesToAverage = 100; + static constexpr int superFramesPerUltraFrame = 12; + static constexpr int framesPerSuperFrame = 13; + static constexpr int framesPerUltraFrame = superFramesPerUltraFrame * framesPerSuperFrame; + static constexpr int numUltraFrames = 12; + static constexpr int dataOffset = 4 + 1; // NB: 4 bytes [hubClock] + 1 byte [probeIndex] - static constexpr uint32_t numLfpSamples = 384 * numUltraFrames; - static constexpr uint32_t numApSamples = 384 * numUltraFrames * superFramesPerUltraFrame; + static constexpr uint16_t NumberOfAdcBins = 1024; + static constexpr float DataMidpoint = NumberOfAdcBins / 2; - static constexpr float lfpSampleRate = 2500.0f; - static constexpr float apSampleRate = 30000.0f; + static constexpr int secondsToSettle = 5; + static constexpr int samplesToAverage = 100; - bool lfpOffsetCalculated = false; - bool apOffsetCalculated = false; + static constexpr uint32_t numLfpSamples = 384 * numUltraFrames; + static constexpr uint32_t numApSamples = 384 * numUltraFrames * superFramesPerUltraFrame; - std::array apOffsets; - std::array lfpOffsets; + static constexpr float lfpSampleRate = 2500.0f; + static constexpr float apSampleRate = 30000.0f; - std::vector> apOffsetValues; - std::vector> lfpOffsetValues; + bool lfpOffsetCalculated = false; + bool apOffsetCalculated = false; - std::array lfpSamples; - std::array apSamples; + std::array apOffsets; + std::array lfpOffsets; - int64 apSampleNumbers[numUltraFrames * superFramesPerUltraFrame]; - double apTimestamps[numUltraFrames * superFramesPerUltraFrame]; - uint64 apEventCodes[numUltraFrames * superFramesPerUltraFrame]; + std::vector> apOffsetValues; + std::vector> lfpOffsetValues; - int64 lfpSampleNumbers[numUltraFrames]; - double lfpTimestamps[numUltraFrames]; - uint64 lfpEventCodes[numUltraFrames]; + std::array lfpSamples; + std::array apSamples; - int superFrameCount = 0; - int ultraFrameCount = 0; + int64 apSampleNumbers[numUltraFrames * superFramesPerUltraFrame]; + double apTimestamps[numUltraFrames * superFramesPerUltraFrame]; + uint64 apEventCodes[numUltraFrames * superFramesPerUltraFrame]; - int apSampleNumber = 0; - int lfpSampleNumber = 0; + int64 lfpSampleNumbers[numUltraFrames]; + double lfpTimestamps[numUltraFrames]; + uint64 lfpEventCodes[numUltraFrames]; - int apGain = 1000; - int lfpGain = 50; + int superFrameCount = 0; + int ultraFrameCount = 0; - std::vector adcValues; + int apSampleNumber = 0; + int lfpSampleNumber = 0; - void updateLfpOffsets(std::array&, int64); - void updateApOffsets(std::array&, int64); + int apGain = 1000; + int lfpGain = 50; - enum class ElectrodeConfiguration : int32_t - { - BankA = 0, - BankB = 1, - BankC = 2, - SingleColumn = 3, - Tetrodes = 4 - }; + std::vector adcValues; - std::map electrodeConfiguration = { - {ElectrodeConfiguration::BankA, "Bank A"}, - {ElectrodeConfiguration::BankB, "Bank B"}, - {ElectrodeConfiguration::BankC, "Bank C"}, - {ElectrodeConfiguration::SingleColumn, "Single Column"}, - {ElectrodeConfiguration::Tetrodes, "Tetrodes"} - }; + void updateLfpOffsets (std::array&, int64); + void updateApOffsets (std::array&, int64); - JUCE_LEAK_DETECTOR(Neuropixels1); - }; + enum class ElectrodeConfiguration : int32_t + { + BankA = 0, + BankB = 1, + BankC = 2, + SingleColumn = 3, + Tetrodes = 4 + }; - /* - A thread that updates Neuropixels 1.0 probe settings in the background and shows a progress bar - */ - class NeuropixelsV1BackgroundUpdater : public ThreadWithProgressWindow - { - public: - NeuropixelsV1BackgroundUpdater(Neuropixels1* d); + std::map electrodeConfiguration = { + { ElectrodeConfiguration::BankA, "Bank A" }, + { ElectrodeConfiguration::BankB, "Bank B" }, + { ElectrodeConfiguration::BankC, "Bank C" }, + { ElectrodeConfiguration::SingleColumn, "Single Column" }, + { ElectrodeConfiguration::Tetrodes, "Tetrodes" } + }; - bool updateSettings(); + JUCE_LEAK_DETECTOR (Neuropixels1); +}; - protected: +/* + A thread that updates Neuropixels 1.0 probe settings in the background and shows a progress bar +*/ +class NeuropixelsV1BackgroundUpdater : public ThreadWithProgressWindow +{ +public: + NeuropixelsV1BackgroundUpdater (Neuropixels1* d); - Neuropixels1* device; + bool updateSettings(); - std::atomic result = false; +protected: + Neuropixels1* device; - private: + std::atomic result = false; - JUCE_LEAK_DETECTOR(NeuropixelsV1BackgroundUpdater); - }; -} +private: + JUCE_LEAK_DETECTOR (NeuropixelsV1BackgroundUpdater); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/Neuropixels1e.cpp b/Source/Devices/Neuropixels1e.cpp index b26d9a3..ae9a995 100644 --- a/Source/Devices/Neuropixels1e.cpp +++ b/Source/Devices/Neuropixels1e.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,398 +24,398 @@ using namespace OnixSourcePlugin; -NeuropixelsV1eBackgroundUpdater::NeuropixelsV1eBackgroundUpdater(Neuropixels1e* d) - : NeuropixelsV1BackgroundUpdater(d) +NeuropixelsV1eBackgroundUpdater::NeuropixelsV1eBackgroundUpdater (Neuropixels1e* d) + : NeuropixelsV1BackgroundUpdater (d) { } void NeuropixelsV1eBackgroundUpdater::run() { - setProgress(0); - - ((Neuropixels1e*)device)->resetProbe(); - - if (!device->parseGainCalibrationFile()) - { - result = false; - return; - } - - if (!device->parseAdcCalibrationFile()) - { - result = false; - return; - } - - setProgress(0.5); - - device->WriteByte((uint32_t)NeuropixelsV1Registers::CAL_MOD, (uint32_t)NeuropixelsV1CalibrationRegisterValues::CAL_OFF); - device->WriteByte((uint32_t)NeuropixelsV1Registers::TEST_CONFIG1, 0); - device->WriteByte((uint32_t)NeuropixelsV1Registers::TEST_CONFIG2, 0); - device->WriteByte((uint32_t)NeuropixelsV1Registers::TEST_CONFIG3, 0); - device->WriteByte((uint32_t)NeuropixelsV1Registers::TEST_CONFIG4, 0); - device->WriteByte((uint32_t)NeuropixelsV1Registers::TEST_CONFIG5, 0); - device->WriteByte((uint32_t)NeuropixelsV1Registers::SYNC, 0); - device->WriteByte((uint32_t)NeuropixelsV1Registers::REC_MOD, (uint32_t)NeuropixelsV1RecordRegisterValues::ACTIVE); - device->WriteByte((uint32_t)NeuropixelsV1Registers::OP_MODE, (uint32_t)NeuropixelsV1OperationRegisterValues::RECORD); - - try - { - ((Neuropixels1e*)device)->writeShiftRegisters(); - } - catch (const error_str& e) - { - Onix1::showWarningMessageBoxAsync("Error Writing Shift Registers", e.what()); - result = false; - return; - } - - setProgress(1); - - result = true; + setProgress (0); + + ((Neuropixels1e*) device)->resetProbe(); + + if (! device->parseGainCalibrationFile()) + { + result = false; + return; + } + + if (! device->parseAdcCalibrationFile()) + { + result = false; + return; + } + + setProgress (0.5); + + device->WriteByte ((uint32_t) NeuropixelsV1Registers::CAL_MOD, (uint32_t) NeuropixelsV1CalibrationRegisterValues::CAL_OFF); + device->WriteByte ((uint32_t) NeuropixelsV1Registers::TEST_CONFIG1, 0); + device->WriteByte ((uint32_t) NeuropixelsV1Registers::TEST_CONFIG2, 0); + device->WriteByte ((uint32_t) NeuropixelsV1Registers::TEST_CONFIG3, 0); + device->WriteByte ((uint32_t) NeuropixelsV1Registers::TEST_CONFIG4, 0); + device->WriteByte ((uint32_t) NeuropixelsV1Registers::TEST_CONFIG5, 0); + device->WriteByte ((uint32_t) NeuropixelsV1Registers::SYNC, 0); + device->WriteByte ((uint32_t) NeuropixelsV1Registers::REC_MOD, (uint32_t) NeuropixelsV1RecordRegisterValues::ACTIVE); + device->WriteByte ((uint32_t) NeuropixelsV1Registers::OP_MODE, (uint32_t) NeuropixelsV1OperationRegisterValues::RECORD); + + try + { + ((Neuropixels1e*) device)->writeShiftRegisters(); + } + catch (const error_str& e) + { + Onix1::showWarningMessageBoxAsync ("Error Writing Shift Registers", e.what()); + result = false; + return; + } + + setProgress (1); + + result = true; } -Neuropixels1e::Neuropixels1e(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx_) : - Neuropixels1(name, hubName, OnixDeviceType::NEUROPIXELSV1E, deviceIdx_, ctx_) +Neuropixels1e::Neuropixels1e (std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx_) + : Neuropixels1 (name, hubName, OnixDeviceType::NEUROPIXELSV1E, deviceIdx_, ctx_) { - std::string port = getPortName(getDeviceIdx()); - StreamInfo apStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), STREAM_NAME_AP }), - "Neuropixels 1.0 AP band data stream", - getStreamIdentifier(), - numberOfChannels, - apSampleRate, - STREAM_NAME_AP, - ContinuousChannel::Type::ELECTRODE, - 0.195f, - "uV", - {}, - "ap" - ); - streamInfos.add(apStream); - - StreamInfo lfpStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), STREAM_NAME_LFP }), - "Neuropixels 1.0 LFP band data stream", - getStreamIdentifier(), - numberOfChannels, - lfpSampleRate, - STREAM_NAME_LFP, - ContinuousChannel::Type::ELECTRODE, - 0.195f, - "uV", - {}, - "lfp" - ); - streamInfos.add(lfpStream); - - defineMetadata(settings[0].get()); - - adcCalibrationFilePath = "None"; - gainCalibrationFilePath = "None"; - - for (int i = 0; i < numUltraFrames; i++) - { - apEventCodes[i] = 0; - lfpEventCodes[i] = 0; - } - - probeNumber = 0; + std::string port = getPortName (getDeviceIdx()); + StreamInfo apStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), STREAM_NAME_AP }), + "Neuropixels 1.0 AP band data stream", + getStreamIdentifier(), + numberOfChannels, + apSampleRate, + STREAM_NAME_AP, + ContinuousChannel::Type::ELECTRODE, + 0.195f, + "uV", + {}, + "ap"); + streamInfos.add (apStream); + + StreamInfo lfpStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), STREAM_NAME_LFP }), + "Neuropixels 1.0 LFP band data stream", + getStreamIdentifier(), + numberOfChannels, + lfpSampleRate, + STREAM_NAME_LFP, + ContinuousChannel::Type::ELECTRODE, + 0.195f, + "uV", + {}, + "lfp"); + streamInfos.add (lfpStream); + + defineMetadata (settings[0].get()); + + adcCalibrationFilePath = "None"; + gainCalibrationFilePath = "None"; + + for (int i = 0; i < numUltraFrames; i++) + { + apEventCodes[i] = 0; + lfpEventCodes[i] = 0; + } + + probeNumber = 0; } int Neuropixels1e::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + throw error_str ("Device context is not initialized properly for " + getName()); - configureSerDes(); + configureSerDes(); - int rc = serializer->set933I2cRate(400e3); - if (rc != ONI_ESUCCESS) - throw error_str("Unable to set I2C rate for " + getName()); + int rc = serializer->set933I2cRate (400e3); + if (rc != ONI_ESUCCESS) + throw error_str ("Unable to set I2C rate for " + getName()); - // Get Probe SN + // Get Probe SN - int errorCode = 0; + int errorCode = 0; - for (int i = 0; i < 8; i++) - { - oni_reg_val_t reg_val; - rc = flex->ReadByte(OFFSET_ID + i, ®_val); + for (int i = 0; i < 8; i++) + { + oni_reg_val_t reg_val; + rc = flex->ReadByte (OFFSET_ID + i, ®_val); - if (rc != ONI_ESUCCESS) - throw error_str("Unable to read the probe serial number for device at address " + getDeviceIdx()); + if (rc != ONI_ESUCCESS) + throw error_str ("Unable to read the probe serial number for device at address " + getDeviceIdx()); - if (reg_val <= 0xFF) - { - probeNumber |= (((uint64_t)reg_val) << (i * 8)); - } - } + if (reg_val <= 0xFF) + { + probeNumber |= (((uint64_t) reg_val) << (i * 8)); + } + } - LOGD("Probe SN: ", probeNumber); + LOGD ("Probe SN: ", probeNumber); - return ONI_ESUCCESS; + return ONI_ESUCCESS; } OnixDeviceType Neuropixels1e::getDeviceType() { - return OnixDeviceType::NEUROPIXELSV1E; + return OnixDeviceType::NEUROPIXELSV1E; } void Neuropixels1e::configureSerDes() { - deviceContext->writeRegister(deviceIdx, DS90UB9x::ENABLE, 1); - - deviceContext->writeRegister(deviceIdx, DS90UB9x::TRIGGEROFF, 0); - deviceContext->writeRegister(deviceIdx, DS90UB9x::TRIGGER, (uint32_t)DS90UB9x::DS90UB9xTriggerMode::Continuous); - deviceContext->writeRegister(deviceIdx, DS90UB9x::SYNCBITS, 0); - deviceContext->writeRegister(deviceIdx, DS90UB9x::DATAGATE, 0b00000001000100110000000000000001); - deviceContext->writeRegister(deviceIdx, DS90UB9x::MARK, (uint32_t)DS90UB9x::DS90UB9xMarkMode::Disabled); - - // configure one magic word-triggered stream for the PSB bus - deviceContext->writeRegister(deviceIdx, DS90UB9x::READSZ, 851973); // 13 frames/superframe, 7x 140-bit words on each serial line per frame - deviceContext->writeRegister(deviceIdx, DS90UB9x::MAGIC_MASK, 0b11000000000000000000001111111111); // Enable inverse, wait for non-inverse, 10-bit magic word - deviceContext->writeRegister(deviceIdx, DS90UB9x::MAGIC, 816); // Super-frame sync word - deviceContext->writeRegister(deviceIdx, DS90UB9x::MAGIC_WAIT, 0); - deviceContext->writeRegister(deviceIdx, DS90UB9x::DATAMODE, 913); - deviceContext->writeRegister(deviceIdx, DS90UB9x::DATALINES0, 0x3245106B); // Sync, psb[0], psb[1], psb[2], psb[3], psb[4], psb[5], psb[6], - deviceContext->writeRegister(deviceIdx, DS90UB9x::DATALINES1, 0xFFFFFFFF); - - std::this_thread::sleep_for(100ms); // Empirical. The gateware seems to need some milliseconds to get i2c initialized. - - deserializer = std::make_unique(DS90UB9x::DES_ADDR, deviceIdx, deviceContext); - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::PortSel, 0x01); // Enable port 0 - int coaxMode = 0x4 + (uint32_t)(DS90UB9x::DS90UB9xMode::Raw12BitHighFrequency); // 0x4 maintains coax mode - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::PortMode, coaxMode); // 0x4 maintains coax mode - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::I2CConfig, 0b01011000); // 7: i2c pass all (0), 6: i2c pass (1), 5: auto_ack (0), 4: BC enable (1), 3: BC crc en (1), 2: reserved (0) 1:0: bc freq (00) 2.5Mbps - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::SerAlias, DS90UB9x::SER_ADDR << 1); - // Enable backchannel GPIO on deserializer. It is then the serializer task to decide if using them or use manual output - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::GpioCtrl0, 0x10); - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::GpioCtrl1, 0x32); - - auto alias = ProbeI2CAddress << 1; - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID1, alias); - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias1, alias); - - alias = FlexEepromI2CAddress << 1; - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID2, alias); - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias2, alias); - - serializer = std::make_unique(DS90UB9x::SER_ADDR, deviceIdx, deviceContext); - flex = std::make_unique(FlexEepromI2CAddress, deviceIdx, deviceContext); + deviceContext->writeRegister (deviceIdx, DS90UB9x::ENABLE, 1); + + deviceContext->writeRegister (deviceIdx, DS90UB9x::TRIGGEROFF, 0); + deviceContext->writeRegister (deviceIdx, DS90UB9x::TRIGGER, (uint32_t) DS90UB9x::DS90UB9xTriggerMode::Continuous); + deviceContext->writeRegister (deviceIdx, DS90UB9x::SYNCBITS, 0); + deviceContext->writeRegister (deviceIdx, DS90UB9x::DATAGATE, 0b00000001000100110000000000000001); + deviceContext->writeRegister (deviceIdx, DS90UB9x::MARK, (uint32_t) DS90UB9x::DS90UB9xMarkMode::Disabled); + + // configure one magic word-triggered stream for the PSB bus + deviceContext->writeRegister (deviceIdx, DS90UB9x::READSZ, 851973); // 13 frames/superframe, 7x 140-bit words on each serial line per frame + deviceContext->writeRegister (deviceIdx, DS90UB9x::MAGIC_MASK, 0b11000000000000000000001111111111); // Enable inverse, wait for non-inverse, 10-bit magic word + deviceContext->writeRegister (deviceIdx, DS90UB9x::MAGIC, 816); // Super-frame sync word + deviceContext->writeRegister (deviceIdx, DS90UB9x::MAGIC_WAIT, 0); + deviceContext->writeRegister (deviceIdx, DS90UB9x::DATAMODE, 913); + deviceContext->writeRegister (deviceIdx, DS90UB9x::DATALINES0, 0x3245106B); // Sync, psb[0], psb[1], psb[2], psb[3], psb[4], psb[5], psb[6], + deviceContext->writeRegister (deviceIdx, DS90UB9x::DATALINES1, 0xFFFFFFFF); + + std::this_thread::sleep_for (100ms); // Empirical. The gateware seems to need some milliseconds to get i2c initialized. + + deserializer = std::make_unique (DS90UB9x::DES_ADDR, deviceIdx, deviceContext); + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::PortSel, 0x01); // Enable port 0 + int coaxMode = 0x4 + (uint32_t) (DS90UB9x::DS90UB9xMode::Raw12BitHighFrequency); // 0x4 maintains coax mode + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::PortMode, coaxMode); // 0x4 maintains coax mode + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::I2CConfig, 0b01011000); // 7: i2c pass all (0), 6: i2c pass (1), 5: auto_ack (0), 4: BC enable (1), 3: BC crc en (1), 2: reserved (0) 1:0: bc freq (00) 2.5Mbps + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::SerAlias, DS90UB9x::SER_ADDR << 1); + // Enable backchannel GPIO on deserializer. It is then the serializer task to decide if using them or use manual output + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::GpioCtrl0, 0x10); + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::GpioCtrl1, 0x32); + + auto alias = ProbeI2CAddress << 1; + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID1, alias); + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias1, alias); + + alias = FlexEepromI2CAddress << 1; + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID2, alias); + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias2, alias); + + serializer = std::make_unique (DS90UB9x::SER_ADDR, deviceIdx, deviceContext); + flex = std::make_unique (FlexEepromI2CAddress, deviceIdx, deviceContext); } void Neuropixels1e::resetProbe() { - auto gpo10Config = DefaultGPO10Config & ~Gpo10ResetMask; - serializer->WriteByte((uint32_t)DS90UB9x::DS90UB933SerializerI2CRegister::Gpio10, gpo10Config); - std::this_thread::sleep_for(1ms); - gpo10Config |= Gpo10ResetMask; - serializer->WriteByte((uint32_t)DS90UB9x::DS90UB933SerializerI2CRegister::Gpio10, gpo10Config); + auto gpo10Config = DefaultGPO10Config & ~Gpo10ResetMask; + serializer->WriteByte ((uint32_t) DS90UB9x::DS90UB933SerializerI2CRegister::Gpio10, gpo10Config); + std::this_thread::sleep_for (1ms); + gpo10Config |= Gpo10ResetMask; + serializer->WriteByte ((uint32_t) DS90UB9x::DS90UB933SerializerI2CRegister::Gpio10, gpo10Config); } bool Neuropixels1e::updateSettings() { - auto updater = NeuropixelsV1eBackgroundUpdater(this); + auto updater = NeuropixelsV1eBackgroundUpdater (this); - return updater.updateSettings() && adcValues.size() == NeuropixelsV1Values::AdcCount; + return updater.updateSettings() && adcValues.size() == NeuropixelsV1Values::AdcCount; } void Neuropixels1e::startAcquisition() { - apGain = getGainValue(getGainEnum(settings[0]->apGainIndex)); - lfpGain = getGainValue(getGainEnum(settings[0]->lfpGainIndex)); - - apOffsetValues.clear(); - apOffsetValues.reserve(numberOfChannels); - lfpOffsetValues.clear(); - lfpOffsetValues.reserve(numberOfChannels); - - for (int i = 0; i < numberOfChannels; i++) - { - apOffsets[i] = 0; - lfpOffsets[i] = 0; - - apOffsetValues.emplace_back(std::vector{}); - lfpOffsetValues.emplace_back(std::vector{}); - } - - lfpOffsetCalculated = false; - apOffsetCalculated = false; - - // WONTFIX: Soft reset inside settings.WriteShiftRegisters() above puts probe in reset set that - // needs to be undone here - WriteByte((uint32_t)NeuropixelsV1Registers::OP_MODE, (uint32_t)NeuropixelsV1OperationRegisterValues::RECORD); - WriteByte((uint32_t)NeuropixelsV1Registers::REC_MOD, (uint32_t)NeuropixelsV1RecordRegisterValues::ACTIVE); - - if (ledEnabled) - { - auto gpo23Config = DefaultGPO32Config & ~Gpo32LedMask; - serializer->WriteByte((uint32_t)DS90UB9x::DS90UB933SerializerI2CRegister::Gpio32, gpo23Config); - } - - superFrameCount = 0; - ultraFrameCount = 0; - apSampleNumber = 0; - lfpSampleNumber = 0; + apGain = getGainValue (getGainEnum (settings[0]->apGainIndex)); + lfpGain = getGainValue (getGainEnum (settings[0]->lfpGainIndex)); + + apOffsetValues.clear(); + apOffsetValues.reserve (numberOfChannels); + lfpOffsetValues.clear(); + lfpOffsetValues.reserve (numberOfChannels); + + for (int i = 0; i < numberOfChannels; i++) + { + apOffsets[i] = 0; + lfpOffsets[i] = 0; + + apOffsetValues.emplace_back (std::vector {}); + lfpOffsetValues.emplace_back (std::vector {}); + } + + lfpOffsetCalculated = false; + apOffsetCalculated = false; + + // WONTFIX: Soft reset inside settings.WriteShiftRegisters() above puts probe in reset set that + // needs to be undone here + WriteByte ((uint32_t) NeuropixelsV1Registers::OP_MODE, (uint32_t) NeuropixelsV1OperationRegisterValues::RECORD); + WriteByte ((uint32_t) NeuropixelsV1Registers::REC_MOD, (uint32_t) NeuropixelsV1RecordRegisterValues::ACTIVE); + + if (ledEnabled) + { + auto gpo23Config = DefaultGPO32Config & ~Gpo32LedMask; + serializer->WriteByte ((uint32_t) DS90UB9x::DS90UB933SerializerI2CRegister::Gpio32, gpo23Config); + } + + superFrameCount = 0; + ultraFrameCount = 0; + apSampleNumber = 0; + lfpSampleNumber = 0; } void Neuropixels1e::stopAcquisition() { - serializer->WriteByte((uint32_t)DS90UB9x::DS90UB933SerializerI2CRegister::Gpio10, DefaultGPO10Config); - serializer->WriteByte((uint32_t)DS90UB9x::DS90UB933SerializerI2CRegister::Gpio32, DefaultGPO32Config); + serializer->WriteByte ((uint32_t) DS90UB9x::DS90UB933SerializerI2CRegister::Gpio10, DefaultGPO10Config); + serializer->WriteByte ((uint32_t) DS90UB9x::DS90UB933SerializerI2CRegister::Gpio32, DefaultGPO32Config); - OnixDevice::stopAcquisition(); + OnixDevice::stopAcquisition(); } -void Neuropixels1e::addSourceBuffers(OwnedArray& sourceBuffers) +void Neuropixels1e::addSourceBuffers (OwnedArray& sourceBuffers) { - for (StreamInfo streamInfo : streamInfos) - { - sourceBuffers.add(new DataBuffer(streamInfo.getNumChannels(), (int)streamInfo.getSampleRate() * bufferSizeInSeconds)); - - if (streamInfo.getChannelPrefix() == STREAM_NAME_AP) - apBuffer = sourceBuffers.getLast(); - else if (streamInfo.getChannelPrefix() == STREAM_NAME_LFP) - lfpBuffer = sourceBuffers.getLast(); - } + for (StreamInfo streamInfo : streamInfos) + { + sourceBuffers.add (new DataBuffer (streamInfo.getNumChannels(), (int) streamInfo.getSampleRate() * bufferSizeInSeconds)); + + if (streamInfo.getChannelPrefix() == STREAM_NAME_AP) + apBuffer = sourceBuffers.getLast(); + else if (streamInfo.getChannelPrefix() == STREAM_NAME_LFP) + lfpBuffer = sourceBuffers.getLast(); + } } void Neuropixels1e::processFrames() { - const float apConversion = (1171.875 / apGain) * -1.0f; - const float lfpConversion = (1171.875 / lfpGain) * -1.0f; - - oni_frame_t* frame; - while (frameQueue.try_dequeue(frame)) - { - uint16_t* dataPtr = (uint16_t*)frame->data; - dataPtr += dataOffset; - - apTimestamps[superFrameCount] = deviceContext->convertTimestampToSeconds(frame->time); - apSampleNumbers[superFrameCount] = apSampleNumber++; - - for (size_t i = 0; i < framesPerSuperFrame; i++) - { - if (i == 0) // LFP data - { - size_t superCountOffset = superFrameCount % superFramesPerUltraFrame; - if (superCountOffset == 0) - { - lfpTimestamps[ultraFrameCount] = apTimestamps[superFrameCount]; - lfpSampleNumbers[ultraFrameCount] = lfpSampleNumber++; - } - - for (int adc = 0; adc < NeuropixelsV1Values::AdcCount; adc++) - { - auto sample = *(dataPtr + adcToFrameIndex[adc]); - sample = sample > adcValues.at(adc).threshold ? sample - adcValues.at(adc).offset : sample; - lfpSamples[(rawToChannel[adc][superCountOffset] * numUltraFrames) + ultraFrameCount] = - lfpConversion * (lfpGainCorrection * sample - DataMidpoint) - lfpOffsets.at(rawToChannel[adc][superCountOffset]); - } - } - else // AP data - { - for (int adc = 0; adc < NeuropixelsV1Values::AdcCount; adc++) - { - auto sample = *(dataPtr + adcToFrameIndex[adc] + i * NeuropixelsV1Values::FrameWordsV1e); - sample = sample > adcValues.at(adc).threshold ? sample - adcValues.at(adc).offset : sample; - apSamples[(rawToChannel[adc][i - 1] * superFramesPerUltraFrame * numUltraFrames) + superFrameCount] = - apConversion * (apGainCorrection * sample - DataMidpoint) - apOffsets.at(rawToChannel[adc][i - 1]); - } - } - } - - oni_destroy_frame(frame); - - superFrameCount++; - - if (superFrameCount % superFramesPerUltraFrame == 0) - { - ultraFrameCount++; - } - - if (ultraFrameCount >= numUltraFrames) - { - ultraFrameCount = 0; - superFrameCount = 0; - - lfpBuffer->addToBuffer(lfpSamples.data(), lfpSampleNumbers, lfpTimestamps, lfpEventCodes, numUltraFrames); - apBuffer->addToBuffer(apSamples.data(), apSampleNumbers, apTimestamps, apEventCodes, numUltraFrames * superFramesPerUltraFrame); - - if (!lfpOffsetCalculated) updateLfpOffsets(lfpSamples, lfpSampleNumbers[0]); - if (!apOffsetCalculated) updateApOffsets(apSamples, apSampleNumbers[0]); - } - } + const float apConversion = (1171.875 / apGain) * -1.0f; + const float lfpConversion = (1171.875 / lfpGain) * -1.0f; + + oni_frame_t* frame; + while (frameQueue.try_dequeue (frame)) + { + uint16_t* dataPtr = (uint16_t*) frame->data; + dataPtr += dataOffset; + + apTimestamps[superFrameCount] = deviceContext->convertTimestampToSeconds (frame->time); + apSampleNumbers[superFrameCount] = apSampleNumber++; + + for (size_t i = 0; i < framesPerSuperFrame; i++) + { + if (i == 0) // LFP data + { + size_t superCountOffset = superFrameCount % superFramesPerUltraFrame; + if (superCountOffset == 0) + { + lfpTimestamps[ultraFrameCount] = apTimestamps[superFrameCount]; + lfpSampleNumbers[ultraFrameCount] = lfpSampleNumber++; + } + + for (int adc = 0; adc < NeuropixelsV1Values::AdcCount; adc++) + { + auto sample = *(dataPtr + adcToFrameIndex[adc]); + sample = sample > adcValues.at (adc).threshold ? sample - adcValues.at (adc).offset : sample; + lfpSamples[(rawToChannel[adc][superCountOffset] * numUltraFrames) + ultraFrameCount] = + lfpConversion * (lfpGainCorrection * sample - DataMidpoint) - lfpOffsets.at (rawToChannel[adc][superCountOffset]); + } + } + else // AP data + { + for (int adc = 0; adc < NeuropixelsV1Values::AdcCount; adc++) + { + auto sample = *(dataPtr + adcToFrameIndex[adc] + i * NeuropixelsV1Values::FrameWordsV1e); + sample = sample > adcValues.at (adc).threshold ? sample - adcValues.at (adc).offset : sample; + apSamples[(rawToChannel[adc][i - 1] * superFramesPerUltraFrame * numUltraFrames) + superFrameCount] = + apConversion * (apGainCorrection * sample - DataMidpoint) - apOffsets.at (rawToChannel[adc][i - 1]); + } + } + } + + oni_destroy_frame (frame); + + superFrameCount++; + + if (superFrameCount % superFramesPerUltraFrame == 0) + { + ultraFrameCount++; + } + + if (ultraFrameCount >= numUltraFrames) + { + ultraFrameCount = 0; + superFrameCount = 0; + + lfpBuffer->addToBuffer (lfpSamples.data(), lfpSampleNumbers, lfpTimestamps, lfpEventCodes, numUltraFrames); + apBuffer->addToBuffer (apSamples.data(), apSampleNumbers, apTimestamps, apEventCodes, numUltraFrames * superFramesPerUltraFrame); + + if (! lfpOffsetCalculated) + updateLfpOffsets (lfpSamples, lfpSampleNumbers[0]); + if (! apOffsetCalculated) + updateApOffsets (apSamples, apSampleNumbers[0]); + } + } } void Neuropixels1e::writeShiftRegisters() { - if (adcValues.size() != NeuropixelsV1Values::AdcCount) - throw error_str("Invalid number of ADC values found."); - - auto shankBits = NeuropixelsV1::makeShankBits(getReference(settings[0]->referenceIndex), settings[0]->selectedElectrode); - auto configBits = NeuropixelsV1::makeConfigBits(getReference(settings[0]->referenceIndex), getGainEnum(settings[0]->apGainIndex), getGainEnum(settings[0]->lfpGainIndex), true, adcValues); - - auto shankBytes = toBitReversedBytes(shankBits); - - int rc = WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SR_LENGTH1, (uint32_t)shankBytes.size() % 0x100); - if (rc != ONI_ESUCCESS) - throw error_str("Could not set shift register length."); - - rc = WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SR_LENGTH2, (uint32_t)shankBytes.size() / 0x100); - if (rc != ONI_ESUCCESS) - throw error_str("Could not set shift register length."); - - for (auto b : shankBytes) - { - rc = WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SR_CHAIN1, b); - if (rc != ONI_ESUCCESS) - throw error_str("Could not write byte for shift register chain for shank configuration."); - } - - const uint32_t shiftRegisterSuccess = 1 << 7; - - for (int i = 0; i < configBits.size(); i++) - { - auto srAddress = i == 0 ? (uint32_t)NeuropixelsV1ShiftRegisters::SR_CHAIN2 : (uint32_t)NeuropixelsV1ShiftRegisters::SR_CHAIN3; - - for (int j = 0; j < 2; j++) - { - // WONTFIX: Without this reset, the ShiftRegisterSuccess check below will always fail - // on whatever the second shift register write sequence regardless of order or - // contents. Could be increased current draw during internal process causes MCLK - // to droop and mess up internal state. Or that MCLK is just not good enough to - // prevent metastability in some logic in the ASIC that is only entered in between - // SR accesses. - WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SOFT_RESET, 0xFF); - WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SOFT_RESET, 0x00); - - auto baseBytes = toBitReversedBytes(configBits[i]); - - rc = WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SR_LENGTH1, (uint32_t)baseBytes.size() % 0x100); - if (rc != ONI_ESUCCESS) - throw error_str("Could not set shift register length."); - - rc = WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SR_LENGTH2, (uint32_t)baseBytes.size() / 0x100); - if (rc != ONI_ESUCCESS) - throw error_str("Could not set shift register length."); - - for (auto b : baseBytes) - { - rc = WriteByte(srAddress, b); - if (rc != ONI_ESUCCESS) - throw error_str("Could not set write byte to shift register for base configuration."); - } - } - - oni_reg_val_t value; - rc = ReadByte((uint32_t)NeuropixelsV1Registers::STATUS, &value); - - if (rc != ONI_ESUCCESS || value != shiftRegisterSuccess) - { - LOGE("Shift register ", srAddress, " status check failed."); - return; - } - } + if (adcValues.size() != NeuropixelsV1Values::AdcCount) + throw error_str ("Invalid number of ADC values found."); + + auto shankBits = NeuropixelsV1::makeShankBits (getReference (settings[0]->referenceIndex), settings[0]->selectedElectrode); + auto configBits = NeuropixelsV1::makeConfigBits (getReference (settings[0]->referenceIndex), getGainEnum (settings[0]->apGainIndex), getGainEnum (settings[0]->lfpGainIndex), true, adcValues); + + auto shankBytes = toBitReversedBytes (shankBits); + + int rc = WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SR_LENGTH1, (uint32_t) shankBytes.size() % 0x100); + if (rc != ONI_ESUCCESS) + throw error_str ("Could not set shift register length."); + + rc = WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SR_LENGTH2, (uint32_t) shankBytes.size() / 0x100); + if (rc != ONI_ESUCCESS) + throw error_str ("Could not set shift register length."); + + for (auto b : shankBytes) + { + rc = WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SR_CHAIN1, b); + if (rc != ONI_ESUCCESS) + throw error_str ("Could not write byte for shift register chain for shank configuration."); + } + + const uint32_t shiftRegisterSuccess = 1 << 7; + + for (int i = 0; i < configBits.size(); i++) + { + auto srAddress = i == 0 ? (uint32_t) NeuropixelsV1ShiftRegisters::SR_CHAIN2 : (uint32_t) NeuropixelsV1ShiftRegisters::SR_CHAIN3; + + for (int j = 0; j < 2; j++) + { + // WONTFIX: Without this reset, the ShiftRegisterSuccess check below will always fail + // on whatever the second shift register write sequence regardless of order or + // contents. Could be increased current draw during internal process causes MCLK + // to droop and mess up internal state. Or that MCLK is just not good enough to + // prevent metastability in some logic in the ASIC that is only entered in between + // SR accesses. + WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SOFT_RESET, 0xFF); + WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SOFT_RESET, 0x00); + + auto baseBytes = toBitReversedBytes (configBits[i]); + + rc = WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SR_LENGTH1, (uint32_t) baseBytes.size() % 0x100); + if (rc != ONI_ESUCCESS) + throw error_str ("Could not set shift register length."); + + rc = WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SR_LENGTH2, (uint32_t) baseBytes.size() / 0x100); + if (rc != ONI_ESUCCESS) + throw error_str ("Could not set shift register length."); + + for (auto b : baseBytes) + { + rc = WriteByte (srAddress, b); + if (rc != ONI_ESUCCESS) + throw error_str ("Could not set write byte to shift register for base configuration."); + } + } + + oni_reg_val_t value; + rc = ReadByte ((uint32_t) NeuropixelsV1Registers::STATUS, &value); + + if (rc != ONI_ESUCCESS || value != shiftRegisterSuccess) + { + LOGE ("Shift register ", srAddress, " status check failed."); + return; + } + } } diff --git a/Source/Devices/Neuropixels1e.h b/Source/Devices/Neuropixels1e.h index 8641b5b..b33146d 100644 --- a/Source/Devices/Neuropixels1e.h +++ b/Source/Devices/Neuropixels1e.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -26,123 +26,146 @@ namespace OnixSourcePlugin { - /** - Configures and streams data from a Neuropixels 1.0e device - */ - class Neuropixels1e : public Neuropixels1 - { - public: - friend class NeuropixelsV1eBackgroundUpdater; - - Neuropixels1e(std::string, std::string, const oni_dev_idx_t, std::shared_ptr); - - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void stopAcquisition() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; - void processFrames() override; - - void configureSerDes(); - - static OnixDeviceType getDeviceType(); - - private: - - static constexpr char* STREAM_NAME_AP = "AP"; - static constexpr char* STREAM_NAME_LFP = "LFP"; - - static constexpr int FlexEepromI2CAddress = 0x50; - - static constexpr uint32_t OFFSET_ID = 0; - static constexpr uint32_t OFFSET_VERSION = 10; - static constexpr uint32_t OFFSET_REVISION = 11; - static constexpr uint32_t OFFSET_FLEXPN = 20; - static constexpr uint32_t OFFSET_PROBEPN = 40; - - static constexpr uint8_t DefaultGPO10Config = 0b00010001; // GPIO0 Low, NP in MUX reset - static constexpr uint8_t DefaultGPO32Config = 0b10010001; // LED off, GPIO1 Low - static constexpr uint32_t Gpo10ResetMask = 1 << 3; // Used to issue mux reset command to probe - static constexpr uint32_t Gpo32LedMask = 1 << 7; // Used to turn on and off LED - - std::unique_ptr deserializer; - std::unique_ptr serializer; - std::unique_ptr flex; - - void resetProbe(); - void writeShiftRegisters(); - - bool ledEnabled = true; - - // ADC to frame index - // Input: ADC index - // Output: index of ADC's data within a frame - static constexpr std::array adcToFrameIndex = { - 1, 9 , 17, 25, 33, - 2, 10, 18, 26, 34, - 3, 11, 19, 27, 35, - 4, 12, 20, 28, 36, - 5, 13, 21, 29, 37, - 6, 14, 22, 30, 38, - 7, 15 - }; - - // ADC to channel - // First dimension: ADC index - // Second dimension: frame index within super frame - // Output: channel number - static constexpr std::array, NeuropixelsV1Values::AdcCount> rawToChannel = { { - {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22 }, - {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 }, - {24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46 }, - {25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47 }, - {48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70 }, - {49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71 }, - {72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94 }, - {73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95 }, - {96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118 }, - {97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119 }, - {120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142 }, - {121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143 }, - {144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166 }, - {145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167 }, - {168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190 }, - {169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191 }, - {192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214 }, - {193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215 }, - {216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238 }, - {217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239 }, - {240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262 }, - {241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263 }, - {264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286 }, - {265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287 }, - {288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310 }, - {289, 291, 293, 295, 297, 299, 301, 303, 305, 307, 309, 311 }, - {312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, 334 }, - {313, 315, 317, 319, 321, 323, 325, 327, 329, 331, 333, 335 }, - {336, 338, 340, 342, 344, 346, 348, 350, 352, 354, 356, 358 }, - {337, 339, 341, 343, 345, 347, 349, 351, 353, 355, 357, 359 }, - {360, 362, 364, 366, 368, 370, 372, 374, 376, 378, 380, 382 }, - {361, 363, 365, 367, 369, 371, 373, 375, 377, 379, 381, 383 } - } }; - - JUCE_LEAK_DETECTOR(Neuropixels1e); - }; - - /* - - A thread that updates probe settings in the background and shows a progress bar - - */ - class NeuropixelsV1eBackgroundUpdater : public NeuropixelsV1BackgroundUpdater - { - public: - NeuropixelsV1eBackgroundUpdater(Neuropixels1e* d); - - void run() override; - - private: - - JUCE_LEAK_DETECTOR(NeuropixelsV1eBackgroundUpdater); - }; -} +/** + Configures and streams data from a Neuropixels 1.0e device +*/ +class Neuropixels1e : public Neuropixels1 +{ +public: + friend class NeuropixelsV1eBackgroundUpdater; + + Neuropixels1e (std::string, std::string, const oni_dev_idx_t, std::shared_ptr); + + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void stopAcquisition() override; + void addSourceBuffers (OwnedArray& sourceBuffers) override; + void processFrames() override; + + void configureSerDes(); + + static OnixDeviceType getDeviceType(); + +private: + static constexpr char* STREAM_NAME_AP = "AP"; + static constexpr char* STREAM_NAME_LFP = "LFP"; + + static constexpr int FlexEepromI2CAddress = 0x50; + + static constexpr uint32_t OFFSET_ID = 0; + static constexpr uint32_t OFFSET_VERSION = 10; + static constexpr uint32_t OFFSET_REVISION = 11; + static constexpr uint32_t OFFSET_FLEXPN = 20; + static constexpr uint32_t OFFSET_PROBEPN = 40; + + static constexpr uint8_t DefaultGPO10Config = 0b00010001; // GPIO0 Low, NP in MUX reset + static constexpr uint8_t DefaultGPO32Config = 0b10010001; // LED off, GPIO1 Low + static constexpr uint32_t Gpo10ResetMask = 1 << 3; // Used to issue mux reset command to probe + static constexpr uint32_t Gpo32LedMask = 1 << 7; // Used to turn on and off LED + + std::unique_ptr deserializer; + std::unique_ptr serializer; + std::unique_ptr flex; + + void resetProbe(); + void writeShiftRegisters(); + + bool ledEnabled = true; + + // ADC to frame index + // Input: ADC index + // Output: index of ADC's data within a frame + static constexpr std::array adcToFrameIndex = { + 1, + 9, + 17, + 25, + 33, + 2, + 10, + 18, + 26, + 34, + 3, + 11, + 19, + 27, + 35, + 4, + 12, + 20, + 28, + 36, + 5, + 13, + 21, + 29, + 37, + 6, + 14, + 22, + 30, + 38, + 7, + 15 + }; + + // ADC to channel + // First dimension: ADC index + // Second dimension: frame index within super frame + // Output: channel number + static constexpr std::array, NeuropixelsV1Values::AdcCount> rawToChannel = { + { { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22 }, + { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 }, + { 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46 }, + { 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47 }, + { 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70 }, + { 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71 }, + { 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94 }, + { 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95 }, + { 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118 }, + { 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119 }, + { 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142 }, + { 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143 }, + { 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166 }, + { 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167 }, + { 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190 }, + { 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191 }, + { 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214 }, + { 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215 }, + { 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238 }, + { 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239 }, + { 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262 }, + { 241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263 }, + { 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286 }, + { 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287 }, + { 288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310 }, + { 289, 291, 293, 295, 297, 299, 301, 303, 305, 307, 309, 311 }, + { 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, 334 }, + { 313, 315, 317, 319, 321, 323, 325, 327, 329, 331, 333, 335 }, + { 336, 338, 340, 342, 344, 346, 348, 350, 352, 354, 356, 358 }, + { 337, 339, 341, 343, 345, 347, 349, 351, 353, 355, 357, 359 }, + { 360, 362, 364, 366, 368, 370, 372, 374, 376, 378, 380, 382 }, + { 361, 363, 365, 367, 369, 371, 373, 375, 377, 379, 381, 383 } } + }; + + JUCE_LEAK_DETECTOR (Neuropixels1e); +}; + +/* + + A thread that updates probe settings in the background and shows a progress bar + +*/ +class NeuropixelsV1eBackgroundUpdater : public NeuropixelsV1BackgroundUpdater +{ +public: + NeuropixelsV1eBackgroundUpdater (Neuropixels1e* d); + + void run() override; + +private: + JUCE_LEAK_DETECTOR (NeuropixelsV1eBackgroundUpdater); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/Neuropixels1f.cpp b/Source/Devices/Neuropixels1f.cpp index 7253338..e7e77cd 100644 --- a/Source/Devices/Neuropixels1f.cpp +++ b/Source/Devices/Neuropixels1f.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,374 +24,384 @@ using namespace OnixSourcePlugin; -NeuropixelsV1fBackgroundUpdater::NeuropixelsV1fBackgroundUpdater(Neuropixels1f* d) - : NeuropixelsV1BackgroundUpdater(d) +NeuropixelsV1fBackgroundUpdater::NeuropixelsV1fBackgroundUpdater (Neuropixels1f* d) + : NeuropixelsV1BackgroundUpdater (d) { } void NeuropixelsV1fBackgroundUpdater::run() { - setProgress(0); - - if (!device->parseGainCalibrationFile()) - { - result = false; - return; - } - - if (!device->parseAdcCalibrationFile()) - { - result = false; - return; - } - - setProgress(0.5); - - try - { - ((Neuropixels1f*)device)->writeShiftRegisters(); - } - catch (const error_str& e) - { - Onix1::showWarningMessageBoxAsync("Error Writing Shift Registers", e.what()); - result = false; - return; - } - - setProgress(1); - - result = true; + setProgress (0); + + if (! device->parseGainCalibrationFile()) + { + result = false; + return; + } + + if (! device->parseAdcCalibrationFile()) + { + result = false; + return; + } + + setProgress (0.5); + + try + { + ((Neuropixels1f*) device)->writeShiftRegisters(); + } + catch (const error_str& e) + { + Onix1::showWarningMessageBoxAsync ("Error Writing Shift Registers", e.what()); + result = false; + return; + } + + setProgress (1); + + result = true; } -Neuropixels1f::Neuropixels1f(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx_) : - Neuropixels1(name, hubName, OnixDeviceType::NEUROPIXELSV1F, deviceIdx_, ctx_) +Neuropixels1f::Neuropixels1f (std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx_) + : Neuropixels1 (name, hubName, OnixDeviceType::NEUROPIXELSV1F, deviceIdx_, ctx_) { - std::string port = getPortName(deviceIdx); - StreamInfo apStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), STREAM_NAME_AP }), - "Neuropixels 1.0 AP band data stream", - getStreamIdentifier(), - numberOfChannels, - apSampleRate, - STREAM_NAME_AP, - ContinuousChannel::Type::ELECTRODE, - 0.195f, - "uV", - {}, - "ap" - ); - streamInfos.add(apStream); - - StreamInfo lfpStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), STREAM_NAME_LFP }), - "Neuropixels 1.0 LFP band data stream", - getStreamIdentifier(), - numberOfChannels, - lfpSampleRate, - STREAM_NAME_LFP, - ContinuousChannel::Type::ELECTRODE, - 0.195f, - "uV", - {}, - "lfp" - ); - streamInfos.add(lfpStream); - - defineMetadata(settings[0].get()); - - adcCalibrationFilePath = "None"; - gainCalibrationFilePath = "None"; - - for (int i = 0; i < numUltraFrames; i++) - { - apEventCodes[i] = 0; - lfpEventCodes[i] = 0; - } - - probeNumber = 0; + std::string port = getPortName (deviceIdx); + StreamInfo apStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), STREAM_NAME_AP }), + "Neuropixels 1.0 AP band data stream", + getStreamIdentifier(), + numberOfChannels, + apSampleRate, + STREAM_NAME_AP, + ContinuousChannel::Type::ELECTRODE, + 0.195f, + "uV", + {}, + "ap"); + streamInfos.add (apStream); + + StreamInfo lfpStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), STREAM_NAME_LFP }), + "Neuropixels 1.0 LFP band data stream", + getStreamIdentifier(), + numberOfChannels, + lfpSampleRate, + STREAM_NAME_LFP, + ContinuousChannel::Type::ELECTRODE, + 0.195f, + "uV", + {}, + "lfp"); + streamInfos.add (lfpStream); + + defineMetadata (settings[0].get()); + + adcCalibrationFilePath = "None"; + gainCalibrationFilePath = "None"; + + for (int i = 0; i < numUltraFrames; i++) + { + apEventCodes[i] = 0; + lfpEventCodes[i] = 0; + } + + probeNumber = 0; } int Neuropixels1f::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); - - int rc = deviceContext->writeRegister(deviceIdx, ENABLE, isEnabled() ? 1 : 0); - if (rc != ONI_ESUCCESS) - throw error_str("Unable to enable " + getName()); - - if (!isEnabled()) - { - return ONI_ESUCCESS; - } - - // Get Probe SN - uint32_t eepromOffset = 0; - uint32_t i2cAddr = 0x50; - int errorCode = 0; - - for (int i = 0; i < 8; i++) - { - oni_reg_addr_t reg_addr = ((eepromOffset + i) << 7) | i2cAddr; - - oni_reg_val_t reg_val; - rc = deviceContext->readRegister(deviceIdx, reg_addr, ®_val); - - if (rc != ONI_ESUCCESS) - { - LOGE(oni_error_str(rc)); - throw error_str("Could not communicate with " + getName() + " on " + getHubName() + ". Ensure that the flex connection is properly seated, or disable the device if it is not connected."); - } - - if (reg_val <= 0xFF) - { - probeNumber |= (((uint64_t)reg_val) << (i * 8)); - } - } - - LOGD("Probe SN: ", probeNumber); - - // Enable device streaming - rc = deviceContext->writeRegister(deviceIdx, 0x8000, 1); - if (rc != ONI_ESUCCESS) - throw error_str("Unable to activate streaming for device at address " + std::to_string(deviceIdx)); - - rc = WriteByte((uint32_t)NeuropixelsV1Registers::CAL_MOD, (uint32_t)NeuropixelsV1CalibrationRegisterValues::CAL_OFF); - if (rc != ONI_ESUCCESS) - throw error_str("Error configuring device at address " + std::to_string(deviceIdx)); - - rc = WriteByte((uint32_t)NeuropixelsV1Registers::SYNC, (uint32_t)0); - if (rc != ONI_ESUCCESS) - throw error_str("Error configuring device at address " + std::to_string(deviceIdx)); - - rc = WriteByte((uint32_t)NeuropixelsV1Registers::REC_MOD, (uint32_t)NeuropixelsV1RecordRegisterValues::DIG_CH_RESET); - if (rc != ONI_ESUCCESS) - throw error_str("Error configuring device at address " + std::to_string(deviceIdx)); - - rc = WriteByte((uint32_t)NeuropixelsV1Registers::OP_MODE, (uint32_t)NeuropixelsV1OperationRegisterValues::RECORD); - if (rc != ONI_ESUCCESS) - throw error_str("Error configuring device at address " + std::to_string(deviceIdx)); - - return rc; + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + throw error_str ("Device context is not initialized properly for " + getName()); + + int rc = deviceContext->writeRegister (deviceIdx, ENABLE, isEnabled() ? 1 : 0); + if (rc != ONI_ESUCCESS) + throw error_str ("Unable to enable " + getName()); + + if (! isEnabled()) + { + return ONI_ESUCCESS; + } + + // Get Probe SN + uint32_t eepromOffset = 0; + uint32_t i2cAddr = 0x50; + int errorCode = 0; + + for (int i = 0; i < 8; i++) + { + oni_reg_addr_t reg_addr = ((eepromOffset + i) << 7) | i2cAddr; + + oni_reg_val_t reg_val; + rc = deviceContext->readRegister (deviceIdx, reg_addr, ®_val); + + if (rc != ONI_ESUCCESS) + { + LOGE (oni_error_str (rc)); + throw error_str ("Could not communicate with " + getName() + " on " + getHubName() + + ". Ensure that the flex connection is properly seated, or disable the device if it is not connected."); + } + + if (reg_val <= 0xFF) + { + probeNumber |= (((uint64_t) reg_val) << (i * 8)); + } + } + + LOGD ("Probe SN: ", probeNumber); + + // Enable device streaming + rc = deviceContext->writeRegister (deviceIdx, 0x8000, 1); + if (rc != ONI_ESUCCESS) + throw error_str ("Unable to activate streaming for device at address " + std::to_string (deviceIdx)); + + rc = WriteByte ((uint32_t) NeuropixelsV1Registers::CAL_MOD, (uint32_t) NeuropixelsV1CalibrationRegisterValues::CAL_OFF); + if (rc != ONI_ESUCCESS) + throw error_str ("Error configuring device at address " + std::to_string (deviceIdx)); + + rc = WriteByte ((uint32_t) NeuropixelsV1Registers::SYNC, (uint32_t) 0); + if (rc != ONI_ESUCCESS) + throw error_str ("Error configuring device at address " + std::to_string (deviceIdx)); + + rc = WriteByte ((uint32_t) NeuropixelsV1Registers::REC_MOD, (uint32_t) NeuropixelsV1RecordRegisterValues::DIG_CH_RESET); + if (rc != ONI_ESUCCESS) + throw error_str ("Error configuring device at address " + std::to_string (deviceIdx)); + + rc = WriteByte ((uint32_t) NeuropixelsV1Registers::OP_MODE, (uint32_t) NeuropixelsV1OperationRegisterValues::RECORD); + if (rc != ONI_ESUCCESS) + throw error_str ("Error configuring device at address " + std::to_string (deviceIdx)); + + return rc; } bool Neuropixels1f::updateSettings() { - auto updater = NeuropixelsV1fBackgroundUpdater(this); + auto updater = NeuropixelsV1fBackgroundUpdater (this); - return updater.updateSettings() && adcValues.size() == NeuropixelsV1Values::AdcCount; + return updater.updateSettings() && adcValues.size() == NeuropixelsV1Values::AdcCount; } OnixDeviceType Neuropixels1f::getDeviceType() { - return OnixDeviceType::NEUROPIXELSV1F; + return OnixDeviceType::NEUROPIXELSV1F; } void Neuropixels1f::startAcquisition() { - apGain = getGainValue(getGainEnum(settings[0]->apGainIndex)); - lfpGain = getGainValue(getGainEnum(settings[0]->lfpGainIndex)); + apGain = getGainValue (getGainEnum (settings[0]->apGainIndex)); + lfpGain = getGainValue (getGainEnum (settings[0]->lfpGainIndex)); - apOffsetValues.clear(); - apOffsetValues.reserve(numberOfChannels); - lfpOffsetValues.clear(); - lfpOffsetValues.reserve(numberOfChannels); + apOffsetValues.clear(); + apOffsetValues.reserve (numberOfChannels); + lfpOffsetValues.clear(); + lfpOffsetValues.reserve (numberOfChannels); - for (int i = 0; i < numberOfChannels; i++) - { - apOffsets[i] = 0; - lfpOffsets[i] = 0; + for (int i = 0; i < numberOfChannels; i++) + { + apOffsets[i] = 0; + lfpOffsets[i] = 0; - apOffsetValues.emplace_back(std::vector{}); - lfpOffsetValues.emplace_back(std::vector{}); - } + apOffsetValues.emplace_back (std::vector {}); + lfpOffsetValues.emplace_back (std::vector {}); + } - lfpOffsetCalculated = false; - apOffsetCalculated = false; + lfpOffsetCalculated = false; + apOffsetCalculated = false; - WriteByte((uint32_t)NeuropixelsV1Registers::REC_MOD, (uint32_t)NeuropixelsV1RecordRegisterValues::ACTIVE); + WriteByte ((uint32_t) NeuropixelsV1Registers::REC_MOD, (uint32_t) NeuropixelsV1RecordRegisterValues::ACTIVE); - superFrameCount = 0; - ultraFrameCount = 0; - apSampleNumber = 0; - lfpSampleNumber = 0; + superFrameCount = 0; + ultraFrameCount = 0; + apSampleNumber = 0; + lfpSampleNumber = 0; } void Neuropixels1f::stopAcquisition() { - WriteByte((uint32_t)NeuropixelsV1Registers::REC_MOD, (uint32_t)NeuropixelsV1RecordRegisterValues::RESET_ALL); + WriteByte ((uint32_t) NeuropixelsV1Registers::REC_MOD, (uint32_t) NeuropixelsV1RecordRegisterValues::RESET_ALL); - OnixDevice::stopAcquisition(); + OnixDevice::stopAcquisition(); } -void Neuropixels1f::addSourceBuffers(OwnedArray& sourceBuffers) +void Neuropixels1f::addSourceBuffers (OwnedArray& sourceBuffers) { - for (StreamInfo streamInfo : streamInfos) - { - sourceBuffers.add(new DataBuffer(streamInfo.getNumChannels(), (int)streamInfo.getSampleRate() * bufferSizeInSeconds)); - - if (streamInfo.getChannelPrefix() == STREAM_NAME_AP) - apBuffer = sourceBuffers.getLast(); - else if (streamInfo.getChannelPrefix() == STREAM_NAME_LFP) - lfpBuffer = sourceBuffers.getLast(); - } + for (StreamInfo streamInfo : streamInfos) + { + sourceBuffers.add (new DataBuffer (streamInfo.getNumChannels(), (int) streamInfo.getSampleRate() * bufferSizeInSeconds)); + + if (streamInfo.getChannelPrefix() == STREAM_NAME_AP) + apBuffer = sourceBuffers.getLast(); + else if (streamInfo.getChannelPrefix() == STREAM_NAME_LFP) + lfpBuffer = sourceBuffers.getLast(); + } } void Neuropixels1f::processFrames() { - const float apConversion = (1171.875 / apGain) * -1.0f; - const float lfpConversion = (1171.875 / lfpGain) * -1.0f; - - oni_frame_t* frame; - while (frameQueue.try_dequeue(frame)) - { - uint16_t* dataPtr = (uint16_t*)frame->data; - - apTimestamps[superFrameCount] = deviceContext->convertTimestampToSeconds(frame->time); - apSampleNumbers[superFrameCount] = apSampleNumber++; - - for (int i = 0; i < framesPerSuperFrame; i++) - { - if (i == 0) // LFP data - { - size_t superCountOffset = superFrameCount % superFramesPerUltraFrame; - if (superCountOffset == 0) - { - lfpTimestamps[ultraFrameCount] = apTimestamps[superFrameCount]; - lfpSampleNumbers[ultraFrameCount] = lfpSampleNumber++; - } - - for (int adc = 0; adc < NeuropixelsV1Values::AdcCount; adc++) - { - size_t chanIndex = adcToChannel[adc] + superCountOffset * 2; - lfpSamples[(chanIndex * numUltraFrames) + ultraFrameCount] = - lfpConversion * (float(*(dataPtr + adcToFrameIndex[adc]) >> 5) - DataMidpoint) - lfpOffsets.at(chanIndex); - } - } - else // AP data - { - int chanOffset = 2 * (i - 1); - for (int adc = 0; adc < NeuropixelsV1Values::AdcCount; adc++) - { - size_t chanIndex = adcToChannel[adc] + chanOffset; - apSamples[(chanIndex * superFramesPerUltraFrame * numUltraFrames) + superFrameCount] = - apConversion * (float(*(dataPtr + adcToFrameIndex[adc] + i * NeuropixelsV1Values::FrameWordsV1f) >> 5) - DataMidpoint) - apOffsets.at(chanIndex); - } - } - } - - oni_destroy_frame(frame); - - superFrameCount++; - - if (superFrameCount % superFramesPerUltraFrame == 0) - { - ultraFrameCount++; - } - - if (ultraFrameCount >= numUltraFrames) - { - ultraFrameCount = 0; - superFrameCount = 0; - - lfpBuffer->addToBuffer(lfpSamples.data(), lfpSampleNumbers, lfpTimestamps, lfpEventCodes, numUltraFrames); - apBuffer->addToBuffer(apSamples.data(), apSampleNumbers, apTimestamps, apEventCodes, numUltraFrames * superFramesPerUltraFrame); - - if (!lfpOffsetCalculated) updateLfpOffsets(lfpSamples, lfpSampleNumbers[0]); - if (!apOffsetCalculated) updateApOffsets(apSamples, apSampleNumbers[0]); - } - } + const float apConversion = (1171.875 / apGain) * -1.0f; + const float lfpConversion = (1171.875 / lfpGain) * -1.0f; + + oni_frame_t* frame; + while (frameQueue.try_dequeue (frame)) + { + uint16_t* dataPtr = (uint16_t*) frame->data; + + apTimestamps[superFrameCount] = deviceContext->convertTimestampToSeconds (frame->time); + apSampleNumbers[superFrameCount] = apSampleNumber++; + + for (int i = 0; i < framesPerSuperFrame; i++) + { + if (i == 0) // LFP data + { + size_t superCountOffset = superFrameCount % superFramesPerUltraFrame; + if (superCountOffset == 0) + { + lfpTimestamps[ultraFrameCount] = apTimestamps[superFrameCount]; + lfpSampleNumbers[ultraFrameCount] = lfpSampleNumber++; + } + + for (int adc = 0; adc < NeuropixelsV1Values::AdcCount; adc++) + { + size_t chanIndex = adcToChannel[adc] + superCountOffset * 2; + lfpSamples[(chanIndex * numUltraFrames) + ultraFrameCount] = + lfpConversion * (float (*(dataPtr + adcToFrameIndex[adc]) >> 5) - DataMidpoint) - lfpOffsets.at (chanIndex); + } + } + else // AP data + { + int chanOffset = 2 * (i - 1); + for (int adc = 0; adc < NeuropixelsV1Values::AdcCount; adc++) + { + size_t chanIndex = adcToChannel[adc] + chanOffset; + apSamples[(chanIndex * superFramesPerUltraFrame * numUltraFrames) + superFrameCount] = + apConversion * (float (*(dataPtr + adcToFrameIndex[adc] + i * NeuropixelsV1Values::FrameWordsV1f) >> 5) - DataMidpoint) - apOffsets.at (chanIndex); + } + } + } + + oni_destroy_frame (frame); + + superFrameCount++; + + if (superFrameCount % superFramesPerUltraFrame == 0) + { + ultraFrameCount++; + } + + if (ultraFrameCount >= numUltraFrames) + { + ultraFrameCount = 0; + superFrameCount = 0; + + lfpBuffer->addToBuffer (lfpSamples.data(), lfpSampleNumbers, lfpTimestamps, lfpEventCodes, numUltraFrames); + apBuffer->addToBuffer (apSamples.data(), apSampleNumbers, apTimestamps, apEventCodes, numUltraFrames * superFramesPerUltraFrame); + + if (! lfpOffsetCalculated) + updateLfpOffsets (lfpSamples, lfpSampleNumbers[0]); + if (! apOffsetCalculated) + updateApOffsets (apSamples, apSampleNumbers[0]); + } + } } void Neuropixels1f::writeShiftRegisters() { - if (adcValues.size() != NeuropixelsV1Values::AdcCount) - throw error_str("Invalid number of ADC values found."); - - auto shankBits = NeuropixelsV1::makeShankBits(getReference(settings[0]->referenceIndex), settings[0]->selectedElectrode); - auto configBits = NeuropixelsV1::makeConfigBits(getReference(settings[0]->referenceIndex), getGainEnum(settings[0]->apGainIndex), getGainEnum(settings[0]->lfpGainIndex), true, adcValues); - - auto shankBytes = toBitReversedBytes(shankBits); - - int rc = WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SR_LENGTH1, (uint32_t)shankBytes.size() % 0x100); - if (rc != ONI_ESUCCESS) return; - - rc = WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SR_LENGTH2, (uint32_t)shankBytes.size() / 0x100); - if (rc != ONI_ESUCCESS) return; - - for (auto b : shankBytes) - { - rc = WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SR_CHAIN1, b); - if (rc != ONI_ESUCCESS) return; - } - - const uint32_t shiftRegisterSuccess = 1 << 7; - - for (int i = 0; i < configBits.size(); i++) - { - auto srAddress = i == 0 ? (uint32_t)NeuropixelsV1ShiftRegisters::SR_CHAIN2 : (uint32_t)NeuropixelsV1ShiftRegisters::SR_CHAIN3; - - for (int j = 0; j < 2; j++) - { - auto baseBytes = toBitReversedBytes(configBits[i]); - - rc = WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SR_LENGTH1, (uint32_t)baseBytes.size() % 0x100); - if (rc != ONI_ESUCCESS) return; - - rc = WriteByte((uint32_t)NeuropixelsV1ShiftRegisters::SR_LENGTH2, (uint32_t)baseBytes.size() / 0x100); - if (rc != ONI_ESUCCESS) return; - - for (auto b : baseBytes) - { - rc = WriteByte(srAddress, b); - if (rc != ONI_ESUCCESS) return; - } - } - - oni_reg_val_t value; - rc = ReadByte((uint32_t)NeuropixelsV1Registers::STATUS, &value); - - if (rc != ONI_ESUCCESS || value != shiftRegisterSuccess) - { - LOGE("Shift register ", srAddress, " status check failed."); - return; - } - } - - const uint32_t ADC01_00_OFF_THRESH = 0x8001; - - for (size_t i = 0; i < adcValues.size(); i += 2) - { - auto value = (uint32_t)(adcValues.at(i + 1).offset << 26 | adcValues.at(i + 1).threshold << 16 | adcValues.at(i).offset << 10 | adcValues.at(i).threshold); - rc = deviceContext->writeRegister(deviceIdx, ADC01_00_OFF_THRESH + i, value); - - if (rc != ONI_ESUCCESS) - { - LOGE("Error writing to register ", ADC01_00_OFF_THRESH + i, "."); - return; - } - } - - auto fixedPointLfPGain = (uint32_t)(lfpGainCorrection * (1 << 14)) & 0xFFFF; - auto fixedPointApGain = (uint32_t)(apGainCorrection * (1 << 14)) & 0xFFFF; - - const uint32_t CHAN001_000_LFPGAIN = 0x8011; - const uint32_t CHAN001_000_APGAIN = 0x80D1; - - for (uint32_t i = 0; i < numberOfChannels / 2; i++) - { - rc = deviceContext->writeRegister(deviceIdx, CHAN001_000_LFPGAIN + i, fixedPointLfPGain << 16 | fixedPointLfPGain); - if (rc != ONI_ESUCCESS) - { - LOGE("Error writing to register ", CHAN001_000_LFPGAIN + i, "."); - return; - } - - rc = deviceContext->writeRegister(deviceIdx, CHAN001_000_APGAIN + i, fixedPointApGain << 16 | fixedPointApGain); - if (rc != ONI_ESUCCESS) - { - LOGE("Error writing to register ", CHAN001_000_APGAIN + i, "."); - return; - } - } + if (adcValues.size() != NeuropixelsV1Values::AdcCount) + throw error_str ("Invalid number of ADC values found."); + + auto shankBits = NeuropixelsV1::makeShankBits (getReference (settings[0]->referenceIndex), settings[0]->selectedElectrode); + auto configBits = NeuropixelsV1::makeConfigBits (getReference (settings[0]->referenceIndex), getGainEnum (settings[0]->apGainIndex), getGainEnum (settings[0]->lfpGainIndex), true, adcValues); + + auto shankBytes = toBitReversedBytes (shankBits); + + int rc = WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SR_LENGTH1, (uint32_t) shankBytes.size() % 0x100); + if (rc != ONI_ESUCCESS) + return; + + rc = WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SR_LENGTH2, (uint32_t) shankBytes.size() / 0x100); + if (rc != ONI_ESUCCESS) + return; + + for (auto b : shankBytes) + { + rc = WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SR_CHAIN1, b); + if (rc != ONI_ESUCCESS) + return; + } + + const uint32_t shiftRegisterSuccess = 1 << 7; + + for (int i = 0; i < configBits.size(); i++) + { + auto srAddress = i == 0 ? (uint32_t) NeuropixelsV1ShiftRegisters::SR_CHAIN2 : (uint32_t) NeuropixelsV1ShiftRegisters::SR_CHAIN3; + + for (int j = 0; j < 2; j++) + { + auto baseBytes = toBitReversedBytes (configBits[i]); + + rc = WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SR_LENGTH1, (uint32_t) baseBytes.size() % 0x100); + if (rc != ONI_ESUCCESS) + return; + + rc = WriteByte ((uint32_t) NeuropixelsV1ShiftRegisters::SR_LENGTH2, (uint32_t) baseBytes.size() / 0x100); + if (rc != ONI_ESUCCESS) + return; + + for (auto b : baseBytes) + { + rc = WriteByte (srAddress, b); + if (rc != ONI_ESUCCESS) + return; + } + } + + oni_reg_val_t value; + rc = ReadByte ((uint32_t) NeuropixelsV1Registers::STATUS, &value); + + if (rc != ONI_ESUCCESS || value != shiftRegisterSuccess) + { + LOGE ("Shift register ", srAddress, " status check failed."); + return; + } + } + + const uint32_t ADC01_00_OFF_THRESH = 0x8001; + + for (size_t i = 0; i < adcValues.size(); i += 2) + { + auto value = (uint32_t) (adcValues.at (i + 1).offset << 26 + | adcValues.at (i + 1).threshold << 16 + | adcValues.at (i).offset << 10 + | adcValues.at (i).threshold); + rc = deviceContext->writeRegister (deviceIdx, ADC01_00_OFF_THRESH + i, value); + + if (rc != ONI_ESUCCESS) + { + LOGE ("Error writing to register ", ADC01_00_OFF_THRESH + i, "."); + return; + } + } + + auto fixedPointLfPGain = (uint32_t) (lfpGainCorrection * (1 << 14)) & 0xFFFF; + auto fixedPointApGain = (uint32_t) (apGainCorrection * (1 << 14)) & 0xFFFF; + + const uint32_t CHAN001_000_LFPGAIN = 0x8011; + const uint32_t CHAN001_000_APGAIN = 0x80D1; + + for (uint32_t i = 0; i < numberOfChannels / 2; i++) + { + rc = deviceContext->writeRegister (deviceIdx, CHAN001_000_LFPGAIN + i, fixedPointLfPGain << 16 | fixedPointLfPGain); + if (rc != ONI_ESUCCESS) + { + LOGE ("Error writing to register ", CHAN001_000_LFPGAIN + i, "."); + return; + } + + rc = deviceContext->writeRegister (deviceIdx, CHAN001_000_APGAIN + i, fixedPointApGain << 16 | fixedPointApGain); + if (rc != ONI_ESUCCESS) + { + LOGE ("Error writing to register ", CHAN001_000_APGAIN + i, "."); + return; + } + } } diff --git a/Source/Devices/Neuropixels1f.h b/Source/Devices/Neuropixels1f.h index b217a65..b7daad3 100644 --- a/Source/Devices/Neuropixels1f.h +++ b/Source/Devices/Neuropixels1f.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -26,66 +26,117 @@ namespace OnixSourcePlugin { - /** - Configures and streams data from a Neuropixels 1.0f device - */ - class Neuropixels1f : public Neuropixels1 - { - public: - Neuropixels1f(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr); - - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void stopAcquisition() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; - void processFrames() override; - - void writeShiftRegisters(); - - static OnixDeviceType getDeviceType(); - - private: - - static constexpr char* STREAM_NAME_AP = "AP"; - static constexpr char* STREAM_NAME_LFP = "LFP"; - - // ADC number to frame index mapping - static constexpr int adcToFrameIndex[] = { - 0, 7 , 14, 21, 28, - 1, 8 , 15, 22, 29, - 2, 9 , 16, 23, 30, - 3, 10, 17, 24, 31, - 4, 11, 18, 25, 32, - 5, 12, 19, 26, 33, - 6, 13 - }; - - // ADC to muxed channel mapping - static constexpr size_t adcToChannel[] = { - 0, 1, 24, 25, 48, 49, 72, 73, 96, 97, - 120, 121, 144, 145, 168, 169, 192, 193, - 216, 217, 240, 241, 264, 265, 288, 289, - 312, 313, 336, 337, 360, 361 - }; - - JUCE_LEAK_DETECTOR(Neuropixels1f); - }; - - /* - - A thread that updates probe settings in the background and shows a progress bar - - */ - class NeuropixelsV1fBackgroundUpdater : public NeuropixelsV1BackgroundUpdater - { - public: - NeuropixelsV1fBackgroundUpdater(Neuropixels1f* d); - - void run() override; - - private: - - JUCE_LEAK_DETECTOR(NeuropixelsV1fBackgroundUpdater); - }; -} +/** + Configures and streams data from a Neuropixels 1.0f device +*/ +class Neuropixels1f : public Neuropixels1 +{ +public: + Neuropixels1f (std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr); + + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void stopAcquisition() override; + void addSourceBuffers (OwnedArray& sourceBuffers) override; + void processFrames() override; + + void writeShiftRegisters(); + + static OnixDeviceType getDeviceType(); + +private: + static constexpr char* STREAM_NAME_AP = "AP"; + static constexpr char* STREAM_NAME_LFP = "LFP"; + + // ADC number to frame index mapping + static constexpr int adcToFrameIndex[] = { + 0, + 7, + 14, + 21, + 28, + 1, + 8, + 15, + 22, + 29, + 2, + 9, + 16, + 23, + 30, + 3, + 10, + 17, + 24, + 31, + 4, + 11, + 18, + 25, + 32, + 5, + 12, + 19, + 26, + 33, + 6, + 13 + }; + + // ADC to muxed channel mapping + static constexpr size_t adcToChannel[] = { + 0, + 1, + 24, + 25, + 48, + 49, + 72, + 73, + 96, + 97, + 120, + 121, + 144, + 145, + 168, + 169, + 192, + 193, + 216, + 217, + 240, + 241, + 264, + 265, + 288, + 289, + 312, + 313, + 336, + 337, + 360, + 361 + }; + + JUCE_LEAK_DETECTOR (Neuropixels1f); +}; + +/* + + A thread that updates probe settings in the background and shows a progress bar + +*/ +class NeuropixelsV1fBackgroundUpdater : public NeuropixelsV1BackgroundUpdater +{ +public: + NeuropixelsV1fBackgroundUpdater (Neuropixels1f* d); + + void run() override; + +private: + JUCE_LEAK_DETECTOR (NeuropixelsV1fBackgroundUpdater); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/Neuropixels2e.cpp b/Source/Devices/Neuropixels2e.cpp index 5a62282..fbce27b 100644 --- a/Source/Devices/Neuropixels2e.cpp +++ b/Source/Devices/Neuropixels2e.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,1067 +24,1068 @@ using namespace OnixSourcePlugin; -Neuropixels2e::Neuropixels2e(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx_) : - OnixDevice(name, hubName, Neuropixels2e::getDeviceType(), deviceIdx_, ctx_, true), - I2CRegisterContext(ProbeI2CAddress, deviceIdx_, ctx_), - INeuropixel(NeuropixelsV2eValues::numberOfSettings, NeuropixelsV2eValues::numberOfShanks) +Neuropixels2e::Neuropixels2e (std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx_) + : OnixDevice (name, hubName, Neuropixels2e::getDeviceType(), deviceIdx_, ctx_, true), + I2CRegisterContext (ProbeI2CAddress, deviceIdx_, ctx_), + INeuropixel (NeuropixelsV2eValues::numberOfSettings, NeuropixelsV2eValues::numberOfShanks) { - probeSN.fill(0); - frameCount.fill(0); - sampleNumber.fill(0); + probeSN.fill (0); + frameCount.fill (0); + sampleNumber.fill (0); - for (int i = 0; i < NeuropixelsV2eValues::numberOfSettings; i++) - { - defineMetadata(settings[i].get(), NeuropixelsV2eValues::numberOfShanks); - } + for (int i = 0; i < NeuropixelsV2eValues::numberOfSettings; i++) + { + defineMetadata (settings[i].get(), NeuropixelsV2eValues::numberOfShanks); + } - for (int i = 0; i < NumberOfProbes; i++) - eventCodes[i].fill(0); + for (int i = 0; i < NumberOfProbes; i++) + eventCodes[i].fill (0); } Neuropixels2e::~Neuropixels2e() { - if (serializer != nullptr) - { - selectProbe(NoProbeSelected); - serializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10, DefaultGPO10Config); - } - - if (deviceContext != nullptr && deviceContext->isInitialized()) - deviceContext->setOption(ONIX_OPT_PASSTHROUGH, 0); + if (serializer != nullptr) + { + selectProbe (NoProbeSelected); + serializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10, DefaultGPO10Config); + } + + if (deviceContext != nullptr && deviceContext->isInitialized()) + deviceContext->setOption (ONIX_OPT_PASSTHROUGH, 0); } -void Neuropixels2e::createDataStream(int n) +void Neuropixels2e::createDataStream (int n) { - StreamInfo apStream = StreamInfo( - OnixDevice::createStreamName({ getPortName(getDeviceIdx()), getHubName(), "Probe" + std::to_string(n) }), - "Neuropixels 2.0 data stream", - getStreamIdentifier(), - numberOfChannels, - sampleRate, - "CH", - ContinuousChannel::Type::ELECTRODE, - 0.195f, - "uV", - {}, - "ap" - ); - streamInfos.add(apStream); + StreamInfo apStream = StreamInfo ( + OnixDevice::createStreamName ({ getPortName (getDeviceIdx()), getHubName(), "Probe" + std::to_string (n) }), + "Neuropixels 2.0 data stream", + getStreamIdentifier(), + numberOfChannels, + sampleRate, + "CH", + ContinuousChannel::Type::ELECTRODE, + 0.195f, + "uV", + {}, + "ap"); + streamInfos.add (apStream); } int Neuropixels2e::getNumProbes() const { - return m_numProbes; + return m_numProbes; } -void Neuropixels2e::selectElectrodesInRange(std::vector& selection, int startIndex, int numberOfElectrodes) +void Neuropixels2e::selectElectrodesInRange (std::vector& selection, int startIndex, int numberOfElectrodes) { - for (int i = startIndex; i < startIndex + numberOfElectrodes; i++) - selection.emplace_back(i); + for (int i = startIndex; i < startIndex + numberOfElectrodes; i++) + selection.emplace_back (i); } -void Neuropixels2e::selectElectrodesAcrossShanks(std::vector& selection, int startIndex, int numberOfElectrodes) +void Neuropixels2e::selectElectrodesAcrossShanks (std::vector& selection, int startIndex, int numberOfElectrodes) { - for (int shank = 0; shank < 4; shank++) - { - auto shankOffset = NeuropixelsV2eValues::electrodesPerShank * shank; - for (int i = startIndex + shankOffset; i < startIndex + numberOfElectrodes + shankOffset; i++) - { - selection.emplace_back(i); - } - } + for (int shank = 0; shank < 4; shank++) + { + auto shankOffset = NeuropixelsV2eValues::electrodesPerShank * shank; + for (int i = startIndex + shankOffset; i < startIndex + numberOfElectrodes + shankOffset; i++) + { + selection.emplace_back (i); + } + } } -std::vector Neuropixels2e::selectElectrodeConfiguration(int electrodeConfigurationIndex) +std::vector Neuropixels2e::selectElectrodeConfiguration (int electrodeConfigurationIndex) { - static int numberOfElectrodesAcrossShanks = 96; - - std::vector selection; - - if (numberOfShanks == 1) - { - if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationSingleShank::BankA) - { - selectElectrodesInRange(selection, 0, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationSingleShank::BankB) - { - selectElectrodesInRange(selection, numberOfChannels, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationSingleShank::BankC) - { - selectElectrodesInRange(selection, numberOfChannels * 2, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationSingleShank::BankD) - { - static int32_t bankDOffset = 896; - selectElectrodesInRange(selection, bankDOffset, numberOfChannels); - } - } - else if (numberOfShanks == 4) - { - if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank1BankA) - { - selectElectrodesInRange(selection, 0, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank1BankB) - { - selectElectrodesInRange(selection, numberOfChannels, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank1BankC) - { - selectElectrodesInRange(selection, numberOfChannels * 2, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank2BankA) - { - selectElectrodesInRange(selection, NeuropixelsV2eValues::electrodesPerShank, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank2BankB) - { - selectElectrodesInRange(selection, NeuropixelsV2eValues::electrodesPerShank + numberOfChannels, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank2BankC) - { - selectElectrodesInRange(selection, NeuropixelsV2eValues::electrodesPerShank + numberOfChannels * 2, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank3BankA) - { - selectElectrodesInRange(selection, NeuropixelsV2eValues::electrodesPerShank * 2, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank3BankB) - { - selectElectrodesInRange(selection, NeuropixelsV2eValues::electrodesPerShank * 2 + numberOfChannels, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank3BankC) - { - selectElectrodesInRange(selection, NeuropixelsV2eValues::electrodesPerShank * 2 + numberOfChannels * 2, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank4BankA) - { - selectElectrodesInRange(selection, NeuropixelsV2eValues::electrodesPerShank * 3, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank4BankB) - { - selectElectrodesInRange(selection, NeuropixelsV2eValues::electrodesPerShank * 3 + numberOfChannels, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::Shank4BankC) - { - selectElectrodesInRange(selection, NeuropixelsV2eValues::electrodesPerShank * 3 + numberOfChannels * 2, numberOfChannels); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks1To96) - { - selectElectrodesAcrossShanks(selection, 0, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks97To192) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks193To288) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 2, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks289To384) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 3, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks385To480) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 4, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks481To576) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 5, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks577To672) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 6, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks673To768) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 7, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks769To864) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 8, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks865To960) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 9, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks961To1056) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 10, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks1057To1152) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 11, numberOfElectrodesAcrossShanks); - } - else if (electrodeConfigurationIndex == (int32_t)ElectrodeConfigurationQuadShank::AllShanks1153To1248) - { - selectElectrodesAcrossShanks(selection, numberOfElectrodesAcrossShanks * 12, numberOfElectrodesAcrossShanks); - } - } - - return selection; + static int numberOfElectrodesAcrossShanks = 96; + + std::vector selection; + + if (numberOfShanks == 1) + { + if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationSingleShank::BankA) + { + selectElectrodesInRange (selection, 0, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationSingleShank::BankB) + { + selectElectrodesInRange (selection, numberOfChannels, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationSingleShank::BankC) + { + selectElectrodesInRange (selection, numberOfChannels * 2, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationSingleShank::BankD) + { + static int32_t bankDOffset = 896; + selectElectrodesInRange (selection, bankDOffset, numberOfChannels); + } + } + else if (numberOfShanks == 4) + { + if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank1BankA) + { + selectElectrodesInRange (selection, 0, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank1BankB) + { + selectElectrodesInRange (selection, numberOfChannels, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank1BankC) + { + selectElectrodesInRange (selection, numberOfChannels * 2, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank2BankA) + { + selectElectrodesInRange (selection, NeuropixelsV2eValues::electrodesPerShank, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank2BankB) + { + selectElectrodesInRange (selection, NeuropixelsV2eValues::electrodesPerShank + numberOfChannels, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank2BankC) + { + selectElectrodesInRange (selection, NeuropixelsV2eValues::electrodesPerShank + numberOfChannels * 2, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank3BankA) + { + selectElectrodesInRange (selection, NeuropixelsV2eValues::electrodesPerShank * 2, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank3BankB) + { + selectElectrodesInRange (selection, NeuropixelsV2eValues::electrodesPerShank * 2 + numberOfChannels, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank3BankC) + { + selectElectrodesInRange (selection, NeuropixelsV2eValues::electrodesPerShank * 2 + numberOfChannels * 2, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank4BankA) + { + selectElectrodesInRange (selection, NeuropixelsV2eValues::electrodesPerShank * 3, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank4BankB) + { + selectElectrodesInRange (selection, NeuropixelsV2eValues::electrodesPerShank * 3 + numberOfChannels, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::Shank4BankC) + { + selectElectrodesInRange (selection, NeuropixelsV2eValues::electrodesPerShank * 3 + numberOfChannels * 2, numberOfChannels); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks1To96) + { + selectElectrodesAcrossShanks (selection, 0, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks97To192) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks193To288) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 2, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks289To384) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 3, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks385To480) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 4, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks481To576) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 5, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks577To672) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 6, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks673To768) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 7, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks769To864) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 8, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks865To960) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 9, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks961To1056) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 10, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks1057To1152) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 11, numberOfElectrodesAcrossShanks); + } + else if (electrodeConfigurationIndex == (int32_t) ElectrodeConfigurationQuadShank::AllShanks1153To1248) + { + selectElectrodesAcrossShanks (selection, numberOfElectrodesAcrossShanks * 12, numberOfElectrodesAcrossShanks); + } + } + + return selection; } -uint64_t Neuropixels2e::getProbeSerialNumber(int index) +uint64_t Neuropixels2e::getProbeSerialNumber (int index) { - try { - return probeSN.at(index); - } - catch (const std::out_of_range& ex) // filter for out of range - { - LOGE("Invalid index given requesting probe serial number."); - } - - return 0ull; + try + { + return probeSN.at (index); + } + catch (const std::out_of_range& ex) // filter for out of range + { + LOGE ("Invalid index given requesting probe serial number."); + } + + return 0ull; } OnixDeviceType Neuropixels2e::getDeviceType() { - return OnixDeviceType::NEUROPIXELSV2E; + return OnixDeviceType::NEUROPIXELSV2E; } int Neuropixels2e::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); - - int rc = deviceContext->writeRegister(deviceIdx, DS90UB9x::ENABLE, isEnabled() ? 1 : 0); - if (rc != ONI_ESUCCESS) - throw error_str("Unable to enable " + getName()); - - configureSerDes(); - setProbeSupply(true); - rc = serializer->set933I2cRate(400e3); - if (rc != ONI_ESUCCESS) - throw error_str("Unable to set I2C rate for " + getName()); - probeSN[0] = getProbeSN(ProbeASelected); - probeSN[1] = getProbeSN(ProbeBSelected); - setProbeSupply(false); - LOGD("Probe A SN: ", probeSN[0]); - LOGD("Probe B SN: ", probeSN[1]); - - if (probeSN[0] == 0 && probeSN[1] == 0) - { - m_numProbes = 0; - throw error_str("No probes were found connected at address " + std::to_string(getDeviceIdx())); - } - else if (probeSN[0] != 0 && probeSN[1] != 0) - { - m_numProbes = 2; - } - else - { - m_numProbes = 1; - } - - streamInfos.clear(); - - for (int i = 0; i < m_numProbes; i++) - { - createDataStream(i); - } - - return ONI_ESUCCESS; + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + throw error_str ("Device context is not initialized properly for " + getName()); + + int rc = deviceContext->writeRegister (deviceIdx, DS90UB9x::ENABLE, isEnabled() ? 1 : 0); + if (rc != ONI_ESUCCESS) + throw error_str ("Unable to enable " + getName()); + + configureSerDes(); + setProbeSupply (true); + rc = serializer->set933I2cRate (400e3); + if (rc != ONI_ESUCCESS) + throw error_str ("Unable to set I2C rate for " + getName()); + probeSN[0] = getProbeSN (ProbeASelected); + probeSN[1] = getProbeSN (ProbeBSelected); + setProbeSupply (false); + LOGD ("Probe A SN: ", probeSN[0]); + LOGD ("Probe B SN: ", probeSN[1]); + + if (probeSN[0] == 0 && probeSN[1] == 0) + { + m_numProbes = 0; + throw error_str ("No probes were found connected at address " + std::to_string (getDeviceIdx())); + } + else if (probeSN[0] != 0 && probeSN[1] != 0) + { + m_numProbes = 2; + } + else + { + m_numProbes = 1; + } + + streamInfos.clear(); + + for (int i = 0; i < m_numProbes; i++) + { + createDataStream (i); + } + + return ONI_ESUCCESS; } bool Neuropixels2e::updateSettings() { - for (int i = 0; i < 2; i++) - { - if (probeSN[i] != 0) - { - if (gainCorrectionFilePath[i] == "None" || gainCorrectionFilePath[i] == "") - { - Onix1::showWarningMessageBoxAsync("Missing File", "Missing gain correction file for probe " + std::to_string(probeSN[i])); - return false; - } - - File gainCorrectionFile = File(gainCorrectionFilePath[i]); - - if (!gainCorrectionFile.existsAsFile()) - { - Onix1::showWarningMessageBoxAsync("Missing File", "The gain correction file \"" + gainCorrectionFilePath[i] + "\" for probe " + std::to_string(probeSN[i]) + " does not exist."); - return false; - } - - StringArray fileLines; - gainCorrectionFile.readLines(fileLines); - - fileLines.removeEmptyStrings(true); - - auto gainSN = std::stoull(fileLines[0].toStdString()); - - if (gainSN != probeSN[i]) - { - Onix1::showWarningMessageBoxAsync("Invalid Serial Number", "Gain correction serial number (" + std::to_string(gainSN) + ") does not match probe serial number (" + std::to_string(probeSN[i]) + ")."); - return false; - } - - if (fileLines.size() != numberOfChannels + 1) - { - Onix1::showWarningMessageBoxAsync("File Format Invalid", "Found the wrong number of lines in the calibration file. Expected " + std::to_string(numberOfChannels + 1) + ", found " + std::to_string(fileLines.size())); - return false; - } - - StringRef breakCharacters = ","; - StringRef noQuote = ""; - StringArray firstLine = StringArray::fromTokens(fileLines[1], breakCharacters, noQuote); - auto correctionValue = std::stod(firstLine[1].toStdString()); - - for (int j = 0; j < numberOfChannels; j++) - { - StringArray calibrationValues = StringArray::fromTokens(fileLines[j + 1], breakCharacters, noQuote); - - if (std::stoi(calibrationValues[0].toStdString()) != j || std::stod(calibrationValues[1].toStdString()) != correctionValue) - { - Onix1::showWarningMessageBoxAsync("File Format Invalid", "Calibration file is incorrectly formatted for probe " + std::to_string(probeSN[i])); - return false; - } - } - - gainCorrection[i] = correctionValue * -1.0f; - } - else - gainCorrection[i] = 0; - } - - setProbeSupply(true); - resetProbes(); - - for (int i = 0; i < NumberOfProbes; i++) - { - if (probeSN[i] != 0) - { - selectProbe(i == 0 ? ProbeASelected : ProbeBSelected); - writeConfiguration(settings[i].get()); - configureProbeStreaming(); - } - } - - selectProbe(NoProbeSelected); - //IMPORTANT! BNO polling thread must be started after this - - return true; + for (int i = 0; i < 2; i++) + { + if (probeSN[i] != 0) + { + if (gainCorrectionFilePath[i] == "None" || gainCorrectionFilePath[i] == "") + { + Onix1::showWarningMessageBoxAsync ("Missing File", "Missing gain correction file for probe " + std::to_string (probeSN[i])); + return false; + } + + File gainCorrectionFile = File (gainCorrectionFilePath[i]); + + if (! gainCorrectionFile.existsAsFile()) + { + Onix1::showWarningMessageBoxAsync ("Missing File", "The gain correction file \"" + gainCorrectionFilePath[i] + "\" for probe " + std::to_string (probeSN[i]) + " does not exist."); + return false; + } + + StringArray fileLines; + gainCorrectionFile.readLines (fileLines); + + fileLines.removeEmptyStrings (true); + + auto gainSN = std::stoull (fileLines[0].toStdString()); + + if (gainSN != probeSN[i]) + { + Onix1::showWarningMessageBoxAsync ("Invalid Serial Number", "Gain correction serial number (" + std::to_string (gainSN) + ") does not match probe serial number (" + std::to_string (probeSN[i]) + ")."); + return false; + } + + if (fileLines.size() != numberOfChannels + 1) + { + Onix1::showWarningMessageBoxAsync ("File Format Invalid", "Found the wrong number of lines in the calibration file. Expected " + std::to_string (numberOfChannels + 1) + ", found " + std::to_string (fileLines.size())); + return false; + } + + StringRef breakCharacters = ","; + StringRef noQuote = ""; + StringArray firstLine = StringArray::fromTokens (fileLines[1], breakCharacters, noQuote); + auto correctionValue = std::stod (firstLine[1].toStdString()); + + for (int j = 0; j < numberOfChannels; j++) + { + StringArray calibrationValues = StringArray::fromTokens (fileLines[j + 1], breakCharacters, noQuote); + + if (std::stoi (calibrationValues[0].toStdString()) != j || std::stod (calibrationValues[1].toStdString()) != correctionValue) + { + Onix1::showWarningMessageBoxAsync ("File Format Invalid", "Calibration file is incorrectly formatted for probe " + std::to_string (probeSN[i])); + return false; + } + } + + gainCorrection[i] = correctionValue * -1.0f; + } + else + gainCorrection[i] = 0; + } + + setProbeSupply (true); + resetProbes(); + + for (int i = 0; i < NumberOfProbes; i++) + { + if (probeSN[i] != 0) + { + selectProbe (i == 0 ? ProbeASelected : ProbeBSelected); + writeConfiguration (settings[i].get()); + configureProbeStreaming(); + } + } + + selectProbe (NoProbeSelected); + // IMPORTANT! BNO polling thread must be started after this + + return true; } void Neuropixels2e::configureProbeStreaming() { - // Write super sync bits into ASIC - probeControl->WriteByte(SUPERSYNC11, 0b00011000); - probeControl->WriteByte(SUPERSYNC10, 0b01100001); - probeControl->WriteByte(SUPERSYNC9, 0b10000110); - probeControl->WriteByte(SUPERSYNC8, 0b00011000); - probeControl->WriteByte(SUPERSYNC7, 0b01100001); - probeControl->WriteByte(SUPERSYNC6, 0b10000110); - probeControl->WriteByte(SUPERSYNC5, 0b00011000); - probeControl->WriteByte(SUPERSYNC4, 0b01100001); - probeControl->WriteByte(SUPERSYNC3, 0b10000110); - probeControl->WriteByte(SUPERSYNC2, 0b00011000); - probeControl->WriteByte(SUPERSYNC1, 0b01100001); - probeControl->WriteByte(SUPERSYNC0, 0b10111001); - - // Activate recording mode on NP - probeControl->WriteByte(OP_MODE, 0b01000000); + // Write super sync bits into ASIC + probeControl->WriteByte (SUPERSYNC11, 0b00011000); + probeControl->WriteByte (SUPERSYNC10, 0b01100001); + probeControl->WriteByte (SUPERSYNC9, 0b10000110); + probeControl->WriteByte (SUPERSYNC8, 0b00011000); + probeControl->WriteByte (SUPERSYNC7, 0b01100001); + probeControl->WriteByte (SUPERSYNC6, 0b10000110); + probeControl->WriteByte (SUPERSYNC5, 0b00011000); + probeControl->WriteByte (SUPERSYNC4, 0b01100001); + probeControl->WriteByte (SUPERSYNC3, 0b10000110); + probeControl->WriteByte (SUPERSYNC2, 0b00011000); + probeControl->WriteByte (SUPERSYNC1, 0b01100001); + probeControl->WriteByte (SUPERSYNC0, 0b10111001); + + // Activate recording mode on NP + probeControl->WriteByte (OP_MODE, 0b01000000); } void Neuropixels2e::configureSerDes() { - deviceContext->writeRegister(deviceIdx, DS90UB9x::ENABLE, 1); - - // configure deserializer trigger mode - deviceContext->writeRegister(deviceIdx, DS90UB9x::TRIGGEROFF, 0); - deviceContext->writeRegister(deviceIdx, DS90UB9x::TRIGGER, (uint32_t)(DS90UB9x::DS90UB9xTriggerMode::Continuous)); - deviceContext->writeRegister(deviceIdx, DS90UB9x::SYNCBITS, 0); - deviceContext->writeRegister(deviceIdx, DS90UB9x::DATAGATE, (uint32_t)(DS90UB9x::DS90UB9xDataGate::Disabled)); - deviceContext->writeRegister(deviceIdx, DS90UB9x::MARK, (uint32_t)(DS90UB9x::DS90UB9xMarkMode::Disabled)); - - // configure two 4-bit magic word-triggered streams, one for each probe - deviceContext->writeRegister(deviceIdx, DS90UB9x::READSZ, 0x00100009); // 16 frames/superframe, 8x 12-bit words + magic bits - deviceContext->writeRegister(deviceIdx, DS90UB9x::MAGIC_MASK, 0xC000003F); // Enable inverse, wait for non-inverse, 14-bit magic word - deviceContext->writeRegister(deviceIdx, DS90UB9x::MAGIC, 0b0000000000101110); // Super-frame sync word - deviceContext->writeRegister(deviceIdx, DS90UB9x::MAGIC_WAIT, 0); - deviceContext->writeRegister(deviceIdx, DS90UB9x::DATAMODE, 0b00100000000000000000001010110101); - deviceContext->writeRegister(deviceIdx, DS90UB9x::DATALINES0, 0xFFFFF8A6); // NP A - deviceContext->writeRegister(deviceIdx, DS90UB9x::DATALINES1, 0xFFFFF97B); // NP B - - deserializer = std::make_unique(DS90UB9x::DES_ADDR, deviceIdx, deviceContext); - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::PortSel, 0x01); - int coaxMode = 0x4 + (uint32_t)(DS90UB9x::DS90UB9xMode::Raw12BitHighFrequency); // 0x4 maintains coax mode - deserializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xDeserializerI2CRegister::PortMode), coaxMode); - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::I2CConfig, 0b01011000); - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::SerAlias, DS90UB9x::SER_ADDR << 1); - - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::GpioCtrl0, 0x10); - deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::GpioCtrl1, 0x32); - - int alias = ProbeI2CAddress << 1; - deserializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID1), alias); - deserializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias1), alias); - - alias = FlexAddress << 1; - deserializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID2), alias); - deserializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias2), alias); - - serializer = std::make_unique(DS90UB9x::SER_ADDR, deviceIdx, deviceContext); - flex = std::make_unique(FlexAddress, deviceIdx, deviceContext); - probeControl = std::make_unique(ProbeI2CAddress, deviceIdx, deviceContext); + deviceContext->writeRegister (deviceIdx, DS90UB9x::ENABLE, 1); + + // configure deserializer trigger mode + deviceContext->writeRegister (deviceIdx, DS90UB9x::TRIGGEROFF, 0); + deviceContext->writeRegister (deviceIdx, DS90UB9x::TRIGGER, (uint32_t) (DS90UB9x::DS90UB9xTriggerMode::Continuous)); + deviceContext->writeRegister (deviceIdx, DS90UB9x::SYNCBITS, 0); + deviceContext->writeRegister (deviceIdx, DS90UB9x::DATAGATE, (uint32_t) (DS90UB9x::DS90UB9xDataGate::Disabled)); + deviceContext->writeRegister (deviceIdx, DS90UB9x::MARK, (uint32_t) (DS90UB9x::DS90UB9xMarkMode::Disabled)); + + // configure two 4-bit magic word-triggered streams, one for each probe + deviceContext->writeRegister (deviceIdx, DS90UB9x::READSZ, 0x00100009); // 16 frames/superframe, 8x 12-bit words + magic bits + deviceContext->writeRegister (deviceIdx, DS90UB9x::MAGIC_MASK, 0xC000003F); // Enable inverse, wait for non-inverse, 14-bit magic word + deviceContext->writeRegister (deviceIdx, DS90UB9x::MAGIC, 0b0000000000101110); // Super-frame sync word + deviceContext->writeRegister (deviceIdx, DS90UB9x::MAGIC_WAIT, 0); + deviceContext->writeRegister (deviceIdx, DS90UB9x::DATAMODE, 0b00100000000000000000001010110101); + deviceContext->writeRegister (deviceIdx, DS90UB9x::DATALINES0, 0xFFFFF8A6); // NP A + deviceContext->writeRegister (deviceIdx, DS90UB9x::DATALINES1, 0xFFFFF97B); // NP B + + deserializer = std::make_unique (DS90UB9x::DES_ADDR, deviceIdx, deviceContext); + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::PortSel, 0x01); + int coaxMode = 0x4 + (uint32_t) (DS90UB9x::DS90UB9xMode::Raw12BitHighFrequency); // 0x4 maintains coax mode + deserializer->WriteByte ((uint32_t) (DS90UB9x::DS90UB9xDeserializerI2CRegister::PortMode), coaxMode); + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::I2CConfig, 0b01011000); + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::SerAlias, DS90UB9x::SER_ADDR << 1); + + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::GpioCtrl0, 0x10); + deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::GpioCtrl1, 0x32); + + int alias = ProbeI2CAddress << 1; + deserializer->WriteByte ((uint32_t) (DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID1), alias); + deserializer->WriteByte ((uint32_t) (DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias1), alias); + + alias = FlexAddress << 1; + deserializer->WriteByte ((uint32_t) (DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID2), alias); + deserializer->WriteByte ((uint32_t) (DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias2), alias); + + serializer = std::make_unique (DS90UB9x::SER_ADDR, deviceIdx, deviceContext); + flex = std::make_unique (FlexAddress, deviceIdx, deviceContext); + probeControl = std::make_unique (ProbeI2CAddress, deviceIdx, deviceContext); } -void Neuropixels2e::setProbeSupply(bool en) +void Neuropixels2e::setProbeSupply (bool en) { - auto gpo10Config = en ? DefaultGPO10Config | GPO10SupplyMask : DefaultGPO10Config; + auto gpo10Config = en ? DefaultGPO10Config | GPO10SupplyMask : DefaultGPO10Config; - selectProbe(NoProbeSelected); - serializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10), gpo10Config); - Thread::sleep(20); + selectProbe (NoProbeSelected); + serializer->WriteByte ((uint32_t) (DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10), gpo10Config); + Thread::sleep (20); } -void Neuropixels2e::selectProbe(uint8_t probeSelect) +void Neuropixels2e::selectProbe (uint8_t probeSelect) { - if (serializer == nullptr) - { - LOGE("Serializer is not initialized for Neuropixels 2.0"); - return; - } - - serializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO32), probeSelect); - Thread::sleep(20); + if (serializer == nullptr) + { + LOGE ("Serializer is not initialized for Neuropixels 2.0"); + return; + } + + serializer->WriteByte ((uint32_t) (DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO32), probeSelect); + Thread::sleep (20); } void Neuropixels2e::resetProbes() { - auto gpo10Config = DefaultGPO10Config | GPO10SupplyMask; - gpo10Config &= ~GPO10ResetMask; - serializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10), gpo10Config); - gpo10Config |= GPO10ResetMask; - serializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10), gpo10Config); + auto gpo10Config = DefaultGPO10Config | GPO10SupplyMask; + gpo10Config &= ~GPO10ResetMask; + serializer->WriteByte ((uint32_t) (DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10), gpo10Config); + gpo10Config |= GPO10ResetMask; + serializer->WriteByte ((uint32_t) (DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10), gpo10Config); } -uint64_t Neuropixels2e::getProbeSN(uint8_t probeSelect) +uint64_t Neuropixels2e::getProbeSN (uint8_t probeSelect) { - if (flex == nullptr) - { - LOGE("Flex is not initialized for Neuropixels 2.0"); - return 0ull; - } - - selectProbe(probeSelect); - uint64_t probeSN = 0ull; - int errorCode = 0, rc; - for (unsigned int i = 0; i < sizeof(probeSN); i++) - { - oni_reg_addr_t reg_addr = OFFSET_PROBE_SN + i; - oni_reg_val_t val; - - rc = flex->ReadByte(reg_addr, &val); - - if (rc != ONI_ESUCCESS) return 0ull; - - if (val <= 0xFF) - { - probeSN |= (((uint64_t)val) << (i * 8)); - } - } - return probeSN; + if (flex == nullptr) + { + LOGE ("Flex is not initialized for Neuropixels 2.0"); + return 0ull; + } + + selectProbe (probeSelect); + uint64_t probeSN = 0ull; + int errorCode = 0, rc; + for (unsigned int i = 0; i < sizeof (probeSN); i++) + { + oni_reg_addr_t reg_addr = OFFSET_PROBE_SN + i; + oni_reg_val_t val; + + rc = flex->ReadByte (reg_addr, &val); + + if (rc != ONI_ESUCCESS) + return 0ull; + + if (val <= 0xFF) + { + probeSN |= (((uint64_t) val) << (i * 8)); + } + } + return probeSN; } void Neuropixels2e::startAcquisition() { - frameCount.fill(0); - sampleNumber.fill(0); + frameCount.fill (0); + sampleNumber.fill (0); } void Neuropixels2e::stopAcquisition() { - setProbeSupply(false); + setProbeSupply (false); - OnixDevice::stopAcquisition(); + OnixDevice::stopAcquisition(); } -void Neuropixels2e::addSourceBuffers(OwnedArray& sourceBuffers) +void Neuropixels2e::addSourceBuffers (OwnedArray& sourceBuffers) { - if (m_numProbes == 1) - { - sourceBuffers.add(new DataBuffer(streamInfos.getFirst().getNumChannels(), (int)streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); - auto bufferIndex = probeSN[0] != 0 ? 0 : 1; - amplifierBuffer[bufferIndex] = sourceBuffers.getLast(); - } - else - { - int bufferIdx = 0; - for (const auto& streamInfo : streamInfos) - { - sourceBuffers.add(new DataBuffer(streamInfo.getNumChannels(), (int)streamInfo.getSampleRate() * bufferSizeInSeconds)); - amplifierBuffer[bufferIdx++] = sourceBuffers.getLast(); - } - } + if (m_numProbes == 1) + { + sourceBuffers.add (new DataBuffer (streamInfos.getFirst().getNumChannels(), (int) streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); + auto bufferIndex = probeSN[0] != 0 ? 0 : 1; + amplifierBuffer[bufferIndex] = sourceBuffers.getLast(); + } + else + { + int bufferIdx = 0; + for (const auto& streamInfo : streamInfos) + { + sourceBuffers.add (new DataBuffer (streamInfo.getNumChannels(), (int) streamInfo.getSampleRate() * bufferSizeInSeconds)); + amplifierBuffer[bufferIdx++] = sourceBuffers.getLast(); + } + } } void Neuropixels2e::processFrames() { - oni_frame_t* frame; - while (frameQueue.try_dequeue(frame)) - { - uint16_t* dataPtr = (uint16_t*)frame->data; + oni_frame_t* frame; + while (frameQueue.try_dequeue (frame)) + { + uint16_t* dataPtr = (uint16_t*) frame->data; - uint16_t probeIndex = *(dataPtr + 4); - uint16_t* amplifierData = dataPtr + 9; + uint16_t probeIndex = *(dataPtr + 4); + uint16_t* amplifierData = dataPtr + 9; - sampleNumbers[probeIndex][frameCount[probeIndex]] = sampleNumber[probeIndex]++; - timestamps[probeIndex][frameCount[probeIndex]] = deviceContext->convertTimestampToSeconds(frame->time); + sampleNumbers[probeIndex][frameCount[probeIndex]] = sampleNumber[probeIndex]++; + timestamps[probeIndex][frameCount[probeIndex]] = deviceContext->convertTimestampToSeconds (frame->time); - for (int i = 0; i < FramesPerSuperFrame; i++) - { - auto adcDataOffset = i * FrameWords; + for (int i = 0; i < FramesPerSuperFrame; i++) + { + auto adcDataOffset = i * FrameWords; - for (int j = 0; j < AdcsPerProbe; j++) - { - const size_t channelIndex = rawToChannel[j][i]; + for (int j = 0; j < AdcsPerProbe; j++) + { + const size_t channelIndex = rawToChannel[j][i]; - samples[probeIndex][channelIndex * numFrames + frameCount[probeIndex]] = - (float)(*(amplifierData + adcIndices[j] + adcDataOffset)) * gainCorrection[probeIndex] + DataMidpoint; - } - } + samples[probeIndex][channelIndex * numFrames + frameCount[probeIndex]] = + (float) (*(amplifierData + adcIndices[j] + adcDataOffset)) * gainCorrection[probeIndex] + DataMidpoint; + } + } - frameCount[probeIndex]++; + frameCount[probeIndex]++; - if (frameCount[probeIndex] >= numFrames) - { - amplifierBuffer[probeIndex]->addToBuffer(samples[probeIndex].data(), sampleNumbers[probeIndex].data(), timestamps[probeIndex].data(), eventCodes[probeIndex].data(), numFrames); - frameCount[probeIndex] = 0; - } + if (frameCount[probeIndex] >= numFrames) + { + amplifierBuffer[probeIndex]->addToBuffer (samples[probeIndex].data(), sampleNumbers[probeIndex].data(), timestamps[probeIndex].data(), eventCodes[probeIndex].data(), numFrames); + frameCount[probeIndex] = 0; + } - oni_destroy_frame(frame); - } + oni_destroy_frame (frame); + } } -void Neuropixels2e::writeConfiguration(ProbeSettings* settings) +void Neuropixels2e::writeConfiguration (ProbeSettings* settings) { - auto baseBits = makeBaseBits(getReference(settings->referenceIndex)); - writeShiftRegister(SR_CHAIN5, baseBits[0]); - writeShiftRegister(SR_CHAIN6, baseBits[1]); - - auto shankBits = makeShankBits(getReference(settings->referenceIndex), settings->electrodeMetadata); - writeShiftRegister(SR_CHAIN1, shankBits[0]); - writeShiftRegister(SR_CHAIN2, shankBits[1]); - writeShiftRegister(SR_CHAIN3, shankBits[2]); - writeShiftRegister(SR_CHAIN4, shankBits[3]); + auto baseBits = makeBaseBits (getReference (settings->referenceIndex)); + writeShiftRegister (SR_CHAIN5, baseBits[0]); + writeShiftRegister (SR_CHAIN6, baseBits[1]); + + auto shankBits = makeShankBits (getReference (settings->referenceIndex), settings->electrodeMetadata); + writeShiftRegister (SR_CHAIN1, shankBits[0]); + writeShiftRegister (SR_CHAIN2, shankBits[1]); + writeShiftRegister (SR_CHAIN3, shankBits[2]); + writeShiftRegister (SR_CHAIN4, shankBits[3]); } -template -void Neuropixels2e::writeShiftRegister(uint32_t srAddress, std::bitset bits) +template +void Neuropixels2e::writeShiftRegister (uint32_t srAddress, std::bitset bits) { - std::vector bytes = toBitReversedBytes(bits); - - for (int i = 2; i > 0; i -= 1) - { - WriteByte(SOFT_RESET, 0xFF); - WriteByte(SOFT_RESET, 0x00); - - WriteByte(SR_LENGTH1, (uint32_t)(bytes.size() % 0x100)); - WriteByte(SR_LENGTH2, (uint32_t)(bytes.size() / 0x100)); - - for (auto b : bytes) - { - WriteByte(srAddress, b); - } - } - - uint32_t status; - ReadByte(STATUS, &status); - if (status != (uint32_t)NeuropixelsV2Status::SR_OK) - { - LOGE("Warning: Shift register ", srAddress, " status check failed. ", getShankName(srAddress), " may be damaged."); - } + std::vector bytes = toBitReversedBytes (bits); + + for (int i = 2; i > 0; i -= 1) + { + WriteByte (SOFT_RESET, 0xFF); + WriteByte (SOFT_RESET, 0x00); + + WriteByte (SR_LENGTH1, (uint32_t) (bytes.size() % 0x100)); + WriteByte (SR_LENGTH2, (uint32_t) (bytes.size() / 0x100)); + + for (auto b : bytes) + { + WriteByte (srAddress, b); + } + } + + uint32_t status; + ReadByte (STATUS, &status); + if (status != (uint32_t) NeuropixelsV2Status::SR_OK) + { + LOGE ("Warning: Shift register ", srAddress, " status check failed. ", getShankName (srAddress), " may be damaged."); + } } -std::string Neuropixels2e::getShankName(uint32_t shiftRegisterAddress) +std::string Neuropixels2e::getShankName (uint32_t shiftRegisterAddress) { - switch (shiftRegisterAddress) - { - case SR_CHAIN1: - return "Shank 1"; - case SR_CHAIN2: - return "Shank 2"; - case SR_CHAIN3: - return "Shank 3"; - case SR_CHAIN4: - return "Shank 4"; - default: - return ""; - } + switch (shiftRegisterAddress) + { + case SR_CHAIN1: + return "Shank 1"; + case SR_CHAIN2: + return "Shank 2"; + case SR_CHAIN3: + return "Shank 3"; + case SR_CHAIN4: + return "Shank 4"; + default: + return ""; + } } -void Neuropixels2e::setGainCorrectionFile(int index, std::string filename) +void Neuropixels2e::setGainCorrectionFile (int index, std::string filename) { - if (index < gainCorrectionFilePath.size()) - { - gainCorrectionFilePath[index] = filename; - } + if (index < gainCorrectionFilePath.size()) + { + gainCorrectionFilePath[index] = filename; + } } -std::string Neuropixels2e::getGainCorrectionFile(int index) +std::string Neuropixels2e::getGainCorrectionFile (int index) { - if (index < gainCorrectionFilePath.size()) - { - return gainCorrectionFilePath[index]; - } - else - return ""; + if (index < gainCorrectionFilePath.size()) + { + return gainCorrectionFilePath[index]; + } + else + return ""; } -NeuropixelsV2Reference Neuropixels2e::getReference(int index) +NeuropixelsV2Reference Neuropixels2e::getReference (int index) { - switch (index) - { - case 0: - return NeuropixelsV2Reference::External; - case 1: - return NeuropixelsV2Reference::Tip1; - case 2: - return NeuropixelsV2Reference::Tip2; - case 3: - return NeuropixelsV2Reference::Tip3; - case 4: - return NeuropixelsV2Reference::Tip4; - default: - break; - } - - return NeuropixelsV2Reference::External; + switch (index) + { + case 0: + return NeuropixelsV2Reference::External; + case 1: + return NeuropixelsV2Reference::Tip1; + case 2: + return NeuropixelsV2Reference::Tip2; + case 3: + return NeuropixelsV2Reference::Tip3; + case 4: + return NeuropixelsV2Reference::Tip4; + default: + break; + } + + return NeuropixelsV2Reference::External; } -Neuropixels2e::BaseBitsArray Neuropixels2e::makeBaseBits(NeuropixelsV2Reference reference) +Neuropixels2e::BaseBitsArray Neuropixels2e::makeBaseBits (NeuropixelsV2Reference reference) { - BaseBitsArray baseBits; + BaseBitsArray baseBits; - int referenceBit; + int referenceBit; - if (reference == NeuropixelsV2Reference::External) - referenceBit = 1; - else - referenceBit = 2; + if (reference == NeuropixelsV2Reference::External) + referenceBit = 1; + else + referenceBit = 2; - for (size_t i = 0; i < numberOfChannels; i++) - { - auto configIndex = i % 2; - auto bitOffset = (382 - i + configIndex) / 2 * baseBitsPerChannel; - baseBits[configIndex][bitOffset + 0] = false; - baseBits[configIndex][bitOffset + referenceBit] = true; - } + for (size_t i = 0; i < numberOfChannels; i++) + { + auto configIndex = i % 2; + auto bitOffset = (382 - i + configIndex) / 2 * baseBitsPerChannel; + baseBits[configIndex][bitOffset + 0] = false; + baseBits[configIndex][bitOffset + referenceBit] = true; + } - return baseBits; + return baseBits; } -Neuropixels2e::ShankBitsArray Neuropixels2e::makeShankBits(NeuropixelsV2Reference reference, std::array channelMap) +Neuropixels2e::ShankBitsArray Neuropixels2e::makeShankBits (NeuropixelsV2Reference reference, std::array channelMap) { - ShankBitsArray shankBits; - - if (reference != NeuropixelsV2Reference::External) - { - shankBits[(size_t)reference - 1][643] = true; - shankBits[(size_t)reference - 1][644] = true; - } - else - { - shankBits[0][2] = true; - shankBits[0][1285] = true; - shankBits[1][2] = true; - shankBits[1][1285] = true; - shankBits[2][2] = true; - shankBits[2][1285] = true; - shankBits[3][2] = true; - shankBits[3][1285] = true; - } - - const int pixelOffset = (NeuropixelsV2eValues::electrodesPerShank - 1) / 2; - const int referencePixelOffset = 3; - - int count = 0; - - for (const auto& e : channelMap) - { - if (e.status == ElectrodeStatus::CONNECTED) - { - auto baseIndex = e.shank_local_index % 2; - auto pixelIndex = e.shank_local_index / 2; - pixelIndex = baseIndex == 0 - ? pixelIndex + pixelOffset + 2 * referencePixelOffset - : pixelOffset - pixelIndex + referencePixelOffset; - - shankBits[e.shank][pixelIndex] = true; - - count++; - } - } - - if (count != numberOfChannels) - { - LOGE("Invalid number of channels connected for Neuropixels 2.0e, configuration might be invalid."); - } - - return shankBits; + ShankBitsArray shankBits; + + if (reference != NeuropixelsV2Reference::External) + { + shankBits[(size_t) reference - 1][643] = true; + shankBits[(size_t) reference - 1][644] = true; + } + else + { + shankBits[0][2] = true; + shankBits[0][1285] = true; + shankBits[1][2] = true; + shankBits[1][1285] = true; + shankBits[2][2] = true; + shankBits[2][1285] = true; + shankBits[3][2] = true; + shankBits[3][1285] = true; + } + + const int pixelOffset = (NeuropixelsV2eValues::electrodesPerShank - 1) / 2; + const int referencePixelOffset = 3; + + int count = 0; + + for (const auto& e : channelMap) + { + if (e.status == ElectrodeStatus::CONNECTED) + { + auto baseIndex = e.shank_local_index % 2; + auto pixelIndex = e.shank_local_index / 2; + pixelIndex = baseIndex == 0 + ? pixelIndex + pixelOffset + 2 * referencePixelOffset + : pixelOffset - pixelIndex + referencePixelOffset; + + shankBits[e.shank][pixelIndex] = true; + + count++; + } + } + + if (count != numberOfChannels) + { + LOGE ("Invalid number of channels connected for Neuropixels 2.0e, configuration might be invalid."); + } + + return shankBits; } -void Neuropixels2e::setSettings(ProbeSettings* settings_, int index) +void Neuropixels2e::setSettings (ProbeSettings* settings_, int index) { - if (index >= settings.size()) - { - LOGE("Invalid index given when trying to update settings."); - return; - } + if (index >= settings.size()) + { + LOGE ("Invalid index given when trying to update settings."); + return; + } - settings[index]->updateProbeSettings(settings_); + settings[index]->updateProbeSettings (settings_); } -void Neuropixels2e::defineMetadata(ProbeSettings* settings, int shankCount) +void Neuropixels2e::defineMetadata (ProbeSettings* settings, int shankCount) { - settings->probeType = ProbeType::NPX_V2; - settings->probeMetadata.name = "Neuropixels 2.0e" + (shankCount == 1) ? " - Single Shank" : " - Quad Shank"; - - constexpr float shankTipY = 0.0f; - constexpr float shankBaseY = 155.0f; - constexpr float shankLengthY = 10000.0f; - constexpr float probeLengthY = 10155.0f; - constexpr float shankOffsetX = 200.0f; - constexpr float shankWidthX = 70.0f; - constexpr float shankPitchX = 250.0f; - - std::vector> probeContour{ - {0, probeLengthY}, - {0, shankLengthY}, - }; - - for (int i = 0; i < shankCount; i++) - { - probeContour.emplace_back(std::array{ shankOffsetX + (shankWidthX + shankPitchX) * i, shankLengthY }); - probeContour.emplace_back(std::array{ shankOffsetX + (shankWidthX + shankPitchX) * i, shankBaseY }); - probeContour.emplace_back(std::array{ shankOffsetX + (shankWidthX + shankPitchX) * i + shankWidthX / 2, shankTipY }); - probeContour.emplace_back(std::array{ shankOffsetX + (shankWidthX + shankPitchX) * i + shankWidthX, shankBaseY }); - probeContour.emplace_back(std::array{ shankOffsetX + (shankWidthX + shankPitchX) * i + shankWidthX, shankLengthY }); - } - - probeContour.emplace_back(std::array{shankOffsetX * 2 + (shankWidthX + shankPitchX) * (numberOfShanks - 1) + shankWidthX, shankLengthY}); - probeContour.emplace_back(std::array{shankOffsetX * 2 + (shankWidthX + shankPitchX) * (numberOfShanks - 1) + shankWidthX, probeLengthY}); - probeContour.emplace_back(std::array{0.0f, probeLengthY}); - - std::vector> shankOutline{ - {27, 31}, - {27, 514}, - {27 + 5, 522}, - {27 + 10, 514}, - {27 + 10, 31} - }; - - settings->probeMetadata.shank_count = shankCount; - settings->probeMetadata.electrodes_per_shank = NeuropixelsV2eValues::electrodesPerShank; - settings->probeMetadata.rows_per_shank = NeuropixelsV2eValues::electrodesPerShank / 2; - settings->probeMetadata.columns_per_shank = 2; - settings->probeMetadata.shankOutline = shankOutline; - settings->probeMetadata.probeContour = probeContour; - settings->probeMetadata.num_adcs = 24; - settings->probeMetadata.adc_bits = 12; - - settings->availableBanks = { - Bank::A, - Bank::B, - Bank::C, - Bank::D, - Bank::NONE //disconnected - }; - - for (int i = 0; i < settings->probeMetadata.electrodes_per_shank * settings->probeMetadata.shank_count; i++) - { - ElectrodeMetadata metadata; - - metadata.global_index = i; - - metadata.shank = i / settings->probeMetadata.electrodes_per_shank; - metadata.shank_local_index = i % settings->probeMetadata.electrodes_per_shank; - - auto offset = shankOffsetX + (shankWidthX + shankPitchX) * metadata.shank + 11.0f; - metadata.xpos = offset + (i % 2) * 32.0f + 8.0f; - metadata.ypos = std::floor((i % settings->probeMetadata.electrodes_per_shank) / 2.0f) * 15 + 170; - metadata.site_width = 12; - - metadata.column_index = i % 2; - metadata.row_index = metadata.shank_local_index / 2; - - metadata.isSelected = false; - - metadata.colour = Colours::lightgrey; - - if (shankCount == 1) - { - if (i < 384) - { - metadata.bank = Bank::A; - - int bank_index = metadata.shank_local_index % 384; - int block = bank_index / 32; - int row = (bank_index % 32) / 2; - - if (i % 2 == 0) - { - metadata.channel = row * 2 + block * 32; - } - else - { - metadata.channel = row * 2 + block * 32 + 1; - } - - metadata.status = ElectrodeStatus::CONNECTED; - } - else if (i >= 384 && i < 768) - { - metadata.bank = Bank::B; - - int bank_index = metadata.shank_local_index % 384; - int block = bank_index / 32; - int row = (bank_index % 32) / 2; - - if (i % 2 == 0) - { - metadata.channel = ((row * 7) % 16) * 2 + block * 32; - } - else - { - metadata.channel = ((row * 7 + 4) % 16) * 2 + block * 32 + 1; - } - - metadata.status = ElectrodeStatus::DISCONNECTED; - } - else if (i >= 768 && i < 1152) - { - metadata.bank = Bank::C; - - int bank_index = metadata.shank_local_index % 384; - int block = bank_index / 32; - int row = (bank_index % 32) / 2; - - if (i % 2 == 0) - { - metadata.channel = ((row * 5) % 16) * 2 + block * 32; - } - else - { - metadata.channel = ((row * 5 + 8) % 16) * 2 + block * 32 + 1; - } - - metadata.status = ElectrodeStatus::DISCONNECTED; - } - else - { - metadata.bank = Bank::D; - - int bank_index = metadata.shank_local_index % 384; - int block = bank_index / 32; - int row = (bank_index % 32) / 2; - - if (i % 2 == 0) - { - metadata.channel = ((row * 3) % 16) * 2 + block * 32; - } - else - { - metadata.channel = ((row * 3 + 12) % 16) * 2 + block * 32 + 1; - } - - metadata.status = ElectrodeStatus::DISCONNECTED; - } - } - else if (shankCount == 4) - { - if (i < 384) - { - metadata.status = ElectrodeStatus::CONNECTED; - } - else - { - metadata.status = ElectrodeStatus::DISCONNECTED; - } - - if (metadata.shank_local_index < 384) - metadata.bank = Bank::A; - else if (metadata.shank_local_index >= 384 && metadata.shank_local_index < 768) - metadata.bank = Bank::B; - else if (metadata.shank_local_index >= 768 && metadata.shank_local_index < 1152) - metadata.bank = Bank::C; - else - metadata.bank = Bank::D; - - int block = metadata.shank_local_index % 384 / 48 + 1; - int block_index = metadata.shank_local_index % 48; - - if (metadata.shank == 0) - { - switch (block) - { - case 1: - metadata.channel = block_index + 48 * 0; // 1-48 (Bank 0-3) - break; - case 2: - metadata.channel = block_index + 48 * 2; // 96-144 (Bank 0-3) - break; - case 3: - metadata.channel = block_index + 48 * 4; // 192-223 (Bank 0-3) - break; - case 4: - metadata.channel = block_index + 48 * 6; // 288-336 (Bank 0-2) - break; - case 5: - metadata.channel = block_index + 48 * 5; // 240-288 (Bank 0-2) - break; - case 6: - metadata.channel = block_index + 48 * 7; // 336-384 (Bank 0-2) - break; - case 7: - metadata.channel = block_index + 48 * 1; // 48-96 (Bank 0-2) - break; - case 8: - metadata.channel = block_index + 48 * 3; // 144-192 (Bank 0-2) - break; - default: - metadata.channel = -1; - } - } - else if (metadata.shank == 1) - { - switch (block) - { - case 1: - metadata.channel = block_index + 48 * 1; - break; - case 2: - metadata.channel = block_index + 48 * 3; - break; - case 3: - metadata.channel = block_index + 48 * 5; - break; - case 4: - metadata.channel = block_index + 48 * 7; - break; - case 5: - metadata.channel = block_index + 48 * 4; - break; - case 6: - metadata.channel = block_index + 48 * 6; - break; - case 7: - metadata.channel = block_index + 48 * 0; - break; - case 8: - metadata.channel = block_index + 48 * 2; - break; - default: - metadata.channel = -1; - } - } - else if (metadata.shank == 2) - { - switch (block) - { - case 1: - metadata.channel = block_index + 48 * 4; - break; - case 2: - metadata.channel = block_index + 48 * 6; - break; - case 3: - metadata.channel = block_index + 48 * 0; - break; - case 4: - metadata.channel = block_index + 48 * 2; - break; - case 5: - metadata.channel = block_index + 48 * 1; - break; - case 6: - metadata.channel = block_index + 48 * 3; - break; - case 7: - metadata.channel = block_index + 48 * 5; - break; - case 8: - metadata.channel = block_index + 48 * 7; - break; - default: - metadata.channel = -1; - } - } - else if (metadata.shank == 3) - { - switch (block) - { - case 1: - metadata.channel = block_index + 48 * 5; - break; - case 2: - metadata.channel = block_index + 48 * 7; - break; - case 3: - metadata.channel = block_index + 48 * 1; - break; - case 4: - metadata.channel = block_index + 48 * 3; - break; - case 5: - metadata.channel = block_index + 48 * 0; - break; - case 6: - metadata.channel = block_index + 48 * 2; - break; - case 7: - metadata.channel = block_index + 48 * 4; - break; - case 8: - metadata.channel = block_index + 48 * 6; - break; - default: - metadata.channel = -1; - } - } - - metadata.type = ElectrodeType::ELECTRODE; - } - - settings->electrodeMetadata[i] = metadata; - } - - settings->apGainIndex = -1; - settings->lfpGainIndex = -1; - settings->referenceIndex = 0; - settings->apFilterState = false; - - settings->electrodeConfigurationIndex = (int32_t)ElectrodeConfigurationSingleShank::BankA; - auto selection = selectElectrodeConfiguration(settings->electrodeConfigurationIndex); - settings->selectElectrodes(selection); - - settings->availableReferences.add("Ext"); - settings->availableReferences.add("Tip1"); - - if (shankCount == 4) - { - settings->availableReferences.add("Tip2"); - settings->availableReferences.add("Tip3"); - settings->availableReferences.add("Tip4"); - } - - if (shankCount == 1) - { - for (const auto& [_, config] : electrodeConfigurationSingleShank) - { - settings->availableElectrodeConfigurations.add(config); - } - } - else if (shankCount == 4) - { - for (const auto& [_, config] : electrodeConfigurationQuadShank) - { - settings->availableElectrodeConfigurations.add(config); - } - } + settings->probeType = ProbeType::NPX_V2; + settings->probeMetadata.name = "Neuropixels 2.0e" + (shankCount == 1) ? " - Single Shank" : " - Quad Shank"; + + constexpr float shankTipY = 0.0f; + constexpr float shankBaseY = 155.0f; + constexpr float shankLengthY = 10000.0f; + constexpr float probeLengthY = 10155.0f; + constexpr float shankOffsetX = 200.0f; + constexpr float shankWidthX = 70.0f; + constexpr float shankPitchX = 250.0f; + + std::vector> probeContour { + { 0, probeLengthY }, + { 0, shankLengthY }, + }; + + for (int i = 0; i < shankCount; i++) + { + probeContour.emplace_back (std::array { shankOffsetX + (shankWidthX + shankPitchX) * i, shankLengthY }); + probeContour.emplace_back (std::array { shankOffsetX + (shankWidthX + shankPitchX) * i, shankBaseY }); + probeContour.emplace_back (std::array { shankOffsetX + (shankWidthX + shankPitchX) * i + shankWidthX / 2, shankTipY }); + probeContour.emplace_back (std::array { shankOffsetX + (shankWidthX + shankPitchX) * i + shankWidthX, shankBaseY }); + probeContour.emplace_back (std::array { shankOffsetX + (shankWidthX + shankPitchX) * i + shankWidthX, shankLengthY }); + } + + probeContour.emplace_back (std::array { shankOffsetX * 2 + (shankWidthX + shankPitchX) * (numberOfShanks - 1) + shankWidthX, shankLengthY }); + probeContour.emplace_back (std::array { shankOffsetX * 2 + (shankWidthX + shankPitchX) * (numberOfShanks - 1) + shankWidthX, probeLengthY }); + probeContour.emplace_back (std::array { 0.0f, probeLengthY }); + + std::vector> shankOutline { + { 27, 31 }, + { 27, 514 }, + { 27 + 5, 522 }, + { 27 + 10, 514 }, + { 27 + 10, 31 } + }; + + settings->probeMetadata.shank_count = shankCount; + settings->probeMetadata.electrodes_per_shank = NeuropixelsV2eValues::electrodesPerShank; + settings->probeMetadata.rows_per_shank = NeuropixelsV2eValues::electrodesPerShank / 2; + settings->probeMetadata.columns_per_shank = 2; + settings->probeMetadata.shankOutline = shankOutline; + settings->probeMetadata.probeContour = probeContour; + settings->probeMetadata.num_adcs = 24; + settings->probeMetadata.adc_bits = 12; + + settings->availableBanks = { + Bank::A, + Bank::B, + Bank::C, + Bank::D, + Bank::NONE // disconnected + }; + + for (int i = 0; i < settings->probeMetadata.electrodes_per_shank * settings->probeMetadata.shank_count; i++) + { + ElectrodeMetadata metadata; + + metadata.global_index = i; + + metadata.shank = i / settings->probeMetadata.electrodes_per_shank; + metadata.shank_local_index = i % settings->probeMetadata.electrodes_per_shank; + + auto offset = shankOffsetX + (shankWidthX + shankPitchX) * metadata.shank + 11.0f; + metadata.xpos = offset + (i % 2) * 32.0f + 8.0f; + metadata.ypos = std::floor ((i % settings->probeMetadata.electrodes_per_shank) / 2.0f) * 15 + 170; + metadata.site_width = 12; + + metadata.column_index = i % 2; + metadata.row_index = metadata.shank_local_index / 2; + + metadata.isSelected = false; + + metadata.colour = Colours::lightgrey; + + if (shankCount == 1) + { + if (i < 384) + { + metadata.bank = Bank::A; + + int bank_index = metadata.shank_local_index % 384; + int block = bank_index / 32; + int row = (bank_index % 32) / 2; + + if (i % 2 == 0) + { + metadata.channel = row * 2 + block * 32; + } + else + { + metadata.channel = row * 2 + block * 32 + 1; + } + + metadata.status = ElectrodeStatus::CONNECTED; + } + else if (i >= 384 && i < 768) + { + metadata.bank = Bank::B; + + int bank_index = metadata.shank_local_index % 384; + int block = bank_index / 32; + int row = (bank_index % 32) / 2; + + if (i % 2 == 0) + { + metadata.channel = ((row * 7) % 16) * 2 + block * 32; + } + else + { + metadata.channel = ((row * 7 + 4) % 16) * 2 + block * 32 + 1; + } + + metadata.status = ElectrodeStatus::DISCONNECTED; + } + else if (i >= 768 && i < 1152) + { + metadata.bank = Bank::C; + + int bank_index = metadata.shank_local_index % 384; + int block = bank_index / 32; + int row = (bank_index % 32) / 2; + + if (i % 2 == 0) + { + metadata.channel = ((row * 5) % 16) * 2 + block * 32; + } + else + { + metadata.channel = ((row * 5 + 8) % 16) * 2 + block * 32 + 1; + } + + metadata.status = ElectrodeStatus::DISCONNECTED; + } + else + { + metadata.bank = Bank::D; + + int bank_index = metadata.shank_local_index % 384; + int block = bank_index / 32; + int row = (bank_index % 32) / 2; + + if (i % 2 == 0) + { + metadata.channel = ((row * 3) % 16) * 2 + block * 32; + } + else + { + metadata.channel = ((row * 3 + 12) % 16) * 2 + block * 32 + 1; + } + + metadata.status = ElectrodeStatus::DISCONNECTED; + } + } + else if (shankCount == 4) + { + if (i < 384) + { + metadata.status = ElectrodeStatus::CONNECTED; + } + else + { + metadata.status = ElectrodeStatus::DISCONNECTED; + } + + if (metadata.shank_local_index < 384) + metadata.bank = Bank::A; + else if (metadata.shank_local_index >= 384 && metadata.shank_local_index < 768) + metadata.bank = Bank::B; + else if (metadata.shank_local_index >= 768 && metadata.shank_local_index < 1152) + metadata.bank = Bank::C; + else + metadata.bank = Bank::D; + + int block = metadata.shank_local_index % 384 / 48 + 1; + int block_index = metadata.shank_local_index % 48; + + if (metadata.shank == 0) + { + switch (block) + { + case 1: + metadata.channel = block_index + 48 * 0; // 1-48 (Bank 0-3) + break; + case 2: + metadata.channel = block_index + 48 * 2; // 96-144 (Bank 0-3) + break; + case 3: + metadata.channel = block_index + 48 * 4; // 192-223 (Bank 0-3) + break; + case 4: + metadata.channel = block_index + 48 * 6; // 288-336 (Bank 0-2) + break; + case 5: + metadata.channel = block_index + 48 * 5; // 240-288 (Bank 0-2) + break; + case 6: + metadata.channel = block_index + 48 * 7; // 336-384 (Bank 0-2) + break; + case 7: + metadata.channel = block_index + 48 * 1; // 48-96 (Bank 0-2) + break; + case 8: + metadata.channel = block_index + 48 * 3; // 144-192 (Bank 0-2) + break; + default: + metadata.channel = -1; + } + } + else if (metadata.shank == 1) + { + switch (block) + { + case 1: + metadata.channel = block_index + 48 * 1; + break; + case 2: + metadata.channel = block_index + 48 * 3; + break; + case 3: + metadata.channel = block_index + 48 * 5; + break; + case 4: + metadata.channel = block_index + 48 * 7; + break; + case 5: + metadata.channel = block_index + 48 * 4; + break; + case 6: + metadata.channel = block_index + 48 * 6; + break; + case 7: + metadata.channel = block_index + 48 * 0; + break; + case 8: + metadata.channel = block_index + 48 * 2; + break; + default: + metadata.channel = -1; + } + } + else if (metadata.shank == 2) + { + switch (block) + { + case 1: + metadata.channel = block_index + 48 * 4; + break; + case 2: + metadata.channel = block_index + 48 * 6; + break; + case 3: + metadata.channel = block_index + 48 * 0; + break; + case 4: + metadata.channel = block_index + 48 * 2; + break; + case 5: + metadata.channel = block_index + 48 * 1; + break; + case 6: + metadata.channel = block_index + 48 * 3; + break; + case 7: + metadata.channel = block_index + 48 * 5; + break; + case 8: + metadata.channel = block_index + 48 * 7; + break; + default: + metadata.channel = -1; + } + } + else if (metadata.shank == 3) + { + switch (block) + { + case 1: + metadata.channel = block_index + 48 * 5; + break; + case 2: + metadata.channel = block_index + 48 * 7; + break; + case 3: + metadata.channel = block_index + 48 * 1; + break; + case 4: + metadata.channel = block_index + 48 * 3; + break; + case 5: + metadata.channel = block_index + 48 * 0; + break; + case 6: + metadata.channel = block_index + 48 * 2; + break; + case 7: + metadata.channel = block_index + 48 * 4; + break; + case 8: + metadata.channel = block_index + 48 * 6; + break; + default: + metadata.channel = -1; + } + } + + metadata.type = ElectrodeType::ELECTRODE; + } + + settings->electrodeMetadata[i] = metadata; + } + + settings->apGainIndex = -1; + settings->lfpGainIndex = -1; + settings->referenceIndex = 0; + settings->apFilterState = false; + + settings->electrodeConfigurationIndex = (int32_t) ElectrodeConfigurationSingleShank::BankA; + auto selection = selectElectrodeConfiguration (settings->electrodeConfigurationIndex); + settings->selectElectrodes (selection); + + settings->availableReferences.add ("Ext"); + settings->availableReferences.add ("Tip1"); + + if (shankCount == 4) + { + settings->availableReferences.add ("Tip2"); + settings->availableReferences.add ("Tip3"); + settings->availableReferences.add ("Tip4"); + } + + if (shankCount == 1) + { + for (const auto& [_, config] : electrodeConfigurationSingleShank) + { + settings->availableElectrodeConfigurations.add (config); + } + } + else if (shankCount == 4) + { + for (const auto& [_, config] : electrodeConfigurationQuadShank) + { + settings->availableElectrodeConfigurations.add (config); + } + } } diff --git a/Source/Devices/Neuropixels2e.h b/Source/Devices/Neuropixels2e.h index 00682a5..1e5f541 100644 --- a/Source/Devices/Neuropixels2e.h +++ b/Source/Devices/Neuropixels2e.h @@ -1,321 +1,338 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #pragma once -#include "../OnixDevice.h" #include "../I2CRegisterContext.h" #include "../NeuropixelsComponents.h" +#include "../OnixDevice.h" #include "DS90UB9x.h" namespace OnixSourcePlugin { - enum class NeuropixelsV2Reference : uint32_t - { - External, - Tip1, - Tip2, - Tip3, - Tip4 - }; +enum class NeuropixelsV2Reference : uint32_t +{ + External, + Tip1, + Tip2, + Tip3, + Tip4 +}; + +enum class NeuropixelsV2Status : uint32_t +{ + SR_OK = 1 << 7 +}; + +/* + Configures and streams data from a Neuropixels 2.0e device (aka a configured raw deserializer) +*/ +class Neuropixels2e : public INeuropixel, + public OnixDevice, + public I2CRegisterContext +{ +public: + Neuropixels2e (std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr); + + ~Neuropixels2e(); - enum class NeuropixelsV2Status : uint32_t - { - SR_OK = 1 << 7 - }; + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void stopAcquisition() override; + void processFrames() override; + void addSourceBuffers (OwnedArray& sourceBuffers) override; - /* - Configures and streams data from a Neuropixels 2.0e device (aka a configured raw deserializer) - */ - class Neuropixels2e : public INeuropixel, - public OnixDevice, - public I2CRegisterContext - { - public: - Neuropixels2e(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr); + int getNumProbes() const; - ~Neuropixels2e(); + static const int baseBitsPerChannel = 4; + static const int configurationBitCount = NeuropixelsV2eValues::numberOfChannels * baseBitsPerChannel / 2; - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void stopAcquisition() override; - void processFrames() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; + static const int referencePixelCount = 4; + static const int dummyPixelCount = 4; + static const int registersPerShank = NeuropixelsV2eValues::electrodesPerShank + referencePixelCount + dummyPixelCount; - int getNumProbes() const; + using BaseBitsArray = std::array, 2>; + using ShankBitsArray = std::array, 4>; - static const int baseBitsPerChannel = 4; - static const int configurationBitCount = NeuropixelsV2eValues::numberOfChannels * baseBitsPerChannel / 2; + BaseBitsArray static makeBaseBits (NeuropixelsV2Reference reference); + ShankBitsArray static makeShankBits (NeuropixelsV2Reference reference, std::array channelMap); - static const int referencePixelCount = 4; - static const int dummyPixelCount = 4; - static const int registersPerShank = NeuropixelsV2eValues::electrodesPerShank + referencePixelCount + dummyPixelCount; - - using BaseBitsArray = std::array, 2>; - using ShankBitsArray = std::array, 4>; - - BaseBitsArray static makeBaseBits(NeuropixelsV2Reference reference); - ShankBitsArray static makeShankBits(NeuropixelsV2Reference reference, std::array channelMap); - - template - void writeShiftRegister(uint32_t srAddress, std::bitset bits); - - void setGainCorrectionFile(int index, std::string filename); - std::string getGainCorrectionFile(int index); - - // INeuropixel Methods - - std::vector selectElectrodeConfiguration(int electrodeConfigurationIndex) override; - uint64_t getProbeSerialNumber(int index) override; - void defineMetadata(ProbeSettings*, int); - void setSettings(ProbeSettings* settings_, int index) override; - static OnixDeviceType getDeviceType(); - - private: - - static constexpr int NumberOfProbes = 2; - - static constexpr uint16_t NumberOfAdcBins = 4096; - static constexpr float DataMidpoint = NumberOfAdcBins / 2; - - DataBuffer* amplifierBuffer[NumberOfProbes]; - - std::array probeSN; - std::array gainCorrection; - std::array gainCorrectionFilePath; - - void createDataStream(int n); - - uint64_t getProbeSN(uint8_t probeSelect); - void configureSerDes(); - void setProbeSupply(bool); - void resetProbes(); - - void selectProbe(uint8_t probeSelect); - void configureProbeStreaming(); - void writeConfiguration(ProbeSettings*); - - NeuropixelsV2Reference getReference(int); - static std::string getShankName(uint32_t shiftRegisterAddress); - - void selectElectrodesInRange(std::vector& selection, int startIndex, int numberOfElectrodes); - void selectElectrodesAcrossShanks(std::vector& selection, int startIndex, int numberOfElectrodes); - - int m_numProbes = 0; - - const float sampleRate = 30000.0f; - static const int numFrames = 10; - static const int numSamples = numberOfChannels * numFrames; - - std::array, NumberOfProbes> samples {}; - - std::array, NumberOfProbes> sampleNumbers {}; - std::array, NumberOfProbes> timestamps {}; - std::array, NumberOfProbes> eventCodes {}; - - std::array frameCount; - std::array sampleNumber; - - std::unique_ptr serializer; - std::unique_ptr deserializer; - std::unique_ptr flex; - std::unique_ptr probeControl; - - static const int ProbeI2CAddress = 0x10; - static const int FlexAddress = 0x50; - - static const int ProbeAddress = 0x10; - static const int FlexEEPROMAddress = 0x50; - - static const uint32_t GPO10SupplyMask = 1 << 3; // Used to turn on VDDA analog supply - static const uint32_t GPO10ResetMask = 1 << 7; // Used to issue full reset commands to probes - static const uint8_t DefaultGPO10Config = 0b00010001; // NPs in reset, VDDA not enabled - static const uint8_t NoProbeSelected = 0b00010001; // No probes selected - static const uint8_t ProbeASelected = 0b00011001; // TODO: Changes in Rev. B of headstage - static const uint8_t ProbeBSelected = 0b10011001; - - static const int FramesPerSuperFrame = 16; - static const int AdcsPerProbe = 24; - static const int ChannelCount = 384; - static const int FrameWords = 36; // TRASH TRASH TRASH 0 ADC0 ADC8 ADC16 0 ADC1 ADC9 ADC17 0 ... ADC7 ADC15 ADC23 0 - - // unmanaged register map - static const uint32_t OP_MODE = 0x00; - static const uint32_t REC_MODE = 0x01; - static const uint32_t CAL_MODE = 0x02; - static const uint32_t ADC_CONFIG = 0x03; - static const uint32_t TEST_CONFIG1 = 0x04; - static const uint32_t TEST_CONFIG2 = 0x05; - static const uint32_t TEST_CONFIG3 = 0x06; - static const uint32_t TEST_CONFIG4 = 0x07; - static const uint32_t TEST_CONFIG5 = 0x08; - static const uint32_t STATUS = 0x09; - static const uint32_t SUPERSYNC0 = 0x0A; - static const uint32_t SUPERSYNC1 = 0x0B; - static const uint32_t SUPERSYNC2 = 0x0C; - static const uint32_t SUPERSYNC3 = 0x0D; - static const uint32_t SUPERSYNC4 = 0x0E; - static const uint32_t SUPERSYNC5 = 0x0F; - static const uint32_t SUPERSYNC6 = 0x10; - static const uint32_t SUPERSYNC7 = 0x11; - static const uint32_t SUPERSYNC8 = 0x12; - static const uint32_t SUPERSYNC9 = 0x13; - static const uint32_t SUPERSYNC10 = 0x14; - static const uint32_t SUPERSYNC11 = 0x15; - static const uint32_t SR_CHAIN6 = 0x16; // Odd channel base config - static const uint32_t SR_CHAIN5 = 0x17; // Even channel base config - static const uint32_t SR_CHAIN4 = 0x18; // Shank 4 - static const uint32_t SR_CHAIN3 = 0x19; // Shank 3 - static const uint32_t SR_CHAIN2 = 0x1A; // Shank 2 - static const uint32_t SR_CHAIN1 = 0x1B; // Shank 1 - static const uint32_t SR_LENGTH2 = 0x1C; - static const uint32_t SR_LENGTH1 = 0x1D; - static const uint32_t PROBE_ID = 0x1E; - static const uint32_t SOFT_RESET = 0x1F; - - const uint32_t OFFSET_PROBE_SN = 0x00; - const uint32_t OFFSET_FLEX_VERSION = 0x10; - const uint32_t OFFSET_FLEX_REVISION = 0x11; - const uint32_t OFFSET_FLEX_PN = 0x20; - const uint32_t OFFSET_PROBE_PN = 0x40; - - static inline const std::array adcIndices = { - 0, 1, 2, - 4, 5, 6, - 8, 9, 10, - 12, 13, 14, - 16, 17, 18, - 20, 21, 22, - 24, 25, 26, - 28, 29, 30 - }; - - static inline const std::array, AdcsPerProbe> rawToChannel = { { - { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 }, // Data Index 9, ADC 0 - { 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158 }, // Data Index 10, ADC 8 - { 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286 }, // Data Index 11, ADC 16 - - { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 }, // Data Index 13, ADC 1 - { 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159 }, // Data Index 14, ADC 9 - { 257, 259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287 }, // Data Index 15, ADC 17 - - { 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62 }, // Data Index 17, ADC 2 - { 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190 }, // Data Index 18, ADC 10 - { 288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310, 312, 314, 316, 318 }, // Data Index 19, ADC 18 - - { 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63 }, // Data Index 21, ADC 3 - { 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191 }, // Data Index 22, ADC 11 - { 289, 291, 293, 295, 297, 299, 301, 303, 305, 307, 309, 311, 313, 315, 317, 319 }, // Data Index 23, ADC 19 - - { 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94 }, // Data Index 25, ADC 4 - { 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222 }, // Data Index 26, ADC 12 - { 320, 322, 324, 326, 328, 330, 332, 334, 336, 338, 340, 342, 344, 346, 348, 350 }, // Data Index 27, ADC 20 - - { 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95 }, // Data Index 29, ADC 5 - { 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223 }, // Data Index 30, ADC 13 - { 321, 323, 325, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349, 351 }, // Data Index 31, ADC 21 - - { 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126 }, // Data Index 33, ADC 6 - { 224, 226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254 }, // Data Index 34, ADC 14 - { 352, 354, 356, 358, 360, 362, 364, 366, 368, 370, 372, 374, 376, 378, 380, 382 }, // Data Index 35, ADC 22 - - { 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127 }, // Data Index 37, ADC 7 - { 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 }, // Data Index 38, ADC 15 - { 353, 355, 357, 359, 361, 363, 365, 367, 369, 371, 373, 375, 377, 379, 381, 383 }, // Data Index 39, ADC 23 - } }; - - enum class ElectrodeConfigurationSingleShank : int32_t - { - BankA = 0, - BankB = 1, - BankC = 2, - BankD = 3 - }; - - std::map electrodeConfigurationSingleShank = { - {ElectrodeConfigurationSingleShank::BankA, "Bank A"}, - {ElectrodeConfigurationSingleShank::BankB, "Bank B"}, - {ElectrodeConfigurationSingleShank::BankC, "Bank C"}, - {ElectrodeConfigurationSingleShank::BankD, "Bank D"} - }; - - enum class ElectrodeConfigurationQuadShank : int32_t - { - Shank1BankA = 0, - Shank1BankB, - Shank1BankC, - Shank2BankA, - Shank2BankB, - Shank2BankC, - Shank3BankA, - Shank3BankB, - Shank3BankC, - Shank4BankA, - Shank4BankB, - Shank4BankC, - AllShanks1To96, - AllShanks97To192, - AllShanks193To288, - AllShanks289To384, - AllShanks385To480, - AllShanks481To576, - AllShanks577To672, - AllShanks673To768, - AllShanks769To864, - AllShanks865To960, - AllShanks961To1056, - AllShanks1057To1152, - AllShanks1153To1248 - }; - - std::map electrodeConfigurationQuadShank = { - {ElectrodeConfigurationQuadShank::Shank1BankA, "Shank 1 Bank A"}, - {ElectrodeConfigurationQuadShank::Shank1BankB, "Shank 1 Bank B"}, - {ElectrodeConfigurationQuadShank::Shank1BankC, "Shank 1 Bank C"}, - {ElectrodeConfigurationQuadShank::Shank2BankA, "Shank 2 Bank A"}, - {ElectrodeConfigurationQuadShank::Shank2BankB, "Shank 2 Bank B"}, - {ElectrodeConfigurationQuadShank::Shank2BankC, "Shank 2 Bank C"}, - {ElectrodeConfigurationQuadShank::Shank3BankA, "Shank 3 Bank A"}, - {ElectrodeConfigurationQuadShank::Shank3BankB, "Shank 3 Bank B"}, - {ElectrodeConfigurationQuadShank::Shank3BankC, "Shank 3 Bank C"}, - {ElectrodeConfigurationQuadShank::Shank4BankA, "Shank 4 Bank A"}, - {ElectrodeConfigurationQuadShank::Shank4BankB, "Shank 4 Bank B"}, - {ElectrodeConfigurationQuadShank::Shank4BankC, "Shank 4 Bank C"}, - {ElectrodeConfigurationQuadShank::AllShanks1To96, "All Shanks 1-96"}, - {ElectrodeConfigurationQuadShank::AllShanks97To192, "All Shanks 97-192"}, - {ElectrodeConfigurationQuadShank::AllShanks193To288, "All Shanks 193-288"}, - {ElectrodeConfigurationQuadShank::AllShanks289To384, "All Shanks 289-384"}, - {ElectrodeConfigurationQuadShank::AllShanks385To480, "All Shanks 385-480"}, - {ElectrodeConfigurationQuadShank::AllShanks481To576, "All Shanks 481-576"}, - {ElectrodeConfigurationQuadShank::AllShanks577To672, "All Shanks 577-672"}, - {ElectrodeConfigurationQuadShank::AllShanks673To768, "All Shanks 673-768"}, - {ElectrodeConfigurationQuadShank::AllShanks769To864, "All Shanks 769-864"}, - {ElectrodeConfigurationQuadShank::AllShanks865To960, "All Shanks 865-960"}, - {ElectrodeConfigurationQuadShank::AllShanks961To1056, "All Shanks 961-1056"}, - {ElectrodeConfigurationQuadShank::AllShanks1057To1152, "All Shanks 1057-1152"}, - {ElectrodeConfigurationQuadShank::AllShanks1153To1248, "All Shanks 1153-1248"} - }; - - JUCE_LEAK_DETECTOR(Neuropixels2e); - }; -} + template + void writeShiftRegister (uint32_t srAddress, std::bitset bits); + + void setGainCorrectionFile (int index, std::string filename); + std::string getGainCorrectionFile (int index); + + // INeuropixel Methods + + std::vector selectElectrodeConfiguration (int electrodeConfigurationIndex) override; + uint64_t getProbeSerialNumber (int index) override; + void defineMetadata (ProbeSettings*, int); + void setSettings (ProbeSettings* settings_, int index) override; + static OnixDeviceType getDeviceType(); + +private: + static constexpr int NumberOfProbes = 2; + + static constexpr uint16_t NumberOfAdcBins = 4096; + static constexpr float DataMidpoint = NumberOfAdcBins / 2; + + DataBuffer* amplifierBuffer[NumberOfProbes]; + + std::array probeSN; + std::array gainCorrection; + std::array gainCorrectionFilePath; + + void createDataStream (int n); + + uint64_t getProbeSN (uint8_t probeSelect); + void configureSerDes(); + void setProbeSupply (bool); + void resetProbes(); + + void selectProbe (uint8_t probeSelect); + void configureProbeStreaming(); + void writeConfiguration (ProbeSettings*); + + NeuropixelsV2Reference getReference (int); + static std::string getShankName (uint32_t shiftRegisterAddress); + + void selectElectrodesInRange (std::vector& selection, int startIndex, int numberOfElectrodes); + void selectElectrodesAcrossShanks (std::vector& selection, int startIndex, int numberOfElectrodes); + + int m_numProbes = 0; + + const float sampleRate = 30000.0f; + static const int numFrames = 10; + static const int numSamples = numberOfChannels * numFrames; + + std::array, NumberOfProbes> samples {}; + + std::array, NumberOfProbes> sampleNumbers {}; + std::array, NumberOfProbes> timestamps {}; + std::array, NumberOfProbes> eventCodes {}; + + std::array frameCount; + std::array sampleNumber; + + std::unique_ptr serializer; + std::unique_ptr deserializer; + std::unique_ptr flex; + std::unique_ptr probeControl; + + static const int ProbeI2CAddress = 0x10; + static const int FlexAddress = 0x50; + + static const int ProbeAddress = 0x10; + static const int FlexEEPROMAddress = 0x50; + + static const uint32_t GPO10SupplyMask = 1 << 3; // Used to turn on VDDA analog supply + static const uint32_t GPO10ResetMask = 1 << 7; // Used to issue full reset commands to probes + static const uint8_t DefaultGPO10Config = 0b00010001; // NPs in reset, VDDA not enabled + static const uint8_t NoProbeSelected = 0b00010001; // No probes selected + static const uint8_t ProbeASelected = 0b00011001; // TODO: Changes in Rev. B of headstage + static const uint8_t ProbeBSelected = 0b10011001; + + static const int FramesPerSuperFrame = 16; + static const int AdcsPerProbe = 24; + static const int ChannelCount = 384; + static const int FrameWords = 36; // TRASH TRASH TRASH 0 ADC0 ADC8 ADC16 0 ADC1 ADC9 ADC17 0 ... ADC7 ADC15 ADC23 0 + + // unmanaged register map + static const uint32_t OP_MODE = 0x00; + static const uint32_t REC_MODE = 0x01; + static const uint32_t CAL_MODE = 0x02; + static const uint32_t ADC_CONFIG = 0x03; + static const uint32_t TEST_CONFIG1 = 0x04; + static const uint32_t TEST_CONFIG2 = 0x05; + static const uint32_t TEST_CONFIG3 = 0x06; + static const uint32_t TEST_CONFIG4 = 0x07; + static const uint32_t TEST_CONFIG5 = 0x08; + static const uint32_t STATUS = 0x09; + static const uint32_t SUPERSYNC0 = 0x0A; + static const uint32_t SUPERSYNC1 = 0x0B; + static const uint32_t SUPERSYNC2 = 0x0C; + static const uint32_t SUPERSYNC3 = 0x0D; + static const uint32_t SUPERSYNC4 = 0x0E; + static const uint32_t SUPERSYNC5 = 0x0F; + static const uint32_t SUPERSYNC6 = 0x10; + static const uint32_t SUPERSYNC7 = 0x11; + static const uint32_t SUPERSYNC8 = 0x12; + static const uint32_t SUPERSYNC9 = 0x13; + static const uint32_t SUPERSYNC10 = 0x14; + static const uint32_t SUPERSYNC11 = 0x15; + static const uint32_t SR_CHAIN6 = 0x16; // Odd channel base config + static const uint32_t SR_CHAIN5 = 0x17; // Even channel base config + static const uint32_t SR_CHAIN4 = 0x18; // Shank 4 + static const uint32_t SR_CHAIN3 = 0x19; // Shank 3 + static const uint32_t SR_CHAIN2 = 0x1A; // Shank 2 + static const uint32_t SR_CHAIN1 = 0x1B; // Shank 1 + static const uint32_t SR_LENGTH2 = 0x1C; + static const uint32_t SR_LENGTH1 = 0x1D; + static const uint32_t PROBE_ID = 0x1E; + static const uint32_t SOFT_RESET = 0x1F; + + const uint32_t OFFSET_PROBE_SN = 0x00; + const uint32_t OFFSET_FLEX_VERSION = 0x10; + const uint32_t OFFSET_FLEX_REVISION = 0x11; + const uint32_t OFFSET_FLEX_PN = 0x20; + const uint32_t OFFSET_PROBE_PN = 0x40; + + static inline const std::array adcIndices = { + 0, + 1, + 2, + 4, + 5, + 6, + 8, + 9, + 10, + 12, + 13, + 14, + 16, + 17, + 18, + 20, + 21, + 22, + 24, + 25, + 26, + 28, + 29, + 30 + }; + + static inline const std::array, AdcsPerProbe> rawToChannel = { + { + { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 }, // Data Index 9, ADC 0 + { 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158 }, // Data Index 10, ADC 8 + { 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286 }, // Data Index 11, ADC 16 + + { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 }, // Data Index 13, ADC 1 + { 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159 }, // Data Index 14, ADC 9 + { 257, 259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287 }, // Data Index 15, ADC 17 + + { 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62 }, // Data Index 17, ADC 2 + { 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190 }, // Data Index 18, ADC 10 + { 288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310, 312, 314, 316, 318 }, // Data Index 19, ADC 18 + + { 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63 }, // Data Index 21, ADC 3 + { 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191 }, // Data Index 22, ADC 11 + { 289, 291, 293, 295, 297, 299, 301, 303, 305, 307, 309, 311, 313, 315, 317, 319 }, // Data Index 23, ADC 19 + + { 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94 }, // Data Index 25, ADC 4 + { 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222 }, // Data Index 26, ADC 12 + { 320, 322, 324, 326, 328, 330, 332, 334, 336, 338, 340, 342, 344, 346, 348, 350 }, // Data Index 27, ADC 20 + + { 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95 }, // Data Index 29, ADC 5 + { 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223 }, // Data Index 30, ADC 13 + { 321, 323, 325, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349, 351 }, // Data Index 31, ADC 21 + + { 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126 }, // Data Index 33, ADC 6 + { 224, 226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254 }, // Data Index 34, ADC 14 + { 352, 354, 356, 358, 360, 362, 364, 366, 368, 370, 372, 374, 376, 378, 380, 382 }, // Data Index 35, ADC 22 + + { 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127 }, // Data Index 37, ADC 7 + { 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 }, // Data Index 38, ADC 15 + { 353, 355, 357, 359, 361, 363, 365, 367, 369, 371, 373, 375, 377, 379, 381, 383 }, // Data Index 39, ADC 23 + } + }; + + enum class ElectrodeConfigurationSingleShank : int32_t + { + BankA = 0, + BankB = 1, + BankC = 2, + BankD = 3 + }; + + std::map electrodeConfigurationSingleShank = { + { ElectrodeConfigurationSingleShank::BankA, "Bank A" }, + { ElectrodeConfigurationSingleShank::BankB, "Bank B" }, + { ElectrodeConfigurationSingleShank::BankC, "Bank C" }, + { ElectrodeConfigurationSingleShank::BankD, "Bank D" } + }; + + enum class ElectrodeConfigurationQuadShank : int32_t + { + Shank1BankA = 0, + Shank1BankB, + Shank1BankC, + Shank2BankA, + Shank2BankB, + Shank2BankC, + Shank3BankA, + Shank3BankB, + Shank3BankC, + Shank4BankA, + Shank4BankB, + Shank4BankC, + AllShanks1To96, + AllShanks97To192, + AllShanks193To288, + AllShanks289To384, + AllShanks385To480, + AllShanks481To576, + AllShanks577To672, + AllShanks673To768, + AllShanks769To864, + AllShanks865To960, + AllShanks961To1056, + AllShanks1057To1152, + AllShanks1153To1248 + }; + + std::map electrodeConfigurationQuadShank = { + { ElectrodeConfigurationQuadShank::Shank1BankA, "Shank 1 Bank A" }, + { ElectrodeConfigurationQuadShank::Shank1BankB, "Shank 1 Bank B" }, + { ElectrodeConfigurationQuadShank::Shank1BankC, "Shank 1 Bank C" }, + { ElectrodeConfigurationQuadShank::Shank2BankA, "Shank 2 Bank A" }, + { ElectrodeConfigurationQuadShank::Shank2BankB, "Shank 2 Bank B" }, + { ElectrodeConfigurationQuadShank::Shank2BankC, "Shank 2 Bank C" }, + { ElectrodeConfigurationQuadShank::Shank3BankA, "Shank 3 Bank A" }, + { ElectrodeConfigurationQuadShank::Shank3BankB, "Shank 3 Bank B" }, + { ElectrodeConfigurationQuadShank::Shank3BankC, "Shank 3 Bank C" }, + { ElectrodeConfigurationQuadShank::Shank4BankA, "Shank 4 Bank A" }, + { ElectrodeConfigurationQuadShank::Shank4BankB, "Shank 4 Bank B" }, + { ElectrodeConfigurationQuadShank::Shank4BankC, "Shank 4 Bank C" }, + { ElectrodeConfigurationQuadShank::AllShanks1To96, "All Shanks 1-96" }, + { ElectrodeConfigurationQuadShank::AllShanks97To192, "All Shanks 97-192" }, + { ElectrodeConfigurationQuadShank::AllShanks193To288, "All Shanks 193-288" }, + { ElectrodeConfigurationQuadShank::AllShanks289To384, "All Shanks 289-384" }, + { ElectrodeConfigurationQuadShank::AllShanks385To480, "All Shanks 385-480" }, + { ElectrodeConfigurationQuadShank::AllShanks481To576, "All Shanks 481-576" }, + { ElectrodeConfigurationQuadShank::AllShanks577To672, "All Shanks 577-672" }, + { ElectrodeConfigurationQuadShank::AllShanks673To768, "All Shanks 673-768" }, + { ElectrodeConfigurationQuadShank::AllShanks769To864, "All Shanks 769-864" }, + { ElectrodeConfigurationQuadShank::AllShanks865To960, "All Shanks 865-960" }, + { ElectrodeConfigurationQuadShank::AllShanks961To1056, "All Shanks 961-1056" }, + { ElectrodeConfigurationQuadShank::AllShanks1057To1152, "All Shanks 1057-1152" }, + { ElectrodeConfigurationQuadShank::AllShanks1153To1248, "All Shanks 1153-1248" } + }; + + JUCE_LEAK_DETECTOR (Neuropixels2e); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/OutputClock.cpp b/Source/Devices/OutputClock.cpp index 16358e7..cdccc1b 100644 --- a/Source/Devices/OutputClock.cpp +++ b/Source/Devices/OutputClock.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,57 +24,67 @@ using namespace OnixSourcePlugin; -OutputClock::OutputClock(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) - : OnixDevice(name, hubName, OutputClock::getDeviceType(), deviceIdx_, oni_ctx) +OutputClock::OutputClock (std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) + : OnixDevice (name, hubName, OutputClock::getDeviceType(), deviceIdx_, oni_ctx) { } OnixDeviceType OutputClock::getDeviceType() { - return OnixDeviceType::OUTPUTCLOCK; + return OnixDeviceType::OUTPUTCLOCK; } int OutputClock::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + throw error_str ("Device context is not initialized properly for " + getName()); - setEnabled(true); - return ONI_ESUCCESS; + setEnabled (true); + return ONI_ESUCCESS; }; bool OutputClock::updateSettings() { - oni_reg_val_t baseFreqHz; - int rc = deviceContext->readRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::BASE_FREQ_HZ, &baseFreqHz); + oni_reg_val_t baseFreqHz; + int rc = deviceContext->readRegister (deviceIdx, (oni_reg_addr_t) OutputClockRegisters::BASE_FREQ_HZ, &baseFreqHz); - auto periodCycles = (uint32_t)(baseFreqHz / frequencyHz); - auto h = (uint32_t)(periodCycles * ((double)dutyCycle / 100.0)); - auto l = periodCycles - h; - auto delayCycles = (uint32_t)(delay * baseFreqHz); + auto periodCycles = (uint32_t) (baseFreqHz / frequencyHz); + auto h = (uint32_t) (periodCycles * ((double) dutyCycle / 100.0)); + auto l = periodCycles - h; + auto delayCycles = (uint32_t) (delay * baseFreqHz); - rc = deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::CLOCK_GATE, 1); if (rc != ONI_ESUCCESS) return false; + rc = deviceContext->writeRegister (deviceIdx, (oni_reg_addr_t) OutputClockRegisters::CLOCK_GATE, 1); + if (rc != ONI_ESUCCESS) + return false; - rc = deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::HIGH_CYCLES, h); if (rc != ONI_ESUCCESS) return false; - rc = deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::LOW_CYCLES, l); if (rc != ONI_ESUCCESS) return false; - rc = deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::DELAY_CYCLES, delayCycles); if (rc != ONI_ESUCCESS) return false; + rc = deviceContext->writeRegister (deviceIdx, (oni_reg_addr_t) OutputClockRegisters::HIGH_CYCLES, h); + if (rc != ONI_ESUCCESS) + return false; + rc = deviceContext->writeRegister (deviceIdx, (oni_reg_addr_t) OutputClockRegisters::LOW_CYCLES, l); + if (rc != ONI_ESUCCESS) + return false; + rc = deviceContext->writeRegister (deviceIdx, (oni_reg_addr_t) OutputClockRegisters::DELAY_CYCLES, delayCycles); + if (rc != ONI_ESUCCESS) + return false; - rc = deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::GATE_RUN, gateRun ? 1 : 0); if (rc != ONI_ESUCCESS) return false; + rc = deviceContext->writeRegister (deviceIdx, (oni_reg_addr_t) OutputClockRegisters::GATE_RUN, gateRun ? 1 : 0); + if (rc != ONI_ESUCCESS) + return false; - return true; + return true; } void OutputClock::startAcquisition() { } -void OutputClock::addSourceBuffers(OwnedArray& sourceBuffers) +void OutputClock::addSourceBuffers (OwnedArray& sourceBuffers) { } -void OutputClock::addFrame(oni_frame_t* frame) +void OutputClock::addFrame (oni_frame_t* frame) { - oni_destroy_frame(frame); + oni_destroy_frame (frame); } void OutputClock::processFrames() @@ -83,46 +93,47 @@ void OutputClock::processFrames() double OutputClock::getFrequencyHz() const { - return frequencyHz; + return frequencyHz; } -void OutputClock::setFrequencyHz(double frequency) +void OutputClock::setFrequencyHz (double frequency) { - frequencyHz = frequency; + frequencyHz = frequency; } int32_t OutputClock::getDutyCycle() const { - return dutyCycle; + return dutyCycle; } -void OutputClock::setDutyCycle(int32_t dutyCycle_) +void OutputClock::setDutyCycle (int32_t dutyCycle_) { - dutyCycle = dutyCycle_; + dutyCycle = dutyCycle_; } int32_t OutputClock::getDelay() const { - return delay; + return delay; } -void OutputClock::setDelay(int32_t delay_) +void OutputClock::setDelay (int32_t delay_) { - delay = delay_; + delay = delay_; } bool OutputClock::getGateRun() const { - return gateRun; + return gateRun; } -void OutputClock::setGateRun(bool gate, bool writeToRegister) +void OutputClock::setGateRun (bool gate, bool writeToRegister) { - gateRun = gate; - if (writeToRegister) writeGateRunRegister(); + gateRun = gate; + if (writeToRegister) + writeGateRunRegister(); } void OutputClock::writeGateRunRegister() { - deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::GATE_RUN, gateRun ? 1 : 0); + deviceContext->writeRegister (deviceIdx, (oni_reg_addr_t) OutputClockRegisters::GATE_RUN, gateRun ? 1 : 0); } diff --git a/Source/Devices/OutputClock.h b/Source/Devices/OutputClock.h index b2573a9..c91f2f4 100644 --- a/Source/Devices/OutputClock.h +++ b/Source/Devices/OutputClock.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -26,56 +26,55 @@ namespace OnixSourcePlugin { - enum class OutputClockRegisters : uint32_t - { - NULL_REGISTER = 0, - CLOCK_GATE = 1, - HIGH_CYCLES = 2, - LOW_CYCLES = 3, - DELAY_CYCLES = 4, - GATE_RUN = 5, - BASE_FREQ_HZ = 6 - }; - - /* - Configures an OutputClock device on a Breakout Board - */ - class OutputClock : public OnixDevice - { - public: - OutputClock(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); - - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; - void addFrame(oni_frame_t* frame) override; - void processFrames() override; - - double getFrequencyHz() const; - void setFrequencyHz(double frequency); - - int32_t getDutyCycle() const; - void setDutyCycle(int32_t dutyCycle_); - - int32_t getDelay() const; - void setDelay(int32_t delay_); - - bool getGateRun() const; - void setGateRun(bool gate, bool writeToRegister = false); - - static OnixDeviceType getDeviceType(); - - private: - - double frequencyHz = 1e6; - int32_t dutyCycle = 50; - int32_t delay = 0; - - bool gateRun = true; - - void writeGateRunRegister(); - - JUCE_LEAK_DETECTOR(OutputClock); - }; -} +enum class OutputClockRegisters : uint32_t +{ + NULL_REGISTER = 0, + CLOCK_GATE = 1, + HIGH_CYCLES = 2, + LOW_CYCLES = 3, + DELAY_CYCLES = 4, + GATE_RUN = 5, + BASE_FREQ_HZ = 6 +}; + +/* + Configures an OutputClock device on a Breakout Board +*/ +class OutputClock : public OnixDevice +{ +public: + OutputClock (std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); + + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void addSourceBuffers (OwnedArray& sourceBuffers) override; + void addFrame (oni_frame_t* frame) override; + void processFrames() override; + + double getFrequencyHz() const; + void setFrequencyHz (double frequency); + + int32_t getDutyCycle() const; + void setDutyCycle (int32_t dutyCycle_); + + int32_t getDelay() const; + void setDelay (int32_t delay_); + + bool getGateRun() const; + void setGateRun (bool gate, bool writeToRegister = false); + + static OnixDeviceType getDeviceType(); + +private: + double frequencyHz = 1e6; + int32_t dutyCycle = 50; + int32_t delay = 0; + + bool gateRun = true; + + void writeGateRunRegister(); + + JUCE_LEAK_DETECTOR (OutputClock); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/PolledBno055.cpp b/Source/Devices/PolledBno055.cpp index f46c94b..db8cc37 100644 --- a/Source/Devices/PolledBno055.cpp +++ b/Source/Devices/PolledBno055.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,318 +24,318 @@ using namespace OnixSourcePlugin; -PolledBno055::PolledBno055(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx) - : OnixDevice(name, hubName, PolledBno055::getDeviceType(), deviceIdx_, ctx, true), - I2CRegisterContext(Bno055Address, deviceIdx_, ctx), - Thread("Polled BNO: " + name) +PolledBno055::PolledBno055 (std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx) + : OnixDevice (name, hubName, PolledBno055::getDeviceType(), deviceIdx_, ctx, true), + I2CRegisterContext (Bno055Address, deviceIdx_, ctx), + Thread ("Polled BNO: " + name) { - auto streamIdentifier = getStreamIdentifier(); - - std::string port = getPortName(deviceIdx); - StreamInfo eulerAngleStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Euler" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Euler angle", - streamIdentifier, - 3, - SampleRate, - "Eul", - ContinuousChannel::Type::AUX, - EulerAngleScale, - "Degrees", - { "Y", "R", "P" }, - "euler", - { "y", "r", "p" } - ); - streamInfos.add(eulerAngleStream); - - StreamInfo quaternionStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Quaternion" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Quaternion", - streamIdentifier, - 4, - SampleRate, - "Quat", - ContinuousChannel::Type::AUX, - QuaternionScale, - "", - { "W", "X", "Y", "Z" }, - "quaternion", - { "w", "x", "y", "z" } - ); - streamInfos.add(quaternionStream); - - StreamInfo accelerationStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Acceleration" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Acceleration", - streamIdentifier, - 3, - SampleRate, - "Acc", - ContinuousChannel::Type::AUX, - AccelerationScale, - "m / s ^ 2", - { "X", "Y", "Z" }, - "acceleration", - { "x","y","z" } - ); - streamInfos.add(accelerationStream); - - StreamInfo gravityStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Gravity" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Gravity", - streamIdentifier, - 3, - SampleRate, - "Grav", - ContinuousChannel::Type::AUX, - AccelerationScale, - "m/s^2", - { "X", "Y", "Z" }, - "gravity", - { "x", "y", "z" } - ); - streamInfos.add(gravityStream); - - StreamInfo temperatureStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Temperature" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Temperature", - streamIdentifier, - 1, - SampleRate, - "Temp", - ContinuousChannel::Type::AUX, - 1.0f, - "Celsius", - { "" }, - "temperature" - ); - streamInfos.add(temperatureStream); - - StreamInfo calibrationStatusStream = StreamInfo( - OnixDevice::createStreamName({ port, getHubName(), getName(), "Calibration" }), - "Bosch Bno055 9-axis inertial measurement unit (IMU) Calibration status", - streamIdentifier, - 4, - SampleRate, - "Cal", - ContinuousChannel::Type::AUX, - 1.0f, - "", - { "Mag", "Acc", "Gyr", "Sys" }, - "calibration", - { "magnetometer", "acceleration", "gyroscope", "system" } - ); - streamInfos.add(calibrationStatusStream); - - for (int i = 0; i < NumFrames; i++) - eventCodes[i] = 0; + auto streamIdentifier = getStreamIdentifier(); + + std::string port = getPortName (deviceIdx); + StreamInfo eulerAngleStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Euler" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Euler angle", + streamIdentifier, + 3, + SampleRate, + "Eul", + ContinuousChannel::Type::AUX, + EulerAngleScale, + "Degrees", + { "Y", "R", "P" }, + "euler", + { "y", "r", "p" }); + streamInfos.add (eulerAngleStream); + + StreamInfo quaternionStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Quaternion" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Quaternion", + streamIdentifier, + 4, + SampleRate, + "Quat", + ContinuousChannel::Type::AUX, + QuaternionScale, + "", + { "W", "X", "Y", "Z" }, + "quaternion", + { "w", "x", "y", "z" }); + streamInfos.add (quaternionStream); + + StreamInfo accelerationStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Acceleration" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Acceleration", + streamIdentifier, + 3, + SampleRate, + "Acc", + ContinuousChannel::Type::AUX, + AccelerationScale, + "m / s ^ 2", + { "X", "Y", "Z" }, + "acceleration", + { "x", "y", "z" }); + streamInfos.add (accelerationStream); + + StreamInfo gravityStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Gravity" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Gravity", + streamIdentifier, + 3, + SampleRate, + "Grav", + ContinuousChannel::Type::AUX, + AccelerationScale, + "m/s^2", + { "X", "Y", "Z" }, + "gravity", + { "x", "y", "z" }); + streamInfos.add (gravityStream); + + StreamInfo temperatureStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Temperature" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Temperature", + streamIdentifier, + 1, + SampleRate, + "Temp", + ContinuousChannel::Type::AUX, + 1.0f, + "Celsius", + { "" }, + "temperature"); + streamInfos.add (temperatureStream); + + StreamInfo calibrationStatusStream = StreamInfo ( + OnixDevice::createStreamName ({ port, getHubName(), getName(), "Calibration" }), + "Bosch Bno055 9-axis inertial measurement unit (IMU) Calibration status", + streamIdentifier, + 4, + SampleRate, + "Cal", + ContinuousChannel::Type::AUX, + 1.0f, + "", + { "Mag", "Acc", "Gyr", "Sys" }, + "calibration", + { "magnetometer", "acceleration", "gyroscope", "system" }); + streamInfos.add (calibrationStatusStream); + + for (int i = 0; i < NumFrames; i++) + eventCodes[i] = 0; } PolledBno055::~PolledBno055() { - stopThread(500); + stopThread (500); } OnixDeviceType PolledBno055::getDeviceType() { - return OnixDeviceType::POLLEDBNO; + return OnixDeviceType::POLLEDBNO; } int PolledBno055::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); - - deserializer = std::make_unique(DS90UB9x::DES_ADDR, deviceIdx, deviceContext); - uint32_t alias = Bno055Address << 1; - int rc = deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID4, alias); - if (rc != ONI_ESUCCESS) - throw error_str("Error while writing bytes for " + getName()); - rc = deserializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias4, alias); - if (rc != ONI_ESUCCESS) - throw error_str("Error while writing bytes for " + getName()); - - return ONI_ESUCCESS; + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + throw error_str ("Device context is not initialized properly for " + getName()); + + deserializer = std::make_unique (DS90UB9x::DES_ADDR, deviceIdx, deviceContext); + uint32_t alias = Bno055Address << 1; + int rc = deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveID4, alias); + if (rc != ONI_ESUCCESS) + throw error_str ("Error while writing bytes for " + getName()); + rc = deserializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xDeserializerI2CRegister::SlaveAlias4, alias); + if (rc != ONI_ESUCCESS) + throw error_str ("Error while writing bytes for " + getName()); + + return ONI_ESUCCESS; } bool PolledBno055::updateSettings() { - if (!isEnabled()) return true; + if (! isEnabled()) + return true; - int rc = WriteByte(0x3E, 0x00); // Power mode normal - if (rc != ONI_ESUCCESS) return false; + int rc = WriteByte (0x3E, 0x00); // Power mode normal + if (rc != ONI_ESUCCESS) + return false; - rc = WriteByte(0x07, 0x00); // Page ID address 0 - if (rc != ONI_ESUCCESS) return false; + rc = WriteByte (0x07, 0x00); // Page ID address 0 + if (rc != ONI_ESUCCESS) + return false; - rc = WriteByte(0x3F, 0x00); // Internal oscillator - if (rc != ONI_ESUCCESS) return false; + rc = WriteByte (0x3F, 0x00); // Internal oscillator + if (rc != ONI_ESUCCESS) + return false; - rc = WriteByte(0x41, (uint32_t)axisMap); // Axis map config - if (rc != ONI_ESUCCESS) return false; + rc = WriteByte (0x41, (uint32_t) axisMap); // Axis map config + if (rc != ONI_ESUCCESS) + return false; - rc = WriteByte(0x42, (uint32_t)axisSign); // Axis sign - if (rc != ONI_ESUCCESS) return false; + rc = WriteByte (0x42, (uint32_t) axisSign); // Axis sign + if (rc != ONI_ESUCCESS) + return false; - rc = WriteByte(0x3D, 0x0C); // Operation mode is NDOF - if (rc != ONI_ESUCCESS) return false; + rc = WriteByte (0x3D, 0x0C); // Operation mode is NDOF + if (rc != ONI_ESUCCESS) + return false; - rc = set933I2cRate(I2cRate); + rc = set933I2cRate (I2cRate); - return rc == ONI_ESUCCESS; + return rc == ONI_ESUCCESS; } void PolledBno055::startAcquisition() { - sampleNumber = 0; - currentFrame = 0; + sampleNumber = 0; + currentFrame = 0; - previousTime = std::chrono::steady_clock::now(); + previousTime = std::chrono::steady_clock::now(); - if (isEnabled()) - startThread(); + if (isEnabled()) + startThread(); } void PolledBno055::run() { - while (!threadShouldExit()) - { - time_point now = std::chrono::steady_clock::now(); + while (! threadShouldExit()) + { + time_point now = std::chrono::steady_clock::now(); - std::chrono::milliseconds dur = std::chrono::duration_cast(now - previousTime); + std::chrono::milliseconds dur = std::chrono::duration_cast (now - previousTime); - // NB: If the interval has not passed yet, wait for the remaining duration - if (dur < TimerIntervalInMilliseconds) - { - std::this_thread::sleep_for(TimerIntervalInMilliseconds - dur); - } + // NB: If the interval has not passed yet, wait for the remaining duration + if (dur < TimerIntervalInMilliseconds) + { + std::this_thread::sleep_for (TimerIntervalInMilliseconds - dur); + } - pollFrame(); + pollFrame(); - previousTime += TimerIntervalInMilliseconds; - } + previousTime += TimerIntervalInMilliseconds; + } } void PolledBno055::stopAcquisition() { - if (isThreadRunning()) - stopThread(500); + if (isThreadRunning()) + stopThread (500); } -void PolledBno055::addFrame(oni_frame_t* frame) +void PolledBno055::addFrame (oni_frame_t* frame) { - oni_destroy_frame(frame); + oni_destroy_frame (frame); } -void PolledBno055::addSourceBuffers(OwnedArray& sourceBuffers) +void PolledBno055::addSourceBuffers (OwnedArray& sourceBuffers) { - sourceBuffers.add(new DataBuffer(NumberOfChannels, (int)SampleRate * bufferSizeInSeconds)); - bnoBuffer = sourceBuffers.getLast(); + sourceBuffers.add (new DataBuffer (NumberOfChannels, (int) SampleRate * bufferSizeInSeconds)); + bnoBuffer = sourceBuffers.getLast(); } void PolledBno055::processFrames() { } -int16_t PolledBno055::getInt16FromUint32(uint32_t value, bool getLowerValue) +int16_t PolledBno055::getInt16FromUint32 (uint32_t value, bool getLowerValue) { - return getLowerValue ? - static_cast(value & 0xFFFFu) : - static_cast((value & 0xFFFF0000u) >> 16); + return getLowerValue ? static_cast (value & 0xFFFFu) : static_cast ((value & 0xFFFF0000u) >> 16); } void PolledBno055::pollFrame() { - size_t offset = 0; - uint32_t value; + size_t offset = 0; + uint32_t value; + // Euler + ReadWord (EulerHeadingLsbAddress, 4, &value); + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, true) * EulerAngleScale; + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, false) * EulerAngleScale; - // Euler - ReadWord(EulerHeadingLsbAddress, 4, &value); - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, true) * EulerAngleScale; - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, false) * EulerAngleScale; + ReadWord (EulerHeadingLsbAddress + 4, 4, &value); + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, true) * EulerAngleScale; - ReadWord(EulerHeadingLsbAddress + 4, 4, &value); - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, true) * EulerAngleScale; + // Quaternion + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, false) * QuaternionScale; - // Quaternion - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, false) * QuaternionScale; + ReadWord (EulerHeadingLsbAddress + 8, 4, &value); + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, true) * QuaternionScale; + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, false) * QuaternionScale; - ReadWord(EulerHeadingLsbAddress + 8, 4, &value); - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, true) * QuaternionScale; - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, false) * QuaternionScale; + ReadWord (EulerHeadingLsbAddress + 12, 4, &value); + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, true) * QuaternionScale; - ReadWord(EulerHeadingLsbAddress + 12, 4, &value); - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, true) * QuaternionScale; + // Acceleration - // Acceleration + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, false) * AccelerationScale; - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, false) * AccelerationScale; + ReadWord (EulerHeadingLsbAddress + 16, 4, &value); + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, true) * AccelerationScale; + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, false) * AccelerationScale; - ReadWord(EulerHeadingLsbAddress + 16, 4, &value); - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, true) * AccelerationScale; - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, false) * AccelerationScale; + // Gravity - // Gravity + ReadWord (EulerHeadingLsbAddress + 20, 4, &value); + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, true) * AccelerationScale; + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, false) * AccelerationScale; - ReadWord(EulerHeadingLsbAddress + 20, 4, &value); - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, true) * AccelerationScale; - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, false) * AccelerationScale; + ReadWord (EulerHeadingLsbAddress + 24, 4, &value); + bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32 (value, true) * AccelerationScale; - ReadWord(EulerHeadingLsbAddress + 24, 4, &value); - bnoSamples[offset++ * NumFrames + currentFrame] = getInt16FromUint32(value, true) * AccelerationScale; + // Temperature - // Temperature + bnoSamples[offset++ * NumFrames + currentFrame] = static_cast ((value & 0xFF0000u) >> 16); - bnoSamples[offset++ * NumFrames + currentFrame] = static_cast((value & 0xFF0000u) >> 16); + // Calibration Status - // Calibration Status + oni_reg_val_t byte = static_cast ((value & 0xFF000000u) >> 24); - oni_reg_val_t byte = static_cast((value & 0xFF000000u) >> 24); + constexpr uint8_t statusMask = 0b11; - constexpr uint8_t statusMask = 0b11; + for (int i = 0; i < 4; i++) + { + bnoSamples[currentFrame + (offset + i) * NumFrames] = (byte & (statusMask << (2 * i))) >> (2 * i); + } - for (int i = 0; i < 4; i++) - { - bnoSamples[currentFrame + (offset + i) * NumFrames] = (byte & (statusMask << (2 * i))) >> (2 * i); - } + oni_reg_val_t timestampL = 0, timestampH = 0; + int rc = deviceContext->readRegister (deviceIdx, DS90UB9x::LASTI2CL, ×tampL); + if (rc != ONI_ESUCCESS) + return; + rc = (uint64_t) deviceContext->readRegister (deviceIdx, DS90UB9x::LASTI2CH, ×tampH); + if (rc != ONI_ESUCCESS) + return; - oni_reg_val_t timestampL = 0, timestampH = 0; - int rc = deviceContext->readRegister(deviceIdx, DS90UB9x::LASTI2CL, ×tampL); - if (rc != ONI_ESUCCESS) return; - rc = (uint64_t)deviceContext->readRegister(deviceIdx, DS90UB9x::LASTI2CH, ×tampH); - if (rc != ONI_ESUCCESS) return; + bnoTimestamps[currentFrame] = deviceContext->convertTimestampToSeconds ((uint64_t (timestampH) << 32) | uint64_t (timestampL)); - bnoTimestamps[currentFrame] = deviceContext->convertTimestampToSeconds((uint64_t(timestampH) << 32) | uint64_t(timestampL)); + sampleNumbers[currentFrame] = sampleNumber++; - sampleNumbers[currentFrame] = sampleNumber++; + currentFrame++; - currentFrame++; - - if (currentFrame >= NumFrames) - { - bnoBuffer->addToBuffer(bnoSamples.data(), sampleNumbers, bnoTimestamps, eventCodes, NumFrames); - currentFrame = 0; - } + if (currentFrame >= NumFrames) + { + bnoBuffer->addToBuffer (bnoSamples.data(), sampleNumbers, bnoTimestamps, eventCodes, NumFrames); + currentFrame = 0; + } } -int16_t PolledBno055::readInt16(uint32_t startAddress) +int16_t PolledBno055::readInt16 (uint32_t startAddress) { - uint32_t value = 0; - int rc = ReadWord(startAddress, 2, &value); + uint32_t value = 0; + int rc = ReadWord (startAddress, 2, &value); - if (rc != ONI_ESUCCESS) - return 0; + if (rc != ONI_ESUCCESS) + return 0; - return static_cast(value); + return static_cast (value); } -void PolledBno055::setBnoAxisMap(Bno055AxisMap map) +void PolledBno055::setBnoAxisMap (Bno055AxisMap map) { - axisMap = map; + axisMap = map; } -void PolledBno055::setBnoAxisSign(uint32_t sign) +void PolledBno055::setBnoAxisSign (uint32_t sign) { - axisSign = sign; + axisSign = sign; } diff --git a/Source/Devices/PolledBno055.h b/Source/Devices/PolledBno055.h index 4855993..c19c34b 100644 --- a/Source/Devices/PolledBno055.h +++ b/Source/Devices/PolledBno055.h @@ -1,124 +1,122 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #pragma once -#include "../OnixDevice.h" +#include + #include "../I2CRegisterContext.h" +#include "../OnixDevice.h" #include "DS90UB9x.h" #include "PortController.h" -#include - namespace OnixSourcePlugin { - class PolledBno055 : public OnixDevice, - public I2CRegisterContext, - public Thread - { - public: - - /** Constructor */ - PolledBno055(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr ctx); - - ~PolledBno055(); +class PolledBno055 : public OnixDevice, + public I2CRegisterContext, + public Thread +{ +public: + /** Constructor */ + PolledBno055 (std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr ctx); - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void stopAcquisition() override; - void addFrame(oni_frame_t*) override; - void processFrames() override; - void pollFrame(); - void addSourceBuffers(OwnedArray& sourceBuffers) override; + ~PolledBno055(); - void run() override; + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void stopAcquisition() override; + void addFrame (oni_frame_t*) override; + void processFrames() override; + void pollFrame(); + void addSourceBuffers (OwnedArray& sourceBuffers) override; - static OnixDeviceType getDeviceType(); + void run() override; - enum class Bno055AxisMap : uint32_t - { - XYZ = 0b00100100, // Specifies that X->X', Y->Y', Z->Z' (chip default). - XZY = 0b00011000, // Specifies that X->X', Z->Y', Y->Z' - YXZ = 0b00100001, // Specifies that Y->X', X->Y', Z->Z' - YZX = 0b00001001, // Specifies that Y->X', Z->Y', X->Z' - ZXY = 0b00010010, // Specifies that Z->X', X->Y', Y->Z' - ZYX = 0b00000110, // Specifies that Z->X', Y->Y', X->Z' - }; + static OnixDeviceType getDeviceType(); - enum class Bno055AxisSign : uint32_t - { - Default = 0b00000000, // Specifies that all axes are positive (chip default). - MirrorZ = 0b00000001, // Specifies that Z' axis should be mirrored. - MirrorY = 0b00000010, // Specifies that Y' axis should be mirrored. - MirrorX = 0b00000100 // Specifies that X' axis should be mirrored. - }; + enum class Bno055AxisMap : uint32_t + { + XYZ = 0b00100100, // Specifies that X->X', Y->Y', Z->Z' (chip default). + XZY = 0b00011000, // Specifies that X->X', Z->Y', Y->Z' + YXZ = 0b00100001, // Specifies that Y->X', X->Y', Z->Z' + YZX = 0b00001001, // Specifies that Y->X', Z->Y', X->Z' + ZXY = 0b00010010, // Specifies that Z->X', X->Y', Y->Z' + ZYX = 0b00000110, // Specifies that Z->X', Y->Y', X->Z' + }; - void setBnoAxisMap(Bno055AxisMap map); - void setBnoAxisSign(uint32_t sign); + enum class Bno055AxisSign : uint32_t + { + Default = 0b00000000, // Specifies that all axes are positive (chip default). + MirrorZ = 0b00000001, // Specifies that Z' axis should be mirrored. + MirrorY = 0b00000010, // Specifies that Y' axis should be mirrored. + MirrorX = 0b00000100 // Specifies that X' axis should be mirrored. + }; - private: + void setBnoAxisMap (Bno055AxisMap map); + void setBnoAxisSign (uint32_t sign); - using time_point = std::chrono::time_point; +private: + using time_point = std::chrono::time_point; - DataBuffer* bnoBuffer; + DataBuffer* bnoBuffer; - static constexpr int Bno055Address = 0x28; - static constexpr int EulerHeadingLsbAddress = 0x1A; + static constexpr int Bno055Address = 0x28; + static constexpr int EulerHeadingLsbAddress = 0x1A; - static constexpr double I2cRate = 400e3; + static constexpr double I2cRate = 400e3; - static constexpr int NumberOfBytes = 28; + static constexpr int NumberOfBytes = 28; - static constexpr float EulerAngleScale = 1.0f / 16; // 1 degree = 16 LSB - static constexpr float QuaternionScale = 1.0f / (1 << 14); // 1 = 2^14 LSB - static constexpr float AccelerationScale = 1.0f / 100; // 1m / s^2 = 100 LSB + static constexpr float EulerAngleScale = 1.0f / 16; // 1 degree = 16 LSB + static constexpr float QuaternionScale = 1.0f / (1 << 14); // 1 = 2^14 LSB + static constexpr float AccelerationScale = 1.0f / 100; // 1m / s^2 = 100 LSB - std::unique_ptr deserializer; + std::unique_ptr deserializer; - Bno055AxisMap axisMap = Bno055AxisMap::XYZ; - uint32_t axisSign = (uint32_t)Bno055AxisSign::Default; // NB: Holds the uint value of the flag. Allows for combinations of X/Y/Z to combined together + Bno055AxisMap axisMap = Bno055AxisMap::XYZ; + uint32_t axisSign = (uint32_t) Bno055AxisSign::Default; // NB: Holds the uint value of the flag. Allows for combinations of X/Y/Z to combined together - static constexpr int NumberOfChannels = 3 + 3 + 4 + 3 + 1 + 4; - static constexpr double SampleRate = 100.0; + static constexpr int NumberOfChannels = 3 + 3 + 4 + 3 + 1 + 4; + static constexpr double SampleRate = 100.0; - static constexpr std::chrono::milliseconds TimerIntervalInMilliseconds = std::chrono::milliseconds((int)(1e3 * (1 / SampleRate))); + static constexpr std::chrono::milliseconds TimerIntervalInMilliseconds = std::chrono::milliseconds ((int) (1e3 * (1 / SampleRate))); - static constexpr int NumFrames = 2; + static constexpr int NumFrames = 2; - std::array bnoSamples; + std::array bnoSamples; - double bnoTimestamps[NumFrames]; - int64 sampleNumbers[NumFrames]; - uint64 eventCodes[NumFrames]; + double bnoTimestamps[NumFrames]; + int64 sampleNumbers[NumFrames]; + uint64 eventCodes[NumFrames]; - unsigned short currentFrame = 0; - int sampleNumber = 0; + unsigned short currentFrame = 0; + int sampleNumber = 0; - time_point previousTime; + time_point previousTime; - int16_t readInt16(uint32_t); - static int16_t getInt16FromUint32(uint32_t, bool); + int16_t readInt16 (uint32_t); + static int16_t getInt16FromUint32 (uint32_t, bool); - JUCE_LEAK_DETECTOR(PolledBno055); - }; -} + JUCE_LEAK_DETECTOR (PolledBno055); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Devices/PortController.cpp b/Source/Devices/PortController.cpp index 55fa8c6..aa45654 100644 --- a/Source/Devices/PortController.cpp +++ b/Source/Devices/PortController.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,190 +24,211 @@ using namespace OnixSourcePlugin; -PortController::PortController(PortName port_, std::shared_ptr ctx_) : - OnixDevice(OnixDevice::getPortName(port_), BREAKOUT_BOARD_NAME, OnixDeviceType::PORT_CONTROL, (oni_dev_idx_t)port_, ctx_), - port(port_) +PortController::PortController (PortName port_, std::shared_ptr ctx_) + : OnixDevice (OnixDevice::getPortName (port_), BREAKOUT_BOARD_NAME, OnixDeviceType::PORT_CONTROL, (oni_dev_idx_t) port_, ctx_), + port (port_) { } int PortController::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) return 1; + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + return 1; - return deviceContext->writeRegister(deviceIdx, (uint32_t)PortControllerRegister::ENABLE, 1u); + return deviceContext->writeRegister (deviceIdx, (uint32_t) PortControllerRegister::ENABLE, 1u); } bool PortController::resetLinkFlags() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) return false; + if (deviceContext == nullptr || ! deviceContext->isInitialized()) + return false; - int rc = deviceContext->writeRegister(deviceIdx, (uint32_t)PortControllerRegister::LINKFLAGS, 0b11); - if (rc != ONI_ESUCCESS) - { - Onix1::showWarningMessageBoxAsync("Port Controller Error", "Unable to set the link flags for " + getName()); - return false; - } + int rc = deviceContext->writeRegister (deviceIdx, (uint32_t) PortControllerRegister::LINKFLAGS, 0b11); + if (rc != ONI_ESUCCESS) + { + Onix1::showWarningMessageBoxAsync ("Port Controller Error", "Unable to set the link flags for " + getName()); + return false; + } - return true; + return true; } uint32_t PortController::getLinkFlags() { - uint32_t linkFlags; - int rc = deviceContext->readRegister(deviceIdx, (uint32_t)PortControllerRegister::LINKFLAGS, &linkFlags); + uint32_t linkFlags; + int rc = deviceContext->readRegister (deviceIdx, (uint32_t) PortControllerRegister::LINKFLAGS, &linkFlags); - if (rc != ONI_ESUCCESS) - { - Onix1::showWarningMessageBoxAsync("Port Controller Error", "Unable to read the link flags for " + getName()); - return false; - } + if (rc != ONI_ESUCCESS) + { + Onix1::showWarningMessageBoxAsync ("Port Controller Error", "Unable to read the link flags for " + getName()); + return false; + } - return linkFlags; + return linkFlags; } bool PortController::updateSettings() { - return getLinkFlags() == 0; + return getLinkFlags() == 0; } -void PortController::addSourceBuffers(OwnedArray& sourceBuffers) +void PortController::addSourceBuffers (OwnedArray& sourceBuffers) { } std::string PortController::getPortNameString() const { - return OnixDevice::getPortName(port); + return OnixDevice::getPortName (port); } PortName PortController::getPort() const { - return port; + return port; } bool PortController::getErrorFlag() { - return errorFlag; + return errorFlag; } double PortController::getLastVoltageSet() const -{ - return lastVoltageSet; +{ + return lastVoltageSet; } void PortController::startAcquisition() { - errorFlag = false; + errorFlag = false; } void PortController::processFrames() { - oni_frame_t* frame; - while (frameQueue.try_dequeue(frame)) - { - int8_t* dataPtr = (int8_t*)frame->data; + oni_frame_t* frame; + while (frameQueue.try_dequeue (frame)) + { + int8_t* dataPtr = (int8_t*) frame->data; - int dataOffset = 8; + int dataOffset = 8; - uint32_t code = (uint32_t) * (dataPtr + dataOffset); - uint32_t data = (uint32_t) * (dataPtr + dataOffset + 1); + uint32_t data = (uint32_t) *(dataPtr + dataOffset + 1); - errorFlag = errorFlag || ((uint32_t)data & LINKSTATE_SL) == 0; + errorFlag = errorFlag || ((uint32_t) data & LINKSTATE_SL) == 0; - oni_destroy_frame(frame); + oni_destroy_frame (frame); - LOGE("Port status changed for " + getName() + "."); - } + LOGE ("Port status changed for " + getName() + "."); + } } -void PortController::updateDiscoveryParameters(DiscoveryParameters parameters) +void PortController::updateDiscoveryParameters (DiscoveryParameters parameters) { - discoveryParameters = parameters; + discoveryParameters = parameters; } -DiscoveryParameters PortController::getHeadstageDiscoveryParameters(std::string headstage) +DiscoveryParameters PortController::getHeadstageDiscoveryParameters (std::string headstage) { - if (headstage == NEUROPIXELSV1E_HEADSTAGE_NAME) - { - return DiscoveryParameters(3.3f, 5.5f, 1.0f, 0.2f); - } - else if (headstage == NEUROPIXELSV1F_HEADSTAGE_NAME) - { - return DiscoveryParameters(5.0f, 7.0f, 1.0f, 0.2f); - } - else if (headstage == NEUROPIXELSV2E_HEADSTAGE_NAME) - { - return DiscoveryParameters(3.3f, 5.5f, 1.0f, 0.2f); - } + if (headstage == NEUROPIXELSV1E_HEADSTAGE_NAME) + { + return DiscoveryParameters (3.3f, 5.5f, 1.0f, 0.2f); + } + else if (headstage == NEUROPIXELSV1F_HEADSTAGE_NAME) + { + return DiscoveryParameters (5.0f, 7.0f, 1.0f, 0.2f); + } + else if (headstage == NEUROPIXELSV2E_HEADSTAGE_NAME) + { + return DiscoveryParameters (3.3f, 5.5f, 1.0f, 0.2f); + } - return DiscoveryParameters(); + return DiscoveryParameters(); } -bool PortController::configureVoltage(double voltage) +bool PortController::configureVoltage (double voltage) { - if (voltage == defaultVoltage) - { - if (discoveryParameters == DiscoveryParameters() || discoveryParameters.voltageIncrement <= 0) - return false; + if (voltage == defaultVoltage) + { + if (discoveryParameters == DiscoveryParameters() || discoveryParameters.voltageIncrement <= 0) + return false; - ConfigureVoltageWithProgressBar progressBar = ConfigureVoltageWithProgressBar(discoveryParameters, this); - progressBar.runThread(); + ConfigureVoltageWithProgressBar progressBar = ConfigureVoltageWithProgressBar (discoveryParameters, this); + progressBar.runThread(); - bool result = progressBar.getResult(); + bool result = progressBar.getResult(); - if (!result) - setVoltageOverride(0, false); + if (! result) + setVoltageOverride (0, false); - return result; - } - else if (voltage >= 0.0 && voltage <= 7.0) - { - setVoltage(voltage); + return result; + } + else if (voltage >= 0.0 && voltage <= 7.0) + { + setVoltage (voltage); - bool result = checkLinkState(); + bool result = checkLinkState(); - if (!result) - setVoltageOverride(0, false); + if (! result) + setVoltageOverride (0, false); - return result; - } + return result; + } - return false; + return false; } -void PortController::setVoltageOverride(double voltage, bool waitToSettle) +void PortController::setVoltageOverride (double voltage, bool waitToSettle) { - if (voltage < 0.0 && voltage > 7.0) { LOGE("Invalid voltage value. Tried to set the port to " + std::to_string(voltage) + " V."); return; } + if (voltage < 0.0 && voltage > 7.0) + { + LOGE ("Invalid voltage value. Tried to set the port to " + std::to_string (voltage) + " V."); + return; + } - int rc = deviceContext->writeRegister((oni_dev_idx_t)port, (oni_reg_addr_t)PortControllerRegister::PORTVOLTAGE, voltage * 10); - if (rc != ONI_ESUCCESS) return; + int rc = deviceContext->writeRegister ((oni_dev_idx_t) port, (oni_reg_addr_t) PortControllerRegister::PORTVOLTAGE, voltage * 10); + if (rc != ONI_ESUCCESS) + return; - lastVoltageSet = voltage; + lastVoltageSet = voltage; - if (waitToSettle) sleep_for(std::chrono::milliseconds(500)); + if (waitToSettle) + sleep_for (std::chrono::milliseconds (500)); } -void PortController::setVoltage(double voltage) +void PortController::setVoltage (double voltage) { - if (voltage < 0.0 && voltage > 7.0) { LOGE("Invalid voltage value. Tried to set the port to " + std::to_string(voltage) + " V."); return; } + if (voltage < 0.0 && voltage > 7.0) + { + LOGE ("Invalid voltage value. Tried to set the port to " + std::to_string (voltage) + " V."); + return; + } - int rc = deviceContext->writeRegister((oni_dev_idx_t)port, (oni_reg_addr_t)PortControllerRegister::PORTVOLTAGE, 0); - sleep_for(std::chrono::milliseconds(300)); - if (rc != ONI_ESUCCESS) return; + int rc = deviceContext->writeRegister ((oni_dev_idx_t) port, (oni_reg_addr_t) PortControllerRegister::PORTVOLTAGE, 0); + sleep_for (std::chrono::milliseconds (300)); + if (rc != ONI_ESUCCESS) + return; - rc = deviceContext->writeRegister((oni_dev_idx_t)port, (oni_reg_addr_t)PortControllerRegister::PORTVOLTAGE, voltage * 10); - if (rc != ONI_ESUCCESS) return; + rc = deviceContext->writeRegister ((oni_dev_idx_t) port, (oni_reg_addr_t) PortControllerRegister::PORTVOLTAGE, voltage * 10); + if (rc != ONI_ESUCCESS) + return; - lastVoltageSet = voltage; + lastVoltageSet = voltage; - sleep_for(std::chrono::milliseconds(500)); + sleep_for (std::chrono::milliseconds (500)); } bool PortController::checkLinkState() const { - oni_reg_val_t linkState; - int rc = deviceContext->readRegister((oni_dev_idx_t)port, (oni_reg_addr_t)PortControllerRegister::LINKSTATE, &linkState); - - if (rc != ONI_ESUCCESS) { return false; } - else if ((linkState & LINKSTATE_SL) == 0) { LOGD("Unable to acquire communication lock."); return false; } - else return true; + oni_reg_val_t linkState; + int rc = deviceContext->readRegister ((oni_dev_idx_t) port, (oni_reg_addr_t) PortControllerRegister::LINKSTATE, &linkState); + + if (rc != ONI_ESUCCESS) + { + return false; + } + else if ((linkState & LINKSTATE_SL) == 0) + { + LOGD ("Unable to acquire communication lock."); + return false; + } + else + return true; } diff --git a/Source/Devices/PortController.h b/Source/Devices/PortController.h index 13c960d..4c441f9 100644 --- a/Source/Devices/PortController.h +++ b/Source/Devices/PortController.h @@ -1,188 +1,190 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #pragma once -#include #include +#include -#include "oni.h" #include "../OnixDevice.h" +#include "oni.h" using namespace std::this_thread; namespace OnixSourcePlugin { - enum class PortControllerRegister : uint32_t - { - ENABLE = 0, - GPOSTATE = 1, - DESPWR = 2, - PORTVOLTAGE = 3, - SAVEVOLTAGE = 4, - LINKSTATE = 5, - LINKFLAGS = 7, - }; - - enum class PortStatusCode : uint32_t - { - SerdesLock = 0x0001, - SerdesParityPass = 0x0002, - CrcError = 0x0100, - TooManyDevices = 0x0200, - InitializationError = 0x0400, - BadPacketFormat = 0x0800, - InitializationCrcError = 0x1000, - }; - - class DiscoveryParameters - { - public: - double minVoltage = 0.0; - double maxVoltage = 0.0; - double voltageOffset = 0.0; - double voltageIncrement = 0.0; - - DiscoveryParameters() {}; - - DiscoveryParameters(double minVoltage_, double maxVoltage_, double voltageOffset_, double voltageIncrement_) - { - minVoltage = minVoltage_; - maxVoltage = maxVoltage_; - voltageOffset = voltageOffset_; - voltageIncrement = voltageIncrement_; - } - - bool operator==(const DiscoveryParameters& rhs) const - { - return rhs.minVoltage == minVoltage && rhs.maxVoltage == maxVoltage && rhs.voltageOffset == voltageOffset && rhs.voltageIncrement == voltageIncrement; - } - }; - - class PortController : public OnixDevice - { - public: - PortController(PortName port_, std::shared_ptr ctx_); - - int configureDevice() override; - bool updateSettings() override; - void startAcquisition() override; - void processFrames() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; - - void updateDiscoveryParameters(DiscoveryParameters parameters); - - bool configureVoltage(double voltage = defaultVoltage); - - /** Sets the voltage to the given value, after setting the voltage to zero */ - void setVoltage(double voltage); - - /** Overrides the voltage setting and directly sets it to the given voltage */ - void setVoltageOverride(double voltage, bool waitToSettle = true); - - bool checkLinkState() const; - - static DiscoveryParameters getHeadstageDiscoveryParameters(std::string headstage); - - std::string getPortNameString() const; - - PortName getPort() const; - - /** Check if the port status changed during acquisition and there is an error reported */ - bool getErrorFlag(); - - double getLastVoltageSet() const; - - /** Writes to the link flags register to reset the flags. This should be called after headstages are locked. */ - bool resetLinkFlags(); - - /** Returns the link flags value, which is zero unless the lock or pass was lost outside of acquisition */ - uint32_t getLinkFlags(); +enum class PortControllerRegister : uint32_t +{ + ENABLE = 0, + GPOSTATE = 1, + DESPWR = 2, + PORTVOLTAGE = 3, + SAVEVOLTAGE = 4, + LINKSTATE = 5, + LINKFLAGS = 7, +}; + +enum class PortStatusCode : uint32_t +{ + SerdesLock = 0x0001, + SerdesParityPass = 0x0002, + CrcError = 0x0100, + TooManyDevices = 0x0200, + InitializationError = 0x0400, + BadPacketFormat = 0x0800, + InitializationCrcError = 0x1000, +}; + +class DiscoveryParameters +{ +public: + double minVoltage = 0.0; + double maxVoltage = 0.0; + double voltageOffset = 0.0; + double voltageIncrement = 0.0; + + DiscoveryParameters() {}; + + DiscoveryParameters (double minVoltage_, double maxVoltage_, double voltageOffset_, double voltageIncrement_) + { + minVoltage = minVoltage_; + maxVoltage = maxVoltage_; + voltageOffset = voltageOffset_; + voltageIncrement = voltageIncrement_; + } + + bool operator== (const DiscoveryParameters& rhs) const + { + return rhs.minVoltage == minVoltage + && rhs.maxVoltage == maxVoltage + && rhs.voltageOffset == voltageOffset + && rhs.voltageIncrement == voltageIncrement; + } +}; + +class PortController : public OnixDevice +{ +public: + PortController (PortName port_, std::shared_ptr ctx_); - private: + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void processFrames() override; + void addSourceBuffers (OwnedArray& sourceBuffers) override; - const PortName port; + void updateDiscoveryParameters (DiscoveryParameters parameters); - static constexpr double defaultVoltage = -1.0; + bool configureVoltage (double voltage = defaultVoltage); - double lastVoltageSet = 0.0; + /** Sets the voltage to the given value, after setting the voltage to zero */ + void setVoltage (double voltage); - static constexpr uint32_t LINKSTATE_PP = 0x2; // parity check pass bit - static constexpr uint32_t LINKSTATE_SL = 0x1; // SERDES lock bit + /** Overrides the voltage setting and directly sets it to the given voltage */ + void setVoltageOverride (double voltage, bool waitToSettle = true); - DiscoveryParameters discoveryParameters; + bool checkLinkState() const; - std::atomic errorFlag = false; + static DiscoveryParameters getHeadstageDiscoveryParameters (std::string headstage); - JUCE_LEAK_DETECTOR(PortController); - }; + std::string getPortNameString() const; - class ConfigureVoltageWithProgressBar : public ThreadWithProgressWindow - { - public: - ConfigureVoltageWithProgressBar(DiscoveryParameters params, PortController* port) - : ThreadWithProgressWindow("Configuring voltage on " + port->getPortNameString(), true, false) - { - m_params = params; - m_port = port; - } + PortName getPort() const; - void run() override - { - double voltage, progress = 0.0; + /** Check if the port status changed during acquisition and there is an error reported */ + bool getErrorFlag(); - double increment = m_params.voltageIncrement / (m_params.maxVoltage - m_params.minVoltage); + double getLastVoltageSet() const; - for (voltage = m_params.minVoltage; voltage <= m_params.maxVoltage; voltage += m_params.voltageIncrement) - { - progress += increment; - setProgress(progress); + /** Writes to the link flags register to reset the flags. This should be called after headstages are locked. */ + bool resetLinkFlags(); - m_port->setVoltage(voltage); + /** Returns the link flags value, which is zero unless the lock or pass was lost outside of acquisition */ + uint32_t getLinkFlags(); - if (m_port->checkLinkState()) - { - setProgress(0.95); - m_port->setVoltage(voltage + m_params.voltageOffset); - result = m_port->checkLinkState();; - setProgress(1.0); - return; - } - } +private: + const PortName port; - result = false; - return; - } + static constexpr double defaultVoltage = -1.0; - bool getResult() const { return result; } + double lastVoltageSet = 0.0; - private: + static constexpr uint32_t LINKSTATE_PP = 0x2; // parity check pass bit + static constexpr uint32_t LINKSTATE_SL = 0x1; // SERDES lock bit - DiscoveryParameters m_params; + DiscoveryParameters discoveryParameters; - PortController* m_port; + std::atomic errorFlag = false; - bool result = false; + JUCE_LEAK_DETECTOR (PortController); +}; - JUCE_LEAK_DETECTOR(ConfigureVoltageWithProgressBar); - }; -} +class ConfigureVoltageWithProgressBar : public ThreadWithProgressWindow +{ +public: + ConfigureVoltageWithProgressBar (DiscoveryParameters params, PortController* port) + : ThreadWithProgressWindow ("Configuring voltage on " + port->getPortNameString(), true, false) + { + m_params = params; + m_port = port; + } + + void run() override + { + double voltage, progress = 0.0; + + double increment = m_params.voltageIncrement / (m_params.maxVoltage - m_params.minVoltage); + + for (voltage = m_params.minVoltage; voltage <= m_params.maxVoltage; voltage += m_params.voltageIncrement) + { + progress += increment; + setProgress (progress); + + m_port->setVoltage (voltage); + + if (m_port->checkLinkState()) + { + setProgress (0.95); + m_port->setVoltage (voltage + m_params.voltageOffset); + result = m_port->checkLinkState(); + ; + setProgress (1.0); + return; + } + } + + result = false; + return; + } + + bool getResult() const { return result; } + +private: + DiscoveryParameters m_params; + + PortController* m_port; + + bool result = false; + + JUCE_LEAK_DETECTOR (ConfigureVoltageWithProgressBar); +}; +} // namespace OnixSourcePlugin diff --git a/Source/Formats/ProbeInterface.h b/Source/Formats/ProbeInterface.h index 38f0845..6c65bd2 100644 --- a/Source/Formats/ProbeInterface.h +++ b/Source/Formats/ProbeInterface.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -26,300 +26,292 @@ namespace OnixSourcePlugin { - class ProbeInterfaceJson - { - public: - template - static bool writeProbeSettingsToJson(File& file, ProbeSettings* settings) - { - DynamicObject output; - - output.setProperty(Identifier("specification"), - var("probeinterface")); - output.setProperty(Identifier("version"), - var("0.2.23")); - - Array contact_positions; - Array shank_ids; - Array device_channel_indices; - Array contact_plane_axes; - Array contact_shapes; - Array contact_shape_params; - - Array ax1 = { 1.0f, 0.0f }; - Array ax2 = { 0.0f, 1.0f }; - Array contact_plane_axis = { ax1, ax2 }; - - for (int elec = 0; elec < settings->electrodeMetadata.size(); elec++) - { - ElectrodeMetadata& em = settings->electrodeMetadata[elec]; - - Array contact_position; - contact_position.add(em.xpos); - contact_position.add(em.ypos); - - DynamicObject::Ptr contact_shape_param = new DynamicObject; - contact_shape_param->setProperty(Identifier("width"), em.site_width); - - contact_positions.add(contact_position); - shank_ids.add(String(em.shank)); - device_channel_indices.add(em.status == ElectrodeStatus::CONNECTED ? em.channel : -1); - contact_plane_axes.add(contact_plane_axis); - contact_shapes.add("square"); - contact_shape_params.add(contact_shape_param.get()); - } - - Array probe_planar_contours; - auto& probeContour = settings->probeMetadata.probeContour; - - for (int i = 0; i < probeContour.size(); i++) - { - probe_planar_contours.add(Array { probeContour[i][0], probeContour[i][1] }); - } - - DynamicObject::Ptr probe = new DynamicObject(); - DynamicObject::Ptr annotations = new DynamicObject(); - annotations->setProperty(Identifier("manufacturer"), "imec"); - auto probeName = getProbeName(settings->probeType); - annotations->setProperty(Identifier("name"), String(probeName)); - - probe->setProperty(Identifier("ndim"), 2); - probe->setProperty(Identifier("si_units"), "um"); - probe->setProperty(Identifier("annotations"), var(annotations)); - probe->setProperty(Identifier("contact_positions"), contact_positions); - probe->setProperty(Identifier("contact_plane_axes"), contact_plane_axes); - probe->setProperty(Identifier("contact_shapes"), contact_shapes); - probe->setProperty(Identifier("contact_shape_params"), contact_shape_params); - probe->setProperty(Identifier("probe_planar_contour"), probe_planar_contours); - probe->setProperty(Identifier("device_channel_indices"), device_channel_indices); - probe->setProperty(Identifier("shank_ids"), shank_ids); - - Array probes; - probes.add(probe.get()); - - output.setProperty(Identifier("probes"), probes); - - if (file.exists()) - file.deleteFile(); - - FileOutputStream f(file); - - JSON::FormatOptions options; - - options = options.withMaxDecimalPlaces(4); - options = options.withSpacing(JSON::Spacing::multiLine); - - output.writeAsJSON(f, options); - - return true; - } - - template - static bool readProbeSettingsFromJson(File& file, ProbeSettings* settings) - { - auto json = JSON::parse(file); - - if (json == var()) - { - Onix1::showWarningMessageBoxAsync("Unable to Parse File", "The file '" + file.getFileName().toStdString() + "' could not be parsed. " + - "Please ensure that the file exists and is in the correct format."); - return false; - } - - DynamicObject::Ptr obj = json.getDynamicObject(); - - if (obj->hasProperty(Identifier("specification"))) - { - std::string specification = obj->getProperty(Identifier("specification")).toString().toStdString(); - - const std::string probeInterfaceSpecification = "probeinterface"; - - if (specification.compare(probeInterfaceSpecification) != 0) - { - Onix1::showWarningMessageBoxAsync("Invalid Specification", "The specification listed in the Probe Interface file is '" + specification + - "', but it is expected to be '" + probeInterfaceSpecification + "'."); - return false; - } - } - else - { - Onix1::showWarningMessageBoxAsync("No Specification Found", "No specification field was found in the JSON file."); - return false; - } - - if (obj->hasProperty(Identifier("probes"))) - { - Array* probes = obj->getProperty(Identifier("probes")).getArray(); - - if (probes->size() != 1) - { - Onix1::showWarningMessageBoxAsync("Wrong Number of Probes", "Expected to find one probe listed in this file, but found " + - std::to_string(probes->size()) + " probes in the file."); - return false; - } - - DynamicObject::Ptr probe = probes->getReference(0).getDynamicObject(); - - if (probe->hasProperty(Identifier("ndim"))) - { - if (!probe->getProperty(Identifier("ndim")).equalsWithSameType(2)) - { - Onix1::showWarningMessageBoxAsync("Invalid Number of Dimensions", "Expected this file to contain two dimensions, but found `" - + probe->getProperty(Identifier("ndim")).toString().toStdString() + "` instead."); - return false; - } - } - else - { - Onix1::showWarningMessageBoxAsync("No Dimensions Found", "Could not find the number of dimensions in the file."); - return false; - } - - if (probe->hasProperty(Identifier("si_units"))) - { - const std::string um = "um"; - - if (!probe->getProperty(Identifier("si_units")).equalsWithSameType(var(um))) - { - Onix1::showWarningMessageBoxAsync("Unexpected Units Found", "Expected to see units `" + um + "`, but found `" - + probe->getProperty(Identifier("si_units")).toString().toStdString() + "` instead."); - return false; - } - } - else - { - Onix1::showWarningMessageBoxAsync("No Units Found", "Could not find any units in the file."); - return false; - } - - Array* contact_positions = nullptr; - - if (probe->hasProperty(Identifier("contact_positions"))) - { - contact_positions = probe->getProperty(Identifier("contact_positions")).getArray(); - - if (contact_positions->size() != settings->electrodeMetadata.size()) - { - Onix1::showWarningMessageBoxAsync("Different Number of Contacts", "Expected to find " + std::to_string(settings->electrodeMetadata.size()) + - " contacts, but found " + std::to_string(contact_positions->size()) + " contacts instead."); - return false; - } - } - else - { - Onix1::showWarningMessageBoxAsync("No Contacts Found", "Could not find any contacts in the file."); - return false; - } - - Array* probe_planar_contour = nullptr; - - if (probe->hasProperty(Identifier("probe_planar_contour"))) - { - probe_planar_contour = probe->getProperty(Identifier("probe_planar_contour")).getArray(); - } - else - { - LOGD("No probe planar contour found."); - } - - Array* device_channel_indices = nullptr; - - if (probe->hasProperty(Identifier("device_channel_indices"))) - { - device_channel_indices = probe->getProperty(Identifier("device_channel_indices")).getArray(); - - if (device_channel_indices->size() != settings->electrodeMetadata.size()) - { - Onix1::showWarningMessageBoxAsync("Wrong Number of Indices Found", "Expected to find " + std::to_string(settings->electrodeMetadata.size()) + - " device channel indices, but found " + std::to_string(device_channel_indices->size()) + " instead."); - return false; - } - } - else - { - Onix1::showWarningMessageBoxAsync("No Indices Found", "No device channel indices found in the file."); - return false; - } - - Array* shank_ids = nullptr; - - if (probe->hasProperty(Identifier("shank_ids"))) - { - shank_ids = probe->getProperty(Identifier("shank_ids")).getArray(); - - if (shank_ids->size() != settings->electrodeMetadata.size()) - { - Onix1::showWarningMessageBoxAsync("Wrong Number of Shank IDs Found", "Expected to find " + std::to_string(settings->electrodeMetadata.size()) + - " shank IDs, but found " + std::to_string(shank_ids->size()) + " instead."); - return false; - } - } - else - { - Onix1::showWarningMessageBoxAsync("No Shank IDs Found", "No shank IDs found in the file."); - return false; - } - - for (int ch = 0; ch < contact_positions->size(); ch++) - { - Array* contact_position = contact_positions->getReference(ch).getArray(); - - settings->electrodeMetadata[ch].xpos = int(contact_position->getReference(0)); - settings->electrodeMetadata[ch].ypos = int(contact_position->getReference(1)); - } - - settings->probeMetadata.probeContour.clear(); - - if (probe_planar_contour != nullptr) - { - for (int i = 0; i < probe_planar_contour->size(); i++) - { - Array* point = probe_planar_contour->getReference(i).getArray(); - - settings->probeMetadata.probeContour.emplace_back(std::array{float(point->getReference(0)), float(point->getReference(1))}); - } - } - - for (int ch = 0; ch < shank_ids->size(); ch++) - { - settings->electrodeMetadata[ch].shank = int(shank_ids->getReference(ch)); - } - - settings->clearElectrodeSelection(); - std::vector selectedChannels{}; - - for (int ch = 0; ch < device_channel_indices->size(); ch++) - { - if (int(device_channel_indices->getReference(ch)) >= 0) - { - selectedChannels.emplace_back(ch); - } - } - - settings->selectElectrodes(selectedChannels); - } - else - { - Onix1::showWarningMessageBoxAsync("No Probes Found", "Could not find any probes in the file."); - return false; - } - - return true; - } - - static std::string getProbeName(ProbeType type) - { - switch (type) - { - case OnixSourcePlugin::ProbeType::NONE: - return ""; - case OnixSourcePlugin::ProbeType::NPX_V1: - return "Neuropixels 1.0"; - case OnixSourcePlugin::ProbeType::NPX_V2: - return "Neuropixels 2.0"; - default: - return ""; - } - } - }; -} +class ProbeInterfaceJson +{ +public: + template + static bool writeProbeSettingsToJson (File& file, ProbeSettings* settings) + { + DynamicObject output; + + output.setProperty (Identifier ("specification"), + var ("probeinterface")); + output.setProperty (Identifier ("version"), + var ("0.2.23")); + + Array contact_positions; + Array shank_ids; + Array device_channel_indices; + Array contact_plane_axes; + Array contact_shapes; + Array contact_shape_params; + + Array ax1 = { 1.0f, 0.0f }; + Array ax2 = { 0.0f, 1.0f }; + Array contact_plane_axis = { ax1, ax2 }; + + for (int elec = 0; elec < settings->electrodeMetadata.size(); elec++) + { + ElectrodeMetadata& em = settings->electrodeMetadata[elec]; + + Array contact_position; + contact_position.add (em.xpos); + contact_position.add (em.ypos); + + DynamicObject::Ptr contact_shape_param = new DynamicObject; + contact_shape_param->setProperty (Identifier ("width"), em.site_width); + + contact_positions.add (contact_position); + shank_ids.add (String (em.shank)); + device_channel_indices.add (em.status == ElectrodeStatus::CONNECTED ? em.channel : -1); + contact_plane_axes.add (contact_plane_axis); + contact_shapes.add ("square"); + contact_shape_params.add (contact_shape_param.get()); + } + + Array probe_planar_contours; + auto& probeContour = settings->probeMetadata.probeContour; + + for (int i = 0; i < probeContour.size(); i++) + { + probe_planar_contours.add (Array { probeContour[i][0], probeContour[i][1] }); + } + + DynamicObject::Ptr probe = new DynamicObject(); + DynamicObject::Ptr annotations = new DynamicObject(); + annotations->setProperty (Identifier ("manufacturer"), "imec"); + auto probeName = getProbeName (settings->probeType); + annotations->setProperty (Identifier ("name"), String (probeName)); + + probe->setProperty (Identifier ("ndim"), 2); + probe->setProperty (Identifier ("si_units"), "um"); + probe->setProperty (Identifier ("annotations"), var (annotations)); + probe->setProperty (Identifier ("contact_positions"), contact_positions); + probe->setProperty (Identifier ("contact_plane_axes"), contact_plane_axes); + probe->setProperty (Identifier ("contact_shapes"), contact_shapes); + probe->setProperty (Identifier ("contact_shape_params"), contact_shape_params); + probe->setProperty (Identifier ("probe_planar_contour"), probe_planar_contours); + probe->setProperty (Identifier ("device_channel_indices"), device_channel_indices); + probe->setProperty (Identifier ("shank_ids"), shank_ids); + + Array probes; + probes.add (probe.get()); + + output.setProperty (Identifier ("probes"), probes); + + if (file.exists()) + file.deleteFile(); + + FileOutputStream f (file); + + JSON::FormatOptions options; + + options = options.withMaxDecimalPlaces (4); + options = options.withSpacing (JSON::Spacing::multiLine); + + output.writeAsJSON (f, options); + + return true; + } + + template + static bool readProbeSettingsFromJson (File& file, ProbeSettings* settings) + { + auto json = JSON::parse (file); + + if (json == var()) + { + Onix1::showWarningMessageBoxAsync ("Unable to Parse File", "The file '" + file.getFileName().toStdString() + "' could not be parsed. " + "Please ensure that the file exists and is in the correct format."); + return false; + } + + DynamicObject::Ptr obj = json.getDynamicObject(); + + if (obj->hasProperty (Identifier ("specification"))) + { + std::string specification = obj->getProperty (Identifier ("specification")).toString().toStdString(); + + const std::string probeInterfaceSpecification = "probeinterface"; + + if (specification.compare (probeInterfaceSpecification) != 0) + { + Onix1::showWarningMessageBoxAsync ("Invalid Specification", "The specification listed in the Probe Interface file is '" + specification + "', but it is expected to be '" + probeInterfaceSpecification + "'."); + return false; + } + } + else + { + Onix1::showWarningMessageBoxAsync ("No Specification Found", "No specification field was found in the JSON file."); + return false; + } + + if (obj->hasProperty (Identifier ("probes"))) + { + Array* probes = obj->getProperty (Identifier ("probes")).getArray(); + + if (probes->size() != 1) + { + Onix1::showWarningMessageBoxAsync ("Wrong Number of Probes", "Expected to find one probe listed in this file, but found " + std::to_string (probes->size()) + " probes in the file."); + return false; + } + + DynamicObject::Ptr probe = probes->getReference (0).getDynamicObject(); + + if (probe->hasProperty (Identifier ("ndim"))) + { + if (! probe->getProperty (Identifier ("ndim")).equalsWithSameType (2)) + { + Onix1::showWarningMessageBoxAsync ("Invalid Number of Dimensions", "Expected this file to contain two dimensions, but found `" + probe->getProperty (Identifier ("ndim")).toString().toStdString() + "` instead."); + return false; + } + } + else + { + Onix1::showWarningMessageBoxAsync ("No Dimensions Found", "Could not find the number of dimensions in the file."); + return false; + } + + if (probe->hasProperty (Identifier ("si_units"))) + { + const std::string um = "um"; + + if (! probe->getProperty (Identifier ("si_units")).equalsWithSameType (var (um))) + { + Onix1::showWarningMessageBoxAsync ("Unexpected Units Found", "Expected to see units `" + um + "`, but found `" + probe->getProperty (Identifier ("si_units")).toString().toStdString() + "` instead."); + return false; + } + } + else + { + Onix1::showWarningMessageBoxAsync ("No Units Found", "Could not find any units in the file."); + return false; + } + + Array* contact_positions = nullptr; + + if (probe->hasProperty (Identifier ("contact_positions"))) + { + contact_positions = probe->getProperty (Identifier ("contact_positions")).getArray(); + + if (contact_positions->size() != settings->electrodeMetadata.size()) + { + Onix1::showWarningMessageBoxAsync ("Different Number of Contacts", "Expected to find " + std::to_string (settings->electrodeMetadata.size()) + " contacts, but found " + std::to_string (contact_positions->size()) + " contacts instead."); + return false; + } + } + else + { + Onix1::showWarningMessageBoxAsync ("No Contacts Found", "Could not find any contacts in the file."); + return false; + } + + Array* probe_planar_contour = nullptr; + + if (probe->hasProperty (Identifier ("probe_planar_contour"))) + { + probe_planar_contour = probe->getProperty (Identifier ("probe_planar_contour")).getArray(); + } + else + { + LOGD ("No probe planar contour found."); + } + + Array* device_channel_indices = nullptr; + + if (probe->hasProperty (Identifier ("device_channel_indices"))) + { + device_channel_indices = probe->getProperty (Identifier ("device_channel_indices")).getArray(); + + if (device_channel_indices->size() != settings->electrodeMetadata.size()) + { + Onix1::showWarningMessageBoxAsync ("Wrong Number of Indices Found", "Expected to find " + std::to_string (settings->electrodeMetadata.size()) + " device channel indices, but found " + std::to_string (device_channel_indices->size()) + " instead."); + return false; + } + } + else + { + Onix1::showWarningMessageBoxAsync ("No Indices Found", "No device channel indices found in the file."); + return false; + } + + Array* shank_ids = nullptr; + + if (probe->hasProperty (Identifier ("shank_ids"))) + { + shank_ids = probe->getProperty (Identifier ("shank_ids")).getArray(); + + if (shank_ids->size() != settings->electrodeMetadata.size()) + { + Onix1::showWarningMessageBoxAsync ("Wrong Number of Shank IDs Found", "Expected to find " + std::to_string (settings->electrodeMetadata.size()) + " shank IDs, but found " + std::to_string (shank_ids->size()) + " instead."); + return false; + } + } + else + { + Onix1::showWarningMessageBoxAsync ("No Shank IDs Found", "No shank IDs found in the file."); + return false; + } + + for (int ch = 0; ch < contact_positions->size(); ch++) + { + Array* contact_position = contact_positions->getReference (ch).getArray(); + + settings->electrodeMetadata[ch].xpos = int (contact_position->getReference (0)); + settings->electrodeMetadata[ch].ypos = int (contact_position->getReference (1)); + } + + settings->probeMetadata.probeContour.clear(); + + if (probe_planar_contour != nullptr) + { + for (int i = 0; i < probe_planar_contour->size(); i++) + { + Array* point = probe_planar_contour->getReference (i).getArray(); + + settings->probeMetadata.probeContour.emplace_back (std::array { float (point->getReference (0)), float (point->getReference (1)) }); + } + } + + for (int ch = 0; ch < shank_ids->size(); ch++) + { + settings->electrodeMetadata[ch].shank = int (shank_ids->getReference (ch)); + } + + settings->clearElectrodeSelection(); + std::vector selectedChannels {}; + + for (int ch = 0; ch < device_channel_indices->size(); ch++) + { + if (int (device_channel_indices->getReference (ch)) >= 0) + { + selectedChannels.emplace_back (ch); + } + } + + settings->selectElectrodes (selectedChannels); + } + else + { + Onix1::showWarningMessageBoxAsync ("No Probes Found", "Could not find any probes in the file."); + return false; + } + + return true; + } + + static std::string getProbeName (ProbeType type) + { + switch (type) + { + case OnixSourcePlugin::ProbeType::NONE: + return ""; + case OnixSourcePlugin::ProbeType::NPX_V1: + return "Neuropixels 1.0"; + case OnixSourcePlugin::ProbeType::NPX_V2: + return "Neuropixels 2.0"; + default: + return ""; + } + } +}; +} // namespace OnixSourcePlugin diff --git a/Source/FrameReader.cpp b/Source/FrameReader.cpp index e66faba..afc3942 100644 --- a/Source/FrameReader.cpp +++ b/Source/FrameReader.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,43 +24,44 @@ using namespace OnixSourcePlugin; -FrameReader::FrameReader(OnixDeviceVector sources_, std::shared_ptr ctx_) - : Thread("FrameReader") +FrameReader::FrameReader (OnixDeviceVector sources_, std::shared_ptr ctx_) + : Thread ("FrameReader") { - sources = sources_; - context = ctx_; + sources = sources_; + context = ctx_; } void FrameReader::run() { - while (!threadShouldExit()) - { - oni_frame_t* frame = context->readFrame(); + while (! threadShouldExit()) + { + oni_frame_t* frame = context->readFrame(); - if (frame == nullptr) - { - if (threadShouldExit()) return; + if (frame == nullptr) + { + if (threadShouldExit()) + return; - CoreServices::sendStatusMessage("Unable to read data frames. Stopping acquisition..."); - CoreServices::setAcquisitionStatus(false); - return; - } + CoreServices::sendStatusMessage ("Unable to read data frames. Stopping acquisition..."); + CoreServices::setAcquisitionStatus (false); + return; + } - bool destroyFrame = true; + bool destroyFrame = true; - for (const auto& source : sources) - { - if (source->compareIndex(frame->dev_idx)) - { - source->addFrame(frame); - destroyFrame = false; - break; - } - } + for (const auto& source : sources) + { + if (source->compareIndex (frame->dev_idx)) + { + source->addFrame (frame); + destroyFrame = false; + break; + } + } - if (destroyFrame) - { - oni_destroy_frame(frame); - } - } + if (destroyFrame) + { + oni_destroy_frame (frame); + } + } } diff --git a/Source/FrameReader.h b/Source/FrameReader.h index 23aacfe..cf4d818 100644 --- a/Source/FrameReader.h +++ b/Source/FrameReader.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -29,18 +29,17 @@ namespace OnixSourcePlugin { - class FrameReader : public Thread - { - public: - FrameReader(OnixDeviceVector sources_, std::shared_ptr); - - void run() override; +class FrameReader : public Thread +{ +public: + FrameReader (OnixDeviceVector sources_, std::shared_ptr); - private: + void run() override; - OnixDeviceVector sources; - std::shared_ptr context; +private: + OnixDeviceVector sources; + std::shared_ptr context; - JUCE_LEAK_DETECTOR(FrameReader); - }; -} + JUCE_LEAK_DETECTOR (FrameReader); +}; +} // namespace OnixSourcePlugin diff --git a/Source/I2CRegisterContext.cpp b/Source/I2CRegisterContext.cpp index d844764..ab70653 100644 --- a/Source/I2CRegisterContext.cpp +++ b/Source/I2CRegisterContext.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,45 +24,47 @@ using namespace OnixSourcePlugin; -I2CRegisterContext::I2CRegisterContext(uint32_t address_, const oni_dev_idx_t devIdx_, std::shared_ptr ctx_) - : deviceIndex(devIdx_), i2cAddress(address_) +I2CRegisterContext::I2CRegisterContext (uint32_t address_, const oni_dev_idx_t devIdx_, std::shared_ptr ctx_) + : deviceIndex (devIdx_), i2cAddress (address_) { - i2cContext = ctx_; + i2cContext = ctx_; } -int I2CRegisterContext::WriteByte(uint32_t address, uint32_t value, bool sixteenBitAddress) +int I2CRegisterContext::WriteByte (uint32_t address, uint32_t value, bool sixteenBitAddress) { - uint32_t registerAddress = (address << 7) | (i2cAddress & 0x7F); - registerAddress |= sixteenBitAddress ? 0x80000000 : 0; + uint32_t registerAddress = (address << 7) | (i2cAddress & 0x7F); + registerAddress |= sixteenBitAddress ? 0x80000000 : 0; - return i2cContext->writeRegister(deviceIndex, registerAddress, value); + return i2cContext->writeRegister (deviceIndex, registerAddress, value); } -int I2CRegisterContext::ReadByte(uint32_t address, oni_reg_val_t* value, bool sixteenBitAddress) +int I2CRegisterContext::ReadByte (uint32_t address, oni_reg_val_t* value, bool sixteenBitAddress) { - return ReadWord(address, 1, value, sixteenBitAddress); + return ReadWord (address, 1, value, sixteenBitAddress); } -int I2CRegisterContext::ReadWord(uint32_t address, uint32_t numBytes, uint32_t* value, bool sixteenBitAddress) +int I2CRegisterContext::ReadWord (uint32_t address, uint32_t numBytes, uint32_t* value, bool sixteenBitAddress) { - if (numBytes < 1 || numBytes > 4) - { - LOGE("Invalid number of bytes requested when reading a word."); - return 1; - } + if (numBytes < 1 || numBytes > 4) + { + LOGE ("Invalid number of bytes requested when reading a word."); + return 1; + } - uint32_t registerAddress = (address << 7) | (i2cAddress & 0x7F); - registerAddress |= sixteenBitAddress ? 0x80000000 : 0; - registerAddress |= (numBytes - 1) << 28; + uint32_t registerAddress = (address << 7) | (i2cAddress & 0x7F); + registerAddress |= sixteenBitAddress ? 0x80000000 : 0; + registerAddress |= (numBytes - 1) << 28; - return i2cContext->readRegister(deviceIndex, registerAddress, value); + return i2cContext->readRegister (deviceIndex, registerAddress, value); } -int I2CRegisterContext::set933I2cRate(double rate) +int I2CRegisterContext::set933I2cRate (double rate) { - auto sclTimes = (uint32_t)(std::round(1.0 / (100e-9 * rate))); - int rc = WriteByte((uint32_t)DS90UB9x::DS90UB933SerializerI2CRegister::SclHigh, sclTimes); - if (rc != ONI_ESUCCESS) return rc; - rc = WriteByte((uint32_t)DS90UB9x::DS90UB933SerializerI2CRegister::SclLow, sclTimes); - if (rc != ONI_ESUCCESS) return rc; + auto sclTimes = (uint32_t) (std::round (1.0 / (100e-9 * rate))); + int rc = WriteByte ((uint32_t) DS90UB9x::DS90UB933SerializerI2CRegister::SclHigh, sclTimes); + if (rc != ONI_ESUCCESS) + return rc; + rc = WriteByte ((uint32_t) DS90UB9x::DS90UB933SerializerI2CRegister::SclLow, sclTimes); + if (rc != ONI_ESUCCESS) + return rc; } \ No newline at end of file diff --git a/Source/I2CRegisterContext.h b/Source/I2CRegisterContext.h index 5a30f00..9ac19e5 100644 --- a/Source/I2CRegisterContext.h +++ b/Source/I2CRegisterContext.h @@ -1,61 +1,56 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #pragma once +#include + #include "ProcessorHeaders.h" -#include "Onix1.h" #include "Devices/DS90UB9x.h" - -#include +#include "Onix1.h" namespace OnixSourcePlugin { - class I2CRegisterContext - { - public: - - I2CRegisterContext(uint32_t address, const oni_dev_idx_t, std::shared_ptr); - - int WriteByte(uint32_t address, uint32_t value, bool sixteenBitAddress = false); - - int WriteWord(uint32_t address, uint32_t value, uint32_t numBytes, bool sixteenBitAddress = false); - - int ReadByte(uint32_t address, oni_reg_val_t* value, bool sixteenBitAddress = false); - - int ReadWord(uint32_t address, uint32_t numBytes, uint32_t* value, bool sixteenBitAddress = false); +class I2CRegisterContext +{ +public: + I2CRegisterContext (uint32_t address, const oni_dev_idx_t, std::shared_ptr); - int set933I2cRate(double); + int WriteByte (uint32_t address, uint32_t value, bool sixteenBitAddress = false); + int WriteWord (uint32_t address, uint32_t value, uint32_t numBytes, bool sixteenBitAddress = false); + int ReadByte (uint32_t address, oni_reg_val_t* value, bool sixteenBitAddress = false); + int ReadWord (uint32_t address, uint32_t numBytes, uint32_t* value, bool sixteenBitAddress = false); - protected: - std::shared_ptr i2cContext; + int set933I2cRate (double); - private: +protected: + std::shared_ptr i2cContext; - const oni_dev_idx_t deviceIndex; +private: + const oni_dev_idx_t deviceIndex; - const uint32_t i2cAddress; + const uint32_t i2cAddress; - JUCE_LEAK_DETECTOR(I2CRegisterContext); - }; -} + JUCE_LEAK_DETECTOR (I2CRegisterContext); +}; +} // namespace OnixSourcePlugin diff --git a/Source/NeuropixelsComponents.h b/Source/NeuropixelsComponents.h index 900337a..e02d5c7 100644 --- a/Source/NeuropixelsComponents.h +++ b/Source/NeuropixelsComponents.h @@ -1,516 +1,513 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #pragma once #include +#include #include #include -#include - -#include "UI/ActivityView.h" #include "Onix1.h" namespace OnixSourcePlugin { - enum class ProbeType - { - NONE = 1, - NPX_V1, - NPX_V2, - }; - - enum class Bank - { - NONE = -1, - A = 0, - B = 1, - C = 2, - D = 3, - E = 4, - F = 5, - G = 6, - H = 7, - I = 8, - J = 9, - K = 10, - L = 11, - M = 12, - A1 = 13, // used for quad base - A2 = 14, - A3 = 15, - A4 = 16, - B1 = 17, - B2 = 18, - B3 = 19, - B4 = 20, - C1 = 21, - C2 = 22, - C3 = 23, - C4 = 24, - D1 = 25, - D2 = 26, - D3 = 27, - D4 = 28, - OFF = 255 //used in v1 API - }; - - enum class ElectrodeStatus - { - CONNECTED, - DISCONNECTED - }; - - enum class ElectrodeType - { - ELECTRODE, - REFERENCE - }; - - struct ElectrodeMetadata - { - int global_index; - int shank_local_index; - int shank; - int column_index; - int channel; - int row_index; - float xpos; // position on shank, in microns - float ypos; // position on shank, in microns - float site_width; // in microns - Bank bank; - ElectrodeStatus status; - ElectrodeType type; - bool isSelected; - Colour colour; - }; - - struct ProbeMetadata - { - int shank_count; - int electrodes_per_shank; - int num_adcs; - int adc_bits; - std::vector> shankOutline; - std::vector> probeContour; - int columns_per_shank; - int rows_per_shank; - std::string name; - bool switchable; - }; - - enum class NeuropixelsV1Registers : uint32_t - { - OP_MODE = 0x00, - REC_MOD = 0x01, - CAL_MOD = 0x02, - TEST_CONFIG1 = 0x03, - TEST_CONFIG2 = 0x04, - TEST_CONFIG3 = 0x05, - TEST_CONFIG4 = 0x06, - TEST_CONFIG5 = 0x07, - STATUS = 0X08, - SYNC = 0X09 - }; - - enum class NeuropixelsV1ShiftRegisters : uint32_t - { - SR_CHAIN1 = 0X0E, - SR_CHAIN3 = 0X0C, - SR_CHAIN2 = 0X0D, - SR_LENGTH2 = 0X0F, - SR_LENGTH1 = 0X10, - SOFT_RESET = 0X11 - }; - - enum class NeuropixelsV1CalibrationRegisterValues : uint32_t - { - CAL_OFF = 0, - OSC_ACTIVE = 1 << 4, // 0 = external osc inactive, 1 = activate the external calibration oscillator - ADC_CAL = 1 << 5, // Enable ADC calibration - CH_CAL = 1 << 6, // Enable channel gain calibration - PIX_CAL = 1 << 7, // Enable pixel + channel gain calibration - - // Useful combinations - OSC_ACTIVE_AND_ADC_CAL = OSC_ACTIVE | ADC_CAL, - OSC_ACTIVE_AND_CH_CAL = OSC_ACTIVE | CH_CAL, - OSC_ACTIVE_AND_PIX_CAL = OSC_ACTIVE | PIX_CAL, - }; - - enum class NeuropixelsV1OperationRegisterValues : uint32_t - { - TEST = 1 << 3, // Enable Test mode - DIG_TEST = 1 << 4, // Enable Digital Test mode - CALIBRATE = 1 << 5, // Enable calibration mode - RECORD = 1 << 6, // Enable recording mode - POWER_DOWN = 1 << 7, // Enable power down mode - - // Useful combinations - RECORD_AND_DIG_TEST = RECORD | DIG_TEST, - RECORD_AND_CALIBRATE = RECORD | CALIBRATE, - }; - - enum class NeuropixelsV1RecordRegisterValues : uint32_t - { - RESET_ALL = 1 << 5, // 1 = Set analog SR chains to default values - DIG_ENABLE = 1 << 6, // 0 = Reset the MUX, ADC, and PSB counter, 1 = Disable reset - CH_ENABLE = 1 << 7, // 0 = Reset channel pseudo-registers, 1 = Disable reset - - // Useful combinations - SR_RESET = RESET_ALL | CH_ENABLE | DIG_ENABLE, - DIG_CH_RESET = 0, // Yes, this is actually correct - ACTIVE = DIG_ENABLE | CH_ENABLE, - }; - - enum class NeuropixelsV1Reference : unsigned char - { - External = 0b001, - Tip = 0b010 - }; - - enum class NeuropixelsV1Gain : unsigned char - { - Gain50 = 0b000, - Gain125 = 0b001, - Gain250 = 0b010, - Gain500 = 0b011, - Gain1000 = 0b100, - Gain1500 = 0b101, - Gain2000 = 0b110, - Gain3000 = 0b111 - }; - - struct NeuropixelsV1Adc - { - public: - const int compP; - const int compN; - const int slope; - const int coarse; - const int fine; - const int cfix; - const int offset; - const int threshold; - - NeuropixelsV1Adc(int compP_ = 16, int compN_ = 16, int slope_ = 0, int coarse_ = 0, int fine_ = 0, int cfix_ = 0, int offset_ = 0, int threshold_ = 512) - : compP(compP_), compN(compN_), slope(slope_), coarse(coarse_), fine(fine_), cfix(cfix_), offset(offset_), threshold(threshold_) - { - } - }; - - struct NeuropixelsV1Values - { - static constexpr int numberOfChannels = 384; - static constexpr int numberOfElectrodes = 960; - static constexpr int numberOfShanks = 1; - static constexpr int numberOfSettings = 1; - static constexpr int AdcCount = 32; - static constexpr int FrameWordsV1e = 40; - static constexpr int FrameWordsV1f = 36; - }; - - struct NeuropixelsV2eValues - { - static constexpr int numberOfChannels = 384; - static constexpr int electrodesPerShank = 1280; - static constexpr int numberOfShanks = 4; - static constexpr int numberOfElectrodes = numberOfShanks * electrodesPerShank; - static constexpr int numberOfSettings = 2; - }; - - template - struct ProbeSettings - { - void updateProbeSettings(ProbeSettings* newSettings) - { - availableElectrodeConfigurations = newSettings->availableElectrodeConfigurations; - availableApGains = newSettings->availableApGains; - availableLfpGains = newSettings->availableLfpGains; - availableReferences = newSettings->availableReferences; - availableBanks = newSettings->availableBanks; - - electrodeConfigurationIndex = newSettings->electrodeConfigurationIndex; - apGainIndex = newSettings->apGainIndex; - lfpGainIndex = newSettings->lfpGainIndex; - referenceIndex = newSettings->referenceIndex; - apFilterState = newSettings->apFilterState; - - selectedBank = newSettings->selectedBank; - selectedShank = newSettings->selectedShank; - selectedElectrode = newSettings->selectedElectrode; - electrodeMetadata = newSettings->electrodeMetadata; - - probeType = newSettings->probeType; - - probeMetadata = newSettings->probeMetadata; - }; - - void clearElectrodeSelection() - { - selectedBank.fill(Bank::A); - selectedShank.fill(0); - selectedElectrode.fill(-1); - } - - void selectElectrodes(std::vector electrodes) - { - for (int i = 0; i < electrodes.size(); i++) - { - selectElectrode(electrodes[i]); - } - } - - void selectElectrode(int electrode) - { - Bank bank = electrodeMetadata[electrode].bank; - int channel = electrodeMetadata[electrode].channel; - int shank = electrodeMetadata[electrode].shank; - int global_index = electrodeMetadata[electrode].global_index; - - for (int j = 0; j < electrodeMetadata.size(); j++) - { - if (electrodeMetadata[j].channel == channel) - { - if (electrodeMetadata[j].bank == bank && electrodeMetadata[j].shank == shank) - { - electrodeMetadata[j].status = ElectrodeStatus::CONNECTED; - } - - else - { - electrodeMetadata[j].status = ElectrodeStatus::DISCONNECTED; - } - } - } - - selectedBank[channel] = bank; - selectedShank[channel] = shank; - selectedElectrode[channel] = global_index; - } - - Array availableElectrodeConfigurations; - Array availableApGains; // Available AP gain values for each channel (if any) - Array availableLfpGains; // Available LFP gain values for each channel (if any) - Array availableReferences; // reference types - Array availableBanks; // bank inds - - int electrodeConfigurationIndex; - int apGainIndex; - int lfpGainIndex; - int referenceIndex; - bool apFilterState; - - std::array selectedBank; - std::array selectedShank; - std::array selectedElectrode; - std::array electrodeMetadata; - - ProbeType probeType; - - ProbeMetadata probeMetadata; - - bool isValid = false; - }; - - template - std::vector toBitReversedBytes(std::bitset bits) - { - std::vector bytes((bits.size() - 1) / 8 + 1); - - for (int i = 0; i < bytes.size(); i++) - { - for (int j = 0; j < 8; j++) - { - bytes[i] |= bits[i * 8 + j] << (8 - j - 1); - } - } - - return bytes; - } - - template - class INeuropixel - { - public: - INeuropixel(int numSettings, int numShanks) : numberOfShanks(numShanks) - { - if (numSettings > 2) return; - - for (int i = 0; i < numSettings; i++) - { - settings.emplace_back(std::make_unique>()); - } - } - - static const int numberOfChannels = ch; - static const int numberOfElectrodes = e; - const int numberOfShanks; - - std::vector>> settings; - - virtual void setSettings(ProbeSettings* settings_, int index) { return; } - - virtual void defineMetadata(ProbeSettings* settings) { return; } - - virtual uint64_t getProbeSerialNumber(int index) { return 0; } - - virtual std::vector selectElectrodeConfiguration(int electrodeConfigurationIndex) { return {}; } - }; - - static constexpr int shankConfigurationBitCount = 968; - static constexpr int BaseConfigurationBitCount = 2448; - - using ShankBitset = std::bitset; - using ConfigBitsArray = std::array, 2>; - - static class NeuropixelsV1 - { - public: - - ShankBitset static makeShankBits(NeuropixelsV1Reference reference, std::array channelMap) - { - constexpr int shankBitExt1 = 965; - constexpr int shankBitExt2 = 2; - constexpr int shankBitTip1 = 484; - constexpr int shankBitTip2 = 483; - constexpr int internalReferenceChannel = 191; - - std::bitset shankBits; - - for (auto e : channelMap) - { - if (e == internalReferenceChannel) continue; - - int bitIndex = e % 2 == 0 - ? 485 + (e / 2) - : 482 - (e / 2); - - shankBits[bitIndex] = true; - } - - switch (reference) - { - case NeuropixelsV1Reference::External: - shankBits[shankBitExt1] = true; - shankBits[shankBitExt2] = true; - break; - case NeuropixelsV1Reference::Tip: - shankBits[shankBitTip1] = true; - shankBits[shankBitTip2] = true; - break; - default: - break; - } - - if (channelMap.size() != NeuropixelsV1Values::numberOfChannels) - { - LOGE("Invalid number of channels connected for Neuropixels 1.0, configuration might be invalid."); - } - - return shankBits; - } - - ConfigBitsArray static makeConfigBits(NeuropixelsV1Reference reference, NeuropixelsV1Gain spikeAmplifierGain, NeuropixelsV1Gain lfpAmplifierGain, bool spikeFilterEnabled, std::vector adcs) - { - if (adcs.size() != NeuropixelsV1Values::AdcCount) - throw error_str("Invalid number of ADC values given."); - - const int BaseConfigurationConfigOffset = 576; - - ConfigBitsArray baseConfigs; - - for (size_t i = 0; i < NeuropixelsV1Values::numberOfChannels; i++) - { - size_t configIdx = i % 2; - - size_t refIdx = configIdx == 0 ? - (382 - i) / 2 * 3 : - (383 - i) / 2 * 3; - - baseConfigs[configIdx][refIdx + 0] = ((unsigned char)reference >> 0 & 0x1) == 1; - baseConfigs[configIdx][refIdx + 1] = ((unsigned char)reference >> 1 & 0x1) == 1; - baseConfigs[configIdx][refIdx + 2] = ((unsigned char)reference >> 2 & 0x1) == 1; - - size_t chanOptsIdx = BaseConfigurationConfigOffset + ((i - configIdx) * 4); - - baseConfigs[configIdx][chanOptsIdx + 0] = ((unsigned char)spikeAmplifierGain >> 0 & 0x1) == 1; - baseConfigs[configIdx][chanOptsIdx + 1] = ((unsigned char)spikeAmplifierGain >> 1 & 0x1) == 1; - baseConfigs[configIdx][chanOptsIdx + 2] = ((unsigned char)spikeAmplifierGain >> 2 & 0x1) == 1; - - baseConfigs[configIdx][chanOptsIdx + 3] = ((unsigned char)lfpAmplifierGain >> 0 & 0x1) == 1; - baseConfigs[configIdx][chanOptsIdx + 4] = ((unsigned char)lfpAmplifierGain >> 1 & 0x1) == 1; - baseConfigs[configIdx][chanOptsIdx + 5] = ((unsigned char)lfpAmplifierGain >> 2 & 0x1) == 1; +enum class ProbeType +{ + NONE = 1, + NPX_V1, + NPX_V2, +}; + +enum class Bank +{ + NONE = -1, + A = 0, + B = 1, + C = 2, + D = 3, + E = 4, + F = 5, + G = 6, + H = 7, + I = 8, + J = 9, + K = 10, + L = 11, + M = 12, + A1 = 13, // used for quad base + A2 = 14, + A3 = 15, + A4 = 16, + B1 = 17, + B2 = 18, + B3 = 19, + B4 = 20, + C1 = 21, + C2 = 22, + C3 = 23, + C4 = 24, + D1 = 25, + D2 = 26, + D3 = 27, + D4 = 28, + OFF = 255 // used in v1 API +}; + +enum class ElectrodeStatus +{ + CONNECTED, + DISCONNECTED +}; + +enum class ElectrodeType +{ + ELECTRODE, + REFERENCE +}; + +struct ElectrodeMetadata +{ + int global_index; + int shank_local_index; + int shank; + int column_index; + int channel; + int row_index; + float xpos; // position on shank, in microns + float ypos; // position on shank, in microns + float site_width; // in microns + Bank bank; + ElectrodeStatus status; + ElectrodeType type; + bool isSelected; + Colour colour; +}; + +struct ProbeMetadata +{ + int shank_count; + int electrodes_per_shank; + int num_adcs; + int adc_bits; + std::vector> shankOutline; + std::vector> probeContour; + int columns_per_shank; + int rows_per_shank; + std::string name; + bool switchable; +}; + +enum class NeuropixelsV1Registers : uint32_t +{ + OP_MODE = 0x00, + REC_MOD = 0x01, + CAL_MOD = 0x02, + TEST_CONFIG1 = 0x03, + TEST_CONFIG2 = 0x04, + TEST_CONFIG3 = 0x05, + TEST_CONFIG4 = 0x06, + TEST_CONFIG5 = 0x07, + STATUS = 0X08, + SYNC = 0X09 +}; + +enum class NeuropixelsV1ShiftRegisters : uint32_t +{ + SR_CHAIN1 = 0X0E, + SR_CHAIN3 = 0X0C, + SR_CHAIN2 = 0X0D, + SR_LENGTH2 = 0X0F, + SR_LENGTH1 = 0X10, + SOFT_RESET = 0X11 +}; + +enum class NeuropixelsV1CalibrationRegisterValues : uint32_t +{ + CAL_OFF = 0, + OSC_ACTIVE = 1 << 4, // 0 = external osc inactive, 1 = activate the external calibration oscillator + ADC_CAL = 1 << 5, // Enable ADC calibration + CH_CAL = 1 << 6, // Enable channel gain calibration + PIX_CAL = 1 << 7, // Enable pixel + channel gain calibration + + // Useful combinations + OSC_ACTIVE_AND_ADC_CAL = OSC_ACTIVE | ADC_CAL, + OSC_ACTIVE_AND_CH_CAL = OSC_ACTIVE | CH_CAL, + OSC_ACTIVE_AND_PIX_CAL = OSC_ACTIVE | PIX_CAL, +}; + +enum class NeuropixelsV1OperationRegisterValues : uint32_t +{ + TEST = 1 << 3, // Enable Test mode + DIG_TEST = 1 << 4, // Enable Digital Test mode + CALIBRATE = 1 << 5, // Enable calibration mode + RECORD = 1 << 6, // Enable recording mode + POWER_DOWN = 1 << 7, // Enable power down mode + + // Useful combinations + RECORD_AND_DIG_TEST = RECORD | DIG_TEST, + RECORD_AND_CALIBRATE = RECORD | CALIBRATE, +}; + +enum class NeuropixelsV1RecordRegisterValues : uint32_t +{ + RESET_ALL = 1 << 5, // 1 = Set analog SR chains to default values + DIG_ENABLE = 1 << 6, // 0 = Reset the MUX, ADC, and PSB counter, 1 = Disable reset + CH_ENABLE = 1 << 7, // 0 = Reset channel pseudo-registers, 1 = Disable reset - baseConfigs[configIdx][chanOptsIdx + 6] = false; - baseConfigs[configIdx][chanOptsIdx + 7] = !spikeFilterEnabled; - } + // Useful combinations + SR_RESET = RESET_ALL | CH_ENABLE | DIG_ENABLE, + DIG_CH_RESET = 0, // Yes, this is actually correct + ACTIVE = DIG_ENABLE | CH_ENABLE, +}; - int k = 0; +enum class NeuropixelsV1Reference : unsigned char +{ + External = 0b001, + Tip = 0b010 +}; - for (const auto& adc : adcs) - { - auto configIdx = k % 2; - size_t d = k++ / 2; - - size_t compOffset = 2406 - 42 * (d / 2) + (d % 2) * 10; - size_t slopeOffset = compOffset + 20 + (d % 2); - - auto compP = std::bitset<8>{ (unsigned char)(adc.compP) }; - auto compN = std::bitset<8>{ (unsigned char)(adc.compN) }; - auto cfix = std::bitset<8>{ (unsigned char)(adc.cfix) }; - auto slope = std::bitset<8>{ (unsigned char)(adc.slope) }; - auto coarse = std::bitset<8>{ (unsigned char)(adc.coarse) }; - auto fine = std::bitset<8>{ (unsigned char)(adc.fine) }; - - baseConfigs[configIdx][compOffset + 0] = compP[0]; - baseConfigs[configIdx][compOffset + 1] = compP[1]; - baseConfigs[configIdx][compOffset + 2] = compP[2]; - baseConfigs[configIdx][compOffset + 3] = compP[3]; - baseConfigs[configIdx][compOffset + 4] = compP[4]; - - baseConfigs[configIdx][compOffset + 5] = compN[0]; - baseConfigs[configIdx][compOffset + 6] = compN[1]; - baseConfigs[configIdx][compOffset + 7] = compN[2]; - baseConfigs[configIdx][compOffset + 8] = compN[3]; - baseConfigs[configIdx][compOffset + 9] = compN[4]; - - baseConfigs[configIdx][slopeOffset + 0] = slope[0]; - baseConfigs[configIdx][slopeOffset + 1] = slope[1]; - baseConfigs[configIdx][slopeOffset + 2] = slope[2]; - - baseConfigs[configIdx][slopeOffset + 3] = fine[0]; - baseConfigs[configIdx][slopeOffset + 4] = fine[1]; - - baseConfigs[configIdx][slopeOffset + 5] = coarse[0]; - baseConfigs[configIdx][slopeOffset + 6] = coarse[1]; - - baseConfigs[configIdx][slopeOffset + 7] = cfix[0]; - baseConfigs[configIdx][slopeOffset + 8] = cfix[1]; - baseConfigs[configIdx][slopeOffset + 9] = cfix[2]; - baseConfigs[configIdx][slopeOffset + 10] = cfix[3]; - } - - return baseConfigs; - } - }; +enum class NeuropixelsV1Gain : unsigned char +{ + Gain50 = 0b000, + Gain125 = 0b001, + Gain250 = 0b010, + Gain500 = 0b011, + Gain1000 = 0b100, + Gain1500 = 0b101, + Gain2000 = 0b110, + Gain3000 = 0b111 +}; + +struct NeuropixelsV1Adc +{ +public: + const int compP; + const int compN; + const int slope; + const int coarse; + const int fine; + const int cfix; + const int offset; + const int threshold; + + NeuropixelsV1Adc (int compP_ = 16, int compN_ = 16, int slope_ = 0, int coarse_ = 0, int fine_ = 0, int cfix_ = 0, int offset_ = 0, int threshold_ = 512) + : compP (compP_), compN (compN_), slope (slope_), coarse (coarse_), fine (fine_), cfix (cfix_), offset (offset_), threshold (threshold_) + { + } +}; + +struct NeuropixelsV1Values +{ + static constexpr int numberOfChannels = 384; + static constexpr int numberOfElectrodes = 960; + static constexpr int numberOfShanks = 1; + static constexpr int numberOfSettings = 1; + static constexpr int AdcCount = 32; + static constexpr int FrameWordsV1e = 40; + static constexpr int FrameWordsV1f = 36; +}; + +struct NeuropixelsV2eValues +{ + static constexpr int numberOfChannels = 384; + static constexpr int electrodesPerShank = 1280; + static constexpr int numberOfShanks = 4; + static constexpr int numberOfElectrodes = numberOfShanks * electrodesPerShank; + static constexpr int numberOfSettings = 2; +}; + +template +struct ProbeSettings +{ + void updateProbeSettings (ProbeSettings* newSettings) + { + availableElectrodeConfigurations = newSettings->availableElectrodeConfigurations; + availableApGains = newSettings->availableApGains; + availableLfpGains = newSettings->availableLfpGains; + availableReferences = newSettings->availableReferences; + availableBanks = newSettings->availableBanks; + + electrodeConfigurationIndex = newSettings->electrodeConfigurationIndex; + apGainIndex = newSettings->apGainIndex; + lfpGainIndex = newSettings->lfpGainIndex; + referenceIndex = newSettings->referenceIndex; + apFilterState = newSettings->apFilterState; + + selectedBank = newSettings->selectedBank; + selectedShank = newSettings->selectedShank; + selectedElectrode = newSettings->selectedElectrode; + electrodeMetadata = newSettings->electrodeMetadata; + + probeType = newSettings->probeType; + + probeMetadata = newSettings->probeMetadata; + }; + + void clearElectrodeSelection() + { + selectedBank.fill (Bank::A); + selectedShank.fill (0); + selectedElectrode.fill (-1); + } + + void selectElectrodes (std::vector electrodes) + { + for (int i = 0; i < electrodes.size(); i++) + { + selectElectrode (electrodes[i]); + } + } + + void selectElectrode (int electrode) + { + Bank bank = electrodeMetadata[electrode].bank; + int channel = electrodeMetadata[electrode].channel; + int shank = electrodeMetadata[electrode].shank; + int global_index = electrodeMetadata[electrode].global_index; + + for (int j = 0; j < electrodeMetadata.size(); j++) + { + if (electrodeMetadata[j].channel == channel) + { + if (electrodeMetadata[j].bank == bank && electrodeMetadata[j].shank == shank) + { + electrodeMetadata[j].status = ElectrodeStatus::CONNECTED; + } + + else + { + electrodeMetadata[j].status = ElectrodeStatus::DISCONNECTED; + } + } + } + + selectedBank[channel] = bank; + selectedShank[channel] = shank; + selectedElectrode[channel] = global_index; + } + + Array availableElectrodeConfigurations; + Array availableApGains; // Available AP gain values for each channel (if any) + Array availableLfpGains; // Available LFP gain values for each channel (if any) + Array availableReferences; // reference types + Array availableBanks; // bank inds + + int electrodeConfigurationIndex; + int apGainIndex; + int lfpGainIndex; + int referenceIndex; + bool apFilterState; + + std::array selectedBank; + std::array selectedShank; + std::array selectedElectrode; + std::array electrodeMetadata; + + ProbeType probeType; + + ProbeMetadata probeMetadata; + + bool isValid = false; +}; + +template +std::vector toBitReversedBytes (std::bitset bits) +{ + std::vector bytes ((bits.size() - 1) / 8 + 1); + + for (int i = 0; i < bytes.size(); i++) + { + for (int j = 0; j < 8; j++) + { + bytes[i] |= bits[i * 8 + j] << (8 - j - 1); + } + } + + return bytes; } + +template +class INeuropixel +{ +public: + INeuropixel (int numSettings, int numShanks) : numberOfShanks (numShanks) + { + if (numSettings > 2) + return; + + for (int i = 0; i < numSettings; i++) + { + settings.emplace_back (std::make_unique>()); + } + } + + static const int numberOfChannels = ch; + static const int numberOfElectrodes = e; + const int numberOfShanks; + + std::vector>> settings; + + virtual void setSettings (ProbeSettings* settings_, int index) { return; } + + virtual void defineMetadata (ProbeSettings* settings) { return; } + + virtual uint64_t getProbeSerialNumber (int index) { return 0; } + + virtual std::vector selectElectrodeConfiguration (int electrodeConfigurationIndex) { return {}; } +}; + +static constexpr int shankConfigurationBitCount = 968; +static constexpr int BaseConfigurationBitCount = 2448; + +using ShankBitset = std::bitset; +using ConfigBitsArray = std::array, 2>; + +static class NeuropixelsV1 +{ +public: + ShankBitset static makeShankBits (NeuropixelsV1Reference reference, std::array channelMap) + { + constexpr int shankBitExt1 = 965; + constexpr int shankBitExt2 = 2; + constexpr int shankBitTip1 = 484; + constexpr int shankBitTip2 = 483; + constexpr int internalReferenceChannel = 191; + + std::bitset shankBits; + + for (auto e : channelMap) + { + if (e == internalReferenceChannel) + continue; + + int bitIndex = e % 2 == 0 + ? 485 + (e / 2) + : 482 - (e / 2); + + shankBits[bitIndex] = true; + } + + switch (reference) + { + case NeuropixelsV1Reference::External: + shankBits[shankBitExt1] = true; + shankBits[shankBitExt2] = true; + break; + case NeuropixelsV1Reference::Tip: + shankBits[shankBitTip1] = true; + shankBits[shankBitTip2] = true; + break; + default: + break; + } + + if (channelMap.size() != NeuropixelsV1Values::numberOfChannels) + { + LOGE ("Invalid number of channels connected for Neuropixels 1.0, configuration might be invalid."); + } + + return shankBits; + } + + ConfigBitsArray static makeConfigBits (NeuropixelsV1Reference reference, NeuropixelsV1Gain spikeAmplifierGain, NeuropixelsV1Gain lfpAmplifierGain, bool spikeFilterEnabled, std::vector adcs) + { + if (adcs.size() != NeuropixelsV1Values::AdcCount) + throw error_str ("Invalid number of ADC values given."); + + const int BaseConfigurationConfigOffset = 576; + + ConfigBitsArray baseConfigs; + + for (size_t i = 0; i < NeuropixelsV1Values::numberOfChannels; i++) + { + size_t configIdx = i % 2; + + size_t refIdx = configIdx == 0 ? (382 - i) / 2 * 3 : (383 - i) / 2 * 3; + + baseConfigs[configIdx][refIdx + 0] = ((unsigned char) reference >> 0 & 0x1) == 1; + baseConfigs[configIdx][refIdx + 1] = ((unsigned char) reference >> 1 & 0x1) == 1; + baseConfigs[configIdx][refIdx + 2] = ((unsigned char) reference >> 2 & 0x1) == 1; + + size_t chanOptsIdx = BaseConfigurationConfigOffset + ((i - configIdx) * 4); + + baseConfigs[configIdx][chanOptsIdx + 0] = ((unsigned char) spikeAmplifierGain >> 0 & 0x1) == 1; + baseConfigs[configIdx][chanOptsIdx + 1] = ((unsigned char) spikeAmplifierGain >> 1 & 0x1) == 1; + baseConfigs[configIdx][chanOptsIdx + 2] = ((unsigned char) spikeAmplifierGain >> 2 & 0x1) == 1; + + baseConfigs[configIdx][chanOptsIdx + 3] = ((unsigned char) lfpAmplifierGain >> 0 & 0x1) == 1; + baseConfigs[configIdx][chanOptsIdx + 4] = ((unsigned char) lfpAmplifierGain >> 1 & 0x1) == 1; + baseConfigs[configIdx][chanOptsIdx + 5] = ((unsigned char) lfpAmplifierGain >> 2 & 0x1) == 1; + + baseConfigs[configIdx][chanOptsIdx + 6] = false; + baseConfigs[configIdx][chanOptsIdx + 7] = ! spikeFilterEnabled; + } + + int k = 0; + + for (const auto& adc : adcs) + { + auto configIdx = k % 2; + size_t d = k++ / 2; + + size_t compOffset = 2406 - 42 * (d / 2) + (d % 2) * 10; + size_t slopeOffset = compOffset + 20 + (d % 2); + + auto compP = std::bitset<8> { (unsigned char) (adc.compP) }; + auto compN = std::bitset<8> { (unsigned char) (adc.compN) }; + auto cfix = std::bitset<8> { (unsigned char) (adc.cfix) }; + auto slope = std::bitset<8> { (unsigned char) (adc.slope) }; + auto coarse = std::bitset<8> { (unsigned char) (adc.coarse) }; + auto fine = std::bitset<8> { (unsigned char) (adc.fine) }; + + baseConfigs[configIdx][compOffset + 0] = compP[0]; + baseConfigs[configIdx][compOffset + 1] = compP[1]; + baseConfigs[configIdx][compOffset + 2] = compP[2]; + baseConfigs[configIdx][compOffset + 3] = compP[3]; + baseConfigs[configIdx][compOffset + 4] = compP[4]; + + baseConfigs[configIdx][compOffset + 5] = compN[0]; + baseConfigs[configIdx][compOffset + 6] = compN[1]; + baseConfigs[configIdx][compOffset + 7] = compN[2]; + baseConfigs[configIdx][compOffset + 8] = compN[3]; + baseConfigs[configIdx][compOffset + 9] = compN[4]; + + baseConfigs[configIdx][slopeOffset + 0] = slope[0]; + baseConfigs[configIdx][slopeOffset + 1] = slope[1]; + baseConfigs[configIdx][slopeOffset + 2] = slope[2]; + + baseConfigs[configIdx][slopeOffset + 3] = fine[0]; + baseConfigs[configIdx][slopeOffset + 4] = fine[1]; + + baseConfigs[configIdx][slopeOffset + 5] = coarse[0]; + baseConfigs[configIdx][slopeOffset + 6] = coarse[1]; + + baseConfigs[configIdx][slopeOffset + 7] = cfix[0]; + baseConfigs[configIdx][slopeOffset + 8] = cfix[1]; + baseConfigs[configIdx][slopeOffset + 9] = cfix[2]; + baseConfigs[configIdx][slopeOffset + 10] = cfix[3]; + } + + return baseConfigs; + } +}; +} // namespace OnixSourcePlugin diff --git a/Source/Onix1.cpp b/Source/Onix1.cpp index 591d291..18bb9bd 100644 --- a/Source/Onix1.cpp +++ b/Source/Onix1.cpp @@ -1,192 +1,192 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #include "Onix1.h" + #include "OnixDevice.h" using namespace OnixSourcePlugin; -Onix1::Onix1(int hostIndex) +Onix1::Onix1 (int hostIndex) { - ctx_ = oni_create_ctx("riffa"); + ctx_ = oni_create_ctx ("riffa"); - if (ctx_ == nullptr) - throw std::system_error(errno, std::system_category()); + if (ctx_ == nullptr) + throw std::system_error (errno, std::system_category()); - int rc = oni_init_ctx(ctx_, hostIndex); + int rc = oni_init_ctx (ctx_, hostIndex); - if (rc != ONI_ESUCCESS) - throw error_t(rc); + if (rc != ONI_ESUCCESS) + throw error_t (rc); - rc = getOption(ONI_OPT_ACQCLKHZ, &ACQ_CLK_HZ); - if (rc != ONI_ESUCCESS) - throw error_t(rc); + rc = getOption (ONI_OPT_ACQCLKHZ, &ACQ_CLK_HZ); + if (rc != ONI_ESUCCESS) + throw error_t (rc); } Onix1::~Onix1() { - oni_destroy_ctx(ctx_); + oni_destroy_ctx (ctx_); } -int Onix1::getDeviceTable(device_map_t* deviceTable) +int Onix1::getDeviceTable (device_map_t* deviceTable) { - if (deviceTable->size() > 0) - deviceTable->clear(); + if (deviceTable->size() > 0) + deviceTable->clear(); - issueReset(); + issueReset(); - oni_size_t numDevices; - int rc = getOption(ONI_OPT_NUMDEVICES, &numDevices); + oni_size_t numDevices; + int rc = getOption (ONI_OPT_NUMDEVICES, &numDevices); - if (numDevices == 0 || rc != ONI_ESUCCESS) return rc; + if (numDevices == 0 || rc != ONI_ESUCCESS) + return rc; - size_t devicesSize = sizeof(oni_device_t) * numDevices; + size_t devicesSize = sizeof (oni_device_t) * numDevices; - std::vector devices; - devices.resize(numDevices); + std::vector devices; + devices.resize (numDevices); - rc = get_opt_(ONI_OPT_DEVICETABLE, devices.data(), &devicesSize); + rc = get_opt_ (ONI_OPT_DEVICETABLE, devices.data(), &devicesSize); - if (rc != ONI_ESUCCESS) return rc; + if (rc != ONI_ESUCCESS) + return rc; - for (const auto& device : devices) - { - deviceTable->insert({ device.idx, device }); - } + for (const auto& device : devices) + { + deviceTable->insert ({ device.idx, device }); + } - return rc; + return rc; } -std::map Onix1::getHubIds(device_map_t deviceTable) const +std::map Onix1::getHubIds (device_map_t deviceTable) const { - std::map hubIds; + std::map hubIds; - if (deviceTable.size() == 0) - return hubIds; + if (deviceTable.size() == 0) + return hubIds; - auto deviceIndices = getDeviceIndices(deviceTable); + auto deviceIndices = getDeviceIndices (deviceTable); - auto offsets = OnixDevice::getUniqueOffsets(deviceIndices, false); + auto offsets = OnixDevice::getUniqueOffsets (deviceIndices, false); - for (int i = 0; i < offsets.size(); i++) - { - oni_reg_val_t hubId = 0; - int rc = oni_read_reg(ctx_, offsets[i] + ONIX_HUB_DEV_IDX, (uint32_t)ONIX_HUB_HARDWAREID, &hubId); - if (rc != ONI_ESUCCESS) - { - LOGE("Unable to read the hub device index for the hub at index ", offsets[i]); - continue; - } + for (int i = 0; i < offsets.size(); i++) + { + oni_reg_val_t hubId = 0; + int rc = oni_read_reg (ctx_, offsets[i] + ONIX_HUB_DEV_IDX, (uint32_t) ONIX_HUB_HARDWAREID, &hubId); + if (rc != ONI_ESUCCESS) + { + LOGE ("Unable to read the hub device index for the hub at index ", offsets[i]); + continue; + } - hubIds.insert({ offsets[i], hubId }); - } + hubIds.insert ({ offsets[i], hubId }); + } - return hubIds; + return hubIds; } -std::vector Onix1::getDeviceIndices(device_map_t deviceMap, int hubIndex) +std::vector Onix1::getDeviceIndices (device_map_t deviceMap, int hubIndex) { - std::vector deviceIndices; + std::vector deviceIndices; - for (const auto& [idx, dev] : deviceMap) - { - if (dev.id != ONIX_NULL && (hubIndex == -1 || OnixDevice::getOffset(dev.idx) == hubIndex)) - deviceIndices.emplace_back(idx); - } + for (const auto& [idx, dev] : deviceMap) + { + if (dev.id != ONIX_NULL && (hubIndex == -1 || OnixDevice::getOffset (dev.idx) == hubIndex)) + deviceIndices.emplace_back (idx); + } - return deviceIndices; + return deviceIndices; } -int Onix1::get_opt_(int option, void* value, size_t* size) const +int Onix1::get_opt_ (int option, void* value, size_t* size) const { - const ScopedLock lock(registerLock); + const ScopedLock lock (registerLock); - int rc = oni_get_opt(ctx_, option, value, size); - if (rc != ONI_ESUCCESS) - LOGE(oni_error_str(rc)); - return rc; + int rc = oni_get_opt (ctx_, option, value, size); + if (rc != ONI_ESUCCESS) + LOGE (oni_error_str (rc)); + return rc; } -int Onix1::readRegister(oni_dev_idx_t devIndex, oni_reg_addr_t registerAddress, oni_reg_val_t* value) const +int Onix1::readRegister (oni_dev_idx_t devIndex, oni_reg_addr_t registerAddress, oni_reg_val_t* value) const { - const ScopedLock lock(registerLock); + const ScopedLock lock (registerLock); - int rc = oni_read_reg(ctx_, devIndex, registerAddress, value); - if (rc != ONI_ESUCCESS) - LOGE(oni_error_str(rc)); - return rc; + int rc = oni_read_reg (ctx_, devIndex, registerAddress, value); + if (rc != ONI_ESUCCESS) + LOGE (oni_error_str (rc)); + return rc; } -int Onix1::writeRegister(oni_dev_idx_t devIndex, oni_reg_addr_t registerAddress, oni_reg_val_t value) const +int Onix1::writeRegister (oni_dev_idx_t devIndex, oni_reg_addr_t registerAddress, oni_reg_val_t value) const { - const ScopedLock lock(registerLock); + const ScopedLock lock (registerLock); - int rc = oni_write_reg(ctx_, devIndex, registerAddress, value); - if (rc != ONI_ESUCCESS) - LOGE(oni_error_str(rc)); - return rc; + int rc = oni_write_reg (ctx_, devIndex, registerAddress, value); + if (rc != ONI_ESUCCESS) + LOGE (oni_error_str (rc)); + return rc; } -int Onix1::issueReset() -{ - int val = 1; - int rc = setOption(ONI_OPT_RESET, val); - return rc; +int Onix1::issueReset() +{ + int val = 1; + int rc = setOption (ONI_OPT_RESET, val); + return rc; } std::string Onix1::getVersion() -{ - return std::to_string(ONI_VERSION_MAJOR) + "." + std::to_string(ONI_VERSION_MINOR) + "." + std::to_string(ONI_VERSION_PATCH); +{ + return std::to_string (ONI_VERSION_MAJOR) + "." + std::to_string (ONI_VERSION_MINOR) + "." + std::to_string (ONI_VERSION_PATCH); } -double Onix1::convertTimestampToSeconds(uint64_t timestamp) const -{ - return static_cast(timestamp) / ACQ_CLK_HZ; +double Onix1::convertTimestampToSeconds (uint64_t timestamp) const +{ + return static_cast (timestamp) / ACQ_CLK_HZ; } oni_frame_t* Onix1::readFrame() const { - const ScopedLock lock(frameLock); + const ScopedLock lock (frameLock); - oni_frame_t* frame = nullptr; - int rc = oni_read_frame(ctx_, &frame); - if (rc < ONI_ESUCCESS) - { - LOGE(oni_error_str(rc)); - return nullptr; - } + oni_frame_t* frame = nullptr; + int rc = oni_read_frame (ctx_, &frame); + if (rc < ONI_ESUCCESS) + { + LOGE (oni_error_str (rc)); + return nullptr; + } - return frame; + return frame; } -void Onix1::showWarningMessageBoxAsync(std::string title, std::string error_msg) +void Onix1::showWarningMessageBoxAsync (std::string title, std::string error_msg) { - LOGD(error_msg); - MessageManager::callAsync([title, error_msg] - { - AlertWindow::showMessageBoxAsync( - MessageBoxIconType::WarningIcon, - title, - error_msg - ); - }); + LOGD (error_msg); + MessageManager::callAsync ([title, error_msg] + { AlertWindow::showMessageBoxAsync ( + MessageBoxIconType::WarningIcon, + title, + error_msg); }); } diff --git a/Source/Onix1.h b/Source/Onix1.h index c5bd778..a27e0ac 100644 --- a/Source/Onix1.h +++ b/Source/Onix1.h @@ -1,31 +1,31 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #pragma once +#include #include #include #include -#include #include @@ -33,119 +33,119 @@ namespace OnixSourcePlugin { - constexpr char* NEUROPIXELSV1F_HEADSTAGE_NAME = "Neuropixels 1.0f Headstage"; - constexpr char* NEUROPIXELSV1E_HEADSTAGE_NAME = "Neuropixels 1.0e Headstage"; - constexpr char* NEUROPIXELSV2E_HEADSTAGE_NAME = "Neuropixels 2.0e Headstage"; - constexpr char* BREAKOUT_BOARD_NAME = "Breakout Board"; - - class error_t : public std::exception - { - public: - explicit error_t(int errorNum) : errorNum_(errorNum) {} +constexpr char* NEUROPIXELSV1F_HEADSTAGE_NAME = "Neuropixels 1.0f Headstage"; +constexpr char* NEUROPIXELSV1E_HEADSTAGE_NAME = "Neuropixels 1.0e Headstage"; +constexpr char* NEUROPIXELSV2E_HEADSTAGE_NAME = "Neuropixels 2.0e Headstage"; +constexpr char* BREAKOUT_BOARD_NAME = "Breakout Board"; - const char* what() const noexcept override - { - return oni_error_str(errorNum_); - } +class error_t : public std::exception +{ +public: + explicit error_t (int errorNum) : errorNum_ (errorNum) {} - int num() const { return errorNum_; } + const char* what() const noexcept override + { + return oni_error_str (errorNum_); + } - private: - int errorNum_; - }; + int num() const { return errorNum_; } - class error_str : public std::exception - { - public: - explicit error_str(std::string errorStr) : m_errorStr(errorStr) {} +private: + int errorNum_; +}; - const char* what() const noexcept override - { - return m_errorStr.c_str(); - } +class error_str : public std::exception +{ +public: + explicit error_str (std::string errorStr) : m_errorStr (errorStr) {} - std::string str() const { return m_errorStr; } + const char* what() const noexcept override + { + return m_errorStr.c_str(); + } - private: - std::string m_errorStr; - }; + std::string str() const { return m_errorStr; } - using device_map_t = std::map; +private: + std::string m_errorStr; +}; - class Onix1 - { - public: - Onix1(int hostIndex = -1); +using device_map_t = std::map; - ~Onix1(); +class Onix1 +{ +public: + Onix1 (int hostIndex = -1); - inline bool isInitialized() const { return ctx_ != nullptr; } + ~Onix1(); - template - int getOption(int option, opt_t* value) - { - size_t len = sizeof(opt_t); - int rc = get_opt_(option, value, &len); - return rc; - } + inline bool isInitialized() const { return ctx_ != nullptr; } - template - int setOption(int option, const opt_t value) - { - const ScopedLock lock(registerLock); + template + int getOption (int option, opt_t* value) + { + size_t len = sizeof (opt_t); + int rc = get_opt_ (option, value, &len); + return rc; + } - int rc = oni_set_opt(ctx_, option, &value, opt_size_(value)); - if (rc != ONI_ESUCCESS) LOGE(oni_error_str(rc)); - return rc; - } + template + int setOption (int option, const opt_t value) + { + const ScopedLock lock (registerLock); - int readRegister(oni_dev_idx_t devIndex, oni_reg_addr_t registerAddress, oni_reg_val_t* value) const; + int rc = oni_set_opt (ctx_, option, &value, opt_size_ (value)); + if (rc != ONI_ESUCCESS) + LOGE (oni_error_str (rc)); + return rc; + } - int writeRegister(oni_dev_idx_t, oni_reg_addr_t, oni_reg_val_t) const; + int readRegister (oni_dev_idx_t devIndex, oni_reg_addr_t registerAddress, oni_reg_val_t* value) const; - int getDeviceTable(device_map_t*); + int writeRegister (oni_dev_idx_t, oni_reg_addr_t, oni_reg_val_t) const; - oni_frame_t* readFrame() const; + int getDeviceTable (device_map_t*); - int issueReset(); + oni_frame_t* readFrame() const; - static std::string getVersion(); + int issueReset(); - double convertTimestampToSeconds(uint64_t timestamp) const; + static std::string getVersion(); - /** Gets a map of all hubs connected, where the index of the map is the hub address, and the value is the hub ID */ - std::map getHubIds(device_map_t) const; + double convertTimestampToSeconds (uint64_t timestamp) const; - /** Gets a vector of device indices from a device_map_t object, optionally filtered by a specific hub */ - static std::vector getDeviceIndices(device_map_t deviceMap, int hubIndex = -1); + /** Gets a map of all hubs connected, where the index of the map is the hub address, and the value is the hub ID */ + std::map getHubIds (device_map_t) const; - /** Displays an AlertWindow::showMessageBoxAsync window using the message thread, with a warning icon */ - static void showWarningMessageBoxAsync(std::string, std::string); + /** Gets a vector of device indices from a device_map_t object, optionally filtered by a specific hub */ + static std::vector getDeviceIndices (device_map_t deviceMap, int hubIndex = -1); - private: + /** Displays an AlertWindow::showMessageBoxAsync window using the message thread, with a warning icon */ + static void showWarningMessageBoxAsync (std::string, std::string); - /** The ONI ctx object */ - oni_ctx ctx_; +private: + /** The ONI ctx object */ + oni_ctx ctx_; - CriticalSection registerLock; - CriticalSection frameLock; + CriticalSection registerLock; + CriticalSection frameLock; - uint32_t ACQ_CLK_HZ; + uint32_t ACQ_CLK_HZ; - template - size_t opt_size_(opt_t opt) - { - size_t len = 0; - if constexpr (std::is_same::value) - len = strlen(len) + 1; - else if constexpr (std::is_same::value) - len = strlen(len) + 1; - else - len = sizeof(opt); + template + size_t opt_size_ (opt_t opt) + { + size_t len = 0; + if constexpr (std::is_same::value) + len = strlen (len) + 1; + else if constexpr (std::is_same::value) + len = strlen (len) + 1; + else + len = sizeof (opt); - return len; - } + return len; + } - int get_opt_(int option, void* value, size_t* size) const; - }; -} + int get_opt_ (int option, void* value, size_t* size) const; +}; +} // namespace OnixSourcePlugin diff --git a/Source/OnixDevice.cpp b/Source/OnixDevice.cpp index 33537b8..fd1f662 100644 --- a/Source/OnixDevice.cpp +++ b/Source/OnixDevice.cpp @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -25,251 +25,253 @@ using namespace OnixSourcePlugin; const std::map OnixDevice::TypeString = { - {OnixDeviceType::BNO, "BNO055"}, - {OnixDeviceType::POLLEDBNO, "BNO055"}, - {OnixDeviceType::NEUROPIXELSV1E, "Neuropixels 1.0e"}, - {OnixDeviceType::NEUROPIXELSV1F, "Neuropixels 1.0f"}, - {OnixDeviceType::NEUROPIXELSV2E, "Neuropixels 2.0"}, - {OnixDeviceType::PORT_CONTROL, "Port Control"}, - {OnixDeviceType::MEMORYMONITOR, "Memory Monitor"}, - {OnixDeviceType::OUTPUTCLOCK, "Output Clock"}, - {OnixDeviceType::HARPSYNCINPUT, "Harp Sync Input"}, - {OnixDeviceType::ANALOGIO, "Analog IO"}, - {OnixDeviceType::DIGITALIO, "Digital IO"} + { OnixDeviceType::BNO, "BNO055" }, + { OnixDeviceType::POLLEDBNO, "BNO055" }, + { OnixDeviceType::NEUROPIXELSV1E, "Neuropixels 1.0e" }, + { OnixDeviceType::NEUROPIXELSV1F, "Neuropixels 1.0f" }, + { OnixDeviceType::NEUROPIXELSV2E, "Neuropixels 2.0" }, + { OnixDeviceType::PORT_CONTROL, "Port Control" }, + { OnixDeviceType::MEMORYMONITOR, "Memory Monitor" }, + { OnixDeviceType::OUTPUTCLOCK, "Output Clock" }, + { OnixDeviceType::HARPSYNCINPUT, "Harp Sync Input" }, + { OnixDeviceType::ANALOGIO, "Analog IO" }, + { OnixDeviceType::DIGITALIO, "Digital IO" } }; -OnixDevice::OnixDevice(std::string name_, std::string hubName, OnixDeviceType type_, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx, bool passthrough) - : type(type_), deviceIdx(deviceIdx_), frameQueue(32) +OnixDevice::OnixDevice (std::string name_, std::string hubName, OnixDeviceType type_, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx, bool passthrough) + : type (type_), deviceIdx (deviceIdx_), frameQueue (32) { - deviceContext = ctx; - name = name_; - m_hubName = hubName; - isPassthrough = passthrough; + deviceContext = ctx; + name = name_; + m_hubName = hubName; + isPassthrough = passthrough; } -oni_dev_idx_t OnixDevice::getDeviceIdx(bool getPassthroughIndex) +oni_dev_idx_t OnixDevice::getDeviceIdx (bool getPassthroughIndex) { - if (isPassthrough && !getPassthroughIndex) - return getDeviceIndexFromPassthroughIndex(deviceIdx); - else - return deviceIdx; + if (isPassthrough && ! getPassthroughIndex) + return getDeviceIndexFromPassthroughIndex (deviceIdx); + else + return deviceIdx; } -std::string OnixDevice::createStreamName(std::vector names) +std::string OnixDevice::createStreamName (std::vector names) { - std::string streamName; + std::string streamName; - for (int i = 0; i < names.size(); i++) - { - streamName += names[i]; + for (int i = 0; i < names.size(); i++) + { + streamName += names[i]; - if (i != names.size() - 1) streamName += "-"; - } + if (i != names.size() - 1) + streamName += "-"; + } - streamName.erase(std::remove(streamName.begin(), streamName.end(), ' '), streamName.end()); + streamName.erase (std::remove (streamName.begin(), streamName.end(), ' '), streamName.end()); - return streamName; + return streamName; } -bool OnixDevice::isValidPassthroughIndex(oni_dev_idx_t passthroughIndex) +bool OnixDevice::isValidPassthroughIndex (oni_dev_idx_t passthroughIndex) { - return passthroughIndex == (uint32_t)PassthroughIndex::A || passthroughIndex == (uint32_t)PassthroughIndex::B; + return passthroughIndex == (uint32_t) PassthroughIndex::A || passthroughIndex == (uint32_t) PassthroughIndex::B; } -oni_dev_idx_t OnixDevice::getHubIndexFromPassthroughIndex(oni_dev_idx_t passthroughIndex) +oni_dev_idx_t OnixDevice::getHubIndexFromPassthroughIndex (oni_dev_idx_t passthroughIndex) { - if (!isValidPassthroughIndex(passthroughIndex)) - { - Onix1::showWarningMessageBoxAsync("Invalid Index", "Invalid passthrough index given. Value was " + std::to_string(passthroughIndex)); - return 0; - } + if (! isValidPassthroughIndex (passthroughIndex)) + { + Onix1::showWarningMessageBoxAsync ("Invalid Index", "Invalid passthrough index given. Value was " + std::to_string (passthroughIndex)); + return 0; + } - return (passthroughIndex - 7) << 8; + return (passthroughIndex - 7) << 8; } -oni_dev_idx_t OnixDevice::getPassthroughIndexFromHubIndex(oni_dev_idx_t hubIndex) +oni_dev_idx_t OnixDevice::getPassthroughIndexFromHubIndex (oni_dev_idx_t hubIndex) { - auto index = (hubIndex >> 8) + 7; + auto index = (hubIndex >> 8) + 7; - if (!isValidPassthroughIndex(index)) - { - Onix1::showWarningMessageBoxAsync("Invalid Index", "Invalid hub index given. Value was " + std::to_string(hubIndex)); - return 0; - } + if (! isValidPassthroughIndex (index)) + { + Onix1::showWarningMessageBoxAsync ("Invalid Index", "Invalid hub index given. Value was " + std::to_string (hubIndex)); + return 0; + } - return index; + return index; } -oni_dev_idx_t OnixDevice::getDeviceIndexFromPassthroughIndex(oni_dev_idx_t passthroughIndex) const +oni_dev_idx_t OnixDevice::getDeviceIndexFromPassthroughIndex (oni_dev_idx_t passthroughIndex) const { - if (!isValidPassthroughIndex(passthroughIndex)) - { - Onix1::showWarningMessageBoxAsync("Invalid Index", "Invalid passthrough index given. Value was " + std::to_string(passthroughIndex)); - return 0; - } + if (! isValidPassthroughIndex (passthroughIndex)) + { + Onix1::showWarningMessageBoxAsync ("Invalid Index", "Invalid passthrough index given. Value was " + std::to_string (passthroughIndex)); + return 0; + } - auto idx = getHubIndexFromPassthroughIndex(passthroughIndex); + auto idx = getHubIndexFromPassthroughIndex (passthroughIndex); - if (type == OnixDeviceType::POLLEDBNO && (m_hubName == NEUROPIXELSV2E_HEADSTAGE_NAME || m_hubName == NEUROPIXELSV1E_HEADSTAGE_NAME)) - idx++; + if (type == OnixDeviceType::POLLEDBNO && (m_hubName == NEUROPIXELSV2E_HEADSTAGE_NAME || m_hubName == NEUROPIXELSV1E_HEADSTAGE_NAME)) + idx++; - return idx; + return idx; } OnixDeviceType OnixDevice::getDeviceType() const { - return type; + return type; } std::string OnixDevice::getStreamIdentifier() { - std::string streamIdentifier = "onix"; - - // Insert the headstage or breakout board - if (getHubName() == NEUROPIXELSV1F_HEADSTAGE_NAME) - { - streamIdentifier += ".npx1f"; - } - else if (getHubName() == NEUROPIXELSV2E_HEADSTAGE_NAME) - { - streamIdentifier += ".npx2e"; - } - else if (getHubName() == BREAKOUT_BOARD_NAME) - { - streamIdentifier += ".breakout"; - } - else - { - streamIdentifier += ".headstage"; - } - - // Insert the device - if (getDeviceType() == OnixDeviceType::ANALOGIO) - { - streamIdentifier += ".analogio"; - } - else if (getDeviceType() == OnixDeviceType::BNO || getDeviceType() == OnixDeviceType::POLLEDBNO) - { - streamIdentifier += ".9dof"; - } - else if (getDeviceType() == OnixDeviceType::DIGITALIO) - { - streamIdentifier += ".digitalio"; - } - else if (getDeviceType() == OnixDeviceType::HARPSYNCINPUT) - { - streamIdentifier += ".harp"; - } - else if (getDeviceType() == OnixDeviceType::MEMORYMONITOR) - { - streamIdentifier += ".memory"; - } - else if (getDeviceType() == OnixDeviceType::NEUROPIXELSV1F) - { - streamIdentifier += ".npx1f"; - } - else if (getDeviceType() == OnixDeviceType::NEUROPIXELSV2E) - { - streamIdentifier += ".npx2e"; - } - else - { - streamIdentifier += ".device"; - } - - return streamIdentifier; + std::string streamIdentifier = "onix"; + + // Insert the headstage or breakout board + if (getHubName() == NEUROPIXELSV1F_HEADSTAGE_NAME) + { + streamIdentifier += ".npx1f"; + } + else if (getHubName() == NEUROPIXELSV2E_HEADSTAGE_NAME) + { + streamIdentifier += ".npx2e"; + } + else if (getHubName() == BREAKOUT_BOARD_NAME) + { + streamIdentifier += ".breakout"; + } + else + { + streamIdentifier += ".headstage"; + } + + // Insert the device + if (getDeviceType() == OnixDeviceType::ANALOGIO) + { + streamIdentifier += ".analogio"; + } + else if (getDeviceType() == OnixDeviceType::BNO || getDeviceType() == OnixDeviceType::POLLEDBNO) + { + streamIdentifier += ".9dof"; + } + else if (getDeviceType() == OnixDeviceType::DIGITALIO) + { + streamIdentifier += ".digitalio"; + } + else if (getDeviceType() == OnixDeviceType::HARPSYNCINPUT) + { + streamIdentifier += ".harp"; + } + else if (getDeviceType() == OnixDeviceType::MEMORYMONITOR) + { + streamIdentifier += ".memory"; + } + else if (getDeviceType() == OnixDeviceType::NEUROPIXELSV1F) + { + streamIdentifier += ".npx1f"; + } + else if (getDeviceType() == OnixDeviceType::NEUROPIXELSV2E) + { + streamIdentifier += ".npx2e"; + } + else + { + streamIdentifier += ".device"; + } + + return streamIdentifier; } -int OnixDevice::getPortOffset(PortName port) +int OnixDevice::getPortOffset (PortName port) { - return (uint32_t)port << 8; + return (uint32_t) port << 8; } -std::string OnixDevice::getPortName(oni_dev_idx_t index) +std::string OnixDevice::getPortName (oni_dev_idx_t index) { - switch (getOffset(index)) - { - case 0: - return ""; - case HubAddressPortA: - return "Port A"; - case HubAddressPortB: - return "Port B"; - default: - return ""; - } + switch (getOffset (index)) + { + case 0: + return ""; + case HubAddressPortA: + return "Port A"; + case HubAddressPortB: + return "Port B"; + default: + return ""; + } } -std::string OnixDevice::getPortName(PortName port) +std::string OnixDevice::getPortName (PortName port) { - return getPortName(getPortOffset(port)); + return getPortName (getPortOffset (port)); } -PortName OnixDevice::getPortFromIndex(oni_dev_idx_t index) +PortName OnixDevice::getPortFromIndex (oni_dev_idx_t index) { - return index & (1 << 8) ? PortName::PortA : PortName::PortB; + return index & (1 << 8) ? PortName::PortA : PortName::PortB; } -int OnixDevice::getOffset(oni_dev_idx_t index) +int OnixDevice::getOffset (oni_dev_idx_t index) { - return index & 0x0000FF00; + return index & 0x0000FF00; } -std::vector OnixDevice::getUniqueOffsets(std::vector indices, bool ignoreBreakoutBoard) +std::vector OnixDevice::getUniqueOffsets (std::vector indices, bool ignoreBreakoutBoard) { - std::set offsets; + std::set offsets; - for (auto index : indices) - { - auto offset = getOffset(index); + for (auto index : indices) + { + auto offset = getOffset (index); - if (offset == HubAddressBreakoutBoard && ignoreBreakoutBoard) continue; + if (offset == HubAddressBreakoutBoard && ignoreBreakoutBoard) + continue; - offsets.emplace(offset); - } + offsets.emplace (offset); + } - return std::vector(offsets.begin(), offsets.end()); + return std::vector (offsets.begin(), offsets.end()); } -std::vector OnixDevice::getUniqueOffsets(OnixDeviceMap devices, bool ignoreBreakoutBoard) +std::vector OnixDevice::getUniqueOffsets (OnixDeviceMap devices, bool ignoreBreakoutBoard) { - std::vector indices; + std::vector indices; - for (const auto& [key, _] : devices) - { - indices.emplace_back(key); - } + for (const auto& [key, _] : devices) + { + indices.emplace_back (key); + } - return getUniqueOffsets(indices, ignoreBreakoutBoard); + return getUniqueOffsets (indices, ignoreBreakoutBoard); } -Array OnixDevice::getUniquePorts(std::vector indices) +Array OnixDevice::getUniquePorts (std::vector indices) { - Array ports; + Array ports; - for (auto index : indices) - { - ports.addIfNotAlreadyThere(getPortFromIndex(index)); - } + for (auto index : indices) + { + ports.addIfNotAlreadyThere (getPortFromIndex (index)); + } - return ports; + return ports; } -bool OnixDevice::compareIndex(uint32_t index) +bool OnixDevice::compareIndex (uint32_t index) { - return index == deviceIdx; + return index == deviceIdx; } -void OnixDevice::addFrame(oni_frame_t* frame) +void OnixDevice::addFrame (oni_frame_t* frame) { - frameQueue.enqueue(frame); + frameQueue.enqueue (frame); } void OnixDevice::stopAcquisition() { - oni_frame_t* frame; - while (frameQueue.try_dequeue(frame)) - { - oni_destroy_frame(frame); - } + oni_frame_t* frame; + while (frameQueue.try_dequeue (frame)) + { + oni_destroy_frame (frame); + } } diff --git a/Source/OnixDevice.h b/Source/OnixDevice.h index fe52a32..1ff9dcf 100644 --- a/Source/OnixDevice.h +++ b/Source/OnixDevice.h @@ -1,30 +1,30 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #pragma once +#include #include #include -#include #include #include "Onix1.h" @@ -35,206 +35,215 @@ using namespace moodycamel; namespace OnixSourcePlugin { - enum class PortName - { - PortA = 1, - PortB = 2 - }; - - enum class OnixDeviceType { - BNO = 0, - POLLEDBNO, - NEUROPIXELSV1E, - NEUROPIXELSV1F, - NEUROPIXELSV2E, - PORT_CONTROL, - MEMORYMONITOR, - OUTPUTCLOCK, - HARPSYNCINPUT, - ANALOGIO, - DIGITALIO, - }; - - static const std::string ProbeString = "Probe"; - - struct StreamInfo { - public: - StreamInfo() {} - - StreamInfo(std::string name, std::string description, std::string streamIdentifier, int numChannels, float sampleRate, std::string channelPrefix, ContinuousChannel::Type channelType, - float bitVolts, std::string units, std::vector channelNameSuffixes, std::string channelIdentifierDataType, std::vector channelIdentifierSubTypes = {}) - { - m_name = name; - m_description = description; - m_streamIdentifier = streamIdentifier; - m_numChannels = numChannels; - m_sampleRate = sampleRate; - m_channelPrefix = channelPrefix; - m_channelType = channelType; - m_bitVolts = bitVolts; - m_units = units; - m_channelNameSuffixes = channelNameSuffixes; - m_channelIdentifierDataType = channelIdentifierDataType; - m_channelIdentifierSubTypes = channelIdentifierSubTypes; - - if (m_numChannels != m_channelNameSuffixes.size()) - { - if (m_channelNameSuffixes.size() != 0) - LOGE("Difference between number of channels and channel name suffixes. Generating default suffixes instead."); - - m_channelNameSuffixes.clear(); - m_channelNameSuffixes.reserve(numChannels); - - for (int i = 0; i < m_numChannels; i++) - { - m_channelNameSuffixes.push_back(std::to_string(i + 1)); - } - } - - if (m_channelIdentifierSubTypes.size() > 0 && m_numChannels != m_channelIdentifierSubTypes.size()) - { - if (m_channelIdentifierSubTypes.size() == 1) - { - m_channelIdentifierSubTypes.reserve(m_numChannels); - - for (int i = 1; i < m_numChannels; i++) - { - m_channelIdentifierSubTypes.push_back(m_channelIdentifierSubTypes[0]); - } - } - else - { - LOGE("Difference between number of channels and channel identifier subtypes. Generating default subtypes instead."); - - m_channelIdentifierSubTypes.clear(); - m_channelIdentifierSubTypes.reserve(numChannels); - - for (int i = 0; i < m_numChannels; i++) - { - m_channelIdentifierSubTypes.push_back(std::to_string(i + 1)); - } - } - } - }; - - std::string getName() const { return m_name; } - std::string getDescription() const { return m_description; } - std::string getStreamIdentifier() const { return m_streamIdentifier; } - int getNumChannels() const { return m_numChannels; } - float getSampleRate() const { return m_sampleRate; } - std::string getChannelPrefix() const { return m_channelPrefix; } - ContinuousChannel::Type getChannelType() const { return m_channelType; } - float getBitVolts() const { return m_bitVolts; } - std::string getUnits() const { return m_units; } - std::vector getChannelNameSuffixes() const { return m_channelNameSuffixes; } - std::string getChannelIdentifierDataType() const { return m_channelIdentifierDataType; } - std::vector getChannelIdentifierSubTypes() const { return m_channelIdentifierSubTypes; } - - private: - std::string m_name = "name"; - std::string m_description = "description"; - std::string m_streamIdentifier = "identifier"; - int m_numChannels = 0; - float m_sampleRate = 0; - std::string m_channelPrefix = "channelPrefix"; - ContinuousChannel::Type m_channelType = ContinuousChannel::Type::INVALID; - float m_bitVolts = 1.0f; - std::string m_units = "units"; - std::vector m_channelNameSuffixes = { "suffixes" }; - std::string m_channelIdentifierDataType = "datatype"; - std::vector m_channelIdentifierSubTypes = { "subtypes" }; - }; - - using OnixDeviceMap = std::map; - - /** - - Streams data from an ONIX device - - */ - class OnixDevice - { - public: - - /** Constructor */ - OnixDevice(std::string name_, std::string hubName, OnixDeviceType type_, const oni_dev_idx_t, std::shared_ptr oni_ctx, bool passthrough = false); - - virtual void addFrame(oni_frame_t*); - virtual void processFrames() = 0; - virtual int configureDevice() = 0; - virtual bool updateSettings() = 0; - virtual void startAcquisition() {}; - virtual void stopAcquisition(); - virtual void addSourceBuffers(OwnedArray& sourceBuffers) = 0; - virtual bool compareIndex(uint32_t index); - - const std::string getName() { return name; } - virtual bool isEnabled() const { return enabled; } - virtual void setEnabled(bool newState) { enabled = newState; } - oni_dev_idx_t getDeviceIdx(bool getPassthroughIndex = false); - - /** Creates a stream name using the provided inputs, returning a string following the pattern: name[0]-name[1]-name[2]-etc., with all spaces removed */ - static std::string createStreamName(std::vector names); - - Array streamInfos; - - const int bufferSizeInSeconds = 10; - - static constexpr int HubAddressBreakoutBoard = 0; - static constexpr int HubAddressPortA = 256; - static constexpr int HubAddressPortB = 512; - - std::string getHubName() { return m_hubName; } - void setHubName(std::string hubName) { m_hubName = hubName; } - - static int getPortOffset(PortName port); - static std::string getPortName(PortName port); - static std::string getPortName(oni_dev_idx_t index); - static PortName getPortFromIndex(oni_dev_idx_t index); - static int getOffset(oni_dev_idx_t index); - static std::vector getUniqueOffsets(std::vector indices, bool ignoreBreakoutBoard = true); - static std::vector getUniqueOffsets(OnixDeviceMap devices, bool ignoreBreakoutBoard = true); - static Array getUniquePorts(std::vector indices); - - OnixDeviceType getDeviceType() const; - - /** Returns a string for this device that follows the pattern: onix.[hub].[device] */ - std::string getStreamIdentifier(); - - static oni_dev_idx_t getHubIndexFromPassthroughIndex(oni_dev_idx_t passthroughIndex); - static oni_dev_idx_t getPassthroughIndexFromHubIndex(oni_dev_idx_t hubIndex); - - static const std::map TypeString; - - protected: - - oni_dev_idx_t getDeviceIndexFromPassthroughIndex(oni_dev_idx_t passthroughIndex) const; - - ReaderWriterQueue frameQueue; - const oni_dev_idx_t deviceIdx; - std::shared_ptr deviceContext; - - private: +enum class PortName +{ + PortA = 1, + PortB = 2 +}; + +enum class OnixDeviceType +{ + BNO = 0, + POLLEDBNO, + NEUROPIXELSV1E, + NEUROPIXELSV1F, + NEUROPIXELSV2E, + PORT_CONTROL, + MEMORYMONITOR, + OUTPUTCLOCK, + HARPSYNCINPUT, + ANALOGIO, + DIGITALIO, +}; + +static const std::string ProbeString = "Probe"; + +struct StreamInfo +{ +public: + StreamInfo() {} + + StreamInfo (std::string name, + std::string description, + std::string streamIdentifier, + int numChannels, + float sampleRate, + std::string channelPrefix, + ContinuousChannel::Type channelType, + float bitVolts, + std::string units, + std::vector channelNameSuffixes, + std::string channelIdentifierDataType, + std::vector channelIdentifierSubTypes = {}) + { + m_name = name; + m_description = description; + m_streamIdentifier = streamIdentifier; + m_numChannels = numChannels; + m_sampleRate = sampleRate; + m_channelPrefix = channelPrefix; + m_channelType = channelType; + m_bitVolts = bitVolts; + m_units = units; + m_channelNameSuffixes = channelNameSuffixes; + m_channelIdentifierDataType = channelIdentifierDataType; + m_channelIdentifierSubTypes = channelIdentifierSubTypes; + + if (m_numChannels != m_channelNameSuffixes.size()) + { + if (m_channelNameSuffixes.size() != 0) + LOGE ("Difference between number of channels and channel name suffixes. Generating default suffixes instead."); + + m_channelNameSuffixes.clear(); + m_channelNameSuffixes.reserve (numChannels); + + for (int i = 0; i < m_numChannels; i++) + { + m_channelNameSuffixes.push_back (std::to_string (i + 1)); + } + } + + if (m_channelIdentifierSubTypes.size() > 0 && m_numChannels != m_channelIdentifierSubTypes.size()) + { + if (m_channelIdentifierSubTypes.size() == 1) + { + m_channelIdentifierSubTypes.reserve (m_numChannels); + + for (int i = 1; i < m_numChannels; i++) + { + m_channelIdentifierSubTypes.push_back (m_channelIdentifierSubTypes[0]); + } + } + else + { + LOGE ("Difference between number of channels and channel identifier subtypes. Generating default subtypes instead."); + + m_channelIdentifierSubTypes.clear(); + m_channelIdentifierSubTypes.reserve (numChannels); + + for (int i = 0; i < m_numChannels; i++) + { + m_channelIdentifierSubTypes.push_back (std::to_string (i + 1)); + } + } + } + }; + + std::string getName() const { return m_name; } + std::string getDescription() const { return m_description; } + std::string getStreamIdentifier() const { return m_streamIdentifier; } + int getNumChannels() const { return m_numChannels; } + float getSampleRate() const { return m_sampleRate; } + std::string getChannelPrefix() const { return m_channelPrefix; } + ContinuousChannel::Type getChannelType() const { return m_channelType; } + float getBitVolts() const { return m_bitVolts; } + std::string getUnits() const { return m_units; } + std::vector getChannelNameSuffixes() const { return m_channelNameSuffixes; } + std::string getChannelIdentifierDataType() const { return m_channelIdentifierDataType; } + std::vector getChannelIdentifierSubTypes() const { return m_channelIdentifierSubTypes; } + +private: + std::string m_name = "name"; + std::string m_description = "description"; + std::string m_streamIdentifier = "identifier"; + int m_numChannels = 0; + float m_sampleRate = 0; + std::string m_channelPrefix = "channelPrefix"; + ContinuousChannel::Type m_channelType = ContinuousChannel::Type::INVALID; + float m_bitVolts = 1.0f; + std::string m_units = "units"; + std::vector m_channelNameSuffixes = { "suffixes" }; + std::string m_channelIdentifierDataType = "datatype"; + std::vector m_channelIdentifierSubTypes = { "subtypes" }; +}; + +using OnixDeviceMap = std::map; + +/** + + Streams data from an ONIX device + +*/ +class OnixDevice +{ +public: + /** Constructor */ + OnixDevice (std::string name_, std::string hubName, OnixDeviceType type_, const oni_dev_idx_t, std::shared_ptr oni_ctx, bool passthrough = false); + + virtual void addFrame (oni_frame_t*); + virtual void processFrames() = 0; + virtual int configureDevice() = 0; + virtual bool updateSettings() = 0; + virtual void startAcquisition() {}; + virtual void stopAcquisition(); + virtual void addSourceBuffers (OwnedArray& sourceBuffers) = 0; + virtual bool compareIndex (uint32_t index); + + const std::string getName() { return name; } + virtual bool isEnabled() const { return enabled; } + virtual void setEnabled (bool newState) { enabled = newState; } + oni_dev_idx_t getDeviceIdx (bool getPassthroughIndex = false); + + /** Creates a stream name using the provided inputs, returning a string following the pattern: name[0]-name[1]-name[2]-etc., with all spaces removed */ + static std::string createStreamName (std::vector names); + + Array streamInfos; + + const int bufferSizeInSeconds = 10; + + static constexpr int HubAddressBreakoutBoard = 0; + static constexpr int HubAddressPortA = 256; + static constexpr int HubAddressPortB = 512; + + std::string getHubName() { return m_hubName; } + void setHubName (std::string hubName) { m_hubName = hubName; } + + static int getPortOffset (PortName port); + static std::string getPortName (PortName port); + static std::string getPortName (oni_dev_idx_t index); + static PortName getPortFromIndex (oni_dev_idx_t index); + static int getOffset (oni_dev_idx_t index); + static std::vector getUniqueOffsets (std::vector indices, bool ignoreBreakoutBoard = true); + static std::vector getUniqueOffsets (OnixDeviceMap devices, bool ignoreBreakoutBoard = true); + static Array getUniquePorts (std::vector indices); + + OnixDeviceType getDeviceType() const; + + /** Returns a string for this device that follows the pattern: onix.[hub].[device] */ + std::string getStreamIdentifier(); + + static oni_dev_idx_t getHubIndexFromPassthroughIndex (oni_dev_idx_t passthroughIndex); + static oni_dev_idx_t getPassthroughIndexFromHubIndex (oni_dev_idx_t hubIndex); + + static const std::map TypeString; + +protected: + oni_dev_idx_t getDeviceIndexFromPassthroughIndex (oni_dev_idx_t passthroughIndex) const; + + ReaderWriterQueue frameQueue; + const oni_dev_idx_t deviceIdx; + std::shared_ptr deviceContext; + +private: + std::string name; + + bool enabled = true; + bool isPassthrough = false; - std::string name; - - bool enabled = true; - bool isPassthrough = false; + std::string m_hubName; - std::string m_hubName; + const OnixDeviceType type; - const OnixDeviceType type; + enum class PassthroughIndex : uint32_t + { + A = 8, + B = 9 + }; - enum class PassthroughIndex : uint32_t - { - A = 8, - B = 9 - }; + static bool isValidPassthroughIndex (oni_dev_idx_t); - static bool isValidPassthroughIndex(oni_dev_idx_t); + JUCE_LEAK_DETECTOR (OnixDevice); +}; - JUCE_LEAK_DETECTOR(OnixDevice); - }; - - using OnixDeviceVector = std::vector>; -} \ No newline at end of file +using OnixDeviceVector = std::vector>; +} // namespace OnixSourcePlugin \ No newline at end of file diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index 58d8bbd..76946ba 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -1,1206 +1,1218 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #include "OnixSource.h" -#include "OnixSourceCanvas.h" + #include "Devices/DeviceList.h" +#include "OnixSourceCanvas.h" using namespace OnixSourcePlugin; -OnixSource::OnixSource(SourceNode* sn) : - DataThread(sn), - devicesFound(false), - editor(NULL) +OnixSource::OnixSource (SourceNode* sn) + : DataThread (sn), + devicesFound (false), + editor (NULL) { - try - { - context = std::make_shared(); - } - catch (const std::system_error& e) - { - Onix1::showWarningMessageBoxAsync( - "Failed to Create Context", - e.what() - ); - } - catch (const error_t& e) - { - Onix1::showWarningMessageBoxAsync( - "Failed to Initialize Context", - e.what() - ); - } - - portA = std::make_shared(PortName::PortA, context); - portB = std::make_shared(PortName::PortB, context); - - if (context == nullptr || !context->isInitialized()) { LOGE("Failed to initialize context."); return; } + try + { + context = std::make_shared(); + } + catch (const std::system_error& e) + { + Onix1::showWarningMessageBoxAsync ( + "Failed to Create Context", + e.what()); + } + catch (const error_t& e) + { + Onix1::showWarningMessageBoxAsync ( + "Failed to Initialize Context", + e.what()); + } + + portA = std::make_shared (PortName::PortA, context); + portB = std::make_shared (PortName::PortB, context); + + if (context == nullptr || ! context->isInitialized()) + { + LOGE ("Failed to initialize context."); + return; + } } OnixSource::~OnixSource() { - if (context != nullptr && context->isInitialized()) - { - portA->setVoltageOverride(0.0f, false); - portB->setVoltageOverride(0.0f, false); - } + if (context != nullptr && context->isInitialized()) + { + portA->setVoltageOverride (0.0f, false); + portB->setVoltageOverride (0.0f, false); + } } void OnixSource::registerParameters() { - addBooleanParameter(Parameter::PROCESSOR_SCOPE, "passthroughA", "Passthrough A", "Enables passthrough mode for e-variant headstages on Port A", false, true); - addBooleanParameter(Parameter::PROCESSOR_SCOPE, "passthroughB", "Passthrough B", "Enables passthrough mode for e-variant headstages on Port B", false, true); + addBooleanParameter (Parameter::PROCESSOR_SCOPE, "passthroughA", "Passthrough A", "Enables passthrough mode for e-variant headstages on Port A", false, true); + addBooleanParameter (Parameter::PROCESSOR_SCOPE, "passthroughB", "Passthrough B", "Enables passthrough mode for e-variant headstages on Port B", false, true); } -DataThread* OnixSource::createDataThread(SourceNode* sn) +DataThread* OnixSource::createDataThread (SourceNode* sn) { - return new OnixSource(sn); + return new OnixSource (sn); } -std::unique_ptr OnixSource::createEditor(SourceNode* sn) +std::unique_ptr OnixSource::createEditor (SourceNode* sn) { - std::unique_ptr e = std::make_unique(sn, this); - editor = e.get(); + std::unique_ptr e = std::make_unique (sn, this); + editor = e.get(); - return e; + return e; } -bool OnixSource::disconnectDevices(bool updateStreamInfo) +bool OnixSource::disconnectDevices (bool updateStreamInfo) { - sourceBuffers.clear(true); + sourceBuffers.clear (true); - sources.clear(); - hubNames.clear(); + sources.clear(); + hubNames.clear(); - devicesFound = false; + devicesFound = false; - if (context != nullptr && context->isInitialized()) - { - if (context->setOption(ONIX_OPT_PASSTHROUGH, 0) != ONI_ESUCCESS) - { - LOGE("Unable to set passthrough option when disconnecting devices."); - return false; - } - } + if (context != nullptr && context->isInitialized()) + { + if (context->setOption (ONIX_OPT_PASSTHROUGH, 0) != ONI_ESUCCESS) + { + LOGE ("Unable to set passthrough option when disconnecting devices."); + return false; + } + } - if (updateStreamInfo) CoreServices::updateSignalChain(editor); + if (updateStreamInfo) + CoreServices::updateSignalChain (editor); - return true; + return true; } template -bool OnixSource::configureDevice(OnixDeviceVector& sources, - OnixSourceEditor* editor, - std::string deviceName, - std::string hubName, - OnixDeviceType deviceType, - const oni_dev_idx_t deviceIdx, - std::shared_ptr ctx) +bool OnixSource::configureDevice (OnixDeviceVector& sources, + OnixSourceEditor* editor, + std::string deviceName, + std::string hubName, + OnixDeviceType deviceType, + const oni_dev_idx_t deviceIdx, + std::shared_ptr ctx) { - auto canvas = editor->getCanvas(); - std::shared_ptr device = std::static_pointer_cast(canvas->getDevicePtr(Device::getDeviceType(), deviceIdx)); - - if (device != nullptr) - { - if (device->getName() != deviceName || device->getHubName() != hubName) - { - LOGD("Difference in names found for device at address ", deviceIdx, ". Found ", deviceName, " on ", hubName, ", but was expecting ", device->getName(), " on ", device->getHubName()); - } - } - else if (device->getDeviceType() == OnixDeviceType::MEMORYMONITOR) - {// NB: These are devices with no equivalent settings tab that still need to be created and added to the vector of devices - LOGD("Creating new device ", deviceName, " on ", hubName); - device = std::make_shared(deviceName, hubName, deviceIdx, ctx); - } - - if (device == nullptr) - { - if (hubName == editor->getHeadstageSelected(OnixDevice::getOffset(deviceIdx))) - { - Onix1::showWarningMessageBoxAsync( - "Device Not Found", - "Could not find " + deviceName + ", at address " + std::to_string(deviceIdx) + " on " + hubName); - } - else - { - Onix1::showWarningMessageBoxAsync( - "Invalid Headstage Selection", - "Expected to find " + editor->getHeadstageSelected(OnixDevice::getOffset(deviceIdx)) + " on " + OnixDevice::getPortName(deviceIdx) + - ", but found " + hubName + " instead. Confirm that the correct headstage is selected, and try to connect again."); - } - - return false; - } - - int res = 1; - - try - { - res = device->configureDevice(); - } - catch (const error_str& e) - { - Onix1::showWarningMessageBoxAsync("Configuration Error", e.what()); - - return false; - } - - sources.emplace_back(device); - - return res == ONI_ESUCCESS; + auto canvas = editor->getCanvas(); + std::shared_ptr device = std::static_pointer_cast (canvas->getDevicePtr (Device::getDeviceType(), deviceIdx)); + + if (device != nullptr) + { + if (device->getName() != deviceName || device->getHubName() != hubName) + { + LOGD ("Difference in names found for device at address ", deviceIdx, ". Found ", deviceName, " on ", hubName, ", but was expecting ", device->getName(), " on ", device->getHubName()); + } + } + else if (device->getDeviceType() == OnixDeviceType::MEMORYMONITOR) + { // NB: These are devices with no equivalent settings tab that still need to be created and added to the vector of devices + LOGD ("Creating new device ", deviceName, " on ", hubName); + device = std::make_shared (deviceName, hubName, deviceIdx, ctx); + } + + if (device == nullptr) + { + if (hubName == editor->getHeadstageSelected (OnixDevice::getOffset (deviceIdx))) + { + Onix1::showWarningMessageBoxAsync ( + "Device Not Found", + "Could not find " + deviceName + ", at address " + std::to_string (deviceIdx) + " on " + hubName); + } + else + { + Onix1::showWarningMessageBoxAsync ( + "Invalid Headstage Selection", + "Expected to find " + editor->getHeadstageSelected (OnixDevice::getOffset (deviceIdx)) + " on " + OnixDevice::getPortName (deviceIdx) + ", but found " + hubName + " instead. Confirm that the correct headstage is selected, and try to connect again."); + } + + return false; + } + + int res = 1; + + try + { + res = device->configureDevice(); + } + catch (const error_str& e) + { + Onix1::showWarningMessageBoxAsync ("Configuration Error", e.what()); + + return false; + } + + sources.emplace_back (device); + + return res == ONI_ESUCCESS; } -bool OnixSource::getHubFirmwareVersion(std::shared_ptr ctx, uint32_t hubIndex, uint32_t* firmwareVersion) +bool OnixSource::getHubFirmwareVersion (std::shared_ptr ctx, uint32_t hubIndex, uint32_t* firmwareVersion) { - if (ctx->readRegister(hubIndex + ONIX_HUB_DEV_IDX, ONIX_HUB_FIRMWAREVER, firmwareVersion) != ONI_ESUCCESS) - { - LOGE("Unable to read the hub firmware version at index ", hubIndex); - return false; - } + if (ctx->readRegister (hubIndex + ONIX_HUB_DEV_IDX, ONIX_HUB_FIRMWAREVER, firmwareVersion) != ONI_ESUCCESS) + { + LOGE ("Unable to read the hub firmware version at index ", hubIndex); + return false; + } - return true; + return true; } -bool OnixSource::enablePassthroughMode(std::shared_ptr ctx, bool passthroughA, bool passthroughB) +bool OnixSource::enablePassthroughMode (std::shared_ptr ctx, bool passthroughA, bool passthroughB) { - if (ctx == nullptr || !ctx->isInitialized()) - { - Onix1::showWarningMessageBoxAsync("Invalid Context", "Cannot enable passthrough mode, context is not initialized correctly."); - return false; - } - - int val = 0; - - if (passthroughA) - { - LOGD("Passthrough mode enabled for Port A"); - val |= 1 << 0; - } - - if (passthroughB) - { - LOGD("Passthrough mode enabled for Port B"); - val |= 1 << 2; - } - - return ctx->setOption(ONIX_OPT_PASSTHROUGH, val) == ONI_ESUCCESS; + if (ctx == nullptr || ! ctx->isInitialized()) + { + Onix1::showWarningMessageBoxAsync ("Invalid Context", "Cannot enable passthrough mode, context is not initialized correctly."); + return false; + } + + int val = 0; + + if (passthroughA) + { + LOGD ("Passthrough mode enabled for Port A"); + val |= 1 << 0; + } + + if (passthroughB) + { + LOGD ("Passthrough mode enabled for Port B"); + val |= 1 << 2; + } + + return ctx->setOption (ONIX_OPT_PASSTHROUGH, val) == ONI_ESUCCESS; } -bool OnixSource::configurePort(PortName port) +bool OnixSource::configurePort (PortName port) { - if (context == nullptr || !context->isInitialized()) - { - Onix1::showWarningMessageBoxAsync("Invalid Context", "Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); - return false; - } - - if (port == PortName::PortA) - { - if (portA->configureDevice() != ONI_ESUCCESS) - { - Onix1::showWarningMessageBoxAsync("Configuration Error", "Unable to configure Port A."); - return false; - } - } - else if (port == PortName::PortB) - { - if (portB->configureDevice() != ONI_ESUCCESS) - { - Onix1::showWarningMessageBoxAsync("Configuration Error", "Unable to configure Port B."); - return false; - } - } - - return true; + if (context == nullptr || ! context->isInitialized()) + { + Onix1::showWarningMessageBoxAsync ("Invalid Context", "Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); + return false; + } + + if (port == PortName::PortA) + { + if (portA->configureDevice() != ONI_ESUCCESS) + { + Onix1::showWarningMessageBoxAsync ("Configuration Error", "Unable to configure Port A."); + return false; + } + } + else if (port == PortName::PortB) + { + if (portB->configureDevice() != ONI_ESUCCESS) + { + Onix1::showWarningMessageBoxAsync ("Configuration Error", "Unable to configure Port B."); + return false; + } + } + + return true; } bool OnixSource::resetPortLinkFlags() { - if (context == nullptr || !context->isInitialized()) - return false; + if (context == nullptr || ! context->isInitialized()) + return false; - return portA->resetLinkFlags() != ONI_ESUCCESS || portB->resetLinkFlags() != ONI_ESUCCESS; + return portA->resetLinkFlags() != ONI_ESUCCESS || portB->resetLinkFlags() != ONI_ESUCCESS; } -bool OnixSource::resetPortLinkFlags(PortName port) +bool OnixSource::resetPortLinkFlags (PortName port) { - if (context == nullptr || !context->isInitialized()) - return false; - - if (port == PortName::PortA) - { - if (portA->resetLinkFlags() != ONI_ESUCCESS) - return false; - } - else if (port == PortName::PortB) - { - if (portB->resetLinkFlags() != ONI_ESUCCESS) - return false; - } - - return true; + if (context == nullptr || ! context->isInitialized()) + return false; + + if (port == PortName::PortA) + { + if (portA->resetLinkFlags() != ONI_ESUCCESS) + return false; + } + else if (port == PortName::PortB) + { + if (portB->resetLinkFlags() != ONI_ESUCCESS) + return false; + } + + return true; } -bool OnixSource::checkHubFirmwareCompatibility(std::shared_ptr context, device_map_t deviceTable) +bool OnixSource::checkHubFirmwareCompatibility (std::shared_ptr context, device_map_t deviceTable) { - auto hubIds = context->getHubIds(deviceTable); - - if (hubIds.size() == 0) - { - LOGE("No hub IDs found."); - return false; - } - - for (const auto& [hubIndex, hubId] : hubIds) - { - if (hubId == ONIX_HUB_FMCHOST) // NB: Breakout Board - { - static constexpr int RequiredMajorVersion = 2; - uint32_t firmwareVersion = 0; - if (!getHubFirmwareVersion(context, hubIndex, &firmwareVersion)) - { - return false; - } - - auto majorVersion = (firmwareVersion & 0xFF00) >> 8; - auto minorVersion = firmwareVersion & 0xFF; - - LOGC("Breakout board firmware version: v", majorVersion, ".", minorVersion); - - if (majorVersion != RequiredMajorVersion) - { - Onix1::showWarningMessageBoxAsync( - "Invalid Firmware Version", - "The breakout board firmware major version is v" + std::to_string(majorVersion) + - ", but this plugin is only compatible with v" + std::to_string(RequiredMajorVersion) + - ". To use this plugin, upgrade the firmware to a version that supports the breakout board v" + std::to_string(majorVersion)); - return false; - } - } - } - - return true; + auto hubIds = context->getHubIds (deviceTable); + + if (hubIds.size() == 0) + { + LOGE ("No hub IDs found."); + return false; + } + + for (const auto& [hubIndex, hubId] : hubIds) + { + if (hubId == ONIX_HUB_FMCHOST) // NB: Breakout Board + { + static constexpr int RequiredMajorVersion = 2; + uint32_t firmwareVersion = 0; + if (! getHubFirmwareVersion (context, hubIndex, &firmwareVersion)) + { + return false; + } + + auto majorVersion = (firmwareVersion & 0xFF00) >> 8; + auto minorVersion = firmwareVersion & 0xFF; + + LOGC ("Breakout board firmware version: v", majorVersion, ".", minorVersion); + + if (majorVersion != RequiredMajorVersion) + { + Onix1::showWarningMessageBoxAsync ( + "Invalid Firmware Version", + "The breakout board firmware major version is v" + + std::to_string (majorVersion) + ", but this plugin is only compatible with v" + + std::to_string (RequiredMajorVersion) + ". To use this plugin, upgrade the firmware" + + " to a version that supports the breakout board v" + std::to_string (majorVersion)); + return false; + } + } + } + + return true; } -bool OnixSource::initializeDevices(device_map_t deviceTable, bool updateStreamInfo) +bool OnixSource::initializeDevices (device_map_t deviceTable, bool updateStreamInfo) { - if (context == nullptr || !context->isInitialized()) - { - Onix1::showWarningMessageBoxAsync("Invalid Context", "Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); - return false; - } - - if (deviceTable.size() == 0) - { - Onix1::showWarningMessageBoxAsync("No Devices Found", "Could not find any devices from the connected hardware."); - if (updateStreamInfo) CoreServices::updateSignalChain(editor); - return false; - } - - auto hubIds = context->getHubIds(deviceTable); - - if (hubIds.size() == 0) - { - Onix1::showWarningMessageBoxAsync("No Hubs Found", "Could not find any hubs from the connected hardware."); - if (updateStreamInfo) CoreServices::updateSignalChain(editor); - return false; - } - - devicesFound = false; - - // NB: Search through all hubs, and initialize devices - for (const auto& [hubIndex, hubId] : hubIds) - { - if (hubId == ONIX_HUB_FMCHOST) // NB: Breakout Board - { - hubNames.insert({ hubIndex, BREAKOUT_BOARD_NAME }); - - static constexpr int OutputClockOffset = 5, AnalogIOOffset = 6, DigitalIOOffset = 7, MemoryMonitorOffset = 10, HarpSyncInputOffset = 12; - - devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(OutputClock::getDeviceType()), BREAKOUT_BOARD_NAME, OutputClock::getDeviceType(), hubIndex + OutputClockOffset, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - - devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(DigitalIO::getDeviceType()), BREAKOUT_BOARD_NAME, DigitalIO::getDeviceType(), hubIndex + DigitalIOOffset, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - - devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(AnalogIO::getDeviceType()), BREAKOUT_BOARD_NAME, AnalogIO::getDeviceType(), hubIndex + AnalogIOOffset, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - - devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(MemoryMonitor::getDeviceType()), BREAKOUT_BOARD_NAME, MemoryMonitor::getDeviceType(), hubIndex + MemoryMonitorOffset, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - - devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(HarpSyncInput::getDeviceType()), BREAKOUT_BOARD_NAME, HarpSyncInput::getDeviceType(), hubIndex + HarpSyncInputOffset, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - } - else if (hubId == ONIX_HUB_HSNP) - { - hubNames.insert({ hubIndex, NEUROPIXELSV1F_HEADSTAGE_NAME }); - - for (int i = 0; i < 2; i++) - { - devicesFound = configureDevice(sources, editor, ProbeString + std::to_string(i), NEUROPIXELSV1F_HEADSTAGE_NAME, Neuropixels1f::getDeviceType(), hubIndex + i, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - } - - devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(Bno055::getDeviceType()), NEUROPIXELSV1F_HEADSTAGE_NAME, Bno055::getDeviceType(), hubIndex + 2, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - } - else - { - Onix1::showWarningMessageBoxAsync( - "Unkown Hub ID", - "Discovered hub ID " + std::to_string(hubId) + " (" + onix_hub_str(hubId) + ") on " + OnixDevice::getPortName(hubIndex) + " which does not match any currently implemented hubs." - ); - devicesFound = false; - sources.clear(); - return false; - } - } - - // NB: Search for passthrough devices, and initialize any headstages found in passthrough mode - for (const auto& [index, device] : deviceTable) - { - if (device.id == ONIX_DS90UB9RAW) - { - LOGD("Passthrough device detected"); - - auto serializer = std::make_unique(DS90UB9x::SER_ADDR, index, context); - serializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xSerializerI2CRegister::SCLHIGH, 20); - serializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xSerializerI2CRegister::SCLLOW, 20); - - auto EEPROM = std::make_unique(index, context); - uint32_t hsid = EEPROM->GetHeadStageID(); - LOGC("Detected headstage ", onix_hub_str(hsid)); - - if (hsid == ONIX_HUB_HSNP2E) - { - auto hubIndex = OnixDevice::getHubIndexFromPassthroughIndex(index); - - devicesFound = configureDevice(sources, editor, ProbeString, NEUROPIXELSV2E_HEADSTAGE_NAME, Neuropixels2e::getDeviceType(), hubIndex, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - - devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(PolledBno055::getDeviceType()), NEUROPIXELSV2E_HEADSTAGE_NAME, PolledBno055::getDeviceType(), hubIndex + 1, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - - if (sources.back()->getDeviceType() != OnixDeviceType::POLLEDBNO) - { - LOGE("Unknown device encountered when configuring headstage ", NEUROPIXELSV2E_HEADSTAGE_NAME); - devicesFound = false; - sources.clear(); - return false; - } - - const auto& polledBno = std::static_pointer_cast(sources.back()); - - polledBno->setBnoAxisMap(PolledBno055::Bno055AxisMap::YZX); - polledBno->setBnoAxisSign((uint32_t)(PolledBno055::Bno055AxisSign::MirrorX) | (uint32_t)(PolledBno055::Bno055AxisSign::MirrorY)); - - hubNames.insert({ OnixDevice::getOffset(polledBno->getDeviceIdx()), NEUROPIXELSV2E_HEADSTAGE_NAME }); - } - else if (hsid == ONIX_HUB_HSNP1ET || hsid == ONIX_HUB_HSNP1EH) - { - auto hubIndex = OnixDevice::getHubIndexFromPassthroughIndex(index); - - devicesFound = configureDevice(sources, editor, ProbeString, NEUROPIXELSV1E_HEADSTAGE_NAME, Neuropixels1e::getDeviceType(), hubIndex, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - - devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(PolledBno055::getDeviceType()), NEUROPIXELSV1E_HEADSTAGE_NAME, PolledBno055::getDeviceType(), hubIndex + 1, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - - if (sources.back()->getDeviceType() != OnixDeviceType::POLLEDBNO) - { - LOGE("Unknown device encountered when setting headstage."); - devicesFound = false; - sources.clear(); - return false; - } - - const auto& polledBno = std::static_pointer_cast(sources.back()); - - polledBno->setBnoAxisMap(PolledBno055::Bno055AxisMap::YZX); - polledBno->setBnoAxisSign((uint32_t)(PolledBno055::Bno055AxisSign::MirrorX) | (uint32_t)(PolledBno055::Bno055AxisSign::MirrorZ)); - - hubNames.insert({ OnixDevice::getOffset(polledBno->getDeviceIdx()), NEUROPIXELSV1E_HEADSTAGE_NAME }); - } - else - { - Onix1::showWarningMessageBoxAsync( - "Unkown Hub ID", - "Discovered hub ID " + std::to_string(hsid) + " (" + onix_hub_str(hsid) + ") on " + OnixDevice::getPortName(OnixDevice::getHubIndexFromPassthroughIndex(index)) + " which does not match any currently implemented hubs." - ); - devicesFound = false; - sources.clear(); - return false; - } - } - } - - context->issueReset(); - - if (updateStreamInfo) CoreServices::updateSignalChain(editor); - - LOGD("All devices initialized."); - return devicesFound; + if (context == nullptr || ! context->isInitialized()) + { + Onix1::showWarningMessageBoxAsync ("Invalid Context", "Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); + return false; + } + + if (deviceTable.size() == 0) + { + Onix1::showWarningMessageBoxAsync ("No Devices Found", "Could not find any devices from the connected hardware."); + if (updateStreamInfo) + CoreServices::updateSignalChain (editor); + return false; + } + + auto hubIds = context->getHubIds (deviceTable); + + if (hubIds.size() == 0) + { + Onix1::showWarningMessageBoxAsync ("No Hubs Found", "Could not find any hubs from the connected hardware."); + if (updateStreamInfo) + CoreServices::updateSignalChain (editor); + return false; + } + + devicesFound = false; + + // NB: Search through all hubs, and initialize devices + for (const auto& [hubIndex, hubId] : hubIds) + { + if (hubId == ONIX_HUB_FMCHOST) // NB: Breakout Board + { + hubNames.insert ({ hubIndex, BREAKOUT_BOARD_NAME }); + + static constexpr int OutputClockOffset = 5, AnalogIOOffset = 6, DigitalIOOffset = 7, MemoryMonitorOffset = 10, HarpSyncInputOffset = 12; + + devicesFound = configureDevice (sources, editor, OnixDevice::TypeString.at (OutputClock::getDeviceType()), BREAKOUT_BOARD_NAME, OutputClock::getDeviceType(), hubIndex + OutputClockOffset, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + + devicesFound = configureDevice (sources, editor, OnixDevice::TypeString.at (DigitalIO::getDeviceType()), BREAKOUT_BOARD_NAME, DigitalIO::getDeviceType(), hubIndex + DigitalIOOffset, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + + devicesFound = configureDevice (sources, editor, OnixDevice::TypeString.at (AnalogIO::getDeviceType()), BREAKOUT_BOARD_NAME, AnalogIO::getDeviceType(), hubIndex + AnalogIOOffset, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + + devicesFound = configureDevice (sources, editor, OnixDevice::TypeString.at (MemoryMonitor::getDeviceType()), BREAKOUT_BOARD_NAME, MemoryMonitor::getDeviceType(), hubIndex + MemoryMonitorOffset, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + + devicesFound = configureDevice (sources, editor, OnixDevice::TypeString.at (HarpSyncInput::getDeviceType()), BREAKOUT_BOARD_NAME, HarpSyncInput::getDeviceType(), hubIndex + HarpSyncInputOffset, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + } + else if (hubId == ONIX_HUB_HSNP) + { + hubNames.insert ({ hubIndex, NEUROPIXELSV1F_HEADSTAGE_NAME }); + + for (int i = 0; i < 2; i++) + { + devicesFound = configureDevice (sources, editor, ProbeString + std::to_string (i), NEUROPIXELSV1F_HEADSTAGE_NAME, Neuropixels1f::getDeviceType(), hubIndex + i, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + } + + devicesFound = configureDevice (sources, editor, OnixDevice::TypeString.at (Bno055::getDeviceType()), NEUROPIXELSV1F_HEADSTAGE_NAME, Bno055::getDeviceType(), hubIndex + 2, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + } + else + { + Onix1::showWarningMessageBoxAsync ( + "Unkown Hub ID", + "Discovered hub ID " + std::to_string (hubId) + " (" + onix_hub_str (hubId) + ") on " + OnixDevice::getPortName (hubIndex) + " which does not match any currently implemented hubs."); + devicesFound = false; + sources.clear(); + return false; + } + } + + // NB: Search for passthrough devices, and initialize any headstages found in passthrough mode + for (const auto& [index, device] : deviceTable) + { + if (device.id == ONIX_DS90UB9RAW) + { + LOGD ("Passthrough device detected"); + + auto serializer = std::make_unique (DS90UB9x::SER_ADDR, index, context); + serializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xSerializerI2CRegister::SCLHIGH, 20); + serializer->WriteByte ((uint32_t) DS90UB9x::DS90UB9xSerializerI2CRegister::SCLLOW, 20); + + auto EEPROM = std::make_unique (index, context); + uint32_t hsid = EEPROM->GetHeadStageID(); + LOGC ("Detected headstage ", onix_hub_str (hsid)); + + if (hsid == ONIX_HUB_HSNP2E) + { + auto hubIndex = OnixDevice::getHubIndexFromPassthroughIndex (index); + + devicesFound = configureDevice (sources, editor, ProbeString, NEUROPIXELSV2E_HEADSTAGE_NAME, Neuropixels2e::getDeviceType(), hubIndex, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + + devicesFound = configureDevice (sources, editor, OnixDevice::TypeString.at (PolledBno055::getDeviceType()), NEUROPIXELSV2E_HEADSTAGE_NAME, PolledBno055::getDeviceType(), hubIndex + 1, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + + if (sources.back()->getDeviceType() != OnixDeviceType::POLLEDBNO) + { + LOGE ("Unknown device encountered when configuring headstage ", NEUROPIXELSV2E_HEADSTAGE_NAME); + devicesFound = false; + sources.clear(); + return false; + } + + const auto& polledBno = std::static_pointer_cast (sources.back()); + + polledBno->setBnoAxisMap (PolledBno055::Bno055AxisMap::YZX); + polledBno->setBnoAxisSign ((uint32_t) (PolledBno055::Bno055AxisSign::MirrorX) | (uint32_t) (PolledBno055::Bno055AxisSign::MirrorY)); + + hubNames.insert ({ OnixDevice::getOffset (polledBno->getDeviceIdx()), NEUROPIXELSV2E_HEADSTAGE_NAME }); + } + else if (hsid == ONIX_HUB_HSNP1ET || hsid == ONIX_HUB_HSNP1EH) + { + auto hubIndex = OnixDevice::getHubIndexFromPassthroughIndex (index); + + devicesFound = configureDevice (sources, editor, ProbeString, NEUROPIXELSV1E_HEADSTAGE_NAME, Neuropixels1e::getDeviceType(), hubIndex, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + + devicesFound = configureDevice (sources, editor, OnixDevice::TypeString.at (PolledBno055::getDeviceType()), NEUROPIXELSV1E_HEADSTAGE_NAME, PolledBno055::getDeviceType(), hubIndex + 1, context); + if (! devicesFound) + { + sources.clear(); + return false; + } + + if (sources.back()->getDeviceType() != OnixDeviceType::POLLEDBNO) + { + LOGE ("Unknown device encountered when setting headstage."); + devicesFound = false; + sources.clear(); + return false; + } + + const auto& polledBno = std::static_pointer_cast (sources.back()); + + polledBno->setBnoAxisMap (PolledBno055::Bno055AxisMap::YZX); + polledBno->setBnoAxisSign ((uint32_t) (PolledBno055::Bno055AxisSign::MirrorX) | (uint32_t) (PolledBno055::Bno055AxisSign::MirrorZ)); + + hubNames.insert ({ OnixDevice::getOffset (polledBno->getDeviceIdx()), NEUROPIXELSV1E_HEADSTAGE_NAME }); + } + else + { + Onix1::showWarningMessageBoxAsync ( + "Unkown Hub ID", + "Discovered hub ID " + std::to_string (hsid) + " (" + onix_hub_str (hsid) + ") on " + OnixDevice::getPortName (OnixDevice::getHubIndexFromPassthroughIndex (index)) + " which does not match any currently implemented hubs."); + devicesFound = false; + sources.clear(); + return false; + } + } + } + + context->issueReset(); + + if (updateStreamInfo) + CoreServices::updateSignalChain (editor); + + LOGD ("All devices initialized."); + return devicesFound; } -bool OnixSource::configureBlockReadSize(std::shared_ptr context, uint32_t blockReadSize) +bool OnixSource::configureBlockReadSize (std::shared_ptr context, uint32_t blockReadSize) { - if (context == nullptr || !context->isInitialized()) - { - Onix1::showWarningMessageBoxAsync("Invalid Context", "Cannot set block read size, context is not initialized correctly. Please try removing the plugin and adding it again."); - return false; - } - - oni_size_t readFrameSize; - int rc = context->getOption(ONI_OPT_MAXREADFRAMESIZE, &readFrameSize); - if (rc == ONI_ESUCCESS) - { - LOGD("Max read frame size: ", readFrameSize, " bytes"); - } - else - { - LOGE("Unable to get read frame size."); - return false; - } - - oni_size_t writeFrameSize; - rc = context->getOption(ONI_OPT_MAXWRITEFRAMESIZE, &writeFrameSize); - if (rc == ONI_ESUCCESS) - { - LOGD("Max write frame size: ", writeFrameSize, " bytes"); - } - else - { - LOGE("Unable to get write frame size."); - return false; - } - - if (!writeBlockReadSize(context, blockReadSize, readFrameSize)) - { - return false; - } - - return true; + if (context == nullptr || ! context->isInitialized()) + { + Onix1::showWarningMessageBoxAsync ("Invalid Context", "Cannot set block read size, context is not initialized correctly. Please try removing the plugin and adding it again."); + return false; + } + + oni_size_t readFrameSize; + int rc = context->getOption (ONI_OPT_MAXREADFRAMESIZE, &readFrameSize); + if (rc == ONI_ESUCCESS) + { + LOGD ("Max read frame size: ", readFrameSize, " bytes"); + } + else + { + LOGE ("Unable to get read frame size."); + return false; + } + + oni_size_t writeFrameSize; + rc = context->getOption (ONI_OPT_MAXWRITEFRAMESIZE, &writeFrameSize); + if (rc == ONI_ESUCCESS) + { + LOGD ("Max write frame size: ", writeFrameSize, " bytes"); + } + else + { + LOGE ("Unable to get write frame size."); + return false; + } + + if (! writeBlockReadSize (context, blockReadSize, readFrameSize)) + { + return false; + } + + return true; } uint32_t OnixSource::getBlockReadSize() const { - return blockReadSize; + return blockReadSize; } -void OnixSource::setBlockReadSize(uint32_t newReadSize) +void OnixSource::setBlockReadSize (uint32_t newReadSize) { - blockReadSize = newReadSize; + blockReadSize = newReadSize; } -bool OnixSource::writeBlockReadSize(std::shared_ptr context, uint32_t blockReadSize, uint32_t readFrameSize) +bool OnixSource::writeBlockReadSize (std::shared_ptr context, uint32_t blockReadSize, uint32_t readFrameSize) { - if (context == nullptr || !context->isInitialized()) - { - Onix1::showWarningMessageBoxAsync("Invalid Context", "Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); - return false; - } + if (context == nullptr || ! context->isInitialized()) + { + Onix1::showWarningMessageBoxAsync ("Invalid Context", "Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); + return false; + } - if (blockReadSize < readFrameSize) - { - Onix1::showWarningMessageBoxAsync("Invalid Block Read Size", "The block read size is too small. The max read frame size is " + std::to_string(readFrameSize) + ", but the block read size is " + std::to_string(blockReadSize) + ".\n\nTo continue, increase the block read size to be greater than " + std::to_string(readFrameSize) + " and reconnect."); - return false; - } + if (blockReadSize < readFrameSize) + { + Onix1::showWarningMessageBoxAsync ("Invalid Block Read Size", "The block read size is too small. The max read frame size is " + std::to_string (readFrameSize) + ", but the block read size is " + std::to_string (blockReadSize) + ".\n\nTo continue, increase the block read size to be greater than " + std::to_string (readFrameSize) + " and reconnect."); + return false; + } - int rc = context->setOption(ONI_OPT_BLOCKREADSIZE, blockReadSize); + int rc = context->setOption (ONI_OPT_BLOCKREADSIZE, blockReadSize); - if (rc != ONI_ESUCCESS) - { - LOGE("Unknown error found when setting block read size to ", blockReadSize); - return false; - } + if (rc != ONI_ESUCCESS) + { + LOGE ("Unknown error found when setting block read size to ", blockReadSize); + return false; + } - LOGD("Block read size: ", blockReadSize, " bytes"); + LOGD ("Block read size: ", blockReadSize, " bytes"); - return true; + return true; } OnixDeviceVector OnixSource::getDataSources() { - OnixDeviceVector devices{}; + OnixDeviceVector devices {}; - for (const auto& source : sources) - { - devices.emplace_back(source); - } + for (const auto& source : sources) + { + devices.emplace_back (source); + } - return devices; + return devices; } OnixDeviceVector OnixSource::getEnabledDataSources() { - OnixDeviceVector devices{}; + OnixDeviceVector devices {}; - for (const auto& source : sources) - { - if (source->isEnabled()) - devices.emplace_back(source); - } + for (const auto& source : sources) + { + if (source->isEnabled()) + devices.emplace_back (source); + } - return devices; + return devices; } -OnixDeviceVector OnixSource::getDataSourcesFromOffset(int offset) +OnixDeviceVector OnixSource::getDataSourcesFromOffset (int offset) { - OnixDeviceVector devices{}; - offset = OnixDevice::getOffset(offset); + OnixDeviceVector devices {}; + offset = OnixDevice::getOffset (offset); - for (const auto& source : sources) - { - if (OnixDevice::getOffset(source->getDeviceIdx()) == offset) - devices.emplace_back(source); - } + for (const auto& source : sources) + { + if (OnixDevice::getOffset (source->getDeviceIdx()) == offset) + devices.emplace_back (source); + } - return devices; + return devices; } -std::shared_ptr OnixSource::getDevice(OnixDeviceType type, int offset) +std::shared_ptr OnixSource::getDevice (OnixDeviceType type, int offset) { - for (const auto& device : sources) - { - if (device->getDeviceType() == type) return device; - } + for (const auto& device : sources) + { + if (device->getDeviceType() == type) + return device; + } - return nullptr; + return nullptr; } -OnixDeviceVector OnixSource::getDevices(OnixDeviceType type) +OnixDeviceVector OnixSource::getDevices (OnixDeviceType type) { - OnixDeviceVector foundDevices{}; + OnixDeviceVector foundDevices {}; - for (const auto& device : sources) - { - if (device->getDeviceType() == type) - foundDevices.emplace_back(device); - } + for (const auto& device : sources) + { + if (device->getDeviceType() == type) + foundDevices.emplace_back (device); + } - return foundDevices; + return foundDevices; } -OnixDeviceMap OnixSource::getConnectedDevices(OnixDeviceVector devices, bool filterDevices) +OnixDeviceMap OnixSource::getConnectedDevices (OnixDeviceVector devices, bool filterDevices) { - OnixDeviceMap deviceMap; + OnixDeviceMap deviceMap; - for (const auto& device : devices) - { - if (filterDevices && (device->getDeviceType() == OnixDeviceType::MEMORYMONITOR)) continue; + for (const auto& device : devices) + { + if (filterDevices && (device->getDeviceType() == OnixDeviceType::MEMORYMONITOR)) + continue; - deviceMap.insert({ device->getDeviceIdx(), device->getDeviceType() }); - } + deviceMap.insert ({ device->getDeviceIdx(), device->getDeviceType() }); + } - return deviceMap; + return deviceMap; } -OnixDeviceMap OnixSource::getConnectedDevices(bool filterDevices) +OnixDeviceMap OnixSource::getConnectedDevices (bool filterDevices) { - return getConnectedDevices(getEnabledDataSources(), filterDevices); + return getConnectedDevices (getEnabledDataSources(), filterDevices); } std::map OnixSource::getHubNames() { - return hubNames; + return hubNames; } void OnixSource::updateSourceBuffers() { - sourceBuffers.clear(true); - - for (const auto& source : sources) - { - if (source->isEnabled()) - { - source->addSourceBuffers(sourceBuffers); - } - } + sourceBuffers.clear (true); + + for (const auto& source : sources) + { + if (source->isEnabled()) + { + source->addSourceBuffers (sourceBuffers); + } + } } -void OnixSource::updateDiscoveryParameters(PortName port, DiscoveryParameters parameters) +void OnixSource::updateDiscoveryParameters (PortName port, DiscoveryParameters parameters) { - switch (port) - { - case PortName::PortA: - portA->updateDiscoveryParameters(parameters); - break; - case PortName::PortB: - portB->updateDiscoveryParameters(parameters); - break; - default: - break; - } + switch (port) + { + case PortName::PortA: + portA->updateDiscoveryParameters (parameters); + break; + case PortName::PortB: + portB->updateDiscoveryParameters (parameters); + break; + default: + break; + } } -bool OnixSource::configurePortVoltage(PortName port, std::string voltage) const +bool OnixSource::configurePortVoltage (PortName port, std::string voltage) const { - if (!context->isInitialized()) return false; - - switch (port) - { - case PortName::PortA: - if (voltage == "" || voltage == "Auto") - return portA->configureVoltage(); - else - return portA->configureVoltage(std::stod(voltage)); - case PortName::PortB: - if (voltage == "" || voltage == "Auto") - return portB->configureVoltage(); - else - return portB->configureVoltage(std::stod(voltage)); - default: - return false; - } + if (! context->isInitialized()) + return false; + + switch (port) + { + case PortName::PortA: + if (voltage == "" || voltage == "Auto") + return portA->configureVoltage(); + else + return portA->configureVoltage (std::stod (voltage)); + case PortName::PortB: + if (voltage == "" || voltage == "Auto") + return portB->configureVoltage(); + else + return portB->configureVoltage (std::stod (voltage)); + default: + return false; + } } -void OnixSource::setPortVoltage(PortName port, double voltage) const +void OnixSource::setPortVoltage (PortName port, double voltage) const { - if (!context->isInitialized()) return; - - bool waitToSettle = voltage > 0; - - switch (port) - { - case PortName::PortA: - portA->setVoltageOverride(voltage, waitToSettle); - return; - case PortName::PortB: - portB->setVoltageOverride(voltage, waitToSettle); - return; - default: - return; - } + if (! context->isInitialized()) + return; + + bool waitToSettle = voltage > 0; + + switch (port) + { + case PortName::PortA: + portA->setVoltageOverride (voltage, waitToSettle); + return; + case PortName::PortB: + portB->setVoltageOverride (voltage, waitToSettle); + return; + default: + return; + } } -double OnixSource::getLastVoltageSet(PortName port) +double OnixSource::getLastVoltageSet (PortName port) { - if (!context->isInitialized()) return 0.0; - - switch (port) - { - case PortName::PortA: - return portA->getLastVoltageSet(); - case PortName::PortB: - return portB->getLastVoltageSet(); - default: - return 0.0; - } + if (! context->isInitialized()) + return 0.0; + + switch (port) + { + case PortName::PortA: + return portA->getLastVoltageSet(); + case PortName::PortB: + return portB->getLastVoltageSet(); + default: + return 0.0; + } } void OnixSource::resetContext() { - if (context != nullptr && context->isInitialized()) - context->issueReset(); + if (context != nullptr && context->isInitialized()) + context->issueReset(); } bool OnixSource::isContextInitialized() { - return context != nullptr && context->isInitialized(); + return context != nullptr && context->isInitialized(); } std::shared_ptr OnixSource::getContext() { - return context; + return context; } -bool OnixSource::getDeviceTable(device_map_t* deviceTable) +bool OnixSource::getDeviceTable (device_map_t* deviceTable) { - return context->getDeviceTable(deviceTable) == ONI_ESUCCESS; + return context->getDeviceTable (deviceTable) == ONI_ESUCCESS; } -void OnixSource::updateSettings(OwnedArray* continuousChannels, - OwnedArray* eventChannels, - OwnedArray* spikeChannels, - OwnedArray* dataStreams, - OwnedArray* deviceInfos, - OwnedArray* configurationObjects) +void OnixSource::updateSettings (OwnedArray* continuousChannels, + OwnedArray* eventChannels, + OwnedArray* spikeChannels, + OwnedArray* dataStreams, + OwnedArray* deviceInfos, + OwnedArray* configurationObjects) { - LOGD("ONIX Source initializing data streams."); - - continuousChannels->clear(); - eventChannels->clear(); - spikeChannels->clear(); - dataStreams->clear(); - deviceInfos->clear(); - configurationObjects->clear(); - - updateSourceBuffers(); - - if (devicesFound) - { - for (const auto& source : sources) - { - if (!source->isEnabled()) continue; - - auto type = source->getDeviceType(); - - if (type == OnixDeviceType::NEUROPIXELSV1F) - { - DeviceInfo::Settings deviceSettings{ - source->getName(), - "Neuropixels 1.0 Probe", - "neuropixels1.probe", - "0000000", - "imec" - }; - - deviceInfos->add(new DeviceInfo(deviceSettings)); - - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); - } - else if (type == OnixDeviceType::BNO || type == OnixDeviceType::POLLEDBNO) - { - DeviceInfo::Settings deviceSettings{ - source->getName(), - "Bno055 9-axis IMU", - "bno055", - "0000000", - "Bosch" - }; - - deviceInfos->add(new DeviceInfo(deviceSettings)); - - DataStream::Settings dataStreamSettings{ - OnixDevice::createStreamName({OnixDevice::getPortName(source->getDeviceIdx()), source->getHubName(), source->getName()}), - "Continuous data from a Bno055 9-axis IMU", - source->getStreamIdentifier(), - source->streamInfos[0].getSampleRate(), - true - }; - - addCombinedStreams(dataStreamSettings, source->streamInfos, dataStreams, deviceInfos, continuousChannels); - } - else if (type == OnixDeviceType::NEUROPIXELSV2E) - { - DeviceInfo::Settings deviceSettings{ - source->getName(), - "Neuropixels 2.0 Probe", - "neuropixels2.probe", - "0000000", - "imec" - }; - - deviceInfos->add(new DeviceInfo(deviceSettings)); - - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); - } - else if (type == OnixDeviceType::MEMORYMONITOR) - { - DeviceInfo::Settings deviceSettings{ - source->getName(), - "Memory Monitor", - "memorymonitor", - "0000000", - "" - }; - - deviceInfos->add(new DeviceInfo(deviceSettings)); - - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); - } - else if (type == OnixDeviceType::HARPSYNCINPUT) - { - DeviceInfo::Settings deviceSettings{ - source->getName(), - "Harp Sync Input", - "harpsyncinput", - "0000000", - "" - }; - - deviceInfos->add(new DeviceInfo(deviceSettings)); - - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); - } - else if (type == OnixDeviceType::DIGITALIO) - { - DeviceInfo::Settings digitalIODeviceSettings{ - source->getName(), - "DigitalIO", - "digitalio", - "0000000", - "" - }; - - deviceInfos->add(new DeviceInfo(digitalIODeviceSettings)); - - DataStream::Settings dataStreamSettings{ - OnixDevice::createStreamName({source->getHubName(), source->getName()}), - "Digital inputs and buttons", - source->getStreamIdentifier(), - source->streamInfos[0].getSampleRate(), - true - }; - - addCombinedStreams(dataStreamSettings, source->streamInfos, dataStreams, deviceInfos, continuousChannels); - - auto eventChannelSettings = std::static_pointer_cast(source)->getEventChannelSettings(dataStreams->getLast()); - eventChannels->add(new EventChannel(eventChannelSettings)); - } - else if (type == OnixDeviceType::ANALOGIO) - { - DeviceInfo::Settings analogIODeviceSettings{ - source->getName(), - "AnalogIO", - "analogio", - "0000000", - "" - }; - - deviceInfos->add(new DeviceInfo(analogIODeviceSettings)); - - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); - } - else if (type == OnixDeviceType::NEUROPIXELSV1E) - { - DeviceInfo::Settings deviceSettings{ - source->getName(), - "Neuropixels 1.0 Probe", - "neuropixels1.probe", - "0000000", - "imec" - }; - - deviceInfos->add(new DeviceInfo(deviceSettings)); - - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); - } - else if (type == OnixDeviceType::OUTPUTCLOCK) - { - continue; - } - else - { - Onix1::showWarningMessageBoxAsync( - "Unknown Source", - "Found an unknown source (" + source->getName() + ") on hub " + source->getHubName() + - " at address " + std::to_string(source->getDeviceIdx())); - } - } - } + LOGD ("ONIX Source initializing data streams."); + + continuousChannels->clear(); + eventChannels->clear(); + spikeChannels->clear(); + dataStreams->clear(); + deviceInfos->clear(); + configurationObjects->clear(); + + updateSourceBuffers(); + + if (devicesFound) + { + for (const auto& source : sources) + { + if (! source->isEnabled()) + continue; + + auto type = source->getDeviceType(); + + if (type == OnixDeviceType::NEUROPIXELSV1F) + { + DeviceInfo::Settings deviceSettings { + source->getName(), + "Neuropixels 1.0 Probe", + "neuropixels1.probe", + "0000000", + "imec" + }; + + deviceInfos->add (new DeviceInfo (deviceSettings)); + + addIndividualStreams (source->streamInfos, dataStreams, deviceInfos, continuousChannels); + } + else if (type == OnixDeviceType::BNO || type == OnixDeviceType::POLLEDBNO) + { + DeviceInfo::Settings deviceSettings { + source->getName(), + "Bno055 9-axis IMU", + "bno055", + "0000000", + "Bosch" + }; + + deviceInfos->add (new DeviceInfo (deviceSettings)); + + DataStream::Settings dataStreamSettings { + OnixDevice::createStreamName ({ OnixDevice::getPortName (source->getDeviceIdx()), source->getHubName(), source->getName() }), + "Continuous data from a Bno055 9-axis IMU", + source->getStreamIdentifier(), + source->streamInfos[0].getSampleRate(), + true + }; + + addCombinedStreams (dataStreamSettings, source->streamInfos, dataStreams, deviceInfos, continuousChannels); + } + else if (type == OnixDeviceType::NEUROPIXELSV2E) + { + DeviceInfo::Settings deviceSettings { + source->getName(), + "Neuropixels 2.0 Probe", + "neuropixels2.probe", + "0000000", + "imec" + }; + + deviceInfos->add (new DeviceInfo (deviceSettings)); + + addIndividualStreams (source->streamInfos, dataStreams, deviceInfos, continuousChannels); + } + else if (type == OnixDeviceType::MEMORYMONITOR) + { + DeviceInfo::Settings deviceSettings { + source->getName(), + "Memory Monitor", + "memorymonitor", + "0000000", + "" + }; + + deviceInfos->add (new DeviceInfo (deviceSettings)); + + addIndividualStreams (source->streamInfos, dataStreams, deviceInfos, continuousChannels); + } + else if (type == OnixDeviceType::HARPSYNCINPUT) + { + DeviceInfo::Settings deviceSettings { + source->getName(), + "Harp Sync Input", + "harpsyncinput", + "0000000", + "" + }; + + deviceInfos->add (new DeviceInfo (deviceSettings)); + + addIndividualStreams (source->streamInfos, dataStreams, deviceInfos, continuousChannels); + } + else if (type == OnixDeviceType::DIGITALIO) + { + DeviceInfo::Settings digitalIODeviceSettings { + source->getName(), + "DigitalIO", + "digitalio", + "0000000", + "" + }; + + deviceInfos->add (new DeviceInfo (digitalIODeviceSettings)); + + DataStream::Settings dataStreamSettings { + OnixDevice::createStreamName ({ source->getHubName(), source->getName() }), + "Digital inputs and buttons", + source->getStreamIdentifier(), + source->streamInfos[0].getSampleRate(), + true + }; + + addCombinedStreams (dataStreamSettings, source->streamInfos, dataStreams, deviceInfos, continuousChannels); + + auto eventChannelSettings = std::static_pointer_cast (source)->getEventChannelSettings (dataStreams->getLast()); + eventChannels->add (new EventChannel (eventChannelSettings)); + } + else if (type == OnixDeviceType::ANALOGIO) + { + DeviceInfo::Settings analogIODeviceSettings { + source->getName(), + "AnalogIO", + "analogio", + "0000000", + "" + }; + + deviceInfos->add (new DeviceInfo (analogIODeviceSettings)); + + addIndividualStreams (source->streamInfos, dataStreams, deviceInfos, continuousChannels); + } + else if (type == OnixDeviceType::NEUROPIXELSV1E) + { + DeviceInfo::Settings deviceSettings { + source->getName(), + "Neuropixels 1.0 Probe", + "neuropixels1.probe", + "0000000", + "imec" + }; + + deviceInfos->add (new DeviceInfo (deviceSettings)); + + addIndividualStreams (source->streamInfos, dataStreams, deviceInfos, continuousChannels); + } + else if (type == OnixDeviceType::OUTPUTCLOCK) + { + continue; + } + else + { + Onix1::showWarningMessageBoxAsync ( + "Unknown Source", + "Found an unknown source (" + source->getName() + ") on hub " + source->getHubName() + " at address " + std::to_string (source->getDeviceIdx())); + } + } + } } -void OnixSource::addCombinedStreams(DataStream::Settings dataStreamSettings, - Array streamInfos, - OwnedArray* dataStreams, - OwnedArray* deviceInfos, - OwnedArray* continuousChannels) +void OnixSource::addCombinedStreams (DataStream::Settings dataStreamSettings, + Array streamInfos, + OwnedArray* dataStreams, + OwnedArray* deviceInfos, + OwnedArray* continuousChannels) { - DataStream* stream = new DataStream(dataStreamSettings); - dataStreams->add(stream); - stream->device = deviceInfos->getLast(); - - for (const auto& streamInfo : streamInfos) - { - auto suffixes = streamInfo.getChannelNameSuffixes(); - - for (int chan = 0; chan < streamInfo.getNumChannels(); chan++) - { - auto prefix = streamInfo.getChannelPrefix(); - - if (suffixes[chan] != "") - { - if (prefix != "") - prefix += "-" + suffixes[chan]; - else - prefix = suffixes[chan]; - } - - ContinuousChannel::Settings channelSettings{ - streamInfo.getChannelType(), - prefix, - streamInfo.getDescription(), - createContinuousChannelIdentifier(streamInfo, chan), - streamInfo.getBitVolts(), - stream - }; - continuousChannels->add(new ContinuousChannel(channelSettings)); - continuousChannels->getLast()->setUnits(streamInfo.getUnits()); - } - } + DataStream* stream = new DataStream (dataStreamSettings); + dataStreams->add (stream); + stream->device = deviceInfos->getLast(); + + for (const auto& streamInfo : streamInfos) + { + auto suffixes = streamInfo.getChannelNameSuffixes(); + + for (int chan = 0; chan < streamInfo.getNumChannels(); chan++) + { + auto prefix = streamInfo.getChannelPrefix(); + + if (suffixes[chan] != "") + { + if (prefix != "") + prefix += "-" + suffixes[chan]; + else + prefix = suffixes[chan]; + } + + ContinuousChannel::Settings channelSettings { + streamInfo.getChannelType(), + prefix, + streamInfo.getDescription(), + createContinuousChannelIdentifier (streamInfo, chan), + streamInfo.getBitVolts(), + stream + }; + continuousChannels->add (new ContinuousChannel (channelSettings)); + continuousChannels->getLast()->setUnits (streamInfo.getUnits()); + } + } } -void OnixSource::addIndividualStreams(Array streamInfos, - OwnedArray* dataStreams, - OwnedArray* deviceInfos, - OwnedArray* continuousChannels) +void OnixSource::addIndividualStreams (Array streamInfos, + OwnedArray* dataStreams, + OwnedArray* deviceInfos, + OwnedArray* continuousChannels) { - for (StreamInfo streamInfo : streamInfos) - { - DataStream::Settings streamSettings - { - streamInfo.getName(), - streamInfo.getDescription(), - streamInfo.getStreamIdentifier(), - streamInfo.getSampleRate(), - true - }; - - DataStream* stream = new DataStream(streamSettings); - dataStreams->add(stream); - stream->device = deviceInfos->getLast(); - - auto suffixes = streamInfo.getChannelNameSuffixes(); - - // Add continuous channels - for (int chan = 0; chan < streamInfo.getNumChannels(); chan++) - { - ContinuousChannel::Settings channelSettings{ - streamInfo.getChannelType(), - streamInfo.getChannelPrefix() + suffixes[chan], - streamInfo.getDescription(), - createContinuousChannelIdentifier(streamInfo, chan), - streamInfo.getBitVolts(), - stream - }; - continuousChannels->add(new ContinuousChannel(channelSettings)); - continuousChannels->getLast()->setUnits(streamInfo.getUnits()); - } - } + for (StreamInfo streamInfo : streamInfos) + { + DataStream::Settings streamSettings { + streamInfo.getName(), + streamInfo.getDescription(), + streamInfo.getStreamIdentifier(), + streamInfo.getSampleRate(), + true + }; + + DataStream* stream = new DataStream (streamSettings); + dataStreams->add (stream); + stream->device = deviceInfos->getLast(); + + auto suffixes = streamInfo.getChannelNameSuffixes(); + + // Add continuous channels + for (int chan = 0; chan < streamInfo.getNumChannels(); chan++) + { + ContinuousChannel::Settings channelSettings { + streamInfo.getChannelType(), + streamInfo.getChannelPrefix() + suffixes[chan], + streamInfo.getDescription(), + createContinuousChannelIdentifier (streamInfo, chan), + streamInfo.getBitVolts(), + stream + }; + continuousChannels->add (new ContinuousChannel (channelSettings)); + continuousChannels->getLast()->setUnits (streamInfo.getUnits()); + } + } } -std::string OnixSource::createContinuousChannelIdentifier(StreamInfo streamInfo, int channelNumber) +std::string OnixSource::createContinuousChannelIdentifier (StreamInfo streamInfo, int channelNumber) { - auto dataType = streamInfo.getChannelIdentifierDataType(); - auto subTypes = streamInfo.getChannelIdentifierSubTypes(); + auto dataType = streamInfo.getChannelIdentifierDataType(); + auto subTypes = streamInfo.getChannelIdentifierSubTypes(); - auto channelIdentifier = streamInfo.getStreamIdentifier() + ".continuous." + dataType; + auto channelIdentifier = streamInfo.getStreamIdentifier() + ".continuous." + dataType; - if (subTypes.size() == streamInfo.getNumChannels()) - channelIdentifier += "." + subTypes[channelNumber]; - else if (subTypes.size() == 1) - channelIdentifier += "." + subTypes[0]; + if (subTypes.size() == streamInfo.getNumChannels()) + channelIdentifier += "." + subTypes[channelNumber]; + else if (subTypes.size() == 1) + channelIdentifier += "." + subTypes[0]; - return channelIdentifier; + return channelIdentifier; } bool OnixSource::isDevicesReady() { - auto tabMap = editor->createTabMapFromCanvas(); - auto sourceMap = getConnectedDevices(true); + auto tabMap = editor->createTabMapFromCanvas(); + auto sourceMap = getConnectedDevices (true); - return tabMap == sourceMap; + return tabMap == sourceMap; } bool OnixSource::foundInputSource() { - return devicesFound; + return devicesFound; } -bool OnixSource::checkPortControllerStatus(OnixSourceEditor* editor, std::shared_ptr port) +bool OnixSource::checkPortControllerStatus (OnixSourceEditor* editor, std::shared_ptr port) { - if (editor->isHeadstageSelected(port->getPort())) - { - if (!port->checkLinkState()) - { - Onix1::showWarningMessageBoxAsync("Port Controller Error", port->getName() + " is not currently connected."); - return false; - } - else if (port->getLinkFlags() != 0) - { - Onix1::showWarningMessageBoxAsync("Port Controller Error", port->getName() + " was disconnected, and must be reconnected."); - return false; - } - } - - return true; + if (editor->isHeadstageSelected (port->getPort())) + { + if (! port->checkLinkState()) + { + Onix1::showWarningMessageBoxAsync ("Port Controller Error", port->getName() + " is not currently connected."); + return false; + } + else if (port->getLinkFlags() != 0) + { + Onix1::showWarningMessageBoxAsync ("Port Controller Error", port->getName() + " was disconnected, and must be reconnected."); + return false; + } + } + + return true; } bool OnixSource::isReady() { - if (context == nullptr || !devicesFound) - return false; - - if (!isDevicesReady()) - { - CoreServices::sendStatusMessage("Devices not initialized properly. Check error logs for details."); - return false; - } - - if (!checkPortControllerStatus(editor, portA) || !checkPortControllerStatus(editor, portB)) - { - editor->setConnectedStatus(false); // NB: If either port controller lost lock, disconnect all devices - return false; - } - - for (const auto& source : sources) - { - if (!source->isEnabled()) continue; - - if (!source->updateSettings()) - return false; - } - - uint32_t val = 2; - int rc = context->setOption(ONI_OPT_RESETACQCOUNTER, val); - if (rc != ONI_ESUCCESS) return false; - - return true; + if (context == nullptr || ! devicesFound) + return false; + + if (! isDevicesReady()) + { + CoreServices::sendStatusMessage ("Devices not initialized properly. Check error logs for details."); + return false; + } + + if (! checkPortControllerStatus (editor, portA) || ! checkPortControllerStatus (editor, portB)) + { + editor->setConnectedStatus (false); // NB: If either port controller lost lock, disconnect all devices + return false; + } + + for (const auto& source : sources) + { + if (! source->isEnabled()) + continue; + + if (! source->updateSettings()) + return false; + } + + uint32_t val = 2; + int rc = context->setOption (ONI_OPT_RESETACQCOUNTER, val); + if (rc != ONI_ESUCCESS) + return false; + + return true; } bool OnixSource::startAcquisition() { - frameReader.reset(); + frameReader.reset(); - enabledSources = getEnabledDataSources(); + enabledSources = getEnabledDataSources(); - enabledSources.emplace_back(portA); - enabledSources.emplace_back(portB); + enabledSources.emplace_back (portA); + enabledSources.emplace_back (portB); - for (const auto& source : enabledSources) - { - source->startAcquisition(); - } + for (const auto& source : enabledSources) + { + source->startAcquisition(); + } - frameReader = std::make_unique(enabledSources, context); - frameReader->startThread(); + frameReader = std::make_unique (enabledSources, context); + frameReader->startThread(); - startThread(); + startThread(); - return true; + return true; } -void OnixSource::disconnectDevicesAfterAcquisition(OnixSourceEditor* editor) +void OnixSource::disconnectDevicesAfterAcquisition (OnixSourceEditor* editor) { - while (CoreServices::getAcquisitionStatus()) - std::this_thread::sleep_for(50ms); - - if (editor != nullptr) - { - const MessageManagerLock mmLock; - editor->setConnectedStatus(false); - } + while (CoreServices::getAcquisitionStatus()) + std::this_thread::sleep_for (50ms); + + if (editor != nullptr) + { + const MessageManagerLock mmLock; + editor->setConnectedStatus (false); + } } bool OnixSource::stopAcquisition() { - if (isThreadRunning()) - signalThreadShouldExit(); + if (isThreadRunning()) + signalThreadShouldExit(); - if (frameReader->isThreadRunning()) - frameReader->signalThreadShouldExit(); + if (frameReader->isThreadRunning()) + frameReader->signalThreadShouldExit(); - if (!portA->getErrorFlag() && !portB->getErrorFlag()) - waitForThreadToExit(2000); + if (! portA->getErrorFlag() && ! portB->getErrorFlag()) + waitForThreadToExit (2000); - oni_size_t reg = 0; - context->setOption(ONI_OPT_RUNNING, reg); + oni_size_t reg = 0; + context->setOption (ONI_OPT_RUNNING, reg); - for (const auto& source : enabledSources) - { - source->stopAcquisition(); - } + for (const auto& source : enabledSources) + { + source->stopAcquisition(); + } - for (auto buffers : sourceBuffers) - buffers->clear(); + for (auto buffers : sourceBuffers) + buffers->clear(); - if (portA->getErrorFlag() || portB->getErrorFlag()) - { - std::string msg = ""; + if (portA->getErrorFlag() || portB->getErrorFlag()) + { + std::string msg = ""; - if (portA->getErrorFlag()) - { - msg += "Port A"; - } + if (portA->getErrorFlag()) + { + msg += "Port A"; + } - if (portA->getErrorFlag() && portB->getErrorFlag()) - { - msg += " and "; - } + if (portA->getErrorFlag() && portB->getErrorFlag()) + { + msg += " and "; + } - if (portB->getErrorFlag()) - { - msg += "Port B"; - } + if (portB->getErrorFlag()) + { + msg += "Port B"; + } - msg += " lost communication lock during acquisition. Inspect hardware connections and port switch before reconnecting."; + msg += " lost communication lock during acquisition. Inspect hardware connections and port switch before reconnecting."; - std::thread t(disconnectDevicesAfterAcquisition, editor); - t.detach(); // NB: Detach to allow the current thread to finish, stopping acquisition and allowing the called thread to complete + std::thread t (disconnectDevicesAfterAcquisition, editor); + t.detach(); // NB: Detach to allow the current thread to finish, stopping acquisition and allowing the called thread to complete - Onix1::showWarningMessageBoxAsync( - "Port Communication Lock Lost", - msg); + Onix1::showWarningMessageBoxAsync ( + "Port Communication Lock Lost", + msg); - return false; - } + return false; + } - return true; + return true; } bool OnixSource::updateBuffer() { - for (const auto& source : enabledSources) - { - source->processFrames(); + for (const auto& source : enabledSources) + { + source->processFrames(); - if (threadShouldExit()) return true; - } + if (threadShouldExit()) + return true; + } - return !portA->getErrorFlag() && !portB->getErrorFlag(); + return ! portA->getErrorFlag() && ! portB->getErrorFlag(); } diff --git a/Source/OnixSource.h b/Source/OnixSource.h index 7e6380a..9223cd6 100644 --- a/Source/OnixSource.h +++ b/Source/OnixSource.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,159 +24,157 @@ #include +#include "Devices/PortController.h" +#include "FrameReader.h" #include "Onix1.h" #include "OnixDevice.h" #include "OnixSourceEditor.h" -#include "FrameReader.h" -#include "Devices/PortController.h" namespace OnixSourcePlugin { - /** +/** - @see DataThread, SourceNode + @see DataThread, SourceNode - */ - class OnixSource : public DataThread - { - public: - - /** Constructor */ - OnixSource(SourceNode* sn); - - /** Destructor */ - ~OnixSource(); +*/ +class OnixSource : public DataThread +{ +public: + /** Constructor */ + OnixSource (SourceNode* sn); - void registerParameters() override; + /** Destructor */ + ~OnixSource(); - /** Static method to create DataThread */ - static DataThread* createDataThread(SourceNode* sn); + void registerParameters() override; - /** Creates the custom editor */ - std::unique_ptr createEditor(SourceNode* sn) override; + /** Static method to create DataThread */ + static DataThread* createDataThread (SourceNode* sn); - /** Reads frames and adds them to the appropriate device */ - bool updateBuffer() override; + /** Creates the custom editor */ + std::unique_ptr createEditor (SourceNode* sn) override; - /** Returns true if the processor is ready to stream data, including all hardware initialization. Returns false if not ready. */ - bool isReady() override; + /** Reads frames and adds them to the appropriate device */ + bool updateBuffer() override; - /** Returns true if the hardware is connected, false otherwise.*/ - bool foundInputSource() override; + /** Returns true if the processor is ready to stream data, including all hardware initialization. Returns false if not ready. */ + bool isReady() override; - /** Returns true if the deviceMap matches the settings tabs that are open */ - bool isDevicesReady(); + /** Returns true if the hardware is connected, false otherwise.*/ + bool foundInputSource() override; - /** Initializes data transfer.*/ - bool startAcquisition() override; + /** Returns true if the deviceMap matches the settings tabs that are open */ + bool isDevicesReady(); - /** Stops data transfer.*/ - bool stopAcquisition() override; + /** Initializes data transfer.*/ + bool startAcquisition() override; - void updateDiscoveryParameters(PortName port, DiscoveryParameters parameters); + /** Stops data transfer.*/ + bool stopAcquisition() override; - /** Takes a string from the editor. Can be an empty string to allow for automated discovery */ - bool configurePortVoltage(PortName port, std::string voltage) const; + void updateDiscoveryParameters (PortName port, DiscoveryParameters parameters); - /** Sets the port voltage */ - void setPortVoltage(PortName port, double voltage) const; + /** Takes a string from the editor. Can be an empty string to allow for automated discovery */ + bool configurePortVoltage (PortName port, std::string voltage) const; - double getLastVoltageSet(PortName port); + /** Sets the port voltage */ + void setPortVoltage (PortName port, double voltage) const; - void resetContext(); + double getLastVoltageSet (PortName port); - bool isContextInitialized(); + void resetContext(); - std::shared_ptr getContext(); + bool isContextInitialized(); - bool getDeviceTable(device_map_t*); + std::shared_ptr getContext(); - static bool enablePassthroughMode(std::shared_ptr, bool, bool); + bool getDeviceTable (device_map_t*); - bool configurePort(PortName); - bool resetPortLinkFlags(); - bool resetPortLinkFlags(PortName); + static bool enablePassthroughMode (std::shared_ptr, bool, bool); - static bool checkHubFirmwareCompatibility(std::shared_ptr, device_map_t); + bool configurePort (PortName); + bool resetPortLinkFlags(); + bool resetPortLinkFlags (PortName); - bool initializeDevices(device_map_t, bool updateStreamInfo = false); + static bool checkHubFirmwareCompatibility (std::shared_ptr, device_map_t); - static bool configureBlockReadSize(std::shared_ptr, uint32_t); + bool initializeDevices (device_map_t, bool updateStreamInfo = false); - bool disconnectDevices(bool updateStreamInfo = false); + static bool configureBlockReadSize (std::shared_ptr, uint32_t); - OnixDeviceVector getDataSources(); - OnixDeviceVector getEnabledDataSources(); - OnixDeviceVector getDataSourcesFromOffset(int offset); + bool disconnectDevices (bool updateStreamInfo = false); - std::shared_ptr getDevice(OnixDeviceType, int); - OnixDeviceVector getDevices(OnixDeviceType); + OnixDeviceVector getDataSources(); + OnixDeviceVector getEnabledDataSources(); + OnixDeviceVector getDataSourcesFromOffset (int offset); - static OnixDeviceMap getConnectedDevices(OnixDeviceVector, bool filterDevices = false); + std::shared_ptr getDevice (OnixDeviceType, int); + OnixDeviceVector getDevices (OnixDeviceType); - OnixDeviceMap getConnectedDevices(bool filterDevices = false); + static OnixDeviceMap getConnectedDevices (OnixDeviceVector, bool filterDevices = false); - std::map getHubNames(); + OnixDeviceMap getConnectedDevices (bool filterDevices = false); - void updateSourceBuffers(); + std::map getHubNames(); - // DataThread Methods - void updateSettings(OwnedArray* continuousChannels, - OwnedArray* eventChannels, - OwnedArray* spikeChannels, - OwnedArray* sourceStreams, - OwnedArray* devices, - OwnedArray* configurationObjects); + void updateSourceBuffers(); - uint32_t getBlockReadSize() const; + // DataThread Methods + void updateSettings (OwnedArray* continuousChannels, + OwnedArray* eventChannels, + OwnedArray* spikeChannels, + OwnedArray* sourceStreams, + OwnedArray* devices, + OwnedArray* configurationObjects); - void setBlockReadSize(uint32_t); + uint32_t getBlockReadSize() const; - static bool checkPortControllerStatus(OnixSourceEditor* editor, std::shared_ptr port); + void setBlockReadSize (uint32_t); - private: + static bool checkPortControllerStatus (OnixSourceEditor* editor, std::shared_ptr port); - /** Available data sources */ - OnixDeviceVector sources; - OnixDeviceVector enabledSources; +private: + /** Available data sources */ + OnixDeviceVector sources; + OnixDeviceVector enabledSources; - /** Available headstages, indexed by their offset value */ - std::map hubNames; + /** Available headstages, indexed by their offset value */ + std::map hubNames; - /** Pointer to the editor */ - OnixSourceEditor* editor; + /** Pointer to the editor */ + OnixSourceEditor* editor; - /** Thread that reads frames */ - std::unique_ptr frameReader; + /** Thread that reads frames */ + std::unique_ptr frameReader; - std::shared_ptr context = nullptr; + std::shared_ptr context = nullptr; - std::shared_ptr portA; - std::shared_ptr portB; + std::shared_ptr portA; + std::shared_ptr portB; - uint32_t blockReadSize = 4096; + uint32_t blockReadSize = 4096; - bool devicesFound = false; + bool devicesFound = false; - static constexpr int BREAKOUT_BOARD_OFFSET = 0; + static constexpr int BREAKOUT_BOARD_OFFSET = 0; - void addIndividualStreams(Array, OwnedArray*, OwnedArray*, OwnedArray*); + void addIndividualStreams (Array, OwnedArray*, OwnedArray*, OwnedArray*); - void addCombinedStreams(DataStream::Settings, Array, OwnedArray*, OwnedArray*, OwnedArray*); + void addCombinedStreams (DataStream::Settings, Array, OwnedArray*, OwnedArray*, OwnedArray*); - std::string createContinuousChannelIdentifier(StreamInfo streamInfo, int channelNumber); + std::string createContinuousChannelIdentifier (StreamInfo streamInfo, int channelNumber); - /** Template method to initialize an OnixDevice and add it to the currently active OnixDeviceVector variable */ - template - static bool configureDevice(OnixDeviceVector&, OnixSourceEditor*, std::string, std::string, OnixDeviceType, const oni_dev_idx_t, std::shared_ptr); + /** Template method to initialize an OnixDevice and add it to the currently active OnixDeviceVector variable */ + template + static bool configureDevice (OnixDeviceVector&, OnixSourceEditor*, std::string, std::string, OnixDeviceType, const oni_dev_idx_t, std::shared_ptr); - static bool getHubFirmwareVersion(std::shared_ptr, uint32_t, uint32_t*); + static bool getHubFirmwareVersion (std::shared_ptr, uint32_t, uint32_t*); - static bool writeBlockReadSize(std::shared_ptr, uint32_t, uint32_t); + static bool writeBlockReadSize (std::shared_ptr, uint32_t, uint32_t); - /** This method is expected to be called in a separate thread, and waits for acquisition to stop before gracefully disconnecting all devices */ - static void disconnectDevicesAfterAcquisition(OnixSourceEditor* editor); + /** This method is expected to be called in a separate thread, and waits for acquisition to stop before gracefully disconnecting all devices */ + static void disconnectDevicesAfterAcquisition (OnixSourceEditor* editor); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(OnixSource); - }; -} + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OnixSource); +}; +} // namespace OnixSourcePlugin diff --git a/Source/OnixSourceCanvas.cpp b/Source/OnixSourceCanvas.cpp index 6293aaf..b011b72 100644 --- a/Source/OnixSourceCanvas.cpp +++ b/Source/OnixSourceCanvas.cpp @@ -1,388 +1,389 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright(C) Open Ephys + Copyright(C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software : you can redistribute it and /or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software : you can redistribute it and /or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program.If not, see < http://www.gnu.org/licenses/>. + You should have received a copy of the GNU General Public License + along with this program.If not, see < http://www.gnu.org/licenses/>. */ #include "OnixSourceCanvas.h" + #include "OnixSource.h" #include "OnixSourceEditor.h" #include "UI/SettingsInterface.h" using namespace OnixSourcePlugin; -OnixSourceCanvas::OnixSourceCanvas(GenericProcessor* processor_, OnixSourceEditor* editor_, OnixSource* onixSource_) : - Visualizer(processor_), - editor(editor_), - source(onixSource_) +OnixSourceCanvas::OnixSourceCanvas (GenericProcessor* processor_, OnixSourceEditor* editor_, OnixSource* onixSource_) + : Visualizer (processor_), + editor (editor_), + source (onixSource_) { - topLevelTabComponent = std::make_unique(true); - addAndMakeVisible(topLevelTabComponent.get()); + topLevelTabComponent = std::make_unique (true); + addAndMakeVisible (topLevelTabComponent.get()); - addHub(BREAKOUT_BOARD_NAME, 0); + addHub (BREAKOUT_BOARD_NAME, 0); } -CustomTabComponent* OnixSourceCanvas::addTopLevelTab(std::string tabName, int index) +CustomTabComponent* OnixSourceCanvas::addTopLevelTab (std::string tabName, int index) { - CustomTabComponent* tab = new CustomTabComponent(false); + CustomTabComponent* tab = new CustomTabComponent (false); - topLevelTabComponent->addTab(tabName, Colours::grey, tab, true, index); - tab->setName(tabName); + topLevelTabComponent->addTab (tabName, Colours::grey, tab, true, index); + tab->setName (tabName); - hubTabs.add(tab); + hubTabs.add (tab); - return tab; + return tab; } -void OnixSourceCanvas::addHub(std::string hubName, int offset) +void OnixSourceCanvas::addHub (std::string hubName, int offset) { - CustomTabComponent* tab = nullptr; - OnixDeviceVector devices; - PortName port = PortController::getPortFromIndex(offset); - auto context = source->getContext(); - - if (context == nullptr || !context->isInitialized()) - { - LOGE("Unable to find an initialized context when adding " + hubName + " to the canvas."); - return; - } - - if (hubName == NEUROPIXELSV1E_HEADSTAGE_NAME) - { - tab = addTopLevelTab(getTopLevelTabName(port, hubName), (int)port); - - const int passthroughIndex = OnixDevice::getPassthroughIndexFromHubIndex(offset); - - devices.emplace_back(std::make_shared(ProbeString, hubName, passthroughIndex, context)); - devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(PolledBno055::getDeviceType()), hubName, passthroughIndex, context)); - } - else if (hubName == NEUROPIXELSV1F_HEADSTAGE_NAME) - { - tab = addTopLevelTab(getTopLevelTabName(port, hubName), (int)port); - - static constexpr int Probe0Offset = 0, Probe1Offset = 1, BnoOffset = 2; - - devices.emplace_back(std::make_shared(ProbeString + "0", hubName, offset + Probe0Offset, context)); - devices.emplace_back(std::make_shared(ProbeString + "1", hubName, offset + Probe1Offset, context)); - devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(Bno055::getDeviceType()), hubName, offset + BnoOffset, context)); - } - else if (hubName == BREAKOUT_BOARD_NAME) - { - tab = addTopLevelTab(hubName, 0); - - static constexpr int OutputClockOffset = 5, AnalogIOOffset = 6, DigitalIOOffset = 7, HarpSyncInputOffset = 12; - - devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(DigitalIO::getDeviceType()), hubName, DigitalIOOffset, context)); - devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(AnalogIO::getDeviceType()), hubName, AnalogIOOffset, context)); - devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(HarpSyncInput::getDeviceType()), hubName, HarpSyncInputOffset, context)); - devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(OutputClock::getDeviceType()), hubName, OutputClockOffset, context)); - } - else if (hubName == NEUROPIXELSV2E_HEADSTAGE_NAME) - { - const int passthroughIndex = OnixDevice::getPassthroughIndexFromHubIndex(offset); - - tab = addTopLevelTab(getTopLevelTabName(port, hubName), (int)port); - - devices.emplace_back(std::make_shared(ProbeString, hubName, passthroughIndex, context)); - devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(OnixDeviceType::POLLEDBNO), hubName, passthroughIndex, context)); - } - - if (tab != nullptr && devices.size() > 0) - { - populateSourceTabs(tab, devices); - } + CustomTabComponent* tab = nullptr; + OnixDeviceVector devices; + PortName port = PortController::getPortFromIndex (offset); + auto context = source->getContext(); + + if (context == nullptr || ! context->isInitialized()) + { + LOGE ("Unable to find an initialized context when adding " + hubName + " to the canvas."); + return; + } + + if (hubName == NEUROPIXELSV1E_HEADSTAGE_NAME) + { + tab = addTopLevelTab (getTopLevelTabName (port, hubName), (int) port); + + const int passthroughIndex = OnixDevice::getPassthroughIndexFromHubIndex (offset); + + devices.emplace_back (std::make_shared (ProbeString, hubName, passthroughIndex, context)); + devices.emplace_back (std::make_shared (OnixDevice::TypeString.at (PolledBno055::getDeviceType()), hubName, passthroughIndex, context)); + } + else if (hubName == NEUROPIXELSV1F_HEADSTAGE_NAME) + { + tab = addTopLevelTab (getTopLevelTabName (port, hubName), (int) port); + + static constexpr int Probe0Offset = 0, Probe1Offset = 1, BnoOffset = 2; + + devices.emplace_back (std::make_shared (ProbeString + "0", hubName, offset + Probe0Offset, context)); + devices.emplace_back (std::make_shared (ProbeString + "1", hubName, offset + Probe1Offset, context)); + devices.emplace_back (std::make_shared (OnixDevice::TypeString.at (Bno055::getDeviceType()), hubName, offset + BnoOffset, context)); + } + else if (hubName == BREAKOUT_BOARD_NAME) + { + tab = addTopLevelTab (hubName, 0); + + static constexpr int OutputClockOffset = 5, AnalogIOOffset = 6, DigitalIOOffset = 7, HarpSyncInputOffset = 12; + + devices.emplace_back (std::make_shared (OnixDevice::TypeString.at (DigitalIO::getDeviceType()), hubName, DigitalIOOffset, context)); + devices.emplace_back (std::make_shared (OnixDevice::TypeString.at (AnalogIO::getDeviceType()), hubName, AnalogIOOffset, context)); + devices.emplace_back (std::make_shared (OnixDevice::TypeString.at (HarpSyncInput::getDeviceType()), hubName, HarpSyncInputOffset, context)); + devices.emplace_back (std::make_shared (OnixDevice::TypeString.at (OutputClock::getDeviceType()), hubName, OutputClockOffset, context)); + } + else if (hubName == NEUROPIXELSV2E_HEADSTAGE_NAME) + { + const int passthroughIndex = OnixDevice::getPassthroughIndexFromHubIndex (offset); + + tab = addTopLevelTab (getTopLevelTabName (port, hubName), (int) port); + + devices.emplace_back (std::make_shared (ProbeString, hubName, passthroughIndex, context)); + devices.emplace_back (std::make_shared (OnixDevice::TypeString.at (OnixDeviceType::POLLEDBNO), hubName, passthroughIndex, context)); + } + + if (tab != nullptr && devices.size() > 0) + { + populateSourceTabs (tab, devices); + } } -std::shared_ptr OnixSourceCanvas::getDevicePtr(OnixDeviceType deviceType, int deviceIndex) +std::shared_ptr OnixSourceCanvas::getDevicePtr (OnixDeviceType deviceType, int deviceIndex) { - for (const auto& settingsInterface : settingsInterfaces) - { - auto device = settingsInterface->getDevice(); + for (const auto& settingsInterface : settingsInterfaces) + { + auto device = settingsInterface->getDevice(); - if (device->getDeviceType() == deviceType && device->getDeviceIdx() == deviceIndex) - { - return device; - } - } + if (device->getDeviceType() == deviceType && device->getDeviceIdx() == deviceIndex) + { + return device; + } + } - return nullptr; + return nullptr; } -void OnixSourceCanvas::populateSourceTabs(CustomTabComponent* tab, OnixDeviceVector devices) +void OnixSourceCanvas::populateSourceTabs (CustomTabComponent* tab, OnixDeviceVector devices) { - int portTabNumber = 0; - - for (const auto& device : devices) - { - auto type = device->getDeviceType(); - - if (type == OnixDeviceType::NEUROPIXELSV1F) - { - auto neuropixInterface = std::make_shared(std::static_pointer_cast(device), editor, this); - addInterfaceToTab(device->getName(), tab, neuropixInterface); - } - else if (type == OnixDeviceType::NEUROPIXELSV1E) - { - auto neuropixInterface = std::make_shared(std::static_pointer_cast(device), editor, this); - addInterfaceToTab(device->getName(), tab, neuropixInterface); - } - else if (type == OnixDeviceType::BNO) - { - auto bno055Interface = std::make_shared(std::static_pointer_cast(device), editor, this); - addInterfaceToTab(device->getName(), tab, bno055Interface); - } - else if (type == OnixDeviceType::OUTPUTCLOCK) - { - auto outputClockInterface = std::make_shared(std::static_pointer_cast(device), editor, this); - addInterfaceToTab(device->getName(), tab, outputClockInterface); - } - else if (type == OnixDeviceType::HARPSYNCINPUT) - { - auto harpSyncInputInterface = std::make_shared(std::static_pointer_cast(device), editor, this); - addInterfaceToTab(device->getName(), tab, harpSyncInputInterface); - } - else if (type == OnixDeviceType::ANALOGIO) - { - auto analogIOInterface = std::make_shared(std::static_pointer_cast(device), editor, this); - addInterfaceToTab(device->getName(), tab, analogIOInterface); - } - else if (type == OnixDeviceType::DIGITALIO) - { - auto digitalIOInterface = std::make_shared(std::static_pointer_cast(device), editor, this); - addInterfaceToTab(device->getName(), tab, digitalIOInterface); - } - else if (type == OnixDeviceType::NEUROPIXELSV2E) - { - auto npxv2eInterface = std::make_shared(std::static_pointer_cast(device), editor, this); - std::string substring = " Headstage"; - std::string hubName = device->getHubName(); - addInterfaceToTab(hubName.erase(hubName.find(substring), substring.size()), tab, npxv2eInterface); - } - else if (type == OnixDeviceType::POLLEDBNO) - { - auto polledBnoInterface = std::make_shared(std::static_pointer_cast(device), editor, this); - addInterfaceToTab(device->getName(), tab, polledBnoInterface); - } - else - { - Onix1::showWarningMessageBoxAsync("Device Type Not Found", "The device type " + OnixDevice::TypeString.at(type) + " is not known."); - return; - } - } + int portTabNumber = 0; + + for (const auto& device : devices) + { + auto type = device->getDeviceType(); + + if (type == OnixDeviceType::NEUROPIXELSV1F) + { + auto neuropixInterface = std::make_shared (std::static_pointer_cast (device), editor, this); + addInterfaceToTab (device->getName(), tab, neuropixInterface); + } + else if (type == OnixDeviceType::NEUROPIXELSV1E) + { + auto neuropixInterface = std::make_shared (std::static_pointer_cast (device), editor, this); + addInterfaceToTab (device->getName(), tab, neuropixInterface); + } + else if (type == OnixDeviceType::BNO) + { + auto bno055Interface = std::make_shared (std::static_pointer_cast (device), editor, this); + addInterfaceToTab (device->getName(), tab, bno055Interface); + } + else if (type == OnixDeviceType::OUTPUTCLOCK) + { + auto outputClockInterface = std::make_shared (std::static_pointer_cast (device), editor, this); + addInterfaceToTab (device->getName(), tab, outputClockInterface); + } + else if (type == OnixDeviceType::HARPSYNCINPUT) + { + auto harpSyncInputInterface = std::make_shared (std::static_pointer_cast (device), editor, this); + addInterfaceToTab (device->getName(), tab, harpSyncInputInterface); + } + else if (type == OnixDeviceType::ANALOGIO) + { + auto analogIOInterface = std::make_shared (std::static_pointer_cast (device), editor, this); + addInterfaceToTab (device->getName(), tab, analogIOInterface); + } + else if (type == OnixDeviceType::DIGITALIO) + { + auto digitalIOInterface = std::make_shared (std::static_pointer_cast (device), editor, this); + addInterfaceToTab (device->getName(), tab, digitalIOInterface); + } + else if (type == OnixDeviceType::NEUROPIXELSV2E) + { + auto npxv2eInterface = std::make_shared (std::static_pointer_cast (device), editor, this); + std::string substring = " Headstage"; + std::string hubName = device->getHubName(); + addInterfaceToTab (hubName.erase (hubName.find (substring), substring.size()), tab, npxv2eInterface); + } + else if (type == OnixDeviceType::POLLEDBNO) + { + auto polledBnoInterface = std::make_shared (std::static_pointer_cast (device), editor, this); + addInterfaceToTab (device->getName(), tab, polledBnoInterface); + } + else + { + Onix1::showWarningMessageBoxAsync ("Device Type Not Found", "The device type " + OnixDevice::TypeString.at (type) + " is not known."); + return; + } + } } -void OnixSourceCanvas::addInterfaceToTab(std::string tabName, CustomTabComponent* tab, std::shared_ptr interface_) +void OnixSourceCanvas::addInterfaceToTab (std::string tabName, CustomTabComponent* tab, std::shared_ptr interface_) { - settingsInterfaces.emplace_back(interface_); - tab->addTab(tabName, Colours::darkgrey, CustomViewport::createCustomViewport(interface_.get()), true); + settingsInterfaces.emplace_back (interface_); + tab->addTab (tabName, Colours::darkgrey, CustomViewport::createCustomViewport (interface_.get()), true); } -std::string OnixSourceCanvas::getTopLevelTabName(PortName port, std::string headstage) +std::string OnixSourceCanvas::getTopLevelTabName (PortName port, std::string headstage) { - return OnixDevice::getPortName(port) + ": " + headstage; + return OnixDevice::getPortName (port) + ": " + headstage; } Array OnixSourceCanvas::getHubTabs() { - Array tabs; + Array tabs; - for (const auto hub : hubTabs) - { - tabs.add(hub); - } + for (const auto hub : hubTabs) + { + tabs.add (hub); + } - return tabs; + return tabs; } void OnixSourceCanvas::refresh() { - repaint(); + repaint(); } void OnixSourceCanvas::refreshState() { - resized(); + resized(); } -void OnixSourceCanvas::removeTabs(PortName port) +void OnixSourceCanvas::removeTabs (PortName port) { - bool tabExists = false; - - for (int i = hubTabs.size() - 1; i >= 0; i -= 1) - { - if (hubTabs[i]->getName().contains(OnixDevice::getPortName(port))) - { - hubTabs.remove(i, true); - tabExists = true; - break; - } - } - - int offset = PortController::getPortOffset(port); - - for (int i = settingsInterfaces.size() - 1; i >= 0; i -= 1) - { - auto selectedDevice = settingsInterfaces[i]->getDevice(); - - if ((selectedDevice->getDeviceIdx() & offset) > 0) - { - settingsInterfaces.erase(settingsInterfaces.begin() + i); - tabExists = true; - } - } - - if (tabExists) - { - if (port == PortName::PortB && hubTabs.size() == 1 && hubTabs[0]->getName().contains(BREAKOUT_BOARD_NAME)) - topLevelTabComponent->removeTab((int)port - 1); // NB: If only one headstage is selected in the editor, the index needs to be corrected here. - else - topLevelTabComponent->removeTab((int)port); - } + bool tabExists = false; + + for (int i = hubTabs.size() - 1; i >= 0; i -= 1) + { + if (hubTabs[i]->getName().contains (OnixDevice::getPortName (port))) + { + hubTabs.remove (i, true); + tabExists = true; + break; + } + } + + int offset = PortController::getPortOffset (port); + + for (int i = settingsInterfaces.size() - 1; i >= 0; i -= 1) + { + auto selectedDevice = settingsInterfaces[i]->getDevice(); + + if ((selectedDevice->getDeviceIdx() & offset) > 0) + { + settingsInterfaces.erase (settingsInterfaces.begin() + i); + tabExists = true; + } + } + + if (tabExists) + { + if (port == PortName::PortB && hubTabs.size() == 1 && hubTabs[0]->getName().contains (BREAKOUT_BOARD_NAME)) + topLevelTabComponent->removeTab ((int) port - 1); // NB: If only one headstage is selected in the editor, the index needs to be corrected here. + else + topLevelTabComponent->removeTab ((int) port); + } } void OnixSourceCanvas::removeAllTabs() { - hubTabs.clear(true); - settingsInterfaces.clear(); + hubTabs.clear (true); + settingsInterfaces.clear(); - topLevelTabComponent->clearTabs(); + topLevelTabComponent->clearTabs(); } -OnixDeviceMap OnixSourceCanvas::getSelectedDevices(std::vector> interfaces) +OnixDeviceMap OnixSourceCanvas::getSelectedDevices (std::vector> interfaces) { - OnixDeviceMap tabMap; + OnixDeviceMap tabMap; - for (const auto& settings : interfaces) - { - auto device = settings->getDevice(); + for (const auto& settings : interfaces) + { + auto device = settings->getDevice(); - if (!device->isEnabled()) - continue; + if (! device->isEnabled()) + continue; - tabMap.insert({ device->getDeviceIdx(), device->getDeviceType() }); - } + tabMap.insert ({ device->getDeviceIdx(), device->getDeviceType() }); + } - return tabMap; + return tabMap; } bool OnixSourceCanvas::verifyHeadstageSelection() { - auto selectedDevices = getSelectedDevices(settingsInterfaces); - auto connectedDevices = source->getConnectedDevices(true); - - if (selectedDevices != connectedDevices) - { - auto connectedOffsets = OnixDevice::getUniqueOffsets(connectedDevices); - auto selectedOffsets = OnixDevice::getUniqueOffsets(selectedDevices); - - auto hubNames = source->getHubNames(); - - std::string title = "Invalid Headstage Selection", msg; - - msg = "There is a mismatch between the headstages that are selected, and the headstages that are connected.\n\n"; - msg += "Selected headstage"; - msg += (selectedOffsets.size() > 1) ? "s" : ""; - msg += ":\n\n"; - - if (selectedOffsets.size() == 0) - { - msg += "None\n"; - } - else - { - for (const auto offset : selectedOffsets) - { - msg += OnixDevice::getPortName(offset) + ": " + editor->getHeadstageSelected(offset) + "\n"; - } - } - - msg += "\nConnected headstage"; - msg += (connectedOffsets.size() > 1) ? "s" : ""; - msg += ":\n\n"; - - if (connectedOffsets.size() == 0) - { - msg += "None\n"; - } - else - { - for (const auto offset : connectedOffsets) - { - msg += OnixDevice::getPortName(offset) + ": " + hubNames[offset] + "\n"; - } - } - - msg += "\nVerify that the correct headstage(s) are selected, and that all hardware connections are correct before trying to connect again."; - - Onix1::showWarningMessageBoxAsync(title, msg); - - return false; - } - - return true; + auto selectedDevices = getSelectedDevices (settingsInterfaces); + auto connectedDevices = source->getConnectedDevices (true); + + if (selectedDevices != connectedDevices) + { + auto connectedOffsets = OnixDevice::getUniqueOffsets (connectedDevices); + auto selectedOffsets = OnixDevice::getUniqueOffsets (selectedDevices); + + auto hubNames = source->getHubNames(); + + std::string title = "Invalid Headstage Selection", msg; + + msg = "There is a mismatch between the headstages that are selected, and the headstages that are connected.\n\n"; + msg += "Selected headstage"; + msg += (selectedOffsets.size() > 1) ? "s" : ""; + msg += ":\n\n"; + + if (selectedOffsets.size() == 0) + { + msg += "None\n"; + } + else + { + for (const auto offset : selectedOffsets) + { + msg += OnixDevice::getPortName (offset) + ": " + editor->getHeadstageSelected (offset) + "\n"; + } + } + + msg += "\nConnected headstage"; + msg += (connectedOffsets.size() > 1) ? "s" : ""; + msg += ":\n\n"; + + if (connectedOffsets.size() == 0) + { + msg += "None\n"; + } + else + { + for (const auto offset : connectedOffsets) + { + msg += OnixDevice::getPortName (offset) + ": " + hubNames[offset] + "\n"; + } + } + + msg += "\nVerify that the correct headstage(s) are selected, and that all hardware connections are correct before trying to connect again."; + + Onix1::showWarningMessageBoxAsync (title, msg); + + return false; + } + + return true; } void OnixSourceCanvas::update() const { - for (int i = 0; i < settingsInterfaces.size(); i++) - settingsInterfaces[i]->updateInfoString(); + for (int i = 0; i < settingsInterfaces.size(); i++) + settingsInterfaces[i]->updateInfoString(); } void OnixSourceCanvas::resized() { - topLevelTabComponent->setBounds(0, -3, getWidth(), getHeight() + 3); + topLevelTabComponent->setBounds (0, -3, getWidth(), getHeight() + 3); } void OnixSourceCanvas::resetContext() { - source->resetContext(); + source->resetContext(); } bool OnixSourceCanvas::foundInputSource() { - return source->foundInputSource(); + return source->foundInputSource(); } void OnixSourceCanvas::startAcquisition() { - for (const auto& settingsInterface : settingsInterfaces) - { - settingsInterface->startAcquisition(); - } + for (const auto& settingsInterface : settingsInterfaces) + { + settingsInterface->startAcquisition(); + } } void OnixSourceCanvas::stopAcquisition() { - for (const auto& settingsInterface : settingsInterfaces) - { - settingsInterface->stopAcquisition(); - } + for (const auto& settingsInterface : settingsInterfaces) + { + settingsInterface->stopAcquisition(); + } } -void OnixSourceCanvas::saveCustomParametersToXml(XmlElement* xml) +void OnixSourceCanvas::saveCustomParametersToXml (XmlElement* xml) { - for (int i = 0; i < settingsInterfaces.size(); i++) - settingsInterfaces[i]->saveParameters(xml); + for (int i = 0; i < settingsInterfaces.size(); i++) + settingsInterfaces[i]->saveParameters (xml); } -void OnixSourceCanvas::loadCustomParametersFromXml(XmlElement* xml) +void OnixSourceCanvas::loadCustomParametersFromXml (XmlElement* xml) { - for (int i = 0; i < settingsInterfaces.size(); i++) - settingsInterfaces[i]->loadParameters(xml); + for (int i = 0; i < settingsInterfaces.size(); i++) + settingsInterfaces[i]->loadParameters (xml); } diff --git a/Source/OnixSourceCanvas.h b/Source/OnixSourceCanvas.h index 5cb7b2d..dcd8ce2 100644 --- a/Source/OnixSourceCanvas.h +++ b/Source/OnixSourceCanvas.h @@ -1,22 +1,22 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ @@ -24,95 +24,94 @@ #include -#include "UI/InterfaceList.h" #include "UI/CustomTabComponent.h" +#include "UI/InterfaceList.h" namespace OnixSourcePlugin { - class OnixSource; - class OnixSourceEditor; - - /** - Holds the visualizer for additional probe settings - */ - class OnixSourceCanvas : public Visualizer - { - public: - /** Constructor */ - OnixSourceCanvas(GenericProcessor*, OnixSourceEditor*, OnixSource*); +class OnixSource; +class OnixSourceEditor; - /** Renders the Visualizer on each animation callback cycle */ - void refresh() override; +/** + Holds the visualizer for additional probe settings +*/ +class OnixSourceCanvas : public Visualizer +{ +public: + /** Constructor */ + OnixSourceCanvas (GenericProcessor*, OnixSourceEditor*, OnixSource*); - /** Starts animation (not needed for this component) */ - void beginAnimation() override {} + /** Renders the Visualizer on each animation callback cycle */ + void refresh() override; - /** Stops animation (not needed for this component) */ - void endAnimation() override {} + /** Starts animation (not needed for this component) */ + void beginAnimation() override {} - /** Called when the Visualizer's tab becomes visible after being hidden */ - void refreshState() override; + /** Stops animation (not needed for this component) */ + void endAnimation() override {} - /** Removes tabs from the canvas at the specified port */ - void removeTabs(PortName port); + /** Called when the Visualizer's tab becomes visible after being hidden */ + void refreshState() override; - /** Removes all tabs from the canvas */ - void removeAllTabs(); + /** Removes tabs from the canvas at the specified port */ + void removeTabs (PortName port); - /** Called when the hardware is connected, to ensure the correct headstage is found */ - bool verifyHeadstageSelection(); + /** Removes all tabs from the canvas */ + void removeAllTabs(); - /** Called when the Visualizer is first created, and optionally when - the parameters of the underlying processor are changed */ - void update() const; + /** Called when the hardware is connected, to ensure the correct headstage is found */ + bool verifyHeadstageSelection(); - /** Starts animation of sub-interfaces */ - void startAcquisition(); + /** Called when the Visualizer is first created, and optionally when + the parameters of the underlying processor are changed */ + void update() const; - /** Stops animation of sub-interfaces */ - void stopAcquisition(); + /** Starts animation of sub-interfaces */ + void startAcquisition(); - /** Add the hub and all of its devices to the canvas */ - void addHub(std::string, int); + /** Stops animation of sub-interfaces */ + void stopAcquisition(); - /** Called when the basestation is created or refreshed */ - void populateSourceTabs(CustomTabComponent*, OnixDeviceVector); + /** Add the hub and all of its devices to the canvas */ + void addHub (std::string, int); - /** Saves custom UI settings */ - void saveCustomParametersToXml(XmlElement* xml) override; + /** Called when the basestation is created or refreshed */ + void populateSourceTabs (CustomTabComponent*, OnixDeviceVector); - /** Loads custom UI settings*/ - void loadCustomParametersFromXml(XmlElement* xml) override; + /** Saves custom UI settings */ + void saveCustomParametersToXml (XmlElement* xml) override; - /** Sets bounds of sub-components*/ - void resized(); + /** Loads custom UI settings*/ + void loadCustomParametersFromXml (XmlElement* xml) override; - Array getHubTabs(); + /** Sets bounds of sub-components*/ + void resized(); - OnixDeviceMap getSelectedDevices(std::vector>); + Array getHubTabs(); - std::vector> settingsInterfaces; + OnixDeviceMap getSelectedDevices (std::vector>); - void resetContext(); + std::vector> settingsInterfaces; - bool foundInputSource(); + void resetContext(); - std::shared_ptr getDevicePtr(OnixDeviceType, int); + bool foundInputSource(); - private: + std::shared_ptr getDevicePtr (OnixDeviceType, int); - OnixSourceEditor* editor; - OnixSource* source; +private: + OnixSourceEditor* editor; + OnixSource* source; - std::unique_ptr topLevelTabComponent; - OwnedArray hubTabs; + std::unique_ptr topLevelTabComponent; + OwnedArray hubTabs; - CustomTabComponent* addTopLevelTab(std::string tabName, int index = -1); + CustomTabComponent* addTopLevelTab (std::string tabName, int index = -1); - void addInterfaceToTab(std::string tabName, CustomTabComponent* tab, std::shared_ptr interface_); + void addInterfaceToTab (std::string tabName, CustomTabComponent* tab, std::shared_ptr interface_); - std::string getTopLevelTabName(PortName port, std::string headstage); + std::string getTopLevelTabName (PortName port, std::string headstage); - JUCE_LEAK_DETECTOR(OnixSourceCanvas); - }; -} + JUCE_LEAK_DETECTOR (OnixSourceCanvas); +}; +} // namespace OnixSourcePlugin diff --git a/Source/OnixSourceEditor.cpp b/Source/OnixSourceEditor.cpp index bb52270..01a4668 100644 --- a/Source/OnixSourceEditor.cpp +++ b/Source/OnixSourceEditor.cpp @@ -1,721 +1,723 @@ /* - ------------------------------------------------------------------ + ------------------------------------------------------------------ - Copyright (C) Open Ephys + Copyright (C) Open Ephys - ------------------------------------------------------------------ + ------------------------------------------------------------------ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #include "OnixSourceEditor.h" + +#include "Devices/MemoryMonitor.h" #include "OnixSource.h" #include "OnixSourceCanvas.h" -#include "Devices/MemoryMonitor.h" using namespace OnixSourcePlugin; -OnixSourceEditor::OnixSourceEditor(GenericProcessor* parentNode, OnixSource* source_) - : VisualizerEditor(parentNode, "Onix Source", 250), source(source_) +OnixSourceEditor::OnixSourceEditor (GenericProcessor* parentNode, OnixSource* source_) + : VisualizerEditor (parentNode, "Onix Source", 250), source (source_) { - canvas = nullptr; - - FontOptions fontOptionRegular = FontOptions("Fira Code", 12.0f, Font::plain); - FontOptions fontOptionTitle = FontOptions("Fira Code", 15.0f, Font::bold); - - memoryUsage = std::make_unique(parentNode); - memoryUsage->setBounds(8, 28, 14, 80); - addAndMakeVisible(memoryUsage.get()); - - blockReadSizeValue = std::make_unique