From 54514375ee805f93a505305e869d648b91ea5a9f Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Mon, 6 Oct 2025 12:37:03 -0500 Subject: [PATCH 1/4] Remove unused Galea BoardIds and related files --- .gitignore | 1 + .../brainflow/board_controller_library.cs | 3 - emulator/brainflow_emulator/galea_manual.py | 79 ++- .../brainflow_emulator/galea_manual_v4.py | 150 ---- .../brainflow_emulator/galea_serial_manual.py | 150 ---- .../galea_serial_manual_v4.py | 150 ---- .../galea_serial_windows.py | 167 ----- .../galea_serial_windows_v4.py | 167 ----- emulator/brainflow_emulator/galea_udp.py | 10 +- emulator/brainflow_emulator/galea_udp_v4.py | 118 ---- .../src/main/java/brainflow/BoardIds.java | 3 - julia_package/brainflow/src/board_shim.jl | 3 - matlab_package/brainflow/BoardIds.m | 3 - nodejs_package/brainflow/brainflow.types.ts | 3 - python_package/brainflow/board_shim.py | 3 - src/board_controller/board_controller.cpp | 12 - src/board_controller/brainflow_boards.cpp | 115 +-- src/board_controller/build.cmake | 3 - src/board_controller/openbci/galea.cpp | 93 ++- src/board_controller/openbci/galea_serial.cpp | 488 ------------- .../openbci/galea_serial_v4.cpp | 529 -------------- src/board_controller/openbci/galea_v4.cpp | 661 ------------------ src/board_controller/openbci/inc/galea.h | 2 +- .../openbci/inc/galea_serial.h | 43 -- .../openbci/inc/galea_serial_v4.h | 43 -- src/board_controller/openbci/inc/galea_v4.h | 49 -- src/utils/inc/brainflow_constants.h | 3 - 27 files changed, 159 insertions(+), 2892 deletions(-) delete mode 100644 emulator/brainflow_emulator/galea_manual_v4.py delete mode 100644 emulator/brainflow_emulator/galea_serial_manual.py delete mode 100644 emulator/brainflow_emulator/galea_serial_manual_v4.py delete mode 100644 emulator/brainflow_emulator/galea_serial_windows.py delete mode 100644 emulator/brainflow_emulator/galea_serial_windows_v4.py delete mode 100644 emulator/brainflow_emulator/galea_udp_v4.py delete mode 100644 src/board_controller/openbci/galea_serial.cpp delete mode 100644 src/board_controller/openbci/galea_serial_v4.cpp delete mode 100644 src/board_controller/openbci/galea_v4.cpp delete mode 100644 src/board_controller/openbci/inc/galea_serial.h delete mode 100644 src/board_controller/openbci/inc/galea_serial_v4.h delete mode 100644 src/board_controller/openbci/inc/galea_v4.h diff --git a/.gitignore b/.gitignore index 8cd184c92..728e34fb5 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ x64/ x86/ bld/ +build/ [Bb]in/ [Oo]bj/ [Ll]og/ diff --git a/csharp_package/brainflow/brainflow/board_controller_library.cs b/csharp_package/brainflow/brainflow/board_controller_library.cs index 10a910621..43a745336 100644 --- a/csharp_package/brainflow/brainflow/board_controller_library.cs +++ b/csharp_package/brainflow/brainflow/board_controller_library.cs @@ -80,7 +80,6 @@ public enum BoardIds FREEEEG32_BOARD = 17, BRAINBIT_BLED_BOARD = 18, GFORCE_DUAL_BOARD = 19, - GALEA_SERIAL_BOARD = 20, MUSE_S_BLED_BOARD = 21, MUSE_2_BLED_BOARD = 22, CROWN_BOARD = 23, @@ -107,8 +106,6 @@ public enum BoardIds EXPLORE_8_CHAN_BOARD = 45, GANGLION_NATIVE_BOARD = 46, EMOTIBIT_BOARD = 47, - GALEA_BOARD_V4 = 48, - GALEA_SERIAL_BOARD_V4 = 49, NTL_WIFI_BOARD = 50, ANT_NEURO_EE_511_BOARD = 51, FREEEEG128_BOARD = 52, diff --git a/emulator/brainflow_emulator/galea_manual.py b/emulator/brainflow_emulator/galea_manual.py index de211c240..810676f0c 100644 --- a/emulator/brainflow_emulator/galea_manual.py +++ b/emulator/brainflow_emulator/galea_manual.py @@ -14,7 +14,7 @@ class State(enum.Enum): class Message(enum.Enum): start_stream = b'b' stop_stream = b's' - ack_values = (b'd', b'~6', b'~5', b'o', b'F0') + ack_values = (b'd', b'~6', b'~5', b'~4', b'o', b'F0') ack_from_device = b'A' time_calc_command = b'F4444444' @@ -25,14 +25,19 @@ def __init__(self): self.local_ip = '127.0.0.1' self.local_port = 2390 self.server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) - self.server_socket.settimeout( - 0.1) # decreases sampling rate significantly because it will wait for recv 0.1 sec but it's just a test self.server_socket.bind((self.local_ip, self.local_port)) self.state = State.wait.value self.addr = None self.package_num = 0 - self.transaction_size = 19 - self.package_size = 72 + self.transaction_size = 12 + self.package_size = 114 + self.server_socket.settimeout(.0001) + self.channel_on_off = [1] * 24 + self.channel_identifiers = [ + '1', '2', '3', '4', '5', '6', '7', '8', + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'A', 'S', 'D', 'G', 'H', 'J', 'K', 'L' + ] def run(self): start_time = time.time() @@ -43,8 +48,14 @@ def run(self): self.state = State.stream.value elif msg == Message.stop_stream.value: self.state = State.wait.value + elif msg.decode('utf-8').startswith('~'): + self.server_socket.sendto(Message.ack_from_device.value, self.addr) + self.process_sampling_rate(msg.decode('utf-8')) elif msg in Message.ack_values.value or msg.decode('utf-8').startswith('x'): self.server_socket.sendto(Message.ack_from_device.value, self.addr) + self.process_channel_on_off(msg.decode('utf-8')) + elif msg in Message.ack_values.value or msg.decode('utf-8').startswith('z'): + self.server_socket.sendto(Message.ack_from_device.value, self.addr) elif msg == Message.time_calc_command.value: cur_time = time.time() resp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) @@ -58,27 +69,40 @@ def run(self): if self.state == State.stream.value: transaction = list() - for _ in range(self.transaction_size): + for t in range(self.transaction_size): single_package = list() + channel = 0 for i in range(self.package_size): - single_package.append(random.randint(0, 255)) + if (i > 4 and i < 77): + sample = i - 4 + if (sample % 3 == 1): + channel += 1 + if (self.channel_on_off[channel - 1] == 1): + if (sample % 3 == 2): + single_package.append(random.randint(0, 8 + (channel * 2))) + else: + single_package.append(0) + else: + single_package.append(0) + else: + single_package.append(random.randint(0, 255)) single_package[0] = self.package_num cur_time = time.time() timestamp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) - eda = bytearray(struct.pack('f', random.random())) + eda = bytearray(struct.pack('f', .5)) ppg_red = bytearray(struct.pack('i', int(random.random() * 5000))) ppg_ir = bytearray(struct.pack('i', int(random.random() * 5000))) - for i in range(64, 72): - single_package[i] = timestamp[i - 64] + for i in range(88, 96): + single_package[i] = timestamp[i - 88] for i in range(1, 5): single_package[i] = eda[i - 1] - for i in range(60, 64): - single_package[i] = ppg_ir[i - 60] - for i in range(56, 60): - single_package[i] = ppg_red[i - 56] - single_package[53] = random.randint(0, 100) + for i in range(84, 88): + single_package[i] = ppg_ir[i - 84] + for i in range(80, 84): + single_package[i] = ppg_red[i - 80] + single_package[77] = random.randint(0, 100) self.package_num = self.package_num + 1 if self.package_num % 256 == 0: @@ -91,7 +115,30 @@ def run(self): self.server_socket.sendto(bytes(package), self.addr) except socket.timeout: logging.info('timeout for send') - + time.sleep(0.001) + + + def process_channel_on_off(self, msg): + if msg.startswith('x'): + for channel_id in self.channel_identifiers: + channel_num = self.channel_identifiers.index(channel_id) + if (msg[1] == channel_id): + if (msg[2] == '0'): # 0 is off (or Power Down), 1 is on + self.channel_on_off[channel_num] = 1 + logging.info('channel '+ str(channel_num + 1) + ' is on') + else: + self.channel_on_off[channel_num] = 0 + logging.info('channel ' + str(channel_num + 1) + ' is off') + + def process_sampling_rate(self, msg): + if (msg[1] == '6'): + logging.info('sampling rate is 250Hz') + elif (msg[1] == '5'): + logging.info('sampling rate is 500Hz') + elif (msg[1] == '4'): + logging.info('sampling rate is 1000Hz') + else: + logging.warning(f'did not recognize sampling rate command: {msg}') def main(): emulator = GaleaEmulator() diff --git a/emulator/brainflow_emulator/galea_manual_v4.py b/emulator/brainflow_emulator/galea_manual_v4.py deleted file mode 100644 index 810676f0c..000000000 --- a/emulator/brainflow_emulator/galea_manual_v4.py +++ /dev/null @@ -1,150 +0,0 @@ -import enum -import logging -import random -import socket -import struct -import time - - -class State(enum.Enum): - wait = 'wait' - stream = 'stream' - - -class Message(enum.Enum): - start_stream = b'b' - stop_stream = b's' - ack_values = (b'd', b'~6', b'~5', b'~4', b'o', b'F0') - ack_from_device = b'A' - time_calc_command = b'F4444444' - - -class GaleaEmulator(object): - - def __init__(self): - self.local_ip = '127.0.0.1' - self.local_port = 2390 - self.server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) - self.server_socket.bind((self.local_ip, self.local_port)) - self.state = State.wait.value - self.addr = None - self.package_num = 0 - self.transaction_size = 12 - self.package_size = 114 - self.server_socket.settimeout(.0001) - self.channel_on_off = [1] * 24 - self.channel_identifiers = [ - '1', '2', '3', '4', '5', '6', '7', '8', - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', - 'A', 'S', 'D', 'G', 'H', 'J', 'K', 'L' - ] - - def run(self): - start_time = time.time() - while True: - try: - msg, self.addr = self.server_socket.recvfrom(128) - if msg == Message.start_stream.value: - self.state = State.stream.value - elif msg == Message.stop_stream.value: - self.state = State.wait.value - elif msg.decode('utf-8').startswith('~'): - self.server_socket.sendto(Message.ack_from_device.value, self.addr) - self.process_sampling_rate(msg.decode('utf-8')) - elif msg in Message.ack_values.value or msg.decode('utf-8').startswith('x'): - self.server_socket.sendto(Message.ack_from_device.value, self.addr) - self.process_channel_on_off(msg.decode('utf-8')) - elif msg in Message.ack_values.value or msg.decode('utf-8').startswith('z'): - self.server_socket.sendto(Message.ack_from_device.value, self.addr) - elif msg == Message.time_calc_command.value: - cur_time = time.time() - resp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) - self.server_socket.sendto(resp, self.addr) - else: - if msg: - # we dont handle board config characters because they dont change package format - logging.warn('received unexpected string %s', str(msg)) - except socket.timeout: - logging.debug('timeout for recv') - - if self.state == State.stream.value: - transaction = list() - for t in range(self.transaction_size): - single_package = list() - channel = 0 - for i in range(self.package_size): - if (i > 4 and i < 77): - sample = i - 4 - if (sample % 3 == 1): - channel += 1 - if (self.channel_on_off[channel - 1] == 1): - if (sample % 3 == 2): - single_package.append(random.randint(0, 8 + (channel * 2))) - else: - single_package.append(0) - else: - single_package.append(0) - else: - single_package.append(random.randint(0, 255)) - single_package[0] = self.package_num - - cur_time = time.time() - timestamp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) - eda = bytearray(struct.pack('f', .5)) - ppg_red = bytearray(struct.pack('i', int(random.random() * 5000))) - ppg_ir = bytearray(struct.pack('i', int(random.random() * 5000))) - - for i in range(88, 96): - single_package[i] = timestamp[i - 88] - for i in range(1, 5): - single_package[i] = eda[i - 1] - for i in range(84, 88): - single_package[i] = ppg_ir[i - 84] - for i in range(80, 84): - single_package[i] = ppg_red[i - 80] - single_package[77] = random.randint(0, 100) - - self.package_num = self.package_num + 1 - if self.package_num % 256 == 0: - self.package_num = 0 - transaction.append(single_package) - try: - package = list() - for i in range(self.transaction_size): - package.extend(bytes(transaction[i])) - self.server_socket.sendto(bytes(package), self.addr) - except socket.timeout: - logging.info('timeout for send') - time.sleep(0.001) - - - def process_channel_on_off(self, msg): - if msg.startswith('x'): - for channel_id in self.channel_identifiers: - channel_num = self.channel_identifiers.index(channel_id) - if (msg[1] == channel_id): - if (msg[2] == '0'): # 0 is off (or Power Down), 1 is on - self.channel_on_off[channel_num] = 1 - logging.info('channel '+ str(channel_num + 1) + ' is on') - else: - self.channel_on_off[channel_num] = 0 - logging.info('channel ' + str(channel_num + 1) + ' is off') - - def process_sampling_rate(self, msg): - if (msg[1] == '6'): - logging.info('sampling rate is 250Hz') - elif (msg[1] == '5'): - logging.info('sampling rate is 500Hz') - elif (msg[1] == '4'): - logging.info('sampling rate is 1000Hz') - else: - logging.warning(f'did not recognize sampling rate command: {msg}') - -def main(): - emulator = GaleaEmulator() - emulator.run() - - -if __name__ == '__main__': - logging.basicConfig(format='%(asctime)s %(levelname)-8s %(message)s', level=logging.INFO) - main() \ No newline at end of file diff --git a/emulator/brainflow_emulator/galea_serial_manual.py b/emulator/brainflow_emulator/galea_serial_manual.py deleted file mode 100644 index 726990d9f..000000000 --- a/emulator/brainflow_emulator/galea_serial_manual.py +++ /dev/null @@ -1,150 +0,0 @@ -import logging -import os -import random -import struct -import subprocess -import threading -import time - -import pkg_resources -from serial import Serial - - -def write(port, data): - return port.write(data) - - -def read(port, num_bytes): - return port.read(num_bytes) - - -def get_isntaller(): - return pkg_resources.resource_filename(__name__, os.path.join('com0com', 'setup_com0com_W7_x64_signed.exe')) - - -def install_com0com(): - this_directory = os.path.abspath(os.path.dirname(__file__)) - directory = os.path.join(this_directory, 'com0com') - if not os.path.exists(directory): - os.makedirs(directory) - cmds = [get_isntaller(), '/NCRC', '/S', '/D=%s' % directory] - logging.info('running %s' % ' '.join(cmds)) - p = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() - if p.returncode != 0: - logging.error('stdout is %s' % out) - logging.error('stderr is %s' % err) - raise Exception('com0com installation failure') - logging.info('Sleeping a few second, it doesnt work in appveyour without it') - time.sleep(10) - return directory - - -def get_ports_windows(): - directory = install_com0com() - # remove ports from previous run if any - p = subprocess.Popen([os.path.join(directory, 'setupc.exe'), 'remove', '0'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory) - stdout, stderr = p.communicate() - logging.info('remove stdout is %s' % stdout) - logging.info('remove stderr is %s' % stderr) - - m_name = 'COM16' - s_name = 'COM17' - - p = subprocess.Popen( - [os.path.join(directory, 'setupc.exe'), 'install', 'PortName=%s' % m_name, 'PortName=%s' % s_name], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory) - stdout, stderr = p.communicate() - logging.info('install stdout is %s' % stdout) - logging.info('install stderr is %s' % stderr) - - if p.returncode != 0: - raise Exception('com0com failure') - logging.info('Sleeping a few second, it doesnt work in appveyour without it') - time.sleep(10) - return m_name, s_name - - -def test_serial(m_name, s_name): - master = Serial('\\\\.\\%s' % m_name, timeout=0) - listen_thread = Listener(master, write, read) - listen_thread.start() - listen_thread.join() - master.close() - return stdout, stderr - - -class Listener(threading.Thread): - - def __init__(self, port, write, read): - # for windows write and read are methods from Serial object, for linux - os.read/write it doesnt work otherwise - threading.Thread.__init__(self) - self.port = port - self.writer_process = None - self.write = write - self.read = read - - def run(self): - start_time = time.time() - while True: - res = self.read(self.port, 9) - if len(res) < 1: - time.sleep(1) - continue - logging.info('read "%s"' % res) - if 'F444' in res.decode('utf-8'): - cur_time = time.time() - resp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) - self.write(self.port, resp) - logging.info('sent resp to calc time command') - elif 'b' in res.decode('utf-8'): - self.writer_process = GaleaWriter(self.port, 0.005, self.write) - self.writer_process.daemon = True - self.writer_process.start() - elif 's' in res.decode('utf-8'): - if self.writer_process is not None: - if self.writer_process.is_alive(): - self.writer_process.need_data = False - self.writer_process.join() - else: - # we dont handle commands to turn on/off channels, gain signal and so on. such commands dont change package format - logging.info('got command "%s"' % res) - - -class GaleaWriter(threading.Thread): - - def __init__(self, port, delay, write): - threading.Thread.__init__(self) - self.port = port - self.write = write - self.delay = delay - self.package_size = 72 * 19 - self.package_num = 0 - self.need_data = True - - def run(self): - while self.need_data: - if self.package_num % 256 == 0: - self.package_num = 0 - - package = list() - package.append(0xA0) - for i in range(self.package_size): - package.append(random.randint(0, 255)) - package.append(0xC0) - logging.debug('package is %s' % ' '.join([str(x) for x in package])) - self.write(self.port, bytes(package)) - - self.package_num = self.package_num + 1 - time.sleep(self.delay) - - -def main(): - m_name, s_name = get_ports_windows() - test_serial(m_name, s_name) - - -if __name__ == '__main__': - logging.basicConfig(level=logging.INFO) - main() \ No newline at end of file diff --git a/emulator/brainflow_emulator/galea_serial_manual_v4.py b/emulator/brainflow_emulator/galea_serial_manual_v4.py deleted file mode 100644 index 7ece5cd6c..000000000 --- a/emulator/brainflow_emulator/galea_serial_manual_v4.py +++ /dev/null @@ -1,150 +0,0 @@ -import logging -import os -import random -import struct -import subprocess -import threading -import time - -import pkg_resources -from serial import Serial - - -def write(port, data): - return port.write(data) - - -def read(port, num_bytes): - return port.read(num_bytes) - - -def get_isntaller(): - return pkg_resources.resource_filename(__name__, os.path.join('com0com', 'setup_com0com_W7_x64_signed.exe')) - - -def install_com0com(): - this_directory = os.path.abspath(os.path.dirname(__file__)) - directory = os.path.join(this_directory, 'com0com') - if not os.path.exists(directory): - os.makedirs(directory) - cmds = [get_isntaller(), '/NCRC', '/S', '/D=%s' % directory] - logging.info('running %s' % ' '.join(cmds)) - p = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() - if p.returncode != 0: - logging.error('stdout is %s' % out) - logging.error('stderr is %s' % err) - raise Exception('com0com installation failure') - logging.info('Sleeping a few second, it doesnt work in appveyour without it') - time.sleep(10) - return directory - - -def get_ports_windows(): - directory = install_com0com() - # remove ports from previous run if any - p = subprocess.Popen([os.path.join(directory, 'setupc.exe'), 'remove', '0'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory) - stdout, stderr = p.communicate() - logging.info('remove stdout is %s' % stdout) - logging.info('remove stderr is %s' % stderr) - - m_name = 'COM16' - s_name = 'COM17' - - p = subprocess.Popen( - [os.path.join(directory, 'setupc.exe'), 'install', 'PortName=%s' % m_name, 'PortName=%s' % s_name], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory) - stdout, stderr = p.communicate() - logging.info('install stdout is %s' % stdout) - logging.info('install stderr is %s' % stderr) - - if p.returncode != 0: - raise Exception('com0com failure') - logging.info('Sleeping a few second, it doesnt work in appveyour without it') - time.sleep(10) - return m_name, s_name - - -def test_serial(m_name, s_name): - master = Serial('\\\\.\\%s' % m_name, timeout=0) - listen_thread = Listener(master, write, read) - listen_thread.start() - listen_thread.join() - master.close() - return stdout, stderr - - -class Listener(threading.Thread): - - def __init__(self, port, write, read): - # for windows write and read are methods from Serial object, for linux - os.read/write it doesnt work otherwise - threading.Thread.__init__(self) - self.port = port - self.writer_process = None - self.write = write - self.read = read - - def run(self): - start_time = time.time() - while True: - res = self.read(self.port, 9) - if len(res) < 1: - time.sleep(1) - continue - logging.info('read "%s"' % res) - if 'F444' in res.decode('utf-8'): - cur_time = time.time() - resp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) - self.write(self.port, resp) - logging.info('sent resp to calc time command') - elif 'b' in res.decode('utf-8'): - self.writer_process = GaleaWriter(self.port, 0.005, self.write) - self.writer_process.daemon = True - self.writer_process.start() - elif 's' in res.decode('utf-8'): - if self.writer_process is not None: - if self.writer_process.is_alive(): - self.writer_process.need_data = False - self.writer_process.join() - else: - # we dont handle commands to turn on/off channels, gain signal and so on. such commands dont change package format - logging.info('got command "%s"' % res) - - -class GaleaWriter(threading.Thread): - - def __init__(self, port, delay, write): - threading.Thread.__init__(self) - self.port = port - self.write = write - self.delay = delay - self.package_size = 114 * 12 - self.package_num = 0 - self.need_data = True - - def run(self): - while self.need_data: - if self.package_num % 256 == 0: - self.package_num = 0 - - package = list() - package.append(0xA0) - for i in range(self.package_size): - package.append(random.randint(0, 255)) - package.append(0xC0) - logging.debug('package is %s' % ' '.join([str(x) for x in package])) - self.write(self.port, bytes(package)) - - self.package_num = self.package_num + 1 - time.sleep(self.delay) - - -def main(): - m_name, s_name = get_ports_windows() - test_serial(m_name, s_name) - - -if __name__ == '__main__': - logging.basicConfig(level=logging.INFO) - main() \ No newline at end of file diff --git a/emulator/brainflow_emulator/galea_serial_windows.py b/emulator/brainflow_emulator/galea_serial_windows.py deleted file mode 100644 index 7beac0f94..000000000 --- a/emulator/brainflow_emulator/galea_serial_windows.py +++ /dev/null @@ -1,167 +0,0 @@ -import logging -import os -import random -import struct -import subprocess -import sys -import threading -import time - -import pkg_resources -from brainflow_emulator.emulate_common import TestFailureError, log_multilines -from serial import Serial - - -def write(port, data): - return port.write(data) - - -def read(port, num_bytes): - return port.read(num_bytes) - - -def get_isntaller(): - return pkg_resources.resource_filename(__name__, os.path.join('com0com', 'setup_com0com_W7_x64_signed.exe')) - - -def install_com0com(): - this_directory = os.path.abspath(os.path.dirname(__file__)) - directory = os.path.join(this_directory, 'com0com') - if not os.path.exists(directory): - os.makedirs(directory) - cmds = [get_isntaller(), '/NCRC', '/S', '/D=%s' % directory] - logging.info('running %s' % ' '.join(cmds)) - p = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() - if p.returncode != 0: - logging.error('stdout is %s' % out) - logging.error('stderr is %s' % err) - raise Exception('com0com installation failure') - logging.info('Sleeping a few second, it doesnt work in appveyour without it') - time.sleep(10) - return directory - - -def get_ports_windows(): - directory = install_com0com() - # remove ports from previous run if any - p = subprocess.Popen([os.path.join(directory, 'setupc.exe'), 'remove', '0'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory) - stdout, stderr = p.communicate() - logging.info('remove stdout is %s' % stdout) - logging.info('remove stderr is %s' % stderr) - - m_name = 'COM16' - s_name = 'COM17' - - p = subprocess.Popen( - [os.path.join(directory, 'setupc.exe'), 'install', 'PortName=%s' % m_name, 'PortName=%s' % s_name], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory) - stdout, stderr = p.communicate() - logging.info('install stdout is %s' % stdout) - logging.info('install stderr is %s' % stderr) - - if p.returncode != 0: - raise Exception('com0com failure') - logging.info('Sleeping a few second, it doesnt work in appveyour without it') - time.sleep(10) - return m_name, s_name - - -def test_serial(cmd_list, m_name, s_name): - master = Serial('\\\\.\\%s' % m_name, timeout=0) - listen_thread = Listener(master, write, read) - listen_thread.daemon = True - listen_thread.start() - - cmd_to_run = cmd_list + [s_name] - logging.info('Running %s' % ' '.join([str(x) for x in cmd_to_run])) - process = subprocess.Popen(cmd_to_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - log_multilines(logging.info, stdout) - log_multilines(logging.info, stderr) - - master.close() - if process.returncode != 0: - raise TestFailureError('Test failed with exit code %s' % str(process.returncode), process.returncode) - - return stdout, stderr - - -class Listener(threading.Thread): - - def __init__(self, port, write, read): - # for windows write and read are methods from Serial object, for linux - os.read/write it doesnt work otherwise - threading.Thread.__init__(self) - self.port = port - self.writer_process = None - self.write = write - self.read = read - - def run(self): - start_time = time.time() - while True: - res = self.read(self.port, 9) - if len(res) < 1: - time.sleep(1) - continue - logging.info('read "%s"' % res) - if 'F444' in res.decode('utf-8'): - cur_time = time.time() - resp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) - self.write(self.port, resp) - logging.info('sent resp to calc time command') - elif 'b' in res.decode('utf-8'): - self.writer_process = GaleaWriter(self.port, 0.005, self.write) - self.writer_process.daemon = True - self.writer_process.start() - elif 's' in res.decode('utf-8'): - if self.writer_process is not None: - if self.writer_process.is_alive(): - self.writer_process.need_data = False - self.writer_process.join() - else: - # we dont handle commands to turn on/off channels, gain signal and so on. such commands dont change package format - logging.info('got command "%s"' % res) - - -class GaleaWriter(threading.Thread): - - def __init__(self, port, delay, write): - threading.Thread.__init__(self) - self.port = port - self.write = write - self.delay = delay - self.package_size = 72 * 19 - self.package_num = 0 - self.need_data = True - - def run(self): - while self.need_data: - if self.package_num % 256 == 0: - self.package_num = 0 - - package = list() - package.append(0xA0) - for i in range(self.package_size): - package.append(random.randint(0, 255)) - package.append(0xC0) - logging.debug('package is %s' % ' '.join([str(x) for x in package])) - self.write(self.port, bytes(package)) - - self.package_num = self.package_num + 1 - time.sleep(self.delay) - - -def main(cmd_list): - if not cmd_list: - raise Exception('No command to execute') - - m_name, s_name = get_ports_windows() - test_serial(cmd_list, m_name, s_name) - - -if __name__ == '__main__': - logging.basicConfig(level=logging.INFO) - main(sys.argv[1:]) \ No newline at end of file diff --git a/emulator/brainflow_emulator/galea_serial_windows_v4.py b/emulator/brainflow_emulator/galea_serial_windows_v4.py deleted file mode 100644 index e333b5204..000000000 --- a/emulator/brainflow_emulator/galea_serial_windows_v4.py +++ /dev/null @@ -1,167 +0,0 @@ -import logging -import os -import random -import struct -import subprocess -import sys -import threading -import time - -import pkg_resources -from brainflow_emulator.emulate_common import TestFailureError, log_multilines -from serial import Serial - - -def write(port, data): - return port.write(data) - - -def read(port, num_bytes): - return port.read(num_bytes) - - -def get_isntaller(): - return pkg_resources.resource_filename(__name__, os.path.join('com0com', 'setup_com0com_W7_x64_signed.exe')) - - -def install_com0com(): - this_directory = os.path.abspath(os.path.dirname(__file__)) - directory = os.path.join(this_directory, 'com0com') - if not os.path.exists(directory): - os.makedirs(directory) - cmds = [get_isntaller(), '/NCRC', '/S', '/D=%s' % directory] - logging.info('running %s' % ' '.join(cmds)) - p = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() - if p.returncode != 0: - logging.error('stdout is %s' % out) - logging.error('stderr is %s' % err) - raise Exception('com0com installation failure') - logging.info('Sleeping a few second, it doesnt work in appveyour without it') - time.sleep(10) - return directory - - -def get_ports_windows(): - directory = install_com0com() - # remove ports from previous run if any - p = subprocess.Popen([os.path.join(directory, 'setupc.exe'), 'remove', '0'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory) - stdout, stderr = p.communicate() - logging.info('remove stdout is %s' % stdout) - logging.info('remove stderr is %s' % stderr) - - m_name = 'COM16' - s_name = 'COM17' - - p = subprocess.Popen( - [os.path.join(directory, 'setupc.exe'), 'install', 'PortName=%s' % m_name, 'PortName=%s' % s_name], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory) - stdout, stderr = p.communicate() - logging.info('install stdout is %s' % stdout) - logging.info('install stderr is %s' % stderr) - - if p.returncode != 0: - raise Exception('com0com failure') - logging.info('Sleeping a few second, it doesnt work in appveyour without it') - time.sleep(10) - return m_name, s_name - - -def test_serial(cmd_list, m_name, s_name): - master = Serial('\\\\.\\%s' % m_name, timeout=0) - listen_thread = Listener(master, write, read) - listen_thread.daemon = True - listen_thread.start() - - cmd_to_run = cmd_list + [s_name] - logging.info('Running %s' % ' '.join([str(x) for x in cmd_to_run])) - process = subprocess.Popen(cmd_to_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - log_multilines(logging.info, stdout) - log_multilines(logging.info, stderr) - - master.close() - if process.returncode != 0: - raise TestFailureError('Test failed with exit code %s' % str(process.returncode), process.returncode) - - return stdout, stderr - - -class Listener(threading.Thread): - - def __init__(self, port, write, read): - # for windows write and read are methods from Serial object, for linux - os.read/write it doesnt work otherwise - threading.Thread.__init__(self) - self.port = port - self.writer_process = None - self.write = write - self.read = read - - def run(self): - start_time = time.time() - while True: - res = self.read(self.port, 9) - if len(res) < 1: - time.sleep(1) - continue - logging.info('read "%s"' % res) - if 'F444' in res.decode('utf-8'): - cur_time = time.time() - resp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) - self.write(self.port, resp) - logging.info('sent resp to calc time command') - elif 'b' in res.decode('utf-8'): - self.writer_process = GaleaWriter(self.port, 0.005, self.write) - self.writer_process.daemon = True - self.writer_process.start() - elif 's' in res.decode('utf-8'): - if self.writer_process is not None: - if self.writer_process.is_alive(): - self.writer_process.need_data = False - self.writer_process.join() - else: - # we dont handle commands to turn on/off channels, gain signal and so on. such commands dont change package format - logging.info('got command "%s"' % res) - - -class GaleaWriter(threading.Thread): - - def __init__(self, port, delay, write): - threading.Thread.__init__(self) - self.port = port - self.write = write - self.delay = delay - self.package_size = 114 * 12 - self.package_num = 0 - self.need_data = True - - def run(self): - while self.need_data: - if self.package_num % 256 == 0: - self.package_num = 0 - - package = list() - package.append(0xA0) - for i in range(self.package_size): - package.append(random.randint(0, 255)) - package.append(0xC0) - logging.debug('package is %s' % ' '.join([str(x) for x in package])) - self.write(self.port, bytes(package)) - - self.package_num = self.package_num + 1 - time.sleep(self.delay) - - -def main(cmd_list): - if not cmd_list: - raise Exception('No command to execute') - - m_name, s_name = get_ports_windows() - test_serial(cmd_list, m_name, s_name) - - -if __name__ == '__main__': - logging.basicConfig(level=logging.INFO) - main(sys.argv[1:]) \ No newline at end of file diff --git a/emulator/brainflow_emulator/galea_udp.py b/emulator/brainflow_emulator/galea_udp.py index ce168665f..6b0c94fcc 100644 --- a/emulator/brainflow_emulator/galea_udp.py +++ b/emulator/brainflow_emulator/galea_udp.py @@ -10,6 +10,8 @@ from brainflow_emulator.emulate_common import TestFailureError, log_multilines +# This file is used for CI tests only, it emulates Galea board over UDP +# It has less functionality than galea_manual.py but it is enough for CI tests class State(enum.Enum): wait = 'wait' @@ -57,7 +59,7 @@ def __init__(self): self.state = State.wait.value self.addr = None self.package_num = 0 - self.package_size = 72 + self.package_size = 114 self.keep_alive = True def run(self): @@ -86,16 +88,18 @@ def run(self): if self.state == State.stream.value: package = list() - for _ in range(19): + for _ in range(12): package.append(self.package_num) self.package_num = self.package_num + 1 if self.package_num % 256 == 0: self.package_num = 0 - for i in range(1, self.package_size - 8): + for i in range(1, 88): package.append(random.randint(0, 255)) cur_time = time.time() timestamp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) package.extend(timestamp) + for i in range(96, self.package_size): + package.append(random.randint(0, 255)) try: self.server_socket.sendto(bytes(package), self.addr) except socket.timeout: diff --git a/emulator/brainflow_emulator/galea_udp_v4.py b/emulator/brainflow_emulator/galea_udp_v4.py deleted file mode 100644 index b6e580e68..000000000 --- a/emulator/brainflow_emulator/galea_udp_v4.py +++ /dev/null @@ -1,118 +0,0 @@ -import enum -import logging -import random -import socket -import struct -import subprocess -import sys -import threading -import time - -from brainflow_emulator.emulate_common import TestFailureError, log_multilines - - -class State(enum.Enum): - wait = 'wait' - stream = 'stream' - - -class Message(enum.Enum): - start_stream = b'b' - stop_stream = b's' - ack_values = (b'd', b'~6', b'~5', b'o', b'F0') - ack_from_device = b'A' - time_calc_command = b'F4444444' - - -def test_socket(cmd_list): - logging.info('Running %s' % ' '.join([str(x) for x in cmd_list])) - process = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - log_multilines(logging.info, stdout) - log_multilines(logging.info, stderr) - - if process.returncode != 0: - raise TestFailureError('Test failed with exit code %s' % str(process.returncode), process.returncode) - - return stdout, stderr - - -def run_socket_server(): - novaxr_thread = GaleaEmulator() - novaxr_thread.start() - return novaxr_thread - - -class GaleaEmulator(threading.Thread): - - def __init__(self): - threading.Thread.__init__(self) - self.local_ip = '127.0.0.1' - self.local_port = 2390 - self.server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) - self.server_socket.settimeout( - 0.1) # decreases sampling rate significantly because it will wait for recv 0.1 sec but it's just a test - self.server_socket.bind((self.local_ip, self.local_port)) - self.state = State.wait.value - self.addr = None - self.package_num = 0 - self.package_size = 114 - self.keep_alive = True - - def run(self): - start_time = time.time() - while self.keep_alive: - try: - msg, self.addr = self.server_socket.recvfrom(128) - if msg == Message.start_stream.value: - self.state = State.stream.value - elif msg == Message.stop_stream.value: - self.state = State.wait.value - elif msg in Message.ack_values.value: - self.server_socket.sendto(Message.ack_from_device.value, self.addr) - elif msg == Message.time_calc_command.value: - cur_time = time.time() - resp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) - self.server_socket.sendto(resp, self.addr) - else: - if msg: - # we dont handle board config characters because they dont change package format - logging.warn('received unexpected string %s', str(msg)) - except socket.timeout: - logging.debug('timeout for recv') - except Exception: - break - - if self.state == State.stream.value: - package = list() - for _ in range(12): - package.append(self.package_num) - self.package_num = self.package_num + 1 - if self.package_num % 256 == 0: - self.package_num = 0 - for i in range(1, 88): - package.append(random.randint(0, 255)) - cur_time = time.time() - timestamp = bytearray(struct.pack('d', (cur_time - start_time) * 1000)) - package.extend(timestamp) - for i in range(96, self.package_size): - package.append(random.randint(0, 255)) - try: - self.server_socket.sendto(bytes(package), self.addr) - except socket.timeout: - logging.info('timeout for send') - - -def main(cmd_list): - if not cmd_list: - raise Exception('No command to execute') - server_thread = run_socket_server() - test_socket(cmd_list) - server_thread.keep_alive = False - server_thread.join() - - -if __name__ == '__main__': - logging.basicConfig(format='%(asctime)s %(levelname)-8s %(message)s', level=logging.INFO) - main(sys.argv[1:]) \ No newline at end of file diff --git a/java_package/brainflow/src/main/java/brainflow/BoardIds.java b/java_package/brainflow/src/main/java/brainflow/BoardIds.java index 487d8fc5a..ce3ac9d0e 100644 --- a/java_package/brainflow/src/main/java/brainflow/BoardIds.java +++ b/java_package/brainflow/src/main/java/brainflow/BoardIds.java @@ -30,7 +30,6 @@ public enum BoardIds FREEEEG32_BOARD (17), BRAINBIT_BLED_BOARD (18), GFORCE_DUAL_BOARD (19), - GALEA_SERIAL_BOARD (20), MUSE_S_BLED_BOARD (21), MUSE_2_BLED_BOARD (22), CROWN_BOARD (23), @@ -57,8 +56,6 @@ public enum BoardIds EXPLORE_8_CHAN_BOARD (45), GANGLION_NATIVE_BOARD (46), EMOTIBIT_BOARD (47), - GALEA_BOARD_V4 (48), - GALEA_SERIAL_BOARD_V4 (49), NTL_WIFI_BOARD (50), ANT_NEURO_EE_511_BOARD (51), FREEEEG128_BOARD (52), diff --git a/julia_package/brainflow/src/board_shim.jl b/julia_package/brainflow/src/board_shim.jl index 3e7086093..dc80214d7 100644 --- a/julia_package/brainflow/src/board_shim.jl +++ b/julia_package/brainflow/src/board_shim.jl @@ -25,7 +25,6 @@ export BrainFlowInputParams FREEEEG32_BOARD = 17 BRAINBIT_BLED_BOARD = 18 GFORCE_DUAL_BOARD = 19 - GALEA_SERIAL_BOARD = 20 MUSE_S_BLED_BOARD = 21 MUSE_2_BLED_BOARD = 22 CROWN_BOARD = 23 @@ -52,8 +51,6 @@ export BrainFlowInputParams EXPLORE_8_CHAN_BOARD = 45 GANGLION_NATIVE_BOARD = 46 EMOTIBIT_BOARD = 47 - GALEA_BOARD_V4 = 48 - GALEA_SERIAL_BOARD_V4 = 49 NTL_WIFI_BOARD = 50 ANT_NEURO_EE_511_BOARD = 51 FREEEEG128_BOARD = 52 diff --git a/matlab_package/brainflow/BoardIds.m b/matlab_package/brainflow/BoardIds.m index cc242952d..adf5bf325 100644 --- a/matlab_package/brainflow/BoardIds.m +++ b/matlab_package/brainflow/BoardIds.m @@ -23,7 +23,6 @@ FREEEEG32_BOARD(17) BRAINBIT_BLED_BOARD(18) GFORCE_DUAL_BOARD(19) - GALEA_SERIAL_BOARD(20) MUSE_S_BLED_BOARD(21) MUSE_2_BLED_BOARD(22) CROWN_BOARD(23) @@ -50,8 +49,6 @@ EXPLORE_8_CHAN_BOARD(45) GANGLION_NATIVE_BOARD(46) EMOTIBIT_BOARD(47) - GALEA_BOARD_V4(48) - GALEA_SERIAL_BOARD_V4(49) NTL_WIFI_BOARD(50) ANT_NEURO_EE_511_BOARD(51) FREEEEG128_BOARD(52) diff --git a/nodejs_package/brainflow/brainflow.types.ts b/nodejs_package/brainflow/brainflow.types.ts index f55f10187..2c8ba5e88 100644 --- a/nodejs_package/brainflow/brainflow.types.ts +++ b/nodejs_package/brainflow/brainflow.types.ts @@ -35,7 +35,6 @@ export enum BoardIds { FREEEEG32_BOARD = 17, BRAINBIT_BLED_BOARD = 18, GFORCE_DUAL_BOARD = 19, - GALEA_SERIAL_BOARD = 20, MUSE_S_BLED_BOARD = 21, MUSE_2_BLED_BOARD = 22, CROWN_BOARD = 23, @@ -62,8 +61,6 @@ export enum BoardIds { EXPLORE_8_CHAN_BOARD = 45, GANGLION_NATIVE_BOARD = 46, EMOTIBIT_BOARD = 47, - GALEA_BOARD_V4 = 48, - GALEA_SERIAL_BOARD_V4 = 49, NTL_WIFI_BOARD = 50, ANT_NEURO_EE_511_BOARD = 51, EXPLORE_PLUS_8_CHAN_BOARD = 54, diff --git a/python_package/brainflow/board_shim.py b/python_package/brainflow/board_shim.py index 0f98d001f..6a73f7b37 100644 --- a/python_package/brainflow/board_shim.py +++ b/python_package/brainflow/board_shim.py @@ -38,7 +38,6 @@ class BoardIds(enum.IntEnum): FREEEEG32_BOARD = 17 #: BRAINBIT_BLED_BOARD = 18 #: GFORCE_DUAL_BOARD = 19 #: - GALEA_SERIAL_BOARD = 20 #: MUSE_S_BLED_BOARD = 21 #: MUSE_2_BLED_BOARD = 22 #: CROWN_BOARD = 23 #: @@ -65,8 +64,6 @@ class BoardIds(enum.IntEnum): EXPLORE_8_CHAN_BOARD = 45 #: GANGLION_NATIVE_BOARD = 46 #: EMOTIBIT_BOARD = 47 #: - GALEA_BOARD_V4 = 48 #: - GALEA_SERIAL_BOARD_V4 = 49 #: NTL_WIFI_BOARD = 50 #: ANT_NEURO_EE_511_BOARD = 51 #: FREEEEG128_BOARD = 52 #: diff --git a/src/board_controller/board_controller.cpp b/src/board_controller/board_controller.cpp index 3cab800f5..60f8e72f4 100644 --- a/src/board_controller/board_controller.cpp +++ b/src/board_controller/board_controller.cpp @@ -38,9 +38,6 @@ #include "explore.h" #include "freeeeg.h" #include "galea.h" -#include "galea_serial.h" -#include "galea_serial_v4.h" -#include "galea_v4.h" #include "ganglion.h" #include "ganglion_native.h" #include "ganglion_wifi.h" @@ -167,9 +164,6 @@ int prepare_session (int board_id, const char *json_brainflow_input_params) case BoardIds::GFORCE_DUAL_BOARD: board = std::shared_ptr (new GforceDual (params)); break; - case BoardIds::GALEA_SERIAL_BOARD: - board = std::shared_ptr (new GaleaSerial (params)); - break; case BoardIds::MUSE_S_BLED_BOARD: board = std::shared_ptr (new MuseBLED (board_id, params)); break; @@ -258,12 +252,6 @@ int prepare_session (int board_id, const char *json_brainflow_input_params) case BoardIds::EMOTIBIT_BOARD: board = std::shared_ptr (new Emotibit (params)); break; - case BoardIds::GALEA_BOARD_V4: - board = std::shared_ptr (new GaleaV4 (params)); - break; - case BoardIds::GALEA_SERIAL_BOARD_V4: - board = std::shared_ptr (new GaleaSerialV4 (params)); - break; case BoardIds::ANT_NEURO_EE_511_BOARD: board = std::shared_ptr ( new AntNeuroBoard ((int)BoardIds::ANT_NEURO_EE_511_BOARD, params)); diff --git a/src/board_controller/brainflow_boards.cpp b/src/board_controller/brainflow_boards.cpp index 37bb244cc..66a0e59be 100644 --- a/src/board_controller/brainflow_boards.cpp +++ b/src/board_controller/brainflow_boards.cpp @@ -185,27 +185,30 @@ BrainFlowBoards::BrainFlowBoards() {"name", "Galea"}, {"sampling_rate", 250}, {"package_num_channel", 0}, - {"timestamp_channel", 19}, - {"marker_channel", 20}, - {"num_rows", 21}, - {"eeg_channels", {7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}, - {"eeg_names", "FP1,FP2,Fz,Cz,Pz,Oz,P3,P4,O1,O2"}, - {"emg_channels", {1, 2, 3, 4}}, + {"timestamp_channel", 27}, + {"marker_channel", 28}, + {"num_rows", 29}, + {"eeg_channels", {9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}}, + {"eeg_names", "F1,C3,F2,Cz,C4,Pz,P4,O2,P3,O1,X1,X2,X3,X4,X5,X6"}, + {"emg_channels", {1, 2, 3, 4, 7, 8}}, {"eog_channels", {5, 6}}, - {"other_channels", {17, 18}}, + {"other_channels", {25, 26}}, }; brainflow_boards_json["boards"]["3"]["auxiliary"] = { {"name", "GaleaAuxiliary"}, {"sampling_rate", 50}, {"package_num_channel", 0}, - {"timestamp_channel", 8}, - {"marker_channel", 9}, - {"num_rows", 10}, + {"timestamp_channel", 17}, + {"marker_channel", 18}, + {"num_rows", 19}, {"battery_channel", 5}, {"eda_channels", {1}}, {"ppg_channels", {2, 3}}, - {"other_channels", {6, 7}}, + {"accel_channels", {6, 7, 8}}, + {"gyro_channels", {9, 10, 11}}, + {"magnetometer_channels", {12, 13, 14}}, + {"other_channels", {15, 16}}, {"temperature_channels", {4}} }; brainflow_boards_json["boards"]["4"]["default"] = @@ -383,34 +386,6 @@ BrainFlowBoards::BrainFlowBoards() {"num_rows", 5}, {"emg_channels", {1, 2}} }; - brainflow_boards_json["boards"]["20"]["default"] = - { - {"name", "GaleaSerial"}, - {"sampling_rate", 250}, - {"package_num_channel", 0}, - {"timestamp_channel", 19}, - {"marker_channel", 20}, - {"num_rows", 21}, - {"eeg_channels", {7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}, - {"eeg_names", "FP1,FP2,Fz,Cz,Pz,Oz,P3,P4,O1,O2"}, - {"emg_channels", {1, 2, 3, 4}}, - {"eog_channels", {5, 6}}, - {"other_channels", {17, 18}}, - }; - brainflow_boards_json["boards"]["20"]["auxiliary"] = - { - {"name", "GaleaSerialAuxiliary"}, - {"sampling_rate", 50}, - {"package_num_channel", 0}, - {"timestamp_channel", 8}, - {"marker_channel", 9}, - {"num_rows", 10}, - {"battery_channel", 5}, - {"eda_channels", {1}}, - {"ppg_channels", {2, 3}}, - {"other_channels", {6, 7}}, - {"temperature_channels", {4}} - }; brainflow_boards_json["boards"]["21"]["default"] = { {"name", "MuseSBLED"}, @@ -919,68 +894,6 @@ BrainFlowBoards::BrainFlowBoards() {"temperature_channels", {2}}, {"other_channels", {3}} }; - brainflow_boards_json["boards"]["48"]["default"] = - { - {"name", "GaleaV4"}, - {"sampling_rate", 250}, - {"package_num_channel", 0}, - {"timestamp_channel", 27}, - {"marker_channel", 28}, - {"num_rows", 29}, - {"eeg_channels", {9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}}, - {"eeg_names", "F1,C3,F2,Cz,C4,Pz,P4,O2,P3,O1,X1,X2,X3,X4,X5,X6"}, - {"emg_channels", {1, 2, 3, 4, 7, 8}}, - {"eog_channels", {5, 6}}, - {"other_channels", {25, 26}}, - }; - brainflow_boards_json["boards"]["48"]["auxiliary"] = - { - {"name", "GaleaV4Auxiliary"}, - {"sampling_rate", 50}, - {"package_num_channel", 0}, - {"timestamp_channel", 17}, - {"marker_channel", 18}, - {"num_rows", 19}, - {"battery_channel", 5}, - {"eda_channels", {1}}, - {"ppg_channels", {2, 3}}, - {"accel_channels", {6, 7, 8}}, - {"gyro_channels", {9, 10, 11}}, - {"magnetometer_channels", {12, 13, 14}}, - {"other_channels", {15, 16}}, - {"temperature_channels", {4}} - }; - brainflow_boards_json["boards"]["49"]["default"] = - { - {"name", "GaleaSerial"}, - {"sampling_rate", 250}, - {"package_num_channel", 0}, - {"timestamp_channel", 27}, - {"marker_channel", 28}, - {"num_rows", 29}, - {"eeg_channels", {9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}}, - {"eeg_names", "F1,C3,F2,Cz,C4,Pz,P4,O2,P3,O1,X1,X2,X3,X4,X5,X6"}, - {"emg_channels", {1, 2, 3, 4, 7, 8}}, - {"eog_channels", {5, 6}}, - {"other_channels", {25, 26}}, - }; - brainflow_boards_json["boards"]["49"]["auxiliary"] = - { - {"name", "GaleaSerialAuxiliary"}, - {"sampling_rate", 50}, - {"package_num_channel", 0}, - {"timestamp_channel", 17}, - {"marker_channel", 18}, - {"num_rows", 19}, - {"battery_channel", 5}, - {"eda_channels", {1}}, - {"ppg_channels", {2, 3}}, - {"accel_channels", {6, 7, 8}}, - {"gyro_channels", {9, 10, 11}}, - {"magnetometer_channels", {12, 13, 14}}, - {"other_channels", {15, 16}}, - {"temperature_channels", {4}} - }; brainflow_boards_json["boards"]["50"]["default"] = { {"name", "NtlWifi"}, diff --git a/src/board_controller/build.cmake b/src/board_controller/build.cmake index 5239cb2d6..a6d4f15f5 100644 --- a/src/board_controller/build.cmake +++ b/src/board_controller/build.cmake @@ -37,10 +37,7 @@ SET (BOARD_CONTROLLER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/multicast_server.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/broadcast_client.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/broadcast_server.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/openbci/galea_v4.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/openbci/galea_serial_v4.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/openbci/galea.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/openbci/galea_serial.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/openbci/openbci_serial_board.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/openbci/openbci_wifi_shield_board.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/openbci/ganglion_wifi.cpp diff --git a/src/board_controller/openbci/galea.cpp b/src/board_controller/openbci/galea.cpp index 7e2a40419..edbe9001c 100644 --- a/src/board_controller/openbci/galea.cpp +++ b/src/board_controller/openbci/galea.cpp @@ -23,7 +23,8 @@ constexpr int Galea::max_num_packages; constexpr int Galea::max_transaction_size; constexpr int Galea::socket_timeout; -Galea::Galea (struct BrainFlowInputParams params) : Board ((int)BoardIds::GALEA_BOARD, params) +Galea::Galea (struct BrainFlowInputParams params) + : Board ((int)BoardIds::GALEA_BOARD, params) { socket = NULL; is_streaming = false; @@ -73,7 +74,7 @@ int Galea::prepare_session () socket->set_timeout (socket_timeout); // force default settings for device std::string tmp; - std::string default_settings = "o"; // use demo mode with agnd + std::string default_settings = "d"; // use default mode res = config_board (default_settings, tmp); if (res != (int)BrainFlowExitCodes::STATUS_OK) { @@ -87,7 +88,7 @@ int Galea::prepare_session () res = config_board (sampl_rate, tmp); if (res != (int)BrainFlowExitCodes::STATUS_OK) { - safe_logger (spdlog::level::err, "failed to apply defaul sampling rate"); + safe_logger (spdlog::level::err, "failed to apply default sampling rate"); delete socket; socket = NULL; return (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; @@ -115,6 +116,23 @@ int Galea::config_board (std::string conf, std::string &response) return res; } + if (conf == "get_gains") + { + std::stringstream gains; + + for (int i = 0; i < 20; i++) + { + gains << gain_tracker.get_gain_for_channel (i); + if (i < 19) + { + gains << ", "; + } + } + response = gains.str (); + safe_logger (spdlog::level::info, "gains for all channels: {}", response); + return (int)BrainFlowExitCodes::STATUS_OK; + } + if (gain_tracker.apply_config (conf) == (int)OpenBCICommandTypes::INVALID_COMMAND) { safe_logger (spdlog::level::warn, "invalid command: {}", conf.c_str ()); @@ -390,10 +408,12 @@ void Galea::read_thread () // calc delta between PC timestamp and device timestamp in last 10 packages, // use this delta later on to assign timestamps double pc_timestamp = get_timestamp (); - double timestamp_last_package = 0.0; - memcpy (×tamp_last_package, b + 64 + offset_last_package, 8); - timestamp_last_package /= 1000; // from ms to seconds - double time_delta = pc_timestamp - timestamp_last_package; + unsigned long long timestamp_last_package = 0.0; + memcpy (×tamp_last_package, b + 88 + offset_last_package, + sizeof (unsigned long long)); // microseconds + double timestamp_last_package_converted = + static_cast (timestamp_last_package) / 1000000.0; // convert to seconds + double time_delta = pc_timestamp - timestamp_last_package_converted; time_buffer.add_data (&time_delta); int num_time_deltas = (int)time_buffer.get_current_data (10, latest_times); time_delta = 0.0; @@ -418,41 +438,50 @@ void Galea::read_thread () for (int cur_package = 0; cur_package < num_packages; cur_package++) { - int offset = cur_package * package_size; + int offset = cur_package * Galea::package_size; // exg (default preset) exg_package[board_descr["default"]["package_num_channel"].get ()] = (double)b[0 + offset]; - for (int i = 4, tmp_counter = 0; i < 20; i++, tmp_counter++) + for (int i = 4, tmp_counter = 0; i < 28; i++, tmp_counter++) { double exg_scale = (double)(4.5 / float ((pow (2, 23) - 1)) / gain_tracker.get_gain_for_channel (tmp_counter) * 1000000.); exg_package[i - 3] = exg_scale * (double)cast_24bit_to_int32 (b + offset + 5 + 3 * (i - 4)); } - double timestamp_device = 0.0; - memcpy (×tamp_device, b + 64 + offset, 8); - timestamp_device /= 1000; // from ms to seconds + unsigned long long timestamp_device = 0.0; + memcpy (×tamp_device, b + 88 + offset, + sizeof (unsigned long long)); // reports microseconds + + double timestamp_device_converted = static_cast (timestamp_device); + timestamp_device_converted /= 1000000.0; // convert to seconds exg_package[board_descr["default"]["timestamp_channel"].get ()] = - timestamp_device + time_delta - half_rtt; + timestamp_device_converted + time_delta - half_rtt; exg_package[board_descr["default"]["other_channels"][0].get ()] = pc_timestamp; exg_package[board_descr["default"]["other_channels"][1].get ()] = - timestamp_device; + timestamp_device_converted; push_package (exg_package); // aux, 5 times smaller sampling rate if (((int)b[0 + offset]) % 5 == 0) { + double accel_scale = (double)(8.0 / static_cast (pow (2, 16) - 1)); + double gyro_scale = (double)(1000.0 / static_cast (pow (2, 16) - 1)); + double magnetometer_scale_xy = + (double)(2.6 / static_cast (pow (2, 13) - 1)); + double magnetometer_scale_z = + (double)(5.0 / static_cast (pow (2, 15) - 1)); aux_package[board_descr["auxiliary"]["package_num_channel"].get ()] = (double)b[0 + offset]; uint16_t temperature = 0; int32_t ppg_ir = 0; int32_t ppg_red = 0; float eda; - memcpy (&temperature, b + 54 + offset, 2); + memcpy (&temperature, b + 78 + offset, 2); memcpy (&eda, b + 1 + offset, 4); - memcpy (&ppg_red, b + 56 + offset, 4); - memcpy (&ppg_ir, b + 60 + offset, 4); + memcpy (&ppg_red, b + 80 + offset, 4); + memcpy (&ppg_ir, b + 84 + offset, 4); // ppg aux_package[board_descr["auxiliary"]["ppg_channels"][0].get ()] = (double)ppg_red; @@ -466,13 +495,37 @@ void Galea::read_thread () temperature / 100.0; // battery aux_package[board_descr["auxiliary"]["battery_channel"].get ()] = - (double)b[53 + offset]; + (double)b[77 + offset]; aux_package[board_descr["auxiliary"]["timestamp_channel"].get ()] = - timestamp_device + time_delta - half_rtt; + timestamp_device_converted + time_delta - half_rtt; aux_package[board_descr["auxiliary"]["other_channels"][0].get ()] = pc_timestamp; aux_package[board_descr["auxiliary"]["other_channels"][1].get ()] = - timestamp_device; + timestamp_device_converted; + // accel + aux_package[board_descr["auxiliary"]["accel_channels"][0].get ()] = + accel_scale * (double)cast_16bit_to_int32_swap_order (b + 96 + offset); + aux_package[board_descr["auxiliary"]["accel_channels"][1].get ()] = + accel_scale * (double)cast_16bit_to_int32_swap_order (b + 98 + offset); + aux_package[board_descr["auxiliary"]["accel_channels"][2].get ()] = + accel_scale * (double)cast_16bit_to_int32_swap_order (b + 100 + offset); + // gyro + aux_package[board_descr["auxiliary"]["gyro_channels"][0].get ()] = + gyro_scale * (double)cast_16bit_to_int32_swap_order (b + 102 + offset); + aux_package[board_descr["auxiliary"]["gyro_channels"][1].get ()] = + gyro_scale * (double)cast_16bit_to_int32_swap_order (b + 104 + offset); + aux_package[board_descr["auxiliary"]["gyro_channels"][2].get ()] = + gyro_scale * (double)cast_16bit_to_int32_swap_order (b + 106 + offset); + // magnetometer + aux_package[board_descr["auxiliary"]["magnetometer_channels"][0].get ()] = + magnetometer_scale_xy * + (double)cast_13bit_to_int32_swap_order (b + 108 + offset); + aux_package[board_descr["auxiliary"]["magnetometer_channels"][1].get ()] = + magnetometer_scale_xy * + (double)cast_13bit_to_int32_swap_order (b + 110 + offset); + aux_package[board_descr["auxiliary"]["magnetometer_channels"][2].get ()] = + magnetometer_scale_z * + (double)cast_15bit_to_int32_swap_order (b + 112 + offset); push_package (aux_package, (int)BrainFlowPresets::AUXILIARY_PRESET); } diff --git a/src/board_controller/openbci/galea_serial.cpp b/src/board_controller/openbci/galea_serial.cpp deleted file mode 100644 index c58306a77..000000000 --- a/src/board_controller/openbci/galea_serial.cpp +++ /dev/null @@ -1,488 +0,0 @@ -#include -#include -#include -#include -#include - -#include "custom_cast.h" -#include "galea_serial.h" -#include "timestamp.h" - -#include "json.hpp" - -using json = nlohmann::json; - -#ifndef _WIN32 -#include -#endif - -#define START_BYTE 0xA0 -#define END_BYTE 0xC0 - - -GaleaSerial::GaleaSerial (struct BrainFlowInputParams params) - : Board ((int)BoardIds::GALEA_SERIAL_BOARD, params) -{ - serial = NULL; - is_streaming = false; - keep_alive = false; - initialized = false; - state = (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; - half_rtt = 0.0; -} - -GaleaSerial::~GaleaSerial () -{ - skip_logs = true; - release_session (); -} - -int GaleaSerial::prepare_session () -{ - // check params - if (initialized) - { - safe_logger (spdlog::level::info, "Session is already prepared"); - return (int)BrainFlowExitCodes::STATUS_OK; - } - if (params.serial_port.empty ()) - { - safe_logger (spdlog::level::err, "serial port is not specified."); - return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; - } - if ((params.timeout > 600) || (params.timeout < 1)) - { - params.timeout = 3; - } - - // create serial object - serial = Serial::create (params.serial_port.c_str (), this); - int res = serial->open_serial_port (); - if (res < 0) - { - safe_logger (spdlog::level::err, - "Make sure you provided correct port name and have permissions to open it(run with " - "sudo/admin). Also, close all other apps using this port."); - return (int)BrainFlowExitCodes::UNABLE_TO_OPEN_PORT_ERROR; - } - res = serial->set_serial_port_settings (params.timeout * 1000, false); - if (res < 0) - { - safe_logger (spdlog::level::err, "Unable to set port settings, res is {}", res); - delete serial; - serial = NULL; - return (int)BrainFlowExitCodes::SET_PORT_ERROR; - } - res = serial->set_custom_baudrate (921600); - if (res < 0) - { - safe_logger (spdlog::level::err, "Unable to set custom baud rate, res is {}", res); - delete serial; - serial = NULL; - return (int)BrainFlowExitCodes::SET_PORT_ERROR; - } - safe_logger (spdlog::level::trace, "set port settings"); - - // set initial settings - std::string tmp; - std::string default_settings = "o"; // use demo mode with agnd - res = config_board (default_settings, tmp); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - safe_logger (spdlog::level::err, "failed to apply default settings"); - delete serial; - serial = NULL; - return (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; - } - std::string sampl_rate = "~6"; - res = config_board (sampl_rate, tmp); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - safe_logger (spdlog::level::err, "failed to apply defaul sampling rate"); - delete serial; - serial = NULL; - return (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; - } - initialized = true; - - return (int)BrainFlowExitCodes::STATUS_OK; -} - -int GaleaSerial::config_board (std::string conf, std::string &response) -{ - if (serial == NULL) - { - safe_logger (spdlog::level::err, "You need to call prepare_session before config_board"); - return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; - } - // special handling for calc_time command - if (conf == "calc_time") - { - if (is_streaming) - { - safe_logger (spdlog::level::err, "can not calc delay during the streaming."); - return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; - } - int res = calc_time (response); - return res; - } - - if (gain_tracker.apply_config (conf) == (int)OpenBCICommandTypes::INVALID_COMMAND) - { - safe_logger (spdlog::level::warn, "invalid command: {}", conf.c_str ()); - return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; - } - - std::string new_conf = conf + "\n"; - int len = (int)new_conf.size (); - safe_logger (spdlog::level::debug, "Trying to config GaleaSerial with {}", new_conf.c_str ()); - int res = serial->send_to_serial_port (new_conf.c_str (), len); - if (len != res) - { - safe_logger (spdlog::level::err, "Failed to config a board"); - gain_tracker.revert_config (); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - - if (is_streaming) - { - safe_logger (spdlog::level::warn, - "reconfiguring device during the streaming may lead to inconsistent data, it's " - "recommended to call stop_stream before config_board"); - } - - return (int)BrainFlowExitCodes::STATUS_OK; -} - -int GaleaSerial::start_stream (int buffer_size, const char *streamer_params) -{ - if (!initialized) - { - safe_logger (spdlog::level::err, "You need to call prepare_session before config_board"); - return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; - } - if (is_streaming) - { - safe_logger (spdlog::level::err, "Streaming thread already running"); - return (int)BrainFlowExitCodes::STREAM_ALREADY_RUN_ERROR; - } - - // calc time before start stream - std::string resp; - for (int i = 0; i < 3; i++) - { - int res = calc_time (resp); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - return res; - } - } - - int res = prepare_for_acquisition (buffer_size, streamer_params); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - return res; - } - - // start streaming - res = serial->send_to_serial_port ("b\n", 2); - if (res != 2) - { - safe_logger (spdlog::level::err, "Failed to send a command to board"); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - - keep_alive = true; - streaming_thread = std::thread ([this] { this->read_thread (); }); - // wait for data to ensure that everything is okay - std::unique_lock lk (this->m); - auto sec = std::chrono::seconds (1); - if (cv.wait_for (lk, 3 * sec, - [this] { return this->state != (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; })) - { - this->is_streaming = true; - return this->state; - } - else - { - safe_logger (spdlog::level::err, "no data received in 5sec, stopping thread"); - this->is_streaming = true; - this->stop_stream (); - return (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; - } -} - -int GaleaSerial::stop_stream () -{ - if (is_streaming) - { - keep_alive = false; - is_streaming = false; - streaming_thread.join (); - this->state = (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; - int res = serial->send_to_serial_port ("s\n", 2); - if (res != 2) - { - safe_logger (spdlog::level::err, "Failed to send a command to board"); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - - // free kernel buffer - unsigned char b; - res = 1; - int max_attempt = 400000; // to dont get to infinite loop - int current_attempt = 0; - while (res == 1) - { - res = serial->read_from_serial_port (&b, 1); - current_attempt++; - if (current_attempt == max_attempt) - { - safe_logger ( - spdlog::level::err, "Command 's' was sent but streaming is still running."); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - } - - std::string resp; - for (int i = 0; i < 3; i++) - { - res = calc_time (resp); // call it in the end once to print time in the end - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - break; // dont send exit code - } - } - return (int)BrainFlowExitCodes::STATUS_OK; - } - else - { - return (int)BrainFlowExitCodes::STREAM_THREAD_IS_NOT_RUNNING; - } -} - -int GaleaSerial::release_session () -{ - if (initialized) - { - if (is_streaming) - { - stop_stream (); - } - free_packages (); - initialized = false; - if (serial) - { - delete serial; - serial = NULL; - } - } - return (int)BrainFlowExitCodes::STATUS_OK; -} - -void GaleaSerial::read_thread () -{ - int res = 0; - constexpr int package_size = 72; - constexpr int max_num_packages = 25; - constexpr int max_transaction_size = package_size * max_num_packages + 2; - unsigned char b[max_transaction_size]; - DataBuffer time_buffer (1, 11); - double latest_times[10]; - for (int i = 0; i < max_transaction_size; i++) - { - b[i] = 0; - } - int num_exg_rows = board_descr["default"]["num_rows"]; - int num_aux_rows = board_descr["auxiliary"]["num_rows"]; - double *exg_package = new double[num_exg_rows]; - double *aux_package = new double[num_aux_rows]; - for (int i = 0; i < num_exg_rows; i++) - { - exg_package[i] = 0.0; - } - for (int i = 0; i < num_aux_rows; i++) - { - aux_package[i] = 0.0; - } - - while (keep_alive) - { - // read and check first byte - int res = serial->read_from_serial_port (b, 1); - if (res != 1) - { - safe_logger (spdlog::level::debug, "unable to read 1 byte"); - continue; - } - if (b[0] != START_BYTE) - { - continue; - } - double pc_timestamp = get_timestamp (); - // read and check reamining bytes - int pos = 1; - int num_packages = 0; - while (keep_alive) - { - if (pos >= 2 + package_size) - { - if ((b[0] == START_BYTE) && (b[pos] == END_BYTE) && ((pos - 2) % package_size == 0)) - { - num_packages = (pos - 2) / package_size; - break; - } - } - if (pos > max_transaction_size - 1) - { - num_packages = 0; - break; - } - res = serial->read_from_serial_port (b + pos, 1); - if (res > 0) - { - pos += res; - } - } - if (!keep_alive) - { - break; - } - if (num_packages < 1) - { - if (pos >= 2) - safe_logger (spdlog::level::warn, - "Failed to parse some data, b[0]: {}, b[pos]: {}, size: {}", (int)b[0], - (int)b[pos], (int)pos); - continue; - } - else - { - if (this->state != (int)BrainFlowExitCodes::STATUS_OK) - { - safe_logger (spdlog::level::info, "received first package streaming is started"); - { - std::lock_guard lk (this->m); - this->state = (int)BrainFlowExitCodes::STATUS_OK; - } - this->cv.notify_one (); - safe_logger (spdlog::level::debug, "start streaming"); - } - } - - // calc delta between PC timestamp and device timestamp in last 10 packages, - // use this delta later on to assign timestamps - unsigned char *package_bytes = b + 1; - int offset_last_package = package_size * (num_packages - 1); - double timestamp_last_package = 0.0; - memcpy (×tamp_last_package, package_bytes + 64 + offset_last_package, 8); - timestamp_last_package /= 1000; // from ms to seconds - double time_delta = pc_timestamp - timestamp_last_package; - time_buffer.add_data (&time_delta); - int num_time_deltas = (int)time_buffer.get_current_data (10, latest_times); - time_delta = 0.0; - for (int i = 0; i < num_time_deltas; i++) - { - time_delta += latest_times[i]; - } - time_delta /= num_time_deltas; - - for (int cur_package = 0; cur_package < num_packages; cur_package++) - { - int offset = cur_package * package_size; - // exg(default preset) - exg_package[board_descr["default"]["package_num_channel"].get ()] = - (double)package_bytes[0 + offset]; - for (int i = 4, tmp_counter = 0; i < 20; i++, tmp_counter++) - { - double exg_scale = (double)(4.5 / float ((pow (2, 23) - 1)) / - gain_tracker.get_gain_for_channel (tmp_counter) * 1000000.); - exg_package[i - 3] = exg_scale * - (double)cast_24bit_to_int32 (package_bytes + offset + 5 + 3 * (i - 4)); - } - double timestamp_device = 0.0; - memcpy (×tamp_device, package_bytes + 64 + offset, 8); - timestamp_device /= 1000; // from ms to seconds - exg_package[board_descr["default"]["timestamp_channel"].get ()] = - timestamp_device + time_delta - half_rtt; - exg_package[board_descr["default"]["other_channels"][0].get ()] = pc_timestamp; - exg_package[board_descr["default"]["other_channels"][1].get ()] = timestamp_device; - - push_package (exg_package); - - // aux, 5 times slower - if (((int)package_bytes[0 + offset]) % 5 == 0) - { - aux_package[board_descr["auxiliary"]["package_num_channel"].get ()] = - (double)package_bytes[0 + offset]; - uint16_t temperature = 0; - int32_t ppg_ir = 0; - int32_t ppg_red = 0; - float eda; - memcpy (&temperature, package_bytes + 54 + offset, 2); - memcpy (&eda, package_bytes + 1 + offset, 4); - memcpy (&ppg_red, package_bytes + 56 + offset, 4); - memcpy (&ppg_ir, package_bytes + 60 + offset, 4); - // ppg - aux_package[board_descr["auxiliary"]["ppg_channels"][0].get ()] = - (double)ppg_red; - aux_package[board_descr["auxiliary"]["ppg_channels"][1].get ()] = - (double)ppg_ir; - // eda - aux_package[board_descr["auxiliary"]["eda_channels"][0].get ()] = (double)eda; - // temperature - aux_package[board_descr["auxiliary"]["temperature_channels"][0].get ()] = - temperature / 100.0; - // battery - aux_package[board_descr["auxiliary"]["battery_channel"].get ()] = - (double)package_bytes[53 + offset]; - aux_package[board_descr["auxiliary"]["timestamp_channel"].get ()] = - timestamp_device + time_delta - half_rtt; - aux_package[board_descr["auxiliary"]["other_channels"][0].get ()] = - pc_timestamp; - aux_package[board_descr["auxiliary"]["other_channels"][1].get ()] = - timestamp_device; - push_package (aux_package, (int)BrainFlowPresets::AUXILIARY_PRESET); - } - } - } - delete[] exg_package; - delete[] aux_package; -} - -int GaleaSerial::calc_time (std::string &resp) -{ - constexpr int bytes_to_calc_rtt = 8; - unsigned char b[bytes_to_calc_rtt]; - - double start = get_timestamp (); - int res = serial->send_to_serial_port ("F4444444\n", 9); - if (res != 9) - { - safe_logger (spdlog::level::warn, "failed to send time calc command to device"); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - res = serial->read_from_serial_port (b, bytes_to_calc_rtt); - double done = get_timestamp (); - if (res != bytes_to_calc_rtt) - { - safe_logger ( - spdlog::level::warn, "failed to recv resp from time calc command, resp size {}", res); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - double duration = done - start; - double timestamp_device = 0; - memcpy (×tamp_device, b, 8); - timestamp_device /= 1000; - half_rtt = duration / 2; - - json result; - result["rtt"] = duration; - result["timestamp_device"] = timestamp_device; - result["pc_timestamp"] = start + half_rtt; - - resp = result.dump (); - safe_logger (spdlog::level::info, "calc_time output: {}", resp); - - return (int)BrainFlowExitCodes::STATUS_OK; -} \ No newline at end of file diff --git a/src/board_controller/openbci/galea_serial_v4.cpp b/src/board_controller/openbci/galea_serial_v4.cpp deleted file mode 100644 index 51e32679f..000000000 --- a/src/board_controller/openbci/galea_serial_v4.cpp +++ /dev/null @@ -1,529 +0,0 @@ -#include -#include -#include -#include -#include - -#include "custom_cast.h" -#include "galea_serial_v4.h" -#include "timestamp.h" - -#include "json.hpp" - -using json = nlohmann::json; - -#ifndef _WIN32 -#include -#endif - -#define START_BYTE 0xA0 -#define END_BYTE 0xC0 - - -GaleaSerialV4::GaleaSerialV4 (struct BrainFlowInputParams params) - : Board ((int)BoardIds::GALEA_SERIAL_BOARD_V4, params) -{ - serial = NULL; - is_streaming = false; - keep_alive = false; - initialized = false; - state = (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; - half_rtt = 0.0; -} - -GaleaSerialV4::~GaleaSerialV4 () -{ - skip_logs = true; - release_session (); -} - -int GaleaSerialV4::prepare_session () -{ - // check params - if (initialized) - { - safe_logger (spdlog::level::info, "Session is already prepared"); - return (int)BrainFlowExitCodes::STATUS_OK; - } - if (params.serial_port.empty ()) - { - safe_logger (spdlog::level::err, "serial port is not specified."); - return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; - } - if ((params.timeout > 600) || (params.timeout < 1)) - { - params.timeout = 3; - } - - // create serial object - serial = Serial::create (params.serial_port.c_str (), this); - int res = serial->open_serial_port (); - if (res < 0) - { - safe_logger (spdlog::level::err, - "Make sure you provided correct port name and have permissions to open it(run with " - "sudo/admin). Also, close all other apps using this port."); - return (int)BrainFlowExitCodes::UNABLE_TO_OPEN_PORT_ERROR; - } - res = serial->set_serial_port_settings (params.timeout * 1000, false); - if (res < 0) - { - safe_logger (spdlog::level::err, "Unable to set port settings, res is {}", res); - delete serial; - serial = NULL; - return (int)BrainFlowExitCodes::SET_PORT_ERROR; - } - res = serial->set_custom_baudrate (921600); - if (res < 0) - { - safe_logger (spdlog::level::err, "Unable to set custom baud rate, res is {}", res); - delete serial; - serial = NULL; - return (int)BrainFlowExitCodes::SET_PORT_ERROR; - } - safe_logger (spdlog::level::trace, "set port settings"); - - // set initial settings - std::string tmp; - std::string default_settings = "d"; // use default mode - res = config_board (default_settings, tmp); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - safe_logger (spdlog::level::err, "failed to apply default settings"); - delete serial; - serial = NULL; - return (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; - } - std::string sampl_rate = "~6"; - res = config_board (sampl_rate, tmp); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - safe_logger (spdlog::level::err, "failed to apply defaul sampling rate"); - delete serial; - serial = NULL; - return (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; - } - initialized = true; - - return (int)BrainFlowExitCodes::STATUS_OK; -} - -int GaleaSerialV4::config_board (std::string conf, std::string &response) -{ - if (serial == NULL) - { - safe_logger (spdlog::level::err, "You need to call prepare_session before config_board"); - return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; - } - // special handling for calc_time command - if (conf == "calc_time") - { - if (is_streaming) - { - safe_logger (spdlog::level::err, "can not calc delay during the streaming."); - return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; - } - int res = calc_time (response); - return res; - } - - if (conf == "get_gains") - { - std::stringstream gains; - - for (int i = 0; i < 20; i++) - { - gains << gain_tracker.get_gain_for_channel (i); - if (i < 19) - { - gains << ", "; - } - } - response = gains.str (); - safe_logger (spdlog::level::info, "gains for all channels: {}", response); - return (int)BrainFlowExitCodes::STATUS_OK; - } - - if (gain_tracker.apply_config (conf) == (int)OpenBCICommandTypes::INVALID_COMMAND) - { - safe_logger (spdlog::level::warn, "invalid command: {}", conf.c_str ()); - return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; - } - - std::string new_conf = conf + "\n"; - int len = (int)new_conf.size (); - safe_logger (spdlog::level::debug, "Trying to config GaleaSerialV4 with {}", new_conf.c_str ()); - int res = serial->send_to_serial_port (new_conf.c_str (), len); - if (len != res) - { - safe_logger (spdlog::level::err, "Failed to config a board"); - gain_tracker.revert_config (); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - - if (is_streaming) - { - safe_logger (spdlog::level::warn, - "reconfiguring device during the streaming may lead to inconsistent data, it's " - "recommended to call stop_stream before config_board"); - } - - return (int)BrainFlowExitCodes::STATUS_OK; -} - -int GaleaSerialV4::start_stream (int buffer_size, const char *streamer_params) -{ - if (!initialized) - { - safe_logger (spdlog::level::err, "You need to call prepare_session before config_board"); - return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; - } - if (is_streaming) - { - safe_logger (spdlog::level::err, "Streaming thread already running"); - return (int)BrainFlowExitCodes::STREAM_ALREADY_RUN_ERROR; - } - - // calc time before start stream - std::string resp; - for (int i = 0; i < 3; i++) - { - int res = calc_time (resp); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - return res; - } - } - - int res = prepare_for_acquisition (buffer_size, streamer_params); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - return res; - } - - // start streaming - res = serial->send_to_serial_port ("b\n", 2); - if (res != 2) - { - safe_logger (spdlog::level::err, "Failed to send a command to board"); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - - keep_alive = true; - streaming_thread = std::thread ([this] { this->read_thread (); }); - // wait for data to ensure that everything is okay - std::unique_lock lk (this->m); - auto sec = std::chrono::seconds (1); - if (cv.wait_for (lk, 3 * sec, - [this] { return this->state != (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; })) - { - this->is_streaming = true; - return this->state; - } - else - { - safe_logger (spdlog::level::err, "no data received in 5sec, stopping thread"); - this->is_streaming = true; - this->stop_stream (); - return (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; - } -} - -int GaleaSerialV4::stop_stream () -{ - if (is_streaming) - { - keep_alive = false; - is_streaming = false; - streaming_thread.join (); - this->state = (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; - int res = serial->send_to_serial_port ("s\n", 2); - if (res != 2) - { - safe_logger (spdlog::level::err, "Failed to send a command to board"); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - - // free kernel buffer - unsigned char b; - res = 1; - int max_attempt = 400000; // to dont get to infinite loop - int current_attempt = 0; - while (res == 1) - { - res = serial->read_from_serial_port (&b, 1); - current_attempt++; - if (current_attempt == max_attempt) - { - safe_logger ( - spdlog::level::err, "Command 's' was sent but streaming is still running."); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - } - - std::string resp; - for (int i = 0; i < 3; i++) - { - res = calc_time (resp); // call it in the end once to print time in the end - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - break; // dont send exit code - } - } - return (int)BrainFlowExitCodes::STATUS_OK; - } - else - { - return (int)BrainFlowExitCodes::STREAM_THREAD_IS_NOT_RUNNING; - } -} - -int GaleaSerialV4::release_session () -{ - if (initialized) - { - if (is_streaming) - { - stop_stream (); - } - free_packages (); - initialized = false; - if (serial) - { - delete serial; - serial = NULL; - } - } - return (int)BrainFlowExitCodes::STATUS_OK; -} - -void GaleaSerialV4::read_thread () -{ - int res = 0; - constexpr int package_size = 114; - constexpr int max_num_packages = 25; - constexpr int max_transaction_size = package_size * max_num_packages + 2; - unsigned char b[max_transaction_size]; - DataBuffer time_buffer (1, 11); - double latest_times[10]; - for (int i = 0; i < max_transaction_size; i++) - { - b[i] = 0; - } - int num_exg_rows = board_descr["default"]["num_rows"]; - int num_aux_rows = board_descr["auxiliary"]["num_rows"]; - double *exg_package = new double[num_exg_rows]; - double *aux_package = new double[num_aux_rows]; - for (int i = 0; i < num_exg_rows; i++) - { - exg_package[i] = 0.0; - } - for (int i = 0; i < num_aux_rows; i++) - { - aux_package[i] = 0.0; - } - - while (keep_alive) - { - // read and check first byte - int res = serial->read_from_serial_port (b, 1); - if (res != 1) - { - safe_logger (spdlog::level::debug, "unable to read 1 byte"); - continue; - } - if (b[0] != START_BYTE) - { - continue; - } - double pc_timestamp = get_timestamp (); - // read and check reamining bytes - int pos = 1; - int num_packages = 0; - while (keep_alive) - { - if (pos >= 2 + package_size) - { - if ((b[0] == START_BYTE) && (b[pos] == END_BYTE) && ((pos - 2) % package_size == 0)) - { - num_packages = (pos - 2) / package_size; - break; - } - } - if (pos > max_transaction_size - 1) - { - num_packages = 0; - break; - } - res = serial->read_from_serial_port (b + pos, 1); - if (res > 0) - { - pos += res; - } - } - if (!keep_alive) - { - break; - } - if (num_packages < 1) - { - if (pos >= 2) - safe_logger (spdlog::level::warn, - "Failed to parse some data, b[0]: {}, b[pos]: {}, size: {}", (int)b[0], - (int)b[pos], (int)pos); - continue; - } - else - { - if (this->state != (int)BrainFlowExitCodes::STATUS_OK) - { - safe_logger (spdlog::level::info, "received first package streaming is started"); - { - std::lock_guard lk (this->m); - this->state = (int)BrainFlowExitCodes::STATUS_OK; - } - this->cv.notify_one (); - safe_logger (spdlog::level::debug, "start streaming"); - } - } - - // calc delta between PC timestamp and device timestamp in last 10 packages, - // use this delta later on to assign timestamps - unsigned char *package_bytes = b + 1; - int offset_last_package = package_size * (num_packages - 1); - double timestamp_last_package = 0.0; - memcpy (×tamp_last_package, package_bytes + 88 + offset_last_package, 8); - timestamp_last_package /= 1000; // from ms to seconds - double time_delta = pc_timestamp - timestamp_last_package; - time_buffer.add_data (&time_delta); - int num_time_deltas = (int)time_buffer.get_current_data (10, latest_times); - time_delta = 0.0; - for (int i = 0; i < num_time_deltas; i++) - { - time_delta += latest_times[i]; - } - time_delta /= num_time_deltas; - - for (int cur_package = 0; cur_package < num_packages; cur_package++) - { - int offset = cur_package * package_size; - // exg(default preset) - exg_package[board_descr["default"]["package_num_channel"].get ()] = - (double)package_bytes[0 + offset]; - for (int i = 4, tmp_counter = 0; i < 28; i++, tmp_counter++) - { - double exg_scale = (double)(4.5 / float ((pow (2, 23) - 1)) / - gain_tracker.get_gain_for_channel (tmp_counter) * 1000000.); - exg_package[i - 3] = exg_scale * - (double)cast_24bit_to_int32 (package_bytes + offset + 5 + 3 * (i - 4)); - } - double timestamp_device = 0.0; - memcpy (×tamp_device, package_bytes + 88 + offset, 8); - timestamp_device /= 1000; // from ms to seconds - exg_package[board_descr["default"]["timestamp_channel"].get ()] = - timestamp_device + time_delta - half_rtt; - exg_package[board_descr["default"]["other_channels"][0].get ()] = pc_timestamp; - exg_package[board_descr["default"]["other_channels"][1].get ()] = timestamp_device; - - push_package (exg_package); - - // aux, 5 times slower - if (((int)package_bytes[0 + offset]) % 5 == 0) - { - double accel_scale = (double)(0.002 / (pow (2, 4))); - double gyro_scale = (double)(0.002 / (pow (2, 4))); // to be confirmed - double magnetometer_scale = (double)(0.002 / (pow (2, 4))); // to be confirmed - aux_package[board_descr["auxiliary"]["package_num_channel"].get ()] = - (double)package_bytes[0 + offset]; - uint16_t temperature = 0; - int32_t ppg_ir = 0; - int32_t ppg_red = 0; - float eda; - memcpy (&temperature, package_bytes + 78 + offset, 2); - memcpy (&eda, package_bytes + 1 + offset, 4); - memcpy (&ppg_red, package_bytes + 80 + offset, 4); - memcpy (&ppg_ir, package_bytes + 84 + offset, 4); - // ppg - aux_package[board_descr["auxiliary"]["ppg_channels"][0].get ()] = - (double)ppg_red; - aux_package[board_descr["auxiliary"]["ppg_channels"][1].get ()] = - (double)ppg_ir; - // eda - aux_package[board_descr["auxiliary"]["eda_channels"][0].get ()] = (double)eda; - // temperature - aux_package[board_descr["auxiliary"]["temperature_channels"][0].get ()] = - temperature / 100.0; - // battery - aux_package[board_descr["auxiliary"]["battery_channel"].get ()] = - (double)package_bytes[77 + offset]; - aux_package[board_descr["auxiliary"]["timestamp_channel"].get ()] = - timestamp_device + time_delta - half_rtt; - aux_package[board_descr["auxiliary"]["other_channels"][0].get ()] = - pc_timestamp; - aux_package[board_descr["auxiliary"]["other_channels"][1].get ()] = - timestamp_device; - // accel - aux_package[board_descr["auxiliary"]["accel_channels"][0].get ()] = - accel_scale * cast_16bit_to_int32 (b + 96 + offset); - aux_package[board_descr["auxiliary"]["accel_channels"][1].get ()] = - accel_scale * cast_16bit_to_int32 (b + 98 + offset); - aux_package[board_descr["auxiliary"]["accel_channels"][2].get ()] = - accel_scale * cast_16bit_to_int32 (b + 100 + offset); - // gyro - aux_package[board_descr["auxiliary"]["gyro_channels"][0].get ()] = - gyro_scale * cast_16bit_to_int32 (b + 102 + offset); - aux_package[board_descr["auxiliary"]["gyro_channels"][1].get ()] = - gyro_scale * cast_16bit_to_int32 (b + 104 + offset); - aux_package[board_descr["auxiliary"]["gyro_channels"][2].get ()] = - gyro_scale * cast_16bit_to_int32 (b + 106 + offset); - // magnetometer - aux_package[board_descr["auxiliary"]["magnetometer_channels"][0].get ()] = - magnetometer_scale * cast_16bit_to_int32 (b + 108 + offset); - aux_package[board_descr["auxiliary"]["magnetometer_channels"][1].get ()] = - magnetometer_scale * cast_16bit_to_int32 (b + 110 + offset); - aux_package[board_descr["auxiliary"]["magnetometer_channels"][2].get ()] = - magnetometer_scale * cast_16bit_to_int32 (b + 112 + offset); - push_package (aux_package, (int)BrainFlowPresets::AUXILIARY_PRESET); - } - } - } - delete[] exg_package; - delete[] aux_package; -} - -int GaleaSerialV4::calc_time (std::string &resp) -{ - constexpr int bytes_to_calc_rtt = 8; - unsigned char b[bytes_to_calc_rtt]; - - double start = get_timestamp (); - int res = serial->send_to_serial_port ("F4444444\n", 9); - if (res != 9) - { - safe_logger (spdlog::level::warn, "failed to send time calc command to device"); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - res = serial->read_from_serial_port (b, bytes_to_calc_rtt); - double done = get_timestamp (); - if (res != bytes_to_calc_rtt) - { - safe_logger ( - spdlog::level::warn, "failed to recv resp from time calc command, resp size {}", res); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - double duration = done - start; - double timestamp_device = 0; - memcpy (×tamp_device, b, 8); - timestamp_device /= 1000; - half_rtt = duration / 2; - - json result; - result["rtt"] = duration; - result["timestamp_device"] = timestamp_device; - result["pc_timestamp"] = start + half_rtt; - - resp = result.dump (); - safe_logger (spdlog::level::info, "calc_time output: {}", resp); - - return (int)BrainFlowExitCodes::STATUS_OK; -} \ No newline at end of file diff --git a/src/board_controller/openbci/galea_v4.cpp b/src/board_controller/openbci/galea_v4.cpp deleted file mode 100644 index 7289c154f..000000000 --- a/src/board_controller/openbci/galea_v4.cpp +++ /dev/null @@ -1,661 +0,0 @@ -#include "galea_v4.h" - -#include -#include -#include - -#include -#include -#include - -#include "custom_cast.h" -#include "json.hpp" -#include "timestamp.h" - -using json = nlohmann::json; - -#ifndef _WIN32 -#include -#endif - -constexpr int GaleaV4::package_size; -constexpr int GaleaV4::max_num_packages; -constexpr int GaleaV4::max_transaction_size; -constexpr int GaleaV4::socket_timeout; - -GaleaV4::GaleaV4 (struct BrainFlowInputParams params) - : Board ((int)BoardIds::GALEA_BOARD_V4, params) -{ - socket = NULL; - is_streaming = false; - keep_alive = false; - initialized = false; - state = (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; - half_rtt = 0.0; -} - -GaleaV4::~GaleaV4 () -{ - skip_logs = true; - release_session (); -} - -int GaleaV4::prepare_session () -{ - if (initialized) - { - safe_logger (spdlog::level::info, "Session is already prepared"); - return (int)BrainFlowExitCodes::STATUS_OK; - } - if ((params.timeout > 600) || (params.timeout < 1)) - { - params.timeout = 5; - } - - if (params.ip_address.empty ()) - { - params.ip_address = find_device (); - if (params.ip_address.empty ()) - { - return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; - } - } - socket = new SocketClientUDP (params.ip_address.c_str (), 2390); - int res = socket->connect (); - if (res != (int)SocketClientUDPReturnCodes::STATUS_OK) - { - safe_logger (spdlog::level::err, "failed to init socket: {}", res); - delete socket; - socket = NULL; - return (int)BrainFlowExitCodes::GENERAL_ERROR; - } - - safe_logger (spdlog::level::trace, "timeout for socket is {}", socket_timeout); - socket->set_timeout (socket_timeout); - // force default settings for device - std::string tmp; - std::string default_settings = "d"; // use default mode - res = config_board (default_settings, tmp); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - safe_logger (spdlog::level::err, "failed to apply default settings"); - delete socket; - socket = NULL; - return (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; - } - // force default sampling rate - 250 - std::string sampl_rate = "~6"; - res = config_board (sampl_rate, tmp); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - safe_logger (spdlog::level::err, "failed to apply default sampling rate"); - delete socket; - socket = NULL; - return (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; - } - initialized = true; - return (int)BrainFlowExitCodes::STATUS_OK; -} - -int GaleaV4::config_board (std::string conf, std::string &response) -{ - if (socket == NULL) - { - safe_logger (spdlog::level::err, "You need to call prepare_session before config_board"); - return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; - } - // special handling for some commands - if (conf == "calc_time") - { - if (is_streaming) - { - safe_logger (spdlog::level::err, "can not calc delay during the streaming."); - return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; - } - int res = calc_time (response); - return res; - } - - if (conf == "get_gains") - { - std::stringstream gains; - - for (int i = 0; i < 20; i++) - { - gains << gain_tracker.get_gain_for_channel (i); - if (i < 19) - { - gains << ", "; - } - } - response = gains.str (); - safe_logger (spdlog::level::info, "gains for all channels: {}", response); - return (int)BrainFlowExitCodes::STATUS_OK; - } - - if (gain_tracker.apply_config (conf) == (int)OpenBCICommandTypes::INVALID_COMMAND) - { - safe_logger (spdlog::level::warn, "invalid command: {}", conf.c_str ()); - return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; - } - - const char *config = conf.c_str (); - safe_logger (spdlog::level::debug, "Trying to config GaleaV4 with {}", config); - int len = (int)strlen (config); - int res = socket->send (config, len); - if (len != res) - { - gain_tracker.revert_config (); - if (res == -1) - { -#ifdef _WIN32 - safe_logger (spdlog::level::err, "WSAGetLastError is {}", WSAGetLastError ()); -#else - safe_logger (spdlog::level::err, "errno {} message {}", errno, strerror (errno)); -#endif - } - safe_logger (spdlog::level::err, "Failed to config a board"); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - - if (!is_streaming) - { - char b[GaleaV4::max_transaction_size]; - res = GaleaV4::max_transaction_size; - int max_attempt = 25; // to dont get to infinite loop - int current_attempt = 0; - while ((res >= 0) && (res % GaleaV4::package_size == 0)) - { - res = socket->recv (b, GaleaV4::max_transaction_size); - if (res == -1) - { -#ifdef _WIN32 - safe_logger (spdlog::level::err, "config_board recv ack WSAGetLastError is {}", - WSAGetLastError ()); -#else - safe_logger (spdlog::level::err, "config_board recv ack errno {} message {}", errno, - strerror (errno)); -#endif - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - current_attempt++; - if (current_attempt == max_attempt) - { - safe_logger (spdlog::level::err, "Device is streaming data while it should not!"); - return (int)BrainFlowExitCodes::STREAM_ALREADY_RUN_ERROR; - } - } - // set response string - for (int i = 0; i < res; i++) - { - response = response + b[i]; - } - switch (b[0]) - { - case 'A': - return (int)BrainFlowExitCodes::STATUS_OK; - case 'I': - safe_logger (spdlog::level::err, "invalid command"); - return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; - default: - safe_logger (spdlog::level::warn, "unknown char received: {}", b[0]); - return (int)BrainFlowExitCodes::STATUS_OK; - } - } - else - { - safe_logger (spdlog::level::warn, - "reconfiguring device during the streaming may lead to inconsistent data, it's " - "recommended to call stop_stream before config_board"); - } - - return (int)BrainFlowExitCodes::STATUS_OK; -} - -int GaleaV4::start_stream (int buffer_size, const char *streamer_params) -{ - if (!initialized) - { - safe_logger (spdlog::level::err, "You need to call prepare_session before config_board"); - return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; - } - if (is_streaming) - { - safe_logger (spdlog::level::err, "Streaming thread already running"); - return (int)BrainFlowExitCodes::STREAM_ALREADY_RUN_ERROR; - } - - // calc time before start stream - std::string resp; - for (int i = 0; i < 3; i++) - { - int res = calc_time (resp); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - return res; - } - } - - int res = prepare_for_acquisition (buffer_size, streamer_params); - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - return res; - } - - // start streaming - res = socket->send ("b", 1); - if (res != 1) - { - if (res == -1) - { -#ifdef _WIN32 - safe_logger (spdlog::level::err, "WSAGetLastError is {}", WSAGetLastError ()); -#else - safe_logger (spdlog::level::err, "errno {} message {}", errno, strerror (errno)); -#endif - } - safe_logger (spdlog::level::err, "Failed to send a command to board"); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - - keep_alive = true; - streaming_thread = std::thread ([this] { this->read_thread (); }); - // wait for data to ensure that everything is okay - std::unique_lock lk (this->m); - auto sec = std::chrono::seconds (1); - if (cv.wait_for (lk, 3 * sec, - [this] { return this->state != (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; })) - { - this->is_streaming = true; - return this->state; - } - else - { - safe_logger (spdlog::level::err, "no data received in 5sec, stopping thread"); - this->is_streaming = true; - this->stop_stream (); - return (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; - } -} - -int GaleaV4::stop_stream () -{ - if (is_streaming) - { - keep_alive = false; - is_streaming = false; - streaming_thread.join (); - this->state = (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; - int res = socket->send ("s", 1); - if (res != 1) - { - if (res == -1) - { -#ifdef _WIN32 - safe_logger (spdlog::level::err, "WSAGetLastError is {}", WSAGetLastError ()); -#else - safe_logger (spdlog::level::err, "errno {} message {}", errno, strerror (errno)); -#endif - } - safe_logger (spdlog::level::err, "Failed to send a command to board"); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - - // free kernel buffer - unsigned char b[GaleaV4::max_transaction_size]; - res = 0; - int max_attempt = 25; // to dont get to infinite loop - int current_attempt = 0; - while (res != -1) - { - res = socket->recv (b, GaleaV4::max_transaction_size); - current_attempt++; - if (current_attempt == max_attempt) - { - safe_logger ( - spdlog::level::err, "Command 's' was sent but streaming is still running."); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - } - - std::string resp; - for (int i = 0; i < 3; i++) - { - res = calc_time (resp); // call it in the end once to print time in the end - if (res != (int)BrainFlowExitCodes::STATUS_OK) - { - break; // dont send exit code - } - } - return (int)BrainFlowExitCodes::STATUS_OK; - } - else - { - return (int)BrainFlowExitCodes::STREAM_THREAD_IS_NOT_RUNNING; - } -} - -int GaleaV4::release_session () -{ - if (initialized) - { - if (is_streaming) - { - stop_stream (); - } - free_packages (); - initialized = false; - if (socket) - { - socket->close (); - delete socket; - socket = NULL; - } - } - return (int)BrainFlowExitCodes::STATUS_OK; -} - -void GaleaV4::read_thread () -{ - int res; - unsigned char b[GaleaV4::max_transaction_size]; - DataBuffer time_buffer (1, 11); - double latest_times[10]; - for (int i = 0; i < GaleaV4::max_transaction_size; i++) - { - b[i] = 0; - } - - int num_exg_rows = board_descr["default"]["num_rows"]; - int num_aux_rows = board_descr["auxiliary"]["num_rows"]; - double *exg_package = new double[num_exg_rows]; - double *aux_package = new double[num_aux_rows]; - for (int i = 0; i < num_exg_rows; i++) - { - exg_package[i] = 0.0; - } - for (int i = 0; i < num_aux_rows; i++) - { - aux_package[i] = 0.0; - } - - while (keep_alive) - { - res = socket->recv (b, GaleaV4::max_transaction_size); - if (res == -1) - { -#ifdef _WIN32 - safe_logger (spdlog::level::err, "WSAGetLastError is {}", WSAGetLastError ()); -#else - safe_logger (spdlog::level::err, "errno {} message {}", errno, strerror (errno)); -#endif - continue; - } - if (res % GaleaV4::package_size != 0) - { - if (res > 0) - { - // more likely its a string received, try to print it - b[res] = '\0'; - safe_logger (spdlog::level::warn, "Received: {}", b); - } - continue; - } - else - { - int num_packages = res / GaleaV4::package_size; - int offset_last_package = GaleaV4::package_size * (num_packages - 1); - // calc delta between PC timestamp and device timestamp in last 10 packages, - // use this delta later on to assign timestamps - double pc_timestamp = get_timestamp (); - unsigned long long timestamp_last_package = 0.0; - memcpy (×tamp_last_package, b + 88 + offset_last_package, - sizeof (unsigned long long)); // microseconds - double timestamp_last_package_converted = - static_cast (timestamp_last_package) / 1000000.0; // convert to seconds - double time_delta = pc_timestamp - timestamp_last_package_converted; - time_buffer.add_data (&time_delta); - int num_time_deltas = (int)time_buffer.get_current_data (10, latest_times); - time_delta = 0.0; - for (int i = 0; i < num_time_deltas; i++) - { - time_delta += latest_times[i]; - } - time_delta /= num_time_deltas; - - // inform main thread that everything is ok and first package was received - if (this->state != (int)BrainFlowExitCodes::STATUS_OK) - { - safe_logger (spdlog::level::info, - "received first package with {} bytes streaming is started", res); - { - std::lock_guard lk (this->m); - this->state = (int)BrainFlowExitCodes::STATUS_OK; - } - this->cv.notify_one (); - safe_logger (spdlog::level::debug, "start streaming"); - } - - for (int cur_package = 0; cur_package < num_packages; cur_package++) - { - int offset = cur_package * GaleaV4::package_size; - // exg (default preset) - exg_package[board_descr["default"]["package_num_channel"].get ()] = - (double)b[0 + offset]; - for (int i = 4, tmp_counter = 0; i < 28; i++, tmp_counter++) - { - double exg_scale = (double)(4.5 / float ((pow (2, 23) - 1)) / - gain_tracker.get_gain_for_channel (tmp_counter) * 1000000.); - exg_package[i - 3] = - exg_scale * (double)cast_24bit_to_int32 (b + offset + 5 + 3 * (i - 4)); - } - unsigned long long timestamp_device = 0.0; - memcpy (×tamp_device, b + 88 + offset, - sizeof (unsigned long long)); // reports microseconds - - double timestamp_device_converted = static_cast (timestamp_device); - timestamp_device_converted /= 1000000.0; // convert to seconds - - exg_package[board_descr["default"]["timestamp_channel"].get ()] = - timestamp_device_converted + time_delta - half_rtt; - exg_package[board_descr["default"]["other_channels"][0].get ()] = pc_timestamp; - exg_package[board_descr["default"]["other_channels"][1].get ()] = - timestamp_device_converted; - push_package (exg_package); - - // aux, 5 times smaller sampling rate - if (((int)b[0 + offset]) % 5 == 0) - { - double accel_scale = (double)(8.0 / static_cast (pow (2, 16) - 1)); - double gyro_scale = (double)(1000.0 / static_cast (pow (2, 16) - 1)); - double magnetometer_scale_xy = - (double)(2.6 / static_cast (pow (2, 13) - 1)); - double magnetometer_scale_z = - (double)(5.0 / static_cast (pow (2, 15) - 1)); - aux_package[board_descr["auxiliary"]["package_num_channel"].get ()] = - (double)b[0 + offset]; - uint16_t temperature = 0; - int32_t ppg_ir = 0; - int32_t ppg_red = 0; - float eda; - memcpy (&temperature, b + 78 + offset, 2); - memcpy (&eda, b + 1 + offset, 4); - memcpy (&ppg_red, b + 80 + offset, 4); - memcpy (&ppg_ir, b + 84 + offset, 4); - // ppg - aux_package[board_descr["auxiliary"]["ppg_channels"][0].get ()] = - (double)ppg_red; - aux_package[board_descr["auxiliary"]["ppg_channels"][1].get ()] = - (double)ppg_ir; - // eda - aux_package[board_descr["auxiliary"]["eda_channels"][0].get ()] = - (double)eda; - // temperature - aux_package[board_descr["auxiliary"]["temperature_channels"][0].get ()] = - temperature / 100.0; - // battery - aux_package[board_descr["auxiliary"]["battery_channel"].get ()] = - (double)b[77 + offset]; - aux_package[board_descr["auxiliary"]["timestamp_channel"].get ()] = - timestamp_device_converted + time_delta - half_rtt; - aux_package[board_descr["auxiliary"]["other_channels"][0].get ()] = - pc_timestamp; - aux_package[board_descr["auxiliary"]["other_channels"][1].get ()] = - timestamp_device_converted; - // accel - aux_package[board_descr["auxiliary"]["accel_channels"][0].get ()] = - accel_scale * (double)cast_16bit_to_int32_swap_order (b + 96 + offset); - aux_package[board_descr["auxiliary"]["accel_channels"][1].get ()] = - accel_scale * (double)cast_16bit_to_int32_swap_order (b + 98 + offset); - aux_package[board_descr["auxiliary"]["accel_channels"][2].get ()] = - accel_scale * (double)cast_16bit_to_int32_swap_order (b + 100 + offset); - // gyro - aux_package[board_descr["auxiliary"]["gyro_channels"][0].get ()] = - gyro_scale * (double)cast_16bit_to_int32_swap_order (b + 102 + offset); - aux_package[board_descr["auxiliary"]["gyro_channels"][1].get ()] = - gyro_scale * (double)cast_16bit_to_int32_swap_order (b + 104 + offset); - aux_package[board_descr["auxiliary"]["gyro_channels"][2].get ()] = - gyro_scale * (double)cast_16bit_to_int32_swap_order (b + 106 + offset); - // magnetometer - aux_package[board_descr["auxiliary"]["magnetometer_channels"][0].get ()] = - magnetometer_scale_xy * - (double)cast_13bit_to_int32_swap_order (b + 108 + offset); - aux_package[board_descr["auxiliary"]["magnetometer_channels"][1].get ()] = - magnetometer_scale_xy * - (double)cast_13bit_to_int32_swap_order (b + 110 + offset); - aux_package[board_descr["auxiliary"]["magnetometer_channels"][2].get ()] = - magnetometer_scale_z * - (double)cast_15bit_to_int32_swap_order (b + 112 + offset); - - push_package (aux_package, (int)BrainFlowPresets::AUXILIARY_PRESET); - } - } - } - } - delete[] exg_package; - delete[] aux_package; -} - -int GaleaV4::calc_time (std::string &resp) -{ - constexpr int bytes_to_calc_rtt = 8; - unsigned char b[bytes_to_calc_rtt]; - - double start = get_timestamp (); - int res = socket->send ("F4444444", bytes_to_calc_rtt); - if (res != bytes_to_calc_rtt) - { - safe_logger (spdlog::level::warn, "failed to send time calc command to device"); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - res = socket->recv (b, bytes_to_calc_rtt); - double done = get_timestamp (); - if (res != bytes_to_calc_rtt) - { - safe_logger ( - spdlog::level::warn, "failed to recv resp from time calc command, resp size {}", res); - return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; - } - - - double duration = done - start; - double timestamp_device = 0; - memcpy (×tamp_device, b, 8); - timestamp_device /= 1000; - half_rtt = duration / 2; - - json result; - result["rtt"] = duration; - result["timestamp_device"] = timestamp_device; - result["pc_timestamp"] = start + half_rtt; - - resp = result.dump (); - safe_logger (spdlog::level::info, "calc_time output: {}", resp); - - return (int)BrainFlowExitCodes::STATUS_OK; -} - -std::string GaleaV4::find_device () -{ -#ifdef _WIN32 - std::string ssdp_ip_address = "192.168.137.255"; -#else - std::string ssdp_ip_address = "239.255.255.250"; -#endif - - safe_logger (spdlog::level::trace, "trying to autodiscover device via SSDP"); - safe_logger (spdlog::level::trace, "timeout for search is {}", params.timeout); - std::string ip_address = ""; - SocketClientUDP udp_client (ssdp_ip_address.c_str (), - 1900); // ssdp ip and port - - int res = udp_client.connect (); - if (res == (int)SocketClientUDPReturnCodes::STATUS_OK) - { - std::string msearch = ("M-SEARCH * HTTP/1.1\r\nHost: " + ssdp_ip_address + - ":1900\r\nMAN: ssdp:discover\r\n" - "ST: urn:schemas-upnp-org:device:Basic:1\r\n" - "MX: 3\r\n" - "\r\n" - "\r\n"); - - safe_logger (spdlog::level::trace, "Use search request {}", msearch.c_str ()); - - res = (int)udp_client.send (msearch.c_str (), (int)msearch.size ()); - if (res == msearch.size ()) - { - unsigned char b[250]; - auto start_time = std::chrono::high_resolution_clock::now (); - int run_time = 0; - while (run_time < params.timeout) - { - res = udp_client.recv (b, 250); - if (res > 1) - { - std::string response ((const char *)b); - safe_logger (spdlog::level::trace, "Search response: {}", b); - std::regex rgx_ip ("LOCATION: http://([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)"); - std::smatch matches; - if (std::regex_search (response, matches, rgx_ip) == true) - { - if (matches.size () == 2) - { - std::regex rgx_sn ( - "USN: uuid:" + params.serial_number + "::upnp:rootdevice"); - if ((params.serial_number.empty ()) || - (std::regex_search (response, rgx_sn))) - { - ip_address = matches.str (1); - break; - } - } - } - } - auto end_time = std::chrono::high_resolution_clock::now (); - run_time = - (int)std::chrono::duration_cast (end_time - start_time) - .count (); - } - } - else - { - safe_logger (spdlog::level::err, "Sent res {}", res); - } - } - else - { - safe_logger (spdlog::level::err, "Failed to connect socket {}", res); - } - - if (ip_address.empty ()) - { - safe_logger (spdlog::level::err, "failed to find ip address"); - } - else - { - safe_logger (spdlog::level::info, "use ip address {}", ip_address.c_str ()); - } - - udp_client.close (); - return ip_address; -} \ No newline at end of file diff --git a/src/board_controller/openbci/inc/galea.h b/src/board_controller/openbci/inc/galea.h index d73eb5e01..47e7362d7 100644 --- a/src/board_controller/openbci/inc/galea.h +++ b/src/board_controller/openbci/inc/galea.h @@ -42,7 +42,7 @@ class Galea : public Board int release_session (); int config_board (std::string config, std::string &response); - static constexpr int package_size = 72; + static constexpr int package_size = 114; static constexpr int max_num_packages = 25; static constexpr int max_transaction_size = package_size * max_num_packages; static constexpr int socket_timeout = 2; diff --git a/src/board_controller/openbci/inc/galea_serial.h b/src/board_controller/openbci/inc/galea_serial.h deleted file mode 100644 index 2ee2ad68e..000000000 --- a/src/board_controller/openbci/inc/galea_serial.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "board.h" -#include "board_controller.h" -#include "openbci_gain_tracker.h" -#include "serial.h" - - -class GaleaSerial : public Board -{ - -private: - volatile bool keep_alive; - bool initialized; - bool is_streaming; - std::thread streaming_thread; - Serial *serial; - GaleaGainTracker gain_tracker; - std::mutex m; - std::condition_variable cv; - volatile int state; - volatile double half_rtt; - - void read_thread (); - int calc_time (std::string &resp); - - -public: - GaleaSerial (struct BrainFlowInputParams params); - ~GaleaSerial (); - - int prepare_session (); - int start_stream (int buffer_size, const char *streamer_params); - int stop_stream (); - int release_session (); - int config_board (std::string config, std::string &response); -}; \ No newline at end of file diff --git a/src/board_controller/openbci/inc/galea_serial_v4.h b/src/board_controller/openbci/inc/galea_serial_v4.h deleted file mode 100644 index 47dccd4f8..000000000 --- a/src/board_controller/openbci/inc/galea_serial_v4.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "board.h" -#include "board_controller.h" -#include "openbci_gain_tracker.h" -#include "serial.h" - - -class GaleaSerialV4 : public Board -{ - -private: - volatile bool keep_alive; - bool initialized; - bool is_streaming; - std::thread streaming_thread; - Serial *serial; - GaleaV4GainTracker gain_tracker; - std::mutex m; - std::condition_variable cv; - volatile int state; - volatile double half_rtt; - - void read_thread (); - int calc_time (std::string &resp); - - -public: - GaleaSerialV4 (struct BrainFlowInputParams params); - ~GaleaSerialV4 (); - - int prepare_session (); - int start_stream (int buffer_size, const char *streamer_params); - int stop_stream (); - int release_session (); - int config_board (std::string config, std::string &response); -}; \ No newline at end of file diff --git a/src/board_controller/openbci/inc/galea_v4.h b/src/board_controller/openbci/inc/galea_v4.h deleted file mode 100644 index 621968153..000000000 --- a/src/board_controller/openbci/inc/galea_v4.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "board.h" -#include "board_controller.h" -#include "openbci_gain_tracker.h" -#include "socket_client_udp.h" - - -class GaleaV4 : public Board -{ - -private: - volatile bool keep_alive; - volatile int state; - volatile double half_rtt; - bool initialized; - bool is_streaming; - std::thread streaming_thread; - SocketClientUDP *socket; - std::mutex m; - std::condition_variable cv; - GaleaV4GainTracker gain_tracker; - - std::string find_device (); - void read_thread (); - int calc_time (std::string &resp); - - -public: - GaleaV4 (struct BrainFlowInputParams params); - ~GaleaV4 (); - - int prepare_session (); - int start_stream (int buffer_size, const char *streamer_params); - int stop_stream (); - int release_session (); - int config_board (std::string config, std::string &response); - - static constexpr int package_size = 114; - static constexpr int max_num_packages = 25; - static constexpr int max_transaction_size = package_size * max_num_packages; - static constexpr int socket_timeout = 2; -}; diff --git a/src/utils/inc/brainflow_constants.h b/src/utils/inc/brainflow_constants.h index 021bbca7e..77a292395 100644 --- a/src/utils/inc/brainflow_constants.h +++ b/src/utils/inc/brainflow_constants.h @@ -52,7 +52,6 @@ enum class BoardIds : int FREEEEG32_BOARD = 17, BRAINBIT_BLED_BOARD = 18, GFORCE_DUAL_BOARD = 19, - GALEA_SERIAL_BOARD = 20, MUSE_S_BLED_BOARD = 21, MUSE_2_BLED_BOARD = 22, CROWN_BOARD = 23, @@ -79,8 +78,6 @@ enum class BoardIds : int EXPLORE_8_CHAN_BOARD = 45, GANGLION_NATIVE_BOARD = 46, EMOTIBIT_BOARD = 47, - GALEA_BOARD_V4 = 48, - GALEA_SERIAL_BOARD_V4 = 49, NTL_WIFI_BOARD = 50, ANT_NEURO_EE_511_BOARD = 51, FREEEEG128_BOARD = 52, From 9fbb1435002cbc402f901277832ec2fd6966e3fa Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Mon, 6 Oct 2025 16:56:08 -0500 Subject: [PATCH 2/4] Update Galea EDA calculation and available data Fixes #784 --- src/board_controller/brainflow_boards.cpp | 22 +++++++++++----------- src/board_controller/openbci/galea.cpp | 15 ++++++++++----- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/board_controller/brainflow_boards.cpp b/src/board_controller/brainflow_boards.cpp index 66a0e59be..670c7bfc3 100644 --- a/src/board_controller/brainflow_boards.cpp +++ b/src/board_controller/brainflow_boards.cpp @@ -199,17 +199,17 @@ BrainFlowBoards::BrainFlowBoards() {"name", "GaleaAuxiliary"}, {"sampling_rate", 50}, {"package_num_channel", 0}, - {"timestamp_channel", 17}, - {"marker_channel", 18}, - {"num_rows", 19}, - {"battery_channel", 5}, - {"eda_channels", {1}}, - {"ppg_channels", {2, 3}}, - {"accel_channels", {6, 7, 8}}, - {"gyro_channels", {9, 10, 11}}, - {"magnetometer_channels", {12, 13, 14}}, - {"other_channels", {15, 16}}, - {"temperature_channels", {4}} + {"timestamp_channel", 18}, + {"marker_channel", 19}, + {"num_rows", 20}, + {"battery_channel", 6}, + {"eda_channels", {1, 2}}, + {"ppg_channels", {3, 4}}, + {"accel_channels", {7, 8, 9}}, + {"gyro_channels", {10, 11, 12}}, + {"magnetometer_channels", {13, 14, 15}}, + {"other_channels", {16, 17}}, + {"temperature_channels", {5}} }; brainflow_boards_json["boards"]["4"]["default"] = { diff --git a/src/board_controller/openbci/galea.cpp b/src/board_controller/openbci/galea.cpp index edbe9001c..6f1122af1 100644 --- a/src/board_controller/openbci/galea.cpp +++ b/src/board_controller/openbci/galea.cpp @@ -23,8 +23,7 @@ constexpr int Galea::max_num_packages; constexpr int Galea::max_transaction_size; constexpr int Galea::socket_timeout; -Galea::Galea (struct BrainFlowInputParams params) - : Board ((int)BoardIds::GALEA_BOARD, params) +Galea::Galea (struct BrainFlowInputParams params) : Board ((int)BoardIds::GALEA_BOARD, params) { socket = NULL; is_streaming = false; @@ -477,9 +476,9 @@ void Galea::read_thread () uint16_t temperature = 0; int32_t ppg_ir = 0; int32_t ppg_red = 0; - float eda; + float eda_volts; memcpy (&temperature, b + 78 + offset, 2); - memcpy (&eda, b + 1 + offset, 4); + memcpy (&eda_volts, b + 1 + offset, 4); memcpy (&ppg_red, b + 80 + offset, 4); memcpy (&ppg_ir, b + 84 + offset, 4); // ppg @@ -489,7 +488,13 @@ void Galea::read_thread () (double)ppg_ir; // eda aux_package[board_descr["auxiliary"]["eda_channels"][0].get ()] = - (double)eda; + (double)eda_volts; + constexpr double eda_coefficient_1 = 3724389.83278; + constexpr double eda_coefficient_2 = 1652406.91447; + double eda_conductance = + 1000000.0 / ((eda_coefficient_1 * eda_volts) - eda_coefficient_2); + aux_package[board_descr["auxiliary"]["eda_channels"][1].get ()] = + eda_conductance; // temperature aux_package[board_descr["auxiliary"]["temperature_channels"][0].get ()] = temperature / 100.0; From 39bbb05c00cc3e2fa9f2c85d0142348448f8364e Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Mon, 6 Oct 2025 16:56:31 -0500 Subject: [PATCH 3/4] Update GaleaGainTracker to align with Galea Firmware v2.0.0 --- .../openbci/inc/openbci_gain_tracker.h | 62 +++++-------------- 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/src/board_controller/openbci/inc/openbci_gain_tracker.h b/src/board_controller/openbci/inc/openbci_gain_tracker.h index b5f2f2a63..ce6f278e5 100644 --- a/src/board_controller/openbci/inc/openbci_gain_tracker.h +++ b/src/board_controller/openbci/inc/openbci_gain_tracker.h @@ -179,50 +179,8 @@ class GaleaGainTracker : public OpenBCIGainTracker { public: GaleaGainTracker () - : OpenBCIGainTracker ({4, 4, 4, 4, 4, 4, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2}) // to be confirmed - { - } - - virtual int apply_config (std::string config) - { - if (config.size () == 1) - { - if ((config.at (0) == 'f') || (config.at (0) == 'g')) - { - std::copy (current_gains.begin (), current_gains.end (), old_gains.begin ()); - std::fill (current_gains.begin (), current_gains.end (), 1); - } - if ((config.at (0) == 'o') || (config.at (0) == 'd')) - { - std::copy (current_gains.begin (), current_gains.end (), old_gains.begin ()); - for (size_t i = 0; i < current_gains.size (); i++) - { - if (i < 6) - { - current_gains[i] = 4; - } - else if ((i == 6) || (i == 7)) - { - current_gains[i] = 12; - } - else - { - current_gains[i] = 2; - } - } - } - } - - return OpenBCIGainTracker::apply_config (config); - } -}; - -class GaleaV4GainTracker : public OpenBCIGainTracker -{ -public: - GaleaV4GainTracker () - : OpenBCIGainTracker ({4, 4, 4, 4, 4, 4, 4, 4, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12}) // to be confirmed + : OpenBCIGainTracker ({4, 4, 4, 4, 4, 4, 4, 4, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 4, 4, + 4, 4, 12, 12}) // 8 EMG + 12 EEG + 4 AUX(EMG) + 2 Reserved { channel_letters = std::vector {'1', '2', '3', '4', '5', '6', '7', '8', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'A', 'S', 'D', 'G', 'H', 'J', 'K', 'L'}; @@ -240,16 +198,28 @@ class GaleaV4GainTracker : public OpenBCIGainTracker if ((config.at (0) == 'o') || (config.at (0) == 'd')) { std::copy (current_gains.begin (), current_gains.end (), old_gains.begin ()); + // 8 EMG + 12 EEG + 4 AUX(EMG) + 2 Reserved for (size_t i = 0; i < current_gains.size (); i++) { - if (i < 8) + if (i < 8) // EMG channels 0-7 { current_gains[i] = 4; } - else + else if (i < 20) // EEG channels 8-19 { current_gains[i] = 12; } + else if (i < 24) // AUX channels 20-23 (4 AUX EMG + 2 Reserved) + { + if (i < 22) // AUX EMG channels 20-21 + { + current_gains[i] = 4; + } + else // Reserved channels 22-23 + { + current_gains[i] = 12; + } + } } } } From 02d525b315a2528158c2c73778a143591b8ee703 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Tue, 7 Oct 2025 23:44:51 -0500 Subject: [PATCH 4/4] Add get_gains_string() method to openbci_gain_tracker and remove hardcoded limits --- src/board_controller/openbci/galea.cpp | 12 +----------- .../openbci/inc/openbci_gain_tracker.h | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/board_controller/openbci/galea.cpp b/src/board_controller/openbci/galea.cpp index 6f1122af1..2473a7eac 100644 --- a/src/board_controller/openbci/galea.cpp +++ b/src/board_controller/openbci/galea.cpp @@ -117,17 +117,7 @@ int Galea::config_board (std::string conf, std::string &response) if (conf == "get_gains") { - std::stringstream gains; - - for (int i = 0; i < 20; i++) - { - gains << gain_tracker.get_gain_for_channel (i); - if (i < 19) - { - gains << ", "; - } - } - response = gains.str (); + response = gain_tracker.get_gains_string (); safe_logger (spdlog::level::info, "gains for all channels: {}", response); return (int)BrainFlowExitCodes::STATUS_OK; } diff --git a/src/board_controller/openbci/inc/openbci_gain_tracker.h b/src/board_controller/openbci/inc/openbci_gain_tracker.h index ce6f278e5..f70c0e85c 100644 --- a/src/board_controller/openbci/inc/openbci_gain_tracker.h +++ b/src/board_controller/openbci/inc/openbci_gain_tracker.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -126,6 +127,20 @@ class OpenBCIGainTracker { std::copy (old_gains.begin (), old_gains.end (), current_gains.begin ()); } + + virtual std::string get_gains_string () + { + std::stringstream gains; + for (size_t i = 0; i < current_gains.size (); i++) + { + gains << current_gains[i]; + if (i < current_gains.size () - 1) + { + gains << ", "; + } + } + return gains.str (); + } }; class CytonGainTracker : public OpenBCIGainTracker