Commit 2a57e3da authored by Jussi Lindgren's avatar Jussi Lindgren
Browse files

Contrib: OpenBCI driver stabilization

* Complete rework of the driver's logic
* Added device name in log messages
* Modified default initialization sequence to
    's': stop streaming
    'v': reset board
    custom commands as per configuration in the GUI
    'b': start streaming
* Automatic port now scans available ports and picks first available
* Optimized drift estimation by sending a single block of sample
  per driver mainloop
* Implemented buffering when reading on serial port
* Updated switch/case parsing automaton to use enum values as states
  instead of integers (shuold be easier to read & maintain)
* Optimized the sample buffer array construction by pre-allocating it
  only once and filling the values by copy (no more sample-wise memory
  (de)allocation)
* Added parsing of board replies (when available) to get board
  information and possibly warn if unexpected return content is sent
* Added multiline support in additional commands
* Fixed bad parsing of accelerometers
* GUI: moved hidden widgets in an hidden window instead of hiding the
  widgets in the displayed window
* GUI: now shows feedback about configuration of the device (number
  of EEG channels, number Accelerometer channels, sampling rate)
* GUI: only shows com ports that can be opened, not all of them
  (usually reduces the list to only the OpenBCI board)
* GUI: exposed two different timeouts (see documentation for details)
* DOC: added one page documentation
* Fixed case / namings / indentation / whatever to comply with coding
  rules

