Skip to content

Commit

Permalink
Merge remote-tracking branch 'openbci/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrey1994 committed Jan 20, 2020
2 parents f465a2d + d46d020 commit a2d920e
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 23 deletions.
2 changes: 1 addition & 1 deletion brainflow_boards.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
},
"3" : {
"name": "NovaXR",
"sampling_rate": 1000,
"sampling_rate": 1940,
"package_num_channel" : 0,
"timestamp_channel" : 21,
"num_rows" : 22,
Expand Down
3 changes: 2 additions & 1 deletion emulator/brainflow_emulator/novaxr_udp.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def run (self):
for _ in range (19):
package.append (self.package_num)
self.package_num = self.package_num + 1
if self.package_num % 255 == 0:
if self.package_num % 256 == 0:
self.package_num = 0
for i in range (1, self.package_size - 8):
package.append (random.randint (0, 255))
Expand All @@ -89,6 +89,7 @@ def run (self):
except socket.timeout:
logging.info ('timeout for send')


def main (cmd_list):
if not cmd_list:
raise Exception ('No command to execute')
Expand Down
15 changes: 11 additions & 4 deletions src/board_controller/openbci/cyton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,19 @@ void Cyton::read_thread ()
continue;
}

res = serial->read_from_serial_port (b, 32);
if (res != 32)
int remaining_bytes = 32;
int pos = 0;
while ((remaining_bytes > 0) && (keep_alive))
{
safe_logger (spdlog::level::debug, "unable to read 32 bytes");
continue;
res = serial->read_from_serial_port (b + pos, remaining_bytes);
remaining_bytes -= res;
pos += res;
}
if (!keep_alive)
{
return;
}

if ((b[31] < END_BYTE_STANDARD) || (b[31] > END_BYTE_MAX))
{
safe_logger (spdlog::level::warn, "Wrong end byte {}", b[31]);
Expand Down
15 changes: 11 additions & 4 deletions src/board_controller/openbci/cyton_daisy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,19 @@ void CytonDaisy::read_thread ()
continue;
}

res = serial->read_from_serial_port (b, 32);
if (res != 32)
int remaining_bytes = 32;
int pos = 0;
while ((remaining_bytes > 0) && (keep_alive))
{
safe_logger (spdlog::level::debug, "unable to read 31 bytes");
continue;
res = serial->read_from_serial_port (b + pos, remaining_bytes);
remaining_bytes -= res;
pos += res;
}
if (!keep_alive)
{
return;
}

if ((b[31] < END_BYTE_STANDARD) || (b[31] > END_BYTE_MAX))
{
safe_logger (spdlog::level::warn, "Wrong end byte {}", b[31]);
Expand Down
21 changes: 21 additions & 0 deletions src/board_controller/openbci/novaxr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,18 @@ void NovaXR::read_thread ()

int res;
unsigned char b[NovaXR::transaction_size];
long counter = 0;
double recv_avg_time = 0;
double process_avg_time = 0;
while (keep_alive)
{
auto recv_start_time = std::chrono::high_resolution_clock::now ();
res = socket->recv (b, NovaXR::transaction_size);
auto recv_stop_time = std::chrono::high_resolution_clock::now ();
auto recv_duration =
std::chrono::duration_cast<std::chrono::microseconds> (recv_stop_time - recv_start_time)
.count ();

if (res == -1)
{
#ifdef _WIN32
Expand Down Expand Up @@ -276,6 +285,7 @@ void NovaXR::read_thread ()
}
}

auto processing_start_time = std::chrono::high_resolution_clock::now ();
for (int cur_package = 0; cur_package < NovaXR::num_packages; cur_package++)
{
double package[NovaXR::num_channels] = {0.};
Expand Down Expand Up @@ -305,5 +315,16 @@ void NovaXR::read_thread ()
streamer->stream_data (package, NovaXR::num_channels, timestamp);
db->add_data (timestamp, package);
}
auto processing_stop_time = std::chrono::high_resolution_clock::now ();
auto processing_duration = std::chrono::duration_cast<std::chrono::microseconds> (
processing_stop_time - processing_start_time)
.count ();
recv_avg_time += recv_duration / 1000.0;
process_avg_time += processing_duration / 1000.0;
counter++;
}
recv_avg_time /= counter;
process_avg_time /= counter;
safe_logger (spdlog::level::trace, "recv avg time in ms {}", recv_avg_time);
safe_logger (spdlog::level::trace, "process avg time in ms {}", process_avg_time);
}
26 changes: 13 additions & 13 deletions src/utils/inc/custom_cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
#include <bitset>
#include <stdint.h>

