diff --git a/Builds/Linux/build/libokFrontPanel.so b/Builds/Linux/build/libokFrontPanel.so index 6eb1a88a56608f52474e05e824176e04535e4a9e..f8c6386c193b5822adc9a585a0445c1e5efa2c95 100755 Binary files a/Builds/Linux/build/libokFrontPanel.so and b/Builds/Linux/build/libokFrontPanel.so differ diff --git a/Source/Processors/DataThreads/DataThread.h b/Source/Processors/DataThreads/DataThread.h index e91cb608799657b99fc1bbadc232f179ed08b486..c78342bcad77bef9e6306c319b1b8a19f3f78b76 100755 --- a/Source/Processors/DataThreads/DataThread.h +++ b/Source/Processors/DataThreads/DataThread.h @@ -27,6 +27,7 @@ #include "../../../JuceLibraryCode/JuceHeader.h" #include <stdio.h> #include "DataBuffer.h" +#include "../GenericProcessor.h" class SourceNode; @@ -92,6 +93,30 @@ public: { return 0; } + virtual int modifyChannelName(channelType t, int stream, int ch, String newName) + { + return -1; + } + virtual int modifyChannelGain(channelType t, int stream, int ch, float gain) + { + return -1; + } + virtual void setDefaultNamingScheme(int scheme) + { + } + + virtual void getChannelsInfo(StringArray &Names, Array<channelType> &type, Array<int> &stream, Array<int> &originalChannelNumber, Array<float> &gains) + { + }; + virtual void getEventChannelNames(StringArray &names) + { + } + + /** Returns the number of ADC channels of the data source.*/ + virtual int getNumADCchannels() + { + return 0; + } /** Changes the names of channels, if the thread needs custom names. */ virtual void updateChannelNames() { } diff --git a/Source/Processors/Editors/RHD2000Editor.cpp b/Source/Processors/Editors/RHD2000Editor.cpp index f5a1da51519374e7876e528607c4ec9de4b32cba..b91873d60ee28c9c6ec5da48cecba4b15a396723 100644 --- a/Source/Processors/Editors/RHD2000Editor.cpp +++ b/Source/Processors/Editors/RHD2000Editor.cpp @@ -25,17 +25,463 @@ #include "../../UI/EditorViewport.h" #include "ChannelSelector.h" - +#include "../SourceNode.h" #include "../DataThreads/RHD2000Thread.h" + +FPGAchannelList::FPGAchannelList(GenericProcessor* proc_, Viewport *p, FPGAcanvas*c) : proc(proc_), viewport(p), canvas(c) +{ + channelComponents.clear(); + + numberingSchemeLabel = new Label("Numbering scheme:","Numbering scheme:"); + numberingSchemeLabel->setEditable(false); + numberingSchemeLabel->setBounds(10,10,150, 25); + numberingSchemeLabel->setColour(Label::textColourId,juce::Colours::white); + addAndMakeVisible(numberingSchemeLabel); + + numberingScheme = new ComboBox("numberingScheme"); + numberingScheme->addItem("Continuous",1); + numberingScheme->addItem("Per Stream",2); + numberingScheme->setBounds(160,10,100,25); + numberingScheme->addListener(this); + numberingScheme->setSelectedId(1, dontSendNotification); + addAndMakeVisible(numberingScheme); + + impedanceButton = new UtilityButton("Measure Impedance", Font("Default", 13, Font::plain)); + impedanceButton->setRadius(3); + impedanceButton->setBounds(280,10,140,25); + impedanceButton->addListener(this); + addAndMakeVisible(impedanceButton); + + + gains.clear(); + gains.add( 0.01); + gains.add( 0.1); + gains.add( 1); + gains.add( 2); + gains.add( 5); + gains.add( 10); + gains.add( 20); + gains.add( 50); + gains.add( 100); + gains.add( 500); + gains.add( 1000); + + + update(); +} + +FPGAchannelList::~FPGAchannelList() +{ + +} + +void FPGAchannelList::paint(Graphics& g) +{ +} + +void FPGAchannelList::buttonClicked(Button *btn) +{ + if (btn == impedanceButton) + { + RHD2000Editor *p = (RHD2000Editor*)proc->getEditor(); + p->measureImpedance(); + } +} + +void FPGAchannelList::update() +{ + // Query processor for number of channels, types, gains, etc... and update the UI + channelComponents.clear(); + staticLabels.clear(); + StringArray names; + Array<float> oldgains; + proc->getChannelsInfo(names,types,stream,orig_number,oldgains); + int numChannels = names.size(); + + // find out which streams are active. + bool streamActive[MAX_NUM_DATA_STREAMS+1]; + bool adcActive = false; + int numActiveStreams = 0; + int streamColumn[MAX_NUM_DATA_STREAMS+1]; + int numChannelsPerStream[MAX_NUM_DATA_STREAMS+1]; + + for (int k=0;k<MAX_NUM_DATA_STREAMS+1;k++) { + numChannelsPerStream[k] = 0; + streamActive[k] = false; + streamColumn[k] = 0; + } + int columnWidth =330; + + for (int k=0;k<numChannels;k++) + { + if (streamActive[stream[k]] == false) + { + streamColumn[stream[k]] = numActiveStreams*columnWidth; + numActiveStreams++; + streamActive[stream[k]] = true; + } + } + + StringArray streamNames; + streamNames.add("Port A1"); + streamNames.add("Port A2"); + streamNames.add("Port B1"); + streamNames.add("Port B2"); + streamNames.add("Port C1"); + streamNames.add("Port C2"); + streamNames.add("Port D1"); + streamNames.add("Port D2"); + streamNames.add("ADC"); + + for (int k=0;k<MAX_NUM_DATA_STREAMS+1;k++) + { + if (streamActive[k]) + { + Label *lbl = new Label(streamNames[k],streamNames[k]); + lbl->setEditable(false); + lbl->setBounds(10+streamColumn[k],40,columnWidth, 25); + lbl->setJustificationType(juce::Justification::centred); + lbl->setColour(Label::textColourId,juce::Colours::white); + staticLabels.add(lbl); + addAndMakeVisible(lbl); + + } + + } + + // add buttons for all DATA,AUX,channels + for (int k=0;k<numChannels;k++) + { + int channelGainIndex = 1; + float ch_gain = oldgains[k]/proc->getDefaultBitVolts(); + for (int j=0;j<gains.size();j++) { + if (fabs(gains[j]-ch_gain) < 1e-3) { + channelGainIndex = j; + break; + } + } + + FPGAchannelComponent* comp = new FPGAchannelComponent(this, stream[k],orig_number[k],types[k],channelGainIndex+1, names[k],gains); + comp->setBounds(10+streamColumn[stream[k]],70+numChannelsPerStream[stream[k]]*22,columnWidth,22); + numChannelsPerStream[stream[k]]++; + + comp->setUserDefinedData(k); + addAndMakeVisible(comp); + channelComponents.add(comp); + } + + StringArray ttlNames; + proc->getEventChannelNames(ttlNames); + // add buttons for TTL channels + for (int k=0;k<ttlNames.size();k++) + { + FPGAchannelComponent* comp = new FPGAchannelComponent(this,-1,k, EVENT_CHANNEL,-1, ttlNames[k],gains); + comp->setBounds( 10+numActiveStreams*columnWidth,70+k*22,columnWidth,22); + comp->setUserDefinedData(k); + addAndMakeVisible(comp); + channelComponents.add(comp); + } + + Label *lbl = new Label("TTL Events","TTL Events"); + lbl->setEditable(false); + lbl->setBounds(numActiveStreams*columnWidth,40,columnWidth, 25); + lbl->setJustificationType(juce::Justification::centred); + lbl->setColour(Label::textColourId,juce::Colours::white); + staticLabels.add(lbl); + addAndMakeVisible(lbl); + + +} + +void FPGAchannelList::disableAll() +{ + for (int k=0;k<channelComponents.size();k++) + { + channelComponents[k]->disableEdit(); + } +} + +void FPGAchannelList::enableAll() +{ + for (int k=0;k<channelComponents.size();k++) + { + channelComponents[k]->enableEdit(); + } + +} + +void FPGAchannelList::setNewGain(int stream, int channel, channelType type, float gain) +{ + SourceNode* p = (SourceNode*) proc; + p->modifyChannelGain(stream, channel, type, gain, true); +} + +void FPGAchannelList::setNewName(int stream, int channelIndex, channelType t, String newName) +{ + proc->modifyChannelName(t, stream, channelIndex, newName, true); +} + +void FPGAchannelList::updateButtons() +{ +} + +int FPGAchannelList::getNumChannels() +{ + return 0; +} + +void FPGAchannelList::comboBoxChanged(ComboBox *b) +{ + if (b == numberingScheme) + { + SourceNode* p = (SourceNode* )proc; + int scheme = numberingScheme->getSelectedId(); + if (scheme == 1) + { + p->setDefaultNamingScheme(scheme); + } else if (scheme == 2) + { + p->setDefaultNamingScheme(scheme); + } + update(); + + } +} + +void FPGAchannelList::updateImpedance(Array<int> streams, Array<int> channels, Array<float> magnitude, Array<float> phase) +{ + for (int k=0;k<streams.size();k++) + { + for (int j=k;j<stream.size();j++) + { + if (stream[j] == streams[k] && types[j] == DATA_CHANNEL && orig_number[j] == channels[k]) { + channelComponents[j]->setImpedanceValues(magnitude[k],phase[k]); + break; + } + + } + } + +} + + +/****************************************************/ +FPGAchannelComponent::FPGAchannelComponent(FPGAchannelList* cl, int stream_, int ch, channelType t, int gainIndex_, String N, Array<float> gains_) : channelList(cl),name(N), channel(ch),gainIndex(gainIndex_), stream(stream_), type(t), gains(gains_) +{ + Font f = Font("Small Text", 13, Font::plain); + + staticLabel = new Label("Channel","Channel"); + staticLabel->setFont(f); + staticLabel->setEditable(false); + addAndMakeVisible(staticLabel); + + editName = new Label(name,name); + editName->setFont(f); + editName->setEditable(true); + editName->setColour(Label::backgroundColourId,juce::Colours::lightgrey); + editName->addListener(this); + addAndMakeVisible(editName); + if (gainIndex > 0) + { + + gainComboBox = new ComboBox("Gains"); + for (int k=0;k<gains.size();k++) + { + if (gains[k] < 1) { + gainComboBox->addItem("x"+String(gains[k],2),k+1); + } else + { + gainComboBox->addItem("x"+String((int)gains[k]),k+1); + } + } + gainComboBox->setSelectedId(gainIndex, sendNotification); + gainComboBox->addListener(this); + addAndMakeVisible(gainComboBox); + } else + { + gainComboBox = nullptr; + } + + if (type == DATA_CHANNEL) + { + impedance = new Label("Impedance","? Ohm"); + impedance->setFont(Font("Default", 13, Font::plain)); + impedance->setEditable(false); + addAndMakeVisible(impedance); + } else + { + impedance = nullptr; + } +} +FPGAchannelComponent::~FPGAchannelComponent() +{ + +} + +void FPGAchannelComponent::setImpedanceValues(float mag, float phase) +{ + if (impedance != nullptr) + { + if (mag > 10000) + impedance->setText(String(mag/1e6,2)+" mOhm, "+String((int)phase) + " deg",juce::NotificationType::dontSendNotification); + else if (mag > 1000) + impedance->setText(String(mag/1e3,0)+" kOhm, "+String((int)phase) + " deg" ,juce::NotificationType::dontSendNotification); + else + impedance->setText(String(mag,0)+" Ohm, "+String((int)phase) + " deg" ,juce::NotificationType::dontSendNotification); + } else + { + + } +} + +void FPGAchannelComponent::comboBoxChanged(ComboBox* comboBox) +{ + if (comboBox == gainComboBox) + { + int newGainIndex = gainComboBox->getSelectedId(); + float mult = gains[newGainIndex-1]; + float bitvolts = channelList->proc->getDefaultBitVolts(); + channelList->setNewGain(stream,channel,type, mult*bitvolts); + } +} +void FPGAchannelComponent::labelTextChanged(Label* lbl) +{ + // channel name change + String newName = lbl->getText(); + channelList->setNewName(stream,channel, type, newName); +} + +void FPGAchannelComponent::disableEdit() +{ + editName->setEnabled(false); +} + +void FPGAchannelComponent::enableEdit() +{ + editName->setEnabled(true); +} + +void FPGAchannelComponent::buttonClicked(Button *btn) +{ +} + +void FPGAchannelComponent::setUserDefinedData(int d) +{ +} + +int FPGAchannelComponent::getUserDefinedData() +{ + return 0; +} + +void FPGAchannelComponent::resized() +{ + editName->setBounds(0,0,90,20); + if (gainComboBox != nullptr) + { + gainComboBox->setBounds(100,0,70,20); + } + if (impedance != nullptr) + { + impedance->setBounds(180,0,130,20); + } + +} + + + +/**********************************************/ + +FPGAcanvas::FPGAcanvas(GenericProcessor* n) : processor(n) +{ + + channelsViewport = new Viewport(); + channelList = new FPGAchannelList(processor, channelsViewport, this); + channelsViewport->setViewedComponent(channelList, false); + channelsViewport->setScrollBarsShown(true, true); + addAndMakeVisible(channelsViewport); + + resized(); + update(); +} + +FPGAcanvas::~FPGAcanvas() +{ +} + +void FPGAcanvas::setParameter(int x, float f) +{ + +} + +void FPGAcanvas::setParameter(int a, int b, int c, float d) +{ +} + +void FPGAcanvas::paint(Graphics& g) +{ + g.fillAll(Colours::grey); + +} + +void FPGAcanvas::refresh() +{ + repaint(); +} + +void FPGAcanvas::refreshState() +{ + resized(); +} + + +void FPGAcanvas::beginAnimation() +{ +} + +void FPGAcanvas::endAnimation() +{ +} + +void FPGAcanvas::update() +{ + // create channel buttons (name, gain, recording, impedance, ... ?) + channelList->update(); +} + +void FPGAcanvas::resized() +{ + int screenWidth = getWidth(); + int screenHeight = getHeight(); + + int scrollBarThickness = channelsViewport->getScrollBarThickness(); + int numChannels = 35; // max channels per stream? (32+3)*2 + + channelsViewport->setBounds(0,0,getWidth(),getHeight()); + channelList->setBounds(0,0,getWidth()-scrollBarThickness, 200+22*numChannels); +} + +void FPGAcanvas::buttonClicked(Button* button) +{ +} + +void FPGAcanvas::updateImpedance(Array<int> streams, Array<int> channels, Array<float> magnitude, Array<float> phase) +{ + channelList->updateImpedance(streams, channels, magnitude, phase); +} + +/***********************************************************************/ + RHD2000Editor::RHD2000Editor(GenericProcessor* parentNode, RHD2000Thread* board_, bool useDefaultParameterEditors ) - : GenericEditor(parentNode, useDefaultParameterEditors), board(board_) + : VisualizerEditor(parentNode, useDefaultParameterEditors), board(board_) { - desiredWidth = 260; - + canvas = nullptr; + desiredWidth = 330; + tabText = "FPGA"; // add headstage-specific controls (currently just an enable/disable button) for (int i = 0; i < 4; i++) { @@ -75,17 +521,15 @@ RHD2000Editor::RHD2000Editor(GenericProcessor* parentNode, addAndMakeVisible(button); button->addListener(this); - + if (i == 0) { button->setTooltip("Audio monitor left channel"); - } - else - { + } else { button->setTooltip("Audio monitor right channel"); } } - + audioLabel = new Label("audio label", "Audio out"); audioLabel->setBounds(180,25,75,15); @@ -97,8 +541,8 @@ RHD2000Editor::RHD2000Editor(GenericProcessor* parentNode, audioInterface = new AudioInterface(board, this); addAndMakeVisible(audioInterface); audioInterface->setBounds(165, 65, 65, 50); - - + + adcButton = new UtilityButton("ADC 1-8", Font("Small Text", 13, Font::plain)); adcButton->setRadius(3.0f); adcButton->setBounds(165,100,65,18); @@ -107,6 +551,50 @@ RHD2000Editor::RHD2000Editor(GenericProcessor* parentNode, adcButton->setTooltip("Enable/disable ADC channels"); addAndMakeVisible(adcButton); + ttlSettleLabel = new Label("TTL Settle","TTL Settle"); + ttlSettleLabel->setFont( Font("Small Text", 11, Font::plain)); + ttlSettleLabel->setBounds(245,80,100,20); + ttlSettleLabel->setColour(Label::textColourId, Colours::darkgrey); + addAndMakeVisible(ttlSettleLabel); + + + ttlSettleCombo = new ComboBox("FastSettleComboBox"); + ttlSettleCombo->setBounds(250,100,60,18); + ttlSettleCombo->addListener(this); + ttlSettleCombo->addItem("-",1); + for (int k=0; k<8; k++) + { + ttlSettleCombo->addItem("TTL"+String(1+k),2+k); + } + ttlSettleCombo->setSelectedId(1, sendNotification); + addAndMakeVisible(ttlSettleCombo); + + dacTTLButton = new UtilityButton("DAC TTL", Font("Small Text", 13, Font::plain)); + dacTTLButton->setRadius(3.0f); + dacTTLButton->setBounds(250,25,65,18); + dacTTLButton->addListener(this); + dacTTLButton->setClickingTogglesState(true); + dacTTLButton->setTooltip("Enable/disable DAC Threshold TTL Output"); + addAndMakeVisible(dacTTLButton); + + dacHPFlabel = new Label("DAC HPF","DAC HPF"); + dacHPFlabel->setFont( Font("Small Text", 11, Font::plain)); + dacHPFlabel->setBounds(250,42,100,20); + dacHPFlabel->setColour(Label::textColourId, Colours::darkgrey); + addAndMakeVisible(dacHPFlabel); + + dacHPFcombo = new ComboBox("dacHPFCombo"); + dacHPFcombo->setBounds(250,60,60,18); + dacHPFcombo->addListener(this); + dacHPFcombo->addItem("OFF",1); + int HPFvalues[10] = {50,100,200,300,400,500,600,700,800,900}; + for (int k=0; k<10; k++) + { + dacHPFcombo->addItem(String(HPFvalues[k])+" Hz",2+k); + } + dacHPFcombo->setSelectedId(1, sendNotification); + addAndMakeVisible(dacHPFcombo); + } @@ -120,9 +608,46 @@ void RHD2000Editor::scanPorts() rescanButton->triggerClick(); } -void RHD2000Editor::buttonEvent(Button* button) +void RHD2000Editor::measureImpedance() +{ + Array<int> stream, channel; + Array<float> magnitude, phase; + board->runImpedanceTest(stream,channel,magnitude,phase); + + // update components... + canvas->updateImpedance(stream,channel,magnitude,phase); +} + +void RHD2000Editor::comboBoxChanged(ComboBox* comboBox) { + if (comboBox == ttlSettleCombo) + { + int selectedChannel = ttlSettleCombo->getSelectedId(); + if (selectedChannel == 1) + { + board->setFastTTLSettle(false,0); + } else + { + board->setFastTTLSettle(true,selectedChannel-2); + } + } else if (comboBox == dacHPFcombo) + { + int selection = dacHPFcombo->getSelectedId(); + if (selection == 1) + { + board->setDAChpf(100,false); + } else + { + int HPFvalues[10] = {50,100,200,300,400,500,600,700,800,900}; + board->setDAChpf(HPFvalues[selection-2],true); + } + } +} + +void RHD2000Editor::buttonEvent(Button* button) +{ + VisualizerEditor::buttonEvent(button); if (button == rescanButton && !acquisitionIsActive) { board->scanPorts(); @@ -131,7 +656,7 @@ void RHD2000Editor::buttonEvent(Button* button) { headstageOptionsInterfaces[i]->checkEnabledState(); } - + board->updateChannelNames(); } else if (button == electrodeButtons[0]) { @@ -144,7 +669,11 @@ void RHD2000Editor::buttonEvent(Button* button) else if (button == adcButton && !acquisitionIsActive) { board->enableAdcs(button->getToggleState()); + board->updateChannelNames(); getEditorViewport()->makeEditorVisible(this, false, true); + } else if (button == dacTTLButton) + { + board->setTTLoutputMode(dacTTLButton->getToggleState()); } } @@ -158,7 +687,7 @@ void RHD2000Editor::channelChanged(int chan) electrodeButtons[i]->setChannelNum(chan); electrodeButtons[i]->repaint(); - board->assignAudioOut(i, chan-1); + board->assignAudioOut(i, chan); } } } @@ -167,12 +696,11 @@ void RHD2000Editor::startAcquisition() { channelSelector->startAcquisition(); - rescanButton->setEnabledState(false); adcButton->setEnabledState(false); - acquisitionIsActive = true; - + if (canvas !=nullptr) + canvas->channelList->setEnabled(false); } void RHD2000Editor::stopAcquisition() @@ -184,28 +712,52 @@ void RHD2000Editor::stopAcquisition() adcButton->setEnabledState(true); acquisitionIsActive = false; - + if (canvas != nullptr) + canvas->channelList->setEnabled(true); + // canvas->channelList->setEnabled(true); } void RHD2000Editor::saveCustomParameters(XmlElement* xml) { - xml->setAttribute("SampleRate", sampleRateInterface->getSelectedId()); - xml->setAttribute("LowCut", bandwidthInterface->getLowerBandwidth()); - xml->setAttribute("HighCut", bandwidthInterface->getUpperBandwidth()); - xml->setAttribute("ADCsOn", adcButton->getToggleState()); + xml->setAttribute("SampleRate", sampleRateInterface->getSelectedId()); + xml->setAttribute("LowCut", bandwidthInterface->getLowerBandwidth()); + xml->setAttribute("HighCut", bandwidthInterface->getUpperBandwidth()); + xml->setAttribute("ADCsOn", adcButton->getToggleState()); + xml->setAttribute("AudioOutputL", electrodeButtons[0]->getChannelNum()); + xml->setAttribute("AudioOutputR", electrodeButtons[1]->getChannelNum()); + xml->setAttribute("NoiseSlicer", audioInterface->getNoiseSlicerLevel()); + xml->setAttribute("TTLFastSettle", ttlSettleCombo->getSelectedId()); + xml->setAttribute("DAC_TTL", dacTTLButton->getToggleState()); + xml->setAttribute("DAC_HPF", dacHPFcombo->getSelectedId()); } void RHD2000Editor::loadCustomParameters(XmlElement* xml) { - + sampleRateInterface->setSelectedId(xml->getIntAttribute("SampleRate")); bandwidthInterface->setLowerBandwidth(xml->getDoubleAttribute("LowCut")); bandwidthInterface->setUpperBandwidth(xml->getDoubleAttribute("HighCut")); adcButton->setToggleState(xml->getBoolAttribute("ADCsOn"), sendNotification); - + electrodeButtons[0]->setChannelNum(xml->getIntAttribute("AudioOutputL")); + board->assignAudioOut(0, xml->getIntAttribute("AudioOutputL")); + electrodeButtons[1]->setChannelNum(xml->getIntAttribute("AudioOutputR")); + board->assignAudioOut(1, xml->getIntAttribute("AudioOutputR")); + audioInterface->setNoiseSlicerLevel(xml->getIntAttribute("NoiseSlicer")); + ttlSettleCombo->setSelectedId(xml->getIntAttribute("TTLFastSettle")); + dacTTLButton->setToggleState(xml->getBoolAttribute("DAC_TTL"), sendNotification); + dacHPFcombo->setSelectedId(xml->getIntAttribute("DAC_HPF")); } +Visualizer* RHD2000Editor::createNewCanvas() +{ + GenericProcessor* processor = (GenericProcessor*) getProcessor(); + canvas= new FPGAcanvas(processor); + //ActionListener* listener = (ActionListener*) canvas; + //getUIComponent()->registerAnimatedComponent(listener); + return canvas; +} + // Bandwidth Options -------------------------------------------------------------------- BandwidthInterface::BandwidthInterface(RHD2000Thread* board_, @@ -539,6 +1091,8 @@ void HeadstageOptionsInterface::buttonClicked(Button* button) hsButton1->setLabel(String(channelsOnHs1)); board->setNumChannels(hsNumber1, channelsOnHs1); + board->updateChannelNames(); + editor->updateSettings(); } else if (button == hsButton2) @@ -550,6 +1104,8 @@ void HeadstageOptionsInterface::buttonClicked(Button* button) hsButton2->setLabel(String(channelsOnHs2)); board->setNumChannels(hsNumber2, channelsOnHs2); + board->updateChannelNames(); + editor->updateSettings(); } @@ -580,29 +1136,29 @@ void HeadstageOptionsInterface::paint(Graphics& g) // (Direct OpalKelly) Audio Options -------------------------------------------------------------------- AudioInterface::AudioInterface(RHD2000Thread* board_, - RHD2000Editor* editor_) : - board(board_), editor(editor_) + RHD2000Editor* editor_) : +board(board_), editor(editor_) { - + name = "Noise Slicer"; - + lastNoiseSlicerString = "0"; - + actualNoiseSlicerLevel = 0.0f; - + noiseSlicerLevelSelection = new Label("Noise Slicer",lastNoiseSlicerString); // this is currently set in RHD2000Thread, the cleaner would be to set it here again noiseSlicerLevelSelection->setEditable(true,false,false); noiseSlicerLevelSelection->addListener(this); noiseSlicerLevelSelection->setBounds(30,10,30,20); noiseSlicerLevelSelection->setColour(Label::textColourId, Colours::darkgrey); addAndMakeVisible(noiseSlicerLevelSelection); - - + + } AudioInterface::~AudioInterface() { - + } @@ -612,28 +1168,27 @@ void AudioInterface::labelTextChanged(Label* label) { if (label == noiseSlicerLevelSelection) { - + Value val = label->getTextValue(); int requestedValue = int(val.getValue()); // Note that it might be nice to translate to actual uV levels (16*value) - + if (requestedValue < 0 || requestedValue > 127) { editor->sendActionMessage("Value out of range."); - + label->setText(lastNoiseSlicerString, dontSendNotification); - + return; } - + actualNoiseSlicerLevel = board->setNoiseSlicerLevel(requestedValue); - + std::cout << "Setting Noise Slicer Level to " << requestedValue << std::endl; label->setText(String((roundFloatToInt)(actualNoiseSlicerLevel)), dontSendNotification); } } - else - { + else { Value val = label->getTextValue(); int requestedValue = int(val.getValue()); // Note that it might be nice to translate to actual uV levels (16*value) if (requestedValue < 0 || requestedValue > 127) @@ -659,15 +1214,15 @@ int AudioInterface::getNoiseSlicerLevel() void AudioInterface::paint(Graphics& g) { - + g.setColour(Colours::darkgrey); - + g.setFont(Font("Small Text",9,Font::plain)); - + g.drawText(name, 0, 0, 200, 15, Justification::left, false); - + g.drawText("Level: ", 0, 10, 200, 20, Justification::left, false); - + } diff --git a/Source/Processors/Editors/RHD2000Editor.h b/Source/Processors/Editors/RHD2000Editor.h index c7413a13b3f67ba7e297444a830fa9a62951d6ef..e7ed9dd6da499b8937e7265aa2b5c936eacf857b 100644 --- a/Source/Processors/Editors/RHD2000Editor.h +++ b/Source/Processors/Editors/RHD2000Editor.h @@ -26,6 +26,7 @@ #include "../../../JuceLibraryCode/JuceHeader.h" #include "GenericEditor.h" +#include "VisualizerEditor.h" #include "ElectrodeButtons.h" // for ElectrodeButton @@ -44,9 +45,116 @@ class UtilityButton; @see SourceNode */ +class SourceNode; +class FPGAchannelComponent; +class RHD2000Editor; +class FPGAcanvas; -class RHD2000Editor : public GenericEditor +class FPGAchannelList : public Component, + public AccessClass, Button::Listener, ComboBox::Listener +{ +public: + + FPGAchannelList(GenericProcessor* proc, Viewport *p, FPGAcanvas*c); + ~FPGAchannelList(); + void setNewName(int stream, int channelIndex, channelType t, String newName); + void setNewGain(int stream, int channel,channelType t, float gain); + void disableAll(); + void enableAll(); + void paint(Graphics& g); + void buttonClicked(Button *btn); + void update(); + void updateButtons(); + int getNumChannels(); + void comboBoxChanged(ComboBox *b); + void updateImpedance(Array<int> streams, Array<int> channels, Array<float> magnitude, Array<float> phase); + GenericProcessor* proc; + +private: + Array<float> gains; + Array<channelType> types; + Array<int> stream; + Array<int> orig_number; + + Viewport *viewport; + FPGAcanvas *canvas; + ScopedPointer<UtilityButton> impedanceButton; + ScopedPointer<ComboBox> numberingScheme; + ScopedPointer<Label> numberingSchemeLabel; + OwnedArray<Label> staticLabels; + OwnedArray<FPGAchannelComponent> channelComponents; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FPGAchannelList); +}; + + +class FPGAchannelComponent : public Component, public AccessClass, Button::Listener, public ComboBox::Listener, public Label::Listener +{ +public: + FPGAchannelComponent(FPGAchannelList* cl,int stream, int ch, channelType t, int gainIndex_, String name_, Array<float> gains_); + ~FPGAchannelComponent(); + Colour getDefaultColor(int ID); + void setImpedanceValues(float mag, float phase); + void disableEdit(); + void enableEdit(); + + + void setEnabledState(bool); + bool getEnabledState() + { + return isEnabled; + } + void buttonClicked(Button *btn); + void setUserDefinedData(int d); + int getUserDefinedData(); + void comboBoxChanged(ComboBox* comboBox); + void labelTextChanged(Label* lbl); + + void resized(); +private: + Array<float> gains; + FPGAchannelList* channelList; + ScopedPointer<Label> staticLabel, editName, impedance; + ScopedPointer<ComboBox> gainComboBox; + int channel; + String name; + int stream; + channelType type; + int gainIndex; + int userDefinedData; + Font font; + bool isEnabled; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FPGAchannelComponent); +}; + + +class FPGAcanvas : public Visualizer, public Button::Listener +{ +public: + FPGAcanvas(GenericProcessor* n); + ~FPGAcanvas(); + + void paint(Graphics& g); + + void refresh(); + + void beginAnimation(); + void endAnimation(); + + void refreshState(); + void update(); + void setParameter(int, float) ; + void setParameter(int, int, int, float) ; + void updateImpedance(Array<int> streams, Array<int> channels, Array<float> magnitude, Array<float> phase); + + void resized(); + void buttonClicked(Button* button); + Viewport* channelsViewport; + GenericProcessor* processor; + ScopedPointer<FPGAchannelList> channelList; +}; + +class RHD2000Editor : public VisualizerEditor, public ComboBox::Listener { public: @@ -56,7 +164,8 @@ public: void buttonEvent(Button* button); void scanPorts(); - + void comboBoxChanged(ComboBox* comboBox); + void startAcquisition(); void stopAcquisition(); @@ -64,7 +173,8 @@ public: void saveCustomParameters(XmlElement* xml); void loadCustomParameters(XmlElement* xml); - + Visualizer* createNewCanvas(void); + void measureImpedance(); private: OwnedArray<HeadstageOptionsInterface> headstageOptionsInterfaces; @@ -75,13 +185,15 @@ private: ScopedPointer<AudioInterface> audioInterface; - ScopedPointer<UtilityButton> rescanButton; + ScopedPointer<UtilityButton> rescanButton,dacTTLButton; ScopedPointer<UtilityButton> adcButton; + ScopedPointer<ComboBox> ttlSettleCombo,dacHPFcombo; - ScopedPointer<Label> audioLabel; - RHD2000Thread* board; + ScopedPointer<Label> audioLabel,ttlSettleLabel,dacHPFlabel ; + RHD2000Thread* board; + FPGAcanvas *canvas; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RHD2000Editor); }; @@ -177,36 +289,36 @@ private: }; class AudioInterface : public Component, - public Label::Listener +public Label::Listener { public: AudioInterface(RHD2000Thread*, RHD2000Editor*); ~AudioInterface(); - + void paint(Graphics& g); void labelTextChanged(Label* te); - + void setNoiseSlicerLevel(int value); int getNoiseSlicerLevel(); //void setGain(double value); //double getGain(); - + private: - + String name; - + String lastNoiseSlicerString; String lastGainString; - + RHD2000Thread* board; RHD2000Editor* editor; - + ScopedPointer<Label> noiseSlicerLevelSelection; //ScopedPointer<Label> gainSelection; - + int actualNoiseSlicerLevel; //double actualGain; - + }; diff --git a/Source/Processors/SourceNode.cpp b/Source/Processors/SourceNode.cpp index 6716b423e7a1deb08b68d6e648359db794a899d3..78a6e81fcc28b9a4fdd4268074e83407a4f3101b 100755 --- a/Source/Processors/SourceNode.cpp +++ b/Source/Processors/SourceNode.cpp @@ -45,7 +45,7 @@ SourceNode::SourceNode(const String& name_) if (getName().equalsIgnoreCase("RHA2000-EVAL")) { - // dataThread = new IntanThread(this); + // dataThread = new IntanThread(this);o } else if (getName().equalsIgnoreCase("Custom FPGA")) { @@ -116,6 +116,85 @@ DataThread* SourceNode::getThread() return dataThread; } +int SourceNode::modifyChannelName(channelType t, int str, int ch, String newName, bool updateSignalChain) +{ + if (dataThread != 0) { + int channel_index = dataThread->modifyChannelName(t, str, ch, newName); + if (channel_index >= 0 && channel_index < channels.size()) + { + if (channels[channel_index]->getChannelName() != newName) + { + channels[channel_index]->setName(newName); + // propagate this information... + + if (updateSignalChain) + getEditorViewport()->makeEditorVisible(getEditor(), false, true); + + } + } + return channel_index; + } + return -1; +} + +int SourceNode::modifyChannelGain(int stream, int channel,channelType type, float gain, bool updateSignalChain) +{ + if (dataThread != 0) + { + + int channel_index = dataThread->modifyChannelGain(type, stream, channel, gain); + + if (channel_index >= 0 && channel_index < channels.size()) + { + // we now need to update the signal chain to propagate this change..... + if (channels[channel_index]->getChannelGain() != gain) + { + channels[channel_index]->setGain(gain); + + if (updateSignalChain) + getEditorViewport()->makeEditorVisible(getEditor(), false, true); + + return channel_index; + } + } + } + + return -1; +} + +void SourceNode::getChannelsInfo(StringArray &names, Array<channelType> &types, Array<int> &stream, Array<int> &originalChannelNumber, Array<float> &gains) +{ + if (dataThread != 0) + dataThread->getChannelsInfo(names, types,stream,originalChannelNumber,gains); +} + +void SourceNode::setDefaultNamingScheme(int scheme) +{ + if (dataThread != 0) + { + dataThread->setDefaultNamingScheme(scheme); + + StringArray names; + Array<channelType> types; + Array<int> stream; + Array<int> originalChannelNumber; + Array<float> gains; + getChannelsInfo(names, types, stream, originalChannelNumber, gains); + for (int k=0;k<names.size();k++) + { + modifyChannelName(types[k],stream[k],originalChannelNumber[k], names[k],false); + } + } + +} + +void SourceNode::getEventChannelNames(StringArray &names) +{ + if (dataThread != 0) + dataThread->getEventChannelNames(names); + +} + void SourceNode::updateSettings() { if (inputBuffer == 0 && dataThread != 0) diff --git a/Source/Processors/SourceNode.h b/Source/Processors/SourceNode.h index 286bfd4ca10806c7bfd4e8f6ad08707fd8901d74..56c19a7e15ba9bc428b34da293403331ff291399 100755 --- a/Source/Processors/SourceNode.h +++ b/Source/Processors/SourceNode.h @@ -60,6 +60,13 @@ public: int getDefaultNumOutputs(); float getDefaultBitVolts(); + int modifyChannelGain(int stream, int channel,channelType type, float gain, bool updateSignalChain); + int modifyChannelName(channelType t, int str, int ch, String newName, bool updateSignalChain); + + void getChannelsInfo(StringArray &Names, Array<channelType> &type, Array<int> &stream, Array<int> &originalChannelNumber, Array<float> &gains); + void setDefaultNamingScheme(int scheme); + void getEventChannelNames(StringArray &names); + AudioProcessorEditor* createEditor(); bool hasEditor() const {