diff --git a/Source/Devices/AnalogIO.cpp b/Source/Devices/AnalogIO.cpp
new file mode 100644
index 0000000..86c4bf6
--- /dev/null
+++ b/Source/Devices/AnalogIO.cpp
@@ -0,0 +1,190 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "AnalogIO.h"
+
+AnalogIO::AnalogIO(String name, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx)
+ : OnixDevice(name, OnixDeviceType::ANALOGIO, deviceIdx_, oni_ctx)
+{
+ StreamInfo analogInputStream = StreamInfo(
+ name + "-AnalogInput",
+ "Analog Input data",
+ "onix-analogio.data.input",
+ 12,
+ std::floor(AnalogIOFrequencyHz / framesToAverage),
+ "AnalogInput",
+ ContinuousChannel::Type::ADC,
+ 20.0f / numberOfDivisions, // NB: +/- 10 Volts
+ "V",
+ {});
+ streamInfos.add(analogInputStream);
+
+ for (int i = 0; i < numFrames; i++)
+ eventCodes[i] = 0;
+
+ for (int i = 0; i < numChannels; i += 1)
+ {
+ channelDirection[i] = AnalogIODirection::Input;
+ channelVoltageRange[i] = AnalogIOVoltageRange::TenVolts;
+ }
+
+ dataType = AnalogIODataType::Volts;
+}
+
+int AnalogIO::configureDevice()
+{
+ if (deviceContext == nullptr || !deviceContext->isInitialized()) return -1;
+
+ deviceContext->writeRegister(deviceIdx, (uint32_t)AnalogIORegisters::ENABLE, (oni_reg_val_t)(isEnabled() ? 1 : 0));
+
+ return deviceContext->getLastResult();
+}
+
+bool AnalogIO::updateSettings()
+{
+ for (int i = 0; i < numChannels; i += 1)
+ {
+ deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)AnalogIORegisters::CH00_IN_RANGE + i, (oni_reg_val_t)channelVoltageRange[i]);
+ if (deviceContext->getLastResult() != ONI_ESUCCESS) return false;
+ }
+
+ uint32_t ioReg = 0;
+
+ for (int i = 0; i < numChannels; i += 1)
+ {
+ ioReg = (ioReg & ~((uint32_t)1 << i)) | ((uint32_t)(channelDirection[i]) << i);
+ }
+
+ deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)AnalogIORegisters::CHDIR, ioReg);
+ if (deviceContext->getLastResult() != ONI_ESUCCESS) return false;
+
+ for (int i = 0; i < numChannels; i += 1)
+ {
+ voltsPerDivision[i] = getVoltsPerDivision(channelVoltageRange[i]);
+ }
+
+ return true;
+}
+
+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;
+ }
+}
+
+void AnalogIO::startAcquisition()
+{
+ currentFrame = 0;
+ currentAverageFrame = 0;
+ sampleNumber = 0;
+
+ analogInputSamples.fill(0);
+}
+
+void AnalogIO::stopAcquisition()
+{
+ while (!frameArray.isEmpty())
+ {
+ const GenericScopedLock frameLock(frameArray.getLock());
+ oni_destroy_frame(frameArray.removeAndReturn(0));
+ }
+}
+
+void AnalogIO::addFrame(oni_frame_t* frame)
+{
+ const GenericScopedLock frameLock(frameArray.getLock());
+ frameArray.add(frame);
+}
+
+void AnalogIO::addSourceBuffers(OwnedArray& sourceBuffers)
+{
+ for (StreamInfo streamInfo : streamInfos)
+ {
+ sourceBuffers.add(new DataBuffer(streamInfo.getNumChannels(), (int)streamInfo.getSampleRate() * bufferSizeInSeconds));
+
+ if (streamInfo.getChannelPrefix().equalsIgnoreCase("AnalogInput"))
+ analogInputBuffer = sourceBuffers.getLast();
+ }
+}
+
+void AnalogIO::processFrames()
+{
+ while (!frameArray.isEmpty())
+ {
+ const GenericScopedLock frameLock(frameArray.getLock());
+ oni_frame_t* frame = frameArray.removeAndReturn(0);
+
+ int16_t* dataPtr = (int16_t*)frame->data;
+
+ int dataOffset = 4;
+
+ for (int i = 0; i < numChannels; i += 1)
+ {
+ if (dataType == AnalogIODataType::S16)
+ analogInputSamples[currentFrame + i * numFrames] += *(dataPtr + dataOffset + i);
+ else
+ analogInputSamples[currentFrame + i * numFrames] += *(dataPtr + dataOffset + i) * voltsPerDivision[i];
+ }
+
+ currentAverageFrame += 1;
+
+ if (currentAverageFrame >= framesToAverage)
+ {
+ for (int i = 0; i < numChannels; i += 1)
+ {
+ analogInputSamples[currentFrame + i * numFrames] /= framesToAverage;
+ }
+
+ currentAverageFrame = 0;
+
+ timestamps[currentFrame] = frame->time;
+ sampleNumbers[currentFrame] = sampleNumber++;
+
+ currentFrame++;
+ }
+
+ oni_destroy_frame(frame);
+
+ if (currentFrame >= numFrames)
+ {
+ shouldAddToBuffer = true;
+ currentFrame = 0;
+ }
+
+ if (shouldAddToBuffer)
+ {
+ shouldAddToBuffer = false;
+ analogInputBuffer->addToBuffer(analogInputSamples.data(), sampleNumbers, timestamps, eventCodes, numFrames);
+
+ analogInputSamples.fill(0);
+ }
+ }
+}
diff --git a/Source/Devices/AnalogIO.h b/Source/Devices/AnalogIO.h
new file mode 100644
index 0000000..d74466a
--- /dev/null
+++ b/Source/Devices/AnalogIO.h
@@ -0,0 +1,178 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "../OnixDevice.h"
+
+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(String name, const oni_dev_idx_t, std::shared_ptr oni_ctx);
+
+ /** Configures the device so that it is ready to stream with default settings */
+ 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;
+
+ /** Given the sourceBuffers from OnixSource, add all streams for the current device to the array */
+ void addSourceBuffers(OwnedArray& sourceBuffers) override;
+
+ void addFrame(oni_frame_t*) override;
+
+ void processFrames() override;
+
+ AnalogIODirection getChannelDirection(int channelNumber)
+ {
+ if (channelNumber > numChannels || channelNumber < 0)
+ {
+ LOGE("Channel number must be between 0 and " + String(channelNumber));
+ return AnalogIODirection::Input;
+ }
+
+ return channelDirection[channelNumber];
+ }
+
+ void setChannelDirection(int channelNumber, AnalogIODirection direction)
+ {
+ if (channelNumber > numChannels || channelNumber < 0)
+ {
+ LOGE("Channel number must be between 0 and " + String(channelNumber));
+ return;
+ }
+
+ channelDirection[channelNumber] = direction;
+ }
+
+ AnalogIOVoltageRange getChannelVoltageRange(int channelNumber)
+ {
+ if (channelNumber > numChannels || channelNumber < 0)
+ {
+ LOGE("Channel number must be between 0 and " + String(channelNumber));
+ return AnalogIOVoltageRange::FiveVolts;
+ }
+
+ return channelVoltageRange[channelNumber];
+ }
+
+ void setChannelVoltageRange(int channelNumber, AnalogIOVoltageRange direction)
+ {
+ if (channelNumber > numChannels || channelNumber < 0)
+ {
+ LOGE("Channel number must be between 0 and " + String(channelNumber));
+ return;
+ }
+
+ channelVoltageRange[channelNumber] = direction;
+ }
+
+ AnalogIODataType getDataType() const { return dataType; }
+
+ void setDataType(AnalogIODataType type) { dataType = type; }
+
+ int getNumChannels() { return numChannels; }
+
+private:
+
+ DataBuffer* analogInputBuffer = nullptr;
+
+ static const int AnalogIOFrequencyHz = 100000;
+
+ static const int numFrames = 25;
+ static const int framesToAverage = 4; // NB: Downsampling from 100 kHz to 25 kHz
+ static const int numChannels = 12;
+
+ static const int numberOfDivisions = 1 << 16;
+ const int dacMidScale = 1 << 15;
+
+ std::array channelDirection;
+ std::array channelVoltageRange;
+
+ AnalogIODataType dataType = AnalogIODataType::Volts;
+
+ Array frameArray;
+
+ unsigned short currentFrame = 0;
+ unsigned short currentAverageFrame = 0;
+ int sampleNumber = 0;
+
+ bool shouldAddToBuffer = false;
+
+ std::array analogInputSamples;
+
+ double timestamps[numFrames];
+ int64 sampleNumbers[numFrames];
+ uint64 eventCodes[numFrames];
+
+ std::array voltsPerDivision;
+
+ static float getVoltsPerDivision(AnalogIOVoltageRange voltageRange);
+
+ JUCE_LEAK_DETECTOR(AnalogIO);
+};
diff --git a/Source/Devices/Bno055.cpp b/Source/Devices/Bno055.cpp
index b73920b..49cc3e3 100644
--- a/Source/Devices/Bno055.cpp
+++ b/Source/Devices/Bno055.cpp
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -39,7 +38,7 @@ Bno055::Bno055(String name, const oni_dev_idx_t deviceIdx_, std::shared_ptrisInitialized()) return -1;
@@ -155,7 +150,7 @@ void Bno055::processFrames()
int16_t* dataPtr = (int16_t*)frame->data;
- bnoTimestamps[currentFrame] = *(uint64_t*)frame->data;
+ bnoTimestamps[currentFrame] = frame->time;
int dataOffset = 4;
diff --git a/Source/Devices/Bno055.h b/Source/Devices/Bno055.h
index aaa0044..9f4c6cf 100644
--- a/Source/Devices/Bno055.h
+++ b/Source/Devices/Bno055.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,8 +20,7 @@
*/
-#ifndef BNO055_H_DEFINED
-#define BNO055_H_DEFINED
+#pragma once
#include "../OnixDevice.h"
@@ -31,6 +29,9 @@ enum class Bno055Registers
ENABLE = 0x00
};
+/*
+ Configures and streams data from a BNO055 device
+*/
class Bno055 : public OnixDevice
{
public:
@@ -38,9 +39,6 @@ class Bno055 : public OnixDevice
/** Constructor */
Bno055(String name, const oni_dev_idx_t, std::shared_ptr ctx);
- /** Destructor */
- ~Bno055();
-
int configureDevice() override;
/** Update the settings of the device */
@@ -82,5 +80,3 @@ class Bno055 : public OnixDevice
JUCE_LEAK_DETECTOR(Bno055);
};
-
-#endif
diff --git a/Source/Devices/DS90UB9x.h b/Source/Devices/DS90UB9x.h
index 57ca7b7..93463b9 100644
--- a/Source/Devices/DS90UB9x.h
+++ b/Source/Devices/DS90UB9x.h
@@ -1,4 +1,27 @@
+/*
+ ------------------------------------------------------------------
+
+ 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
static class DS90UB9x
diff --git a/Source/Devices/DeviceList.h b/Source/Devices/DeviceList.h
index d4ed212..30ebb78 100644
--- a/Source/Devices/DeviceList.h
+++ b/Source/Devices/DeviceList.h
@@ -1,6 +1,34 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "Bno055.h"
#include "DS90UB9x.h"
#include "HeadStageEEPROM.h"
#include "Neuropixels_1.h"
#include "Neuropixels2e.h"
+#include "MemoryMonitor.h"
+#include "OutputClock.h"
+#include "Heartbeat.h"
+#include "HarpSyncInput.h"
+#include "AnalogIO.h"
+#include "DigitalIO.h"
#include "PortController.h"
diff --git a/Source/Devices/DigitalIO.cpp b/Source/Devices/DigitalIO.cpp
new file mode 100644
index 0000000..32dff0f
--- /dev/null
+++ b/Source/Devices/DigitalIO.cpp
@@ -0,0 +1,119 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "DigitalIO.h"
+
+DigitalIO::DigitalIO(String name, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx)
+ : OnixDevice(name, OnixDeviceType::DIGITALIO, deviceIdx_, oni_ctx)
+{
+}
+
+int DigitalIO::configureDevice()
+{
+ if (deviceContext == nullptr || !deviceContext->isInitialized()) return -1;
+
+ deviceContext->writeRegister(deviceIdx, (uint32_t)DigitalIORegisters::ENABLE, (oni_reg_val_t)(isEnabled() ? 1 : 0));
+
+ return deviceContext->getLastResult();
+}
+
+bool DigitalIO::updateSettings()
+{
+ return true;
+}
+
+void DigitalIO::startAcquisition()
+{
+}
+
+void DigitalIO::stopAcquisition()
+{
+ while (!frameArray.isEmpty())
+ {
+ const GenericScopedLock frameLock(frameArray.getLock());
+ oni_destroy_frame(frameArray.removeAndReturn(0));
+ }
+}
+
+EventChannel::Settings DigitalIO::getEventChannelSettings()
+{
+ // NB: The stream must be assigned before adding the channel
+ EventChannel::Settings settings{
+ EventChannel::Type::TTL,
+ "DigitalIO Event Channel",
+ "Digital inputs and breakout button states coming from a DigitalIO device",
+ "onix-digitalio.events",
+ nullptr,
+ numButtons + numDigitalInputs
+ };
+
+ return settings;
+}
+
+void DigitalIO::addFrame(oni_frame_t* frame)
+{
+ const GenericScopedLock frameLock(frameArray.getLock());
+ frameArray.add(frame);
+}
+
+void DigitalIO::processFrames()
+{
+ while (!frameArray.isEmpty())
+ {
+ const GenericScopedLock frameLock(frameArray.getLock());
+ const GenericScopedLock digitalInputsLock(eventWords.getLock());
+ oni_frame_t* frame = frameArray.removeAndReturn(0);
+
+ uint16_t* dataPtr = (uint16_t*)frame->data;
+ uint64_t timestamp = frame->time;
+
+ int dataOffset = 4;
+
+ uint64_t portState = *(dataPtr + dataOffset);
+ uint64_t buttonState = *(dataPtr + dataOffset + 1);
+
+ if (portState != 0 || buttonState != 0)
+ {
+ uint64_t ttlEventWord = (portState & 255) << 6 | (buttonState & 63);
+ eventWords.add(ttlEventWord);
+ }
+
+ oni_destroy_frame(frame);
+ }
+}
+
+uint64_t DigitalIO::getEventWord()
+{
+ const GenericScopedLock digitalInputsLock(eventWords.getLock());
+
+ if (eventWords.size() != 0)
+ return eventWords.removeAndReturn(0);
+
+ return 0;
+}
+
+bool DigitalIO::hasEventWord()
+{
+ const GenericScopedLock digitalInputsLock(eventWords.getLock());
+
+ return eventWords.size() > 0;
+}
diff --git a/Source/Devices/DigitalIO.h b/Source/Devices/DigitalIO.h
new file mode 100644
index 0000000..3e9d02c
--- /dev/null
+++ b/Source/Devices/DigitalIO.h
@@ -0,0 +1,102 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "../OnixDevice.h"
+
+enum class DigitalIORegisters : uint32_t
+{
+ ENABLE = 0
+};
+
+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
+{
+ 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 an AnalogIO device on a Breakout Board
+*/
+class DigitalIO : public OnixDevice
+{
+public:
+ DigitalIO(String name, const oni_dev_idx_t, std::shared_ptr oni_ctx);
+
+ /** Configures the device so that it is ready to stream with default settings */
+ 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;
+
+ /** Given the sourceBuffers from OnixSource, add all streams for the current device to the array */
+ void addSourceBuffers(OwnedArray& sourceBuffers) override {};
+
+ EventChannel::Settings getEventChannelSettings();
+
+ void addFrame(oni_frame_t*) override;
+
+ void processFrames() override;
+
+ uint64_t getEventWord();
+
+ bool hasEventWord();
+
+private:
+
+ static const int numDigitalInputs = 8;
+ static const int numButtons = 6;
+
+ Array frameArray;
+ Array eventWords;
+
+ JUCE_LEAK_DETECTOR(DigitalIO);
+};
diff --git a/Source/Devices/HarpSyncInput.cpp b/Source/Devices/HarpSyncInput.cpp
new file mode 100644
index 0000000..1af5d66
--- /dev/null
+++ b/Source/Devices/HarpSyncInput.cpp
@@ -0,0 +1,126 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "HarpSyncInput.h"
+
+HarpSyncInput::HarpSyncInput(String name, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx)
+ : OnixDevice(name, OnixDeviceType::HARPSYNCINPUT, deviceIdx_, oni_ctx)
+{
+ setEnabled(false);
+
+ StreamInfo harpTimeStream = StreamInfo(
+ name + "-HarpTime",
+ "Harp clock time corresponding to the local acquisition ONIX clock count",
+ "onix-harpsyncinput.data.harptime",
+ 1,
+ 1,
+ "HarpTime",
+ ContinuousChannel::Type::AUX,
+ 1.0f,
+ "s",
+ {""});
+ streamInfos.add(harpTimeStream);
+
+ for (int i = 0; i < numFrames; i++)
+ eventCodes[i] = 0;
+}
+
+int HarpSyncInput::configureDevice()
+{
+ if (deviceContext == nullptr || !deviceContext->isInitialized()) return -1;
+
+ deviceContext->writeRegister(deviceIdx, (uint32_t)HarpSyncInputRegisters::ENABLE, (oni_reg_val_t)(isEnabled() ? 1 : 0));
+
+ return deviceContext->getLastResult();
+}
+
+bool HarpSyncInput::updateSettings()
+{
+ deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)HarpSyncInputRegisters::SOURCE, (oni_reg_val_t)HarpSyncSource::Breakout);
+
+ return deviceContext->getLastResult() == ONI_ESUCCESS;
+}
+
+void HarpSyncInput::startAcquisition()
+{
+ currentFrame = 0;
+ sampleNumber = 0;
+}
+
+void HarpSyncInput::stopAcquisition()
+{
+ while (!frameArray.isEmpty())
+ {
+ const GenericScopedLock frameLock(frameArray.getLock());
+ oni_destroy_frame(frameArray.removeAndReturn(0));
+ }
+}
+
+void HarpSyncInput::addFrame(oni_frame_t* frame)
+{
+ const GenericScopedLock frameLock(frameArray.getLock());
+ frameArray.add(frame);
+}
+
+void HarpSyncInput::addSourceBuffers(OwnedArray& sourceBuffers)
+{
+ for (StreamInfo streamInfo : streamInfos)
+ {
+ sourceBuffers.add(new DataBuffer(streamInfo.getNumChannels(), (int)streamInfo.getSampleRate() * bufferSizeInSeconds));
+
+ if (streamInfo.getChannelPrefix().equalsIgnoreCase("HarpTime"))
+ harpTimeBuffer = sourceBuffers.getLast();
+ }
+}
+
+void HarpSyncInput::processFrames()
+{
+ while (!frameArray.isEmpty())
+ {
+ const GenericScopedLock frameLock(frameArray.getLock());
+ oni_frame_t* frame = frameArray.removeAndReturn(0);
+
+ uint32_t* dataPtr = (uint32_t*)frame->data;
+
+ timestamps[currentFrame] = frame->time;
+
+ harpTimeSamples[currentFrame] = *(dataPtr + 2) + 1;
+
+ oni_destroy_frame(frame);
+
+ sampleNumbers[currentFrame] = sampleNumber++;
+
+ currentFrame++;
+
+ if (currentFrame >= numFrames)
+ {
+ shouldAddToBuffer = true;
+ currentFrame = 0;
+ }
+
+ if (shouldAddToBuffer)
+ {
+ shouldAddToBuffer = false;
+ harpTimeBuffer->addToBuffer(harpTimeSamples, sampleNumbers, timestamps, eventCodes, numFrames);
+ }
+ }
+}
diff --git a/Source/Devices/HarpSyncInput.h b/Source/Devices/HarpSyncInput.h
new file mode 100644
index 0000000..cabdc2d
--- /dev/null
+++ b/Source/Devices/HarpSyncInput.h
@@ -0,0 +1,86 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "../OnixDevice.h"
+
+enum class HarpSyncInputRegisters : uint32_t
+{
+ ENABLE = 0,
+ SOURCE = 1
+};
+
+enum class HarpSyncSource : uint32_t
+{
+ Breakout = 0,
+ ClockAdapter = 1
+};
+
+/*
+ Configures and streams data from a HarpSyncInput device on a Breakout Board
+*/
+class HarpSyncInput : public OnixDevice
+{
+public:
+ HarpSyncInput(String name, const oni_dev_idx_t, std::shared_ptr oni_ctx);
+
+ /** Configures the device so that it is ready to stream with default settings */
+ 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;
+
+ /** Given the sourceBuffers from OnixSource, add all streams for the current device to the array */
+ void addSourceBuffers(OwnedArray& sourceBuffers) override;
+
+ void addFrame(oni_frame_t* frame) override;
+
+ void processFrames() override;
+
+private:
+
+ DataBuffer* harpTimeBuffer;
+
+ static const int numFrames = 2;
+
+ Array frameArray;
+
+ unsigned short currentFrame = 0;
+ int sampleNumber = 0;
+
+ bool shouldAddToBuffer = false;
+
+ float harpTimeSamples[numFrames];
+
+ double timestamps[numFrames];
+ int64 sampleNumbers[numFrames];
+ uint64 eventCodes[numFrames];
+
+ JUCE_LEAK_DETECTOR(HarpSyncInput);
+};
diff --git a/Source/Devices/HeadStageEEPROM.cpp b/Source/Devices/HeadStageEEPROM.cpp
index 1ae4cc4..7f2577c 100644
--- a/Source/Devices/HeadStageEEPROM.cpp
+++ b/Source/Devices/HeadStageEEPROM.cpp
@@ -1,3 +1,25 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "HeadStageEEPROM.h"
#include "DS90UB9x.h"
#include
diff --git a/Source/Devices/HeadStageEEPROM.h b/Source/Devices/HeadStageEEPROM.h
index c5936db..c211a0c 100644
--- a/Source/Devices/HeadStageEEPROM.h
+++ b/Source/Devices/HeadStageEEPROM.h
@@ -1,3 +1,26 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "../I2CRegisterContext.h"
diff --git a/Source/Devices/Heartbeat.cpp b/Source/Devices/Heartbeat.cpp
new file mode 100644
index 0000000..6ddd99f
--- /dev/null
+++ b/Source/Devices/Heartbeat.cpp
@@ -0,0 +1,49 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "Heartbeat.h"
+
+Heartbeat::Heartbeat(String name, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx)
+ : OnixDevice(name, OnixDeviceType::HEARTBEAT, deviceIdx_, oni_ctx)
+{
+}
+
+int Heartbeat::configureDevice()
+{
+ setEnabled(true);
+
+ if (deviceContext == nullptr || !deviceContext->isInitialized()) return -1;
+
+ deviceContext->writeRegister(deviceIdx, (uint32_t)HeartbeatRegisters::ENABLE, 1);
+
+ return deviceContext->getLastResult();
+}
+
+bool Heartbeat::updateSettings()
+{
+ oni_reg_val_t clkHz = deviceContext->readRegister(deviceIdx, (oni_reg_addr_t)HeartbeatRegisters::CLK_HZ);
+ deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)HeartbeatRegisters::CLK_DIV, clkHz / beatsPerSecond);
+
+ if (deviceContext->getLastResult() == ONI_EREADONLY) return true; // NB: Ignore read-only errors
+
+ return deviceContext->getLastResult() == ONI_ESUCCESS;
+}
diff --git a/Source/Devices/Heartbeat.h b/Source/Devices/Heartbeat.h
new file mode 100644
index 0000000..e23bcfc
--- /dev/null
+++ b/Source/Devices/Heartbeat.h
@@ -0,0 +1,66 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "../OnixDevice.h"
+
+enum class HeartbeatRegisters : uint32_t
+{
+ ENABLE = 0,
+ CLK_DIV = 1,
+ CLK_HZ = 2
+};
+
+/*
+ Configures and streams data from a MemoryMonitor device on a Breakout Board
+*/
+class Heartbeat : public OnixDevice
+{
+public:
+ Heartbeat(String name, const oni_dev_idx_t, std::shared_ptr oni_ctx);
+
+ /** Configures the device so that it is ready to stream with default settings */
+ 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 {};
+
+ /** Given the sourceBuffers from OnixSource, add all streams for the current device to the array */
+ void addSourceBuffers(OwnedArray& sourceBuffers) override {};
+
+ void addFrame(oni_frame_t* frame) override { oni_destroy_frame(frame); }
+
+ void processFrames() override {};
+
+private:
+
+ const uint32_t beatsPerSecond = 100;
+
+ JUCE_LEAK_DETECTOR(Heartbeat);
+};
diff --git a/Source/Devices/MemoryMonitor.cpp b/Source/Devices/MemoryMonitor.cpp
new file mode 100644
index 0000000..b631305
--- /dev/null
+++ b/Source/Devices/MemoryMonitor.cpp
@@ -0,0 +1,178 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "MemoryMonitor.h"
+#include "DigitalIO.h"
+
+MemoryMonitorUsage::MemoryMonitorUsage(GenericProcessor* p)
+ : LevelMonitor(p)
+{
+ device = nullptr;
+}
+
+void MemoryMonitorUsage::timerCallback()
+{
+ if (device != nullptr)
+ {
+ setFillPercentage(std::log(device->getLastPercentUsedValue() + 1) / maxLogarithmicValue);
+ repaint();
+ }
+}
+
+void MemoryMonitorUsage::setMemoryMonitor(std::shared_ptr memoryMonitor)
+{
+ device = memoryMonitor;
+}
+
+void MemoryMonitorUsage::startAcquisition()
+{
+ startTimerHz(TimerFrequencyHz);
+}
+
+void MemoryMonitorUsage::stopAcquisition()
+{
+ stopTimer();
+ setFillPercentage(0.0f);
+ repaint();
+}
+
+MemoryMonitor::MemoryMonitor(String name, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx)
+ : OnixDevice(name, OnixDeviceType::MEMORYMONITOR, deviceIdx_, oni_ctx)
+{
+ StreamInfo percentUsedStream = StreamInfo(
+ name + "-PercentUsed",
+ "Percent of available memory that is currently used",
+ "onix - memorymonitor.data.percentused",
+ 1,
+ samplesPerSecond,
+ "Percent",
+ ContinuousChannel::Type::AUX,
+ 1.0f,
+ "%",
+ {""});
+ streamInfos.add(percentUsedStream);
+
+ for (int i = 0; i < numFrames; i++)
+ eventCodes[i] = 0;
+}
+
+int MemoryMonitor::configureDevice()
+{
+ setEnabled(true);
+
+ if (deviceContext == nullptr || !deviceContext->isInitialized()) return -1;
+
+ deviceContext->writeRegister(deviceIdx, (uint32_t)MemoryMonitorRegisters::ENABLE, 1);
+ if (deviceContext->getLastResult() != ONI_ESUCCESS) return deviceContext->getLastResult();
+
+ totalMemory = deviceContext->readRegister(deviceIdx, (oni_reg_addr_t)MemoryMonitorRegisters::TOTAL_MEM);
+
+ return deviceContext->getLastResult();
+}
+
+bool MemoryMonitor::updateSettings()
+{
+ oni_reg_val_t clkHz = deviceContext->readRegister(deviceIdx, (oni_reg_addr_t)MemoryMonitorRegisters::CLK_HZ);
+
+ deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)MemoryMonitorRegisters::CLK_DIV, clkHz / samplesPerSecond);
+
+ return deviceContext->getLastResult() == ONI_ESUCCESS;
+}
+
+void MemoryMonitor::startAcquisition()
+{
+ currentFrame = 0;
+ sampleNumber = 0;
+
+ lastPercentUsedValue = 0.0f;
+}
+
+void MemoryMonitor::stopAcquisition()
+{
+ while (!frameArray.isEmpty())
+ {
+ const GenericScopedLock frameLock(frameArray.getLock());
+ oni_destroy_frame(frameArray.removeAndReturn(0));
+ }
+}
+
+void MemoryMonitor::addFrame(oni_frame_t* frame)
+{
+ const GenericScopedLock frameLock(frameArray.getLock());
+ frameArray.add(frame);
+}
+
+void MemoryMonitor::addSourceBuffers(OwnedArray& sourceBuffers)
+{
+ for (StreamInfo streamInfo : streamInfos)
+ {
+ sourceBuffers.add(new DataBuffer(streamInfo.getNumChannels(), (int)streamInfo.getSampleRate() * bufferSizeInSeconds));
+
+ if (streamInfo.getChannelPrefix().equalsIgnoreCase("Percent"))
+ percentUsedBuffer = sourceBuffers.getLast();
+ }
+}
+
+float MemoryMonitor::getLastPercentUsedValue()
+{
+ return lastPercentUsedValue;
+}
+
+void MemoryMonitor::processFrames()
+{
+ while (!frameArray.isEmpty())
+ {
+ const GenericScopedLock frameLock(frameArray.getLock());
+ oni_frame_t* frame = frameArray.removeAndReturn(0);
+
+ uint32_t* dataPtr = (uint32_t*)frame->data;
+ uint64_t timestamp = frame->time;
+
+ timestamps[currentFrame] = frame->time;
+
+ percentUsedSamples[currentFrame] = 100.0f * float(*(dataPtr + 2)) / totalMemory;
+
+ lastPercentUsedValue = percentUsedSamples[currentFrame];
+
+ oni_destroy_frame(frame);
+
+ sampleNumbers[currentFrame] = sampleNumber++;
+
+ prevWord = m_digitalIO != nullptr && m_digitalIO->hasEventWord() ? m_digitalIO->getEventWord() : prevWord;
+
+ eventCodes[currentFrame] = prevWord;
+
+ currentFrame++;
+
+ if (currentFrame >= numFrames)
+ {
+ shouldAddToBuffer = true;
+ currentFrame = 0;
+ }
+
+ if (shouldAddToBuffer)
+ {
+ shouldAddToBuffer = false;
+ percentUsedBuffer->addToBuffer(percentUsedSamples, sampleNumbers, timestamps, eventCodes, numFrames);
+ }
+ }
+}
diff --git a/Source/Devices/MemoryMonitor.h b/Source/Devices/MemoryMonitor.h
new file mode 100644
index 0000000..add5cab
--- /dev/null
+++ b/Source/Devices/MemoryMonitor.h
@@ -0,0 +1,130 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "../OnixDevice.h"
+
+class DigitalIO;
+
+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(String name, const oni_dev_idx_t, std::shared_ptr oni_ctx);
+
+ /** Configures the device so that it is ready to stream with default settings */
+ 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;
+
+ /** Given the sourceBuffers from OnixSource, add all streams for the current device to the array */
+ void addSourceBuffers(OwnedArray& sourceBuffers) override;
+
+ void addFrame(oni_frame_t*) override;
+
+ void processFrames() override;
+
+ float getLastPercentUsedValue();
+
+ void setDigitalIO(std::shared_ptr digitalIO) { m_digitalIO = digitalIO; }
+
+private:
+
+ DataBuffer* percentUsedBuffer;
+
+ std::shared_ptr m_digitalIO;
+
+ uint64_t prevWord = 0;
+
+ static const int numFrames = 10;
+
+ Array frameArray;
+
+ unsigned short currentFrame = 0;
+ int sampleNumber = 0;
+
+ /** The frequency at which memory use is recorded in Hz. */
+ const uint32_t samplesPerSecond = 100;
+
+ bool shouldAddToBuffer = false;
+
+ float percentUsedSamples[numFrames];
+ float bytesUsedSamples[numFrames];
+
+ double timestamps[numFrames];
+ int64_t sampleNumbers[numFrames];
+ uint64_t eventCodes[numFrames];
+
+ /** The total amount of memory, in 32-bit words, on the hardware that is available for data buffering*/
+ uint32_t totalMemory;
+
+ std::atomic lastPercentUsedValue = 0.0f;
+
+ JUCE_LEAK_DETECTOR(MemoryMonitor);
+};
+
+/*
+ Tracks the MemoryMonitor usage while data acquisition is running
+*/
+class MemoryMonitorUsage : public LevelMonitor
+{
+public:
+ MemoryMonitorUsage(GenericProcessor*);
+
+ void timerCallback() override;
+
+ void setMemoryMonitor(std::shared_ptr memoryMonitor);
+
+ void startAcquisition();
+
+ void stopAcquisition();
+
+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);
+
+ const int TimerFrequencyHz = 10;
+
+ JUCE_LEAK_DETECTOR(MemoryMonitorUsage);
+};
diff --git a/Source/Devices/Neuropixels2e.cpp b/Source/Devices/Neuropixels2e.cpp
index db1f17f..8ccb09d 100644
--- a/Source/Devices/Neuropixels2e.cpp
+++ b/Source/Devices/Neuropixels2e.cpp
@@ -1,3 +1,25 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "Neuropixels2e.h"
#include "DS90UB9x.h"
@@ -18,11 +40,7 @@ void Neuropixels2e::createDataStream(int n)
0.195f,
CharPointer_UTF8("\xc2\xb5V"),
{});
- streams.add(apStream);
-}
-
-Neuropixels2e::~Neuropixels2e()
-{
+ streamInfos.add(apStream);
}
int Neuropixels2e::getNumProbes() const
@@ -32,6 +50,8 @@ int Neuropixels2e::getNumProbes() const
int Neuropixels2e::configureDevice()
{
+ if (deviceContext == nullptr || !deviceContext->isInitialized()) return -1;
+
configureSerDes();
setProbeSupply(true);
probeSNA = getProbeSN(ProbeASelected);
@@ -43,7 +63,7 @@ int Neuropixels2e::configureDevice()
if (probeSNA == -1 && probeSNB == -1)
{
m_numProbes = 0;
- return -1;
+ return -2;
}
else if (probeSNA != -1 && probeSNB != -1)
{
@@ -215,7 +235,7 @@ void Neuropixels2e::addFrame(oni_frame_t* frame)
void Neuropixels2e::addSourceBuffers(OwnedArray& sourceBuffers)
{
int bufferIdx = 0;
- for (const auto& streamInfo : streams)
+ for (const auto& streamInfo : streamInfos)
{
sourceBuffers.add(new DataBuffer(streamInfo.getNumChannels(), (int)streamInfo.getSampleRate() * bufferSizeInSeconds));
apBuffer[bufferIdx++] = sourceBuffers.getLast();
@@ -233,6 +253,7 @@ void Neuropixels2e::processFrames()
dataPtr = (uint16_t*)frame->data;
uint16_t probeIndex = *(dataPtr + 4);
uint16_t* amplifierData = dataPtr + 6;
+ uint64_t timestamp = frame->time;
if (m_numProbes == 1)
{
diff --git a/Source/Devices/Neuropixels2e.h b/Source/Devices/Neuropixels2e.h
index 4f8b41a..2c7f692 100644
--- a/Source/Devices/Neuropixels2e.h
+++ b/Source/Devices/Neuropixels2e.h
@@ -1,15 +1,38 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "../OnixDevice.h"
#include "../I2CRegisterContext.h"
+/*
+ Configures and streams data from a Neuropixels 2.0e device
+*/
class Neuropixels2e : public OnixDevice
{
public:
Neuropixels2e(String name, const oni_dev_idx_t, std::shared_ptr);
- /** Destructor */
- ~Neuropixels2e();
-
int configureDevice() override;
/** Update the settings of the device */
diff --git a/Source/Devices/Neuropixels_1.cpp b/Source/Devices/Neuropixels_1.cpp
index a6ada23..3854746 100644
--- a/Source/Devices/Neuropixels_1.cpp
+++ b/Source/Devices/Neuropixels_1.cpp
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -22,7 +21,6 @@
*/
#include "Neuropixels_1.h"
-#include "../OnixSource.h"
BackgroundUpdaterWithProgressWindow::BackgroundUpdaterWithProgressWindow(Neuropixels_1* d)
: ThreadWithProgressWindow("Writing calibration files to Neuropixels Probe: " + d->getName(), true, false)
@@ -30,10 +28,6 @@ BackgroundUpdaterWithProgressWindow::BackgroundUpdaterWithProgressWindow(Neuropi
device = d;
}
-BackgroundUpdaterWithProgressWindow::~BackgroundUpdaterWithProgressWindow()
-{
-}
-
bool BackgroundUpdaterWithProgressWindow::updateSettings()
{
if (device->isEnabled())
@@ -189,7 +183,7 @@ Neuropixels_1::Neuropixels_1(String name, const oni_dev_idx_t deviceIdx_, std::s
0.195f,
CharPointer_UTF8("\xc2\xb5V"),
{});
- streams.add(apStream);
+ streamInfos.add(apStream);
StreamInfo lfpStream = StreamInfo(
name + "-LFP",
@@ -202,7 +196,7 @@ Neuropixels_1::Neuropixels_1(String name, const oni_dev_idx_t deviceIdx_, std::s
0.195f,
CharPointer_UTF8("\xc2\xb5V"),
{});
- streams.add(lfpStream);
+ streamInfos.add(lfpStream);
settings = std::make_unique();
defineMetadata(settings.get());
@@ -211,10 +205,6 @@ Neuropixels_1::Neuropixels_1(String name, const oni_dev_idx_t deviceIdx_, std::s
gainCalibrationFilePath = "None";
}
-Neuropixels_1::~Neuropixels_1()
-{
-}
-
NeuropixelsGain Neuropixels_1::getGainEnum(int index)
{
switch (index)
@@ -325,7 +315,7 @@ int Neuropixels_1::configureDevice()
WriteByte((uint32_t)NeuropixelsRegisters::OP_MODE, (uint32_t)OpMode::RECORD);
if (i2cContext->getLastResult() != ONI_ESUCCESS) return -3;
- return 0;
+ return deviceContext->getLastResult();
}
bool Neuropixels_1::updateSettings()
@@ -429,7 +419,7 @@ void Neuropixels_1::stopAcquisition()
void Neuropixels_1::addSourceBuffers(OwnedArray& sourceBuffers)
{
- for (StreamInfo streamInfo : streams)
+ for (StreamInfo streamInfo : streamInfos)
{
sourceBuffers.add(new DataBuffer(streamInfo.getNumChannels(), (int)streamInfo.getSampleRate() * bufferSizeInSeconds));
@@ -456,16 +446,9 @@ void Neuropixels_1::processFrames()
const GenericScopedLock frameLock(frameArray.getLock());
oni_frame_t* frame = frameArray.removeAndReturn(0);
- uint16_t* dataPtr;
- dataPtr = (uint16_t*)frame->data;
-
- auto dataclock = (unsigned char*)frame->data + 936;
- uint64 hubClock = ((uint64_t)(*(uint16_t*)dataclock) << 48) |
- ((uint64_t)(*(uint16_t*)(dataclock + 2)) << 32) |
- ((uint64_t)(*(uint16_t*)(dataclock + 4)) << 16) |
- ((uint64_t)(*(uint16_t*)(dataclock + 6)) << 0);
+ uint16_t* dataPtr = (uint16_t*)frame->data;
- apTimestamps[superFrameCount] = hubClock;
+ apTimestamps[superFrameCount] = frame->time;
apSampleNumbers[superFrameCount] = apSampleNumber++;
for (int i = 0; i < framesPerSuperFrame; i++)
diff --git a/Source/Devices/Neuropixels_1.h b/Source/Devices/Neuropixels_1.h
index f2a51aa..2c4ac49 100644
--- a/Source/Devices/Neuropixels_1.h
+++ b/Source/Devices/Neuropixels_1.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,14 +20,11 @@
*/
-#ifndef NEUROPIXELS1_H_DEFINED
-#define NEUROPIXELS1_H_DEFINED
+#pragma once
#include "../OnixDevice.h"
#include "../NeuropixComponents.h"
-class OnixSource;
-
#include
#include
#include
@@ -149,7 +145,7 @@ struct NeuropixelsV1Adc
/**
- Streams data from an ONIX device
+ Configures and streams data from a Neuropixels 1.0f device
*/
class Neuropixels_1 : public OnixDevice,
@@ -159,10 +155,7 @@ class Neuropixels_1 : public OnixDevice,
/** Constructor */
Neuropixels_1(String name, const oni_dev_idx_t, std::shared_ptr);
- /** Destructor */
- ~Neuropixels_1();
-
- /** Enables the device so that it is ready to stream with default settings */
+ /** Configures the device so that it is ready to stream with default settings */
int configureDevice() override;
/** Update the settings of the device by writing to hardware */
@@ -292,8 +285,6 @@ class BackgroundUpdaterWithProgressWindow : public ThreadWithProgressWindow
public:
BackgroundUpdaterWithProgressWindow(Neuropixels_1* d);
- ~BackgroundUpdaterWithProgressWindow();
-
void run();
bool updateSettings();
@@ -306,5 +297,3 @@ class BackgroundUpdaterWithProgressWindow : public ThreadWithProgressWindow
JUCE_LEAK_DETECTOR(BackgroundUpdaterWithProgressWindow);
};
-
-#endif
diff --git a/Source/Devices/OutputClock.cpp b/Source/Devices/OutputClock.cpp
new file mode 100644
index 0000000..c3f1ca4
--- /dev/null
+++ b/Source/Devices/OutputClock.cpp
@@ -0,0 +1,63 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "OutputClock.h"
+
+OutputClock::OutputClock(String name, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx)
+ : OnixDevice(name, OnixDeviceType::OUTPUTCLOCK, deviceIdx_, oni_ctx)
+{
+}
+
+OutputClock::~OutputClock()
+{
+ if (deviceContext != nullptr && deviceContext->isInitialized()) setGateRun(false, true);
+}
+
+bool OutputClock::updateSettings()
+{
+ oni_reg_val_t baseFreqHz = deviceContext->readRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::BASE_FREQ_HZ);
+
+ 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);
+
+ deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::CLOCK_GATE, 1); if (deviceContext->getLastResult() != ONI_ESUCCESS) return false;
+
+ deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::HIGH_CYCLES, h); if (deviceContext->getLastResult() != ONI_ESUCCESS) return false;
+ deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::LOW_CYCLES, l); if (deviceContext->getLastResult() != ONI_ESUCCESS) return false;
+ deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::DELAY_CYCLES, delayCycles); if (deviceContext->getLastResult() != ONI_ESUCCESS) return false;
+
+ deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::GATE_RUN, gateRun ? 1 : 0); if (deviceContext->getLastResult() != ONI_ESUCCESS) return false;
+
+ return true;
+}
+
+void OutputClock::startAcquisition()
+{
+ writeGateRunRegister();
+}
+
+void OutputClock::stopAcquisition()
+{
+ setGateRun(false, true);
+}
\ No newline at end of file
diff --git a/Source/Devices/OutputClock.h b/Source/Devices/OutputClock.h
new file mode 100644
index 0000000..d93f859
--- /dev/null
+++ b/Source/Devices/OutputClock.h
@@ -0,0 +1,98 @@
+/*
+ ------------------------------------------------------------------
+
+ 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 "../OnixDevice.h"
+
+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(String name, const oni_dev_idx_t, std::shared_ptr oni_ctx);
+
+ ~OutputClock();
+
+ /** Device is always enabled */
+ int configureDevice() override { setEnabled(true); return 0; };
+
+ /** Update the settings of the device */
+ bool updateSettings() override;
+
+ /** Starts probe data streaming */
+ void startAcquisition() override;
+
+ /** Stops probe data streaming*/
+ void stopAcquisition() override;
+
+ /** Given the sourceBuffers from OnixSource, add all streams for the current device to the array */
+ void addSourceBuffers(OwnedArray& sourceBuffers) override {};
+
+ void addFrame(oni_frame_t* frame) override { oni_destroy_frame(frame); }
+
+ void processFrames() override {};
+
+ float getFrequencyHz() const { return frequencyHz; }
+
+ void setFrequencyHz(float frequency) { frequencyHz = frequency; }
+
+ uint32_t getDutyCycle() const { return dutyCycle; }
+
+ void setDutyCycle(uint32_t dutyCycle_) { dutyCycle = dutyCycle_; }
+
+ uint32_t getDelay() const { return delay; }
+
+ void setDelay(uint32_t delay_) { delay = delay_; }
+
+ bool getGateRun() const { return gateRun; }
+
+ void setGateRun(bool gate, bool writeToRegister = false)
+ {
+ gateRun = gate;
+ if (writeToRegister) writeGateRunRegister();
+ }
+
+private:
+
+ double frequencyHz = 1e6;
+ uint32_t dutyCycle = 50;
+ uint32_t delay = 0;
+
+ bool gateRun = true;
+
+ void writeGateRunRegister() { deviceContext->writeRegister(deviceIdx, (oni_reg_addr_t)OutputClockRegisters::GATE_RUN, gateRun ? 1 : 0); }
+
+ JUCE_LEAK_DETECTOR(OutputClock);
+};
diff --git a/Source/Devices/PortController.cpp b/Source/Devices/PortController.cpp
index 6dd130c..c244773 100644
--- a/Source/Devices/PortController.cpp
+++ b/Source/Devices/PortController.cpp
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2023 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -29,12 +28,10 @@ PortController::PortController(PortName port_, std::shared_ptr ctx_) :
{
}
-PortController::~PortController()
-{
-}
-
int PortController::configureDevice()
{
+ if (deviceContext == nullptr || !deviceContext->isInitialized()) return -5;
+
deviceContext->writeRegister(deviceIdx, (uint32_t)PortControllerRegister::ENABLE, 1);
return deviceContext->getLastResult();
@@ -97,6 +94,21 @@ DiscoveryParameters PortController::getHeadstageDiscoveryParameters(String heads
return DiscoveryParameters();
}
+String PortController::getPortName(int offset)
+{
+ switch (offset)
+ {
+ case 0:
+ return "";
+ case HubAddressPortA:
+ return "Port A";
+ case HubAddressPortB:
+ return "Port B";
+ default:
+ return "";
+ }
+}
+
bool PortController::configureVoltage(float voltage)
{
if (voltage == defaultVoltage)
@@ -142,7 +154,7 @@ void PortController::setVoltage(float voltage)
deviceContext->writeRegister((oni_dev_idx_t)port, (oni_reg_addr_t)PortControllerRegister::PORTVOLTAGE, 0);
if (deviceContext->getLastResult() != ONI_ESUCCESS) return;
sleep_for(std::chrono::milliseconds(300));
-
+
deviceContext->writeRegister((oni_dev_idx_t)port, (oni_reg_addr_t)PortControllerRegister::PORTVOLTAGE, voltage * 10);
if (deviceContext->getLastResult() != ONI_ESUCCESS) return;
sleep_for(std::chrono::milliseconds(500));
@@ -162,6 +174,23 @@ PortName PortController::getPortFromIndex(oni_dev_idx_t index)
return index & (1 << 8) ? PortName::PortA : PortName::PortB;
}
+int PortController::getOffsetFromIndex(oni_dev_idx_t index)
+{
+ return index & 0b1100000000;
+}
+
+Array PortController::getUniqueOffsetsFromIndices(std::vector indices)
+{
+ Array offsets;
+
+ for (auto index : indices)
+ {
+ offsets.addIfNotAlreadyThere(getOffsetFromIndex(index));
+ }
+
+ return offsets;
+}
+
Array PortController::getUniquePortsFromIndices(std::vector indices)
{
Array ports;
diff --git a/Source/Devices/PortController.h b/Source/Devices/PortController.h
index 823322c..5e1a39a 100644
--- a/Source/Devices/PortController.h
+++ b/Source/Devices/PortController.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2023 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,8 +20,7 @@
*/
-#ifndef __PORTCONTROLLER_H__
-#define __PORTCONTROLLER_H__
+#pragma once
#include
#include
@@ -71,8 +69,6 @@ class DiscoveryParameters
voltageIncrement = voltageIncrement_;
}
- ~DiscoveryParameters() {};
-
bool operator==(const DiscoveryParameters& rhs) const
{
return rhs.minVoltage == minVoltage && rhs.maxVoltage == maxVoltage && rhs.voltageOffset == voltageOffset && rhs.voltageIncrement == voltageIncrement;
@@ -84,8 +80,6 @@ class PortController : public OnixDevice
public:
PortController(PortName port_, std::shared_ptr ctx_);
- ~PortController();
-
int configureDevice() override;
bool updateSettings() override { return true; }
@@ -116,15 +110,24 @@ class PortController : public OnixDevice
static int getPortOffset(PortName port) { return (uint32_t)port << 8; }
+ static String getPortName(int offset);
+
static String getPortName(PortName port) { return port == PortName::PortA ? "Port A" : "Port B"; }
static PortName getPortFromIndex(oni_dev_idx_t index);
+ static int getOffsetFromIndex(oni_dev_idx_t index);
+
+ static Array getUniqueOffsetsFromIndices(std::vector indices);
+
static Array getUniquePortsFromIndices(std::vector);
/** Check if the port status changed and there is an error reported */
bool getErrorFlag() { return errorFlag; }
+ static const int HubAddressPortA = 256;
+ static const int HubAddressPortB = 512;
+
private:
Array frameArray;
@@ -141,5 +144,3 @@ class PortController : public OnixDevice
JUCE_LEAK_DETECTOR(PortController);
};
-
-#endif // !__PORTCONTROLLER_H__
diff --git a/Source/Formats/ProbeInterface.h b/Source/Formats/ProbeInterface.h
index 6865c16..eca22e1 100644
--- a/Source/Formats/ProbeInterface.h
+++ b/Source/Formats/ProbeInterface.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2024 Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,8 +20,7 @@
*/
-#ifndef __PROBEINTERFACE_H__
-#define __PROBEINTERFACE_H__
+#pragma once
#include "../NeuropixComponents.h"
@@ -171,5 +169,3 @@ class ProbeInterfaceJson
return true;
}
};
-
-#endif
\ No newline at end of file
diff --git a/Source/FrameReader.cpp b/Source/FrameReader.cpp
index 1dc3aef..ae15ed5 100644
--- a/Source/FrameReader.cpp
+++ b/Source/FrameReader.cpp
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2023 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -30,10 +29,6 @@ FrameReader::FrameReader(OnixDeviceVector sources_, std::shared_ptr ctx_)
context = ctx_;
}
-FrameReader::~FrameReader()
-{
-}
-
void FrameReader::run()
{
while (!threadShouldExit())
@@ -42,6 +37,8 @@ void FrameReader::run()
if (context->getLastResult() < ONI_ESUCCESS)
{
+ if (threadShouldExit()) return;
+
CoreServices::sendStatusMessage("Unable to read data frames. Stopping acquisition...");
CoreServices::setAcquisitionStatus(false);
return;
diff --git a/Source/FrameReader.h b/Source/FrameReader.h
index 1bfb6c6..210786a 100644
--- a/Source/FrameReader.h
+++ b/Source/FrameReader.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2023 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,8 +20,7 @@
*/
-#ifndef __FRAMEREADER_H__
-#define __FRAMEREADER_H__
+#pragma once
#include
@@ -34,8 +32,6 @@ class FrameReader : public Thread
public:
FrameReader(OnixDeviceVector sources_, std::shared_ptr);
- ~FrameReader();
-
void run() override;
private:
@@ -45,5 +41,3 @@ class FrameReader : public Thread
JUCE_LEAK_DETECTOR(FrameReader);
};
-
-#endif // __FRAMEREADER_H__
diff --git a/Source/I2CRegisterContext.cpp b/Source/I2CRegisterContext.cpp
index 06acc1e..46405e6 100644
--- a/Source/I2CRegisterContext.cpp
+++ b/Source/I2CRegisterContext.cpp
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -22,7 +21,6 @@
*/
#include "I2CRegisterContext.h"
-#include
I2CRegisterContext::I2CRegisterContext(uint32_t address_, const oni_dev_idx_t devIdx_, std::shared_ptr ctx_)
: deviceIndex(devIdx_), i2caddress(address_)
diff --git a/Source/I2CRegisterContext.h b/Source/I2CRegisterContext.h
index d08f793..1820d1b 100644
--- a/Source/I2CRegisterContext.h
+++ b/Source/I2CRegisterContext.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,8 +20,7 @@
*/
-#ifndef __I2CRegisterContext_H__
-#define __I2CRegisterContext_H__
+#pragma once
#include "ProcessorHeaders.h"
@@ -53,5 +51,3 @@ class I2CRegisterContext
JUCE_LEAK_DETECTOR(I2CRegisterContext);
};
-
-#endif // !__I2CRegisterContext_H__
diff --git a/Source/NeuropixComponents.h b/Source/NeuropixComponents.h
index fe4e5f3..e29363c 100644
--- a/Source/NeuropixComponents.h
+++ b/Source/NeuropixComponents.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,8 +20,7 @@
*/
-#ifndef __NEUROPIXELCOMPONENTS_H__
-#define __NEUROPIXELCOMPONENTS_H__
+#pragma once
#include
#include
@@ -162,5 +160,3 @@ struct ProbeSettings
ProbeMetadata probeMetadata;
};
-
-#endif // __NEUROPIXELCOMPONENTS_H__
diff --git a/Source/Onix1.cpp b/Source/Onix1.cpp
index cae2c61..7dfdcb0 100644
--- a/Source/Onix1.cpp
+++ b/Source/Onix1.cpp
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
diff --git a/Source/Onix1.h b/Source/Onix1.h
index 1ac2a54..279a8be 100644
--- a/Source/Onix1.h
+++ b/Source/Onix1.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2023 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,8 +20,7 @@
*/
-#ifndef __ONIX1_H__
-#define __ONIX1_H__
+#pragma once
#include
#include
@@ -32,6 +30,7 @@
#include "../../plugin-GUI/Source/Utils/Utils.h"
constexpr const char* NEUROPIXELSV1F_HEADSTAGE_NAME = "Neuropixels 1.0f";
+constexpr const char* BREAKOUT_BOARD_NAME = "Breakout Board";
class error_t : public std::exception
{
@@ -115,5 +114,3 @@ class Onix1
void get_opt_(int option, void* value, size_t* size);
};
-
-#endif // !__ONIX1_H__
diff --git a/Source/OnixDevice.cpp b/Source/OnixDevice.cpp
index e49e940..1ae5b3b 100644
--- a/Source/OnixDevice.cpp
+++ b/Source/OnixDevice.cpp
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
diff --git a/Source/OnixDevice.h b/Source/OnixDevice.h
index f529d79..b3a7a6d 100644
--- a/Source/OnixDevice.h
+++ b/Source/OnixDevice.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,8 +20,7 @@
*/
-#ifndef __OnixDevice_H__
-#define __OnixDevice_H__
+#pragma once
#include
@@ -49,7 +47,13 @@ enum class OnixDeviceType {
NEUROPIXELS_1,
NEUROPIXELS_2,
ADC,
- PORT_CONTROL
+ PORT_CONTROL,
+ MEMORYMONITOR,
+ OUTPUTCLOCK,
+ HEARTBEAT,
+ HARPSYNCINPUT,
+ ANALOGIO,
+ DIGITALIO,
};
struct StreamInfo {
@@ -84,7 +88,9 @@ struct StreamInfo {
if (numChannels_ != suffixes_.size())
{
- LOGE("Difference between number of channels and suffixes. Generating default suffixes instead.");
+ if (suffixes_.size() != 0)
+ LOGE("Difference between number of channels and suffixes. Generating default suffixes instead.");
+
suffixes_.clear();
suffixes_.ensureStorageAllocated(numChannels);
@@ -97,7 +103,7 @@ struct StreamInfo {
String getName() const { return name_; }
String getDescription() const { return description_; }
- String getIdentifer() const { return identifier_; }
+ String getIdentifier() const { return identifier_; }
int getNumChannels() const { return numChannels_; }
float getSampleRate() const { return sampleRate_; }
String getChannelPrefix() const { return channelPrefix_; }
@@ -159,7 +165,7 @@ class OnixDevice
OnixDeviceType type;
- Array streams;
+ Array streamInfos;
const int bufferSizeInSeconds = 10;
@@ -178,5 +184,3 @@ class OnixDevice
};
using OnixDeviceVector = std::vector>;
-
-#endif
diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp
index c421e1e..a1a864b 100644
--- a/Source/OnixSource.cpp
+++ b/Source/OnixSource.cpp
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2023 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -56,6 +55,7 @@ OnixSource::OnixSource(SourceNode* sn) :
return;
}
+ // TODO: Add these parameters in the registerParameters() override?
addBooleanParameter(Parameter::PROCESSOR_SCOPE, "passthroughA", "Passthrough", "Enables passthrough mode for e-variant headstages on Port A", false, true);
addBooleanParameter(Parameter::PROCESSOR_SCOPE, "passthroughB", "Passthrough", "Enables passthrough mode for e-variant headstages on Port B", false, true);
@@ -169,7 +169,7 @@ void OnixSource::initializeDevices(bool updateStreamInfo)
}
sources.emplace_back(np1);
- headstages.insert({ PortController::getPortFromIndex(index), NEUROPIXELSV1F_HEADSTAGE_NAME });
+ headstages.insert({ PortController::getOffsetFromIndex(index), NEUROPIXELSV1F_HEADSTAGE_NAME });
npxProbeIdx++;
}
@@ -216,6 +216,96 @@ void OnixSource::initializeDevices(bool updateStreamInfo)
sources.emplace_back(np2);
}
}
+ else if (device.id == ONIX_MEMUSAGE)
+ {
+ auto memoryMonitor = std::make_shared("Memory Monitor", index, context);
+
+ int result = memoryMonitor->configureDevice();
+
+ if (result != 0)
+ {
+ LOGE("Device Idx: ", index, " Error enabling device stream.");
+ continue;
+ }
+
+ sources.emplace_back(memoryMonitor);
+ headstages.insert({ PortController::getOffsetFromIndex(index), BREAKOUT_BOARD_NAME });
+ }
+ else if (device.id == ONIX_FMCCLKOUT1R3)
+ {
+ auto outputClock = std::make_shared("Output Clock", index, context);
+
+ int result = outputClock->configureDevice();
+
+ if (result != 0)
+ {
+ LOGE("Device Idx: ", index, " Error enabling device stream.");
+ continue;
+ }
+
+ sources.emplace_back(outputClock);
+ headstages.insert({ PortController::getOffsetFromIndex(index), BREAKOUT_BOARD_NAME });
+ }
+ else if (device.id == ONIX_HEARTBEAT)
+ {
+ auto heartbeat = std::make_shared("Heartbeat", index, context);
+
+ int result = heartbeat->configureDevice();
+
+ if (result != 0)
+ {
+ LOGE("Device Idx: ", index, " Error enabling device stream.");
+ continue;
+ }
+
+ sources.emplace_back(heartbeat);
+ headstages.insert({ PortController::getOffsetFromIndex(index), BREAKOUT_BOARD_NAME });
+ }
+ else if (device.id == ONIX_HARPSYNCINPUT)
+ {
+ auto harpSyncInput = std::make_shared("Harp Sync Input", index, context);
+
+ int result = harpSyncInput->configureDevice();
+
+ if (result != 0)
+ {
+ LOGE("Device Idx: ", index, " Error enabling device stream.");
+ continue;
+ }
+
+ sources.emplace_back(harpSyncInput);
+ headstages.insert({ PortController::getOffsetFromIndex(index), BREAKOUT_BOARD_NAME });
+ }
+ else if (device.id == ONIX_FMCANALOG1R3)
+ {
+ auto analogIO = std::make_shared("Analog IO", index, context);
+
+ int result = analogIO->configureDevice();
+
+ if (result != 0)
+ {
+ LOGE("Device Idx: ", index, " Error enabling device stream.");
+ continue;
+ }
+
+ sources.emplace_back(analogIO);
+ headstages.insert({ PortController::getOffsetFromIndex(index), BREAKOUT_BOARD_NAME });
+ }
+ else if (device.id == ONIX_BREAKDIG1R3)
+ {
+ auto digitalIO = std::make_shared("Digital IO", index, context);
+
+ int result = digitalIO->configureDevice();
+
+ if (result != 0)
+ {
+ LOGE("Device Idx: ", index, " Error enabling device stream.");
+ continue;
+ }
+
+ sources.emplace_back(digitalIO);
+ headstages.insert({ PortController::getOffsetFromIndex(index), BREAKOUT_BOARD_NAME });
+ }
}
if (portA->configureDevice() != ONI_ESUCCESS) LOGE("Unable to configure Port A.");
@@ -236,7 +326,7 @@ void OnixSource::initializeDevices(bool updateStreamInfo)
LOGD("All devices initialized.");
}
-OnixDeviceVector OnixSource::getDataSources() const
+OnixDeviceVector OnixSource::getDataSources()
{
OnixDeviceVector devices{};
@@ -248,7 +338,7 @@ OnixDeviceVector OnixSource::getDataSources() const
return devices;
}
-OnixDeviceVector OnixSource::getDataSourcesFromPort(PortName port) const
+OnixDeviceVector OnixSource::getDataSourcesFromPort(PortName port)
{
OnixDeviceVector devices{};
@@ -261,24 +351,50 @@ OnixDeviceVector OnixSource::getDataSourcesFromPort(PortName port) const
return devices;
}
-std::map OnixSource::createDeviceMap(OnixDeviceVector devices)
+OnixDeviceVector OnixSource::getDataSourcesFromOffset(int offset)
+{
+ OnixDeviceVector devices{};
+ offset = PortController::getOffsetFromIndex(offset);
+
+ for (const auto& source : sources)
+ {
+ if (PortController::getOffsetFromIndex(source->getDeviceIdx()) == offset)
+ devices.emplace_back(source);
+ }
+
+ return devices;
+}
+
+std::shared_ptr OnixSource::getDevice(OnixDeviceType type)
+{
+ for (const auto& device : sources)
+ {
+ if (device->type == type) return device;
+ }
+
+ return nullptr;
+}
+
+std::map OnixSource::createDeviceMap(OnixDeviceVector devices, bool filterDevices)
{
std::map deviceMap;
for (const auto& device : devices)
{
+ if (filterDevices && (device->type == OnixDeviceType::HEARTBEAT || device->type == OnixDeviceType::MEMORYMONITOR)) continue;
+
deviceMap.insert({ device->getDeviceIdx(), device->type });
}
return deviceMap;
}
-std::map OnixSource::createDeviceMap()
+std::map OnixSource::createDeviceMap(bool filterDevices)
{
- return createDeviceMap(getDataSources());
+ return createDeviceMap(getDataSources(), filterDevices);
}
-std::map OnixSource::getHeadstageMap()
+std::map OnixSource::getHeadstageMap()
{
return headstages;
}
@@ -367,13 +483,14 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel
updateSourceBuffers();
+ std::shared_ptr digitalIO = std::static_pointer_cast(getDevice(OnixDeviceType::DIGITALIO));
+
if (devicesFound)
{
for (const auto& source : sources)
{
if (!source->isEnabled()) continue;
- // create device info object
if (source->type == OnixDeviceType::NEUROPIXELS_1)
{
DeviceInfo::Settings deviceSettings{
@@ -386,7 +503,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel
deviceInfos->add(new DeviceInfo(deviceSettings));
- addIndividualStreams(source->streams, dataStreams, deviceInfos, continuousChannels);
+ addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels);
}
else if (source->type == OnixDeviceType::BNO)
{
@@ -404,10 +521,10 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel
"Bno055",
"Continuous data from a Bno055 9-axis IMU",
"onix-bno055.data",
- source->streams[0].getSampleRate()
+ source->streamInfos[0].getSampleRate()
};
- addCombinedStreams(dataStreamSettings, source->streams, dataStreams, deviceInfos, continuousChannels);
+ addCombinedStreams(dataStreamSettings, source->streamInfos, dataStreams, deviceInfos, continuousChannels);
}
else if (source->type == OnixDeviceType::NEUROPIXELS_2)
{
@@ -421,7 +538,58 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel
deviceInfos->add(new DeviceInfo(deviceSettings));
- addIndividualStreams(source->streams, dataStreams, deviceInfos, continuousChannels);
+ addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels);
+ }
+ else if (source->type == OnixDeviceType::MEMORYMONITOR)
+ {
+ DeviceInfo::Settings deviceSettings{
+ source->getName(),
+ "Memory Monitor",
+ "memorymonitor",
+ "0000000",
+ ""
+ };
+
+ deviceInfos->add(new DeviceInfo(deviceSettings));
+
+ addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels);
+
+ if (digitalIO != nullptr && digitalIO->isEnabled())
+ {
+ auto ttlChannelSettings = digitalIO->getEventChannelSettings();
+ ttlChannelSettings.stream = dataStreams->getLast();
+ eventChannels->add(new EventChannel(ttlChannelSettings));
+
+ std::static_pointer_cast(source)->setDigitalIO(digitalIO);
+ }
+ }
+ else if (source->type == OnixDeviceType::ANALOGIO)
+ {
+ DeviceInfo::Settings deviceSettings{
+ source->getName(),
+ "Analog IO",
+ "analogio",
+ "0000000",
+ ""
+ };
+
+ deviceInfos->add(new DeviceInfo(deviceSettings));
+
+ addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels);
+ }
+ else if (source->type == OnixDeviceType::HARPSYNCINPUT)
+ {
+ DeviceInfo::Settings deviceSettings{
+ source->getName(),
+ "Harp Sync Input",
+ "harpsyncinput",
+ "0000000",
+ ""
+ };
+
+ deviceInfos->add(new DeviceInfo(deviceSettings));
+
+ addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels);
}
}
}
@@ -445,7 +613,7 @@ void OnixSource::addCombinedStreams(DataStream::Settings dataStreamSettings,
streamInfo.getChannelType(),
streamInfo.getChannelPrefix() + streamInfo.getSuffixes()[chan],
streamInfo.getDescription(),
- streamInfo.getIdentifer(),
+ streamInfo.getIdentifier(),
streamInfo.getBitVolts(),
stream
};
@@ -455,9 +623,9 @@ void OnixSource::addCombinedStreams(DataStream::Settings dataStreamSettings,
}
}
-void OnixSource::addIndividualStreams(Array streamInfos,
- OwnedArray* dataStreams,
- OwnedArray* deviceInfos,
+void OnixSource::addIndividualStreams(Array streamInfos,
+ OwnedArray* dataStreams,
+ OwnedArray* deviceInfos,
OwnedArray* continuousChannels)
{
for (StreamInfo streamInfo : streamInfos)
@@ -466,7 +634,7 @@ void OnixSource::addIndividualStreams(Array streamInfos,
{
streamInfo.getName(),
streamInfo.getDescription(),
- streamInfo.getIdentifer(),
+ streamInfo.getIdentifier(),
streamInfo.getSampleRate()
};
@@ -481,7 +649,7 @@ void OnixSource::addIndividualStreams(Array streamInfos,
streamInfo.getChannelType(),
streamInfo.getChannelPrefix() + streamInfo.getSuffixes()[chan],
streamInfo.getDescription(),
- streamInfo.getIdentifer(),
+ streamInfo.getIdentifier(),
streamInfo.getBitVolts(),
stream
};
@@ -494,7 +662,7 @@ void OnixSource::addIndividualStreams(Array streamInfos,
bool OnixSource::isDevicesReady()
{
auto tabMap = editor->createTabMapFromCanvas();
- auto sourceMap = createDeviceMap(sources);
+ auto sourceMap = createDeviceMap(true);
return tabMap == sourceMap;
}
@@ -541,6 +709,8 @@ bool OnixSource::startAcquisition()
for (const auto& source : sources)
{
+ if (!source->isEnabled()) continue;
+
devices.emplace_back(source);
}
@@ -549,8 +719,6 @@ bool OnixSource::startAcquisition()
for (const auto& source : devices)
{
- if (!source->isEnabled()) continue;
-
source->startAcquisition();
}
@@ -577,10 +745,6 @@ bool OnixSource::stopAcquisition()
{
oni_size_t reg = 0;
context->setOption(ONI_OPT_RUNNING, reg);
- if (context->getLastResult() != ONI_ESUCCESS) return false;
-
- uint32_t val = 1;
- context->setOption(ONI_OPT_RESET, val);
}
for (const auto& source : sources)
diff --git a/Source/OnixSource.h b/Source/OnixSource.h
index 5d4d13c..c5dccf3 100644
--- a/Source/OnixSource.h
+++ b/Source/OnixSource.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2023 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,8 +20,7 @@
*/
-#ifndef __OnixSource_H__
-#define __OnixSource_H__
+#pragma once
#include
@@ -94,15 +92,19 @@ class OnixSource : public DataThread
void disconnectDevices(bool updateStreamInfo = false);
- OnixDeviceVector getDataSources() const;
+ OnixDeviceVector getDataSources();
- OnixDeviceVector getDataSourcesFromPort(PortName port) const;
+ OnixDeviceVector getDataSourcesFromPort(PortName port);
- std::map createDeviceMap(OnixDeviceVector);
+ OnixDeviceVector getDataSourcesFromOffset(int offset);
- std::map createDeviceMap();
+ std::shared_ptr getDevice(OnixDeviceType type);
- std::map getHeadstageMap();
+ static std::map createDeviceMap(OnixDeviceVector, bool filterDevices = false);
+
+ std::map createDeviceMap(bool filterDevices = false);
+
+ std::map getHeadstageMap();
void updateSourceBuffers();
@@ -119,8 +121,8 @@ class OnixSource : public DataThread
/** Available data sources */
OnixDeviceVector sources;
- /** Available headstages */
- std::map headstages;
+ /** Available headstages, indexed by their offset value */
+ std::map headstages;
/** Pointer to the editor */
OnixSourceEditor* editor;
@@ -143,5 +145,3 @@ class OnixSource : public DataThread
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(OnixSource);
};
-
-#endif // __OnixSource_H__
diff --git a/Source/OnixSourceCanvas.cpp b/Source/OnixSourceCanvas.cpp
index abce423..2db1685 100644
--- a/Source/OnixSourceCanvas.cpp
+++ b/Source/OnixSourceCanvas.cpp
@@ -1,23 +1,22 @@
/*
-------------------------------------------------------------------
+ ------------------------------------------------------------------
-This file is part of the Open Ephys GUI
-Copyright(C) 2020 Allen Institute for Brain Science and 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/>.
*/
@@ -55,6 +54,8 @@ OnixSourceCanvas::OnixSourceCanvas(GenericProcessor* processor_, OnixSourceEdito
{
topLevelTabComponent = std::make_unique(editor, true);
addAndMakeVisible(topLevelTabComponent.get());
+
+ addHub(BREAKOUT_BOARD_NAME, 0);
}
CustomTabComponent* OnixSourceCanvas::addTopLevelTab(String tabName, int index)
@@ -74,20 +75,29 @@ Parameter* OnixSourceCanvas::getSourceParameter(String name)
return source->getParameter(name);
}
-void OnixSourceCanvas::addHeadstage(String headstage, PortName port)
+void OnixSourceCanvas::addHub(String hubName, int offset)
{
- int offset = PortController::getPortOffset(port);
CustomTabComponent* tab = nullptr;
OnixDeviceVector devices;
+ PortName port = PortController::getPortFromIndex(offset);
- if (headstage == NEUROPIXELSV1F_HEADSTAGE_NAME)
+ if (hubName == NEUROPIXELSV1F_HEADSTAGE_NAME)
{
- tab = addTopLevelTab(getTopLevelTabName(port, headstage), (int)port - 1);
+ tab = addTopLevelTab(getTopLevelTabName(port, hubName), (int)port);
devices.emplace_back(std::make_shared("Probe-A", offset, nullptr));
devices.emplace_back(std::make_shared("Probe-B", offset + 1, nullptr));
devices.emplace_back(std::make_shared("BNO055", offset + 2, nullptr));
}
+ else if (hubName == BREAKOUT_BOARD_NAME)
+ {
+ tab = addTopLevelTab(hubName, 0);
+
+ devices.emplace_back(std::make_shared("Output Clock", 5, nullptr));
+ devices.emplace_back(std::make_shared("Analog IO", 6, nullptr));
+ devices.emplace_back(std::make_shared("Digital IO", 7, nullptr));
+ devices.emplace_back(std::make_shared("Harp Sync Input", 12, nullptr));
+ }
if (tab != nullptr && devices.size() > 0)
{
@@ -111,6 +121,26 @@ void OnixSourceCanvas::populateSourceTabs(CustomTabComponent* tab, OnixDeviceVec
auto bno055Interface = std::make_shared(std::static_pointer_cast(device), editor, this);
addInterfaceToTab(getDeviceTabName(device), tab, bno055Interface);
}
+ else if (device->type == OnixDeviceType::OUTPUTCLOCK)
+ {
+ auto outputClockInterface = std::make_shared(std::static_pointer_cast(device), editor, this);
+ addInterfaceToTab(getDeviceTabName(device), tab, outputClockInterface);
+ }
+ else if (device->type == OnixDeviceType::HARPSYNCINPUT)
+ {
+ auto harpSyncInputInterface = std::make_shared(std::static_pointer_cast(device), editor, this);
+ addInterfaceToTab(getDeviceTabName(device), tab, harpSyncInputInterface);
+ }
+ else if (device->type == OnixDeviceType::ANALOGIO)
+ {
+ auto analogIOInterface = std::make_shared(std::static_pointer_cast(device), editor, this);
+ addInterfaceToTab(getDeviceTabName(device), tab, analogIOInterface);
+ }
+ else if (device->type == OnixDeviceType::DIGITALIO)
+ {
+ auto digitalIOInterface = std::make_shared(std::static_pointer_cast(device), editor, this);
+ addInterfaceToTab(getDeviceTabName(device), tab, digitalIOInterface);
+ }
}
}
@@ -134,18 +164,40 @@ void OnixSourceCanvas::updateSettingsInterfaceDataSource(std::shared_ptrgetName() + " to an open tab."); return; }
+ if (ind == -1)
+ {
+ if (device->type != OnixDeviceType::MEMORYMONITOR && device->type != OnixDeviceType::HEARTBEAT)
+ LOGD("Unable to match " + device->getName() + " to an open tab.");
+
+ return;
+ }
if (device->type == OnixDeviceType::NEUROPIXELS_1)
{
- // NB: Neuropixels-specific settings need to be updated
auto npx1Found = std::static_pointer_cast(device);
auto npx1Selected = std::static_pointer_cast(settingsInterfaces[ind]->device);
npx1Found->setSettings(npx1Selected->settings.get());
npx1Found->adcCalibrationFilePath = npx1Selected->adcCalibrationFilePath;
npx1Found->gainCalibrationFilePath = npx1Selected->gainCalibrationFilePath;
}
- // TODO: Add more devices, since they will have device-specific settings to be updated
+ else if (device->type == OnixDeviceType::OUTPUTCLOCK)
+ {
+ auto outputClockFound = std::static_pointer_cast(device);
+ auto outputClockSelected = std::static_pointer_cast(settingsInterfaces[ind]->device);
+ outputClockFound->setDelay(outputClockSelected->getDelay());
+ outputClockFound->setDutyCycle(outputClockSelected->getDutyCycle());
+ outputClockFound->setFrequencyHz(outputClockSelected->getFrequencyHz());
+ outputClockFound->setGateRun(outputClockSelected->getGateRun());
+ }
+ else if (device->type == OnixDeviceType::ANALOGIO)
+ {
+ auto analogIOFound = std::static_pointer_cast(device);
+ auto analogIOSelected = std::static_pointer_cast(settingsInterfaces[ind]->device);
+ for (int i = 0; i < analogIOFound->getNumChannels(); i++)
+ {
+ analogIOFound->setChannelDirection(i, analogIOSelected->getChannelDirection(i));
+ }
+ }
device->setEnabled(settingsInterfaces[ind]->device->isEnabled());
settingsInterfaces[ind]->device.reset();
@@ -181,14 +233,6 @@ CustomViewport* OnixSourceCanvas::createCustomViewport(SettingsInterface* settin
return new CustomViewport(settingsInterface, bounds.getWidth(), bounds.getHeight());
}
-OnixSourceCanvas::~OnixSourceCanvas()
-{
-}
-
-void OnixSourceCanvas::paint(Graphics& g)
-{
-}
-
void OnixSourceCanvas::refresh()
{
repaint();
@@ -225,7 +269,12 @@ void OnixSourceCanvas::removeTabs(PortName port)
}
if (tabExists)
- topLevelTabComponent->removeTab((int)port - 1);
+ {
+ if (port == PortName::PortB && headstageTabs.size() == 1 && headstageTabs[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()
@@ -248,11 +297,11 @@ std::map OnixSourceCanvas::createSelectedMap(std::vectorgetHeadstageSelected(port);
+ String selectedHeadstage = editor->getHeadstageSelected(offset);
- String msg = "Headstage " + selectedHeadstage + " is selected on " + PortController::getPortName(port) + ", but was not discovered there.\n\n";
+ String msg = "Headstage " + selectedHeadstage + " is selected on " + PortController::getPortName(offset) + ", but was not discovered there.\n\n";
msg += "Select one of the options below to continue:\n";
msg += " [Keep Current] to keep " + selectedHeadstage + " selected.\n";
msg += " [Remove] to remove " + selectedHeadstage + ".\n - Note: this will delete any settings that were modified.";
@@ -269,7 +318,7 @@ void OnixSourceCanvas::askKeepRemove(PortName port)
switch (result)
{
case 0: // Remove
- removeTabs(port);
+ removeTabs(PortController::getPortFromIndex(offset));
break;
case 1: // Keep Current
break;
@@ -278,12 +327,12 @@ void OnixSourceCanvas::askKeepRemove(PortName port)
}
}
-void OnixSourceCanvas::askKeepUpdate(PortName port, String foundHeadstage, OnixDeviceVector devices)
+void OnixSourceCanvas::askKeepUpdate(int offset, String foundHeadstage, OnixDeviceVector devices)
{
- String selectedHeadstage = editor->getHeadstageSelected(port);
+ String selectedHeadstage = editor->getHeadstageSelected(offset);
- String msg = "Headstage " + selectedHeadstage + " is selected on " + PortController::getPortName(port) + ". ";
- msg += "However, headstage " + foundHeadstage + " was found on " + PortController::getPortName(port) + ". \n\n";
+ String msg = "Headstage " + selectedHeadstage + " is selected on " + PortController::getPortName(offset) + ". ";
+ msg += "However, headstage " + foundHeadstage + " was found on " + PortController::getPortName(offset) + ". \n\n";
msg += "Select one of the options below to continue:\n";
msg += " [Keep Current] to keep " + selectedHeadstage + " selected.\n";
msg += " [Update] to change the selected headstage to " + foundHeadstage + ".\n - Note: this will delete any settings that were modified.";
@@ -300,13 +349,14 @@ void OnixSourceCanvas::askKeepUpdate(PortName port, String foundHeadstage, OnixD
switch (result)
{
case 0: // Update
+ {
+ PortName port = PortController::getPortFromIndex(offset);
removeTabs(port);
- {
- CustomTabComponent* tab = addTopLevelTab(getTopLevelTabName(port, foundHeadstage), (int)port);
- populateSourceTabs(tab, devices);
- }
- break;
+ CustomTabComponent* tab = addTopLevelTab(getTopLevelTabName(port, foundHeadstage), (int)port);
+ populateSourceTabs(tab, devices);
+ }
+ break;
case 1: // Keep Current
break;
default:
@@ -317,7 +367,7 @@ void OnixSourceCanvas::askKeepUpdate(PortName port, String foundHeadstage, OnixD
void OnixSourceCanvas::refreshTabs()
{
auto selectedMap = createSelectedMap(settingsInterfaces);
- auto foundMap = source->createDeviceMap();
+ auto foundMap = source->createDeviceMap(true);
if (selectedMap != foundMap)
{
@@ -326,8 +376,8 @@ void OnixSourceCanvas::refreshTabs()
for (const auto& [key, _] : selectedMap) { selectedIndices.emplace_back(key); }
for (const auto& [key, _] : foundMap) { foundIndices.emplace_back(key); }
- auto selectedPorts = PortController::getUniquePortsFromIndices(selectedIndices);
- auto foundPorts = PortController::getUniquePortsFromIndices(foundIndices);
+ auto selectedOffsets = PortController::getUniqueOffsetsFromIndices(selectedIndices);
+ auto foundOffsets = PortController::getUniqueOffsetsFromIndices(foundIndices);
if (foundIndices.size() == 0) // NB: No devices found, inform the user if they were expecting to find something
{
@@ -336,24 +386,24 @@ void OnixSourceCanvas::refreshTabs()
AlertWindow::showMessageBox(
MessageBoxIconType::WarningIcon,
"No Headstages Found",
- "No headstages were found when connecting. Double check that the correct headstage is selected. " +
- String("If the correct headstage is selected, try pressing disconnect / connect again.\n\n") +
- String("If the port voltage is manually set, try clearing the value and letting the automated voltage discovery algorithm run.")
+ "No headstages were found when connecting. Double check that the correct headstage is selected. " +
+ String("If the correct headstage is selected, try pressing disconnect / connect again.\n\n") +
+ String("If the port voltage is manually set, try clearing the value and letting the automated voltage discovery algorithm run.")
);
}
}
else if (selectedIndices.size() == 0) // NB: No headstages selected, add all found headstages
{
- for (auto& [port, headstageName] : source->getHeadstageMap())
+ for (auto& [offset, headstageName] : source->getHeadstageMap())
{
- addHeadstage(headstageName, port);
+ addHub(headstageName, offset);
}
}
- else if (selectedPorts.size() == foundPorts.size()) // NB: Same number of ports selected and found
+ else if (selectedOffsets.size() == foundOffsets.size()) // NB: Same number of ports selected and found
{
auto headstages = source->getHeadstageMap();
- if (selectedPorts.size() == 1)
+ if (selectedOffsets.size() == 1)
{
if (headstages.size() != 1)
{
@@ -361,24 +411,24 @@ void OnixSourceCanvas::refreshTabs()
return;
}
- if (selectedPorts[0] == foundPorts[0]) // NB: Selected headstage is different from the found headstage on the same port
+ if (selectedOffsets[0] == foundOffsets[0]) // NB: Selected headstage is different from the found headstage on the same port
{
- askKeepUpdate(selectedPorts[0], headstages[foundPorts[0]], source->getDataSources());
+ askKeepUpdate(selectedOffsets[0], headstages[foundOffsets[0]], source->getDataSources());
}
else // NB: Selected headstage on one port is not found, and the found headstage is not selected on the other port
{
- askKeepRemove(selectedPorts[0]);
+ askKeepRemove(selectedOffsets[0]);
- addHeadstage(headstages[foundPorts[0]], foundPorts[0]);
+ addHub(headstages[foundOffsets[0]], foundOffsets[0]);
}
}
else // NB: Two headstages are selected on different ports, and at least one of those headstages does not match the found headstages
{
- for (auto port : foundPorts)
+ for (auto offset : foundOffsets)
{
- if (headstages[port] != editor->getHeadstageSelected(port))
+ if (headstages[offset] != editor->getHeadstageSelected(offset))
{
- askKeepUpdate(port, headstages[port], source->getDataSourcesFromPort(port));
+ askKeepUpdate(offset, headstages[offset], source->getDataSourcesFromOffset(offset));
}
}
}
@@ -387,37 +437,37 @@ void OnixSourceCanvas::refreshTabs()
{
auto headstages = source->getHeadstageMap();
- if (selectedPorts.size() > foundPorts.size()) // NB: More headstages selected than found
+ if (selectedOffsets.size() > foundOffsets.size()) // NB: More headstages selected than found
{
- for (auto port : selectedPorts)
+ for (auto offset : selectedOffsets)
{
- if (port == foundPorts[0])
+ if (offset == foundOffsets[0])
{
- if (headstages[port] != editor->getHeadstageSelected(port))
+ if (headstages[offset] != editor->getHeadstageSelected(offset))
{
- askKeepUpdate(port, headstages[port], source->getDataSourcesFromPort(port));
+ askKeepUpdate(offset, headstages[offset], source->getDataSourcesFromOffset(offset));
}
}
else
{
- askKeepRemove(port);
+ askKeepRemove(offset);
}
}
}
else // NB: More headstages found than selected
{
- for (auto port : foundPorts)
+ for (auto offset : foundOffsets)
{
- if (port == selectedPorts[0])
+ if (offset == selectedOffsets[0])
{
- if (headstages[port] != editor->getHeadstageSelected(port))
+ if (headstages[offset] != editor->getHeadstageSelected(offset))
{
- askKeepUpdate(port, headstages[port], source->getDataSourcesFromPort(port));
+ askKeepUpdate(offset, headstages[offset], source->getDataSourcesFromOffset(offset));
}
}
else
{
- addHeadstage(headstages[port], port);
+ addHub(headstages[offset], offset);
}
}
}
diff --git a/Source/OnixSourceCanvas.h b/Source/OnixSourceCanvas.h
index ba8b059..c7bbced 100644
--- a/Source/OnixSourceCanvas.h
+++ b/Source/OnixSourceCanvas.h
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2020 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -21,8 +20,7 @@
*/
-# ifndef __ONIXSOURCECANVAS_H__
-# define __ONIXSOURCECANVAS_H__
+#pragma once
#include
@@ -31,6 +29,8 @@
#include "OnixSourceEditor.h"
#include "OnixSource.h"
+class OnixSource;
+
/**
TabBarButton with custom appearance
@@ -83,12 +83,6 @@ class OnixSourceCanvas : public Visualizer
/** Constructor */
OnixSourceCanvas(GenericProcessor*, OnixSourceEditor*, OnixSource*);
- /** Destructor */
- ~OnixSourceCanvas();
-
- /** Fills background */
- void paint(Graphics& g);
-
/** Renders the Visualizer on each animation callback cycle */
void refresh() override;
@@ -120,8 +114,8 @@ class OnixSourceCanvas : public Visualizer
/** Stops animation of sub-interfaces */
void stopAcquisition();
- /** Add the headstage and all of its devices to the canvas */
- void addHeadstage(String headstage, PortName port);
+ /** Add the hub and all of its devices to the canvas */
+ void addHub(String, int);
/** Called when the basestation is created or refreshed */
void populateSourceTabs(CustomTabComponent*, OnixDeviceVector);
@@ -174,15 +168,13 @@ class OnixSourceCanvas : public Visualizer
Create an alert window that asks whether to keep the selected headstage on the given port,
or to remove it since the hardware was not found
*/
- void askKeepRemove(PortName port);
+ void askKeepRemove(int offset);
/**
Create an alert window that asks whether to keep the selected headstage on the given port,
or to update to the headstage that was found
*/
- void askKeepUpdate(PortName port, String foundHeadstage, OnixDeviceVector devices);
+ void askKeepUpdate(int offset, String foundHeadstage, OnixDeviceVector devices);
JUCE_LEAK_DETECTOR(OnixSourceCanvas);
};
-
-# endif // __ONIXSOURCECANVAS_H__
diff --git a/Source/OnixSourceEditor.cpp b/Source/OnixSourceEditor.cpp
index 523ab57..af9b6c2 100644
--- a/Source/OnixSourceEditor.cpp
+++ b/Source/OnixSourceEditor.cpp
@@ -1,8 +1,7 @@
/*
------------------------------------------------------------------
- This file is part of the Open Ephys GUI
- Copyright (C) 2023 Allen Institute for Brain Science and Open Ephys
+ Copyright (C) Open Ephys
------------------------------------------------------------------
@@ -25,17 +24,22 @@
#include "OnixSource.h"
OnixSourceEditor::OnixSourceEditor(GenericProcessor* parentNode, OnixSource* source_)
- : VisualizerEditor(parentNode, "Onix Source", 200), source(source_)
+ : VisualizerEditor(parentNode, "Onix Source", 220), source(source_)
{
canvas = nullptr;
FontOptions fontOptionSmall = FontOptions("Fira Code", "Regular", 12.0f);
FontOptions fontOptionTitle = FontOptions("Fira Code", "Bold", 15.0f);
+ memoryUsage = std::make_unique(parentNode);
+ memoryUsage->setBounds(10, 30, 15, 95);
+ memoryUsage->setTooltip("Monitors the percent of the hardware memory buffer used.");
+ addAndMakeVisible(memoryUsage.get());
+
if (source->isContextInitialized())
{
portLabelA = std::make_unique