diff --git a/Source/EphysSocket.cpp b/Source/EphysSocket.cpp index d14656d..c503268 100644 --- a/Source/EphysSocket.cpp +++ b/Source/EphysSocket.cpp @@ -26,6 +26,9 @@ EphysSocket::EphysSocket(SourceNode* sn) : DataThread(sn) element_size = DEFAULT_ELEMENT_SIZE; num_bytes = DEFAULT_NUM_BYTES; + data_scale = DEFAULT_DATA_SCALE; + data_offset = DEFAULT_DATA_OFFSET; + full_flag = false; stop_flag = false; error_flag = false; @@ -152,7 +155,7 @@ void EphysSocket::updateSettings(OwnedArray* continuousChanne "Channel acquired via network stream", "ephyssocket.continuous", - 1, // NB: Data scale + data_scale, sourceStreams->getFirst() }; @@ -245,7 +248,7 @@ void EphysSocket::convertData() int k = 0; for (int i = 0; i < num_samp; i++) { for (int j = 0; j < num_channels; j++) { - convbuf[k++] = (float)buf[j * num_samp + i]; + convbuf[k++] = data_scale * (float)(buf[j * num_samp + i]) - data_offset; } } } @@ -368,3 +371,101 @@ bool EphysSocket::updateBuffer() else return true; } + +String EphysSocket::handleConfigMessage(String msg) +{ + // Available commands: + // ES INFO - Returns info on current variables that can be modified over HTTP + // ES SCALE - Updates the data scale to data_scale + // ES OFFSET - Updates the offset to data_offset + // ES PORT - Updates the port number that EphysSocket connects to + // ES FREQUENCY - Updates the sampling rate + + if (CoreServices::getAcquisitionStatus()) { + return "Ephys Socket plugin cannot update settings while acquisition is active."; + } + + StringArray parts = StringArray::fromTokens(msg, " ", ""); + + if (parts.size() > 0) + { + if (parts[0].equalsIgnoreCase("ES")) + { + if (parts.size() == 3) + { + if (parts[1].equalsIgnoreCase("SCALE")) + { + float scale = parts[2].getFloatValue(); + + if (scale > MIN_DATA_SCALE && scale < MAX_DATA_SCALE) + { + data_scale = scale; + LOGC("Scale updated to: ", scale); + return "SUCCESS"; + } + + return "Invalid scale requested. Scale can be set between '" + String(MIN_DATA_SCALE) + "' and '" + String(MAX_DATA_SCALE) + "'"; + } + else if (parts[1].equalsIgnoreCase("OFFSET")) + { + float offset = parts[2].getFloatValue(); + + if (offset >= MIN_DATA_OFFSET && offset < MAX_DATA_OFFSET) + { + data_offset = offset; + LOGC("Offset updated to: ", offset); + return "SUCCESS"; + } + + return "Invalid offset requested. Offset can be set between '" + String(MIN_DATA_OFFSET) + "' and '" + String(MAX_DATA_OFFSET) + "'"; + } + else if (parts[1].equalsIgnoreCase("PORT")) + { + float _port = parts[2].getFloatValue(); + + if (_port > MIN_PORT && _port < MAX_PORT) + { + port = _port; + LOGC("Port updated to: ", _port); + return "SUCCESS"; + } + + return "Invalid port requested. Port can be set between '" + String(MIN_PORT) + "' and '" + String(MAX_PORT) + "'"; + } + else if (parts[1].equalsIgnoreCase("FREQUENCY")) + { + float frequency = parts[2].getFloatValue(); + + if (frequency > MIN_SAMPLE_RATE && frequency < MAX_SAMPLE_RATE) + { + sample_rate = frequency; + LOGC("Frequency updated to: ", sample_rate); + return "SUCCESS"; + } + + return "Invalid frequency requested. Frequency can be set between '" + String(MIN_SAMPLE_RATE) + "' and '" + String(MAX_SAMPLE_RATE) + "'"; + } + else + { + return "ES command " + parts[1] + "not recognized."; + } + } + else if (parts.size() == 2) + { + if (parts[1].equalsIgnoreCase("INFO")) + { + return "Port = " + String(port) + ". Sample rate = " + String(sample_rate) + + "Scale = " + String(data_scale) + ". Offset = " + String(data_offset) + "."; + } + else + { + return "ES command " + parts[1] + "not recognized."; + } + } + + return "Unknown number of inputs given."; + } + + return "Command not recognized."; + } +} diff --git a/Source/EphysSocket.h b/Source/EphysSocket.h index 198f362..11d6381 100644 --- a/Source/EphysSocket.h +++ b/Source/EphysSocket.h @@ -16,6 +16,18 @@ namespace EphysSocketNode /** Default parameters */ const int DEFAULT_PORT = 9001; const float DEFAULT_SAMPLE_RATE = 30000.0f; + const float DEFAULT_DATA_SCALE = 0.195f; + const float DEFAULT_DATA_OFFSET = 32768.0f; + + /** Parameter limits */ + const float MIN_DATA_SCALE = 0.0f; + const float MAX_DATA_SCALE = 9999.9f; + const float MIN_DATA_OFFSET = 0; + const float MAX_DATA_OFFSET = 65536; + const float MIN_PORT = 1023; + const float MAX_PORT = 65535; + const float MIN_SAMPLE_RATE = 0; + const float MAX_SAMPLE_RATE = 50000.0f; /** Constructor */ EphysSocket(SourceNode* sn); @@ -58,6 +70,8 @@ namespace EphysSocketNode int num_samp; int num_channels; Depth depth; + float data_scale; + float data_offset; private: @@ -85,6 +99,9 @@ namespace EphysSocketNode /** Stops thread */ bool stopAcquisition() override; + /** Handles incoming HTTP messages */ + String handleConfigMessage(String msg) override; + /** Compares a newly parsed header to existing variables */ bool compareHeaders(Header header) const; diff --git a/Source/EphysSocketEditor.cpp b/Source/EphysSocketEditor.cpp index 14926b6..cb95e98 100644 --- a/Source/EphysSocketEditor.cpp +++ b/Source/EphysSocketEditor.cpp @@ -10,19 +10,19 @@ EphysSocketEditor::EphysSocketEditor(GenericProcessor* parentNode, EphysSocket * { node = socket; - desiredWidth = 120; + desiredWidth = 180; // Add connect button connectButton = new UtilityButton("CONNECT", Font("Small Text", 12, Font::bold)); connectButton->setRadius(3.0f); - connectButton->setBounds(25, 35, 70, 20); + connectButton->setBounds(50, 35, 80, 20); connectButton->addListener(this); addAndMakeVisible(connectButton); // Port portLabel = new Label("Port", "Port"); portLabel->setFont(Font("Small Text", 10, Font::plain)); - portLabel->setBounds(20, 63, 70, 8); + portLabel->setBounds(10, 63, 70, 8); portLabel->setColour(Label::textColourId, Colours::darkgrey); addAndMakeVisible(portLabel); @@ -31,23 +31,53 @@ EphysSocketEditor::EphysSocketEditor(GenericProcessor* parentNode, EphysSocket * portInput->setColour(Label::backgroundColourId, Colours::lightgrey); portInput->setEditable(true); portInput->addListener(this); - portInput->setBounds(25, 73, 70, 15); + portInput->setBounds(15, 73, 70, 15); addAndMakeVisible(portInput); // Frequency sampleRateLabel = new Label("FREQ (HZ)", "Freq (Hz)"); sampleRateLabel->setFont(Font("Small Text", 10, Font::plain)); - sampleRateLabel->setBounds(20, 98, 85, 8); + sampleRateLabel->setBounds(10, 98, 85, 8); sampleRateLabel->setColour(Label::textColourId, Colours::darkgrey); addAndMakeVisible(sampleRateLabel); sampleRateInput = new Label("Fs (Hz)", String((int) node->sample_rate)); sampleRateInput->setFont(Font("Small Text", 10, Font::plain)); - sampleRateInput->setBounds(25, 108, 70, 15); + sampleRateInput->setBounds(15, 108, 70, 15); sampleRateInput->setEditable(true); sampleRateInput->setColour(Label::backgroundColourId, Colours::lightgrey); sampleRateInput->addListener(this); addAndMakeVisible(sampleRateInput); + + // Scale + scaleLabel = new Label("Scale", "Scale"); + scaleLabel->setFont(Font("Small Text", 10, Font::plain)); + scaleLabel->setBounds(95, 63, 85, 8); + scaleLabel->setColour(Label::textColourId, Colours::darkgrey); + addAndMakeVisible(scaleLabel); + + scaleInput = new Label("Scale", String(node->data_scale)); + scaleInput->setFont(Font("Small Text", 10, Font::plain)); + scaleInput->setBounds(100, 73, 70, 15); + scaleInput->setEditable(true); + scaleInput->setColour(Label::backgroundColourId, Colours::lightgrey); + scaleInput->addListener(this); + addAndMakeVisible(scaleInput); + + // Offset + offsetLabel = new Label("Offset", "Offset"); + offsetLabel->setFont(Font("Small Text", 10, Font::plain)); + offsetLabel->setBounds(95, 98, 85, 8); + offsetLabel->setColour(Label::textColourId, Colours::darkgrey); + addAndMakeVisible(offsetLabel); + + offsetInput = new Label("Offset", String(node->data_offset)); + offsetInput->setFont(Font("Small Text", 10, Font::plain)); + offsetInput->setBounds(100, 108, 70, 15); + offsetInput->setEditable(true); + offsetInput->setColour(Label::backgroundColourId, Colours::lightgrey); + offsetInput->addListener(this); + addAndMakeVisible(offsetInput); } void EphysSocketEditor::labelTextChanged(Label* label) @@ -56,7 +86,7 @@ void EphysSocketEditor::labelTextChanged(Label* label) { float sampleRate = sampleRateInput->getText().getFloatValue(); - if (sampleRate > 0 && sampleRate < 50000.0f) + if (sampleRate > node->MIN_SAMPLE_RATE && sampleRate < node->MAX_SAMPLE_RATE) { node->sample_rate = sampleRate; CoreServices::updateSignalChain(this); @@ -69,7 +99,7 @@ void EphysSocketEditor::labelTextChanged(Label* label) { int port = portInput->getText().getIntValue(); - if (port > 1023 && port < 65535) + if (port > node->MIN_PORT && port < node->MAX_PORT) { node->port = port; } @@ -77,6 +107,31 @@ void EphysSocketEditor::labelTextChanged(Label* label) portInput->setText(String(node->port), dontSendNotification); } } + else if (label == scaleInput) + { + float scale = scaleInput->getText().getFloatValue(); + + if (scale > node->MIN_DATA_SCALE && scale < node->MAX_DATA_SCALE) + { + node->data_scale = scale; + CoreServices::updateSignalChain(this); + } + else { + scaleInput->setText(String(node->data_scale), dontSendNotification); + } + } + else if (label == offsetInput) + { + int offset = offsetInput->getText().getIntValue(); + + if (offset >= node->MIN_DATA_OFFSET && offset < node->MAX_DATA_OFFSET) + { + node->data_offset = offset; + } + else { + offsetInput->setText(String(node->data_offset), dontSendNotification); + } + } } void EphysSocketEditor::startAcquisition() @@ -85,6 +140,8 @@ void EphysSocketEditor::startAcquisition() portInput->setEnabled(false); sampleRateInput->setEnabled(false); connectButton->setEnabled(false); + scaleInput->setEnabled(false); + offsetInput->setEnabled(false); } void EphysSocketEditor::stopAcquisition() @@ -93,6 +150,8 @@ void EphysSocketEditor::stopAcquisition() portInput->setEnabled(true); sampleRateInput->setEnabled(true); connectButton->setEnabled(true); + scaleInput->setEnabled(true); + offsetInput->setEnabled(true); } void EphysSocketEditor::buttonClicked(Button* button) @@ -112,6 +171,8 @@ void EphysSocketEditor::saveCustomParametersToXml(XmlElement* xmlNode) parameters->setAttribute("port", portInput->getText()); parameters->setAttribute("fs", sampleRateInput->getText()); + parameters->setAttribute("scale", scaleInput->getText()); + parameters->setAttribute("offset", offsetInput->getText()); } void EphysSocketEditor::loadCustomParametersFromXml(XmlElement* xmlNode) @@ -125,6 +186,12 @@ void EphysSocketEditor::loadCustomParametersFromXml(XmlElement* xmlNode) sampleRateInput->setText(subNode->getStringAttribute("fs", ""), dontSendNotification); node->sample_rate = subNode->getDoubleAttribute("fs", node->DEFAULT_SAMPLE_RATE); + + scaleInput->setText(subNode->getStringAttribute("scale", ""), dontSendNotification); + node->data_scale = subNode->getDoubleAttribute("scale", node->DEFAULT_DATA_SCALE); + + offsetInput->setText(subNode->getStringAttribute("offset", ""), dontSendNotification); + node->data_offset = subNode->getIntAttribute("offset", node->DEFAULT_DATA_OFFSET); } } } diff --git a/Source/EphysSocketEditor.h b/Source/EphysSocketEditor.h index 2647826..1b03f68 100644 --- a/Source/EphysSocketEditor.h +++ b/Source/EphysSocketEditor.h @@ -54,6 +54,14 @@ namespace EphysSocketNode ScopedPointer