Skip to content

Commit

Permalink
Added impedance mode for ANT neuro devices (#751)
Browse files Browse the repository at this point in the history
* added impedance_mode to ant neuro boards
---------

Signed-off-by: Andrey Parfenov <[email protected]>
Co-authored-by: Benjamin Dieudonné <[email protected]>
Co-authored-by: Andrey Parfenov <[email protected]>
  • Loading branch information
3 people authored Jan 13, 2025
1 parent 4643c09 commit 7a63d28
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 61 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cppcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
env:
DEBIAN_FRONTEND: noninteractive
- name: Run Cppcheck
run: cppcheck --std=c++11 -ithird_party/spdlog -ithird_party/libsvm -ithird_party/SimpleBLE -ithird_party/fmt -ithird_party/SimpleDBus -ithird_party/SimpleBluez -ithird_party/kissfft -isrc/utils/os_serial_ioctl.cpp --error-exitcode=1 --xml --xml-version=2 --force src cpp_package third_party 2>cppcheck_res.xml
run: cppcheck --std=c++11 -ithird_party/wavelib -ithird_party/spdlog -ithird_party/libsvm -ithird_party/SimpleBLE -ithird_party/fmt -ithird_party/SimpleDBus -ithird_party/SimpleBluez -ithird_party/kissfft -isrc/utils/os_serial_ioctl.cpp --error-exitcode=1 --xml --xml-version=2 --force src cpp_package third_party 2>cppcheck_res.xml
- name: Generate Report
if: ${{ failure() }}
run: cppcheck-htmlreport --title=BrainFlow --file=cppcheck_res.xml --report-dir=report
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run_android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
cd $GITHUB_WORKSPACE/tools
zip -r jniLibs.zip jniLibs
- name: Install AWS CLI
run: sudo -H python3 -m pip install awscli==1.21.10
run: sudo -H python3 -m pip install awscli==1.35.24
- name: Upload To AWS
if: ${{ github.event_name == 'push' && github.repository == 'brainflow-dev/brainflow' }}
run: |
Expand Down
9 changes: 7 additions & 2 deletions docs/SupportedBoards.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1026,12 +1026,14 @@ Ant Neuro has many devices and all of them are supported by BrainFlow:
- :code:`ANT_NEURO_EE_225_BOARD`
- :code:`ANT_NEURO_EE_511_BOARD`

Initialization Example:
Initialization example:

.. code-block:: python
params = BrainFlowInputParams()
board = BoardShim(BoardIds.ANT_NEURO_EE_410_BOARD, params)
board = BoardShim(BoardIds.ANT_NEURO_EE_410_BOARD, params) # 8 channel amplifier
`More elaborate example <https://github.com/brainflow-dev/brainflow/blob/master/python_package/examples/tests/eego_impedances_and_eeg.py>`_ (reading EEG and impedances)

Supported platforms:

Expand All @@ -1040,7 +1042,10 @@ Supported platforms:

Available commands:

- Set impedance mode: :code:`board.config_board("impedance_mode:1")`, mode 0 or 1.
- Set sampling rate: :code:`board.config_board("sampling_rate:500")`, for available values check docs from Ant Neuro.
- Set reference range: :code:`board.config_board("reference_range:1.0")`, for available values check docs from Ant Neuro.
- Set bipolar range: :code:`board.config_board("bipolar_range:2.5")`, for available values check docs from Ant Neuro.

For more information about Ant Neuro boards please refer to their User Manual.

Expand Down
29 changes: 29 additions & 0 deletions python_package/examples/tests/eego_impedances_and_eeg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import time

from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds

if __name__ == '__main__':
params = BrainFlowInputParams()
board = BoardShim(BoardIds.ANT_NEURO_EE_411_BOARD, params) # 8 channel amplifier
board.prepare_session()

# Get impedance data
board.config_board('impedance_mode:1')
board.start_stream()
for i in range(5):
time.sleep(1)
data = board.get_board_data() # get all data and remove it from internal buffer
print(f'{data.shape[0]} channels x {data.shape[1]} samples')
board.stop_stream()

# Get EEG data
board.config_board('impedance_mode:0')
board.start_stream()
for i in range(3):
time.sleep(1)
data = board.get_board_data() # get all data and remove it from internal buffer
print(f'{data.shape[0]} channels x {data.shape[1]} samples')
board.stop_stream()

board.release_session()

89 changes: 85 additions & 4 deletions src/board_controller/ant_neuro/ant_neuro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ AntNeuroBoard::AntNeuroBoard (int board_id, struct BrainFlowInputParams params)
}
reference_range = -1.0;
bipolar_range = -1.0;
impedance_mode = false;
impedance_package_num = -1;
}

