Skip to content

Commit

Permalink
Merge pull request #109 from OpenBCI/measurement
Browse files Browse the repository at this point in the history
add hardware test and measurement code for novaxr
  • Loading branch information
Andrey1994 authored Jan 20, 2020
2 parents 1089d06 + b7e07c4 commit d46d020
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 2 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
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);
}
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 d46d020

Please sign in to comment.