diff --git a/Source/Processors/DataThreads/RHD2000Editor.cpp b/Source/Processors/DataThreads/RHD2000Editor.cpp index be4e292554735c41eef02fa816bbd5f729b667f3..c919920f7b57ff07a80c77d64c55b1b9d70e51df 100644 --- a/Source/Processors/DataThreads/RHD2000Editor.cpp +++ b/Source/Processors/DataThreads/RHD2000Editor.cpp @@ -27,6 +27,7 @@ #include "../Editors/ChannelSelector.h" #include "../SourceNode/SourceNode.h" +#include "../RecordNode/RecordNode.h" #include "RHD2000Thread.h" #ifdef WIN32 @@ -62,6 +63,18 @@ FPGAchannelList::FPGAchannelList(GenericProcessor* proc_, Viewport* p, FPGAcanva impedanceButton->addListener(this); addAndMakeVisible(impedanceButton); + RHD2000Editor *e = static_cast<RHD2000Editor*>(proc->getEditor()); + saveImpedanceButton = new ToggleButton("Save impedance measurements"); + saveImpedanceButton->setBounds(430,10,110,25); + saveImpedanceButton->setToggleState(e->getSaveImpedance(),dontSendNotification); + saveImpedanceButton->addListener(this); + addAndMakeVisible(saveImpedanceButton); + + autoMeasureButton = new ToggleButton("Measure impedance at acquisition start"); + autoMeasureButton->setBounds(550,10,150,25); + autoMeasureButton->setToggleState(e->getAutoMeasureImpedance(),dontSendNotification); + autoMeasureButton->addListener(this); + addAndMakeVisible(autoMeasureButton); gains.clear(); gains.add(0.01); @@ -91,11 +104,19 @@ void FPGAchannelList::paint(Graphics& g) void FPGAchannelList::buttonClicked(Button* btn) { + RHD2000Editor* p = (RHD2000Editor*)proc->getEditor(); if (btn == impedanceButton) { - RHD2000Editor* p = (RHD2000Editor*)proc->getEditor(); p->measureImpedance(); } + else if (btn == saveImpedanceButton) + { + p->setSaveImpedance(btn->getToggleState()); + } + else if (btn == autoMeasureButton) + { + p->setAutoMeasureImpedance(btn->getToggleState()); + } } void FPGAchannelList::update() @@ -513,6 +534,8 @@ RHD2000Editor::RHD2000Editor(GenericProcessor* parentNode, canvas = nullptr; desiredWidth = 330; tabText = "FPGA"; + measureWhenRecording = false; + saveImpedances = false; // add headstage-specific controls (currently just an enable/disable button) for (int i = 0; i < 4; i++) @@ -666,8 +689,57 @@ void RHD2000Editor::measureImpedance() Array<float> magnitude, phase; board->runImpedanceTest(stream,channel,magnitude,phase); + if (canvas == nullptr) + VisualizerEditor::canvas = createNewCanvas(); // update components... canvas->updateImpedance(stream,channel,magnitude,phase); + if (saveImpedances) + { + getProcessorGraph()->getRecordNode()->createNewDirectory(); + + String path(getProcessorGraph()->getRecordNode()->getDataDirectory().getFullPathName() + + File::separatorString + "impedance_measurement.xml"); + std::cout << "Saving impedance measurements in " << path << std::endl; + File file(path); + + if (!file.getParentDirectory().exists()) + file.getParentDirectory().createDirectory(); + + XmlDocument doc(file); + ScopedPointer<XmlElement> xml = new XmlElement("CHANNEL_IMPEDANCES"); + for (int i = 0; i < channel.size(); i++) + { + XmlElement* chan = new XmlElement("CHANNEL"); + chan->setAttribute("name",board->getChannelName(DATA_CHANNEL,stream[i],channel[i])); + chan->setAttribute("stream",stream[i]); + chan->setAttribute("channel_number",channel[i]); + chan->setAttribute("magnitude",magnitude[i]); + chan->setAttribute("phase",phase[i]); + xml->addChildElement(chan); + } + xml->writeToFile(file,String::empty); + } + +} + +void RHD2000Editor::setSaveImpedance(bool en) +{ + saveImpedances = en; +} + +void RHD2000Editor::setAutoMeasureImpedance(bool en) +{ + measureWhenRecording = en; +} + +bool RHD2000Editor::getSaveImpedance() +{ + return saveImpedances; +} + +bool RHD2000Editor::getAutoMeasureImpedance() +{ + return measureWhenRecording; } void RHD2000Editor::comboBoxChanged(ComboBox* comboBox) @@ -758,6 +830,8 @@ void RHD2000Editor::channelChanged(int chan) void RHD2000Editor::startAcquisition() { + if (measureWhenRecording) + measureImpedance(); channelSelector->startAcquisition(); @@ -802,6 +876,8 @@ void RHD2000Editor::saveCustomParameters(XmlElement* xml) xml->setAttribute("DAC_HPF", dacHPFcombo->getSelectedId()); xml->setAttribute("DSPOffset", dspoffsetButton->getToggleState()); xml->setAttribute("DSPCutoffFreq", dspInterface->getDspCutoffFreq()); + xml->setAttribute("save_impedance_measurements",saveImpedances); + xml->setAttribute("auto_measure_impedances",measureWhenRecording); } void RHD2000Editor::loadCustomParameters(XmlElement* xml) @@ -821,6 +897,8 @@ void RHD2000Editor::loadCustomParameters(XmlElement* xml) dacHPFcombo->setSelectedId(xml->getIntAttribute("DAC_HPF")); dspoffsetButton->setToggleState(xml->getBoolAttribute("DSPOffset"), sendNotification); dspInterface->setDspCutoffFreq(xml->getDoubleAttribute("DSPCutoffFreq")); + saveImpedances = xml->getBoolAttribute("save_impedance_measurements"); + measureWhenRecording = xml->getBoolAttribute("auto_measure_impedances"); } diff --git a/Source/Processors/DataThreads/RHD2000Editor.h b/Source/Processors/DataThreads/RHD2000Editor.h index 290e95d011035f2cf2c63e56bd7356bd69df5c41..4caa6630cf1633bf7e5a9f851e83cf075d79d7bb 100644 --- a/Source/Processors/DataThreads/RHD2000Editor.h +++ b/Source/Processors/DataThreads/RHD2000Editor.h @@ -81,6 +81,8 @@ private: Viewport *viewport; FPGAcanvas *canvas; ScopedPointer<UtilityButton> impedanceButton; + ScopedPointer<ToggleButton> saveImpedanceButton; + ScopedPointer<ToggleButton> autoMeasureButton; ScopedPointer<ComboBox> numberingScheme; ScopedPointer<Label> numberingSchemeLabel; OwnedArray<Label> staticLabels; @@ -176,6 +178,12 @@ public: void loadCustomParameters(XmlElement* xml); Visualizer* createNewCanvas(void); void measureImpedance(); + + void setSaveImpedance(bool en); + void setAutoMeasureImpedance(bool en); + bool getSaveImpedance(); + bool getAutoMeasureImpedance(); + private: OwnedArray<HeadstageOptionsInterface> headstageOptionsInterfaces; @@ -196,6 +204,8 @@ private: ScopedPointer<Label> audioLabel,ttlSettleLabel,dacHPFlabel ; + bool saveImpedances, measureWhenRecording; + RHD2000Thread* board; FPGAcanvas *canvas; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RHD2000Editor); diff --git a/Source/Processors/DataThreads/RHD2000Thread.cpp b/Source/Processors/DataThreads/RHD2000Thread.cpp index 83c08fbd71d51056bb40944045d9249dc3f71ebf..afe46cd62ea36febd679915befb35bc107863e13 100644 --- a/Source/Processors/DataThreads/RHD2000Thread.cpp +++ b/Source/Processors/DataThreads/RHD2000Thread.cpp @@ -733,6 +733,17 @@ int RHD2000Thread::modifyChannelName(channelType t, int str, int ch, String newN return -1; } +String RHD2000Thread::getChannelName(channelType t, int str, int ch) +{ + for (int k=0; k<Names.size(); k++) + { + if (type[k] == t && stream[k] == str && originalChannelNumber[k] == ch) + { + return Names[k]; + } + } +} + int RHD2000Thread::modifyChannelGain(channelType t, int str, int ch, float gain) { String dummy; diff --git a/Source/Processors/DataThreads/RHD2000Thread.h b/Source/Processors/DataThreads/RHD2000Thread.h index 6f28cedc6ed3465e04b55922a3afc954555d5070..48f12de158c49f7c9b749d337e9e804615c54cb4 100644 --- a/Source/Processors/DataThreads/RHD2000Thread.h +++ b/Source/Processors/DataThreads/RHD2000Thread.h @@ -115,6 +115,8 @@ public: void setDACthreshold(int dacOutput, float threshold); void setDefaultNamingScheme(int scheme); + String getChannelName(channelType t, int str, int ch); + private: void setDefaultChannelNamesAndType(); bool channelModified(channelType t, int str, int k, String &oldName, float &oldGain, int &index); diff --git a/Source/Processors/RecordNode/RecordNode.cpp b/Source/Processors/RecordNode/RecordNode.cpp index 0f5786738f3fdf4c0823219fc7d201d3ecafc15e..1bc936469c6d91ba457a8bcf2953894ba9a542f9 100755 --- a/Source/Processors/RecordNode/RecordNode.cpp +++ b/Source/Processors/RecordNode/RecordNode.cpp @@ -25,6 +25,7 @@ #include "../ProcessorGraph/ProcessorGraph.h" #include "../../UI/EditorViewport.h" #include "../../UI/ControlPanel.h" +#include "RecordEngine.h" #define EVERY_ENGINE for(int eng = 0; eng < engineArray.size(); eng++) engineArray[eng] diff --git a/Source/Processors/RecordNode/RecordNode.h b/Source/Processors/RecordNode/RecordNode.h index 446ba15968ced19e3c20a4330d1f07958c46da3e..032db2f73c295c78b5bf9750d8cc88f8a48b7285 100755 --- a/Source/Processors/RecordNode/RecordNode.h +++ b/Source/Processors/RecordNode/RecordNode.h @@ -30,13 +30,15 @@ #include "../GenericProcessor/GenericProcessor.h" #include "../Channel/Channel.h" -#include "RecordEngine.h" - #define HEADER_SIZE 1024 #define BLOCK_LENGTH 1024 +struct SpikeRecordInfo; +struct SpikeObject; +class RecordEngine; + /** Receives inputs from all processors that want to save their data. diff --git a/Source/UI/ControlPanel.h b/Source/UI/ControlPanel.h index 9ac30aef6580471fc41922b4d2ff472971547959..c9d178f0dbe8fbccaa55b140e6664429186667b4 100755 --- a/Source/UI/ControlPanel.h +++ b/Source/UI/ControlPanel.h @@ -29,6 +29,7 @@ #include "../Processors/AudioNode/AudioEditor.h" #include "../Processors/ProcessorGraph/ProcessorGraph.h" #include "../Processors/RecordNode/RecordNode.h" +#include "../Processors/RecordNode/RecordEngine.h" #include "CustomLookAndFeel.h" #include "../AccessClass.h" #include "../Processors/Editors/GenericEditor.h" // for UtilityButton