// copypasted from OpenBCI_JavaScript_Utilities
inline int32_t cast_24bit_to_int32 (unsigned char *byte_array)
{
int32_t new_int =
(((0xFF & byte_array[0]) << 16) | ((0xFF & byte_array[1]) << 8) | (0xFF & byte_array[2]));
if ((new_int & 0x00800000) > 0)
new_int |= 0xFF000000;
else
new_int &= 0x00FFFFFF;
return new_int;
int prefix = 0;
if (byte_array[0] > 127)
{
prefix = 255;
}
return (prefix << 24) | (byte_array[0] << 16) | (byte_array[1] << 8) | byte_array[2];
}

inline int32_t cast_16bit_to_int32 (unsigned char *byte_array)
{
int32_t new_int = (((0xFF & byte_array[0]) << 8) | (0xFF & byte_array[1]));
if ((new_int & 0x00008000) > 0)
new_int |= 0xFFFF0000;
else
new_int &= 0x0000FFFF;
return new_int;
int prefix = 0;
if (byte_array[0] > 127)
{
prefix = 65535;
}
return (prefix << 16) | (byte_array[0] << 8) | byte_array[1];
}

inline void uchar_to_bits (unsigned char c, unsigned char *bits)
Expand Down
4 changes: 4 additions & 0 deletions src/utils/serial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ int Serial::close_serial_port ()
if (this->is_port_open ())
{
CloseHandle (this->port_descriptor);
this->port_descriptor = NULL;
}
return SerialExitCodes::OK;
}
Expand Down Expand Up @@ -158,8 +159,11 @@ int Serial::close_serial_port ()
if (this->is_port_open ())
{
int res = close (port_descriptor);
port_descriptor = 0;
if (res < 0)
{
return SerialExitCodes::CLOSE_ERROR;
}
}
return SerialExitCodes::OK;
}
Expand Down
123 changes: 123 additions & 0 deletions tests/python/hardware_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import argparse
import time
import statistics

import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt

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


def main ():
parser = argparse.ArgumentParser ()
# use docs to check which parameters are required for specific board, e.g. for Cyton - set serial port
parser.add_argument ('--ip-port', type = int, help = 'ip port', required = False, default = 0)
parser.add_argument ('--ip-protocol', type = int, help = 'ip protocol, check IpProtocolType enum', required = False, default = 0)
parser.add_argument ('--ip-address', type = str, help = 'ip address', required = False, default = '')
parser.add_argument ('--serial-port', type = str, help = 'serial port', required = False, default = '')
parser.add_argument ('--mac-address', type = str, help = 'mac address', required = False, default = '')
parser.add_argument ('--other-info', type = str, help = 'other info', required = False, default = '')
parser.add_argument ('--streamer-params', type = str, help = 'other info', required = False, default = '')
parser.add_argument ('--board-id', type = int, help = 'board id, check docs to get a list of supported boards', required = True)
parser.add_argument ('--log', action = 'store_true')
parser.add_argument ('--run-time', type = int, help = 'run time in sec', required = True)
args = parser.parse_args ()

params = BrainFlowInputParams ()
params.ip_port = args.ip_port
params.serial_port = args.serial_port
params.mac_address = args.mac_address
params.other_info = args.other_info
params.ip_address = args.ip_address
params.ip_protocol = args.ip_protocol

