diff --git a/Source/Devices/Neuropixels2e.cpp b/Source/Devices/Neuropixels2e.cpp index 3f6466f..99433f3 100644 --- a/Source/Devices/Neuropixels2e.cpp +++ b/Source/Devices/Neuropixels2e.cpp @@ -30,6 +30,7 @@ Neuropixels2e::Neuropixels2e(std::string name, std::string hubName, const oni_de INeuropixel(NeuropixelsV2eValues::numberOfSettings, NeuropixelsV2eValues::numberOfShanks) { probeSN.fill(0); + probeEnabled.fill(true); for (int i = 0; i < NeuropixelsV2eValues::numberOfSettings; i++) { @@ -40,6 +41,18 @@ Neuropixels2e::Neuropixels2e(std::string name, std::string hubName, const oni_de eventCodes[i] = 0; } +Neuropixels2e::~Neuropixels2e() +{ + if (serializer != nullptr) + { + selectProbe(ProbeSelected::NoProbe); + serializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10, DefaultGPO10Config); + } + + if (deviceContext != nullptr && deviceContext->isInitialized()) + deviceContext->setOption(ONIX_OPT_PASSTHROUGH, 0); +} + void Neuropixels2e::createDataStream(int n) { StreamInfo apStream = StreamInfo( @@ -63,6 +76,64 @@ int Neuropixels2e::getNumProbes() const return m_numProbes; } +std::string Neuropixels2e::getName(int index) +{ + switch (index) + { + case 0: return "Probe0"; + case 1: return "Probe1"; + default: return "UNKNOWN"; + } + + return ""; +} + +std::string Neuropixels2e::getName(ProbeSelected probe) +{ + switch (probe) + { + case ProbeSelected::ProbeA: return getName(0); + case ProbeSelected::ProbeB: return getName(1); + default: return "UNKNOWN"; + } + + return ""; +} + +bool Neuropixels2e::isEnabled() const +{ + return std::any_of(probeEnabled.cbegin(), probeEnabled.cend(), [](bool en) { return en; }); +} + +bool Neuropixels2e::setProbeEnabled(int index, bool status) +{ + try + { + probeEnabled[index] = status; + return true; + } + catch (const std::out_of_range& ex) // filter for out of range + { + LOGE("Invalid index given setting probe enable status."); + } + + return false; +} + +bool Neuropixels2e::getProbeEnabled(int index) +{ + try + { + return probeEnabled[index]; + } + catch (const std::out_of_range& ex) // filter for out of range + { + LOGE("Invalid index given getting probe enable status."); + } + + return false; +} + void Neuropixels2e::selectElectrodesInRange(std::vector& selection, int startIndex, int numberOfElectrodes) { for (int i = startIndex; i < startIndex + numberOfElectrodes; i++) @@ -235,22 +306,34 @@ OnixDeviceType Neuropixels2e::getDeviceType() int Neuropixels2e::configureDevice() { if (deviceContext == nullptr || !deviceContext->isInitialized()) - throw error_str("Device context is not initialized properly for " + getName()); + throw error_str("Device context is not initialized properly for " + OnixDevice::getName()); - int rc = deviceContext->writeRegister(deviceIdx, DS90UB9x::ENABLE, isEnabled() ? 1 : 0); + int rc = deviceContext->writeRegister(deviceIdx, DS90UB9x::ENABLE, isEnabled() ? 1u : 0u); if (rc != ONI_ESUCCESS) - throw error_str("Unable to enable " + getName()); + throw error_str("Unable to enable passthrough device."); + + if (!isEnabled()) + return ONI_ESUCCESS; 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); + throw error_str("Unable to set I2C rate for " + OnixDevice::getName()); + + probeSN[0] = getProbeSN(ProbeSelected::ProbeA); + if (probeSN[0] != 0) + { + LOGD("Probe A SN: ", probeSN[0]); + } + + probeSN[1] = getProbeSN(ProbeSelected::ProbeB); + if (probeSN[1] != 0) + { + LOGD("Probe B SN: ", probeSN[1]); + } + setProbeSupply(false); - LOGD("Probe A SN: ", probeSN[0]); - LOGD("Probe B SN: ", probeSN[1]); if (probeSN[0] == 0 && probeSN[1] == 0) { @@ -280,7 +363,7 @@ bool Neuropixels2e::updateSettings() { for (int i = 0; i < 2; i++) { - if (probeSN[i] != 0) + if (probeSN[i] != 0 && probeEnabled[i]) { if (gainCorrectionFilePath[i] == "None" || gainCorrectionFilePath[i] == "") { @@ -344,20 +427,59 @@ bool Neuropixels2e::updateSettings() { if (probeSN[i] != 0) { - selectProbe(i == 0 ? ProbeASelected : ProbeBSelected); - writeConfiguration(settings[i].get()); - configureProbeStreaming(); + auto probeSelected = i == 0 ? ProbeSelected::ProbeA: ProbeSelected::ProbeB; + + if (probeEnabled[i]) + { + writeConfiguration(probeSelected, settings[i].get()); + if (!configureProbeStreaming(probeSelected)) + return false; + } + else + { + if (!disableProbeStreaming(probeSelected)) + return false; + } } } - selectProbe(NoProbeSelected); + selectProbe(ProbeSelected::NoProbe); //IMPORTANT! BNO polling thread must be started after this return true; } -void Neuropixels2e::configureProbeStreaming() +bool Neuropixels2e::disableProbeStreaming(ProbeSelected probeSelect) { + if (probeControl == nullptr) + { + Onix1::showWarningMessageBoxAsync( + "Probe Stream", + "Unable to disable " + getName(probeSelect) + " streaming." + ); + return false; + } + + selectProbe(probeSelect); + + probeControl->WriteByte(OP_MODE, 1 << 7); + + return true; +} + +bool Neuropixels2e::configureProbeStreaming(ProbeSelected probeSelect) +{ + if (probeControl == nullptr) + { + Onix1::showWarningMessageBoxAsync( + "Probe Stream Configuration", + "Unable to configure " + getName(probeSelect) + " streaming." + ); + return false; + } + + selectProbe(probeSelect); + // Write super sync bits into ASIC probeControl->WriteByte(SUPERSYNC11, 0b00011000); probeControl->WriteByte(SUPERSYNC10, 0b01100001); @@ -378,6 +500,8 @@ void Neuropixels2e::configureProbeStreaming() // Set global ADC settings // TODO: Undocumented probeControl->WriteByte(ADC_CONFIG, 0b00001000); + + return true; } void Neuropixels2e::configureSerDes() @@ -427,12 +551,12 @@ void Neuropixels2e::setProbeSupply(bool en) { auto gpo10Config = en ? DefaultGPO10Config | GPO10SupplyMask : DefaultGPO10Config; - selectProbe(NoProbeSelected); + selectProbe(ProbeSelected::NoProbe); serializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10), gpo10Config); Thread::sleep(20); } -void Neuropixels2e::selectProbe(uint8_t probeSelect) +void Neuropixels2e::selectProbe(ProbeSelected probeSelect) { if (serializer == nullptr) { @@ -440,7 +564,7 @@ void Neuropixels2e::selectProbe(uint8_t probeSelect) return; } - serializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO32), probeSelect); + serializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO32), (uint8_t)probeSelect); Thread::sleep(20); } @@ -453,7 +577,7 @@ void Neuropixels2e::resetProbes() serializer->WriteByte((uint32_t)(DS90UB9x::DS90UB9xSerializerI2CRegister::GPIO10), gpo10Config); } -uint64_t Neuropixels2e::getProbeSN(uint8_t probeSelect) +uint64_t Neuropixels2e::getProbeSN(ProbeSelected probeSelect) { if (flex == nullptr) { @@ -561,8 +685,10 @@ void Neuropixels2e::processFrames() } } -void Neuropixels2e::writeConfiguration(ProbeSettings* settings) +void Neuropixels2e::writeConfiguration(ProbeSelected probeSelect, ProbeSettings* settings) { + selectProbe(probeSelect); + auto baseBits = makeBaseBits(getReference(settings->referenceIndex)); writeShiftRegister(SR_CHAIN5, baseBits[0]); writeShiftRegister(SR_CHAIN6, baseBits[1]); diff --git a/Source/Devices/Neuropixels2e.h b/Source/Devices/Neuropixels2e.h index aa20fd0..a5478b2 100644 --- a/Source/Devices/Neuropixels2e.h +++ b/Source/Devices/Neuropixels2e.h @@ -43,6 +43,13 @@ namespace OnixSourcePlugin SR_OK = 1 << 7 }; + enum class ProbeSelected : uint8_t + { + NoProbe = 0b00010001, // No probes selected + ProbeA = 0b00011001, // Probe A + ProbeB = 0b10011001, // Probe B + }; + /* Configures and streams data from a Neuropixels 2.0e device (aka a configured raw deserializer) */ @@ -53,37 +60,26 @@ namespace OnixSourcePlugin public: Neuropixels2e(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr); - ~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); - } + ~Neuropixels2e(); int configureDevice() override; - - /** Update the settings of the device */ bool updateSettings() override; - - /** Starts probe data streaming */ void startAcquisition() override; - - /** Stops probe data streaming*/ void stopAcquisition() override; - void addFrame(oni_frame_t*) override; - void processFrames() override; - void addSourceBuffers(OwnedArray& sourceBuffers) override; int getNumProbes() const; + using OnixDevice::getName; + static std::string getName(int); + static std::string getName(ProbeSelected); + + bool isEnabled() const override; + bool setProbeEnabled(int, bool); + bool getProbeEnabled(int); + static const int baseBitsPerChannel = 4; static const int configurationBitCount = NeuropixelsV2eValues::numberOfChannels * baseBitsPerChannel / 2; @@ -101,41 +97,38 @@ namespace OnixSourcePlugin 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; + private: + DataBuffer* amplifierBuffer[NumberOfProbes]; std::array probeSN; std::array gainCorrection; std::array gainCorrectionFilePath; + std::array probeEnabled; void createDataStream(int n); - uint64_t getProbeSN(uint8_t probeSelect); + uint64_t getProbeSN(ProbeSelected probeSelect); void configureSerDes(); void setProbeSupply(bool); void resetProbes(); - void selectProbe(uint8_t probeSelect); - void configureProbeStreaming(); - void writeConfiguration(ProbeSettings*); + void selectProbe(ProbeSelected probeSelect); + bool configureProbeStreaming(ProbeSelected probeSelect); + bool disableProbeStreaming(ProbeSelected probeSelect); + void writeConfiguration(ProbeSelected probeSelect, ProbeSettings*); NeuropixelsV2Reference getReference(int); static std::string getShankName(uint32_t shiftRegisterAddress); @@ -145,9 +138,9 @@ namespace OnixSourcePlugin int m_numProbes = 0; - const float sampleRate = 30000.0f; - static const int numFrames = 10; - static const int numSamples = numberOfChannels * numFrames; + static constexpr float sampleRate = 30000.0f; + static constexpr int numFrames = 10; + static constexpr int numSamples = numberOfChannels * numFrames; std::array samples; @@ -163,67 +156,64 @@ namespace OnixSourcePlugin std::unique_ptr flex; std::unique_ptr probeControl; - static const int ProbeI2CAddress = 0x10; - static const int FlexAddress = 0x50; + static constexpr int ProbeI2CAddress = 0x10; + static constexpr int FlexAddress = 0x50; - static const int ProbeAddress = 0x10; - static const int FlexEEPROMAddress = 0x50; + static constexpr int ProbeAddress = 0x10; + static constexpr 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 constexpr uint32_t GPO10SupplyMask = 1 << 3; // Used to turn on VDDA analog supply + static constexpr uint32_t GPO10ResetMask = 1 << 7; // Used to issue full reset commands to probes + static constexpr uint8_t DefaultGPO10Config = 0b00010001; // NPs in reset, VDDA not enabled - 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 + static constexpr int FramesPerSuperFrame = 16; + static constexpr int AdcsPerProbe = 24; + static constexpr int ChannelCount = 384; + static constexpr 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 constexpr uint32_t OP_MODE = 0x00; + static constexpr uint32_t REC_MODE = 0x01; + static constexpr uint32_t CAL_MODE = 0x02; + static constexpr uint32_t ADC_CONFIG = 0x03; + static constexpr uint32_t TEST_CONFIG1 = 0x04; + static constexpr uint32_t TEST_CONFIG2 = 0x05; + static constexpr uint32_t TEST_CONFIG3 = 0x06; + static constexpr uint32_t TEST_CONFIG4 = 0x07; + static constexpr uint32_t TEST_CONFIG5 = 0x08; + static constexpr uint32_t STATUS = 0x09; + static constexpr uint32_t SUPERSYNC0 = 0x0A; + static constexpr uint32_t SUPERSYNC1 = 0x0B; + static constexpr uint32_t SUPERSYNC2 = 0x0C; + static constexpr uint32_t SUPERSYNC3 = 0x0D; + static constexpr uint32_t SUPERSYNC4 = 0x0E; + static constexpr uint32_t SUPERSYNC5 = 0x0F; + static constexpr uint32_t SUPERSYNC6 = 0x10; + static constexpr uint32_t SUPERSYNC7 = 0x11; + static constexpr uint32_t SUPERSYNC8 = 0x12; + static constexpr uint32_t SUPERSYNC9 = 0x13; + static constexpr uint32_t SUPERSYNC10 = 0x14; + static constexpr uint32_t SUPERSYNC11 = 0x15; + static constexpr uint32_t SR_CHAIN6 = 0x16; // Odd channel base config + static constexpr uint32_t SR_CHAIN5 = 0x17; // Even channel base config + static constexpr uint32_t SR_CHAIN4 = 0x18; // Shank 4 + static constexpr uint32_t SR_CHAIN3 = 0x19; // Shank 3 + static constexpr uint32_t SR_CHAIN2 = 0x1A; // Shank 2 + static constexpr uint32_t SR_CHAIN1 = 0x1B; // Shank 1 + static constexpr uint32_t SR_LENGTH2 = 0x1C; + static constexpr uint32_t SR_LENGTH1 = 0x1D; + static constexpr uint32_t PROBE_ID = 0x1E; + static constexpr uint32_t SOFT_RESET = 0x1F; + + static constexpr uint32_t OFFSET_PROBE_SN = 0x00; + static constexpr uint32_t OFFSET_FLEX_VERSION = 0x10; + static constexpr uint32_t OFFSET_FLEX_REVISION = 0x11; + static constexpr uint32_t OFFSET_FLEX_PN = 0x20; + static constexpr uint32_t OFFSET_PROBE_PN = 0x40; Array frameArray; - static inline const std::array adcIndices = { + static inline constexpr std::array adcIndices = { 0, 1, 2, 4, 5, 6, 8, 9, 10, @@ -234,7 +224,7 @@ namespace OnixSourcePlugin 28, 29, 30 }; - static inline const std::array, AdcsPerProbe> rawToChannel = { { + static inline constexpr 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 diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index 7e0c76f..ef133d9 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -422,7 +422,7 @@ bool OnixSource::initializeDevices(device_map_t deviceTable, bool updateStreamIn { auto hubIndex = OnixDevice::getHubIndexFromPassthroughIndex(index); - devicesFound = configureDevice(sources, editor, "Neuropixels 2.0", NEUROPIXELSV2E_HEADSTAGE_NAME, Neuropixels2e::getDeviceType(), hubIndex, context); + devicesFound = configureDevice(sources, editor, "Neuropixels 2.0e", NEUROPIXELSV2E_HEADSTAGE_NAME, Neuropixels2e::getDeviceType(), hubIndex, context); if (!devicesFound) { sources.clear(); @@ -798,7 +798,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel deviceInfos->add(new DeviceInfo(deviceSettings)); - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); + addIndividualStreams(source->streamInfos, dataStreams, deviceInfos->getLast(), continuousChannels); } else if (source->getDeviceType() == OnixDeviceType::BNO || source->getDeviceType() == OnixDeviceType::POLLEDBNO) { @@ -820,7 +820,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel true }; - addCombinedStreams(dataStreamSettings, source->streamInfos, dataStreams, deviceInfos, continuousChannels); + addCombinedStreams(dataStreamSettings, source->streamInfos, dataStreams, deviceInfos->getLast(), continuousChannels); } else if (source->getDeviceType() == OnixDeviceType::NEUROPIXELSV2E) { @@ -834,7 +834,24 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel deviceInfos->add(new DeviceInfo(deviceSettings)); - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); + auto npx = std::static_pointer_cast(source); + + if (npx->getNumProbes() == 1) + { + for (int i = 0; i < Neuropixels2e::NumberOfProbes; i++) + { + if (npx->getProbeEnabled(i) && npx->getProbeSerialNumber(i) != 0ull) + addIndividualStreams(source->streamInfos, dataStreams, deviceInfos->getLast(), continuousChannels); + } + } + else + { + for (int i = 0; i < Neuropixels2e::NumberOfProbes; i++) + { + if (npx->getProbeEnabled(i)) + addIndividualStream(npx->streamInfos[i], dataStreams, deviceInfos->getLast(), continuousChannels); + } + } } else if (source->getDeviceType() == OnixDeviceType::MEMORYMONITOR) { @@ -848,7 +865,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel deviceInfos->add(new DeviceInfo(deviceSettings)); - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); + addIndividualStreams(source->streamInfos, dataStreams, deviceInfos->getLast(), continuousChannels); } else if (source->getDeviceType() == OnixDeviceType::HARPSYNCINPUT) { @@ -862,7 +879,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel deviceInfos->add(new DeviceInfo(deviceSettings)); - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); + addIndividualStreams(source->streamInfos, dataStreams, deviceInfos->getLast(), continuousChannels); } else if (source->getDeviceType() == OnixDeviceType::NEUROPIXELSV1E) { @@ -876,7 +893,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel deviceInfos->add(new DeviceInfo(deviceSettings)); - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); + addIndividualStreams(source->streamInfos, dataStreams, deviceInfos->getLast(), continuousChannels); } else if (source->getDeviceType() == OnixDeviceType::OUTPUTCLOCK) { @@ -900,7 +917,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel auto auxiliaryIO = std::static_pointer_cast(compositeDevice); - addIndividualStreams(auxiliaryIO->getAnalogIO()->streamInfos, dataStreams, deviceInfos, continuousChannels); + addIndividualStreams(auxiliaryIO->getAnalogIO()->streamInfos, dataStreams, deviceInfos->getLast(), continuousChannels); auto eventChannelSettings = auxiliaryIO->getDigitalIO()->getEventChannelSettings(dataStreams->getLast()); eventChannels->add(new EventChannel(eventChannelSettings)); @@ -927,12 +944,12 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel void OnixSource::addCombinedStreams(DataStream::Settings dataStreamSettings, Array streamInfos, OwnedArray* dataStreams, - OwnedArray* deviceInfos, + DeviceInfo* deviceInfo, OwnedArray* continuousChannels) { DataStream* stream = new DataStream(dataStreamSettings); dataStreams->add(stream); - stream->device = deviceInfos->getLast(); + stream->device = deviceInfo; for (const auto& streamInfo : streamInfos) { @@ -959,42 +976,50 @@ void OnixSource::addCombinedStreams(DataStream::Settings dataStreamSettings, } } -void OnixSource::addIndividualStreams(Array streamInfos, +void OnixSource::addIndividualStream(StreamInfo streamInfo, OwnedArray* dataStreams, - OwnedArray* deviceInfos, + DeviceInfo* deviceInfo, OwnedArray* continuousChannels) { - for (StreamInfo streamInfo : streamInfos) + DataStream::Settings streamSettings { - DataStream::Settings streamSettings - { - streamInfo.getName(), - streamInfo.getDescription(), - streamInfo.getStreamIdentifier(), - streamInfo.getSampleRate(), - true - }; + streamInfo.getName(), + streamInfo.getDescription(), + streamInfo.getStreamIdentifier(), + streamInfo.getSampleRate(), + true + }; + + DataStream* stream = new DataStream(streamSettings); + dataStreams->add(stream); + stream->device = deviceInfo; - DataStream* stream = new DataStream(streamSettings); - dataStreams->add(stream); - stream->device = deviceInfos->getLast(); + auto suffixes = streamInfo.getChannelNameSuffixes(); - 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()); + } +} - // 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()); - } +void OnixSource::addIndividualStreams(Array streamInfos, + OwnedArray* dataStreams, + DeviceInfo* deviceInfo, + OwnedArray* continuousChannels) +{ + for (StreamInfo streamInfo : streamInfos) + { + addIndividualStream(streamInfo, dataStreams, deviceInfo, continuousChannels); } } diff --git a/Source/OnixSource.h b/Source/OnixSource.h index c60b86c..6b10834 100644 --- a/Source/OnixSource.h +++ b/Source/OnixSource.h @@ -162,9 +162,10 @@ namespace OnixSourcePlugin static constexpr int BREAKOUT_BOARD_OFFSET = 0; - void addIndividualStreams(Array, OwnedArray*, OwnedArray*, OwnedArray*); + void addIndividualStreams(Array, OwnedArray*, DeviceInfo*, OwnedArray*); + void addIndividualStream(StreamInfo, OwnedArray*, DeviceInfo*, OwnedArray*); - void addCombinedStreams(DataStream::Settings, Array, OwnedArray*, OwnedArray*, OwnedArray*); + void addCombinedStreams(DataStream::Settings, Array, OwnedArray*, DeviceInfo*, OwnedArray*); std::string createContinuousChannelIdentifier(StreamInfo streamInfo, int channelNumber); diff --git a/Source/OnixSourceCanvas.cpp b/Source/OnixSourceCanvas.cpp index 40110f2..abe0966 100644 --- a/Source/OnixSourceCanvas.cpp +++ b/Source/OnixSourceCanvas.cpp @@ -94,7 +94,7 @@ void OnixSourceCanvas::addHub(std::string hubName, int offset) tab = addTopLevelTab(getTopLevelTabName(port, hubName), (int)port); - devices.emplace_back(std::make_shared("", hubName, passthroughIndex, context)); + devices.emplace_back(std::make_shared("Neuropixels 2.0e", hubName, passthroughIndex, context)); devices.emplace_back(std::make_shared("BNO055", hubName, passthroughIndex, context)); } @@ -152,10 +152,11 @@ void OnixSourceCanvas::populateSourceTabs(CustomTabComponent* tab, OnixDeviceVec } else if (device->getDeviceType() == 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); + for (int i = 0; i < Neuropixels2e::NumberOfProbes; i++) + { + auto npxv2eProbeInterface = std::make_shared(std::static_pointer_cast(device), i, editor, this); + addInterfaceToTab(Neuropixels2e::getName(i), tab, npxv2eProbeInterface); + } } else if (device->getDeviceType() == OnixDeviceType::POLLEDBNO) { diff --git a/Source/UI/InterfaceList.h b/Source/UI/InterfaceList.h index b747e9a..7d033dd 100644 --- a/Source/UI/InterfaceList.h +++ b/Source/UI/InterfaceList.h @@ -27,6 +27,6 @@ #include "DigitalIOInterface.h" #include "HarpSyncInputInterface.h" #include "NeuropixelsV1Interface.h" -#include "NeuropixelsV2eInterface.h" +#include "NeuropixelsV2eProbeInterface.h" #include "OutputClockInterface.h" #include "PolledBno055Interface.h" diff --git a/Source/UI/NeuropixelsV1Interface.cpp b/Source/UI/NeuropixelsV1Interface.cpp index eb5a02a..c74b5bb 100644 --- a/Source/UI/NeuropixelsV1Interface.cpp +++ b/Source/UI/NeuropixelsV1Interface.cpp @@ -43,7 +43,7 @@ NeuropixelsV1Interface::NeuropixelsV1Interface(std::shared_ptr d, { type = device->getDeviceType() == OnixDeviceType::NEUROPIXELSV1E ? SettingsInterface::Type::NEUROPIXELS1E_SETTINGS_INTERFACE : SettingsInterface::Type::NEUROPIXELS1F_SETTINGS_INTERFACE; - mode = SettingsInterface::VisualizationMode::ENABLE_VIEW; + mode = VisualizationMode::ENABLE_VIEW; probeBrowser = std::make_unique(this, 0); probeBrowser->setBounds(0, 0, 600, 600); diff --git a/Source/UI/NeuropixelsV2eInterface.cpp b/Source/UI/NeuropixelsV2eInterface.cpp deleted file mode 100644 index ad4416e..0000000 --- a/Source/UI/NeuropixelsV2eInterface.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* - ------------------------------------------------------------------ - - 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 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 . - -*/ - -#include "NeuropixelsV2eInterface.h" -#include "../OnixSourceEditor.h" -#include "../OnixSourceCanvas.h" - -using namespace OnixSourcePlugin; - -NeuropixelsV2eInterface::NeuropixelsV2eInterface(std::shared_ptr d, OnixSourceEditor* e, OnixSourceCanvas* c) : - SettingsInterface(d, e, c) -{ - topLevelTabComponent = std::make_unique(true); - addAndMakeVisible(topLevelTabComponent.get()); - - probeInterfaces[0] = std::make_unique(d, 0, e, c); - topLevelTabComponent->addTab("Probe0", Colours::grey, CustomViewport::createCustomViewport(probeInterfaces[0].get()), true, 0); - - probeInterfaces[1] = std::make_unique(d, 1, e, c); - topLevelTabComponent->addTab("Probe1", Colours::grey, CustomViewport::createCustomViewport(probeInterfaces[1].get()), true, 1); - - deviceComponent = std::make_unique(); - deviceComponent->setBounds(0, 0, 600, 40); - - deviceEnableButton = std::make_unique(enabledButtonText); - deviceEnableButton->setFont(FontOptions("Fira Code", "Regular", 12.0f)); - deviceEnableButton->setRadius(3.0f); - deviceEnableButton->setBounds(10, 15, 100, 22); - deviceEnableButton->setClickingTogglesState(true); - deviceEnableButton->setToggleState(device->isEnabled(), dontSendNotification); - deviceEnableButton->setTooltip("If disabled, probe will not stream data during acquisition"); - deviceEnableButton->addListener(this); - deviceComponent->addAndMakeVisible(deviceEnableButton.get()); - - addAndMakeVisible(deviceComponent.get()); - - setBounds(0, 0, 1000, 800); - - type = Type::NEUROPIXELS2E_SETTINGS_INTERFACE; -} - -void NeuropixelsV2eInterface::resized() -{ - topLevelTabComponent->setBounds(0, 50, canvas->getWidth() * 0.99, canvas->getHeight() - 50); -} - -void NeuropixelsV2eInterface::updateInfoString() -{ - for (const auto& probeInterface : probeInterfaces) - { - probeInterface->updateInfoString(); - } -} - -void NeuropixelsV2eInterface::buttonClicked(Button* button) -{ - if (button == deviceEnableButton.get()) - { - device->setEnabled(deviceEnableButton->getToggleState()); - - if (canvas->foundInputSource()) - { - try - { - device->configureDevice(); - } - catch (const error_str& e) - { - LOGE(e.what()); - button->setToggleState(!button->getToggleState(), dontSendNotification); - return; - } - - canvas->resetContext(); - } - - if (device->isEnabled()) - { - deviceEnableButton->setLabel(enabledButtonText); - } - else - { - deviceEnableButton->setLabel(disabledButtonText); - } - - updateInfoString(); - repaint(); - - CoreServices::updateSignalChain(editor); - } -} - -void NeuropixelsV2eInterface::updateSettings() -{ - if (device == nullptr) return; - - deviceEnableButton->setToggleState(device->isEnabled(), sendNotification); - - for (const auto& probeInterface : probeInterfaces) - { - probeInterface->updateSettings(); - } -} - -void NeuropixelsV2eInterface::setInterfaceEnabledState(bool newState) -{ - if (device == nullptr) return; - - if (deviceEnableButton != nullptr) - deviceEnableButton->setEnabled(newState); - - for (const auto& probeInterface : probeInterfaces) - { - probeInterface->setInterfaceEnabledState(newState); - } -} - -void NeuropixelsV2eInterface::saveParameters(XmlElement* xml) -{ - if (device == nullptr) return; - - LOGD("Saving NeuropixelsV2e settings."); - - XmlElement* xmlNode = xml->createNewChildElement("NEUROPIXELSV2E"); - - xmlNode->setAttribute("idx", (int)device->getDeviceIdx()); - - xmlNode->setAttribute("isEnabled", device->isEnabled()); - - for (const auto& probeInterface : probeInterfaces) - { - probeInterface->saveParameters(xmlNode); - } -} - -void NeuropixelsV2eInterface::loadParameters(XmlElement* xml) -{ - if (device == nullptr) return; - - LOGD("Loading NeuropixelsV2e settings."); - - XmlElement* xmlNode = nullptr; - - for (auto* node : xml->getChildIterator()) - { - if (node->hasTagName("NEUROPIXELSV2E")) - { - if (node->getIntAttribute("idx") == device->getDeviceIdx()) - { - xmlNode = node; - break; - } - } - } - - if (xmlNode == nullptr) - { - LOGD("No NeuropixelsV2e element found."); - return; - } - - device->setEnabled(xmlNode->getBoolAttribute("isEnabled")); - - for (const auto& probeInterface : probeInterfaces) - { - probeInterface->loadParameters(xmlNode); - } - - updateSettings(); -} diff --git a/Source/UI/NeuropixelsV2eInterface.h b/Source/UI/NeuropixelsV2eInterface.h deleted file mode 100644 index f63973e..0000000 --- a/Source/UI/NeuropixelsV2eInterface.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - ------------------------------------------------------------------ - - 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 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 . - -*/ - -#pragma once - -#include "NeuropixelsV2eProbeInterface.h" - -namespace OnixSourcePlugin -{ - class OnixSourceCanvas; - class OnixSourceEditor; - - class NeuropixelsV2eInterface : public SettingsInterface, - public Button::Listener - { - public: - NeuropixelsV2eInterface(std::shared_ptr d, OnixSourceEditor* e, OnixSourceCanvas* c); - - void saveParameters(XmlElement* xml) override; - - void loadParameters(XmlElement* xml) override; - - void updateInfoString() override; - - void resized() override; - - void buttonClicked(Button* b) override; - - void updateSettings() override; - - private: - - void setInterfaceEnabledState(bool newState) override; - - std::unique_ptr topLevelTabComponent; - - std::unique_ptr deviceEnableButton; - std::unique_ptr deviceComponent; - - static const int numProbes = 2; - - std::array, numProbes> probeInterfaces; - }; -} diff --git a/Source/UI/NeuropixelsV2eProbeBrowser.h b/Source/UI/NeuropixelsV2eProbeBrowser.h index 229191c..71ee85d 100644 --- a/Source/UI/NeuropixelsV2eProbeBrowser.h +++ b/Source/UI/NeuropixelsV2eProbeBrowser.h @@ -38,7 +38,22 @@ namespace OnixSourcePlugin ProbeSettings* getSettings() override { - return std::static_pointer_cast(parent->getDevice())->settings[probeIndex].get(); + return std::static_pointer_cast(parent->getDevice())->settings[m_probeIndex].get(); } + + protected: + + bool isProbeEnabled() override + { + auto device = parent->getDevice(); + + if (device == nullptr) + return false; + + auto npx = std::static_pointer_cast(device); + + return npx->getProbeEnabled(m_probeIndex); + } + }; } diff --git a/Source/UI/NeuropixelsV2eProbeInterface.cpp b/Source/UI/NeuropixelsV2eProbeInterface.cpp index b1c6a6b..fca02dd 100644 --- a/Source/UI/NeuropixelsV2eProbeInterface.cpp +++ b/Source/UI/NeuropixelsV2eProbeInterface.cpp @@ -32,19 +32,20 @@ using namespace ColourScheme; NeuropixelsV2eProbeInterface::NeuropixelsV2eProbeInterface(std::shared_ptr d, int ind, OnixSourceEditor* e, OnixSourceCanvas* c) : SettingsInterface(d, e, c), - probeIndex(ind) + m_probeIndex(ind) { ColourScheme::setColourScheme(ColourSchemeId::PLASMA); if (device != nullptr) { - auto settings = std::static_pointer_cast(device)->settings[probeIndex].get(); + auto npx = std::static_pointer_cast(device); + auto settings = npx->settings[m_probeIndex].get(); type = SettingsInterface::Type::PROBE_SETTINGS_INTERFACE; mode = VisualizationMode::ENABLE_VIEW; - probeBrowser = std::make_unique(this, probeIndex); + probeBrowser = std::make_unique(this, m_probeIndex); probeBrowser->setBounds(0, 0, 600, 600); addAndMakeVisible(probeBrowser.get()); @@ -59,12 +60,24 @@ NeuropixelsV2eProbeInterface::NeuropixelsV2eProbeInterface(std::shared_ptrsetBounds(625, 40, 430, 45); addAndMakeVisible(deviceLabel.get()); + deviceEnableButton = std::make_unique(enabledButtonText); + deviceEnableButton->setFont(fontRegularButton); + deviceEnableButton->setRadius(3.0f); + deviceEnableButton->setBounds(deviceLabel->getX(), deviceLabel->getBottom() + 3, 100, 22); + deviceEnableButton->setClickingTogglesState(true); + deviceEnableButton->setTooltip("If disabled, probe will not stream data during acquisition"); + deviceEnableButton->setToggleState(true, dontSendNotification); + deviceEnableButton->addListener(this); + addAndMakeVisible(deviceEnableButton.get()); + infoLabel = std::make_unique