AntNeuroBoard::~AntNeuroBoard ()
Expand Down Expand Up @@ -100,6 +102,8 @@ int AntNeuroBoard::prepare_session ()
{
sampling_rate = amp->getSamplingRatesAvailable ()[0];
}
impedance_mode = false;
impedance_package_num = 0;
}
catch (const exceptions::notFound &e)
{
Expand Down Expand Up @@ -143,10 +147,18 @@ int AntNeuroBoard::start_stream (int buffer_size, const char *streamer_params)

try
{
safe_logger (spdlog::level::info,
"sampling rate: {}, reference range: {}, bipolar range: {}", sampling_rate,
reference_range, bipolar_range);
stream = amp->OpenEegStream (sampling_rate, reference_range, bipolar_range);
if (impedance_mode)
{
safe_logger (spdlog::level::info, "start impedance stream");
stream = amp->OpenImpedanceStream ();
}
else
{
safe_logger (spdlog::level::info,
"start eeg stream (sampling rate: {}, reference range: {}, bipolar range: {})",
sampling_rate, reference_range, bipolar_range);
stream = amp->OpenEegStream (sampling_rate, reference_range, bipolar_range);
}
}
catch (const std::runtime_error &e)
{
Expand Down Expand Up @@ -217,6 +229,7 @@ void AntNeuroBoard::read_thread ()
}
std::vector<int> emg_channels;
std::vector<int> eeg_channels;
std::vector<int> resistance_channels;
try
{
emg_channels = board_descr["default"]["emg_channels"].get<std::vector<int>> ();
Expand All @@ -233,6 +246,15 @@ void AntNeuroBoard::read_thread ()
{
safe_logger (spdlog::level::trace, "device has no eeg channels");
}
try
{
resistance_channels =
board_descr["default"]["resistance_channels"].get<std::vector<int>> ();
}
catch (...)
{
safe_logger (spdlog::level::trace, "device has no resistance_channels channels");
}
std::vector<channel> ant_channels = stream->getChannelList ();

while (keep_alive)
Expand All @@ -245,17 +267,26 @@ void AntNeuroBoard::read_thread ()
{
int eeg_counter = 0;
int emg_counter = 0;
int resistance_counter = 0;
for (int j = 0; j < buf_channels_len; j++)
{
if ((ant_channels[j].getType () == channel::reference) &&
(eeg_counter < (int)eeg_channels.size ()))
{
package[eeg_channels[eeg_counter++]] = buf.getSample (j, i);
if (impedance_mode)
{
resistance_counter++;
}
}
if ((ant_channels[j].getType () == channel::bipolar) &&
(emg_counter < (int)emg_channels.size ()))
{
package[emg_channels[emg_counter++]] = buf.getSample (j, i);
if (impedance_mode)
{
resistance_counter++;
}
}
if (ant_channels[j].getType () == channel::sample_counter)
{
Expand All @@ -267,11 +298,32 @@ void AntNeuroBoard::read_thread ()
package[board_descr["default"]["other_channels"][0].get<int> ()] =
buf.getSample (j, i);
}
if ((ant_channels[j].getType () == channel::impedance_reference) &&
(resistance_counter < (int)resistance_channels.size ()))
{
package[resistance_channels[resistance_counter++]] = buf.getSample (j, i);
}
if ((ant_channels[j].getType () == channel::impedance_ground) &&
(resistance_counter < (int)resistance_channels.size ()))
{
package[resistance_channels[resistance_counter++]] = buf.getSample (j, i);
}
}
package[board_descr["default"]["timestamp_channel"].get<int> ()] = get_timestamp ();
if (impedance_mode)
{
package[board_descr["default"]["package_num_channel"].get<int> ()] =
impedance_package_num++;
}
push_package (package);
}
std::this_thread::sleep_for (std::chrono::milliseconds (1));
if (impedance_mode)
{
// some more sleep; twice every second should be more than enough
// if left out, it yields impedances at around 64 Hz
std::this_thread::sleep_for (std::chrono::milliseconds (500));
}
}
catch (...)
{
Expand All @@ -293,6 +345,7 @@ int AntNeuroBoard::config_board (std::string config, std::string &response)
std::string prefix = "sampling_rate:";
std::string rv_prefix = "reference_range:";
std::string bv_prefix = "bipolar_range:";
std::string mode_prefix = "impedance_mode:";

if (config.find (prefix) != std::string::npos)
{
Expand Down Expand Up @@ -391,6 +444,34 @@ int AntNeuroBoard::config_board (std::string config, std::string &response)

return (int)BrainFlowExitCodes::STATUS_OK;
}
else if (config.find (mode_prefix) != std::string::npos)
{
bool new_impedance_mode;
std::string value = config.substr (mode_prefix.size ());

if (value == "0" || value == "1")
{
try
{
new_impedance_mode = static_cast<bool> (std::stod (value));
}
catch (...)
{
safe_logger (spdlog::level::err, "format is '{}value'", mode_prefix.c_str ());
return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR;
}

impedance_mode = new_impedance_mode;
return (int)BrainFlowExitCodes::STATUS_OK;
}
else
{
safe_logger (spdlog::level::err, "not supported value provided");
safe_logger (spdlog::level::debug, "supported values: '0' or '1'");
return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR;
}
}

safe_logger (spdlog::level::err, "format is '{}value'", prefix.c_str ());
return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR;
}
Expand Down
2 changes: 2 additions & 0 deletions src/board_controller/ant_neuro/inc/ant_neuro.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class AntNeuroBoard : public Board
int sampling_rate;
double reference_range;
double bipolar_range;
bool impedance_mode;
int impedance_package_num;

void read_thread ();
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/board_controller/board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ int Board::get_board_data (int data_count, int preset, double *data_buf)
if (dbs.find (preset) == dbs.end ())
{
safe_logger (spdlog::level::err,
"stream is not startted or no preset: {} found for this board", preset);
"stream is not started or no preset: {} found for this board", preset);
return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR;
}
if (!dbs[preset])
Expand Down
Loading

0 comments on commit 7a63d28

Please sign in to comment.