if (args.log):
BoardShim.enable_dev_board_logger ()
else:
BoardShim.disable_board_logger ()

# for streaming board need to use master board id
master_board_id = args.board_id
if args.board_id == BoardIds.STREAMING_BOARD.value:
master_board_id = int (params.other_info)

board = BoardShim (args.board_id, params)
board.prepare_session ()

buffer_size = int (BoardShim.get_sampling_rate (master_board_id) * args.run_time * 1.2) # + 20% for safety

board.start_stream (buffer_size, args.streamer_params)
time.sleep (args.run_time)
board.stop_stream ()
data = board.get_board_data ()
board.release_session ()

if master_board_id in (BoardIds.CYTON_BOARD.value, BoardIds.CYTON_WIFI_BOARD.value, BoardIds.GANGLION_WIFI_BOARD.value):
bytes_per_package = 33
elif master_board_id in (BoardIds.CYTON_DAISY_BOARD, BoardIds.CYTON_DAISY_WIFI_BOARD.value):
bytes_per_package = 66
elif master_board_id == BoardIds.SYNTHETIC_BOARD.value:
bytes_per_package = 104
elif master_board_id == BoardIds.NOVAXR_BOARD.value:
bytes_per_package = 72
else:
raise ValueError ('unsupported board')

total_bytes_received = bytes_per_package * data.shape[1]
packages_per_sec = float (data.shape[1]) / float (args.run_time);

timestamp_channel = BoardShim.get_timestamp_channel (master_board_id)
timestamp_array = data[timestamp_channel]
time_diff_array = list ()
for i in range (0, timestamp_array.size - 1):
time_diff_array.append (timestamp_array[i + 1] - timestamp_array[i])

package_num_channel = BoardShim.get_package_num_channel (master_board_id)
package_num_array = data[package_num_channel]
lost_packages = 0
expected = 0
cur_id = 0
while cur_id < package_num_array.size:
if expected == 256:
expected = 0
if package_num_array[cur_id] != expected:
BoardShim.log_message (LogLevels.LEVEL_WARN.value,
'package loss detected: position %d package_num value %d expected value %d' % (cur_id, package_num_array[cur_id], expected))
lost_packages = lost_packages + 1
else:
cur_id = cur_id + 1
expected = expected + 1

package_loss = (lost_packages / data.shape[1]) * 100

BoardShim.log_message (LogLevels.LEVEL_INFO.value, '\nResults:\n')
BoardShim.log_message (LogLevels.LEVEL_INFO.value, 'package loss percent %f' % package_loss)
BoardShim.log_message (LogLevels.LEVEL_INFO.value, 'average time delta %f' % statistics.mean (time_diff_array))
BoardShim.log_message (LogLevels.LEVEL_INFO.value, 'std deviation of time delta %f' % statistics.pstdev (time_diff_array))
BoardShim.log_message (LogLevels.LEVEL_INFO.value, 'total packages received %d' % data.shape[1])
BoardShim.log_message (LogLevels.LEVEL_INFO.value, 'packages per sec %f' % packages_per_sec)
BoardShim.log_message (LogLevels.LEVEL_INFO.value, 'total bytes received %d' % total_bytes_received)

eeg_channels = BoardShim.get_eeg_channels (master_board_id)
emg_channels = BoardShim.get_emg_channels (master_board_id)
total_channels = eeg_channels
# for cyton/ganglion eeg_channels and emg_channels are the same array because we can not split it
# for novaxr its 2 different arrays, join them
for ch in emg_channels:
if ch not in total_channels:
total_channels.append (ch)

df = pd.DataFrame (np.transpose (data))
df[total_channels].to_csv ('eeg_emg_data.csv')
df.to_csv ('all_data.csv')
plt.figure ()
df[total_channels].plot (subplots = True)
plt.show ()


if __name__ == "__main__":
main ()

0 comments on commit a2d920e

Please sign in to comment.