Skip to content

Commit

Permalink
Merge pull request #962 from OpenBCI/development
Browse files Browse the repository at this point in the history
GUI 5.0.4
  • Loading branch information
retiutut authored Mar 27, 2021
2 parents 4b3dfbd + fd87f89 commit f78cefa
Show file tree
Hide file tree
Showing 27 changed files with 824 additions and 621 deletions.
20 changes: 0 additions & 20 deletions .github/workflows/mergenovadev.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ libBoardController.so
libDataHandler.so
libGanglionLib.so
libGanglionScan.so
libunicorn.so
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ jobs:
branches:
only:
- master
- novaxr-dev

before_install:
- if [ "$TRAVIS_OS_NAME" = osx ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then openssl aes-256-cbc -K $encrypted_2f5d2771e3cb_key -iv $encrypted_2f5d2771e3cb_iv -in release_script/mac_only/Certificates.p12.enc -out release_script/mac_only/Certificates.p12 -d; fi
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# v5.0.4

### Improvements
* Add Copy/Paste for all textfields on all OS #940
* Update BrainFlow library to version that includes a marker channel
* Handle paths with spaces on Linux Standalone GUI #916
* Allow Expert Ganglion Users to send square wave commands via keyboard #950
* Show Send Custom Hardware Command UI for Cyton Expert Mode in Hardware Settings
* Improve Hardware Setting UX/UI for ADS1299 boards #954

### Bug Fixes
* Clean up GUI code to fix Processing/JVM memory issue causing crash #955
* Avoid playback history file not found exception #959
* Fix issue with Spectrogram Widget data image default height
* Fix issue with Accelerometer Widget graph default vertical scale
* Fix text drawing in wrong spot in Session Data box in Control Panel

# v5.0.3

### Improvements
Expand Down
147 changes: 147 additions & 0 deletions Networking-Test-Kit/LSL/brainflow_lsl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#############################################################################
## BrainFlow + LSL ##
## Use BrainFlow to read data from board send it as an LSL stream ##
#############################################################################

# Install dependencies with:
# pip install --upgrade numpy brainflow pylsl

# Here are example commands using Cyton and get_exg_channels()from BrainFlow. This has only been tested with Cyton + Dongle, for now.

# Mac:
# python3 Networking-Test-Kit/LSL/brainflow_lsl.py --board-id 2 --serial-port /dev/cu.usbserial-DM00D7TW --name test --data-type EXG --channel-names 1,2,3,4,5,6,7,8 --uid brainflow

# Windows:
# python3 Networking-Test-Kit/LSL/brainflow_lsl.py --board-id 2 --serial-port COM3 --name test --data-type EXG --channel-names 1,2,3,4,5,6,7,8 --uid brainflow

import argparse
import time
import numpy as np

from queue import Queue

import brainflow
from brainflow.board_shim import BoardShim, BrainFlowInputParams
from brainflow.data_filter import DataFilter, FilterTypes, AggOperations

from pylsl import StreamInfo, StreamOutlet, local_clock

def channel_select(board, board_id, data_type):
switcher = {
'EXG': board.get_exg_channels(board_id),
# can add more
}

return switcher.get(data_type, "error")

def main():
BoardShim.enable_dev_board_logger()

parser = argparse.ArgumentParser()

# brainflow params - use docs to check which parameters are required for specific board, e.g. for Cyton set serial port
parser.add_argument('--timeout', type=int, help='timeout for device discovery or connection', required=False, default=0)
parser.add_argument('--ip-address', type=str, help='ip address', required=False, default='')
parser.add_argument('--board-id', type=int, help='board id, check docs to get a list of supported boards', required=True)
parser.add_argument('--serial-port', type=str, help='serial port', required=False, default='')
parser.add_argument('--streamer-params', type=str, help='streamer params', required=False, default='')

# LSL params
parser.add_argument('--name', type=str, help='name', required=True)
parser.add_argument('--data-type', type=str, help='data type', required=True)
parser.add_argument('--channel-names', type=str, help='channel names', required=True)
parser.add_argument('--uid', type=str, help='uid', required=True)

args = parser.parse_args()

# brainflow initialization
params = BrainFlowInputParams()
params.serial_port = args.serial_port
params.ip_address = args.ip_address
board = BoardShim(args.board_id, params)

# LSL initialization
channel_names = args.channel_names.split(',')
n_channels = len(channel_names)
srate = board.get_sampling_rate(args.board_id)
info = StreamInfo(args.name, args.data_type, n_channels, srate, 'double64', args.uid)
outlet = StreamOutlet(info)
fw_delay = 0

# prepare session
board.prepare_session()

# send commands to the board for every channel. Cyton has 8 Channels. Here, we turn off every channel except for 1 and 8.
# This is here for testing purposes.
#board.config_board("x1000110X") #Lower the gain to 1x on channel 1
#board.config_board("x1061000X")
#board.config_board("x2161000X")
#board.config_board("x3161000X")
#board.config_board("x4161000X")
#board.config_board("x5161000X")
#board.config_board("x6161000X")
#board.config_board("x7161000X")
#board.config_board("x8060110X")

# start stream
board.start_stream(45000, args.streamer_params)
time.sleep(1)
start_time = local_clock()
sent_samples = 0
queue = Queue(maxsize = 5*srate)
chans = channel_select(board, args.board_id, args.data_type)

# Vars for filters
applyBandStop = True
applyBandPass = True
bandStopFrequency = 60.0
bp_lowerBound = 5.0
bp_upperBound = 50.0
bp_centerFreq = (bp_upperBound + bp_lowerBound) / 2.0;
bp_bandWidth = bp_upperBound - bp_lowerBound


# read data with brainflow and send it via LSL
print("Now sending data...")
while True:
data = board.get_board_data()[chans]

# It's best to apply filters on the receiving end, but this is here just for testing purposes.
"""
for chan in range(len(chans)):
if applyBandStop:
DataFilter.perform_bandstop(data[chan],
BoardShim.get_sampling_rate(args.board_id),
bandStopFrequency,
4.0,
2,
FilterTypes.BUTTERWORTH.value,
0);
if applyBandPass:
DataFilter.perform_bandpass(
data[chan],
BoardShim.get_sampling_rate(args.board_id),
bp_centerFreq,
bp_bandWidth,
2,
FilterTypes.BUTTERWORTH.value,
0);
"""

for i in range(len(data[0])):
queue.put(data[:,i].tolist())
elapsed_time = local_clock() - start_time
required_samples = int(srate * elapsed_time) - sent_samples
if required_samples > 0 and queue.qsize() >= required_samples:
mychunk = []

for i in range(required_samples):
mychunk.append(queue.get())
stamp = local_clock() - fw_delay
outlet.push_chunk(mychunk, stamp)
sent_samples += required_samples
time.sleep(1)


if __name__ == "__main__":
main()
15 changes: 10 additions & 5 deletions Networking-Test-Kit/LSL/lslStreamTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,25 @@

def testLSLSamplingRate():
start = time.time()
numSamples = 0
totalNumSamples = 0
validSamples = 0
numChunks = 0

while time.time() <= start + duration:
# get chunks of samples
samples, timestamp = inlet.pull_chunk()
if timestamp:
if samples:
numChunks += 1
print( len(samples) )
numSamples += len(samples)
totalNumSamples += len(samples)
# print(samples);
for sample in samples:
print(sample)
validSamples += 1

print( "Number of Chunks == {}".format(numChunks) )
print( "Avg Sampling Rate == {}".format(numSamples / duration) )
print( "Number of Chunks and Samples == {} , {}".format(numChunks, totalNumSamples) )
print( "Valid Samples and Duration == {} / {}".format(validSamples, duration) )
print( "Avg Sampling Rate == {}".format(validSamples / duration) )


testLSLSamplingRate()
58 changes: 58 additions & 0 deletions Networking-Test-Kit/LSL/lslStreamTest_Chan1Pulse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""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', 'EXG')

# create a new inlet to read from the stream
inlet = StreamInlet(streams[0])
duration = 10

sleep(0)

def testLSLSamplingRate():
start = time.time()
numSamples = 0
numChunks = 0

while time.time() <= start + duration:
# get chunks of samples
chunk, timestamp = inlet.pull_chunk()
if timestamp:
numChunks += 1
for sample in chunk:
numSamples += 1

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:
chunk, timestamp = inlet.pull_chunk()
if timestamp:
for sample in chunk:
print(sample)
raw_pulse_signal.append(sample[0])

# 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()

testLSLPulseData()
Loading

0 comments on commit f78cefa

Please sign in to comment.