Patch contributed by Yann Renard
parent 006b2322
![OpenViBE logo][OpenViBELogo]
![OpenBCI logo][OpenBCILogo]
# OpenBCI driver for OpenViBE documentation #
(documentation stands for [OpenViBE][OpenViBE] **1.2.0-OpenBCI-RC1**)
## Welcome ##
Welcome in the documentation of the [OpenBCI][OpenBCI] driver for [OpenViBE][OpenViBE]. This document gives you step by step details on how to configure your system, hardware and software to get the best possible experience. For further details about OpenBCI and OpenViBE, please refer to their [respective][OpenBCI] [documentation][OpenViBE].
## Getting Started ##
This section gives guidelines on how to set-up your environment configuration to get the best experience using [OpenBCI][OpenBCI] with [OpenViBE][OpenViBE]
### Linux ###
In order to use the [OpenBCI][OpenBCI] driver on Linux, you need to be granted read/write access to the serial port that is used to communicate with the dongle. Unfortunately, this does not come as a standard configuration on many Linux distributions and you will need to configure your setup so that the user is granted these access. On [Ubuntu][UbuntuDotCom] and its derivative, as well as on [Debian][DebianDotOrg], this can simply be achieved by adding the user to the `dialog` group with the following command :
> sudo adduser *username* dialout
Please note that you may need to close and reopen the session for changes to take effect.
On [Fedora][FedoraDotOrg] and its derivative, a similar command should be used to add the user in the `dialout` group :
> sudo usermode -a -G dialout *username*
Again, please note that you may need to close and reopen the session for changes to take effect.
### Windows ###
It is necessary to walk through a litlle bit of configuration to have the best experience with [OpenBCI][OpenBCI] and [OpenViBE][OpenViBE] on Windows. Indeed, when Windows first installs a serial port, its default configuration is not optimal for realtime streaming of the data. It especially configures large buffering and latencies, causing delays in the communication which eventually lead in delays in the acquired signals. While this only delays the detection of Motor Imagery or SSVEP tasks by a few hundreds of milliseconds, this also delays the ERP (eventually leading to a misdetection) and in all cases, alters the neurophysiological validity of the acquired data by a significant fraction. Consequently, it is recommended to configure the serial ports so that they are suitable for realtime use.
Note that this configuration is necessary each time Windows creates a new serial port. This may unfortunately happen **every time** you move the dongle on a new USB port on the computer !
In order to open the configuration dialog of the serial port, please follow these steps:
- Right click on the `Computer` icon and chose `Manage`
- After the `Computer Management` tool is opened, browse the tree in the left pane to the `Device Manager` section
- After the `Device Manager` tree is opened, browse in the right pane to the `Ports (COM & LPT)` section
- Then double click your port (in my case, `USB Serial Port (COM3)`
![The Windows Device Manager][WindowsTweaking1]
- In the `USB Serial Port Properties` dialog, go to the `Port Settings` tab and click `Advanced`
![The Port Settings dialog][WindowsTweaking2]
- This opens the `Advanced Settings for your COM port`
![The Advanced Port Settings dialog][WindowsTweaking3]
From this window, there are three settings we need to change :
- The `Receive` and `Transmit` settings of the `USB Transfer Sizes` section should both be changed from 4096 to **64** as this will reduce the time taken by the OS to deliver the data to either the dongle or the [OpenViBE][OpenViBE] driver
- The `Latency Timer` of the `BM Options` section should be reduced down to **1** as this will dramatically reduce the latency induced by the OS.
After you changed the settings as shown on the following figure, please click `OK`.
![The tweaked configuration of COM ports to have best experienece with OpenViBE and OpenBCI][WindowsTweaking4]
It is *not necessary* to restart the computer for the changes to take effect.
## Configuration ##
The configuration dialog of the [OpenBCI][OpenBCI] driver comes with a number of settings that one can change or pick depending on his preference.
![The OpenBCI configuration dialog][DriverDialog]
The following table documents each option that is specific to the [OpenBCI][OpenBCI] device. Please refer to the [OpenViBE documentation][OpenViBEDoc] for anything generic to the [OpenViBE][OpenViBE] Acquisition Server.
| Option | Default Value | Documentation |
| :-------------------------: | :-------------------------: | :-----------------------------------------------------------------------------------|
| **Device** | *empty* | This allows you to pick a serial port to connect on. The drodown list shows the serial ports that can currently be opened on this computer. If no port is found, the mention *No valid serial port* is shown in this list. If you cannot find your device in this list, please refer . |
| **Use Daisy Module** | *false* | This allows you to configure the daisy module. Four cases should be considered. 1/ if the daisy module is present and this option is set to **true**, then the device will turn to 16 channels samples 125 Hz. 2/ if no daisy module is present and this option is set to **false**, then the device will turn to 8 channels, 250 Hz. 3/ if the daisy module is **not present** on the board and this option is set to **true**, then the initialization of the driver will **fail**. 4/ if the daisy module is **present** on the board and this option is set to **false**, the daisy module will be disabled and the acquisition will be done as if the daisy module was not present on the board, turning the device back to 8 channels sampled at 125 Hz. |
| **Custom Command On Initialization** | *empty* | This option contains additional commands to send to the device at initialization. You must use one line per command, some command may contain multiple characters. For details about the commands, please refer to the [OpenBCI protocol documentation][OpenBCIProto]. Be advised that this will increase the delay of initialization by an order of magnitude that is a direct relation of the number and types of commands you want to add. Finally, not all the commands take the same time to be executed, if you include custom commands, you should consider adjusting the timeout values. |
| **Board Reply Reading Timeout** | 5000 | This allows to define the maximum time until reading a reply from the board after sending a command times out. Many commands end with a **\$\$\$** pattern, which can handily be captured and release the waiting loop when reading the board reply, but not all the commands have this **\$\$\$** pattern. Consequently, it is necessary to have a timeout for the other commands. The default value has been chosen to behave well even with custom commands that need a long time to reply such as **?**. If you don't use such command in your *Custom Command On Initialization*, you may reduce that delay. But be aware that if you reduce it too much, the driver may miss the **\$\$\$** pattern even though the board has sent it, resulting in unexpected behavior. |
| **Board Reply Flushing Timeout** | 500 | This option allows to flush and get rid of the streaming buffer. This is especially used when the driver asks the board to stop streaming and makes the streaming state absolutely clean when the driver needs to send a new command after stopping the streaming. You may reduce this value to make (re)connection faster, but if the buffer came not to be completely flushed, the remaining would be taken as the begining of the next command and this may result in unexpected behavior. |
The Configuration Summary gives information on the current configuration, especially the number of channels and the sampling rate of the device.
## Advanced Configuration ##
In addition to the above settings, another few settings are available to the user as advanced configuration. They are not exposed in the GUI and should be directly set in the [OpenViBEConfig][OpenViBE configuration] file instead. Refere to the [Configuration Manager section][OpenViBEConfig] of the [OpenViBE documentation][OpenViBEDoc] for further details on the configuration file format, location and others.
| Token | Default Value | Documentation |
| :-------------------------: | :-------------------------: | :-----------------------------------------------------------------------------------|
| **AcquisitionDriver OpenBCI MissingSampleDelayBeforeReset** | *1000* | This defines the size of the window to continuously monitor reception of samples from the driver. If no sample is received within that timeframe, the board is requested to stop and restart streaming. While the non-reception of samples from the board may reflect an unexpected state in the board, this strategy seems to sometimes recover and let the streaming go back to normal. The default value allows a good compromise between dealing with buffering and actual transmission delays and recovering fast when something goes wrong. If you experience such unstability in the transmision, we recommend that you first explore anything that may (in)directly affect the quality of the transmission before tweaking this setting. |
| **AcquisitionDriver OpenBCI DroppedSampleCountBeforeReset** | *5* | This defines the number of sample loss events until a recovery is attempted. It happens that the board gets in an unstable state where some sample would be missing in the stream. Stopping and restarting the streaming has proved to recover well. The default setting has been set so that a few occasional sample loss may occur (due to e.g. quality transmission) and be corrected by the drift correction process, while not waiting too long to attempt recovery when too many sample are lost. |
| **AcquisitionDriver OpenBCI DroppedSampleSafetyDelayBeforeReset** | *1000* | This defines a sefety delay where no reset should be attempted because of sample loss (see **AcquisitionDriver OpenBCI DroppedSampleCountBeforeReset**). This prevents a reset on the first sample where the driver synchronises with the streaming protocol and may miss a few samples until it is perfectly synced with the header and tail of the protocol frame. |
[FedoraDotOrg]: http://www.fedora.org
[UbuntuDotCom]: http://www.ubuntu.com
[DebianDotOrg]: http://www.debian.org
[OpenViBE]: http://openvibe.inria.fr
[OpenViBEDoc]: http://openvibe.inria.fr/documentation-index
[OpenViBEConfig]: http://openvibe.inria.fr/the-configuration-manager
[OpenViBELogo]: http://openvibe.inria.fr/openvibe/wp-content/themes/openvibe/images/openvibe-banner.png
[OpenBCI]: http://docs.openbci.com
[OpenBCIProto]: http://docs.openbci.com/software/01-OpenBCI_SDK
[OpenBCILogo]: http://openbci.com/community/wp-content/uploads/2015/09/logo_wide_web_borders_BIG-copy-1024x137.png
[WindowsTweaking1]: ServerDriver_OpenBCI_windows_tweaking_1.png
[WindowsTweaking2]: ServerDriver_OpenBCI_windows_tweaking_2.png
[WindowsTweaking3]: ServerDriver_OpenBCI_windows_tweaking_3.png
[WindowsTweaking4]: ServerDriver_OpenBCI_windows_tweaking_4.png
[DriverDialog]: ServerDriver_OpenBCI_configuration.png
......@@ -2,16 +2,102 @@
* OpenBCI driver for OpenViBE
*
* \author Jeremy Frey
*
* \note Based on OpenEEG code; inherits its AGPL3 license conditions
* \author Yann Renard
*
*/
#include "ovasCConfigurationOpenBCI.h"
#include <algorithm>
#include <string>
#if defined TARGET_OS_Windows
#include <windows.h>
#include <winbase.h>
#include <cstdio>
#include <cstdlib>
#include <commctrl.h>
#include <winsock2.h> // htons and co.
//#define TERM_SPEED 57600
#define TERM_SPEED CBR_115200 // OpenBCI is a bit faster than others
#elif defined TARGET_OS_Linux
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/select.h>
#include <netinet/in.h> // htons and co.
#include <unistd.h>
#define TERM_SPEED B115200
#else
#endif
#define boolean OpenViBE::boolean
using namespace OpenViBE;
using namespace OpenViBE::Kernel;
using namespace OpenViBEAcquisitionServer;
#define MAXIMUM_SERIAL_TTY (32)
#define MAXIMUM_SERIAL_USB_TTY (256-MAXIMUM_SERIAL_TTY)
uint32 OpenViBEAcquisitionServer::CConfigurationOpenBCI::getMaximumTTYCount(void)
{
return MAXIMUM_SERIAL_USB_TTY + MAXIMUM_SERIAL_TTY;
}
CString OpenViBEAcquisitionServer::CConfigurationOpenBCI::getTTYFileName(const uint32 ui32TTYNumber)
{
char l_sBuffer[1024];
#if defined TARGET_OS_Windows
::sprintf(l_sBuffer, "\\\\.\\COM%u", ui32TTYNumber);
#elif defined TARGET_OS_Linux
if(ui32TTYNumber<MAXIMUM_SERIAL_USB_TTY)
{
::sprintf(l_sBuffer, "/dev/ttyUSB%u", ui32TTYNumber);
}
else
{
::sprintf(l_sBuffer, "/dev/ttyS%u", ui32TTYNumber-MAXIMUM_SERIAL_USB_TTY);
}
#else
::sprintf(l_sBuffer, "");
#endif
return l_sBuffer;
}
bool OpenViBEAcquisitionServer::CConfigurationOpenBCI::isTTYFile(const CString& sFileName)
{
#if defined TARGET_OS_Windows
HANDLE l_pFile=::CreateFile(
(LPCSTR)sFileName,
GENERIC_READ,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if(l_pFile == INVALID_HANDLE_VALUE || l_pFile == NULL)
{
return false;
}
CloseHandle(l_pFile);
return true;
#elif defined TARGET_OS_Linux
int l_iFile=::open(sFileName, O_RDONLY);
if(l_iFile < 0)
{
return false;
}
close(l_iFile);
return true;
#else
// Caution, this path will claim all serial ports do exist because there was no platform specific implementation
return true;
#endif
}
static void checkbutton_daisy_module_cb(::GtkToggleButton* pButton, CConfigurationOpenBCI* pConfigurationOpenBCI)
{
pConfigurationOpenBCI->checkbuttonDaisyModuleCB(gtk_toggle_button_get_active(pButton)?CConfigurationOpenBCI::DaisyStatus_Active:CConfigurationOpenBCI::DaisyStatus_Inactive);
}
CConfigurationOpenBCI::CConfigurationOpenBCI(const char* sGtkBuilderFileName, OpenViBE::uint32& rUSBIndex)
:CConfigurationBuilder(sGtkBuilderFileName)
,m_rUSBIndex(rUSBIndex)
......@@ -31,46 +117,55 @@ boolean CConfigurationOpenBCI::preConfigure(void)
return false;
}
#if 0
::GtkEntry* l_pEntryComInit=GTK_ENTRY(gtk_builder_get_object(m_pBuilderConfigureInterface, "entry_com_init"));
::gtk_entry_set_text(l_pEntryComInit, m_sComInit.toASCIIString());
::gtk_entry_set_text(l_pEntryComInit, m_sAdditionalCommands.toASCIIString());
#else
std::string l_sAdditionalCommands=m_sAdditionalCommands.toASCIIString();
std::replace(l_sAdditionalCommands.begin(), l_sAdditionalCommands.end(), '\255', '\n');
::GtkTextView* l_pTextViewComInit=GTK_TEXT_VIEW(gtk_builder_get_object(m_pBuilderConfigureInterface, "text_view_com_init"));
::GtkTextBuffer* l_pTextBufferComInit=::gtk_text_view_get_buffer(l_pTextViewComInit);
::gtk_text_buffer_set_text(l_pTextBufferComInit, l_sAdditionalCommands.c_str(), -1);
#endif
::GtkSpinButton* l_pSpinButtonReadBoardReplyTimeout=GTK_SPIN_BUTTON(gtk_builder_get_object(m_pBuilderConfigureInterface, "spinbutton_read_board_reply_timeout"));
::gtk_spin_button_set_value(l_pSpinButtonReadBoardReplyTimeout, m_iReadBoardReplyTimeout);
::GtkSpinButton* l_pSpinButtonFlushBoardReplyTimeout=GTK_SPIN_BUTTON(gtk_builder_get_object(m_pBuilderConfigureInterface, "spinbutton_flush_board_reply_timeout"));
::gtk_spin_button_set_value(l_pSpinButtonFlushBoardReplyTimeout, m_iFlushBoardReplyTimeout);
::GtkSpinButton* l_pSpinButtonComDelay=GTK_SPIN_BUTTON(gtk_builder_get_object(m_pBuilderConfigureInterface, "spinbutton_com_delay"));
::gtk_spin_button_set_value(l_pSpinButtonComDelay, m_iComDelay);
::GtkToggleButton* l_pToggleButtonDaisyModule=GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_pBuilderConfigureInterface, "checkbutton_daisy_module"));
::gtk_toggle_button_set_active(l_pToggleButtonDaisyModule, m_bDaisyModule?true:false);
::g_signal_connect(::gtk_builder_get_object(m_pBuilderConfigureInterface, "checkbutton_daisy_module"), "toggled", G_CALLBACK(checkbutton_daisy_module_cb), this);
this->checkbuttonDaisyModuleCB(m_bDaisyModule?DaisyStatus_Active:DaisyStatus_Inactive);
::GtkComboBox* l_pComboBox=GTK_COMBO_BOX(gtk_builder_get_object(m_pBuilderConfigureInterface, "combobox_device"));
g_object_unref(m_pListStore);
m_pListStore=gtk_list_store_new(1, G_TYPE_STRING);
m_vComboSlotIndexToSerialPort.clear();
gtk_combo_box_set_model(l_pComboBox, GTK_TREE_MODEL(m_pListStore));
char l_sBuffer[1024];
boolean l_bSelected=false;
for(uint32 i=1; i<17; i++)
m_vComboSlotIndexToSerialPort[0]=-1;
::gtk_combo_box_append_text(l_pComboBox, "Automatic");
for(uint32 i=0, j=1; i<CConfigurationOpenBCI::getMaximumTTYCount(); i++)
{
#if defined TARGET_OS_Windows
::sprintf(l_sBuffer, "\\\\.\\COM%i", i);
#elif defined TARGET_OS_Linux
if(i<10)
{
::sprintf(l_sBuffer, i<10?"/dev/ttyS%d":"/dev/ttyUSB%d", i);
}
else
CString l_sFileName = CConfigurationOpenBCI::getTTYFileName(i);
if(CConfigurationOpenBCI::isTTYFile(l_sFileName))
{
::sprintf(l_sBuffer, "/dev/ttyUSB%d", i-10);
}
#else
::sprintf(l_sBuffer, "");
#endif
::gtk_combo_box_append_text(l_pComboBox, l_sBuffer);
if(m_rUSBIndex==i)
{
::gtk_combo_box_set_active(l_pComboBox, i-1);
l_bSelected=true;
m_vComboSlotIndexToSerialPort[j]=i;
::gtk_combo_box_append_text(l_pComboBox, l_sFileName.toASCIIString());
if(m_rUSBIndex==i)
{
::gtk_combo_box_set_active(l_pComboBox, j);
l_bSelected=true;
}
j++;
}
}
......@@ -88,21 +183,41 @@ boolean CConfigurationOpenBCI::postConfigure(void)
if(m_bApplyConfiguration)
{
int l_iUSBIndex=gtk_combo_box_get_active(l_pComboBox);
int l_iUSBIndex=m_vComboSlotIndexToSerialPort[gtk_combo_box_get_active(l_pComboBox)];
if(l_iUSBIndex>=0)
{
m_rUSBIndex=(uint32)l_iUSBIndex+1;
m_rUSBIndex=(uint32)l_iUSBIndex;
}
else
{
m_rUSBIndex=(uint32)-1;
}
#if 0
::GtkEntry* l_pEntryComInit=GTK_ENTRY(gtk_builder_get_object(m_pBuilderConfigureInterface, "entry_com_init"));
m_sComInit=::gtk_entry_get_text(l_pEntryComInit);
::GtkSpinButton* l_pSpinButtonComDelay=GTK_SPIN_BUTTON(gtk_builder_get_object(m_pBuilderConfigureInterface, "spinbutton_com_delay"));
gtk_spin_button_update(GTK_SPIN_BUTTON(l_pSpinButtonComDelay));
m_iComDelay=::gtk_spin_button_get_value_as_int(l_pSpinButtonComDelay);
m_sAdditionalCommands=::gtk_entry_get_text(l_pEntryComInit);
#else
::GtkTextView* l_pTextViewComInit=GTK_TEXT_VIEW(gtk_builder_get_object(m_pBuilderConfigureInterface, "text_view_com_init"));
::GtkTextBuffer* l_pTextBufferComInit=::gtk_text_view_get_buffer(l_pTextViewComInit);
::GtkTextIter l_oStartIter;
::GtkTextIter l_oEndIter;
::gtk_text_buffer_get_start_iter(l_pTextBufferComInit, &l_oStartIter);
::gtk_text_buffer_get_end_iter(l_pTextBufferComInit, &l_oEndIter);
std::string l_sAdditionalCommands=::gtk_text_buffer_get_text(l_pTextBufferComInit, &l_oStartIter, &l_oEndIter, FALSE);
std::replace(l_sAdditionalCommands.begin(), l_sAdditionalCommands.end(), '\n', '\255');
m_sAdditionalCommands = l_sAdditionalCommands.c_str();
#endif
::GtkSpinButton* l_pSpinButtonReadBoardReplyTimeout=GTK_SPIN_BUTTON(gtk_builder_get_object(m_pBuilderConfigureInterface, "spinbutton_read_board_reply_timeout"));
gtk_spin_button_update(GTK_SPIN_BUTTON(l_pSpinButtonReadBoardReplyTimeout));
m_iReadBoardReplyTimeout=::gtk_spin_button_get_value_as_int(l_pSpinButtonReadBoardReplyTimeout);
::GtkSpinButton* l_pSpinButtonFlushBoardReplyTimeout=GTK_SPIN_BUTTON(gtk_builder_get_object(m_pBuilderConfigureInterface, "spinbutton_flush_board_reply_timeout"));
gtk_spin_button_update(GTK_SPIN_BUTTON(l_pSpinButtonFlushBoardReplyTimeout));
m_iFlushBoardReplyTimeout=::gtk_spin_button_get_value_as_int(l_pSpinButtonFlushBoardReplyTimeout);
::GtkToggleButton* l_pToggleButtonDaisyModule=GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_pBuilderConfigureInterface, "checkbutton_daisy_module"));
m_bDaisyModule=::gtk_toggle_button_get_active(l_pToggleButtonDaisyModule)?true:false;
m_bDaisyModule=::gtk_toggle_button_get_active(l_pToggleButtonDaisyModule)?true:false;
}
if(!CConfigurationBuilder::postConfigure())
......@@ -112,28 +227,37 @@ boolean CConfigurationOpenBCI::postConfigure(void)
return true;
}
boolean CConfigurationOpenBCI::setComInit(const CString& sComInit)
boolean CConfigurationOpenBCI::setAdditionalCommands(const CString& sAdditionalCommands)
{
m_sComInit=sComInit;
m_sAdditionalCommands=sAdditionalCommands;
return true;
}
CString CConfigurationOpenBCI::getComInit(void) const
CString CConfigurationOpenBCI::getAdditionalCommands(void) const
{
return m_sComInit;
return m_sAdditionalCommands;
}
boolean CConfigurationOpenBCI::setComDelay(uint32 iComDelay)
boolean CConfigurationOpenBCI::setReadBoardReplyTimeout(uint32 iReadBoardReplyTimeout)
{
m_iComDelay=iComDelay;
m_iReadBoardReplyTimeout=iReadBoardReplyTimeout;
return true;
}
uint32 CConfigurationOpenBCI::getReadBoardReplyTimeout(void) const
{
return m_iReadBoardReplyTimeout;
}
boolean CConfigurationOpenBCI::setFlushBoardReplyTimeout(uint32 iFlushBoardReplyTimeout)
{
m_iFlushBoardReplyTimeout=iFlushBoardReplyTimeout;
return true;
}
uint32 CConfigurationOpenBCI::getComDelay(void) const
uint32 CConfigurationOpenBCI::getFlushBoardReplyTimeout(void) const
{
return m_iComDelay;
return m_iFlushBoardReplyTimeout;
}
bool CConfigurationOpenBCI::setDaisyModule(bool bDaisyModule)
......@@ -142,8 +266,53 @@ bool CConfigurationOpenBCI::setDaisyModule(bool bDaisyModule)
return true;
}
bool CConfigurationOpenBCI::getDaisyModule(void) const
{
return m_bDaisyModule;
}
\ No newline at end of file
}
void CConfigurationOpenBCI::checkbuttonDaisyModuleCB(EDaisyStatus eStatus)
{
SDaisyInformation l_oDaisyInformation=this->getDaisyInformation(eStatus);
char l_sBuffer[1024];
::sprintf(l_sBuffer, "%i EEG Channels", l_oDaisyInformation.iEEGChannelCount);
::gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(m_pBuilderConfigureInterface, "label_status_eeg_channel_count")), l_sBuffer);
::sprintf(l_sBuffer, "%i Accelerometer Channels", l_oDaisyInformation.iAccChannelCount);
::gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(m_pBuilderConfigureInterface, "label_status_acc_channel_count")), l_sBuffer);
::sprintf(l_sBuffer, "%i Hz Sampling Rate", l_oDaisyInformation.iSamplingRate);
::gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(m_pBuilderConfigureInterface, "label_status_sampling_rate")), l_sBuffer);
::gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(m_pBuilderConfigureInterface, "spinbutton_number_of_channels")), l_oDaisyInformation.iEEGChannelCount+l_oDaisyInformation.iAccChannelCount);
}
CConfigurationOpenBCI::SDaisyInformation CConfigurationOpenBCI::getDaisyInformation(EDaisyStatus eStatus)
{
SDaisyInformation l_oResult;
switch(eStatus)
{
case DaisyStatus_Inactive:
l_oResult.iEEGChannelCount = DefaultEEGChannelCount;
l_oResult.iAccChannelCount = DefaultAccChannelCount;
l_oResult.iSamplingRate = DefaultSamplingRate;
break;
case DaisyStatus_Active:
l_oResult.iEEGChannelCount = DefaultEEGChannelCount*2;
l_oResult.iAccChannelCount = DefaultAccChannelCount;
l_oResult.iSamplingRate = DefaultSamplingRate/2;
break;
default:
l_oResult.iEEGChannelCount = 0;
l_oResult.iAccChannelCount = 0;
l_oResult.iSamplingRate = 0;
break;
}
return l_oResult;
}
......@@ -2,9 +2,7 @@
* OpenBCI driver for OpenViBE
*
* \author Jeremy Frey
*
* \note Based on OpenEEG code; inherits its AGPL3 license conditions
*
* \author Yann Renard
*/
#ifndef __OpenViBE_AcquisitionServer_CConfigurationOpenBCI_H__
#define __OpenViBE_AcquisitionServer_CConfigurationOpenBCI_H__
......@@ -12,34 +10,66 @@
#include "../ovasCConfigurationBuilder.h"
#include <gtk/gtk.h>
#include <map>
namespace OpenViBEAcquisitionServer
{
class CConfigurationOpenBCI : public OpenViBEAcquisitionServer::CConfigurationBuilder
{
public:
const static OpenViBE::uint16 DefaultSamplingRate = 250; // sampling rate with no daisy module (divided by 2 with daisy module)
const static OpenViBE::uint16 DefaultEEGChannelCount = 8; // number of EEG channels with no daisy module (multiplied by 2 with daisy module)
const static OpenViBE::uint16 DefaultAccChannelCount = 3; // number of Acc channels (daisy module does not have an impact)
typedef enum
{
DaisyStatus_Active,
DaisyStatus_Inactive,
} EDaisyStatus;
typedef struct
{
int iEEGChannelCount;
int iAccChannelCount;
int iSamplingRate;
} SDaisyInformation;
static OpenViBE::uint32 getMaximumTTYCount(void);
static OpenViBE::CString getTTYFileName(const OpenViBE::uint32 ui32TTYNumber);
static bool isTTYFile(const OpenViBE::CString& sTTYFileName);
CConfigurationOpenBCI(const char* sGtkBuilderFileName, OpenViBE::uint32& rUSBIndex);
virtual ~CConfigurationOpenBCI(void);
virtual OpenViBE::boolean preConfigure(void);
virtual OpenViBE::boolean postConfigure(void);
OpenViBE::boolean setComInit(const OpenViBE::CString& sComInit);
OpenViBE::CString getComInit(void) const;
OpenViBE::boolean setComDelay(const OpenViBE::uint32 iComDelay);
OpenViBE::uint32 getComDelay(void) const;
OpenViBE::boolean setAdditionalCommands(const OpenViBE::CString& sAdditionalCommands);
OpenViBE::CString getAdditionalCommands(void) const;
OpenViBE::boolean setReadBoardReplyTimeout(const OpenViBE::uint32 iReadBoardReplyTimeout);
OpenViBE::uint32 getReadBoardReplyTimeout(void) const;
OpenViBE::boolean setFlushBoardReplyTimeout(const OpenViBE::uint32 iFlushBoardReplyTimeout);
OpenViBE::uint32 getFlushBoardReplyTimeout(void) const;
OpenViBE::boolean setDaisyModule(const OpenViBE::boolean sDaisyModule);
OpenViBE::boolean getDaisyModule(void) const;
void checkbuttonDaisyModuleCB(EDaisyStatus eStatus);
static SDaisyInformation getDaisyInformation(EDaisyStatus eStatus);
protected:
std::map <int, int> m_vComboSlotIndexToSerialPort;
OpenViBE::uint32& m_rUSBIndex;
::GtkListStore* m_pListStore;
::GtkEntry* l_pEntryComInit;
OpenViBE::CString m_sComInit;
OpenViBE::uint32 m_iComDelay;
OpenViBE::CString m_sAdditionalCommands;
OpenViBE::uint32 m_iReadBoardReplyTimeout;
OpenViBE::uint32 m_iFlushBoardReplyTimeout;
OpenViBE::boolean m_bDaisyModule;
};
};
......
......@@ -2,8 +2,7 @@
* OpenBCI driver for OpenViBE
*
* \author Jeremy Frey
*
* \note Based on OpenEEG code; inherits its AGPL3 license conditions
* \author Yann Renard
*
*/
#ifndef __OpenViBE_AcquisitionServer_CDriverOpenBCI_H__
......@@ -23,23 +22,14 @@
#endif
#include <vector>
// some constants related to the boardWriteAndPrint
#define ADS1299_VREF 4.5 // reference voltage for ADC in ADS1299. set by its hardware
#define ADS1299_GAIN 24.0 //assumed gain setting for ADS1299. set by its Arduino code
// wait a little before before new writings are initiated (ms)
#define SLEEP_BEFORE_WRITE 500