From 1a13a52754a900c41cf8c6e42d0ad49e3a368270 Mon Sep 17 00:00:00 2001 From: retiutut Date: Mon, 18 Jan 2021 18:24:44 -0600 Subject: [PATCH 01/15] Add analog/pulse LSL networking test --- .gitignore | 4 ++ .../LSL/lslStreamTest_AnalogPinPulse.py | 57 +++++++++++++++++++ OpenBCI_GUI/W_Networking.pde | 13 +++-- 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py diff --git a/.gitignore b/.gitignore index 5ca027772..c2b853dcd 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,7 @@ application.* *.autosave .vscode/* temp/* +libBoardController.so +libDataHandler.so +libGanglionLib.so +libGanglionScan.so diff --git a/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py b/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py new file mode 100644 index 000000000..ba3112917 --- /dev/null +++ b/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py @@ -0,0 +1,57 @@ +"""Example program to show how to read a multi-channel time series from LSL.""" +import time +from pylsl import StreamInlet, resolve_stream +from time import sleep +import numpy as np +import matplotlib.pyplot as plt +from matplotlib import style +from collections import deque + +# first resolve an EEG stream on the lab network +print("looking for an EEG stream...") +streams = resolve_stream('type', 'EEG') + +# create a new inlet to read from the stream +inlet = StreamInlet(streams[0]) +duration = 3 + +sleep(0) + +def testLSLSamplingRate(): + start = time.time() + numSamples = 0 + numChunks = 0 + + while time.time() <= start + duration: + # get chunks of samples + samples, timestamp = inlet.pull_sample() + if timestamp: + numChunks += 1 + # print( len(samples) ) + numSamples += len(samples) + # print(samples) + + print( "Number of Chunks == {}".format(numChunks) ) + print( "Avg Sampling Rate == {}".format(numSamples / duration) ) + + +testLSLSamplingRate() + +print("gathering data to plot...") + +def testLSLPulseData(): + start = time.time() + raw_pulse_signal = [] + + while time.time() <= start + duration: + sample, timestamp = inlet.pull_sample() + if sample: + print(sample[1]) + raw_pulse_signal.append(sample[1]) + + print(raw_pulse_signal) + plt.plot(raw_pulse_signal) + plt.ylabel('raw analog signal') + plt.show() + +testLSLPulseData() \ No newline at end of file diff --git a/OpenBCI_GUI/W_Networking.pde b/OpenBCI_GUI/W_Networking.pde index 59f53c5ab..961e3abf0 100644 --- a/OpenBCI_GUI/W_Networking.pde +++ b/OpenBCI_GUI/W_Networking.pde @@ -2015,13 +2015,14 @@ class Stream extends Thread { } // LSL } else if (this.protocol.equals("LSL")) { ///////////////////This needs to be checked - for (int i = 0; i < (w_pulsesensor.PulseWaveY.length); i++) { - dataToSend[0] = w_pulsesensor.BPM; //Array output - dataToSend[1] = w_pulsesensor.PulseWaveY[i]; - dataToSend[2] = w_pulsesensor.IBI; - } + float[] _dataToSend = new float[3]; + //for (int i = 0; i < (w_pulsesensor.PulseWaveY.length); i++) { + _dataToSend[0] = w_pulsesensor.BPM; //Array output + _dataToSend[1] = w_pulsesensor.PulseWaveY[w_pulsesensor.PulseWaveY.length-1]; + _dataToSend[2] = w_pulsesensor.IBI; + //} // Add timestamp to LSL Stream - outlet_data.push_chunk(dataToSend); + outlet_data.push_sample(_dataToSend); // Serial } else if (this.protocol.equals("Serial")) { // Send Pulse Data (BPM,Signal,IBI) over Serial for (int i = 0; i < (w_pulsesensor.PulseWaveY.length); i++) { From 98a582512d830201d31db88d5cd5c420f2244bb7 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Tue, 19 Jan 2021 14:31:11 -0600 Subject: [PATCH 02/15] Fix Pulse Widget IBI and BPM processing! - Thanks Andrey! --- OpenBCI_GUI/W_PulseSensor.pde | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/OpenBCI_GUI/W_PulseSensor.pde b/OpenBCI_GUI/W_PulseSensor.pde index 25b67713a..0126d34ad 100644 --- a/OpenBCI_GUI/W_PulseSensor.pde +++ b/OpenBCI_GUI/W_PulseSensor.pde @@ -100,10 +100,17 @@ class W_PulseSensor extends Widget { for (int i=0; i < PulseBuffSize; i++ ) { int signal = (int)(allData.get(i)[analogChannels[0]]); - processSignal(signal); + //processSignal(signal); PulseWaveY[i] = signal; } + double[][] frameData = currentBoard.getFrameData(); + for (int i = 0; i < frameData[0].length; i++) + { + int signal = (int)(frameData[analogChannels[0]][i]); + processSignal(signal); + } + //ignore top left button interaction when widgetSelector dropdown is active lockElementOnOverlapCheck(analogModeButton); From e7266d17f435596213130a4c09ffc9ff13603690 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Tue, 19 Jan 2021 16:18:01 -0600 Subject: [PATCH 03/15] WIP - Use chunks of data for Pulse LSL output --- .../LSL/lslStreamTest_AnalogPinPulse.py | 13 +++++----- OpenBCI_GUI/W_Networking.pde | 26 +++++++++++++++++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py b/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py index ba3112917..3f66a52ea 100644 --- a/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py +++ b/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py @@ -24,10 +24,10 @@ def testLSLSamplingRate(): while time.time() <= start + duration: # get chunks of samples - samples, timestamp = inlet.pull_sample() + samples, timestamp = inlet.pull_chunk() if timestamp: numChunks += 1 - # print( len(samples) ) + print( len(samples) ) numSamples += len(samples) # print(samples) @@ -44,10 +44,11 @@ def testLSLPulseData(): raw_pulse_signal = [] while time.time() <= start + duration: - sample, timestamp = inlet.pull_sample() - if sample: - print(sample[1]) - raw_pulse_signal.append(sample[1]) + chunk, timestamp = inlet.pull_chunk() + if chunk: + for sample in chunk: + print(sample) + raw_pulse_signal.append(sample[1]) print(raw_pulse_signal) plt.plot(raw_pulse_signal) diff --git a/OpenBCI_GUI/W_Networking.pde b/OpenBCI_GUI/W_Networking.pde index 961e3abf0..7855b50b8 100644 --- a/OpenBCI_GUI/W_Networking.pde +++ b/OpenBCI_GUI/W_Networking.pde @@ -2015,14 +2015,36 @@ class Stream extends Thread { } // LSL } else if (this.protocol.equals("LSL")) { ///////////////////This needs to be checked - float[] _dataToSend = new float[3]; + /* + float[] _dataToSend = new float[[3]; //for (int i = 0; i < (w_pulsesensor.PulseWaveY.length); i++) { _dataToSend[0] = w_pulsesensor.BPM; //Array output _dataToSend[1] = w_pulsesensor.PulseWaveY[w_pulsesensor.PulseWaveY.length-1]; _dataToSend[2] = w_pulsesensor.IBI; //} + float[] _dataToSend = new float[numChan * 125]; + for (int i = 0; i < numChan; i++) { + for (int j = 0; j < 125; j++) { + _dataToSend[j+125*i] = fftBuff[i].getBand(j); + } + } // Add timestamp to LSL Stream - outlet_data.push_sample(_dataToSend); + // From LSLLink Library: The time stamps of other samples are automatically derived based on the sampling rate of the stream. + outlet_data.push_chunk(_dataToSend); + */ + int numDataPoints = 3; + double[][] frameData = currentBoard.getFrameData(); + int[] analogChannels = ((AnalogCapableBoard)currentBoard).getAnalogChannels(); + float[] _dataToSend = new float[frameData[0].length * numDataPoints]; + for (int i = 0; i < frameData[0].length; i++) + { + int raw_signal = (int)(frameData[analogChannels[0]][i]); + _dataToSend[numDataPoints*i] = w_pulsesensor.BPM; + _dataToSend[numDataPoints*i+1] = raw_signal; + _dataToSend[numDataPoints*i+2] = w_pulsesensor.IBI; + } + // Add timestamp to LSL Stream + outlet_data.push_chunk(_dataToSend); // Serial } else if (this.protocol.equals("Serial")) { // Send Pulse Data (BPM,Signal,IBI) over Serial for (int i = 0; i < (w_pulsesensor.PulseWaveY.length); i++) { From 8af7422d7422369dcfe8829150f42058c30f514f Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Tue, 19 Jan 2021 17:55:08 -0600 Subject: [PATCH 04/15] Modify Pulse LSL test to check sampling rate twice --- .../LSL/lslStreamTest_AnalogPinPulse.py | 12 ++++++------ OpenBCI_GUI/W_Networking.pde | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py b/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py index 3f66a52ea..692521314 100644 --- a/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py +++ b/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py @@ -24,12 +24,11 @@ def testLSLSamplingRate(): while time.time() <= start + duration: # get chunks of samples - samples, timestamp = inlet.pull_chunk() - if timestamp: + chunk, timestamp = inlet.pull_chunk() + if chunk: numChunks += 1 - print( len(samples) ) - numSamples += len(samples) - # print(samples) + for sample in chunk: + numSamples += 1 print( "Number of Chunks == {}".format(numChunks) ) print( "Avg Sampling Rate == {}".format(numSamples / duration) ) @@ -47,10 +46,11 @@ def testLSLPulseData(): chunk, timestamp = inlet.pull_chunk() if chunk: for sample in chunk: - print(sample) + # print(sample) raw_pulse_signal.append(sample[1]) print(raw_pulse_signal) + print( "Avg Sampling Rate == {}".format(len(raw_pulse_signal) / duration) ) plt.plot(raw_pulse_signal) plt.ylabel('raw analog signal') plt.show() diff --git a/OpenBCI_GUI/W_Networking.pde b/OpenBCI_GUI/W_Networking.pde index 7855b50b8..1385f8a52 100644 --- a/OpenBCI_GUI/W_Networking.pde +++ b/OpenBCI_GUI/W_Networking.pde @@ -1358,6 +1358,7 @@ class Stream extends Thread { } else { if (checkForData()) { sendData(); + setDataFalse(); } } } From 0d0d2742ed516496b5d24937af70766b7fe1f53e Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Tue, 19 Jan 2021 22:58:57 -0600 Subject: [PATCH 05/15] Do not "checkForData()" with LSL - TESTING WIP --- Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py | 6 +++--- OpenBCI_GUI/W_Networking.pde | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py b/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py index 692521314..91b58d1db 100644 --- a/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py +++ b/Networking-Test-Kit/LSL/lslStreamTest_AnalogPinPulse.py @@ -13,7 +13,7 @@ # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) -duration = 3 +duration = 10 sleep(0) @@ -25,7 +25,7 @@ def testLSLSamplingRate(): while time.time() <= start + duration: # get chunks of samples chunk, timestamp = inlet.pull_chunk() - if chunk: + if timestamp: numChunks += 1 for sample in chunk: numSamples += 1 @@ -44,7 +44,7 @@ def testLSLPulseData(): while time.time() <= start + duration: chunk, timestamp = inlet.pull_chunk() - if chunk: + if timestamp: for sample in chunk: # print(sample) raw_pulse_signal.append(sample[1]) diff --git a/OpenBCI_GUI/W_Networking.pde b/OpenBCI_GUI/W_Networking.pde index 1385f8a52..a273217c4 100644 --- a/OpenBCI_GUI/W_Networking.pde +++ b/OpenBCI_GUI/W_Networking.pde @@ -1357,9 +1357,10 @@ class Stream extends Thread { } } else { if (checkForData()) { - sendData(); - setDataFalse(); + //sendData(); + //setDataFalse(); } + sendData(); } } } From 81de9b67b19b5009609d7297292c1f4f58b2c238 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Thu, 21 Jan 2021 21:48:28 -0600 Subject: [PATCH 06/15] Add udp receive pulse test --- Networking-Test-Kit/UDP/udp_receive.py | 16 ++- Networking-Test-Kit/UDP/udp_receive_pulse.py | 106 +++++++++++++++++++ OpenBCI_GUI/W_Networking.pde | 32 ++++-- 3 files changed, 139 insertions(+), 15 deletions(-) create mode 100755 Networking-Test-Kit/UDP/udp_receive_pulse.py diff --git a/Networking-Test-Kit/UDP/udp_receive.py b/Networking-Test-Kit/UDP/udp_receive.py index f8a1f0df6..51109281c 100755 --- a/Networking-Test-Kit/UDP/udp_receive.py +++ b/Networking-Test-Kit/UDP/udp_receive.py @@ -10,10 +10,11 @@ # Print received message to console def print_message(*args): try: - obj = json.loads(args[0]) - print obj.get('data') + print(args[0]) #added to see raw data + obj = json.loads(args[0].decode()) + print(obj.get('data')) except BaseException as e: - print e + print(e) # print("(%s) RECEIVED MESSAGE: " % time.time() + # ''.join(str(struct.unpack('>%df' % int(length), args[0])))) @@ -78,9 +79,16 @@ def close_file(*args): # Receive messages print("Listening...") - while True: + start = time.time() + numSamples = 0 + duration = 10 + while time.time() <= start + duration: data, addr = sock.recvfrom(20000) # buffer size is 20000 bytes if args.option=="print": print_message(data) + numSamples += 1 elif args.option=="record": record_to_file(data) +print( "Samples == {}".format(numSamples) ) +print( "Duration == {}".format(duration) ) +print( "Avg Sampling Rate == {}".format(numSamples / duration) ) diff --git a/Networking-Test-Kit/UDP/udp_receive_pulse.py b/Networking-Test-Kit/UDP/udp_receive_pulse.py new file mode 100755 index 000000000..eec641d3f --- /dev/null +++ b/Networking-Test-Kit/UDP/udp_receive_pulse.py @@ -0,0 +1,106 @@ +import socket +import sys +import time +import argparse +import signal +import struct +import os +import json +import numpy as np +import matplotlib.pyplot as plt +from matplotlib import style + +raw_pulse_signal = [] + +# Print received message to console +def print_message(*args): + try: + # print(args[0]) #added to see raw data + obj = json.loads(args[0].decode()) + print(obj.get('data')) + except BaseException as e: + print(e) + # print("(%s) RECEIVED MESSAGE: " % time.time() + + # ''.join(str(struct.unpack('>%df' % int(length), args[0])))) + +# Clean exit from print mode +def exit_print(signal, frame): + print("Closing listener") + sys.exit(0) + +# Record received message in text file +def record_to_file(*args): + textfile.write(str(time.time()) + ",") + textfile.write(''.join(str(struct.unpack('>%df' % length,args[0])))) + textfile.write("\n") + +# Save recording, clean exit from record mode +def close_file(*args): + print("\nFILE SAVED") + textfile.close() + sys.exit(0) + +if __name__ == "__main__": + # Collect command line arguments + parser = argparse.ArgumentParser() + parser.add_argument("--ip", + default="127.0.0.1", help="The ip to listen on") + parser.add_argument("--port", + type=int, default=12345, help="The port to listen on") + parser.add_argument("--address",default="/openbci", help="address to listen to") + parser.add_argument("--option",default="print",help="Debugger option") + parser.add_argument("--len",default=8,help="Debugger option") + args = parser.parse_args() + + # Set up necessary parameters from command line + length = args.len + if args.option=="print": + signal.signal(signal.SIGINT, exit_print) + elif args.option=="record": + i = 0 + while os.path.exists("udp_test%s.txt" % i): + i += 1 + filename = "udp_test%i.txt" % i + textfile = open(filename, "w") + textfile.write("time,address,messages\n") + textfile.write("-------------------------\n") + print("Recording to %s" % filename) + signal.signal(signal.SIGINT, close_file) + + # Connect to socket + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server_address = (args.ip, args.port) + sock.bind(server_address) + + # Display socket attributes + print('--------------------') + print("-- UDP LISTENER -- ") + print('--------------------') + print("IP:", args.ip) + print("PORT:", args.port) + print('--------------------') + print("%s option selected" % args.option) + + # Receive messages + print("Listening...") + start = time.time() + numSamples = 0 + duration = 3 + + while time.time() <= start + duration: + data, addr = sock.recvfrom(20000) # buffer size is 20000 bytes + if args.option=="print": + print_message(data) + sample = json.loads(data.decode()).get('data')[1] + raw_pulse_signal.append(sample) + numSamples += 1 + elif args.option=="record": + record_to_file(data) + +print( "Samples == {}".format(numSamples) ) +print( "Duration == {}".format(duration) ) +print( "Avg Sampling Rate == {}".format(numSamples / duration) ) +plt.plot(raw_pulse_signal) +plt.ylabel('raw analog signal') +plt.show() diff --git a/OpenBCI_GUI/W_Networking.pde b/OpenBCI_GUI/W_Networking.pde index a273217c4..8ea45d1a7 100644 --- a/OpenBCI_GUI/W_Networking.pde +++ b/OpenBCI_GUI/W_Networking.pde @@ -1207,6 +1207,7 @@ class Stream extends Thread { // Data buffers set dynamically in updateNumChan() int start; float[] dataToSend; + private double[][] previousFrameData; //OSC Objects OscP5 osc; @@ -1336,8 +1337,9 @@ class Stream extends Thread { println(e.getMessage()); } } else { + sendData(); if (checkForData()) { - sendData(); + setDataFalse(); } else { try { @@ -2003,17 +2005,25 @@ class Stream extends Thread { } // UDP } else if (this.protocol.equals("UDP")) { //////////////////This needs to be checked - String outputter = "{\"type\":\"pulse\",\"data\":"; - for (int i = 0; i < (w_pulsesensor.PulseWaveY.length); i++) { - outputter += str(w_pulsesensor.BPM) + ","; //Comma separated string output (BPM,Raw Signal,IBI) - outputter += str(w_pulsesensor.PulseWaveY[i]) + ","; - outputter += str(w_pulsesensor.IBI); - outputter += "]}\r\n"; - try { - this.udp.send(outputter, this.ip, this.port); - } catch (Exception e) { - println(e.getMessage()); + int numDataPoints = 3; + double[][] frameData = currentBoard.getFrameData(); + int[] analogChannels = ((AnalogCapableBoard)currentBoard).getAnalogChannels(); + if (!frameData.equals(previousFrameData)) { + for (int i = 0; i < frameData[0].length; i++) + { + String outputter = "{\"type\":\"pulse\",\"data\":["; + int raw_signal = (int)(frameData[analogChannels[0]][i]); + outputter += str(w_pulsesensor.BPM) + ","; //Comma separated string output (BPM,Raw Signal,IBI) + outputter += str(raw_signal) + ","; + outputter += str(w_pulsesensor.IBI); + outputter += "]}\r\n"; + try { + this.udp.send(outputter, this.ip, this.port); + } catch (Exception e) { + println(e.getMessage()); + } } + previousFrameData = frameData; } // LSL } else if (this.protocol.equals("LSL")) { ///////////////////This needs to be checked From 2c13f7c4ec7dc188f4118fc3ea77103afbb97d68 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Fri, 22 Jan 2021 00:04:07 -0600 Subject: [PATCH 07/15] Update UDP recieve and use DecimalFormat in Networking Widget Fixes #944 --- CHANGELOG.md | 7 +++++++ Networking-Test-Kit/UDP/udp_receive.py | 7 ++++--- OpenBCI_GUI/Info.plist.tmpl | 2 +- OpenBCI_GUI/OpenBCI_GUI.pde | 4 ++-- OpenBCI_GUI/W_Networking.pde | 19 ++++++++++++++++--- 5 files changed, 30 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92e07ee48..6a16bddce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # v5.0.2 +### Improvements + +### Bug Fixes +* Fix Accel/Aux UDP output #944 + +# v5.0.2 + ### Improvements * Improved Cyton Auto-Connect button w/ Auto-Scan * Update Hardware Settings UI for ADS1299 boards diff --git a/Networking-Test-Kit/UDP/udp_receive.py b/Networking-Test-Kit/UDP/udp_receive.py index f8a1f0df6..7133e6279 100755 --- a/Networking-Test-Kit/UDP/udp_receive.py +++ b/Networking-Test-Kit/UDP/udp_receive.py @@ -10,10 +10,11 @@ # Print received message to console def print_message(*args): try: - obj = json.loads(args[0]) - print obj.get('data') + print(args[0]) #added to see raw data + obj = json.loads(args[0].decode()) + print(obj.get('data')) except BaseException as e: - print e + print(e) # print("(%s) RECEIVED MESSAGE: " % time.time() + # ''.join(str(struct.unpack('>%df' % int(length), args[0])))) diff --git a/OpenBCI_GUI/Info.plist.tmpl b/OpenBCI_GUI/Info.plist.tmpl index 0f869a761..870861d37 100644 --- a/OpenBCI_GUI/Info.plist.tmpl +++ b/OpenBCI_GUI/Info.plist.tmpl @@ -23,7 +23,7 @@ CFBundleShortVersionString 4 CFBundleVersion - 5.0.2 + 5.0.3-alpha.1 CFBundleSignature ???? NSHumanReadableCopyright diff --git a/OpenBCI_GUI/OpenBCI_GUI.pde b/OpenBCI_GUI/OpenBCI_GUI.pde index a48a81a8f..badd1aa94 100644 --- a/OpenBCI_GUI/OpenBCI_GUI.pde +++ b/OpenBCI_GUI/OpenBCI_GUI.pde @@ -64,8 +64,8 @@ import http.requests.*; // Global Variables & Instances //------------------------------------------------------------------------ //Used to check GUI version in TopNav.pde and displayed on the splash screen on startup -String localGUIVersionString = "v5.0.2"; -String localGUIVersionDate = "December 2020"; +String localGUIVersionString = "v5.0.3-alpha.1"; +String localGUIVersionDate = "January 2020"; String guiLatestVersionGithubAPI = "https://api.github.com/repos/OpenBCI/OpenBCI_GUI/releases/latest"; String guiLatestReleaseLocation = "https://github.com/OpenBCI/OpenBCI_GUI/releases/latest"; diff --git a/OpenBCI_GUI/W_Networking.pde b/OpenBCI_GUI/W_Networking.pde index 59f53c5ab..cc34139d9 100644 --- a/OpenBCI_GUI/W_Networking.pde +++ b/OpenBCI_GUI/W_Networking.pde @@ -13,7 +13,8 @@ // Created by: Gabriel Ibagon (github.com/gabrielibagon), January 2017 // /////////////////////////////////////////////////////////////////////////////// - +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; class W_Networking extends Widget { @@ -1201,6 +1202,8 @@ class Stream extends Thread { String streamName; int nChanLSL; int numChan = 0; + DecimalFormat threeDecimalPlaces; + DecimalFormat fourLeadingPlaces; Boolean isStreaming; Boolean newData = false; @@ -1266,6 +1269,15 @@ class Stream extends Thread { this.filter = filter; this.isStreaming = false; updateNumChan(_nchan); + + //Force decimal formatting for all Locales + Locale currentLocale = Locale.getDefault(); + DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(currentLocale); + otherSymbols.setDecimalSeparator('.'); + otherSymbols.setGroupingSeparator(','); + threeDecimalPlaces = new DecimalFormat("0.000", otherSymbols); + fourLeadingPlaces = new DecimalFormat("####", otherSymbols); + if (this.dataType.equals("TimeSeries")) { buffer = ByteBuffer.allocate(4*numChan); } else { @@ -1799,7 +1811,8 @@ class Stream extends Thread { String outputter = "{\"type\":\"accelerometer\",\"data\":["; for (int i = 0; i < NUM_ACCEL_DIMS; i++) { float accelData = w_accelerometer.getLastAccelVal(i); - String accelData_3dec = String.format("%.3f", accelData); + String accelData_3dec = threeDecimalPlaces.format(accelData); + //String accelData_3dec = String.format("%.3f", accelData); //This does not work in all international settings outputter += accelData_3dec; if (i != NUM_ACCEL_DIMS - 1) { outputter += ","; @@ -1871,7 +1884,7 @@ class Stream extends Thread { String outputter = "{\"type\":\"auxiliary\",\"data\":["; for (int i = 0; i < NUM_ANALOG_READS; i++) { int auxData = (int)lastSample[analogChannels[i]]; - String auxData_formatted = String.format("%04d", auxData); + String auxData_formatted = fourLeadingPlaces.format(auxData); outputter += auxData_formatted; if (i != NUM_ANALOG_READS - 1) { outputter += ","; From 9daefb8d6d1907cf29c6473b8f99078765af7421 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Fri, 22 Jan 2021 01:24:15 -0600 Subject: [PATCH 08/15] Fix Bug - Expert mode unplanned keys crash GUI Fixes #941 --- CHANGELOG.md | 1 + OpenBCI_GUI/Info.plist.tmpl | 2 +- OpenBCI_GUI/Interactivity.pde | 208 ++++++++++++++++++---------------- OpenBCI_GUI/OpenBCI_GUI.pde | 2 +- 4 files changed, 114 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a16bddce..7348cc577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Bug Fixes * Fix Accel/Aux UDP output #944 +* Fix Expert Mode unplanned keyboard shortcuts crash GUI #941 # v5.0.2 diff --git a/OpenBCI_GUI/Info.plist.tmpl b/OpenBCI_GUI/Info.plist.tmpl index 870861d37..abc917b9d 100644 --- a/OpenBCI_GUI/Info.plist.tmpl +++ b/OpenBCI_GUI/Info.plist.tmpl @@ -23,7 +23,7 @@ CFBundleShortVersionString 4 CFBundleVersion - 5.0.3-alpha.1 + 5.0.3-alpha.2 CFBundleSignature ???? NSHumanReadableCopyright diff --git a/OpenBCI_GUI/Interactivity.pde b/OpenBCI_GUI/Interactivity.pde index 282612d99..6f4dff755 100644 --- a/OpenBCI_GUI/Interactivity.pde +++ b/OpenBCI_GUI/Interactivity.pde @@ -44,10 +44,10 @@ void parseKey(char val) { case ' ': // space to start/stop the stream topNav.stopButtonWasPressed(); - break; + return; case ',': drawContainers = !drawContainers; - break; + return; case '{': if(colorScheme == COLOR_SCHEME_DEFAULT){ colorScheme = COLOR_SCHEME_ALTERNATIVE_A; @@ -56,153 +56,167 @@ void parseKey(char val) { } //topNav.updateNavButtonsBasedOnColorScheme(); output("New Dark color scheme coming soon!"); - break; + return; - //deactivate channels 1-16 + //deactivate channels 1-4 case '1': currentBoard.setEXGChannelActive(1-1, false); - break; + return; case '2': currentBoard.setEXGChannelActive(2-1, false); - break; + return; case '3': currentBoard.setEXGChannelActive(3-1, false); - break; + return; case '4': currentBoard.setEXGChannelActive(4-1, false); - break; - case '5': - currentBoard.setEXGChannelActive(5-1, false); - break; - case '6': - currentBoard.setEXGChannelActive(6-1, false); - break; - case '7': - currentBoard.setEXGChannelActive(7-1, false); - break; - case '8': - currentBoard.setEXGChannelActive(8-1, false); - break; - case 'q': - currentBoard.setEXGChannelActive(9-1, false); - break; - case 'w': - currentBoard.setEXGChannelActive(10-1, false); - break; - case 'e': - currentBoard.setEXGChannelActive(11-1, false); - break; - case 'r': - currentBoard.setEXGChannelActive(12-1, false); - break; - case 't': - currentBoard.setEXGChannelActive(13-1, false); - break; - case 'y': - currentBoard.setEXGChannelActive(14-1, false); - break; - case 'u': - currentBoard.setEXGChannelActive(15-1, false); - break; - case 'i': - currentBoard.setEXGChannelActive(16-1, false); - break; + return; - //activate channels 1-16 + //activate channels 1-4 case '!': currentBoard.setEXGChannelActive(1-1, true); - break; + return; case '@': currentBoard.setEXGChannelActive(2-1, true); - break; + return; case '#': currentBoard.setEXGChannelActive(3-1, true); - break; + return; case '$': currentBoard.setEXGChannelActive(4-1, true); - break; - case '%': - currentBoard.setEXGChannelActive(5-1, true); - break; - case '^': - currentBoard.setEXGChannelActive(6-1, true); - break; - case '&': - currentBoard.setEXGChannelActive(7-1, true); - break; - case '*': - currentBoard.setEXGChannelActive(8-1, true); - break; - case 'Q': - currentBoard.setEXGChannelActive(9-1, true); - break; - case 'W': - currentBoard.setEXGChannelActive(10-1, true); - break; - case 'E': - currentBoard.setEXGChannelActive(11-1, true); - break; - case 'R': - currentBoard.setEXGChannelActive(12-1, true); - break; - case 'T': - currentBoard.setEXGChannelActive(13-1, true); - break; - case 'Y': - currentBoard.setEXGChannelActive(14-1, true); - break; - case 'U': - currentBoard.setEXGChannelActive(15-1, true); - break; - case 'I': - currentBoard.setEXGChannelActive(16-1, true); - break; + return; //other controls case 's': stopRunning(); - break; + return; case 'b': startRunning(); - break; + return; ///////////////////// Save User settings lowercase n case 'n': println("Save key pressed!"); settings.save(settings.getPath("User", eegDataSource, nchan)); outputSuccess("Settings Saved! The GUI will now load with these settings. Click \"Default\" to revert to factory settings."); - break; + return; ///////////////////// Load User settings uppercase N case 'N': println("Load key pressed!"); settings.loadKeyPressed(); - break; + return; case '?': if(currentBoard instanceof BoardCyton) { ((BoardCyton)currentBoard).printRegisters(); } - break; + return; case 'd': - break; + return; case 'm': String picfname = "OpenBCI-" + directoryManager.getFileNameDateTime() + ".jpg"; //println("OpenBCI_GUI: 'm' was pressed...taking screenshot:" + picfname); saveFrame(directoryManager.getGuiDataPath() + "Screenshots" + System.getProperty("file.separator") + picfname); // take a shot of that! output("Screenshot captured! Saved to /Documents/OpenBCI_GUI/Screenshots/" + picfname); - break; - + return; default: - if (currentBoard instanceof Board) { - println("Interactivity: '" + key + "' Pressed...sending to Board..."); - ((Board)currentBoard).sendCommand(str(key)); - } break; } + + if (nchan > 4) { + switch (val) { + case '5': + currentBoard.setEXGChannelActive(5-1, false); + return; + case '6': + currentBoard.setEXGChannelActive(6-1, false); + return; + case '7': + currentBoard.setEXGChannelActive(7-1, false); + return; + case '8': + currentBoard.setEXGChannelActive(8-1, false); + return; + case '%': + currentBoard.setEXGChannelActive(5-1, true); + return; + case '^': + currentBoard.setEXGChannelActive(6-1, true); + return; + case '&': + currentBoard.setEXGChannelActive(7-1, true); + return; + case '*': + currentBoard.setEXGChannelActive(8-1, true); + return; + default: + break; + } + } + + if (nchan > 8) { + switch (val) { + case 'q': + currentBoard.setEXGChannelActive(9-1, false); + return; + case 'w': + currentBoard.setEXGChannelActive(10-1, false); + return; + case 'e': + currentBoard.setEXGChannelActive(11-1, false); + return; + case 'r': + currentBoard.setEXGChannelActive(12-1, false); + return; + case 't': + currentBoard.setEXGChannelActive(13-1, false); + return; + case 'y': + currentBoard.setEXGChannelActive(14-1, false); + return; + case 'u': + currentBoard.setEXGChannelActive(15-1, false); + return; + case 'i': + currentBoard.setEXGChannelActive(16-1, false); + return; + case 'Q': + currentBoard.setEXGChannelActive(9-1, true); + return; + case 'W': + currentBoard.setEXGChannelActive(10-1, true); + return; + case 'E': + currentBoard.setEXGChannelActive(11-1, true); + return; + case 'R': + currentBoard.setEXGChannelActive(12-1, true); + return; + case 'T': + currentBoard.setEXGChannelActive(13-1, true); + return; + case 'Y': + currentBoard.setEXGChannelActive(14-1, true); + return; + case 'U': + currentBoard.setEXGChannelActive(15-1, true); + return; + case 'I': + currentBoard.setEXGChannelActive(16-1, true); + return; + default: + break; + } + } + + if (currentBoard instanceof Board) { + output("Expert Mode: '" + key + "' pressed. This is not assigned or applicable to current setup."); + //((Board)currentBoard).sendCommand(str(key)); + } } void mouseDragged() { diff --git a/OpenBCI_GUI/OpenBCI_GUI.pde b/OpenBCI_GUI/OpenBCI_GUI.pde index badd1aa94..f4557570f 100644 --- a/OpenBCI_GUI/OpenBCI_GUI.pde +++ b/OpenBCI_GUI/OpenBCI_GUI.pde @@ -64,7 +64,7 @@ import http.requests.*; // Global Variables & Instances //------------------------------------------------------------------------ //Used to check GUI version in TopNav.pde and displayed on the splash screen on startup -String localGUIVersionString = "v5.0.3-alpha.1"; +String localGUIVersionString = "v5.0.3-alpha.2"; String localGUIVersionDate = "January 2020"; String guiLatestVersionGithubAPI = "https://api.github.com/repos/OpenBCI/OpenBCI_GUI/releases/latest"; String guiLatestReleaseLocation = "https://github.com/OpenBCI/OpenBCI_GUI/releases/latest"; From dfee43b8c3f8e4fe9e5c09d3866c5fbc50bee233 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Fri, 22 Jan 2021 01:26:21 -0600 Subject: [PATCH 09/15] update changelog for 5.0.3-alpha.2 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7348cc577..9446a351c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# v5.0.2 +# v5.0.3 ### Improvements From bc0bd357962499d39193a65d65917ba046489d62 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Fri, 22 Jan 2021 16:59:17 -0600 Subject: [PATCH 10/15] WIP BROKEN - Remove checkForData() for LSL networking output --- OpenBCI_GUI/W_Networking.pde | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/OpenBCI_GUI/W_Networking.pde b/OpenBCI_GUI/W_Networking.pde index 86c8a137b..485784a46 100644 --- a/OpenBCI_GUI/W_Networking.pde +++ b/OpenBCI_GUI/W_Networking.pde @@ -1349,9 +1349,8 @@ class Stream extends Thread { println(e.getMessage()); } } else { - sendData(); - if (checkForData()) { - + if (checkForData()) { //This needs to be removed in next version of the GUI + sendData(); setDataFalse(); } else { try { @@ -1370,10 +1369,10 @@ class Stream extends Thread { println(e.getMessage()); } } else { - if (checkForData()) { + //if (checkForData()) { //sendData(); //setDataFalse(); - } + //} sendData(); } } From 1de7334c6e06896e443a60d0aca5889c8461730c Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Fri, 22 Jan 2021 17:53:07 -0600 Subject: [PATCH 11/15] Refactor Pulse networking output and improve outbound sampling rate --- OpenBCI_GUI/W_Networking.pde | 213 +++++++++++++++++------------------ 1 file changed, 102 insertions(+), 111 deletions(-) diff --git a/OpenBCI_GUI/W_Networking.pde b/OpenBCI_GUI/W_Networking.pde index 485784a46..e82ded28b 100644 --- a/OpenBCI_GUI/W_Networking.pde +++ b/OpenBCI_GUI/W_Networking.pde @@ -1349,7 +1349,7 @@ class Stream extends Thread { println(e.getMessage()); } } else { - if (checkForData()) { //This needs to be removed in next version of the GUI + if (checkForData()) { //This needs to be removed or modified in next version of the GUI sendData(); setDataFalse(); } else { @@ -1369,16 +1369,15 @@ class Stream extends Thread { println(e.getMessage()); } } else { - //if (checkForData()) { - //sendData(); - //setDataFalse(); - //} - sendData(); + if (checkForData()) { //This needs to be removed or modified in next version of the GUI + sendData(); + setDataFalse(); + } } } } - Boolean checkForData() { + Boolean checkForData() { //Try to remove these methods in next version of GUI if (this.dataType.equals("TimeSeries")) { return w_networking.newDataToSend; } else if (this.dataType.equals("FFT")) { @@ -1390,7 +1389,7 @@ class Stream extends Thread { } else if (this.dataType.equals("Accel/Aux")) { return w_networking.newDataToSend; } else if (this.dataType.equals("Pulse")) { - return w_networking.newDataToSend; + return true; } return false; } @@ -1869,65 +1868,64 @@ class Stream extends Thread { final int NUM_ANALOG_READS = analogChannels.length; // UNFILTERED & FILTERED, Aux data is not affected by filters anyways - if (this.filter==false || this.filter==true) { - // OSC - if (this.protocol.equals("OSC")) { - for (int i = 0; i < NUM_ANALOG_READS; i++) { - msg.clearArguments(); - msg.add(i+1); - msg.add((int)lastSample[analogChannels[i]]); - try { - this.osc.send(msg,this.netaddress); - } catch (Exception e) { - println(e.getMessage()); - } - } - // UDP - } else if (this.protocol.equals("UDP")) { - String outputter = "{\"type\":\"auxiliary\",\"data\":["; - for (int i = 0; i < NUM_ANALOG_READS; i++) { - int auxData = (int)lastSample[analogChannels[i]]; - String auxData_formatted = fourLeadingPlaces.format(auxData); - outputter += auxData_formatted; - if (i != NUM_ANALOG_READS - 1) { - outputter += ","; - } else { - outputter += "]}\r\n"; - } - } + //if (this.filter==false || this.filter==true) { + // OSC + if (this.protocol.equals("OSC")) { + for (int i = 0; i < NUM_ANALOG_READS; i++) { + msg.clearArguments(); + msg.add(i+1); + msg.add((int)lastSample[analogChannels[i]]); try { - this.udp.send(outputter, this.ip, this.port); + this.osc.send(msg,this.netaddress); } catch (Exception e) { println(e.getMessage()); } - // LSL - } else if (this.protocol.equals("LSL")) { - for (int i = 0; i < NUM_ANALOG_READS; i++) { - dataToSend[i] = (int)lastSample[analogChannels[i]]; - } - // Add timestamp to LSL Stream - outlet_data.push_sample(dataToSend); - } else if (this.protocol.equals("Serial")) { - // Data Format: 0001,0002,0003\n or 0001,0002\n depending if Wifi Shield is used - // 5 chars per pin, including \n char for Z - serialMessage = ""; - for (int i = 0; i < NUM_ANALOG_READS; i++) { - int auxData = (int)lastSample[analogChannels[i]]; - String auxData_formatted = String.format("%04d", auxData); - serialMessage += auxData_formatted; - if (i != NUM_ANALOG_READS - 1) { - serialMessage += ","; - } else { - serialMessage += "\n"; - } + } + // UDP + } else if (this.protocol.equals("UDP")) { + String outputter = "{\"type\":\"auxiliary\",\"data\":["; + for (int i = 0; i < NUM_ANALOG_READS; i++) { + int auxData = (int)lastSample[analogChannels[i]]; + String auxData_formatted = fourLeadingPlaces.format(auxData); + outputter += auxData_formatted; + if (i != NUM_ANALOG_READS - 1) { + outputter += ","; + } else { + outputter += "]}\r\n"; } - try { - //println(serialMessage); - this.serial_networking.write(serialMessage); - } catch (Exception e) { - println(e.getMessage()); + } + try { + this.udp.send(outputter, this.ip, this.port); + } catch (Exception e) { + println(e.getMessage()); + } + // LSL + } else if (this.protocol.equals("LSL")) { + for (int i = 0; i < NUM_ANALOG_READS; i++) { + dataToSend[i] = (int)lastSample[analogChannels[i]]; + } + // Add timestamp to LSL Stream + outlet_data.push_sample(dataToSend); + } else if (this.protocol.equals("Serial")) { + // Data Format: 0001,0002,0003\n or 0001,0002\n depending if Wifi Shield is used + // 5 chars per pin, including \n char for Z + serialMessage = ""; + for (int i = 0; i < NUM_ANALOG_READS; i++) { + int auxData = (int)lastSample[analogChannels[i]]; + String auxData_formatted = String.format("%04d", auxData); + serialMessage += auxData_formatted; + if (i != NUM_ANALOG_READS - 1) { + serialMessage += ","; + } else { + serialMessage += "\n"; } } + try { + //println(serialMessage); + this.serial_networking.write(serialMessage); + } catch (Exception e) { + println(e.getMessage()); + } } } @@ -1997,15 +1995,29 @@ class Stream extends Thread { } ////////////////////////////////////// Stream pulse data from W_PulseSensor + //This data type is not affected by GUI filters + //JAN 2021 - Using this method to test refactoring Networking streaming void sendPulseData() { - if (this.filter==false || this.filter==true) { + //Get data from Board that + int numDataPoints = 3; + double[][] frameData = currentBoard.getFrameData(); + int[] analogChannels = ((AnalogCapableBoard)currentBoard).getAnalogChannels(); + + //Check for state change in the available frameData. This works, but maybe checkIfEnoughDataToSend could be used instead and be more accurate... + if (!frameData.equals(previousFrameData)) { + + previousFrameData = frameData; + // OSC if (this.protocol.equals("OSC")) { - //ADD BPM Data (BPM, Signal, IBI) - for (int i = 0; i < (w_pulsesensor.PulseWaveY.length); i++) {//This works + + for (int i = 0; i < frameData[0].length; i++) + { + int raw_signal = (int)(frameData[analogChannels[0]][i]); + //ADD BPM Data (BPM, Signal, IBI) msg.clearArguments(); //This belongs here msg.add(w_pulsesensor.BPM); //Add BPM first - msg.add(w_pulsesensor.PulseWaveY[i]); //Add Raw Signal second + msg.add(raw_signal); //Add Raw Signal second msg.add(w_pulsesensor.IBI); //Add IBI third //Message received in Max via OSC is a list of three integers without commas: 75 512 600 : BPM Signal IBI //println(" " + this.port + " ~~~~ " + w_pulsesensor.BPM + "," + w_pulsesensor.PulseWaveY[i] + "," + w_pulsesensor.IBI); @@ -2015,50 +2027,28 @@ class Stream extends Thread { println(e.getMessage()); } } + // UDP } else if (this.protocol.equals("UDP")) { //////////////////This needs to be checked - int numDataPoints = 3; - double[][] frameData = currentBoard.getFrameData(); - int[] analogChannels = ((AnalogCapableBoard)currentBoard).getAnalogChannels(); - if (!frameData.equals(previousFrameData)) { - for (int i = 0; i < frameData[0].length; i++) - { - String outputter = "{\"type\":\"pulse\",\"data\":["; - int raw_signal = (int)(frameData[analogChannels[0]][i]); - outputter += str(w_pulsesensor.BPM) + ","; //Comma separated string output (BPM,Raw Signal,IBI) - outputter += str(raw_signal) + ","; - outputter += str(w_pulsesensor.IBI); - outputter += "]}\r\n"; - try { - this.udp.send(outputter, this.ip, this.port); - } catch (Exception e) { - println(e.getMessage()); - } + + for (int i = 0; i < frameData[0].length; i++) + { + String outputter = "{\"type\":\"pulse\",\"data\":["; + int raw_signal = (int)(frameData[analogChannels[0]][i]); + outputter += str(w_pulsesensor.BPM) + ","; //Comma separated string output (BPM,Raw Signal,IBI) + outputter += str(raw_signal) + ","; + outputter += str(w_pulsesensor.IBI); + outputter += "]}\r\n"; + try { + this.udp.send(outputter, this.ip, this.port); + } catch (Exception e) { + println(e.getMessage()); } - previousFrameData = frameData; } + // LSL } else if (this.protocol.equals("LSL")) { ///////////////////This needs to be checked - /* - float[] _dataToSend = new float[[3]; - //for (int i = 0; i < (w_pulsesensor.PulseWaveY.length); i++) { - _dataToSend[0] = w_pulsesensor.BPM; //Array output - _dataToSend[1] = w_pulsesensor.PulseWaveY[w_pulsesensor.PulseWaveY.length-1]; - _dataToSend[2] = w_pulsesensor.IBI; - //} - float[] _dataToSend = new float[numChan * 125]; - for (int i = 0; i < numChan; i++) { - for (int j = 0; j < 125; j++) { - _dataToSend[j+125*i] = fftBuff[i].getBand(j); - } - } - // Add timestamp to LSL Stream - // From LSLLink Library: The time stamps of other samples are automatically derived based on the sampling rate of the stream. - outlet_data.push_chunk(_dataToSend); - */ - int numDataPoints = 3; - double[][] frameData = currentBoard.getFrameData(); - int[] analogChannels = ((AnalogCapableBoard)currentBoard).getAnalogChannels(); + float[] _dataToSend = new float[frameData[0].length * numDataPoints]; for (int i = 0; i < frameData[0].length; i++) { @@ -2067,18 +2057,19 @@ class Stream extends Thread { _dataToSend[numDataPoints*i+1] = raw_signal; _dataToSend[numDataPoints*i+2] = w_pulsesensor.IBI; } - // Add timestamp to LSL Stream - outlet_data.push_chunk(_dataToSend); + // From LSLLink Library: The time stamps of other samples are automatically derived based on the sampling rate of the stream. + outlet_data.push_chunk(_dataToSend); + // Serial } else if (this.protocol.equals("Serial")) { // Send Pulse Data (BPM,Signal,IBI) over Serial - for (int i = 0; i < (w_pulsesensor.PulseWaveY.length); i++) { + + for (int i = 0; i < frameData[0].length; i++) + { serialMessage = ""; //clear message - int BPM = (w_pulsesensor.BPM); - int Signal = (w_pulsesensor.PulseWaveY[i]); - int IBI = (w_pulsesensor.IBI); - serialMessage += BPM + ","; //Comma separated string output (BPM,Raw Signal,IBI) - serialMessage += Signal + ","; - serialMessage += IBI; + int raw_signal = (int)(frameData[analogChannels[0]][i]); + serialMessage += w_pulsesensor.BPM + ","; //Comma separated string output (BPM,Raw Signal,IBI) + serialMessage += raw_signal + ","; + serialMessage += w_pulsesensor.IBI; try { println(serialMessage); this.serial_networking.write(serialMessage); From f462b4ccf2cf98df896b3f0c65cb9f984d8a031f Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Fri, 22 Jan 2021 18:11:06 -0600 Subject: [PATCH 12/15] Update changelog and bump version to 5.0.3-alpha.3 --- CHANGELOG.md | 2 ++ OpenBCI_GUI/Info.plist.tmpl | 2 +- OpenBCI_GUI/OpenBCI_GUI.pde | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9446a351c..442eea2a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ # v5.0.3 ### Improvements +* Increase sampling rate for Pulse data output in Networking Widget ### Bug Fixes +* Fix Pulse LSL output error #943 * Fix Accel/Aux UDP output #944 * Fix Expert Mode unplanned keyboard shortcuts crash GUI #941 diff --git a/OpenBCI_GUI/Info.plist.tmpl b/OpenBCI_GUI/Info.plist.tmpl index abc917b9d..9de05e0c6 100644 --- a/OpenBCI_GUI/Info.plist.tmpl +++ b/OpenBCI_GUI/Info.plist.tmpl @@ -23,7 +23,7 @@ CFBundleShortVersionString 4 CFBundleVersion - 5.0.3-alpha.2 + 5.0.3-alpha.3 CFBundleSignature ???? NSHumanReadableCopyright diff --git a/OpenBCI_GUI/OpenBCI_GUI.pde b/OpenBCI_GUI/OpenBCI_GUI.pde index f4557570f..abc488e47 100644 --- a/OpenBCI_GUI/OpenBCI_GUI.pde +++ b/OpenBCI_GUI/OpenBCI_GUI.pde @@ -64,7 +64,7 @@ import http.requests.*; // Global Variables & Instances //------------------------------------------------------------------------ //Used to check GUI version in TopNav.pde and displayed on the splash screen on startup -String localGUIVersionString = "v5.0.3-alpha.2"; +String localGUIVersionString = "v5.0.3-alpha.3"; String localGUIVersionDate = "January 2020"; String guiLatestVersionGithubAPI = "https://api.github.com/repos/OpenBCI/OpenBCI_GUI/releases/latest"; String guiLatestReleaseLocation = "https://github.com/OpenBCI/OpenBCI_GUI/releases/latest"; From a813659e9d27d79da42e51c352d54b2f9e3e5805 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Mon, 25 Jan 2021 21:39:07 -0600 Subject: [PATCH 13/15] Fix Session Settings Errors found in 5.0.2 Fixes #942 --- OpenBCI_GUI/DataProcessing.pde | 4 +-- OpenBCI_GUI/FilterEnums.pde | 44 ++++++++++++++++++++------- OpenBCI_GUI/SessionSettings.pde | 54 ++++++++++++++++++++------------- OpenBCI_GUI/TopNav.pde | 4 +++ OpenBCI_GUI/W_Spectrogram.pde | 3 -- 5 files changed, 72 insertions(+), 37 deletions(-) diff --git a/OpenBCI_GUI/DataProcessing.pde b/OpenBCI_GUI/DataProcessing.pde index 01b203cf8..6d39cbb52 100644 --- a/OpenBCI_GUI/DataProcessing.pde +++ b/OpenBCI_GUI/DataProcessing.pde @@ -93,8 +93,8 @@ class DataProcessing { float data_std_uV[]; float polarity[]; boolean newDataToSend; - BandPassRanges bpRange = BandPassRanges.FiveToFifty; - BandStopRanges bsRange = BandStopRanges.Sixty; + public BandPassRanges bpRange = BandPassRanges.FiveToFifty; + public BandStopRanges bsRange = BandStopRanges.Sixty; final int[] processing_band_low_Hz = { 1, 4, 8, 13, 30 }; //lower bound for each frequency band of interest (2D classifier only) diff --git a/OpenBCI_GUI/FilterEnums.pde b/OpenBCI_GUI/FilterEnums.pde index 65cb449fd..26d7a9ec1 100644 --- a/OpenBCI_GUI/FilterEnums.pde +++ b/OpenBCI_GUI/FilterEnums.pde @@ -1,21 +1,32 @@ public enum BandStopRanges { - Sixty(60.0d), - Fifty(50.0d), - None(null); + Sixty(0, 60.0d), + Fifty(1, 50.0d), + None(2, null); + private int index; private Double freq; private static BandStopRanges[] vals = values(); - BandStopRanges(Double freq) { + BandStopRanges(int index, Double freq) { + this.index = index; this.freq = freq; } + + public int getIndex() { + return index; + } public Double getFreq() { return freq; } + public static BandStopRanges getByIndex(int i) + { + return vals[i]; + } + public BandStopRanges next() { return vals[(this.ordinal() + 1) % vals.length]; @@ -31,22 +42,28 @@ public enum BandStopRanges public enum BandPassRanges { - FiveToFifty(5.0d, 50.0d), - SevenToThirteen(7.0d, 13.0d), - FifteenToFifty(15.0d, 50.0d), - OneToFifty(1.0d, 50.0d), - OneToHundred(1.0d, 100.0d), - None(null, null); + FiveToFifty(0, 5.0d, 50.0d), + SevenToThirteen(1, 7.0d, 13.0d), + FifteenToFifty(2, 15.0d, 50.0d), + OneToFifty(3, 1.0d, 50.0d), + OneToHundred(4, 1.0d, 100.0d), + None(5, null, null); + private int index; private Double start; private Double stop; private static BandPassRanges[] vals = values(); - BandPassRanges(Double start, Double stop) { + BandPassRanges(int index, Double start, Double stop) { + this.index = index; this.start = start; this.stop = stop; } + + public int getIndex() { + return index; + } public Double getStart() { return start; @@ -56,6 +73,11 @@ public enum BandPassRanges return stop; } + public static BandPassRanges getByIndex(int i) + { + return vals[i]; + } + public BandPassRanges next() { return vals[(this.ordinal() + 1) % vals.length]; diff --git a/OpenBCI_GUI/SessionSettings.pde b/OpenBCI_GUI/SessionSettings.pde index fcf050386..7fb6c8968 100644 --- a/OpenBCI_GUI/SessionSettings.pde +++ b/OpenBCI_GUI/SessionSettings.pde @@ -61,9 +61,6 @@ class SessionSettings { CColor dropdownColors = new CColor(); ///These `Save` vars are set to default when each widget instantiates ///and updated every time user selects from dropdown - //Notch and Bandpass filter variables for save - int dataProcessingNotchSave = 0; - int dataProcessingBandpassSave = 3; //Accelerometer settings int accVertScaleSave; int accHorizScaleSave; @@ -171,11 +168,6 @@ class SessionSettings { String[] spectMaxFrqArray = {"20 Hz", "40 Hz", "60 Hz", "100 Hz", "120 Hz", "250 Hz"}; String[] spectSampleRateArray = {"1 Hz", "5 hz", "10 Hz", "20 Hz", "40 Hz"}; - //Load global settings variables - int loadLayoutSetting; - int loadNotchSetting; - int loadBandpassSetting; - //Load Accel. dropdown variables int loadAccelVertScale; int loadAccelHorizScale; @@ -376,10 +368,13 @@ class SessionSettings { JSONObject saveGlobalSettings = new JSONObject(); saveGlobalSettings.setBoolean("Expert Mode", expertModeToggle); saveGlobalSettings.setInt("Current Layout", currentLayout); - saveGlobalSettings.setInt("Notch", dataProcessingNotchSave); - saveGlobalSettings.setInt("Bandpass Filter", dataProcessingBandpassSave); + saveGlobalSettings.setInt("Notch", dataProcessing.bsRange.getIndex()); + saveGlobalSettings.setInt("Bandpass Filter", dataProcessing.bpRange.getIndex()); saveGlobalSettings.setInt("Analog Read Vert Scale", arVertScaleSave); saveGlobalSettings.setInt("Analog Read Horiz Scale", arHorizScaleSave); + if (currentBoard instanceof SmoothingCapableBoard) { + saveGlobalSettings.setBoolean("Data Smoothing", ((SmoothingCapableBoard)currentBoard).getSmoothingActive()); + } saveSettingsJSONData.setJSONObject(kJSONKeySettings, saveGlobalSettings); /////Setup JSON Object for gui version and settings Version @@ -585,15 +580,15 @@ class SessionSettings { //get the global settings JSON object JSONObject loadGlobalSettings = loadSettingsJSONData.getJSONObject(kJSONKeySettings); - loadLayoutSetting = loadGlobalSettings.getInt("Current Layout"); - loadNotchSetting = loadGlobalSettings.getInt("Notch"); - loadBandpassSetting = loadGlobalSettings.getInt("Bandpass Filter"); - Boolean loadExpertModeToggle = loadGlobalSettings.getBoolean("Expert Mode"); + //Store loaded layout to current layout variable + currentLayout = loadGlobalSettings.getInt("Current Layout"); loadAnalogReadVertScale = loadGlobalSettings.getInt("Analog Read Vert Scale"); loadAnalogReadHorizScale = loadGlobalSettings.getInt("Analog Read Horiz Scale"); - //Store loaded layout to current layout variable - currentLayout = loadLayoutSetting; //Load more global settings after this line, if needed + int loadNotchSetting = loadGlobalSettings.getInt("Notch"); + int loadBandpassSetting = loadGlobalSettings.getInt("Bandpass Filter"); + Boolean loadExpertModeToggle = loadGlobalSettings.getBoolean("Expert Mode"); + Boolean loadDataSmoothingSetting = (currentBoard instanceof SmoothingCapableBoard) ? loadGlobalSettings.getBoolean("Data Smoothing") : null; //get the FFT settings JSONObject loadFFTSettings = loadSettingsJSONData.getJSONObject(kJSONKeyFFT); @@ -717,8 +712,8 @@ class SessionSettings { //get the Widget/Container settings JSONObject loadWidgetSettings = loadSettingsJSONData.getJSONObject(kJSONKeyWidget); //Apply Layout directly before loading and applying widgets to containers - wm.setNewContainerLayout(loadLayoutSetting); - verbosePrint("LoadGUISettings: Layout " + loadLayoutSetting + " Loaded!"); + wm.setNewContainerLayout(currentLayout); + verbosePrint("LoadGUISettings: Layout " + currentLayout + " Loaded!"); numLoadedWidgets = loadWidgetSettings.size(); @@ -749,11 +744,28 @@ class SessionSettings { ///////////////////////////////////////////////////////////// // Load more widget settings above this line as above // + ///////////////////////////////////////////////////////////// - //}//end case for all objects in JSON + ///////////////////////////////////////////////////////////// + // Apply Settings below this line // + ///////////////////////////////////////////////////////////// + + //Apply notch + dataProcessing.bsRange = BandStopRanges.getByIndex(loadNotchSetting); + topNav.filtNotchButton.getCaptionLabel().setText("Notch\n" + dataProcessing.getShortNotchDescription()); + //Apply Bandpass filter + dataProcessing.bpRange = BandPassRanges.getByIndex(loadBandpassSetting); + topNav.filtBPButton.getCaptionLabel().setText("BP Filt\n" + dataProcessing.getShortFilterDescription()); + + //Apply Data Smoothing for capable boards + if (currentBoard instanceof SmoothingCapableBoard) { + ((SmoothingCapableBoard)currentBoard).setSmoothingActive(loadDataSmoothingSetting); + topNav.updateSmoothingButtonText(); + } //Apply Expert Mode toggle - topNav.configSelector.toggleExpertMode(loadExpertModeToggle); + //This should not be loaded with other session settings - RW Jan 2021 + //topNav.configSelector.toggleExpertMode(loadExpertModeToggle); //Load and apply all of the settings that are in dropdown menus. It's a bit much, so it has it's own function below. loadApplyWidgetDropdownText(); @@ -987,7 +999,7 @@ class SessionSettings { w_timeSeries.cp5_widget.getController("VertScale_TS").getCaptionLabel().setText(w_timeSeries.getTSVertScale().getString()); //changes front-end w_timeSeries.setTSHorizScale(loadTimeSeriesSettings.getInt("Time Series Horiz Scale")); - w_timeSeries.cp5_widget.getController("Duration").getCaptionLabel().setText(w_timeSeries.getTSVertScale().getString()); + w_timeSeries.cp5_widget.getController("Duration").getCaptionLabel().setText(w_timeSeries.getTSHorizScale().getString()); JSONArray loadTSChan = loadTimeSeriesSettings.getJSONArray("activeChannels"); w_timeSeries.tsChanSelect.deactivateAllButtons(); diff --git a/OpenBCI_GUI/TopNav.pde b/OpenBCI_GUI/TopNav.pde index 78b7acbe4..448dec2b9 100644 --- a/OpenBCI_GUI/TopNav.pde +++ b/OpenBCI_GUI/TopNav.pde @@ -398,6 +398,10 @@ class TopNav { return val; } + public void updateSmoothingButtonText() { + smoothingButton.getCaptionLabel().setText(getSmoothingString()); + } + private String getSmoothingString() { return ((SmoothingCapableBoard)currentBoard).getSmoothingActive() ? "Smoothing\n On" : "Smoothing\n Off"; } diff --git a/OpenBCI_GUI/W_Spectrogram.pde b/OpenBCI_GUI/W_Spectrogram.pde index c93b066c1..02a01321c 100644 --- a/OpenBCI_GUI/W_Spectrogram.pde +++ b/OpenBCI_GUI/W_Spectrogram.pde @@ -424,9 +424,6 @@ class W_Spectrogram extends Widget { //triggered when there is an event in the Spectrogram Widget MaxFreq. Dropdown void SpectrogramMaxFreq(int n) { settings.spectMaxFrqSave = n; - //Link the choices made in the FFT widget and the Spectrogram Widget for this parameter - MaxFreq(n); - w_fft.cp5_widget.getController("MaxFreq").getCaptionLabel().setText(settings.fftMaxFrqArray[n]); //reset the vertical axis labelss w_spectrogram.vertAxisLabel = w_spectrogram.vertAxisLabels[n]; //Resize the height of the data image From 8b9fee80fef646841d3ef4b687a098fb339993e5 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Mon, 25 Jan 2021 21:43:13 -0600 Subject: [PATCH 14/15] Update changelog and bump version to 5.0.3-beta.0 --- CHANGELOG.md | 1 + OpenBCI_GUI/Info.plist.tmpl | 2 +- OpenBCI_GUI/OpenBCI_GUI.pde | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 442eea2a2..5e53da4b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Fix Pulse LSL output error #943 * Fix Accel/Aux UDP output #944 * Fix Expert Mode unplanned keyboard shortcuts crash GUI #941 +* Fix bugs found when loading Session Settings #942 # v5.0.2 diff --git a/OpenBCI_GUI/Info.plist.tmpl b/OpenBCI_GUI/Info.plist.tmpl index 9de05e0c6..4599da95a 100644 --- a/OpenBCI_GUI/Info.plist.tmpl +++ b/OpenBCI_GUI/Info.plist.tmpl @@ -23,7 +23,7 @@ CFBundleShortVersionString 4 CFBundleVersion - 5.0.3-alpha.3 + 5.0.3-beta.0 CFBundleSignature ???? NSHumanReadableCopyright diff --git a/OpenBCI_GUI/OpenBCI_GUI.pde b/OpenBCI_GUI/OpenBCI_GUI.pde index abc488e47..51c9bd5fe 100644 --- a/OpenBCI_GUI/OpenBCI_GUI.pde +++ b/OpenBCI_GUI/OpenBCI_GUI.pde @@ -64,7 +64,7 @@ import http.requests.*; // Global Variables & Instances //------------------------------------------------------------------------ //Used to check GUI version in TopNav.pde and displayed on the splash screen on startup -String localGUIVersionString = "v5.0.3-alpha.3"; +String localGUIVersionString = "v5.0.3-beta.0"; String localGUIVersionDate = "January 2020"; String guiLatestVersionGithubAPI = "https://api.github.com/repos/OpenBCI/OpenBCI_GUI/releases/latest"; String guiLatestReleaseLocation = "https://github.com/OpenBCI/OpenBCI_GUI/releases/latest"; From 637e9b1409782817578a62d7eecf72e2e7dde2e9 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Fri, 29 Jan 2021 11:24:41 -0600 Subject: [PATCH 15/15] Bump version to 5.0.3 - jan 2021 --- OpenBCI_GUI/Info.plist.tmpl | 6 +++--- OpenBCI_GUI/OpenBCI_GUI.pde | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/OpenBCI_GUI/Info.plist.tmpl b/OpenBCI_GUI/Info.plist.tmpl index 4599da95a..e814301d7 100644 --- a/OpenBCI_GUI/Info.plist.tmpl +++ b/OpenBCI_GUI/Info.plist.tmpl @@ -23,16 +23,16 @@ CFBundleShortVersionString 4 CFBundleVersion - 5.0.3-beta.0 + 5.0.3 CFBundleSignature ???? NSHumanReadableCopyright MIT License - Copyright © 2020 OpenBCI + Copyright © 2021 OpenBCI CFBundleGetInfoString - December 2020 + January 2021 @@jvm_runtime@@ diff --git a/OpenBCI_GUI/OpenBCI_GUI.pde b/OpenBCI_GUI/OpenBCI_GUI.pde index 51c9bd5fe..880cb5327 100644 --- a/OpenBCI_GUI/OpenBCI_GUI.pde +++ b/OpenBCI_GUI/OpenBCI_GUI.pde @@ -64,8 +64,8 @@ import http.requests.*; // Global Variables & Instances //------------------------------------------------------------------------ //Used to check GUI version in TopNav.pde and displayed on the splash screen on startup -String localGUIVersionString = "v5.0.3-beta.0"; -String localGUIVersionDate = "January 2020"; +String localGUIVersionString = "v5.0.3"; +String localGUIVersionDate = "January 2021"; String guiLatestVersionGithubAPI = "https://api.github.com/repos/OpenBCI/OpenBCI_GUI/releases/latest"; String guiLatestReleaseLocation = "https://github.com/OpenBCI/OpenBCI_GUI/releases/latest";