Skip to content
Snippets Groups Projects
RHD2000Editor.cpp 46.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
        ------------------------------------------------------------------
    
        This file is part of the Open Ephys GUI
        Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
    
    */
    
    #include "RHD2000Editor.h"
    #include <cmath>
    
    
    #include "../../Editors/ChannelSelector.h"
    #include "../../SourceNode/SourceNode.h"
    
    #include "RHD2000Thread.h"
    
    #ifdef WIN32
    #if (_MSC_VER < 1800) //round doesn't exist on MSVC prior to 2013 version
    inline double round(double x)
    {
        return floor(x+0.5);
    }
    #endif
    #endif
    
    FPGAchannelList::FPGAchannelList(GenericProcessor* proc_, Viewport* p, FPGAcanvas* c) : chainUpdate(false), viewport(p), canvas(c)
    {
        proc = (SourceNode*)proc_;
        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);
    
        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);
        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)
    {
        RHD2000Editor* p = (RHD2000Editor*)proc->getEditor();
        if (btn == impedanceButton)
        {
            p->measureImpedance();
        }
        else if (btn == saveImpedanceButton)
        {
            p->setSaveImpedance(btn->getToggleState());
        }
        else if (btn == autoMeasureButton)
        {
            p->setAutoMeasureImpedance(btn->getToggleState());
        }
    }
    
    void FPGAchannelList::update()
    {
       // const int columnWidth = 330;
    	const int columnWidth = 250;
        // Query processor for number of channels, types, gains, etc... and update the UI
        channelComponents.clear();
        staticLabels.clear();
    
        RHD2000Thread* thread = (RHD2000Thread*)proc->getThread();
    
        DataChannel::DataChannelTypes type;
    
    
        // find out which streams are active.
        bool hsActive[MAX_NUM_HEADSTAGES+1];
        //bool adcActive = false;
        int numActiveHeadstages = 0;
        int hsColumn[MAX_NUM_HEADSTAGES + 1];
        int numChannelsPerHeadstage[MAX_NUM_HEADSTAGES + 1];
        chainUpdate = false;
    
        for (int k = 0; k<MAX_NUM_HEADSTAGES; k++)
        {
            if (thread->isHeadstageEnabled(k))
            {
                numChannelsPerHeadstage[k] = thread->getActiveChannelsInHeadstage(k);
                hsActive[k] = true;
                hsColumn[k] = numActiveHeadstages*columnWidth;
                numActiveHeadstages++;
            }
            else
            {
                numChannelsPerHeadstage[k] = 0;
                hsActive[k] = false;
                hsColumn[k] = 0;
            }
        }
    
    
        if (thread->getNumDataOutputs(DataChannel::ADC_CHANNEL,0) > 0)
    
    		numChannelsPerHeadstage[MAX_NUM_HEADSTAGES] = thread->getNumDataOutputs(DataChannel::ADC_CHANNEL, 0);
    
            hsActive[MAX_NUM_HEADSTAGES] = true;
            hsColumn[MAX_NUM_HEADSTAGES] = numActiveHeadstages*columnWidth;
            numActiveHeadstages++;
        }
        else
        {
            numChannelsPerHeadstage[MAX_NUM_HEADSTAGES] = 0;
            hsActive[MAX_NUM_HEADSTAGES] = false;
            hsColumn[MAX_NUM_HEADSTAGES] = 0;
        }
    
        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_HEADSTAGES + 1; k++)
        {
            if (hsActive[k])
            {
                Label* lbl = new Label(streamNames[k],streamNames[k]);
                lbl->setEditable(false);
                lbl->setBounds(10+hsColumn[k],40,columnWidth, 25);
                lbl->setJustificationType(juce::Justification::centred);
                lbl->setColour(Label::textColourId,juce::Colours::white);
                staticLabels.add(lbl);
                addAndMakeVisible(lbl);
    
            }
    
        }
    
        for (int k = 0; k < MAX_NUM_HEADSTAGES + 1; k++)
        {
            if (hsActive[k])
            {
                for (int ch = 0; ch < numChannelsPerHeadstage[k]+ (k < MAX_NUM_HEADSTAGES ? 3 : 0); ch++)
                {
                    int channelGainIndex = 1;
                    int realChan = thread->getChannelFromHeadstage(k, ch);
    
                    float ch_gain = proc->getDataChannel(realChan)->getBitVolts() / proc->getBitVolts(proc->getDataChannel(realChan));
    
                    for (int j = 0; j < gains.size(); j++)
                    {
                        if (fabs(gains[j] - ch_gain) < 1e-3)
                        {
                            channelGainIndex = j;
                            break;
                        }
                    }
                    if (k < MAX_NUM_HEADSTAGES)
    
                        type = ch < numChannelsPerHeadstage[k] ? DataChannel::HEADSTAGE_CHANNEL : DataChannel::AUX_CHANNEL;
    
                        type = DataChannel::ADC_CHANNEL;
    
    
                    FPGAchannelComponent* comp = new FPGAchannelComponent(this, realChan, channelGainIndex + 1, thread->getChannelName(realChan), gains,type);
                    comp->setBounds(10 + hsColumn[k], 70 + ch * 22, columnWidth, 22);
                    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,k, -1, ttlNames[k],gains,DataChannel::INVALID); //let's treat invalid as an event channel
    
            comp->setBounds(10+numActiveHeadstages*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(numActiveHeadstages*columnWidth,40,columnWidth, 25);
        lbl->setJustificationType(juce::Justification::centred);
        lbl->setColour(Label::textColourId,juce::Colours::white);
        staticLabels.add(lbl);
        addAndMakeVisible(lbl);
    
        chainUpdate = true;
    }
    
    void FPGAchannelList::disableAll()
    {
        for (int k=0; k<channelComponents.size(); k++)
        {
            channelComponents[k]->disableEdit();
        }
    	impedanceButton->setEnabled(false);
    	saveImpedanceButton->setEnabled(false);
    	autoMeasureButton->setEnabled(false);
    	numberingScheme->setEnabled(false);
    }
    
    void FPGAchannelList::enableAll()
    {
        for (int k=0; k<channelComponents.size(); k++)
        {
            channelComponents[k]->enableEdit();
        }
    	impedanceButton->setEnabled(true);
    	saveImpedanceButton->setEnabled(true);
    	autoMeasureButton->setEnabled(true);
    	numberingScheme->setEnabled(true);
    }
    
    void FPGAchannelList::setNewGain(int channel, float gain)
    {
        RHD2000Thread* thread = (RHD2000Thread*)proc->getThread();
        thread->modifyChannelGain(channel, gain);
        if (chainUpdate)
            proc->requestChainUpdate();
    }
    
    void FPGAchannelList::setNewName(int channel, String newName)
    {
        RHD2000Thread* thread = (RHD2000Thread*)proc->getThread();
        thread->modifyChannelName(channel, newName);
        if (chainUpdate)
            proc->requestChainUpdate();
    }
    
    void FPGAchannelList::updateButtons()
    {
    }
    
    int FPGAchannelList::getNumChannels()
    {
        return 0;
    }
    
    void FPGAchannelList::comboBoxChanged(ComboBox* b)
    {
        if (b == numberingScheme)
        {
            SourceNode* p = (SourceNode*)proc;
            RHD2000Thread* thread = (RHD2000Thread*)p->getThread();
            int scheme = numberingScheme->getSelectedId();
            thread->setDefaultNamingScheme(scheme);
            update();
            p->requestChainUpdate();
        }
    }
    
    void FPGAchannelList::updateImpedance(Array<int> streams, Array<int> channels, Array<float> magnitude, Array<float> phase)
    {
    	int i = 0;
        for (int k = 0; k < streams.size(); k++)
        {
    		if (i >= channelComponents.size())
    			break; //little safety
    
    
    		if (channelComponents[i]->type != DataChannel::HEADSTAGE_CHANNEL)
    
    		{
    			k--;
    		}
    		else
    		{
    			channelComponents[i]->setImpedanceValues(magnitude[k], phase[k]);
    		}
    		i++;
        }
    
    }
    
    
    /****************************************************/
    
    FPGAchannelComponent::FPGAchannelComponent(FPGAchannelList* cl, int ch, int gainIndex_, String N, Array<float> gains_, DataChannel::DataChannelTypes type_) :
    
    type(type_), gains(gains_), channelList(cl), channel(ch), name(N), gainIndex(gainIndex_)
    {
        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, sendNotificationSync);
            gainComboBox->addListener(this);
            addAndMakeVisible(gainComboBox);
        }
        else
        {*/
            gainComboBox = nullptr;
        //}
    
    
        if (type == DataChannel::HEADSTAGE_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->getBitVolts(channelList->proc->getDataChannel(channel));
    
            channelList->setNewGain(channel, mult*bitvolts);
        }
    }
    void FPGAchannelComponent::labelTextChanged(Label* lbl)
    {
        // channel name change
        String newName = lbl->getText();
        channelList->setNewName(channel, 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);
    		impedance->setBounds(100, 0, 130, 20);
        }
    
    }
    
    
    
    /**********************************************/
    
    FPGAcanvas::FPGAcanvas(GenericProcessor* n)
    {
        processor = (SourceNode*)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();
    	if (static_cast<RHD2000Thread*>(processor->getThread())->isAcquisitionActive())
    	{
    		channelList->disableAll();
    	}
    }
    
    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
                                )
        : VisualizerEditor(parentNode, useDefaultParameterEditors), board(board_)
    {
        canvas = nullptr;
        desiredWidth = 340;
        tabText = "FPGA";
        measureWhenRecording = false;
        saveImpedances = false;
    
    	impedanceData = new ImpedanceData();
    	impedanceData->valid = false;
    
        // add headstage-specific controls (currently just an enable/disable button)
        for (int i = 0; i < 4; i++)
        {
            HeadstageOptionsInterface* hsOptions = new HeadstageOptionsInterface(board, this, i);
            headstageOptionsInterfaces.add(hsOptions);
            addAndMakeVisible(hsOptions);
            hsOptions->setBounds(3, 28+i*20, 70, 18);
        }
    
        // add sample rate selection
        sampleRateInterface = new SampleRateInterface(board, this);
        addAndMakeVisible(sampleRateInterface);
        sampleRateInterface->setBounds(80, 25, 110, 50);
    
        // add Bandwidth selection
        bandwidthInterface = new BandwidthInterface(board, this);
        addAndMakeVisible(bandwidthInterface);
        bandwidthInterface->setBounds(80, 58, 80, 50);
    
        // add DSP selection
      //  dspInterface = new DSPInterface(board, this);
      //  addAndMakeVisible(dspInterface);
      //  dspInterface->setBounds(80, 58, 80, 50);
    
        // add rescan button
        rescanButton = new UtilityButton("RESCAN", Font("Small Text", 13, Font::plain));
        rescanButton->setRadius(3.0f);
        rescanButton->setBounds(6, 108,65,18);
        rescanButton->addListener(this);
        rescanButton->setTooltip("Check for connected headstages");
        addAndMakeVisible(rescanButton);
    
        for (int i = 0; i < 2; i++)
        {
            ElectrodeButton* button = new ElectrodeButton(-1);
            electrodeButtons.add(button);
    
            button->setBounds(200+i*25, 40, 25, 15);
            button->setChannelNum(-1);
            button->setToggleState(false, dontSendNotification);
            button->setRadioGroupId(999);
    
            addAndMakeVisible(button);
            button->addListener(this);
    
            if (i == 0)
            {
                button->setTooltip("Audio monitor left channel");
            }
            else
            {
                button->setTooltip("Audio monitor right channel");
            }
        }
    
        audioLabel = new Label("audio label", "Audio out");
        audioLabel->setBounds(190,25,75,15);
        audioLabel->setFont(Font("Small Text", 10, Font::plain));
        audioLabel->setColour(Label::textColourId, Colours::darkgrey);
        addAndMakeVisible(audioLabel);
    
        // add HW audio parameter selection
        audioInterface = new AudioInterface(board, this);
        addAndMakeVisible(audioInterface);
    
        audioInterface->setBounds(179, 58, 70, 50);
    
        clockInterface = new ClockDivideInterface(board, this);
        addAndMakeVisible(clockInterface);
        clockInterface->setBounds(179, 82, 70, 50);
    
    
        adcButton = new UtilityButton("ADC 1-8", Font("Small Text", 13, Font::plain));
        adcButton->setRadius(3.0f);
    
        adcButton->setBounds(179,108,70,18);
    
        adcButton->addListener(this);
        adcButton->setClickingTogglesState(true);
        adcButton->setTooltip("Enable/disable ADC channels");
        addAndMakeVisible(adcButton);
    
    	ledButton = new UtilityButton("LED", Font("Very Small Text", 13, Font::plain));
    	ledButton->setRadius(3.0f);
    	ledButton->setBounds(140, 108, 30, 18);
    	ledButton->addListener(this);
    	ledButton->setClickingTogglesState(true);
    	ledButton->setTooltip("Enable/disable board LEDs");
    	addAndMakeVisible(ledButton);
    	ledButton->setToggleState(true, dontSendNotification);
    
        // add DSP Offset Button
        dspoffsetButton = new UtilityButton("DSP", Font("Very Small Text", 13, Font::plain));
        dspoffsetButton->setRadius(3.0f); // sets the radius of the button's corners
        dspoffsetButton->setBounds(80, 108,30,18); // sets the x position, y position, width, and height of the button
        dspoffsetButton->addListener(this);
        dspoffsetButton->setClickingTogglesState(true); // makes the button toggle its state when clicked
        dspoffsetButton->setTooltip("Enable/disable DSP offset removal");
        addAndMakeVisible(dspoffsetButton); // makes the button a child component of the editor and makes it visible
        dspoffsetButton->setToggleState(true, dontSendNotification);
    
        // add DSP Frequency Selection field
        dspInterface = new DSPInterface(board, this);
        addAndMakeVisible(dspInterface);
        dspInterface->setBounds(110, 108, 30, 50);
    
        ttlSettleLabel = new Label("TTL Settle","TTL Settle");
        ttlSettleLabel->setFont(Font("Small Text", 11, Font::plain));
    
        ttlSettleLabel->setBounds(255,80,70,20);
    
        ttlSettleLabel->setColour(Label::textColourId, Colours::darkgrey);
        addAndMakeVisible(ttlSettleLabel);
    
    
        ttlSettleCombo = new ComboBox("FastSettleComboBox");
        ttlSettleCombo->setBounds(260,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(260,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(260,42,65,20);
    
        dacHPFlabel->setColour(Label::textColourId, Colours::darkgrey);
        addAndMakeVisible(dacHPFlabel);
    
        dacHPFcombo = new ComboBox("dacHPFCombo");
        dacHPFcombo->setBounds(260,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);
    
    }
    
    RHD2000Editor::~RHD2000Editor()
    {
    
    }
    
    void RHD2000Editor::scanPorts()
    {
        rescanButton->triggerClick();
    }
    
    void RHD2000Editor::measureImpedance()
    {
    	impedanceData->valid = false;
    	board->runImpedanceTest(impedanceData);
    }
    
    void RHD2000Editor::handleAsyncUpdate()
    {
    	if (!impedanceData->valid)
    		return;
        if (canvas == nullptr)
            VisualizerEditor::canvas = createNewCanvas();
        // update components...
    	canvas->updateImpedance(impedanceData->streams, impedanceData->channels, impedanceData->magnitudes, impedanceData->phases);
        if (saveImpedances)
        {
    		CoreServices::RecordNode::createNewrecordingDir();
    
    		String path(CoreServices::RecordNode::getRecordingPath().getFullPathName()
                        + File::separatorString + "impedance_measurement.xml");
    
            std::cout << "Saving impedance measurements in " << path << "\n";
    
            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 < impedanceData->channels.size(); i++)
            {
                XmlElement* chan = new XmlElement("CHANNEL");
                chan->setAttribute("name",board->getChannelName(i));
    			chan->setAttribute("stream", impedanceData->streams[i]);
    			chan->setAttribute("channel_number", impedanceData->channels[i]);
    			chan->setAttribute("magnitude", impedanceData->magnitudes[i]);
    			chan->setAttribute("phase", impedanceData->phases[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)
    {
        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)
    {
        if (button == rescanButton && !acquisitionIsActive)
        {
            board->scanPorts();
    
            for (int i = 0; i < 4; i++)
            {
                headstageOptionsInterfaces[i]->checkEnabledState();
            }
            // board->updateChannelNames();
    		CoreServices::updateSignalChain(this);
        }
        else if (button == electrodeButtons[0])
        {
            channelSelector->setRadioStatus(true);
        }
        else if (button == electrodeButtons[1])
        {
            channelSelector->setRadioStatus(true);
        }
        else if (button == adcButton && !acquisitionIsActive)
        {
            board->enableAdcs(button->getToggleState());
            //        board->updateChannelNames();
    
            std::cout << "ADC Button toggled" << "\n";
    
    		CoreServices::updateSignalChain(this);
    
            std::cout << "Editor visible." << "\n";
    
        }
        else if (button == dacTTLButton)
        {
            board->setTTLoutputMode(dacTTLButton->getToggleState());
        }
        else if (button == dspoffsetButton && !acquisitionIsActive)
        {
    
            std::cout << "DSP offset " << button->getToggleState() << "\n";
    
            board->setDSPOffset(button->getToggleState());
        }
    	else if (button == ledButton)
    	{
    		board->enableBoardLeds(button->getToggleState());
    	}
    
    	else
    	{
    		VisualizerEditor::buttonEvent(button);
    	}
    
    void RHD2000Editor::channelChanged (int channel, bool /*newState*/)
    
        // Audio output is tied to DAC channels 0 and 1
    
        for (int i = 0; i < 2; i++)
        {
            if (electrodeButtons[i]->getToggleState())
            {
    
                electrodeButtons[i]->setChannelNum (channel);
    
                electrodeButtons[i]->repaint();
    
                board->setDACchannel (i, channel - 1); // HW channels are zero-based
    
            }
        }
    }
    
    void RHD2000Editor::startAcquisition()
    {
        if (measureWhenRecording)
            measureImpedance();
    
        channelSelector->startAcquisition();
    
        rescanButton->setEnabledState(false);
        adcButton->setEnabledState(false);
        dspoffsetButton-> setEnabledState(false);
        acquisitionIsActive = true;
    	if (canvas != nullptr)
    		canvas->channelList->disableAll();
            //canvas->channelList->setEnabled(false);
    }
    
    void RHD2000Editor::stopAcquisition()
    {
    
        channelSelector->stopAcquisition();
    
        rescanButton->setEnabledState(true);
        adcButton->setEnabledState(true);
        dspoffsetButton-> setEnabledState(true);
    
        acquisitionIsActive = false;
    	if (canvas != nullptr)
    		canvas->channelList->enableAll();
        //  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());
        xml->setAttribute("DSPOffset", dspoffsetButton->getToggleState());
        xml->setAttribute("DSPCutoffFreq", dspInterface->getDspCutoffFreq());
        xml->setAttribute("save_impedance_measurements",saveImpedances);
        xml->setAttribute("auto_measure_impedances",measureWhenRecording);
    	xml->setAttribute("LEDs", ledButton->getToggleState());
    
    	xml->setAttribute("ClockDivideRatio", clockInterface->getClockDivideRatio());
    
    }
    
    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"));
        dspoffsetButton->setToggleState(xml->getBoolAttribute("DSPOffset"), sendNotification);
        dspInterface->setDspCutoffFreq(xml->getDoubleAttribute("DSPCutoffFreq"));
        saveImpedances = xml->getBoolAttribute("save_impedance_measurements");
        measureWhenRecording = xml->getBoolAttribute("auto_measure_impedances");
    	ledButton->setToggleState(xml->getBoolAttribute("LEDs", true),sendNotification);
    
        clockInterface->setClockDivideRatio(xml->getIntAttribute("ClockDivideRatio")); 
    
    }
    
    
    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_,
                                           RHD2000Editor* editor_) :
        board(board_), editor(editor_)
    {
    
        name = "Bandwidth";
    
        lastHighCutString = "7500";
        lastLowCutString = "1";
    
        actualUpperBandwidth = 7500.0f;
        actualLowerBandwidth = 1.0f;
    
        upperBandwidthSelection = new Label("UpperBandwidth",lastHighCutString); // this is currently set in RHD2000Thread, the cleaner would be to set it here again
        upperBandwidthSelection->setEditable(true,false,false);
        upperBandwidthSelection->addListener(this);
        upperBandwidthSelection->setBounds(30,30,60,20);
        upperBandwidthSelection->setColour(Label::textColourId, Colours::darkgrey);
        addAndMakeVisible(upperBandwidthSelection);
    
    
        lowerBandwidthSelection = new Label("LowerBandwidth",lastLowCutString);
        lowerBandwidthSelection->setEditable(true,false,false);
        lowerBandwidthSelection->addListener(this);
        lowerBandwidthSelection->setBounds(25,10,60,20);