This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,151 @@
//CRingBuffer provided by g.tec
//website: http://www.gtec.at
//redistribution of this file has been offcially granted by g.tec
#pragma once
#include <algorithm> // std::min
#include <winnt.h>
#include <stdlib.h>
//#include <afxwin.h>
/*
* Class representing a ring buffer with elements of type float.
*/
template <typename T>
class CRingBuffer
{
public:
//Constructor. Creates an empty buffer with an initial capacity of zero.
CRingBuffer() : _buffer(nullptr) { }
//Destructor. Frees the allocated buffer.
~CRingBuffer()
{
if (_buffer != nullptr) { VirtualFree(_buffer, 0, MEM_RELEASE); }
_buffer = nullptr;
}
/*
* Initializes the buffer with the specified capacity representing the number of elements that the buffer can contain.
* Returns false if the memory couldn't be allocated (e.g. because of not enough free disk space); true, if the call succeeded.
*/
bool Initialize(const uint32_t capacity)
{
//if the buffer has been allocated before, release this memory first
if (_buffer != nullptr)
{
VirtualFree(_buffer, 0, MEM_RELEASE);
_buffer = nullptr;
}
if (capacity > 0)
{
//allocate memory for the buffer
_buffer = static_cast<T*>(VirtualAlloc(nullptr, capacity * sizeof(T), MEM_COMMIT, PAGE_READWRITE));
//check if allocation succeeded
if (_buffer == nullptr) { return false; }
_capacity = capacity;
}
//reset the buffer positions
Reset();
return true;
}
//Clears the buffer by resetting both the start and end position to zero.
void Reset()
{
_start = 0;
_end = 0;
_isEmpty = true;
}
//Returns the buffer's capacity it has been initialized to, i.e. the number of elements the buffer can contain.
int GetCapacity() const { return _capacity; }
//Returns the free space of the buffer, i.e. the number of new elements that can be enqueued before the buffer will overrun.
int GetFreeSize() const { return _capacity - GetSize(); }
//Returns the number of elements that the buffer currently contains (don't confuse the size (number of ACTUALLY contained elements) with the capacity (maximum number of elements that the buffer CAN contain)!).
int GetSize() const
{
if (_isEmpty) { return 0; }
if (_start < _end) { return _end - _start; }
return _capacity - (_start - _end);
}
/*
* Writes the specified number of elements from the specified source array into the ring buffer. If the number of elements to copy exceeds the free buffer space, only the free buffer space will be written, existing elements will NOT be overwritten.
* float* source: pointer to the first element of the source array whose elements should be stored into the ring buffer.
* uint32_t length: the number of elements from the source array that should be copied into the ring buffer.
*/
void Write(T* source, const uint32_t length)
{
//if buffer is full or no elements should be written, no elements can be written
if ((!_isEmpty && _start == _end) || length <= 0) { return; }
//if _start <= _end, split the free buffer space into two parts
uint32_t firstPartCapacity = (_start <= _end) ? _capacity - _end : _start - _end;
const uint32_t secondPartCapacity = (_start <= _end) ? _start : 0;
//copy first part
CopyMemory(&_buffer[_end], source, std::min(firstPartCapacity, length) * sizeof(T));
//if a second part exists, copy second part
if (length > firstPartCapacity)
{
CopyMemory(&_buffer[0], &source[firstPartCapacity], std::min(secondPartCapacity, length - firstPartCapacity) * sizeof(T));
}
//update buffer positions
_end = (_end + std::min(length, firstPartCapacity + secondPartCapacity)) % _capacity;
_isEmpty = false;
}
/*
* Copys the specified number of elements from the ring buffer into the specified destination array.
* If there are less elements in the buffer than the to read, only available elements will be copied.
* float *destination: The array where to copy the elements from the ring buffer to.
* uint32_t length: The number of elements to copy from the ring buffer into the destination array.
*/
void Read(T* destination, const uint32_t length)
{
if (length <= 0) { return; }
//if _start >= _end, split the read operation into two parts
int firstPartSize = (_start < _end) ? std::min(length, _end - _start) : std::min(length, _capacity - _start);
const int secondPartSize = (_start < _end) ? 0 : std::min(_end, length - firstPartSize);
//copy first part
CopyMemory(destination, &_buffer[_start], firstPartSize * sizeof(T));
//if a second part exists, copy second part
if (secondPartSize > 0) { CopyMemory(&destination[firstPartSize], &_buffer[0], secondPartSize * sizeof(T)); }
//update the buffer positions
_start = (_start + (firstPartSize + secondPartSize)) % _capacity;
if (_start == _end) { _isEmpty = true; }
}
protected:
//the buffer array
T* _buffer = nullptr;
//the number of elements the buffer can contain
uint32_t _capacity = 0;
//the position of the first contained element of the buffer in the internal array
uint32_t _start = 0;
//the position of the first free element of the buffer in the internal array (this position - 1 equals the position of the last contained element of the buffer)
uint32_t _end = 0;
//flag indicating if the buffer is empty. Necessary because when _start == _end it is undefined if the buffer is full or empty.
bool _isEmpty = true;
};
@@ -0,0 +1,999 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkAdjustment" id="adjustment1">
<property name="lower">1</property>
<property name="upper">999</property>
<property name="value">4</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment2">
<property name="upper">100</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment3">
<property name="upper">100</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkDialog" id="dialog-common-gnd-ref">
<property name="can_focus">False</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">Common Ground and Reference</property>
<property name="window_position">center</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="column_spacing">5</property>
<property name="row_spacing">5</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;big&gt;Connect to Common Ground&lt;/big&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
</object>
<packing>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton-gnd-blockA">
<property name="label" translatable="yes">Block A (yellow)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton-gnd-blockB">
<property name="label" translatable="yes">Block B (red)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton-gnd-blockC">
<property name="label" translatable="yes">Block C (blue)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton-gnd-blockD">
<property name="label" translatable="yes">Block D (green)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator4">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkTable" id="table3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="column_spacing">5</property>
<property name="row_spacing">5</property>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;big&gt;Connect to Common Reference&lt;/big&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
</object>
<packing>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton-ref-blockA">
<property name="label" translatable="yes">Block A (yellow)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton-ref-blockB">
<property name="label" translatable="yes">Block B (red)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton-ref-blockC">
<property name="label" translatable="yes">Block C (blue)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton-ref-blockD">
<property name="label" translatable="yes">Block D (green)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkDialog" id="dialog-filters">
<property name="can_focus">False</property>
<property name="border_width">7</property>
<property name="title" translatable="yes">Amplifier Filters</property>
<property name="window_position">center</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;big&gt;Notch filter&lt;/big&gt;
Type - Order - [Low Pass; High Pass] - Frequency</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="combobox-notch">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">liststore1</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator5">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;big&gt;Band Pass filter&lt;/big&gt;
Type - Order - [Low Cut; High Cut] - Frequency</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="combobox-band-pass">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">liststore2</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext2"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkListStore" id="liststore1">
<columns>
<!-- column-name filter -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkListStore" id="liststore2">
<columns>
<!-- column-name filter -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkListStore" id="model1">
<columns>
<!-- column-name gchararray -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">32</col>
</row>
<row>
<col id="0" translatable="yes">64</col>
</row>
<row>
<col id="0" translatable="yes">128</col>
</row>
<row>
<col id="0" translatable="yes">256</col>
</row>
<row>
<col id="0" translatable="yes">512</col>
</row>
<row>
<col id="0" translatable="yes">600</col>
</row>
<row>
<col id="0" translatable="yes">1200</col>
</row>
<row>
<col id="0" translatable="yes">2400</col>
</row>
<row>
<col id="0" translatable="yes">4800</col>
</row>
<row>
<col id="0" translatable="yes">9600</col>
</row>
<row>
<col id="0" translatable="yes">19200</col>
</row>
<row>
<col id="0" translatable="yes">38400</col>
</row>
</data>
</object>
<object class="GtkListStore" id="model2">
<columns>
<!-- column-name gchararray -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">unspecified</col>
</row>
<row>
<col id="0" translatable="yes">female</col>
</row>
<row>
<col id="0" translatable="yes">male</col>
</row>
<row>
<col id="0" translatable="yes">unknown</col>
</row>
</data>
</object>
<object class="GtkListStore" id="model3">
<columns>
<!-- column-name gchararray -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
<property name="can_focus">False</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">Device configuration</property>
<property name="window_position">center</property>
<property name="type_hint">dialog</property>
<property name="gravity">center</property>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK</property>
<property name="spacing">8</property>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="button_apply">
<property name="label">gtk-apply</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_cancel">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">g.Tec gUSBamp</property>
<property name="justify">center</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">8</property>
<property name="spacing">8</property>
<child>
<object class="GtkHBox" id="hbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">8</property>
<child>
<object class="GtkTable" id="table2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">10</property>
<property name="n_columns">2</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment1</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_number_of_channels">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Number of channels :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_master_device">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Master device :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="combobox_sampling_frequency">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">model1</property>
<child>
<object class="GtkCellRendererText" id="renderer1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_sampling_frequency">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Sampling frequency :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="combobox_gender">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">model2</property>
<child>
<object class="GtkCellRendererText" id="renderer2"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="combobox_master_device">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">model3</property>
<child>
<object class="GtkCellRendererText" id="renderer3"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_identifier">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment2</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_age">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment3</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_identifier">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Identifier :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
</child>
<child>
<object class="GtkLabel" id="label_age">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Age :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_gender">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Gender :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton_EventChannel">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_event_channel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Event channel</property>
</object>
<packing>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_bipolar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Bipolar electrodes</property>
</object>
<packing>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton_Bipolar">
<property name="label" translatable="yes">1-2=1,...,15-16=15</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="x_padding">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_Calibrationsignal">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Calibration signal</property>
</object>
<packing>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton_CalibrationSignal">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="x_padding">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label-impedance">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Check impedance :</property>
</object>
<packing>
<property name="top_attach">9</property>
<property name="bottom_attach">10</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton_impedance">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">9</property>
<property name="bottom_attach">10</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton_ShowDeviceName">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">10</property>
<property name="bottom_attach">11</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_show_device_name">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Show device name</property>
</object>
<packing>
<property name="top_attach">10</property>
<property name="bottom_attach">11</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_calibrate">
<property name="label" translatable="yes">Auto calibrate in 4 seconds...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator2">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button-common-gnd-ref">
<property name="label" translatable="yes">Connect to Common Gnd and Ref</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button-filters">
<property name="label" translatable="yes">Set Amplifier Filters</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator3">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_change_channel_names">
<property name="label" translatable="yes">Change channel names...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-10">button_apply</action-widget>
<action-widget response="-6">button_cancel</action-widget>
</action-widgets>
</object>
</interface>
@@ -0,0 +1,401 @@
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI
#include "CConfigurationGTecGUSBamp.hpp"
#include <windows.h>
#include <gUSBamp.h>
#include <iostream>
#include <sstream>
namespace OpenViBE {
namespace AcquisitionServer {
static void ApplyFiltersPressedCB(GtkButton* /*button*/, void* data) { static_cast<CConfigurationGTecGUSBamp*>(data)->buttonFiltersApplyPressedCB(); }
static void CalibratePressedCB(GtkButton* /*button*/, void* data) { static_cast<CConfigurationGTecGUSBamp*>(data)->buttonCalibratePressedCB(); }
static void CommonGndRefPressedCB(GtkButton* /*button*/, void* data) { static_cast<CConfigurationGTecGUSBamp*>(data)->buttonCommonGndRefPressedCB(); }
static void FiltersPressedCB(GtkButton* /*button*/, void* data) { static_cast<CConfigurationGTecGUSBamp*>(data)->buttonFiltersPressedCB(); }
static gboolean idle_calibrate_cb(void* data)
{
static_cast<CConfigurationGTecGUSBamp*>(data)->idleCalibrateCB();
return FALSE;
}
CConfigurationGTecGUSBamp::CConfigurationGTecGUSBamp(const char* gtkBuilderFilename, uint8_t& commonGndAndRefBitmap, int& notchFilterIdx,
int& bandPassFilterIdx, bool& triggerInput, const std::vector<std::string>& devicesSerials,
std::string& masterDeviceIndex, bool& bipolar, bool& calibrationSignalEnabled, bool& showDeviceName)
: CConfigurationBuilder(gtkBuilderFilename), m_commonGndAndRefBitmap(commonGndAndRefBitmap), m_notchFilterIdx(notchFilterIdx),
m_bandPassFilterIdx(bandPassFilterIdx), m_triggerInput(triggerInput), m_devicesSerials(devicesSerials), m_masterDeviceIdx(masterDeviceIndex),
m_bipolarEnabled(bipolar), m_calibrationSignalEnabled(calibrationSignalEnabled), m_showDeviceName(showDeviceName) {}
bool CConfigurationGTecGUSBamp::preConfigure()
{
if (!CConfigurationBuilder::preConfigure()) { return false; }
GtkCheckButton* hardwareTagging = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_EventChannel"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hardwareTagging), m_triggerInput);
GtkCheckButton* bipolar = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_Bipolar"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bipolar), m_bipolarEnabled);
GtkCheckButton* showDeviceName = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_ShowDeviceName"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(showDeviceName), m_showDeviceName);
GtkCheckButton* calibrationMode = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_CalibrationSignal"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(calibrationMode), m_calibrationSignalEnabled);
GtkComboBox* comboBox = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_master_device"));
// Default active device is the last one
if (!m_devicesSerials.empty()) { gtk_combo_box_set_active(comboBox, m_devicesSerials.size() - 1); }
// If a device is already set, try to use that as the combo box selection; if not found, use last (set above).
int masterIndex = int(m_devicesSerials.size()) - 1;
for (size_t i = 0; i < m_devicesSerials.size(); ++i)
{
gtk_combo_box_append_text(comboBox, m_devicesSerials[i].c_str());
if (this->m_masterDeviceIdx == m_devicesSerials[i]) { masterIndex = i; }
}
if (masterIndex >= 0) { gtk_combo_box_set_active(comboBox, masterIndex); }
// Sets the channel limits depending on the number of amps
GtkSpinButton* numChannels = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder, "spinbutton_number_of_channels"));
gtk_spin_button_set_range(numChannels, 1, m_devicesSerials.size() * 16); // GTEC_NUM_CHANNELS
const size_t count = this->m_devicesSerials.size();
/*
char buffer[1024];
int count=0;
bool selected=false;*/
// autodetection of the connected device
/*for (uint32_t i=1; i<11; ++i)
{
::HANDLE handle=::GT_OpenDevice(i);
if(handle)
{
::GT_CloseDevice(&handle);
sprintf(buffer, "USB port %i", i);
::gtk_combo_box_append_text(comboBox, buffer);
if(m_usbIdx==i)
{
::gtk_combo_box_set_active(comboBox, count);
selected=true;
}
count++;
}
}
*/
//if(!selected && count!=0) { ::gtk_combo_box_set_active(comboBox, 0); }
g_signal_connect(gtk_builder_get_object(m_builder, "button_calibrate"), "pressed", G_CALLBACK(CalibratePressedCB), this);
g_signal_connect(gtk_builder_get_object(m_builder, "button-common-gnd-ref"), "pressed", G_CALLBACK(CommonGndRefPressedCB), this);
g_signal_connect(gtk_builder_get_object(m_builder, "button-filters"), "pressed", G_CALLBACK(FiltersPressedCB), this);
GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(m_builder, "dialog-common-gnd-ref"));
gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_APPLY, GTK_RESPONSE_APPLY);
gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
dialog = GTK_WIDGET(gtk_builder_get_object(m_builder, "dialog-filters"));
GtkWidget* buttonApplyFilters = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_APPLY, GTK_RESPONSE_APPLY);
g_signal_connect(buttonApplyFilters, "pressed", G_CALLBACK(ApplyFiltersPressedCB), this);
gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
GtkToggleButton* checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockA"));
gtk_toggle_button_set_active(checkBox, (m_commonGndAndRefBitmap & 1));
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockB"));
gtk_toggle_button_set_active(checkBox, (m_commonGndAndRefBitmap & (1 << 1)));
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockC"));
gtk_toggle_button_set_active(checkBox, (m_commonGndAndRefBitmap & (1 << 2)));
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockD"));
gtk_toggle_button_set_active(checkBox, (m_commonGndAndRefBitmap & (1 << 3)));
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockA"));
gtk_toggle_button_set_active(checkBox, (m_commonGndAndRefBitmap & (1 << 4)));
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockB"));
gtk_toggle_button_set_active(checkBox, (m_commonGndAndRefBitmap & (1 << 5)));
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockC"));
gtk_toggle_button_set_active(checkBox, (m_commonGndAndRefBitmap & (1 << 6)));
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockD"));
gtk_toggle_button_set_active(checkBox, (m_commonGndAndRefBitmap & (1 << 7)));
if (count == 0)
{
// deactivate the buttons
GtkWidget* button = GTK_WIDGET(gtk_builder_get_object(m_builder, "button-filters"));
gtk_widget_set_sensitive(button, false);
button = GTK_WIDGET(gtk_builder_get_object(m_builder, "button-common-gnd-ref"));
gtk_widget_set_sensitive(button, false);
}
return true;
}
bool CConfigurationGTecGUSBamp::postConfigure()
{
GtkComboBox* comboBox = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_master_device"));
if (m_applyConfig)
{
/*
int usbIdx=0;
const char* usbIdx=::gtk_combo_box_get_active_text(comboBox);
if(usbIdx) { if(sscanf(usbIdx, "USB port %i", &usbIdx)==1) { m_usbIdx=(uint32_t)usbIdx; } }
*/
if (this->m_devicesSerials.size() > 1)
{
char* selectedSerial = gtk_combo_box_get_active_text(comboBox);
m_masterDeviceIdx = (selectedSerial == nullptr) ? "" : selectedSerial;
}
GtkToggleButton* checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockA"));
m_commonGndAndRefBitmap = (gtk_toggle_button_get_active(checkBox) ? 1 : 0);
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockB"));
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(checkBox) ? (1 << 1) : 0);
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockC"));
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(checkBox) ? (1 << 2) : 0);
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockD"));
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(checkBox) ? (1 << 3) : 0);
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockA"));
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(checkBox) ? (1 << 4) : 0);
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockB"));
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(checkBox) ? (1 << 5) : 0);
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockC"));
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(checkBox) ? (1 << 6) : 0);
checkBox = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockD"));
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(checkBox) ? (1 << 7) : 0);
GtkComboBox* notch = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox-notch"));
if (gtk_combo_box_get_active(notch) >= 0)
{
// Only update the filter index if the user chose something. This is needed so going to the Configuration menu doesn't
// change previous filter choices.
m_notchFilterIdx = ((gtk_combo_box_get_active(notch) == 0) ? -1 : m_comboBoxNotchFilterIdx[gtk_combo_box_get_active(notch) - 1]
); //-1 because there is one more in the beginning
}
GtkComboBox* bandPass = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox-band-pass"));
if (gtk_combo_box_get_active(bandPass) >= 0)
{
// Only update the filter index if the user chose something -1 because there is one more in the beginning
m_bandPassFilterIdx = ((gtk_combo_box_get_active(bandPass) == 0) ? -1 : int(m_comboBoxBandPassFilterIdx[gtk_combo_box_get_active(bandPass) - 1]));
}
GtkCheckButton* hardwareTagging = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_EventChannel"));
m_triggerInput = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hardwareTagging)) ? true : false);
GtkCheckButton* showDeviceName = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_ShowDeviceName"));
m_showDeviceName = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(showDeviceName)) ? true : false);
GtkCheckButton* bipolar = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_Bipolar"));
m_bipolarEnabled = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bipolar)) ? true : false);
GtkCheckButton* calibrationSignal = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_CalibrationSignal"));
m_calibrationSignalEnabled = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(calibrationSignal)) ? true : false);
}
if (!CConfigurationBuilder::postConfigure()) { return false; }
return true;
}
void CConfigurationGTecGUSBamp::buttonCalibratePressedCB()
{
g_idle_add(idle_calibrate_cb, this);
m_calibrateDialog = gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, "Calibrating...");
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(m_calibrateDialog), "Please wait a few seconds...");
gtk_dialog_run(GTK_DIALOG(m_calibrateDialog));
gtk_widget_destroy(m_calibrateDialog);
if (m_calibrationDone)
{
GtkWidget* dialog = gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Calibration finished !");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
else
{
GtkWidget* dialog = gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Calibration failed !");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
}
void CConfigurationGTecGUSBamp::idleCalibrateCB()
{
GtkComboBox* comboBox = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_master_device"));
m_calibrationDone = false;
//calibrate all detected devices
for (uint32_t i = 0; i < this->m_devicesSerials.size(); ++i)
{
LPSTR serial = const_cast<char*>(m_devicesSerials[i].c_str());
HANDLE handle = GT_OpenDeviceEx(serial);
if (handle)
{
m_calibrationDone = true;
SCALE calibration;
if (!GT_Calibrate(handle, &calibration)) { std::cout << "err GT_Calibrate\n", m_calibrationDone = false; }
if (!GT_SetScale(handle, &calibration))
{
std::cout << "err GT_SetScale\n";
m_calibrationDone = false;
}
GT_CloseDevice(&handle);
}
}
gtk_dialog_response(GTK_DIALOG(m_calibrateDialog), 0);
}
void CConfigurationGTecGUSBamp::buttonCommonGndRefPressedCB()
{
GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(m_builder, "dialog-common-gnd-ref"));
gint resp = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_hide(dialog);
}
void CConfigurationGTecGUSBamp::buttonFiltersPressedCB()
{
GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(m_builder, "dialog-filters"));
setHardwareFiltersDialog();
gint resp = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_hide(dialog);
}
void CConfigurationGTecGUSBamp::setHardwareFiltersDialog()
{
m_comboBoxBandPassFilterIdx.clear();
m_comboBoxNotchFilterIdx.clear();
GtkComboBox* comboBoxBandPass = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox-band-pass"));
GtkTreeModel* bandPassListStore = gtk_combo_box_get_model(comboBoxBandPass);
gtk_list_store_clear(GTK_LIST_STORE(bandPassListStore));
GtkComboBox* comboBoxNotch = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox-notch"));
GtkTreeModel* notchListStore = gtk_combo_box_get_model(comboBoxNotch);
gtk_list_store_clear(GTK_LIST_STORE(notchListStore));
// To check for available filters in the amplifier, we must connect to it.
if (!m_devicesSerials[0].empty())
{
LPSTR serial = const_cast<char*>(m_devicesSerials[0].c_str());
HANDLE handle = GT_OpenDeviceEx(serial);
int nBandPassFilters, nNotchFilters;
if (!GT_GetNumberOfFilter(&nBandPassFilters)) { std::cout << "err GT_GetNumberOfFilter\n"; }
if (nBandPassFilters == 0) { std::cout << "err No band pass filters found at all!\n"; }
if (!GT_GetNumberOfNotch(&nNotchFilters)) { std::cout << "err GT_GetNumberOfNotch\n"; }
if (nNotchFilters == 0) { std::cout << "err No notch filters found at all!\n"; }
FILT* bpFilterSpec = new FILT[nBandPassFilters];
FILT* notchFilterSpec = new FILT[nNotchFilters];
if (!GT_GetFilterSpec(bpFilterSpec)) { std::cout << "err GT_GetFilterSpec\n"; }
if (!GT_GetNotchSpec(notchFilterSpec)) { std::cout << "err GT_GetNotchSpec\n"; }
//Set BandPass filter list
std::stringstream desc;
desc << "no band pass filter.";
GtkTreeIter it;
gtk_list_store_append(GTK_LIST_STORE(bandPassListStore), &it);
gtk_list_store_set(GTK_LIST_STORE(bandPassListStore), &it, 0, desc.str().c_str(), -1);
desc.clear();
desc.str("");
gchar* sSampling = gtk_combo_box_get_active_text(GTK_COMBO_BOX(m_sampling));
const uint32_t sampling = (sSampling ? atoi(sSampling) : 0);
int cbBandPassSelectedIdx = -1;
// std::cout << "The device reports " << nBandPassFilters << " band pass filters and " << nNotchFilters << " notch filters\n";
if (nBandPassFilters > 0)
{
for (int i = 0; i < nBandPassFilters; ++i)
{
if (sampling == uint32_t(bpFilterSpec[i].fs))
{
if (bpFilterSpec[i].type == 1) { desc << "Butterworth - "; }
if (bpFilterSpec[i].type == 2) { desc << "Chebyshev - "; }
desc << bpFilterSpec[i].order << " - [" << bpFilterSpec[i].fu << "; " << bpFilterSpec[i].fo << "] - " << bpFilterSpec[i].fs;
GtkTreeIter iter;
gtk_list_store_append(GTK_LIST_STORE(bandPassListStore), &iter);
gtk_list_store_set(GTK_LIST_STORE(bandPassListStore), &iter, 0, desc.str().c_str(), -1);
desc.clear();
desc.str("");
m_comboBoxBandPassFilterIdx.push_back(i);
//here a previous selection is loaded
if (cbBandPassSelectedIdx == -1 && i == m_bandPassFilterIdx) { cbBandPassSelectedIdx = int(m_comboBoxBandPassFilterIdx.size()) - 1; }
}
}
}
gtk_combo_box_set_active(comboBoxBandPass, cbBandPassSelectedIdx + 1); // +1 because -1 is for "no filter".
//Set Notch filter List
desc << "no notch filter.";
gtk_list_store_append(GTK_LIST_STORE(notchListStore), &it);
gtk_list_store_set(GTK_LIST_STORE(notchListStore), &it, 0, desc.str().c_str(), -1);
desc.clear();
desc.str("");
int cbNotchSelectedIndex = -1;
if (nNotchFilters > 0)
{
for (int i = 0; i < nNotchFilters; ++i)
{
if (sampling == int(notchFilterSpec[i].fs))
{
if (notchFilterSpec[i].type == 1) { desc << "Butterworth - "; }
if (notchFilterSpec[i].type == 2) { desc << "Chebyshev - "; }
desc << notchFilterSpec[i].order << " - [" << notchFilterSpec[i].fu << "; " << notchFilterSpec[i].fo << "] - " << notchFilterSpec[i].fs;
GtkTreeIter iter;
gtk_list_store_append(GTK_LIST_STORE(notchListStore), &iter);
gtk_list_store_set(GTK_LIST_STORE(notchListStore), &iter, 0, desc.str().c_str(), -1);
desc.clear();
desc.str("");
m_comboBoxNotchFilterIdx.push_back(i);
//here a previous selection is loaded
if (cbNotchSelectedIndex == -1 && i == m_notchFilterIdx) { cbNotchSelectedIndex = int(m_comboBoxNotchFilterIdx.size()) - 1; }
}
}
}
gtk_combo_box_set_active(comboBoxNotch, cbNotchSelectedIndex + 1); // +1 because -1 is for "no filter".
delete bpFilterSpec;
delete notchFilterSpec;
GT_CloseDevice(&handle);
}
}
void CConfigurationGTecGUSBamp::buttonFiltersApplyPressedCB()
{
GtkComboBox* comboBoxBandPass = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox-band-pass"));
//-1 because there is one more in the beginning
m_bandPassFilterIdx = (gtk_combo_box_get_active(comboBoxBandPass) == -1 || gtk_combo_box_get_active(comboBoxBandPass) == 0) ? -1
: int(m_comboBoxBandPassFilterIdx[gtk_combo_box_get_active(comboBoxBandPass) - 1]);
GtkComboBox* comboBoxNotch = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox-notch"));
//-1 because there is one more in the beginning
m_notchFilterIdx = (gtk_combo_box_get_active(comboBoxNotch) == -1 || gtk_combo_box_get_active(comboBoxNotch) == 0) ? -1
: int(m_comboBoxNotchFilterIdx[gtk_combo_box_get_active(comboBoxNotch) - 1]);
}
} // namespace AcquisitionServer
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI
@@ -0,0 +1,53 @@
#pragma once
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI
#include "../ovasCConfigurationBuilder.h"
#include <gtk/gtk.h>
#include <string>
#include <vector>
namespace OpenViBE
{
namespace AcquisitionServer
{
class CConfigurationGTecGUSBamp final : public CConfigurationBuilder
{
public:
CConfigurationGTecGUSBamp(const char* gtkBuilderFilename, uint8_t& commonGndAndRefBitmap, int& notchFilterIdx, int& bandPassFilterIdx,
bool& triggerInput, const std::vector<std::string>& devicesSerials, std::string& masterDeviceIndex, bool& bipolar,
bool& calibrationSignalEnabled, bool& showDeviceName);
bool preConfigure() override;
bool postConfigure() override;
void buttonCalibratePressedCB();
void idleCalibrateCB();
void buttonCommonGndRefPressedCB();
void buttonFiltersPressedCB();
void setHardwareFiltersDialog();
void buttonFiltersApplyPressedCB();
protected:
uint8_t& m_commonGndAndRefBitmap;
int& m_notchFilterIdx;
int& m_bandPassFilterIdx;
bool& m_triggerInput;
std::vector<std::string> m_devicesSerials;
std::string& m_masterDeviceIdx;
std::vector<size_t> m_comboBoxBandPassFilterIdx;
std::vector<size_t> m_comboBoxNotchFilterIdx;
bool& m_bipolarEnabled;
bool& m_calibrationSignalEnabled;
bool& m_showDeviceName;
GtkWidget* m_calibrateDialog = nullptr;
bool m_calibrationDone = false;
};
} // namespace AcquisitionServer
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI
@@ -0,0 +1,183 @@
#pragma once
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI
#include "ovasIDriver.h"
#include "../ovasCHeader.h"
#include "../ovasCSettingsHelper.h"
#include "../ovasCSettingsHelperOperators.h"
#include <Windows.h>
#include "ringbuffer.h"
#include <gtk/gtk.h>
#include <vector>
//threading
#include <thread>
#include <mutex>
#include <condition_variable>
#include <memory> // unique_ptr
#include <deque>
namespace OpenViBE
{
namespace AcquisitionServer
{
/**
* \class CDriverGTecGUSBamp
* \author Anton Andreev, Gipsa-lab, VIBS team
* \date 19/07/2012
* \brief GTEC driver
*
* This driver was rewritten to match the code provided by Guger as much as possible. There are several things
* that all must work together so that higher frequencies are supported and no hardware triggers are lost.
*
* This driver supports several buffers so that the more than one GT_GetData can be executed in the beginning (QUEUE_SIZE)
* and then calls to GT_GetData are queued. This allows data to be processed by OpenVibe while waiting for the next result of
* a previously issued GT_GetData. The extra thread is added to support this and to allow for async IO.
*
* Hardware triggers on the parallel port are supported.
*
* The driver supports several g.tec devices working with the provided async cables. There are several requirements for async
* acquisition to work properly and these are checked in verifySyncMode().
*/
#ifndef __GDEVICE_H__
#define __GDEVICE_H__
struct GDevice
{
HANDLE handle;
std::string serial;
};
#endif
class CDriverGTecGUSBamp final : public IDriver
{
public:
explicit CDriverGTecGUSBamp(IDriverContext& ctx);
void release() { delete this; }
const char* getName() override { return "g.tec gUSBamp Gipsa-lab"; }
bool initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback) override;
bool uninitialize() override;
bool start() override;
bool stop() override;
bool loop() override;
bool isConfigurable() override { return true; }
bool configure() override;
const IHeader* getHeader() override { return &m_header; }
bool CDriverGTecGUSBamp::acquire();
void configFiltering(HANDLE device);
protected:
static const int BUFFER_SIZE_SECONDS = 2; //the size of the GTEC ring buffer in seconds
static const int GTEC_NUM_CHANNELS = 16; //the number of channels without countig the trigger channel
static const int QUEUE_SIZE =
8; //4 default //the number of GT_GetData calls that will be queued during acquisition to avoid loss of data
static const int NUMBER_OF_SCANS =
32; //the number of scans that should be received simultaneously (depending on the _sampleRate; see C-API documentation for this value!)
size_t numDevices() const { return m_devices.size(); }
static const uint32_t N_POINTS = NUMBER_OF_SCANS * (GTEC_NUM_CHANNELS + 1);
int m_validPoints = 0;
static const DWORD BUFFER_SIZE_BYTES;
SettingsHelper m_settings;
IDriverCallback* m_callback = nullptr;
CHeader m_header;
float* m_sample = nullptr;
uint32_t m_nSamplePerSentBlock = 0;
uint32_t m_globalImpedanceIdx = 0;
uint8_t m_commonGndAndRefBitmap = 0;
int m_notchFilterIdx = -1;
int m_bandPassFilterIdx = -1;
bool m_triggerInputEnabled = false;
bool m_bipolarEnabled =
false; //electrodes are substracted in sepecific sequence 1-2=1, ... 15-16=15 which results in 8 instead of 16 electrodes - used for EMG
bool m_calibrationSignalEnabled = false;
bool m_showDeviceName = false; //adds the amplifier serial number to the name of the channel
bool m_reconfigurationRequired = false; // After some gt calls, we may need reconfig
uint32_t m_nAcquiredChannel = GTEC_NUM_CHANNELS; //number of channels specified by the user, never counts the event channels
uint32_t m_totalHardwareStimulations = 0; //since start button clicked
uint32_t m_totalDriverChunksLost = 0; //since start button clicked
uint32_t m_totalDriverTimeouts = 0; //since start button clicked
uint32_t m_totalRingBufferOverruns = 0;
uint32_t m_totalDataUnavailable = 0;
//contains buffer per device and then QUEUE_SIZE buffers so that several calls to GT_GetData can be supported
BYTE*** m_buffers = nullptr;
OVERLAPPED** m_overlapped = nullptr;
bool m_flagIsFirstLoop = true;
bool m_bufferOverrun = false;
//ring buffer provided by Guger
CRingBuffer<float> m_ringBuffer;
uint32_t m_currentQueueIdx = 0;
std::unique_ptr<std::thread> m_threadPtr;
bool m_isThreadRunning = false;
std::mutex m_io_mutex;
float* m_bufferReceivedData = nullptr;
std::condition_variable m_itemAvailable;
bool configureDevice(uint32_t deviceNumber);
bool verifySyncMode();//Checks if devices are configured correctly when acquiring data from multiple devices
//Selects which device to become the new master, used only when more than 1 device is available
bool setMasterDevice(const std::string& targetMasterSerial); //0 first device
void detectDevices();
uint32_t m_mastersCnt = 0;
uint32_t m_slavesCnt = 0;
std::string m_masterSerial = "";
void remapChannelNames(); // Converts channel names while appending the device name and handling event channels
void restoreChannelNames(); // Restores channel names without the device name
std::vector<std::string> m_originalChannelNames; // Channel names without the device name inserted
std::vector<GDevice> m_devices; // List of amplifiers
std::string CDriverGTecGUSBamp::getSerialByHandler(HANDLE device);
// Stores information related to each channel available in the recording system
struct SChannel
{
int idx; // Channel index in openvibe Designer, -1 is unused
int oldIdx; // Channel index in the user-settable channel name list
int gtecDeviceIdx; // Device index of the gtec amplifier this channel is in
int gtecChannelIdx; // Channel index in the device-specific numbering
bool isEventChannel; // Is this the special digital channel?
};
// Channel indexes are seen as a sequence [dev1chn1, dev1chn2,...,dev1chnN, dev2chn1, dev2chn2, ..., dev2chnN, ...]
// The following vector is used to map these 'system indexes' to openvibe channels
std::vector<SChannel> m_channels;
};
} // namespace AcquisitionServer
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI
@@ -0,0 +1,458 @@
/**
* Software License Agreement (AGPL-3 License)
*
* \file CDriverGTecUnicorn.cpp
* \author Anton Andreev, Gipsa-lab, VIBS team
* \date 21/08/2020
* \brief Implementation of GTEC Unicorn Black Driver
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#if defined TARGET_HAS_ThirdPartyGtecUnicron
#include "CDriverGTecUnicorn.hpp"
#include <toolkit/ovtk_all.h>
#include <system/ovCTime.h>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <limits>
#include <mutex>
#include <thread>
#include "unicorn.h"
namespace OpenViBE {
namespace AcquisitionServer {
#if defined(TARGET_OS_Windows)
#pragma warning(disable: 4800) // disable "forcing value to bool 'true' or 'false' (performance warning)" nag coming from BOOL->bool cast on e.g. VS2010
#endif
CDriverGTecUnicorn::CDriverGTecUnicorn(IDriverContext& rDriverContext)
: IDriver(rDriverContext), m_settings("AcquisitionServer_Driver_GTecGUSBamp", m_driverCtx.getConfigurationManager())
{
m_header.setSamplingFrequency(UNICORN_SAMPLING_RATE);
m_header.setChannelCount(0);
//m_settings.load();
}
//___________________________________________________________________//
// //
bool CDriverGTecUnicorn::initialize(
const uint32_t sampleCountPerSentBlock,
IDriverCallback& callback)
{
if (m_driverCtx.isConnected()) { return false; }
detectDevices();
if (numDevices() == 0) { return false; }
m_sampleCountPerSentBlock = sampleCountPerSentBlock;
m_callback = &callback;
// Set number of channels
int errorCode = UNICORN_GetNumberOfAcquiredChannels(m_devices[kSelectedDevice].handle, &m_acquiredChannelCount);
if (errorCode != UNICORN_ERROR_SUCCESS)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unable to get channel count.\n";
return false;
}
else { m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Number of available channels is: " << m_acquiredChannelCount << "\n"; }
m_header.setChannelCount(m_acquiredChannelCount); //only 8 channels are EEG, but currently we provide everything
m_lengthBufferRawUnicornDevice = m_acquiredChannelCount * kFrameLength;
/*
* Set channel names
* 17 channels from Unicorn Black on every sample: | EEG1| EEG2| EEG3| EEG4| EEG5| EEG6| EEG7| EEG8| ACCX|ACCY| ACCZ| GYRX|GYRY| GYRZ|CNT|BATLVL|VALID|
* Order might be different. This is why we need to use UNICORN_GetChannelIndex(channel name);
*/
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 1"), "EEG1");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 2"), "EEG2");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 3"), "EEG3");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 4"), "EEG4");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 5"), "EEG5");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 6"), "EEG6");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 7"), "EEG7");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 8"), "EEG8");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Accelerometer X"), "ACCX");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Accelerometer Y"), "ACCY");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Accelerometer Z"), "ACCZ");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Gyroscope X"), "GYRX");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Gyroscope Y"), "GYRY");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Gyroscope Z"), "GYRZ");
m_channelCounterIndex = getChannelIndex(m_devices[kSelectedDevice].handle, "Counter");
m_header.setChannelName(m_channelCounterIndex, "Counter");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Battery Level"), "Battery");
m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Validation Indicator"), "VI");
// Initialize buffers
m_bufferReceivedDataFromRing = new float[m_lengthBufferRawUnicornDevice];
m_bufferForOpenVibe = new float[m_lengthBufferRawUnicornDevice];
m_ringBuffer.Initialize(static_cast<uint32_t>(kBufferSizeSeconds * m_header.getSamplingFrequency() * m_lengthBufferRawUnicornDevice));
// Configure each device
for (size_t i = 0; i < numDevices(); i++) { configureDevice(i); }
return true;
}
void CDriverGTecUnicorn::detectDevices()
{
int errorCode = UNICORN_ERROR_SUCCESS;
// Get number of available devices
unsigned int availableDevicesCount = 0;
errorCode = UNICORN_GetAvailableDevices(NULL, &availableDevicesCount, TRUE);
// Get serials of available devices
UNICORN_DEVICE_SERIAL* availableDevices = new UNICORN_DEVICE_SERIAL[availableDevicesCount];
errorCode = UNICORN_GetAvailableDevices(availableDevices, &availableDevicesCount, TRUE);
if (errorCode != UNICORN_ERROR_SUCCESS || availableDevicesCount < 1)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "No device available. Please pair with a Unicorn device first.\n";
}
else
{
// Create a GDevice list for all devices and print available device serials
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Available Unicorn devices:\n";
for (unsigned int i = 0; i < availableDevicesCount; i++)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "#" << i << ": " << availableDevices[i] << "\n";
GDevice device;
UNICORN_HANDLE deviceHandle;
int errorCode = UNICORN_OpenDevice(availableDevices[i], &deviceHandle);
if (errorCode != UNICORN_ERROR_SUCCESS)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unable to connect to device: '" << availableDevices[i] << "'\n";
}
device.handle = deviceHandle;
device.serial = availableDevices[i];
m_devices.push_back(device);
}
}
}
bool CDriverGTecUnicorn::configureDevice(size_t deviceNumber)
{
int errorCode = UNICORN_ERROR_SUCCESS;
UNICORN_HANDLE device = m_devices[deviceNumber].handle;
const std::string currentSerial = m_devices[deviceNumber].serial;
UNICORN_AMPLIFIER_CONFIGURATION configuration;
errorCode = UNICORN_GetConfiguration(device, &configuration);
if (errorCode != UNICORN_ERROR_SUCCESS)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unable to get configuration for: '" << currentSerial.c_str() << "'\n";
return false;
}
return true;
}
bool CDriverGTecUnicorn::start()
{
if (!m_driverCtx.isConnected()) { return false; }
if (m_driverCtx.isStarted()) { return false; }
m_totalHardwareStimulations = 0;
m_totalRingBufferOverruns = 0;
m_totalCounterErrors = 0;
{
std::lock_guard<std::mutex> lock(m_mutex);
m_ringBuffer.Reset();
}
for (size_t i = 0; i < numDevices(); i++)
{
UNICORN_HANDLE device = m_devices[i].handle;
UNICORN_StartAcquisition(device, kTestSignalEnabled);
}
m_isThreadRunning = true;
m_flagIsFirstLoop = true;
m_bufferOverrun = false;
m_thread.reset(new std::thread(std::bind(&CDriverGTecUnicorn::acquire, this)));
return true;
}
// This method is called by the AS and it supplies the acquired data to the AS
bool CDriverGTecUnicorn::loop()
{
if (m_driverCtx.isStarted())
{
{
std::unique_lock<std::mutex> lock(m_mutex);
while (m_ringBuffer.GetSize() < static_cast<int>(m_lengthBufferRawUnicornDevice)) { m_itemAvailable.wait(lock); }
try
{
if (m_bufferOverrun)
{
m_ringBuffer.Reset();
m_bufferOverrun = false;
m_totalRingBufferOverruns++;
return true;
}
m_ringBuffer.Read(m_bufferReceivedDataFromRing, m_lengthBufferRawUnicornDevice);
}
catch (std::exception& e)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error reading GTEC ring buffer! Error is:" << e.what() << "\n";
}
m_itemAvailable.notify_one();
}
// covert to openvibe format ch1,ch1,ch1,ch2,ch2,ch2 ...
for (size_t i = 0; i < m_acquiredChannelCount; i++)
{
for (size_t j = 0; j < kFrameLength; j++)
{
m_bufferForOpenVibe[kFrameLength * i + j] = m_bufferReceivedDataFromRing[j * m_acquiredChannelCount + i];
}
}
// verify counter
const size_t counterPos = kFrameLength * m_channelCounterIndex;
for (size_t i = counterPos; i < kFrameLength; i++) { if (!(m_bufferForOpenVibe[i] < m_bufferForOpenVibe[i + 1])) { m_totalCounterErrors++; } }
// forward data
m_callback->setSamples(m_bufferForOpenVibe, kFrameLength);
CStimulationSet stimulationSet;
m_callback->setStimulationSet(stimulationSet);
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
}
else { System::Time::sleep(20); }
return true;
}
// This function used by the thread
bool CDriverGTecUnicorn::acquire()
{
if (m_flagIsFirstLoop) //First time do some memory initialization, etc
{
m_bufferRawUnicornDevice = new float[m_lengthBufferRawUnicornDevice];
m_flagIsFirstLoop = false;
}
while (m_isThreadRunning == true)
{
try
{
bool flagChunkLostDetected = false;
bool flagChunkTimeOutDetected = false;
UNICORN_HANDLE device = m_devices[kSelectedDevice].handle;
// Get kFrameLength number of samples
if (UNICORN_GetData(device, kFrameLength, m_bufferRawUnicornDevice, m_lengthBufferRawUnicornDevice * sizeof(float)) != UNICORN_ERROR_SUCCESS)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error on GT_GetData\n";
return false;
}
// store to ring buffer, lock it during insertion
{
std::lock_guard<std::mutex> lock(m_mutex);
try
{
// if we are going to overrun on writing the received data into the buffer, set the appropriate flag; the reading thread will handle the overrun
m_bufferOverrun = (m_ringBuffer.GetFreeSize() < static_cast<int>(m_lengthBufferRawUnicornDevice));
m_ringBuffer.Write(m_bufferRawUnicornDevice, m_lengthBufferRawUnicornDevice);
}
catch (std::exception& e)
{
// buffer should be unclocked automatically once the scope is left
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error writing to GTEC ring buffer! Error is: " << e.what() << "\n";
}
// buffer should be unclocked automatically once the scope is left
m_itemAvailable.notify_one();
}
}
catch (std::exception& e)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error <<
"General error in the thread function acquiring data from GTEC! Acquisition interrupted. Error is: " << e.what() << "\n";
m_isThreadRunning = false;
return false;
}
}
// This code stops the amplifiers in the same thread:
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Stopping devices and cleaning up...\n";
// clean up allocated resources for each device
for (size_t i = 0; i < numDevices(); i++)
{
UNICORN_HANDLE device = m_devices[i].handle;
// stop device
m_driverCtx.getLogManager() << Kernel::LogLevel_Debug << "Sending stop command ...\n";
if (UNICORN_StopAcquisition(device) != UNICORN_ERROR_SUCCESS)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Stopping device failed! Serial = " << m_devices[numDevices() - 1].serial.c_str() << "\n";
}
// clear memory
delete[] m_bufferRawUnicornDevice;
}
m_flagIsFirstLoop = true;
m_isThreadRunning = false;
}
return true;
}
bool CDriverGTecUnicorn::stop()
{
if (!m_driverCtx.isConnected()) { return false; }
if (!m_driverCtx.isStarted()) { return false; }
// stop thread
m_isThreadRunning = false;
m_thread->join(); //wait until the thread has stopped data acquisition
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Data acquisition completed.\n";
if (m_totalRingBufferOverruns > 0)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Total internal ring buffer overruns: " << m_totalRingBufferOverruns << "\n";
}
if (m_totalCounterErrors > 0) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Total Unicorn counter errors: " << m_totalCounterErrors << "\n"; }
else { m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Total Unicorn counter errors: " << m_totalCounterErrors << "\n"; }
return true;
}
bool CDriverGTecUnicorn::uninitialize()
{
if (!m_driverCtx.isConnected()) { return false; }
if (m_driverCtx.isStarted()) { return false; }
const size_t totalDevices = numDevices();
size_t deviceClosed = 0;
for (std::vector<GDevice>::iterator it = m_devices.begin(); it != m_devices.end();)
{
if (UNICORN_CloseDevice(&it->handle) != UNICORN_ERROR_SUCCESS)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unable to close device: " << it->serial.c_str() << "\n";
}
else { deviceClosed++; }
it = m_devices.erase(it);
}
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Total devices closed: " << deviceClosed << " / " << totalDevices << "\n";
if (!m_devices.empty() || deviceClosed != totalDevices)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Some devices were not closed properly!\n";
}
m_devices.clear();
// clear memory
if (m_bufferReceivedDataFromRing != nullptr)
{
delete[] m_bufferReceivedDataFromRing;
m_bufferReceivedDataFromRing = nullptr;
}
if (m_bufferForOpenVibe != nullptr)
{
delete[] m_bufferForOpenVibe;
m_bufferForOpenVibe = nullptr;
}
m_callback = nullptr;
return true;
}
//___________________________________________________________________//
// //
inline size_t CDriverGTecUnicorn::getChannelIndex(UNICORN_HANDLE device, const char* name)
{
uint32_t* result = new uint32_t[1];
if (UNICORN_GetChannelIndex(device, name, result) != UNICORN_ERROR_SUCCESS)
{
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error getting channel index for channel: '" << name << "'\n";
}
return static_cast<size_t>(*result);
}
inline std::ostream& operator<<(std::ostream& out, const std::vector<std::string>& var)
{
for (size_t i = 0; i < var.size(); i++) { out << var[i] << " "; }
return out;
}
inline std::istream& operator>>(std::istream& in, std::vector<std::string>& var)
{
var.clear();
std::string tmp;
while (in >> tmp) { var.push_back(tmp); }
return in;
}
inline std::ostream& operator<<(std::ostream& out, const std::vector<GDevice>& var)
{
for (size_t i = 0; i < var.size(); i++) { out << var[i].serial << " "; }
return out;
}
inline std::istream& operator>>(std::istream& in, std::vector<GDevice>& var)
{
// "Error not implemented operator >>!";
return in;
}
} // namespace AcquisitionServer
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI
@@ -0,0 +1,144 @@
/**
* Software License Agreement (AGPL-3 License)
*
* \file CDriverGTecUnicorn.hpp
* \author Anton Andreev, Gipsa-lab, VIBS team
* \date 21/08/2020
* \brief GTEC Unicorn Black Driver
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if defined TARGET_HAS_ThirdPartyGtecUnicron
#include "ovasIDriver.h"
#include "../ovasCHeader.h"
#include "../ovasCSettingsHelper.h"
#include "../ovasCSettingsHelperOperators.h"
#include <Windows.h>
#include "ringbuffer.h"
#include <vector>
//threading
#include <thread>
#include <mutex>
#include <condition_variable>
#include <memory> // unique_ptr
#include <deque>
#include "unicorn.h"
namespace OpenViBE {
namespace AcquisitionServer {
#ifndef __GDEVICE_H__
#define __GDEVICE_H__
struct GDevice
{
UNICORN_HANDLE handle;
std::string serial;
};
#endif
class CDriverGTecUnicorn : public OpenViBE::AcquisitionServer::IDriver
{
public:
CDriverGTecUnicorn(OpenViBE::AcquisitionServer::IDriverContext& rDriverContext);
void release() { delete this; };
const char* getName() override { return "g.tec Unicorn Gipsa-lab"; };
bool initialize(const uint32_t sampleCountPerSentBlock, OpenViBE::AcquisitionServer::IDriverCallback& callback) override;
bool uninitialize() override;
bool start() override;
bool stop() override;
bool loop() override;
bool isConfigurable() override { return false; }
bool configure() override { return true; };
const OpenViBE::AcquisitionServer::IHeader* getHeader() override { return &m_header; }
bool acquire();
bool isFlagSet(const EDriverFlag flag) const override { return flag == EDriverFlag::IsUnstable; }
protected:
static const uint64_t kBufferSizeSeconds = 2; // The size of the GTEC ring buffer in seconds
static const uint32_t kGTecNumChannels = 17; // The number of channels
static const uint32_t kFrameLength = 8; // The number of samples acquired per Get_Data() call and the number of samples supplied to OpenVibe (per loop)
static const bool kTestSignalEnabled = FALSE; // Flag to enable or disable testsignal.
static const size_t kSelectedDevice = 0; // If several Unicorn devices are in range, the first one will be automatically selected
SettingsHelper m_settings;
OpenViBE::AcquisitionServer::IDriverCallback* m_callback = nullptr;
OpenViBE::AcquisitionServer::CHeader m_header;
size_t m_sampleCountPerSentBlock = 0;
// START declaration buffers
float* m_bufferRawUnicornDevice = nullptr; // buffer 1 : data from device to ring buffer
float* m_bufferReceivedDataFromRing = nullptr; // buffer 2 : data from ring buffer
float* m_bufferForOpenVibe = nullptr; // buffer 3 : data converted to OpenVibe format
// END declaration buffers
uint32_t m_acquiredChannelCount = kGTecNumChannels; //number of channels specified by the user, never counts the event channels
size_t m_totalHardwareStimulations = 0; //since start button clicked
size_t m_totalRingBufferOverruns = 0;
size_t m_totalCounterErrors = 0;
bool m_flagIsFirstLoop = true;
bool m_bufferOverrun = false;
// ring buffer provided by Guger
CRingBuffer<float> m_ringBuffer;
std::unique_ptr<std::thread> m_thread;
bool m_isThreadRunning = false;
std::mutex m_mutex;
std::condition_variable m_itemAvailable;
// List of amplifiers
std::vector<GDevice> m_devices;
uint32_t m_lengthBufferRawUnicornDevice = 0;
size_t m_channelCounterIndex = 0;
bool configureDevice(size_t deviceNumber);
void detectDevices();
size_t numDevices() const { return m_devices.size(); };
size_t getChannelIndex(UNICORN_HANDLE hDevice, const char *name);
};
} // namespace AcquisitionServer
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyGtecUnicron