init
This commit is contained in:
+768
@@ -0,0 +1,768 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="upper">100</property>
|
||||
<property name="value">18</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="lower">1</property>
|
||||
<property name="upper">1024</property>
|
||||
<property name="value">4</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="dummy_window">
|
||||
<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">8</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<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">2</property>
|
||||
<property name="bottom_attach">3</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">model_sampling_frequency</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer_sampling_frequency"/>
|
||||
<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>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_type">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Device type :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_number_of_channels">
|
||||
<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">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_type">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">model_type</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer_type"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char_set">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>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model_baud_rate">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">default</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">9 600</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">115 200</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">460 800</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model_bit_depth">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">default</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">8 bits</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">16 bits</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">24 bits</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model_gender">
|
||||
<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="model_notch_filters">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">default</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">none</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">50Hz</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">60Hz</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model_preset">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Custom</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Discovery 24</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Atlantis 4x4</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Atlantis 2x2</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model_sampling_frequency">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">256</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">512</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">1024</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">2048</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model_type">
|
||||
<columns>
|
||||
<!-- column-name text -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Discovery</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Atlantis</col>
|
||||
</row>
|
||||
</data>
|
||||
</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">Brainmaster Discovery / Atlantis
|
||||
|
||||
<small><span color="darkred">Attention : You should obtain a device passkey from Brainmaster
|
||||
in order to operate your device with OpenViBE</span></small></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">1</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">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="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="n_rows">12</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<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">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</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">3</property>
|
||||
<property name="bottom_attach">4</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>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_gender">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">model_gender</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer_gender"/>
|
||||
<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">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_age">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char_set">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">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">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="invisible_char">●</property>
|
||||
<property name="invisible_char_set">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>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<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_device_preset">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Configuration preset :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_device_preset">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">model_preset</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer_device_preset"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_device">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Device port :</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_device">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer_device"/>
|
||||
<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_baud_rate">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Baud rate :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_bit_depth">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Bit depth :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">7</property>
|
||||
<property name="bottom_attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_notch_filters">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Notch filter :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">8</property>
|
||||
<property name="bottom_attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_baud_rate">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">model_baud_rate</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer_baud_rate"/>
|
||||
<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">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_bit_depth">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">model_bit_depth</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer_bit_depth"/>
|
||||
<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">7</property>
|
||||
<property name="bottom_attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_notch_filters">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">model_notch_filters</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer_notch_filters"/>
|
||||
<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">8</property>
|
||||
<property name="bottom_attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">9</property>
|
||||
<property name="bottom_attach">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_device_serial">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Device Serial Nr :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">10</property>
|
||||
<property name="bottom_attach">11</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_device_passkey">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Device Passkey :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">11</property>
|
||||
<property name="bottom_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry_device_serial">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char_set">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>
|
||||
</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="GtkEntry" id="entry_device_passkey">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char_set">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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">11</property>
|
||||
<property name="bottom_attach">12</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_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">1</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="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">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>
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* ovasCConfigurationBrainmasterDiscovery.cpp
|
||||
*
|
||||
* Copyright (c) 2012, Yann Renard. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
|
||||
#include "ovasCConfigurationBrainmasterDiscovery.h"
|
||||
|
||||
#include "ovas_defines_brainmaster_discovery.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
namespace {
|
||||
static bool guardFlag = false;
|
||||
|
||||
void on_spin_button_changed(GtkSpinButton* pAnySpinButton, GtkComboBox* pDevicePresetComboBox)
|
||||
{
|
||||
if (!guardFlag) { gtk_combo_box_set_active(pDevicePresetComboBox, 0); } // Custom
|
||||
}
|
||||
|
||||
void on_combo_box_changed(GtkComboBox* pAnyComboBox, GtkComboBox* pDevicePresetComboBox)
|
||||
{
|
||||
if (!guardFlag) { gtk_combo_box_set_active(pDevicePresetComboBox, 0); } // Custom
|
||||
}
|
||||
|
||||
void on_device_preset_changed(GtkComboBox* pComboBox, CConfigurationBrainmasterDiscovery* pThis)
|
||||
{
|
||||
guardFlag = true;
|
||||
|
||||
// int i;
|
||||
const int active = gtk_combo_box_get_active(pComboBox);
|
||||
switch (active)
|
||||
{
|
||||
default:
|
||||
case Preset_Custom: break;
|
||||
case Preset_Discovery_24: gtk_spin_button_set_value(pThis->m_buttonChannelCount, 24); // 22 EEG channels + 2 AUX
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxType, 0); // Discovery
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxSamplingRate, 0); // default
|
||||
//gtk_combo_box_set_active(pThis->m_comboBoxDevice, );
|
||||
//gtk_combo_box_set_active(pThis->m_comboBoxPreset, );
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxBaudRate, 0); // default
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxBitDepth, 0); // default
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxNotchFilters, 0); // default
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(pThis->m_comboBoxBitDepth), FALSE); // default
|
||||
pThis->m_rvChannelType.clear();
|
||||
//for (i=0; i<22; ++i) pThis->m_rvChannelType[i]=ChannelType_EEG;
|
||||
//for (i=22; i<24; ++i) pThis->m_rvChannelType[i]=ChannelType_AUX;
|
||||
pThis->m_channelNames[0] = "Fp1";
|
||||
pThis->m_channelNames[1] = "F3";
|
||||
pThis->m_channelNames[2] = "C3";
|
||||
pThis->m_channelNames[3] = "P3";
|
||||
pThis->m_channelNames[4] = "O1";
|
||||
pThis->m_channelNames[5] = "F7";
|
||||
pThis->m_channelNames[6] = "T3";
|
||||
pThis->m_channelNames[7] = "T5";
|
||||
pThis->m_channelNames[8] = "Fz";
|
||||
pThis->m_channelNames[9] = "Fp2";
|
||||
pThis->m_channelNames[10] = "F4";
|
||||
pThis->m_channelNames[11] = "C4";
|
||||
pThis->m_channelNames[12] = "P4";
|
||||
pThis->m_channelNames[13] = "O2";
|
||||
pThis->m_channelNames[14] = "F8";
|
||||
pThis->m_channelNames[15] = "T4";
|
||||
pThis->m_channelNames[16] = "T6";
|
||||
pThis->m_channelNames[17] = "Cz";
|
||||
pThis->m_channelNames[18] = "Pz";
|
||||
pThis->m_channelNames[19] = "A2";
|
||||
pThis->m_channelNames[20] = "Fpz";
|
||||
pThis->m_channelNames[21] = "Oz";
|
||||
pThis->m_channelNames[22] = "AUX1";
|
||||
pThis->m_channelNames[23] = "AUX2";
|
||||
break;
|
||||
case Preset_Atlantis_2x2: gtk_spin_button_set_value(pThis->m_buttonChannelCount, 4); // 2 EEG channels + 2 AUX
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxType, 1); // Atlantis
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxSamplingRate, 0); // default
|
||||
//gtk_combo_box_set_active(pThis->m_comboBoxDevice, );
|
||||
//gtk_combo_box_set_active(pThis->m_comboBoxPreset, );
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxBaudRate, 0); // default
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxBitDepth, 0); // default
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxNotchFilters, 0); // default
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(pThis->m_comboBoxBitDepth), TRUE); // default
|
||||
pThis->m_rvChannelType.clear();
|
||||
pThis->m_rvChannelType[0] = ChannelType_AUX;
|
||||
pThis->m_rvChannelType[1] = ChannelType_AUX;
|
||||
pThis->m_rvChannelType[2] = ChannelType_EEG;
|
||||
pThis->m_rvChannelType[3] = ChannelType_EEG;
|
||||
pThis->m_channelNames[0] = "AUX1";
|
||||
pThis->m_channelNames[1] = "AUX2";
|
||||
pThis->m_channelNames[2] = "A1-R1";
|
||||
pThis->m_channelNames[3] = "A2-R2";
|
||||
break;
|
||||
case Preset_Atlantis_4x4: gtk_spin_button_set_value(pThis->m_buttonChannelCount, 8); // 2 EEG channels + 2 AUX + 2 EEG channels + 2 AUX
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxType, 1); // Atlantis
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxSamplingRate, 0); // default
|
||||
//gtk_combo_box_set_active(pThis->m_comboBoxDevice, );
|
||||
//gtk_combo_box_set_active(pThis->m_comboBoxPreset, );
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxBaudRate, 0); // default
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxBitDepth, 0); // default
|
||||
gtk_combo_box_set_active(pThis->m_comboBoxNotchFilters, 0); // default
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(pThis->m_comboBoxBitDepth), TRUE); // default
|
||||
pThis->m_rvChannelType.clear();
|
||||
pThis->m_rvChannelType[0] = ChannelType_AUX;
|
||||
pThis->m_rvChannelType[1] = ChannelType_AUX;
|
||||
pThis->m_rvChannelType[2] = ChannelType_EEG;
|
||||
pThis->m_rvChannelType[3] = ChannelType_EEG;
|
||||
pThis->m_rvChannelType[4] = ChannelType_AUX;
|
||||
pThis->m_rvChannelType[5] = ChannelType_AUX;
|
||||
pThis->m_rvChannelType[6] = ChannelType_EEG;
|
||||
pThis->m_rvChannelType[7] = ChannelType_EEG;
|
||||
pThis->m_channelNames[0] = "AUX1";
|
||||
pThis->m_channelNames[1] = "AUX2";
|
||||
pThis->m_channelNames[2] = "A1-R1";
|
||||
pThis->m_channelNames[3] = "A2-R2";
|
||||
pThis->m_channelNames[4] = "AUX3";
|
||||
pThis->m_channelNames[5] = "AUX4";
|
||||
pThis->m_channelNames[6] = "A3-R3";
|
||||
pThis->m_channelNames[7] = "A4-R4";
|
||||
break;
|
||||
}
|
||||
|
||||
guardFlag = false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CConfigurationBrainmasterDiscovery::CConfigurationBrainmasterDiscovery(const char* gtkBuilderFilename, uint32_t& rPort, uint32_t& rPreset, uint32_t& rType,
|
||||
uint32_t& rBaudRate, uint32_t& rSamplingRate, uint32_t& rBitDepth,
|
||||
uint32_t& rNotchFilters, std::map<uint32_t, uint32_t>& rvChannelType,
|
||||
std::string& sDeviceSerial, std::string& sDevicePasskey)
|
||||
: CConfigurationBuilder(gtkBuilderFilename), m_rPort(rPort), m_rPreset(rPreset), m_rType(rType), m_rBaudRate(rBaudRate), m_rSamplingRate(rSamplingRate),
|
||||
m_rBitDepth(rBitDepth), m_rNotchFilters(rNotchFilters), m_rvChannelType(rvChannelType), m_deviceSerial(sDeviceSerial), m_devicePasskey(sDevicePasskey)
|
||||
{
|
||||
m_listStore = gtk_list_store_new(1, G_TYPE_STRING);
|
||||
}
|
||||
|
||||
CConfigurationBrainmasterDiscovery::~CConfigurationBrainmasterDiscovery() { g_object_unref(m_listStore); }
|
||||
|
||||
bool CConfigurationBrainmasterDiscovery::preConfigure()
|
||||
{
|
||||
if (!CConfigurationBuilder::preConfigure()) { return false; }
|
||||
|
||||
const uint32_t port = m_rPort;
|
||||
|
||||
m_buttonChannelCount = GTK_SPIN_BUTTON(this->m_nChannels);
|
||||
m_comboBoxDevice = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_device"));
|
||||
m_comboBoxPreset = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_device_preset"));
|
||||
m_comboBoxType = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_type"));
|
||||
m_comboBoxBaudRate = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_baud_rate"));
|
||||
m_comboBoxSamplingRate = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_sampling_frequency"));
|
||||
m_comboBoxBitDepth = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_bit_depth"));
|
||||
m_comboBoxNotchFilters = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_notch_filters"));
|
||||
m_pEntryDeviceSerial = GTK_ENTRY(gtk_builder_get_object(m_builder, "entry_device_serial"));
|
||||
m_pEntryDevicePasskey = GTK_ENTRY(gtk_builder_get_object(m_builder, "entry_device_passkey"));
|
||||
|
||||
g_signal_connect(G_OBJECT(m_buttonChannelCount), "value-changed", G_CALLBACK(on_spin_button_changed), m_comboBoxPreset);
|
||||
g_signal_connect(G_OBJECT(m_comboBoxPreset), "changed", G_CALLBACK(on_device_preset_changed), this);
|
||||
g_signal_connect(G_OBJECT(m_comboBoxType), "changed", G_CALLBACK(on_combo_box_changed), m_comboBoxPreset);
|
||||
g_signal_connect(G_OBJECT(m_comboBoxBaudRate), "changed", G_CALLBACK(on_combo_box_changed), m_comboBoxPreset);
|
||||
g_signal_connect(G_OBJECT(m_comboBoxSamplingRate), "changed", G_CALLBACK(on_combo_box_changed), m_comboBoxPreset);
|
||||
g_signal_connect(G_OBJECT(m_comboBoxBitDepth), "changed", G_CALLBACK(on_combo_box_changed), m_comboBoxPreset);
|
||||
g_signal_connect(G_OBJECT(m_comboBoxNotchFilters), "changed", G_CALLBACK(on_combo_box_changed), m_comboBoxPreset);
|
||||
|
||||
g_object_unref(m_listStore);
|
||||
m_listStore = gtk_list_store_new(1, G_TYPE_STRING);
|
||||
|
||||
gtk_combo_box_set_model(m_comboBoxDevice, GTK_TREE_MODEL(m_listStore));
|
||||
|
||||
char buffer[1024];
|
||||
bool selected = false;
|
||||
|
||||
gtk_combo_box_append_text(m_comboBoxDevice, "Auto");
|
||||
for (uint32_t i = 1; i < 17; ++i)
|
||||
{
|
||||
sprintf(buffer, "COM%i", i);
|
||||
gtk_combo_box_append_text(m_comboBoxDevice, buffer);
|
||||
if (port == i)
|
||||
{
|
||||
gtk_combo_box_set_active(m_comboBoxDevice, i);
|
||||
selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!selected) { gtk_combo_box_set_active(m_comboBoxDevice, 0); }
|
||||
|
||||
switch (m_rType)
|
||||
{
|
||||
default:
|
||||
case Type_Discovery: gtk_combo_box_set_active(m_comboBoxType, 0);
|
||||
break;
|
||||
case Type_Atlantis: gtk_combo_box_set_active(m_comboBoxType, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_rBaudRate)
|
||||
{
|
||||
default:
|
||||
case BaudRate_Default: gtk_combo_box_set_active(m_comboBoxBaudRate, 0);
|
||||
break;
|
||||
case BaudRate_9600: gtk_combo_box_set_active(m_comboBoxBaudRate, 1);
|
||||
break;
|
||||
case BaudRate_115200: gtk_combo_box_set_active(m_comboBoxBaudRate, 2);
|
||||
break;
|
||||
case BaudRate_460800: gtk_combo_box_set_active(m_comboBoxBaudRate, 3);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_rSamplingRate)
|
||||
{
|
||||
default:
|
||||
case SamplingFrequency_256: gtk_combo_box_set_active(m_comboBoxSamplingRate, 0);
|
||||
break;
|
||||
case SamplingFrequency_512: gtk_combo_box_set_active(m_comboBoxSamplingRate, 1);
|
||||
break;
|
||||
case SamplingFrequency_1024: gtk_combo_box_set_active(m_comboBoxSamplingRate, 2);
|
||||
break;
|
||||
case SamplingFrequency_2048: gtk_combo_box_set_active(m_comboBoxSamplingRate, 3);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_rBitDepth)
|
||||
{
|
||||
default:
|
||||
case BitDepth_Default: gtk_combo_box_set_active(m_comboBoxBitDepth, 0);
|
||||
break;
|
||||
case BitDepth_8: gtk_combo_box_set_active(m_comboBoxBitDepth, 1);
|
||||
break;
|
||||
case BitDepth_16: gtk_combo_box_set_active(m_comboBoxBitDepth, 2);
|
||||
break;
|
||||
case BitDepth_24: gtk_combo_box_set_active(m_comboBoxBitDepth, 3);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_rNotchFilters)
|
||||
{
|
||||
default:
|
||||
case NotchFilter_Default: gtk_combo_box_set_active(m_comboBoxNotchFilters, 0);
|
||||
break;
|
||||
case NotchFilter_Off: gtk_combo_box_set_active(m_comboBoxNotchFilters, 1);
|
||||
break;
|
||||
case NotchFilter_50: gtk_combo_box_set_active(m_comboBoxNotchFilters, 2);
|
||||
break;
|
||||
case NotchFilter_60: gtk_combo_box_set_active(m_comboBoxNotchFilters, 3);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_rPreset)
|
||||
{
|
||||
default:
|
||||
case Preset_Custom: gtk_combo_box_set_active(m_comboBoxPreset, 0);
|
||||
break;
|
||||
case Preset_Discovery_24: gtk_combo_box_set_active(m_comboBoxPreset, 1);
|
||||
break;
|
||||
case Preset_Atlantis_4x4: gtk_combo_box_set_active(m_comboBoxPreset, 2);
|
||||
break;
|
||||
case Preset_Atlantis_2x2: gtk_combo_box_set_active(m_comboBoxPreset, 3);
|
||||
break;
|
||||
}
|
||||
|
||||
gtk_entry_set_text(m_pEntryDeviceSerial, m_deviceSerial.c_str());
|
||||
gtk_entry_set_text(m_pEntryDevicePasskey, m_devicePasskey.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfigurationBrainmasterDiscovery::postConfigure()
|
||||
{
|
||||
if (m_applyConfig)
|
||||
{
|
||||
const int port = gtk_combo_box_get_active(m_comboBoxDevice);
|
||||
if (port >= 0) { m_rPort = uint32_t(port); }
|
||||
|
||||
const int preset = gtk_combo_box_get_active(m_comboBoxPreset);
|
||||
if (preset >= 0) { m_rPreset = uint32_t(preset); }
|
||||
|
||||
const int type = gtk_combo_box_get_active(m_comboBoxType);
|
||||
if (type >= 0) { m_rType = uint32_t(type); }
|
||||
|
||||
const int baudRate = gtk_combo_box_get_active(m_comboBoxBaudRate);
|
||||
if (baudRate >= 0) { m_rBaudRate = uint32_t(baudRate); }
|
||||
|
||||
const int sampling = gtk_combo_box_get_active(m_comboBoxSamplingRate);
|
||||
if (sampling >= 0) { m_rSamplingRate = uint32_t(sampling); }
|
||||
|
||||
const int bitDepth = gtk_combo_box_get_active(m_comboBoxBitDepth);
|
||||
if (bitDepth >= 0) { m_rBitDepth = uint32_t(bitDepth); }
|
||||
|
||||
const int notchFilters = gtk_combo_box_get_active(m_comboBoxNotchFilters);
|
||||
if (notchFilters >= 0) { m_rNotchFilters = uint32_t(notchFilters); }
|
||||
|
||||
m_deviceSerial = gtk_entry_get_text(m_pEntryDeviceSerial);
|
||||
m_devicePasskey = gtk_entry_get_text(m_pEntryDevicePasskey);
|
||||
}
|
||||
|
||||
if (!CConfigurationBuilder::postConfigure()) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* ovasCConfigurationBrainmasterDiscovery.h
|
||||
*
|
||||
* Copyright (c) 2012, Yann Renard. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
class CConfigurationBrainmasterDiscovery final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
CConfigurationBrainmasterDiscovery(const char* gtkBuilderFilename, uint32_t& rPort, uint32_t& rPreset, uint32_t& rType,
|
||||
uint32_t& rBaudRate, uint32_t& rSamplingFrequency, uint32_t& rBitDepth, uint32_t& rNotchFilters,
|
||||
std::map<uint32_t, uint32_t>& rvChannelType, std::string& sDeviceSerial, std::string& sDevicePasskey);
|
||||
~CConfigurationBrainmasterDiscovery() override;
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
|
||||
GtkSpinButton* m_buttonChannelCount = nullptr;
|
||||
GtkComboBox* m_comboBoxDevice = nullptr;
|
||||
GtkComboBox* m_comboBoxPreset = nullptr;
|
||||
GtkComboBox* m_comboBoxType = nullptr;
|
||||
GtkComboBox* m_comboBoxBaudRate = nullptr;
|
||||
GtkComboBox* m_comboBoxSamplingRate = nullptr;
|
||||
GtkComboBox* m_comboBoxBitDepth = nullptr;
|
||||
GtkComboBox* m_comboBoxNotchFilters = nullptr;
|
||||
GtkEntry* m_pEntryDeviceSerial = nullptr;
|
||||
GtkEntry* m_pEntryDevicePasskey = nullptr;
|
||||
|
||||
using CConfigurationBuilder::m_channelNames;
|
||||
|
||||
|
||||
GtkListStore* m_listStore = nullptr;
|
||||
|
||||
uint32_t& m_rPort;
|
||||
uint32_t& m_rPreset;
|
||||
uint32_t& m_rType;
|
||||
uint32_t& m_rBaudRate;
|
||||
uint32_t& m_rSamplingRate;
|
||||
uint32_t& m_rBitDepth;
|
||||
uint32_t& m_rNotchFilters;
|
||||
std::map<uint32_t, uint32_t>& m_rvChannelType;
|
||||
std::string& m_deviceSerial;
|
||||
std::string& m_devicePasskey;
|
||||
|
||||
std::vector<GtkWidget*> m_sensitiveWidgets;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
+601
@@ -0,0 +1,601 @@
|
||||
/*
|
||||
* ovasCDriverBrainmasterDiscovery.cpp
|
||||
*
|
||||
* Copyright (c) 2012, Yann Renard. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
|
||||
#include "ovasCDriverBrainmasterDiscovery.h"
|
||||
#include "ovasCConfigurationBrainmasterDiscovery.h"
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "ovas_defines_brainmaster_discovery.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
extern "C" {
|
||||
int OvAtlLoginDevice(const char* sSerialNumber, const char* sPassKey);
|
||||
}
|
||||
|
||||
static float decode_24(unsigned char*& rpBuffer)
|
||||
{
|
||||
int res = rpBuffer[0] + rpBuffer[1] * 0x100 + rpBuffer[2] * 0x10000;
|
||||
if (res > 0x800000) { res = res - 0x1000000; }
|
||||
rpBuffer += 3;
|
||||
return res * 0.01163f;
|
||||
}
|
||||
|
||||
static float decode_16(unsigned char*& rpBuffer)
|
||||
{
|
||||
int res = rpBuffer[0] + rpBuffer[1] * 0x100;
|
||||
if (res > 0x8000) { res = res - 0x10000; }
|
||||
rpBuffer += 2;
|
||||
return res * 0.1983f;
|
||||
}
|
||||
|
||||
static float decode_8(unsigned char*& rpBuffer)
|
||||
{
|
||||
int res = rpBuffer[0];
|
||||
res -= 0x80;
|
||||
rpBuffer += 1;
|
||||
return res * 100 / 128.f;
|
||||
}
|
||||
|
||||
static uint32_t next_sync(uint32_t sync)
|
||||
{
|
||||
sync++;
|
||||
sync &= 7;
|
||||
return sync ? sync : 1;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
CDriverBrainmasterDiscovery::CDriverBrainmasterDiscovery(IDriverContext& ctx)
|
||||
: IDriver(ctx)
|
||||
, m_preset(Preset_Discovery_24)
|
||||
{
|
||||
m_deviceSerial = ctx.getConfigurationManager().expand("${AcquisitionServer_Driver_BrainmasterDeviceSerial}").toASCIIString();
|
||||
m_devicePasskey = ctx.getConfigurationManager().expand("${AcquisitionServer_Driver_BrainmasterDevicePasskey}").toASCIIString();
|
||||
m_frameDumpFilename = ctx.getConfigurationManager().expand("${AcquisitionServer_Driver_BrainmasterFrameDumpFilename}").toASCIIString();
|
||||
|
||||
m_baudRates[BaudRate_9600] = 0x30; // 9600
|
||||
m_baudRates[BaudRate_115200] = 0x20; // 115200
|
||||
m_baudRates[BaudRate_460800] = 0x10; // 460800
|
||||
|
||||
m_baudRateValues[BaudRate_9600] = 9600; // 9600
|
||||
m_baudRateValues[BaudRate_115200] = 115200; // 115200
|
||||
m_baudRateValues[BaudRate_460800] = 460800; // 460800
|
||||
|
||||
m_notchsFilters[NotchFilter_Default] = 0; // Off
|
||||
m_notchsFilters[NotchFilter_Off] = 0; // Off
|
||||
m_notchsFilters[NotchFilter_50] = 2; // 50 Hz
|
||||
m_notchsFilters[NotchFilter_60] = 3; // 60 Hz
|
||||
|
||||
m_notchFiltersValues[NotchFilter_Default] = 0; // Off
|
||||
m_notchFiltersValues[NotchFilter_Off] = 0; // Off
|
||||
m_notchFiltersValues[NotchFilter_50] = 50; // 50 Hz
|
||||
m_notchFiltersValues[NotchFilter_60] = 60; // 60 Hz
|
||||
|
||||
m_bitDepths[BitDepth_8] = 1; // 8 bits
|
||||
m_bitDepths[BitDepth_16] = 2; // 16 bits
|
||||
m_bitDepths[BitDepth_24] = 3; // 24 bits
|
||||
|
||||
m_bitDepthValues[BitDepth_8] = 8; // 8 bits
|
||||
m_bitDepthValues[BitDepth_16] = 16; // 16 bits
|
||||
m_bitDepthValues[BitDepth_24] = 24; // 24 bits
|
||||
|
||||
// 0xcc for 2 EEG, 0xff for 2 EEG + 2 AUX
|
||||
m_channelSelectionMasks[24] = 0xffffff; // Type_Discovery
|
||||
m_channelSelectionMasks[4] = 0xf; // Type_Atlantis 2x2
|
||||
m_channelSelectionMasks[8] = 0xff; // Type_Atlantis 4x4
|
||||
|
||||
m_bitDepthDecoders[ChannelType_EEG][BitDepth_8] = decode_8; // 8 bits
|
||||
m_bitDepthDecoders[ChannelType_EEG][BitDepth_16] = decode_16; // 16 bits
|
||||
m_bitDepthDecoders[ChannelType_EEG][BitDepth_24] = decode_24; // 24 bits
|
||||
|
||||
m_bitDepthDecoders[ChannelType_AUX][BitDepth_8] = decode_8; // 8 bits
|
||||
m_bitDepthDecoders[ChannelType_AUX][BitDepth_16] = decode_16; // 16 bits
|
||||
m_bitDepthDecoders[ChannelType_AUX][BitDepth_24] = decode_16; // 24 bits
|
||||
|
||||
m_startModules[Type_Discovery] = ::DiscStartModule;
|
||||
m_startModules[Type_Atlantis] = ::AtlStartModule;
|
||||
|
||||
m_stopModules[Type_Discovery] = ::DiscStopModule;
|
||||
m_stopModules[Type_Atlantis] = ::AtlStopModule;
|
||||
|
||||
m_header.setSamplingFrequency(256);
|
||||
m_header.setChannelCount(24);
|
||||
m_header.setChannelName(0, "Fp1"/*"FP1"*/);
|
||||
m_header.setChannelName(1, "F3");
|
||||
m_header.setChannelName(2, "C3");
|
||||
m_header.setChannelName(3, "P3");
|
||||
m_header.setChannelName(4, "O1");
|
||||
m_header.setChannelName(5, "F7");
|
||||
m_header.setChannelName(6, "T3");
|
||||
m_header.setChannelName(7, "T5");
|
||||
m_header.setChannelName(8, "Fz");
|
||||
m_header.setChannelName(9, "Fp2"/*"FP2"*/);
|
||||
m_header.setChannelName(10, "F4");
|
||||
m_header.setChannelName(11, "C4");
|
||||
m_header.setChannelName(12, "P4");
|
||||
m_header.setChannelName(13, "O2");
|
||||
m_header.setChannelName(14, "F8");
|
||||
m_header.setChannelName(15, "T4");
|
||||
m_header.setChannelName(16, "T6");
|
||||
m_header.setChannelName(17, "Cz");
|
||||
m_header.setChannelName(18, "Pz");
|
||||
m_header.setChannelName(19, "A2");
|
||||
m_header.setChannelName(20, "Fpz"/*"FPz"*/);
|
||||
m_header.setChannelName(21, "Oz");
|
||||
m_header.setChannelName(22, "AUX1");
|
||||
m_header.setChannelName(23, "AUX2");
|
||||
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i) { m_header.setChannelUnits(i, OVTK_UNIT_Volts, OVTK_FACTOR_Micro); }
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDriverBrainmasterDiscovery::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (m_driverCtx.isConnected()) { return false; }
|
||||
|
||||
if (m_deviceSerial == "" || m_devicePasskey == "")
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "The device serial or passkey were not configured\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error <<
|
||||
"You can set their value from the configuration pannel or from the following configuration tokens :\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << " - " << CString("AcquisitionServer_Driver_BrainmasterDeviceSerial") << "\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << " - " << CString("AcquisitionServer_Driver_BrainmasterDevicePasskey") << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
default:
|
||||
case Type_Discovery: m_baudRateReal = (m_baudRate != BaudRate_Default ? m_baudRate : BaudRate_460800);
|
||||
m_samplingRateReal = m_samplingRate;
|
||||
m_bitDepthReal = BitDepth_24;
|
||||
m_notchFiltersReal = (m_notchFilters != NotchFilter_Default ? m_notchFilters : NotchFilter_Default);
|
||||
m_frameSize = 3;
|
||||
m_dataOffset = 3;
|
||||
break;
|
||||
|
||||
case Type_Atlantis: m_baudRateReal = (m_baudRate != BaudRate_Default ? m_baudRate : BaudRate_115200);
|
||||
m_samplingRateReal = m_samplingRate;
|
||||
m_bitDepthReal = (m_bitDepth != BitDepth_Default ? m_bitDepth : BitDepth_24);
|
||||
m_notchFiltersReal = (m_notchFilters != NotchFilter_Default ? m_notchFilters : NotchFilter_Default);
|
||||
m_frameSize = 1;
|
||||
m_dataOffset = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_bitDepthReal)
|
||||
{
|
||||
default:
|
||||
case BitDepth_24: for (i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
switch (m_channelTypes[i])
|
||||
{
|
||||
default:
|
||||
case ChannelType_EEG: m_frameSize += 3;
|
||||
break;
|
||||
|
||||
case ChannelType_AUX: m_frameSize += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BitDepth_16: m_frameSize += m_header.getChannelCount() * 2;
|
||||
break;
|
||||
|
||||
case BitDepth_8: m_frameSize += m_header.getChannelCount();
|
||||
break;
|
||||
}
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Computed frame size of " << m_frameSize << " bytes\n";
|
||||
|
||||
m_samples.resize(m_header.getChannelCount());
|
||||
m_buffers.resize(m_frameSize);
|
||||
|
||||
m_callback = &callback;
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
|
||||
// Sets/Autodetects port
|
||||
m_portReal = m_port;
|
||||
if (m_portReal == 0)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Auto detecting COM port...\n";
|
||||
|
||||
m_portReal = autoDetectPort();
|
||||
if (m_portReal == uint32_t(-1))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Port is not set and could not be auto detected, please configure the driver\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Found COM port " << m_portReal << " !\n";
|
||||
}
|
||||
|
||||
// Opens device handle at default speed
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Opening COM port " << m_portReal << " at 9600 bauds\n";
|
||||
if (!::AtlOpenPort(m_portReal, 9600, nullptr))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not open port " << m_portReal << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Writes baud rate
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Setting baud rate to " << m_baudRateValues[m_baudRateReal] << " bauds (code " << m_baudRates[
|
||||
m_baudRateReal] << ")\n";
|
||||
if (!::AtlSetBaudRate(m_baudRates[m_baudRateReal]))
|
||||
{
|
||||
::AtlClosePort(m_portReal);
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not set baud rate\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Closes port (which was at default speed)
|
||||
::AtlClosePort(m_portReal);
|
||||
|
||||
// Opens device handle at targeted speed
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Re-opening COM port " << m_portReal << " at " << m_baudRateValues[m_baudRateReal] <<
|
||||
" bauds (code " << m_baudRates[m_baudRateReal] << ")\n";
|
||||
if (!::AtlOpenPort(m_portReal, m_baudRateValues[m_baudRateReal], nullptr))
|
||||
// if(!::AtlOpenPort(m_portReal, 9600, nullptr))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not open port " << m_portReal << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Logs in the device
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Logging in the device...\n";
|
||||
const int code = OvAtlLoginDevice(m_deviceSerial.c_str(), m_devicePasskey.c_str());
|
||||
if (!code)
|
||||
{
|
||||
::AtlSetBaudRate(m_baudRates[BaudRate_9600]);
|
||||
::AtlClosePort(m_portReal);
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not log in device (error code was " << uint32_t(code) << ")\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Please check the device serial and passkey configuration\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error <<
|
||||
"You can set their value from the configuration pannel or from the following configuration tokens :\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << " - " << CString("AcquisitionServer_Driver_BrainmasterDeviceSerial") << " (was set to [" <<
|
||||
m_deviceSerial.c_str() << "])\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << " - " << CString("AcquisitionServer_Driver_BrainmasterDevicePasskey") << " (was set to [" <<
|
||||
m_devicePasskey.c_str() << "])\n";
|
||||
|
||||
if (!checkDeviceSerial(m_deviceSerial))
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error <<
|
||||
"By the way, serial seems to be malformed, it is supposed to be XXXXX with each X be a digit\n";
|
||||
if (!checkDevicePasskey(m_devicePasskey))
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error <<
|
||||
"By the way, passkey seems to be malformed, it is supposed to be XXXX-XXXX-XXXX with each X be a digit or a letter\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reads firmware version
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Successfully opened device on port " << m_portReal << " | Firware nr : " <<
|
||||
uint32_t(AtlQueryFirmware(0)) << "\n";
|
||||
|
||||
// Writes notch filters
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Setting notch filters to " << m_notchFiltersValues[m_notchFiltersReal] << " (code " <<
|
||||
m_notchsFilters
|
||||
[m_notchFiltersReal] << ")...\n";
|
||||
if (!::AtlSetNotchFilters(m_notchsFilters[m_notchFiltersReal]))
|
||||
{
|
||||
::AtlSetBaudRate(m_baudRates[BaudRate_9600]);
|
||||
::AtlClosePort(m_portReal);
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not set notch filters\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Writes bit depth
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Setting bit depth to " << m_bitDepthValues[m_bitDepthReal] << " bits (code " << m_bitDepths[
|
||||
m_bitDepthReal] << ")...\n";
|
||||
if (!::AtlSetBytesPerSample(m_bitDepths[m_bitDepthReal]))
|
||||
{
|
||||
::AtlSetBaudRate(m_baudRates[BaudRate_9600]);
|
||||
::AtlClosePort(m_portReal);
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not set bytes per sample\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Selects all the channels
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Selecting all channels with channel mask " << m_channelSelectionMasks[m_header.getChannelCount()]
|
||||
<<
|
||||
"...\n";
|
||||
if (!::AtlSelectChannels(m_channelSelectionMasks[m_header.getChannelCount()]))
|
||||
{
|
||||
::AtlSetBaudRate(m_baudRates[BaudRate_9600]);
|
||||
::AtlClosePort(m_portReal);
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not select all the channels\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clears special data
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Clearing specials...\n";
|
||||
if (!::AtlClearSpecials())
|
||||
{
|
||||
::AtlSetBaudRate(m_baudRates[BaudRate_9600]);
|
||||
::AtlClosePort(m_portReal);
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not clear special data selection\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Poking special code for Atlantis 4x4
|
||||
if (m_header.getChannelCount() == 8)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Poking special code for Atlantis 4x4...\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << uint32_t(AtlPoke(0xc006, 0x00)) <<
|
||||
"\n"; // Sets internal sampling rate set to 512 Hz - This hack solves the EEG chan 3 & 4 flat lines issue
|
||||
// m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << uint32_t(AtlPoke(0xb607, 0xff)) << "\n"; // ATC_CHANOUTMASK
|
||||
// m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << uint32_t(AtlPoke(0xb608, 0x20)) << "\n"; // ATC_ADCMODE
|
||||
// m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << uint32_t(AtlPoke(0xb7e9, 0x03)) << "\n"; // ??? Bit mode ?
|
||||
}
|
||||
|
||||
// serialn_p ::AtlQuerySerialNumber(int auth)
|
||||
|
||||
// Actually starts the acquisition
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Starting module !\n";
|
||||
if (!m_startModules[m_type]())
|
||||
{
|
||||
::AtlSetBaudRate(m_baudRates[BaudRate_9600]);
|
||||
::AtlClosePort(m_portReal);
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not start module\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_syncByte = 1;
|
||||
|
||||
// Preparing optional frame dump
|
||||
if (m_frameDumpFilename == "")
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Won't dump frames\n";
|
||||
m_frameDumpFlag = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
FILE* file = fopen(m_frameDumpFilename.c_str(), "wb");
|
||||
if (file)
|
||||
{
|
||||
m_frameDumpFlag = true;
|
||||
fclose(file);
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Will dump frames in [" << m_frameDumpFilename << "]\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
m_frameDumpFlag = false;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Warning << "Should have dumped frames in [" << m_frameDumpFilename <<
|
||||
"] but file can't be opened for writing !\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverBrainmasterDiscovery::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverBrainmasterDiscovery::loop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (!m_driverCtx.isStarted()) { return true; }
|
||||
bool gotData = false;
|
||||
|
||||
for (uint32_t i = 0; i < m_nSamplePerSentBlock; ++i)
|
||||
{
|
||||
// Updates Sync bits
|
||||
m_syncByte = next_sync(m_syncByte);
|
||||
|
||||
// Searches for Sync bits
|
||||
this->read(&m_buffers[0], 1);
|
||||
if (((m_buffers[0] >> 5) & 7) != m_syncByte)
|
||||
{
|
||||
// Needs resync
|
||||
uint32_t sync = this->sync();
|
||||
if (sync != uint32_t(-1)) { m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Resynced by " << sync << " byte(s) !\n"; }
|
||||
else { m_driverCtx.getLogManager() << Kernel::LogLevel_ImportantWarning << "Could not resync, did not find the sync bytes !\n"; }
|
||||
|
||||
m_wasLastFrameCorrect = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sends samples if last frame was correct
|
||||
if (m_wasLastFrameCorrect)
|
||||
{
|
||||
m_callback->setSamples(&m_samples[0], 1);
|
||||
gotData = true;
|
||||
}
|
||||
|
||||
// Assumed synced stream - Reads leading bytes
|
||||
this->read(&m_buffers[1], m_buffers.size() - 1);
|
||||
|
||||
#if 1
|
||||
// For debug purpose
|
||||
if (m_frameDumpFlag)
|
||||
{
|
||||
FILE* file = fopen(m_frameDumpFilename.c_str(), "ab");
|
||||
if (file)
|
||||
{
|
||||
fprintf(file, "Frame : ");
|
||||
for (uint32_t i = 0; i < m_buffers.size(); ++i) { fprintf(file, "0x%02x ", m_buffers[i]); }
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Warning << "Unexpected: Could not open dump file [" << m_frameDumpFilename <<
|
||||
"] for writing\n";
|
||||
m_frameDumpFlag = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Converts new buffer to samples
|
||||
unsigned char* buffer = &m_buffers[m_dataOffset];
|
||||
|
||||
for (uint32_t j = 0; j < m_samples.size(); ++j) { m_samples[j] = m_bitDepthDecoders[m_channelTypes[j]][m_bitDepthReal](buffer); }
|
||||
|
||||
m_wasLastFrameCorrect = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (gotData) { m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount()); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverBrainmasterDiscovery::read(unsigned char* frame, uint32_t size)
|
||||
{
|
||||
const uint32_t count = size;
|
||||
uint32_t idx = 0;
|
||||
|
||||
while (idx != count)
|
||||
{
|
||||
int iCode = ::AtlReadData(frame + idx, count - idx);
|
||||
if (iCode >= 0) { idx += iCode; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t CDriverBrainmasterDiscovery::sync()
|
||||
{
|
||||
std::vector<unsigned char> buffer;
|
||||
buffer.resize(m_buffers.size() * 4);
|
||||
|
||||
bool finished = false;
|
||||
uint32_t nSync = 0;
|
||||
uint32_t i = 0;
|
||||
|
||||
while (!finished && i < 16)
|
||||
{
|
||||
i++;
|
||||
this->read(&buffer[0], buffer.size());
|
||||
for (uint32_t i = 0; i < m_buffers.size() && !finished; ++i)
|
||||
{
|
||||
uint32_t syncCandidate[4];
|
||||
syncCandidate[0] = buffer[i + 0 * m_buffers.size()] >> 5;
|
||||
syncCandidate[1] = buffer[i + 1 * m_buffers.size()] >> 5;
|
||||
syncCandidate[2] = buffer[i + 2 * m_buffers.size()] >> 5;
|
||||
syncCandidate[3] = buffer[i + 3 * m_buffers.size()] >> 5;
|
||||
if (next_sync(syncCandidate[0]) == syncCandidate[1]
|
||||
&& next_sync(syncCandidate[1]) == syncCandidate[2]
|
||||
&& next_sync(syncCandidate[2]) == syncCandidate[3])
|
||||
{
|
||||
this->read(&buffer[0], i);
|
||||
m_syncByte = syncCandidate[3];
|
||||
nSync = i;
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
// For debug purpose
|
||||
if (m_frameDumpFlag)
|
||||
{
|
||||
FILE* file = fopen(m_frameDumpFilename.c_str(), "ab");
|
||||
if (file)
|
||||
{
|
||||
fprintf(file, "Syncing...\n");
|
||||
for (uint32_t i = 0; i < buffer.size(); ++i)
|
||||
{
|
||||
fprintf(file, "0x%02x ", buffer[i]);
|
||||
if (((i + 1) % m_buffers.size()) == 0) { fprintf(file, "\n"); }
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Warning << "Unexpected: Could not open dump file [" << m_frameDumpFilename <<
|
||||
"] for writing\n";
|
||||
m_frameDumpFlag = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return finished ? nSync : uint32_t(-1);
|
||||
}
|
||||
|
||||
bool CDriverBrainmasterDiscovery::stop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverBrainmasterDiscovery::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// Stops acquisition
|
||||
if (!m_stopModules[m_type]())
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could stop module\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Writes baud rate back to 9600
|
||||
if (!::AtlSetBaudRate(m_baudRates[BaudRate_9600]))
|
||||
{
|
||||
::AtlClosePort(m_portReal);
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not set baud rate\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Closes device handle
|
||||
if (!::AtlClosePort(m_portReal))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not close port " << m_portReal << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDriverBrainmasterDiscovery::configure()
|
||||
{
|
||||
CConfigurationBrainmasterDiscovery config(Directories::getDataDir() + "/applications/acquisition-server/interface-Brainmaster-Discovery.ui",
|
||||
m_port, m_preset, m_type, m_baudRate, m_samplingRate, m_bitDepth,
|
||||
m_notchFilters, m_channelTypes, m_deviceSerial, m_devicePasskey);
|
||||
if (!config.configure(m_header)) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* ovasCDriverBrainmasterDiscovery.h
|
||||
*
|
||||
* Copyright (c) 2012, Yann Renard. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CDriverBrainmasterDiscovery
|
||||
* \author Yann Renard
|
||||
*/
|
||||
class CDriverBrainmasterDiscovery final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CDriverBrainmasterDiscovery(IDriverContext& ctx);
|
||||
void release() { delete this; }
|
||||
const char* getName() override { return "Brainmaster Discovery and Atlantis"; }
|
||||
|
||||
bool initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback) override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool start() override;
|
||||
bool stop() override;
|
||||
bool loop() override;
|
||||
bool read(unsigned char* frame, uint32_t size);
|
||||
uint32_t sync();
|
||||
|
||||
bool isConfigurable() override { return true; }
|
||||
bool configure() override;
|
||||
const IHeader* getHeader() override { return &m_header; }
|
||||
|
||||
protected:
|
||||
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
CHeader m_header;
|
||||
uint32_t m_nSamplePerSentBlock = 0;
|
||||
|
||||
std::string m_deviceSerial;
|
||||
std::string m_devicePasskey;
|
||||
std::string m_frameDumpFilename;
|
||||
bool m_frameDumpFlag = false;
|
||||
|
||||
uint32_t m_port = 0;
|
||||
uint32_t m_portReal = 0;
|
||||
uint32_t m_preset = 0;
|
||||
uint32_t m_type = 0;
|
||||
uint32_t m_baudRate = 0;
|
||||
uint32_t m_baudRateReal = 0;
|
||||
uint32_t m_samplingRate = 0;
|
||||
uint32_t m_samplingRateReal = 0;
|
||||
uint32_t m_bitDepth = 0;
|
||||
uint32_t m_bitDepthReal = 0;
|
||||
uint32_t m_notchFilters = 0;
|
||||
uint32_t m_notchFiltersReal = 0;
|
||||
uint32_t m_frameSize = 0;
|
||||
uint32_t m_dataOffset = 0;
|
||||
|
||||
std::map<uint32_t, uint32_t> m_baudRates;
|
||||
std::map<uint32_t, uint32_t> m_baudRateValues;
|
||||
std::map<uint32_t, uint32_t> m_notchsFilters;
|
||||
std::map<uint32_t, uint32_t> m_notchFiltersValues;
|
||||
std::map<uint32_t, uint32_t> m_bitDepths;
|
||||
std::map<uint32_t, uint32_t> m_bitDepthValues;
|
||||
std::map<uint32_t, uint32_t> m_channelTypes;
|
||||
std::map<uint32_t, uint32_t> m_channelSelectionMasks;
|
||||
std::map<uint32_t, std::map<uint32_t, float (*)(unsigned char*&)>> m_bitDepthDecoders;
|
||||
std::map<uint32_t, int (*)()> m_startModules;
|
||||
std::map<uint32_t, int (*)()> m_stopModules;
|
||||
|
||||
std::vector<float> m_samples;
|
||||
std::vector<unsigned char> m_buffers;
|
||||
|
||||
uint32_t m_syncByte = 0;
|
||||
bool m_wasLastFrameCorrect = false;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* ovas_defines_brainmaster_discovery.h
|
||||
*
|
||||
* Copyright (c) 2012, Yann Renard. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
|
||||
#include "CMKRDLLU.H"
|
||||
#include <string>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
typedef enum
|
||||
{
|
||||
Preset_Custom=0,
|
||||
Preset_Discovery_24=1,
|
||||
Preset_Atlantis_4x4=2,
|
||||
Preset_Atlantis_2x2=3,
|
||||
|
||||
Type_Discovery=0,
|
||||
Type_Atlantis=1,
|
||||
|
||||
ChannelType_EEG=0,
|
||||
ChannelType_AUX=1,
|
||||
|
||||
BaudRate_Default=0,
|
||||
BaudRate_9600=1,
|
||||
BaudRate_115200=2,
|
||||
BaudRate_460800=3,
|
||||
|
||||
NotchFilter_Default=0,
|
||||
NotchFilter_Off=1,
|
||||
NotchFilter_50=2,
|
||||
NotchFilter_60=3,
|
||||
|
||||
BitDepth_Default=0,
|
||||
BitDepth_8=1,
|
||||
BitDepth_16=2,
|
||||
BitDepth_24=3,
|
||||
|
||||
SamplingFrequency_256=0,
|
||||
SamplingFrequency_512=1,
|
||||
SamplingFrequency_1024=2,
|
||||
SamplingFrequency_2048=3,
|
||||
} EParameter;
|
||||
|
||||
inline size_t autoDetectPort(const size_t startPort = 1, const size_t stopPort = 16, const size_t fallback = size_t(-1))
|
||||
{
|
||||
for (size_t i = startPort; i <= stopPort; ++i)
|
||||
{
|
||||
const BOOL res = ::AtlOpenPort(i, /*m_baudRate*/ 0, nullptr);
|
||||
::AtlClosePort(i);
|
||||
if (res) { return i; }
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// returns true on success
|
||||
// returns false on error
|
||||
inline bool checkDeviceSerial(const std::string& serial)
|
||||
{
|
||||
// Checks length
|
||||
if (serial.length() != 5) { return false; }
|
||||
|
||||
// Checks digits
|
||||
for (size_t i = 0; i < serial.length(); ++i) { if (serial[i] < '0' || serial[i] > '9') { return false; } }
|
||||
|
||||
// First digit gives device kind
|
||||
switch (serial[0])
|
||||
{
|
||||
case '3': // Atlantis
|
||||
case '4': // Atlantis
|
||||
case '6': // Discovery
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Everything's fine
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true on success
|
||||
// returns false on error
|
||||
inline bool checkDevicePasskey(const std::string& passkey)
|
||||
{
|
||||
// Checks length -- other checks are hardcoded assuming length is 15
|
||||
if (passkey.length() != 14) { return false; }
|
||||
|
||||
// Checks separators
|
||||
if (passkey[4] != '-' || passkey[9] != '-') { return false; }
|
||||
|
||||
// Checks alpha-nums
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < 4; ++j)
|
||||
{
|
||||
const size_t k = i * 5 + j;
|
||||
if ((passkey[k] < '0' || passkey[k] > '9') && (passkey[k] < 'a' || passkey[k] > 'z')
|
||||
&& (passkey[k] < 'A' || passkey[k] > 'Z')) { return false; }
|
||||
}
|
||||
}
|
||||
// Everything's fine
|
||||
return true;
|
||||
}
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyBrainmasterCodeMakerAPI
|
||||
+656
@@ -0,0 +1,656 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 2.12 -->
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="value">1025</property>
|
||||
<property name="upper">65535</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="GtkAdjustment" id="adjustment4">
|
||||
<property name="value">1</property>
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">1024</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model1">
|
||||
<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="model2">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<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">1024</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">2048</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">4096</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
|
||||
<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>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox">
|
||||
<property name="visible">True</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>
|
||||
<object class="GtkLabel" id="label_title">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Brainamp Standard (through Vision Recorder)</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox">
|
||||
<property name="visible">True</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="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">5</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_host_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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="GtkEntry" id="entry_host_name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
</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="GtkLabel" id="label_host_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Vision Recorder connection port</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_host_name">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Vision Recorder host name</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_identifier">
|
||||
<property name="visible">True</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="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="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="GtkSpinButton" id="spinbutton_identifier">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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="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="GtkComboBox" id="combobox_gender">
|
||||
<property name="visible">True</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">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area">
|
||||
<property name="visible">True</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_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_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="pack_type">end</property>
|
||||
<property name="position">0</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>
|
||||
<object class="GtkWindow" id="dummy-widgets">
|
||||
<child>
|
||||
<object class="GtkTable" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">10</property>
|
||||
<property name="n_columns">10</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment4</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_sampling_frequency">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">model2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer2"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</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">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
#include "ovasCDriverBrainProductsBrainVisionRecorder.h"
|
||||
#include "../ovasCConfigurationNetworkBuilder.h"
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
CDriverBrainProductsBrainVisionRecorder::CDriverBrainProductsBrainVisionRecorder(IDriverContext& ctx)
|
||||
: IDriver(ctx), m_settings("AcquisitionServer_Driver_BrainVisionRecorder", m_driverCtx.getConfigurationManager())
|
||||
{
|
||||
m_settings.add("Header", &m_header);
|
||||
m_settings.add("ServerHostName", &m_sServerHostName);
|
||||
m_settings.add("ServerHostPort", &m_serverHostPort);
|
||||
m_settings.load();
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDriverBrainProductsBrainVisionRecorder::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected()) { return false; }
|
||||
|
||||
// Initialize GUID value
|
||||
M_DEFINE_GUID(GUID_RDAHeader, 1129858446, 51606, 19590, uint8_t(175), uint8_t(74), uint8_t(152), uint8_t(187), uint8_t(246), uint8_t(201), uint8_t(20),
|
||||
uint8_t(80));
|
||||
|
||||
// Builds up client connection
|
||||
m_connectionClient = Socket::createConnectionClient();
|
||||
|
||||
// Tries to connect to server
|
||||
m_connectionClient->connect(m_sServerHostName, m_serverHostPort);
|
||||
|
||||
// Checks if connection is correctly established
|
||||
if (!m_connectionClient->isConnected())
|
||||
{
|
||||
// In case it is not, try to reconnect
|
||||
m_connectionClient->connect(m_sServerHostName, m_serverHostPort);
|
||||
}
|
||||
|
||||
if (!m_connectionClient->isConnected())
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Connection problem! Tried 2 times without success! :(\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Verify port number and/or Hostname...\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "> Client connected\n";
|
||||
|
||||
// Initialize vars for reception
|
||||
m_msgHeader = nullptr;
|
||||
RDA_MessageHeader msgHeader;
|
||||
m_msgHeaderStr = (char*)&msgHeader;
|
||||
|
||||
uint32_t received = 0;
|
||||
uint32_t size = sizeof(msgHeader);
|
||||
|
||||
// Receive Header
|
||||
while (received < size)
|
||||
{
|
||||
const uint32_t reqLength = size - received;
|
||||
const uint32_t res = m_connectionClient->receiveBuffer((char*)m_msgHeaderStr, reqLength);
|
||||
|
||||
received += res;
|
||||
m_msgHeaderStr += res;
|
||||
}
|
||||
|
||||
// Check for correct header GUID.
|
||||
if (!M_COMPARE_GUID(msgHeader.guid, GUID_RDAHeader))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "GUID received is not correct!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for correct header nType
|
||||
if (msgHeader.nType != 1)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "First Message received is not an header!\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Try to reconnect....\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we need a larger buffer, allocate
|
||||
if (!reallocateHeaderBuffer(msgHeader.nSize)) { return false; }
|
||||
|
||||
// Retrieve rest of data
|
||||
memcpy(*(&m_msgHeader), &msgHeader, sizeof(msgHeader));
|
||||
m_msgHeaderStr = (char*)(*(&m_msgHeader)) + sizeof(msgHeader);
|
||||
received = 0;
|
||||
size = msgHeader.nSize - sizeof(msgHeader);
|
||||
while (received < size)
|
||||
{
|
||||
const uint32_t reqLength = size - received;
|
||||
const uint32_t res = m_connectionClient->receiveBuffer((char*)m_msgHeaderStr, reqLength);
|
||||
|
||||
received += res;
|
||||
m_msgHeaderStr += res;
|
||||
}
|
||||
|
||||
m_msgStart = (RDA_MessageStart*)m_msgHeader; // pHeader will retain the pointer ownership
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "> Header received\n";
|
||||
|
||||
// Save Header info into m_header
|
||||
//m_header.setExperimentID();
|
||||
//m_header.setExperimentDate();
|
||||
|
||||
//m_header.setSubjectId();
|
||||
//m_header.setSubjectName();
|
||||
//m_header.setSubjectAge(m_structHeader.subjectAge);
|
||||
//m_header.setSubjectGender();
|
||||
|
||||
//m_header.setLaboratoryId();
|
||||
//m_header.setLaboratoryName();
|
||||
|
||||
//m_header.setTechnicianId();
|
||||
//m_header.setTechnicianName();
|
||||
|
||||
m_header.setChannelCount((uint32_t)m_msgStart->nChannels);
|
||||
|
||||
char* channelNames = (char*)m_msgStart->dResolutions + (m_msgStart->nChannels * sizeof(m_msgStart->dResolutions[0]));
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
m_header.setChannelName(i, channelNames);
|
||||
m_header.setChannelGain(i, float((m_msgStart->dResolutions[i])));
|
||||
m_header.setChannelUnits(i, OVTK_UNIT_Volts, OVTK_FACTOR_Micro);
|
||||
channelNames += strlen(channelNames) + 1;
|
||||
}
|
||||
|
||||
m_header.setSamplingFrequency(uint32_t(1000000 / m_msgStart->dSamplingInterval)); //dSamplingInterval in microseconds
|
||||
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
|
||||
m_callback = &callback;
|
||||
|
||||
m_indexIn = 0;
|
||||
m_indexOut = 0;
|
||||
m_buffDataIdx = 0;
|
||||
|
||||
m_nMarker = 0;
|
||||
m_nMarkers = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverBrainProductsBrainVisionRecorder::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverBrainProductsBrainVisionRecorder::loop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (!m_driverCtx.isStarted()) { return true; }
|
||||
|
||||
M_DEFINE_GUID(GUID_RDAHeader,
|
||||
1129858446, 51606, 19590, uint8_t(175), uint8_t(74), uint8_t(152), uint8_t(187), uint8_t(246), uint8_t(201), uint8_t(20), uint8_t(80)
|
||||
);
|
||||
|
||||
// Initialize var to receive buffer of data
|
||||
m_msgHeader = nullptr;
|
||||
RDA_MessageHeader msgHeader;
|
||||
m_msgHeaderStr = (char*)&msgHeader;
|
||||
uint32_t received = 0;
|
||||
uint32_t size = sizeof(msgHeader);
|
||||
|
||||
// Receive Header
|
||||
while (received < size)
|
||||
{
|
||||
const uint32_t reqLength = size - received;
|
||||
const uint32_t res = m_connectionClient->receiveBuffer((char*)m_msgHeaderStr, reqLength);
|
||||
received += res;
|
||||
m_msgHeaderStr += res;
|
||||
}
|
||||
|
||||
// Check for correct header nType
|
||||
if (msgHeader.nType == 1)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Message received is a header!\n";
|
||||
return false;
|
||||
}
|
||||
if (msgHeader.nType == 3)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Message received is a STOP!\n";
|
||||
return false;
|
||||
}
|
||||
if (msgHeader.nType != 4) { return true; }
|
||||
|
||||
// Check for correct header GUID.
|
||||
if (!M_COMPARE_GUID(msgHeader.guid, GUID_RDAHeader))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "GUID received is not correct!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we need a larger buffer, allocate
|
||||
if (!reallocateHeaderBuffer(msgHeader.nSize)) { return false; }
|
||||
|
||||
// Retrieve rest of block.
|
||||
memcpy(*(&m_msgHeader), &msgHeader, sizeof(msgHeader));
|
||||
m_msgHeaderStr = (char*)(*(&m_msgHeader)) + sizeof(msgHeader);
|
||||
received = 0;
|
||||
size = msgHeader.nSize - sizeof(msgHeader);
|
||||
while (received < size)
|
||||
{
|
||||
const uint32_t reqLength = size - received;
|
||||
const uint32_t res = m_connectionClient->receiveBuffer((char*)m_msgHeaderStr, reqLength);
|
||||
|
||||
received += res;
|
||||
m_msgHeaderStr += res;
|
||||
}
|
||||
m_buffDataIdx++;
|
||||
|
||||
// Put the data into MessageData32 structure
|
||||
m_msgData = nullptr;
|
||||
m_msgData = (RDA_MessageData32*)m_msgHeader;
|
||||
|
||||
//////////////////////
|
||||
//Markers
|
||||
if (m_msgData->nMarkers > 0)
|
||||
{
|
||||
// if (m_msgData->nMarkers == 0)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
m_marker = (RDA_Marker*)((char*)m_msgData->fData + m_msgData->nPoints * m_header.getChannelCount() * sizeof(m_msgData->fData[0]));
|
||||
|
||||
m_nMarkers = m_msgData->nMarkers;
|
||||
|
||||
m_stimulationIDs.assign(m_nMarkers, 0);
|
||||
m_stimulationDates.assign(m_nMarkers, 0);
|
||||
m_stimulationSamples.assign(m_nMarkers, 0);
|
||||
|
||||
for (uint32_t i = 0; i < m_msgData->nMarkers; ++i)
|
||||
{
|
||||
char* type = m_marker->sTypeDesc;
|
||||
char* desc = type + strlen(type) + 1;
|
||||
|
||||
m_stimulationIDs[i] = atoi(strtok(desc, "S"));
|
||||
m_stimulationDates[i] = CTime(m_header.getSamplingFrequency(), m_marker->nPosition).time();
|
||||
m_stimulationSamples[i] = m_marker->nPosition;
|
||||
m_marker = (RDA_Marker*)((char*)m_marker + m_marker->nSize);
|
||||
}
|
||||
|
||||
m_nMarker += m_msgData->nMarkers;
|
||||
}
|
||||
|
||||
size = m_header.getChannelCount() * uint32_t(m_msgData->nPoints);
|
||||
if (m_signalBuffers.size() < size) { m_signalBuffers.resize(size); }
|
||||
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < uint32_t(m_msgData->nPoints); ++j)
|
||||
{
|
||||
m_signalBuffers[j + (i * uint32_t(m_msgData->nPoints))] =
|
||||
float(m_msgData->fData[(m_header.getChannelCount() * j) + i]) * m_header.getChannelGain(i);
|
||||
}
|
||||
}
|
||||
|
||||
// send data
|
||||
CStimulationSet stimSet;
|
||||
stimSet.setStimulationCount(m_nMarkers);
|
||||
for (uint32_t i = 0; i < m_nMarkers; ++i)
|
||||
{
|
||||
stimSet.setStimulationIdentifier(i, OVTK_StimulationId_Label(m_stimulationIDs[i]));
|
||||
stimSet.setStimulationDate(i, m_stimulationDates[i]);
|
||||
stimSet.setStimulationDuration(i, 0);
|
||||
}
|
||||
|
||||
m_callback->setSamples(&m_signalBuffers[0], uint32_t(m_msgData->nPoints));
|
||||
m_callback->setStimulationSet(stimSet);
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
|
||||
m_nMarkers = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverBrainProductsBrainVisionRecorder::stop()
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "> Connection stopped\n";
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverBrainProductsBrainVisionRecorder::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
if (m_msgHeader)
|
||||
{
|
||||
free(m_msgHeader);
|
||||
m_msgHeader = nullptr;
|
||||
}
|
||||
|
||||
m_msgHeaderStr = nullptr;
|
||||
m_msgStart = nullptr;
|
||||
m_msgStop = nullptr;
|
||||
m_msgData = nullptr;
|
||||
m_marker = nullptr;
|
||||
|
||||
m_callback = nullptr;
|
||||
|
||||
m_headerBufferSize = 0;
|
||||
m_signalBuffers.clear();
|
||||
|
||||
// Cleans up client connection
|
||||
m_connectionClient->close();
|
||||
m_connectionClient->release();
|
||||
m_connectionClient = nullptr;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "> Client disconnected\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDriverBrainProductsBrainVisionRecorder::configure()
|
||||
{
|
||||
CConfigurationNetworkBuilder
|
||||
config(Directories::getDataDir() + "/applications/acquisition-server/interface-BrainProducts-BrainVisionRecorder.ui");
|
||||
|
||||
config.setHostName(m_sServerHostName);
|
||||
config.setHostPort(m_serverHostPort);
|
||||
|
||||
if (config.configure(m_header))
|
||||
{
|
||||
m_sServerHostName = config.getHostName();
|
||||
m_serverHostPort = config.getHostPort();
|
||||
|
||||
m_settings.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDriverBrainProductsBrainVisionRecorder::reallocateHeaderBuffer(const size_t newSize)
|
||||
{
|
||||
// Reallocate buffer?
|
||||
if (newSize > m_headerBufferSize)
|
||||
{
|
||||
if (m_msgHeader) { free(m_msgHeader); }
|
||||
m_headerBufferSize = newSize;
|
||||
m_msgHeader = (RDA_MessageHeader*)malloc(m_headerBufferSize);
|
||||
if (!m_msgHeader)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Couldn't allocate memory\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <socket/IConnectionClient.h>
|
||||
|
||||
#ifndef M_DEFINE_GUID
|
||||
#define M_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
#endif
|
||||
|
||||
#ifndef M_COMPARE_GUID
|
||||
#define M_COMPARE_GUID(name1, name2) \
|
||||
( name1.Data1 == name2.Data1 && name1.Data2 == name2.Data2 && \
|
||||
name1.Data3 == name2.Data3 && name1.Data4[0] == name2.Data4[0] && \
|
||||
name1.Data4[1] == name2.Data4[1] && name1.Data4[2] == name2.Data4[2] &&\
|
||||
name1.Data4[3] == name2.Data4[3] && name1.Data4[4] == name2.Data4[4] &&\
|
||||
name1.Data4[5] == name2.Data4[5] && name1.Data4[6] == name2.Data4[6] &&\
|
||||
name1.Data4[7] == name2.Data4[7] \
|
||||
)
|
||||
#endif
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
class CDriverBrainProductsBrainVisionRecorder final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CDriverBrainProductsBrainVisionRecorder(IDriverContext& ctx);
|
||||
~CDriverBrainProductsBrainVisionRecorder() override {}
|
||||
const char* getName() override { return "Brain Products amplifiers (through BrainVision Recorder)"; }
|
||||
|
||||
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; }
|
||||
|
||||
protected:
|
||||
|
||||
SettingsHelper m_settings;
|
||||
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
Socket::IConnectionClient* m_connectionClient = nullptr;
|
||||
CString m_sServerHostName = "localhost";
|
||||
uint32_t m_serverHostPort = 51244;
|
||||
|
||||
CHeader m_header;
|
||||
|
||||
uint32_t m_nSamplePerSentBlock = 0;
|
||||
|
||||
uint32_t m_indexIn = 0;
|
||||
uint32_t m_indexOut = 0;
|
||||
uint32_t m_buffDataIdx = 0;
|
||||
|
||||
uint32_t m_nMarker = 0;
|
||||
uint32_t m_nMarkers = 0;
|
||||
|
||||
std::vector<size_t> m_stimulationIDs;
|
||||
std::vector<size_t> m_stimulationDates;
|
||||
std::vector<size_t> m_stimulationSamples;
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t Data1;
|
||||
unsigned short Data2;
|
||||
unsigned short Data3;
|
||||
uint8_t Data4[8];
|
||||
} GUID;
|
||||
|
||||
struct RDA_Marker //; A single marker in the marker array of RDA_MessageData
|
||||
{
|
||||
uint32_t nSize; // Size of this marker.
|
||||
uint32_t nPosition; // Relative position in the data block.
|
||||
uint32_t nPoints; // Number of points of this marker
|
||||
int nChannel; // Associated channel number (-1 = all channels).
|
||||
char sTypeDesc[1]; // Type, description in ASCII delimited by '\0'.
|
||||
};
|
||||
|
||||
struct RDA_MessageHeader //; Message header
|
||||
{
|
||||
GUID guid; // Always GUID_RDAHeader
|
||||
uint32_t nSize; // Size of the message block in bytes including this header
|
||||
uint32_t nType; // Message type.
|
||||
};
|
||||
|
||||
// **** Messages sent by the RDA server to the clients. ****
|
||||
struct RDA_MessageStart : RDA_MessageHeader //; Setup / Start infos, Header -> nType = 1
|
||||
{
|
||||
uint32_t nChannels; // Number of channels
|
||||
double dSamplingInterval; // Sampling interval in microseconds
|
||||
double dResolutions[1]; // Array of channel resolutions -> double dResolutions[nChannels] coded in microvolts. i.e. RealValue = resolution * A/D value
|
||||
char sChannelNames[1]; // Channel names delimited by '\0'. The real size is larger than 1.
|
||||
};
|
||||
|
||||
struct RDA_MessageStop : RDA_MessageHeader { }; //; Data acquisition has been stopped. // Header -> nType = 3
|
||||
|
||||
struct RDA_MessageData32 : RDA_MessageHeader //; Block of 32-bit floating point data, Header -> nType = 4, sent only from port 51244
|
||||
{
|
||||
uint32_t nBlock; // Block number, i.e. acquired blocks since acquisition started.
|
||||
uint32_t nPoints; // Number of data points in this block
|
||||
uint32_t nMarkers; // Number of markers in this data block
|
||||
float fData[1]; // Data array -> float fData[nChannels * nPoints], multiplexed
|
||||
RDA_Marker Markers[1]; // Array of markers -> RDA_Marker Markers[nMarkers]
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
RDA_MessageHeader* m_msgHeader = nullptr;
|
||||
char* m_msgHeaderStr = nullptr;
|
||||
size_t m_headerBufferSize = 0;
|
||||
|
||||
RDA_MessageStart* m_msgStart = nullptr;
|
||||
RDA_MessageStop* m_msgStop = nullptr;
|
||||
RDA_MessageData32* m_msgData = nullptr;
|
||||
RDA_Marker* m_marker = nullptr;
|
||||
|
||||
std::vector<float> m_signalBuffers;
|
||||
|
||||
private:
|
||||
|
||||
bool reallocateHeaderBuffer(size_t newSize);
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 2.6 -->
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="upper">100</property>
|
||||
<property name="value">18</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="lower">1</property>
|
||||
<property name="upper">128</property>
|
||||
<property name="value">128</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment4">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">254</property>
|
||||
<property name="value">1</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model1">
|
||||
<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="model2">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">300</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">500</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">540</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">600</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">1000</col>
|
||||
</row>
|
||||
</data>
|
||||
</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_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_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">Cognionics</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">7</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_gender">
|
||||
<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">2</property>
|
||||
<property name="bottom_attach">3</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">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">1</property>
|
||||
<property name="bottom_attach">2</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="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">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">3</property>
|
||||
<property name="bottom_attach">4</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="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_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="GtkComboBox" id="combobox_sampling_frequency">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">model2</property>
|
||||
<property name="active">0</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">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
|
||||
<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">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0.49000000953674316</property>
|
||||
<property name="label" translatable="yes">COM Port:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_comport">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</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">adjustment4</property>
|
||||
</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="label2">
|
||||
<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">6</property>
|
||||
<property name="bottom_attach">7</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="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>
|
||||
</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_change_channel_names">
|
||||
<property name="label" translatable="yes">Change channel names</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</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>
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#if defined(WIN32)
|
||||
|
||||
#include "ovasCConfigurationCognionics.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
/*_________________________________________________
|
||||
|
||||
Insert callback to specific widget here
|
||||
Example with a button that launch a calibration of the device:
|
||||
|
||||
//Callback connected to a dedicated gtk button:
|
||||
static void button_calibrate_pressed_cb(GtkButton* button, void* data)
|
||||
{
|
||||
CConfigurationCognionics* config=static_cast<CConfigurationCognionics*>(data);
|
||||
config->buttonCalibratePressedCB();
|
||||
}
|
||||
|
||||
//Callback actually called:
|
||||
void CConfigurationGTecGUSBamp::buttonCalibratePressedCB()
|
||||
{
|
||||
// Connect to the hardware, ask for calibration, verify the return code, etc.
|
||||
}
|
||||
_________________________________________________*/
|
||||
|
||||
// If you added more reference attribute, initialize them here
|
||||
CConfigurationCognionics::CConfigurationCognionics(IDriverContext& ctx, const char* gtkBuilderFilename, uint32_t& comport)
|
||||
: CConfigurationBuilder(gtkBuilderFilename), m_COMPORT(comport), m_driverCtx(ctx) {}
|
||||
|
||||
bool CConfigurationCognionics::preConfigure()
|
||||
{
|
||||
if (! CConfigurationBuilder::preConfigure()) { return false; }
|
||||
|
||||
GtkSpinButton* comport = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder, "spinbutton_comport"));
|
||||
|
||||
gtk_spin_button_set_value(comport, m_COMPORT);
|
||||
|
||||
// Connect here all callbacks
|
||||
// Example:
|
||||
// g_signal_connect(gtk_builder_get_object(m_builder, "button_calibrate"), "pressed", G_CALLBACK(button_calibrate_pressed_cb), this);
|
||||
|
||||
// Insert here the pre-configure code.
|
||||
// For example, you may want to check if a device is currently connected
|
||||
// and if more than one are connected. Then you can list in a dedicated combo-box
|
||||
// the device currently connected so the user can choose which one he wants to acquire from.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfigurationCognionics::postConfigure()
|
||||
{
|
||||
if (m_applyConfig)
|
||||
{
|
||||
GtkSpinButton* comport = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder, "spinbutton_comport"));
|
||||
|
||||
m_COMPORT = uint32_t(gtk_spin_button_get_value(comport));
|
||||
//printf("Selected COM Port: %d", m_COMPORT);
|
||||
// If the user pressed the "apply" button, you need to save the changes made in the configuration.
|
||||
// For example, you can save the connection ID of the selected device:
|
||||
// m_connectionID = <value-from-gtk-widget>
|
||||
}
|
||||
|
||||
if (! CConfigurationBuilder::postConfigure()
|
||||
) { return false; } // normal header is filled (Subject ID, Age, Gender, channels, sampling frequency), ressources are realesed
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // WIN32
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
#include "ovasIDriver.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CConfigurationCognionics
|
||||
* \author Mike Chi (Cognionics, Inc.)
|
||||
* \copyright AGPL3
|
||||
* \date Thu Apr 18 21:19:49 2013
|
||||
* \brief The CConfigurationCognionics handles the configuration dialog specific to the Cognionics device.
|
||||
*
|
||||
* TODO: details
|
||||
*
|
||||
* \sa CDriverCognionics
|
||||
*/
|
||||
class CConfigurationCognionics final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
// you may have to add to your constructor some reference parameters
|
||||
// for example, a connection ID:
|
||||
//CConfigurationCognionics(IDriverContext& ctx, const char* gtkBuilderFilename, uint32_t& rConnectionId);
|
||||
CConfigurationCognionics(IDriverContext& ctx, const char* gtkBuilderFilename, uint32_t& comport);
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
|
||||
uint32_t& m_COMPORT;
|
||||
|
||||
|
||||
protected:
|
||||
IDriverContext& m_driverCtx;
|
||||
|
||||
/*
|
||||
* Insert here all specific attributes, such as a connection ID.
|
||||
* use references to directly modify the corresponding attribute of the driver
|
||||
* Example:
|
||||
*/
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+398
@@ -0,0 +1,398 @@
|
||||
#include "ovasCDriverCognionics.h"
|
||||
#include "ovasCConfigurationCognionics.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
//Cognionics functions
|
||||
|
||||
int init_SPP(int port);
|
||||
void getData();
|
||||
double get_IMP_CH(int ch);
|
||||
void close_SPP();
|
||||
unsigned long grab_SPP(int bytes, unsigned char* buf);
|
||||
void write_SPP(int bytes, unsigned char* buf);
|
||||
|
||||
//Cognionics defs
|
||||
#define GAIN 3.0
|
||||
#define VREF 2.5
|
||||
#define ISTIM 0.000000024
|
||||
#define ADC_TO_VOLTS 2.0*(VREF/(4294967296.0*GAIN))
|
||||
#define TO_Z 1.4/(ISTIM*2.0)
|
||||
|
||||
//Cognionics variables
|
||||
int EEG_GRAB;
|
||||
int CHS;
|
||||
int SAMPLE_RATE;
|
||||
float* dataBufferPtr;
|
||||
int prev_trigger;
|
||||
CStimulationSet cogStimulationSet;
|
||||
|
||||
//Cognionics Buffers
|
||||
int* prev_4_samples; //buffer for previous 4 samples in each channel for computing MA and IMP
|
||||
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
CDriverCognionics::CDriverCognionics(IDriverContext& ctx)
|
||||
: IDriver(ctx), m_settings("AcquisitionServer_Driver_Cognionics", m_driverCtx.getConfigurationManager()), m_comPort(1)
|
||||
{
|
||||
m_header.setSamplingFrequency(300);
|
||||
m_header.setChannelCount(64);
|
||||
|
||||
m_settings.add("Header", &m_header);
|
||||
m_settings.add("ComPort", &m_comPort);
|
||||
m_settings.load();
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDriverCognionics::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected() || !m_header.isChannelCountSet() || !m_header.isSamplingFrequencySet()) { return false; }
|
||||
|
||||
// Builds up a buffer to store
|
||||
// acquired samples. This buffer
|
||||
// will be sent to the acquisition
|
||||
// server later...
|
||||
m_sample = new float[m_header.getChannelCount() * nSamplePerSentBlock];
|
||||
|
||||
//initalize Cognionics varaibles, pointers and buffers
|
||||
EEG_GRAB = nSamplePerSentBlock;
|
||||
SAMPLE_RATE = m_header.getSamplingFrequency();
|
||||
CHS = m_header.getChannelCount();
|
||||
dataBufferPtr = &m_sample[0];
|
||||
prev_4_samples = new int[EEG_GRAB * CHS];
|
||||
|
||||
if (!m_sample)
|
||||
{
|
||||
delete [] m_sample;
|
||||
delete [] prev_4_samples;
|
||||
m_sample = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ...
|
||||
// initialize hardware and get
|
||||
// available header information
|
||||
// from it
|
||||
// Using for example the connection ID provided by the configuration (m_connectionID)
|
||||
// ...
|
||||
|
||||
//init serial port
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Attempting to Connect to Device at COM Port: " << m_comPort << "\n";
|
||||
|
||||
const int serSuccess = init_SPP(m_comPort);
|
||||
if (serSuccess == -1)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unable to Open Port, Please Check Device and Settings\n";
|
||||
return false;
|
||||
}
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Successfully Opened Port at COM: " << m_comPort << "\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Number Channels (EEG + Packet Counter + Trigger): " << m_header.getChannelCount() << "\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Sampling Rate: " << m_header.getSamplingFrequency() << "\n";
|
||||
|
||||
for (size_t c = 0; c < m_header.getChannelCount(); ++c) { m_header.setChannelUnits(c, OVTK_UNIT_Volts, OVTK_FACTOR_Base); }
|
||||
|
||||
//set impedance check mode ON
|
||||
getData();
|
||||
unsigned char imp_on = 0x11;
|
||||
write_SPP(1, &imp_on);
|
||||
|
||||
// Saves parameters
|
||||
m_callback = &callback;
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverCognionics::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// request hardware to start
|
||||
// sending data
|
||||
// ...
|
||||
|
||||
//set impedance check mode off
|
||||
getData();
|
||||
unsigned char imp_off = 0x12;
|
||||
write_SPP(1, &imp_off);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverCognionics::loop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
|
||||
if (m_driverCtx.isStarted())
|
||||
{
|
||||
// ...
|
||||
// receive samples from hardware
|
||||
// put them the correct way in the sample array
|
||||
// whether the buffer is full, send it to the acquisition server
|
||||
//...
|
||||
|
||||
//get the number of samples specified
|
||||
getData();
|
||||
|
||||
//OpenVibe call back for new samples
|
||||
m_callback->setSamples(m_sample);
|
||||
|
||||
// When your sample buffer is fully loaded,
|
||||
// it is advised to ask the acquisition server
|
||||
// to correct any drift in the acquisition automatically.
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
|
||||
// ...
|
||||
// receive events from hardware
|
||||
// and put them the correct way in a CStimulationSet object
|
||||
//...
|
||||
m_callback->setStimulationSet(cogStimulationSet);
|
||||
cogStimulationSet.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
//get a fresh batch of samples to keep connection alive
|
||||
getData();
|
||||
if (m_driverCtx.isImpedanceCheckRequested())
|
||||
{
|
||||
//update impedance values
|
||||
for (int c = 0; c < CHS; ++c) { m_driverCtx.updateImpedance(c, get_IMP_CH(c)); }
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverCognionics::stop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// request the hardware to stop
|
||||
// sending data
|
||||
// ...
|
||||
|
||||
//set impedance check mode on
|
||||
getData();
|
||||
unsigned char imp_off = 0x11;
|
||||
write_SPP(1, &imp_off);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverCognionics::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// uninitialize hardware here
|
||||
// ...
|
||||
close_SPP();
|
||||
|
||||
//free buffers
|
||||
delete [] m_sample;
|
||||
delete [] prev_4_samples;
|
||||
m_sample = nullptr;
|
||||
m_callback = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDriverCognionics::isConfigurable()
|
||||
{
|
||||
return true; // change to false if your device is not configurable
|
||||
}
|
||||
|
||||
bool CDriverCognionics::configure()
|
||||
{
|
||||
// Change this line if you need to specify some references to your driver attribute that need configuration, e.g. the connection ID.
|
||||
CConfigurationCognionics config(m_driverCtx,
|
||||
Directories::getDataDir() + "/applications/acquisition-server/interface-Cognionics.ui",
|
||||
m_comPort); // the specific header is passed into the specific configuration
|
||||
|
||||
if (!config.configure(m_header)) { return false; }
|
||||
|
||||
m_settings.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void getData()
|
||||
{
|
||||
unsigned char packet_start = 0;
|
||||
int lsb2, lsb1;
|
||||
unsigned char temp;
|
||||
|
||||
for (int c = 0; c < EEG_GRAB; ++c)
|
||||
{
|
||||
packet_start = 0;
|
||||
|
||||
//wait for packet start
|
||||
while (packet_start != 0xFF) { grab_SPP(1, &packet_start); }
|
||||
|
||||
//get packet counter
|
||||
grab_SPP(1, &packet_start);
|
||||
|
||||
//grab CHS-2 since the last two channels are for sync and packet counter
|
||||
for (int j = 0; j < (CHS - 2); ++j)
|
||||
{
|
||||
grab_SPP(1, &temp);
|
||||
int msb = temp;
|
||||
grab_SPP(1, &temp);
|
||||
lsb2 = temp;
|
||||
grab_SPP(1, &temp);
|
||||
lsb1 = temp;
|
||||
|
||||
//reassemble 24-bit 2's compltement promoted to 32 bit int
|
||||
msb = (msb << 24) | (lsb2 << 17) | (lsb1 << 10);
|
||||
|
||||
//shift previous 4 sample buffer
|
||||
*(prev_4_samples + j * 4 + 3) = *(prev_4_samples + j * 4 + 2);
|
||||
*(prev_4_samples + j * 4 + 2) = *(prev_4_samples + j * 4 + 1);
|
||||
*(prev_4_samples + j * 4 + 1) = *(prev_4_samples + j * 4 + 0);
|
||||
*(prev_4_samples + j * 4 + 0) = msb;
|
||||
|
||||
const double sample = ADC_TO_VOLTS * double(msb);
|
||||
|
||||
//4-point MA for devices without way to disable impedance
|
||||
/*
|
||||
eeg_sample = ADC_TO_VOLTS * ((double) *(prev_4_samples + j*4 + 3) +
|
||||
*(prev_4_samples + j*4 + 2) +
|
||||
*(prev_4_samples + j*4 + 1) +
|
||||
*(prev_4_samples + j*4 + 0) )/4.0;
|
||||
*/
|
||||
|
||||
*(dataBufferPtr + j * EEG_GRAB + c) = float(sample);
|
||||
}
|
||||
|
||||
|
||||
//save packet counter;
|
||||
*(dataBufferPtr + (CHS - 1) * EEG_GRAB + c) = float(packet_start);
|
||||
|
||||
//dummy data
|
||||
grab_SPP(1, &temp);
|
||||
grab_SPP(1, &temp);
|
||||
|
||||
//grab trigger
|
||||
grab_SPP(1, &temp);
|
||||
lsb2 = temp;
|
||||
grab_SPP(1, &temp);
|
||||
lsb1 = temp;
|
||||
|
||||
//assemble trigger code
|
||||
int newTrigger = (lsb2 << 8) | lsb1;
|
||||
//downshift serial triggers if necessary
|
||||
if (newTrigger > 255) { newTrigger = newTrigger >> 8; }
|
||||
|
||||
//store in EEG channel
|
||||
*(dataBufferPtr + (CHS - 2) * EEG_GRAB + c) = float(newTrigger);
|
||||
|
||||
//detect new stimulation
|
||||
if (newTrigger != prev_trigger)
|
||||
{
|
||||
//time offset from start of chunk (c - current sample in the chunk)
|
||||
const uint64_t date = c * (1LL << 32) / SAMPLE_RATE;
|
||||
cogStimulationSet.appendStimulation(OVTK_StimulationId_Label(newTrigger&0x000000ff), date, 0);
|
||||
}
|
||||
|
||||
prev_trigger = newTrigger;
|
||||
}
|
||||
}
|
||||
|
||||
double get_IMP_CH(const int ch)
|
||||
{
|
||||
double d1 = double(*(prev_4_samples + ch * 4 + 2)) - double(*(prev_4_samples + ch * 4 + 0));
|
||||
double d2 = double(*(prev_4_samples + ch * 4 + 3)) - double(*(prev_4_samples + ch * 4 + 1));
|
||||
d1 = d1 * ADC_TO_VOLTS;
|
||||
d2 = d2 * ADC_TO_VOLTS;
|
||||
|
||||
if (d1 < 0) { d1 = -d1; }
|
||||
if (d2 < 0) { d2 = -d2; }
|
||||
|
||||
if (d2 > d1) { d1 = d2; }
|
||||
|
||||
return d1 * TO_Z;
|
||||
}
|
||||
|
||||
//serial port handling
|
||||
|
||||
HANDLE hSerial;
|
||||
COMMTIMEOUTS timeouts = { 0 };
|
||||
|
||||
int init_SPP(const int port)
|
||||
{
|
||||
char com[100];
|
||||
|
||||
sprintf(com, "\\\\.\\COM%d", port);
|
||||
|
||||
hSerial = CreateFile(com, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
|
||||
if (hSerial == INVALID_HANDLE_VALUE) { return -1; }
|
||||
|
||||
//setup serial port parameters
|
||||
DCB dcbSerialParams = { 0 };
|
||||
|
||||
if (!GetCommState(hSerial, &dcbSerialParams))
|
||||
{
|
||||
//error getting state
|
||||
}
|
||||
|
||||
dcbSerialParams.BaudRate = 1500000;
|
||||
dcbSerialParams.ByteSize = 8;
|
||||
dcbSerialParams.StopBits = ONESTOPBIT;
|
||||
dcbSerialParams.Parity = NOPARITY;
|
||||
|
||||
if (!SetCommState(hSerial, &dcbSerialParams))
|
||||
{
|
||||
//error setting serial port state
|
||||
}
|
||||
|
||||
//setup serial port timeout
|
||||
COMMTIMEOUTS timeouts;
|
||||
timeouts.ReadIntervalTimeout = 500;
|
||||
timeouts.ReadTotalTimeoutConstant = 50;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 10;
|
||||
|
||||
timeouts.WriteTotalTimeoutConstant = 50;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 10;
|
||||
if (!SetCommTimeouts(hSerial, &timeouts))
|
||||
{
|
||||
//error occureed. Inform user
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void close_SPP() { CloseHandle(hSerial); }
|
||||
|
||||
unsigned long grab_SPP(const int bytes, unsigned char* buf)
|
||||
{
|
||||
DWORD dwBytesRead = 0;
|
||||
ReadFile(hSerial, buf, bytes, &dwBytesRead, nullptr);
|
||||
|
||||
return int(dwBytesRead);
|
||||
}
|
||||
|
||||
void write_SPP(const int bytes, unsigned char* buf)
|
||||
{
|
||||
DWORD dwBytesWritten = 0;
|
||||
WriteFile(hSerial, buf, bytes, &dwBytesWritten, nullptr);
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif //WIN32
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CDriverCognionics
|
||||
* \author Mike Chi (Cognionics, Inc.)
|
||||
* \copyright AGPL3
|
||||
* \date Thu Apr 18 21:19:49 2013
|
||||
|
||||
* \brief The CDriverCognionics allows the acquisition server to acquire data from a Cognionics device.
|
||||
*
|
||||
* TODO: details
|
||||
*
|
||||
* \sa CConfigurationCognionics
|
||||
*/
|
||||
class CDriverCognionics final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CDriverCognionics(IDriverContext& ctx);
|
||||
~CDriverCognionics() override {}
|
||||
const char* getName() override { return "Cognionics"; }
|
||||
|
||||
bool initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback) override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool start() override;
|
||||
bool stop() override;
|
||||
bool loop() override;
|
||||
|
||||
bool isConfigurable() override;
|
||||
bool configure() override;
|
||||
const IHeader* getHeader() override { return &m_header; }
|
||||
|
||||
bool isFlagSet(const EDriverFlag flag) const override { return flag == EDriverFlag::IsUnstable; }
|
||||
|
||||
protected:
|
||||
|
||||
SettingsHelper m_settings;
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
|
||||
// Replace this generic Header with any specific header you might have written
|
||||
CHeader m_header;
|
||||
uint32_t m_nSamplePerSentBlock = 0;
|
||||
float* m_sample = nullptr;
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Insert here all specific attributes, such as USB port number or device ID.
|
||||
* Example :
|
||||
*/
|
||||
uint32_t m_comPort = 0;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+639
@@ -0,0 +1,639 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 2.12 -->
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment2">
|
||||
<property name="upper">65535</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment3">
|
||||
<property name="value">1</property>
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">1024</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment4">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model1">
|
||||
<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="model2">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<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">1024</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">2048</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">4096</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
|
||||
<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>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox">
|
||||
<property name="visible">True</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>
|
||||
<object class="GtkLabel" id="label_title">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">CTF/VSM MEG</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox">
|
||||
<property name="visible">True</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="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">4</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_identifier">
|
||||
<property name="visible">True</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="GtkSpinButton" id="spinbutton_identifier">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_gender">
|
||||
<property name="visible">True</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">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_gender">
|
||||
<property name="visible">True</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">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_host_name">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Connection host name</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry_host_name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="text" translatable="yes">localhost</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>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_host_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_host_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Connection host port</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area">
|
||||
<property name="visible">True</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_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_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="pack_type">end</property>
|
||||
<property name="position">0</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>
|
||||
<object class="GtkWindow" id="dummy-widgets">
|
||||
<child>
|
||||
<object class="GtkTable" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">10</property>
|
||||
<property name="n_columns">10</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment3</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_sampling_frequency">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">model2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer2"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</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">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_age">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment4</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
#include "ovasCDriverCtfVsmMeg.h"
|
||||
#include "../ovasCConfigurationNetworkBuilder.h"
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
CDriverCtfVsmMeg::CDriverCtfVsmMeg(IDriverContext& ctx)
|
||||
: IDriver(ctx), m_settings("AcquisitionServer_Driver_CtfVsmMeg", m_driverCtx.getConfigurationManager())
|
||||
{
|
||||
m_settings.add("Header", &m_header);
|
||||
m_settings.add("ServerHostName", &m_sServerHostName);
|
||||
m_settings.add("ServerHostPort", &m_serverHostPort);
|
||||
m_settings.load();
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDriverCtfVsmMeg::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected()) { return false; }
|
||||
|
||||
// Builds up client connection
|
||||
m_connectionClient = Socket::createConnectionClient();
|
||||
|
||||
// Tries to connect to server
|
||||
m_connectionClient->connect(m_sServerHostName, m_serverHostPort);
|
||||
|
||||
// Checks if connection is correctly established
|
||||
if (!m_connectionClient->isConnected())
|
||||
{
|
||||
// In case it is not, try to reconnect
|
||||
m_connectionClient->connect(m_sServerHostName, m_serverHostPort);
|
||||
}
|
||||
|
||||
if (!m_connectionClient->isConnected())
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Connection problem! Tried 2 times without success! :(\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Verify port number and/or Hostname...\n";
|
||||
return false;
|
||||
}
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "> Client connected\n";
|
||||
// Initialize vars for reception
|
||||
uint32_t received = 0;
|
||||
m_pStructHeader = (char*)&m_structHeader;
|
||||
// Receive Header
|
||||
while (received < sizeof(m_structHeader))
|
||||
{
|
||||
const uint32_t reqLength = sizeof(m_structHeader) - received;
|
||||
const uint32_t res = m_connectionClient->receiveBuffer((char*)m_pStructHeader, reqLength);
|
||||
|
||||
received += res;
|
||||
m_pStructHeader += res;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "> Receiving Header....\n";
|
||||
}
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "> Header received\n";
|
||||
|
||||
// Save Header info into m_header
|
||||
//m_header.setExperimentID();
|
||||
//m_header.setExperimentDate();
|
||||
|
||||
//m_header.setSubjectId();
|
||||
//m_header.setSubjectName();
|
||||
m_header.setSubjectAge(m_structHeader.subjectAge);
|
||||
//m_header.setSubjectGender();
|
||||
|
||||
//m_header.setLaboratoryId();
|
||||
//m_header.setLaboratoryName();
|
||||
|
||||
//m_header.setTechnicianId();
|
||||
//m_header.setTechnicianName();
|
||||
|
||||
m_header.setChannelCount(m_structHeader.numberOfChannels);
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
m_header.setChannelName(i, m_structHeader.channelLabel[i]);
|
||||
m_header.setChannelGain(i, (m_structHeader.qGain[i] * m_structHeader.properGain[i]));
|
||||
}
|
||||
|
||||
m_header.setSamplingFrequency(uint32_t(m_structHeader.samplingRate));
|
||||
|
||||
//m_header.setSampleCount(nSamplePerSentBlock);
|
||||
|
||||
m_sample = new float[m_header.getChannelCount() * nSamplePerSentBlock];
|
||||
|
||||
if (!m_sample)
|
||||
{
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_callback = &callback;
|
||||
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
|
||||
m_indexIn = 0;
|
||||
m_indexOut = 0;
|
||||
m_socketFlag = 1976;
|
||||
m_buffDataIdx = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverCtfVsmMeg::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
if (m_buffDataIdx == 0)
|
||||
{
|
||||
// Initialize var to send flag
|
||||
uint32_t sent = 0;
|
||||
uint32_t* flag = static_cast<uint32_t*>(&m_socketFlag);
|
||||
|
||||
// Send flag to server
|
||||
while (sent < sizeof(m_socketFlag))
|
||||
{
|
||||
const uint32_t reqLength = sizeof(m_socketFlag) - sent;
|
||||
const uint32_t res = m_connectionClient->sendBuffer(static_cast<uint32_t*>(flag), reqLength);
|
||||
sent += res;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << " > Sending flag to start....\n";
|
||||
}
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "> Flag sent\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverCtfVsmMeg::loop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (!m_driverCtx.isStarted()) { return true; }
|
||||
|
||||
// Initialize var to receive buffer of data
|
||||
uint32_t received = 0;
|
||||
m_pStructBuffData = (char*)&m_structBuffData;
|
||||
|
||||
// Read a first buffer of data
|
||||
m_buffDataIdx++;
|
||||
while (received < sizeof(m_structBuffData))
|
||||
{
|
||||
const uint32_t reqLength = sizeof(m_structBuffData) - received;
|
||||
const uint32_t res = m_connectionClient->receiveBuffer((char*)m_pStructBuffData, reqLength);
|
||||
received += res;
|
||||
m_pStructBuffData += res;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << " > Receiving buffer of data....nb: " << m_buffDataIdx << "\n";
|
||||
}
|
||||
|
||||
// if input flow is equal to output one
|
||||
if (m_nSamplePerSentBlock == uint32_t(m_structBuffData.nbSamplesPerChanPerBlock))
|
||||
{
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < m_nSamplePerSentBlock; ++j)
|
||||
{
|
||||
if ((m_structHeader.channelTypeIndex[i] == 0) | (m_structHeader.channelTypeIndex[i] == 1) | (m_structHeader.channelTypeIndex[i] == 5))
|
||||
{
|
||||
m_sample[j + i * m_nSamplePerSentBlock] = float(
|
||||
float(m_structBuffData.data[m_header.getChannelCount() * j + i]) * 1e15 / m_header.getChannelGain(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sample[j + i * m_nSamplePerSentBlock] =
|
||||
float(m_structBuffData.data[m_header.getChannelCount() * j + i]) / m_header.getChannelGain(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
// send data
|
||||
m_callback->setSamples(m_sample);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if output flow is bigger
|
||||
if (m_nSamplePerSentBlock > uint32_t(m_structBuffData.nbSamplesPerChanPerBlock))
|
||||
{
|
||||
while (m_indexOut + uint32_t(m_structBuffData.nbSamplesPerChanPerBlock) < m_nSamplePerSentBlock)
|
||||
{
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < uint32_t(m_structBuffData.nbSamplesPerChanPerBlock); ++j)
|
||||
{
|
||||
if ((m_structHeader.channelTypeIndex[i] == 0) | (m_structHeader.channelTypeIndex[i] == 1) | (m_structHeader.channelTypeIndex[i] == 5))
|
||||
{
|
||||
m_sample[m_indexOut + j + i * m_nSamplePerSentBlock] = float(
|
||||
float(m_structBuffData.data[m_header.getChannelCount() * j + i]) * 1e15 / m_header.getChannelGain(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sample[m_indexOut + j + i * m_nSamplePerSentBlock] =
|
||||
float(m_structBuffData.data[m_header.getChannelCount() * j + i]) / m_header.getChannelGain(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_indexOut = m_indexOut + uint32_t(m_structBuffData.nbSamplesPerChanPerBlock);
|
||||
|
||||
// Ask for another buffer
|
||||
received = 0;
|
||||
m_pStructBuffData = (char*)&m_structBuffData;
|
||||
|
||||
while (received < sizeof(m_structBuffData))
|
||||
{
|
||||
const uint32_t reqLength = sizeof(m_structBuffData) - received;
|
||||
const uint32_t res = m_connectionClient->receiveBuffer((char*)m_pStructBuffData, reqLength);
|
||||
|
||||
received += res;
|
||||
m_pStructBuffData += res;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << " > Receiving buffer of data....nb: " << m_buffDataIdx << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Finishing filling up
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < m_nSamplePerSentBlock - m_indexOut; ++j)
|
||||
{
|
||||
if ((m_structHeader.channelTypeIndex[i] == 0) | (m_structHeader.channelTypeIndex[i] == 1) | (m_structHeader.channelTypeIndex[i] == 5))
|
||||
{
|
||||
m_sample[m_indexOut + j + i * m_nSamplePerSentBlock] = float(
|
||||
float(m_structBuffData.data[m_header.getChannelCount() * j + i]) * 1e15 / m_header.getChannelGain(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sample[m_indexOut + j + i * m_nSamplePerSentBlock] =
|
||||
float(m_structBuffData.data[m_header.getChannelCount() * j + i]) / m_header.getChannelGain(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send data
|
||||
m_callback->setSamples(m_sample);
|
||||
|
||||
// Reset index out because new output
|
||||
m_indexIn = m_nSamplePerSentBlock - m_indexOut;
|
||||
m_indexOut = 0;
|
||||
|
||||
// Save the rest of the buffer input
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < uint32_t(m_structBuffData.nbSamplesPerChanPerBlock) - m_indexIn; ++j)
|
||||
{
|
||||
if ((m_structHeader.channelTypeIndex[i] == 0) | (m_structHeader.channelTypeIndex[i] == 1) | (m_structHeader.channelTypeIndex[i] == 5))
|
||||
{
|
||||
m_sample[j + i * m_nSamplePerSentBlock] = float(
|
||||
float(m_structBuffData.data[m_indexIn * m_header.getChannelCount() + m_header.getChannelCount() * j + i]) * 1e15 / m_header.
|
||||
getChannelGain(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sample[j + i * m_nSamplePerSentBlock] =
|
||||
float(m_structBuffData.data[m_indexIn * m_header.getChannelCount() + m_header.getChannelCount() * j + i]) / m_header.
|
||||
getChannelGain(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_indexOut = uint32_t(m_structBuffData.nbSamplesPerChanPerBlock) - m_indexIn;
|
||||
m_indexIn = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if output flow is smaller
|
||||
while (m_indexIn + (m_nSamplePerSentBlock - m_indexOut) < uint32_t(m_structBuffData.nbSamplesPerChanPerBlock))
|
||||
{
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < m_nSamplePerSentBlock - m_indexOut; ++j)
|
||||
{
|
||||
if ((m_structHeader.channelTypeIndex[i] == 0) | (m_structHeader.channelTypeIndex[i] == 1) | (m_structHeader.channelTypeIndex[i] == 5))
|
||||
{
|
||||
m_sample[j + m_indexOut + i * m_nSamplePerSentBlock] = float(
|
||||
float(m_structBuffData.data[m_header.getChannelCount() * m_indexIn + m_header.getChannelCount() * j + i]) * 1e15 /
|
||||
m_header.getChannelGain(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sample[j + m_indexOut + i * m_nSamplePerSentBlock] =
|
||||
float(m_structBuffData.data[m_header.getChannelCount() * m_indexIn + m_header.getChannelCount() * j + i]) / m_header.
|
||||
getChannelGain(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
//send data
|
||||
m_callback->setSamples(m_sample);
|
||||
|
||||
m_indexIn = m_indexIn + (m_nSamplePerSentBlock - m_indexOut);
|
||||
m_indexOut = 0;
|
||||
}
|
||||
|
||||
// save the rest of buff data
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < uint32_t(m_structBuffData.nbSamplesPerChanPerBlock) - m_indexIn; ++j)
|
||||
{
|
||||
if ((m_structHeader.channelTypeIndex[i] == 0) | (m_structHeader.channelTypeIndex[i] == 1) | (m_structHeader.channelTypeIndex[i] == 5))
|
||||
{
|
||||
m_sample[j + m_indexOut + i * m_nSamplePerSentBlock] = float(
|
||||
float(m_structBuffData.data[m_header.getChannelCount() * m_indexIn + m_header.getChannelCount() * j + i]) * 1e15 / m_header.
|
||||
getChannelGain(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sample[j + m_indexOut + i * m_nSamplePerSentBlock] =
|
||||
float(m_structBuffData.data[m_header.getChannelCount() * m_indexIn + m_header.getChannelCount() * j + i]) / m_header.
|
||||
getChannelGain(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_indexOut = uint32_t(m_structBuffData.nbSamplesPerChanPerBlock) - m_indexIn;
|
||||
m_indexIn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverCtfVsmMeg::stop()
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "> Connection stopped\n";
|
||||
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverCtfVsmMeg::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
m_callback = nullptr;
|
||||
|
||||
// Cleans up client connection
|
||||
m_connectionClient->close();
|
||||
m_connectionClient->release();
|
||||
m_connectionClient = nullptr;
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "> Client disconnected\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDriverCtfVsmMeg::configure()
|
||||
{
|
||||
CConfigurationNetworkBuilder config(Directories::getDataDir() + "/applications/acquisition-server/interface-CtfVsm-Meg.ui");
|
||||
|
||||
config.setHostName(m_sServerHostName);
|
||||
config.setHostPort(m_serverHostPort);
|
||||
|
||||
if (config.configure(m_header))
|
||||
{
|
||||
m_sServerHostName = config.getHostName();
|
||||
m_serverHostPort = config.getHostPort();
|
||||
|
||||
m_settings.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include <socket/IConnectionClient.h>
|
||||
|
||||
#define NB_CHAN_RECORDED_MAX 410
|
||||
#define NB_SAMP_ACQ_Packet 28160
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CDriverCtfVsmMeg
|
||||
* \author Pierre-Emmanuel Aguera (INSERM)
|
||||
*/
|
||||
class CDriverCtfVsmMeg final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
typedef char str32[32];
|
||||
typedef char str100[100];
|
||||
|
||||
explicit CDriverCtfVsmMeg(IDriverContext& ctx);
|
||||
~CDriverCtfVsmMeg() override { }
|
||||
const char* getName() override { return "CTF/VSM MEG"; }
|
||||
|
||||
|
||||
bool isFlagSet(const EDriverFlag flag) const override { return flag == EDriverFlag::IsUnstable; }
|
||||
|
||||
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; }
|
||||
|
||||
Socket::IConnectionClient* m_connectionClient = nullptr;
|
||||
CString m_sServerHostName = "localhost";
|
||||
uint32_t m_serverHostPort = 9999;
|
||||
|
||||
struct
|
||||
{
|
||||
int nbCharExperimentId;
|
||||
str100 experimentId;
|
||||
int nbCharExperimentDate;
|
||||
str32 experimentDate;
|
||||
|
||||
int nbCharSubjectName;
|
||||
str32 subjectName;
|
||||
int subjectAge;
|
||||
char subjectGender[1]; /**F: female or M: Male*/
|
||||
|
||||
int labId;
|
||||
str32 labName;
|
||||
int technicianId;
|
||||
str32 technicianName;
|
||||
|
||||
float samplingRate;
|
||||
int numberOfChannels;
|
||||
str32 channelLabel[NB_CHAN_RECORDED_MAX];
|
||||
int channelTypeIndex[NB_CHAN_RECORDED_MAX];
|
||||
|
||||
float properGain[NB_CHAN_RECORDED_MAX];
|
||||
float qGain[NB_CHAN_RECORDED_MAX];
|
||||
float ioGain[NB_CHAN_RECORDED_MAX];
|
||||
|
||||
int numberOfCoils[NB_CHAN_RECORDED_MAX];
|
||||
int gradOrderNum[NB_CHAN_RECORDED_MAX];
|
||||
} m_structHeader;
|
||||
|
||||
char* m_pStructHeader = nullptr;
|
||||
|
||||
struct
|
||||
{
|
||||
int sampleNumber;
|
||||
int nbSamplesPerChanPerBlock;
|
||||
int nbSamplesTotPerBlock;
|
||||
signed int data[NB_SAMP_ACQ_Packet];
|
||||
} m_structBuffData;
|
||||
|
||||
char* m_pStructBuffData = nullptr;
|
||||
|
||||
protected:
|
||||
|
||||
SettingsHelper m_settings;
|
||||
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
CHeader m_header;
|
||||
|
||||
uint32_t m_nSamplePerSentBlock = 0;
|
||||
float* m_sample = nullptr;
|
||||
|
||||
uint32_t m_indexIn = 0;
|
||||
uint32_t m_indexOut = 0;
|
||||
uint32_t m_socketFlag = 0;
|
||||
uint32_t m_buffDataIdx = 0;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+1099
File diff suppressed because it is too large
Load Diff
+165
@@ -0,0 +1,165 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyEEGOAPI)
|
||||
|
||||
#include <bitset>
|
||||
#include <sstream>
|
||||
|
||||
#include "ovasCConfigurationEEGO.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
// Function to set a predefined string in the combobox.
|
||||
// Copied from ovasCConfigurationBuilder. Seems to be OK, albeit it is strange to have the code duplication,
|
||||
// but other amplifier drivers do it too.
|
||||
// Would be nicer if the method would move to some utilities provider.
|
||||
static void GTKComboBoxSetActiveText(GtkComboBox* box, const gchar* text)
|
||||
{
|
||||
GtkTreeModel* treeModel = gtk_combo_box_get_model(box);
|
||||
GtkTreeIter it;
|
||||
int index = 0;
|
||||
gchar* name = nullptr;
|
||||
if (gtk_tree_model_get_iter_first(treeModel, &it))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get(treeModel, &it, 0, &name, -1);
|
||||
if (std::string(name) == std::string(text))
|
||||
{
|
||||
gtk_combo_box_set_active(box, index);
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
} while (gtk_tree_model_iter_next(treeModel, &it));
|
||||
}
|
||||
}
|
||||
|
||||
// If you added more reference attribute, initialize them here
|
||||
CConfigurationEEGO::CConfigurationEEGO(IDriverContext& ctx, const char* gtkBuilderFilename, CHeaderEEGO& eegoHeader)
|
||||
: CConfigurationBuilder(gtkBuilderFilename), m_driverCtx(ctx), m_eegoHeader(eegoHeader) {}
|
||||
|
||||
bool CConfigurationEEGO::preConfigure()
|
||||
{
|
||||
if (!CConfigurationBuilder::preConfigure()) { return false; }
|
||||
|
||||
// Insert here the pre-configure code.
|
||||
// For example, you may want to check if a device is currently connected
|
||||
// and if more than one are connected. Then you can list in a dedicated combo-box
|
||||
// the device currently connected so the user can choose which one he wants to acquire from.
|
||||
// Steffen Heimes: This is actually a TODO:
|
||||
|
||||
GtkWidget* widget = GTK_WIDGET(gtk_builder_get_object(m_builder, "combobox_signal_range_eeg"));
|
||||
m_eegRangeComboBox = GTK_COMBO_BOX(widget);
|
||||
|
||||
widget = GTK_WIDGET(gtk_builder_get_object(m_builder, "combobox_signal_range_bip"));
|
||||
m_bipRangeComboBox = GTK_COMBO_BOX(widget);
|
||||
|
||||
widget = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_eeg_mask"));
|
||||
m_eegEntryMask = GTK_ENTRY(widget);
|
||||
g_signal_connect(widget, "changed", G_CALLBACK(updateChannelNumCB), this);
|
||||
|
||||
widget = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_bip_mask"));
|
||||
m_bipEntryMask = GTK_ENTRY(widget);
|
||||
g_signal_connect(widget, "changed", G_CALLBACK(updateChannelNumCB), this);
|
||||
|
||||
widget = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_num_channels"));
|
||||
m_nChannelEntry = GTK_ENTRY(widget);
|
||||
|
||||
if (!m_eegRangeComboBox) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not connect to range widget \n"; }
|
||||
else
|
||||
{
|
||||
if (m_eegoHeader.isEEGRangeSet())
|
||||
{
|
||||
const std::string range = std::to_string(m_eegoHeader.getEEGRange());
|
||||
GTKComboBoxSetActiveText(m_eegRangeComboBox, range.c_str());
|
||||
}
|
||||
else { gtk_combo_box_set_active(m_eegRangeComboBox, 0); }
|
||||
|
||||
if (m_eegoHeader.isBIPRangeSet())
|
||||
{
|
||||
const std::string range = std::to_string(m_eegoHeader.getBIPRange());
|
||||
GTKComboBoxSetActiveText(m_bipRangeComboBox, range.c_str());
|
||||
}
|
||||
else { gtk_combo_box_set_active(m_bipRangeComboBox, 0); }
|
||||
}
|
||||
|
||||
if (m_eegoHeader.isBIPMaskSet()) { gtk_entry_set_text(m_bipEntryMask, m_eegoHeader.getBIPMask()); }
|
||||
if (m_eegoHeader.isEEGMaskSet()) { gtk_entry_set_text(m_eegEntryMask, m_eegoHeader.getEEGMask()); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfigurationEEGO::postConfigure()
|
||||
{
|
||||
if (m_applyConfig)
|
||||
{
|
||||
const gchar* rangeEEG = gtk_combo_box_get_active_text(m_eegRangeComboBox);
|
||||
const gchar* rangeBip = gtk_combo_box_get_active_text(m_bipRangeComboBox);
|
||||
const gchar* maskBip = gtk_entry_get_text(m_bipEntryMask);
|
||||
const gchar* maskEEG = gtk_entry_get_text(m_eegEntryMask);
|
||||
|
||||
m_eegoHeader.setBIPMask(maskBip);
|
||||
m_eegoHeader.setEEGMask(maskEEG);
|
||||
m_eegoHeader.setEEGRange(rangeEEG ? atoi(rangeEEG) : 0);
|
||||
m_eegoHeader.setBIPRange(rangeBip ? atoi(rangeBip) : 0);
|
||||
}
|
||||
|
||||
if (!CConfigurationBuilder::postConfigure()
|
||||
) { return false; } // normal header is filled (Subject ID, Age, Gender, channels, sampling frequency), ressources are released
|
||||
|
||||
// get sum of max active channels. It would be a good idea to use the amplifier connected as a source of the maximum of available channels.
|
||||
const uint64_t bip = m_eegoHeader.getBIPMaskInt();
|
||||
const uint64_t eeg = m_eegoHeader.getEEGMaskInt();
|
||||
|
||||
const std::bitset<64> bitsetEEG(eeg);
|
||||
const std::bitset<24> bitsetBip(bip);
|
||||
|
||||
m_header->setChannelCount(bitsetEEG.count() + bitsetBip.count() + 2);
|
||||
// Plus status channels: trigger and sample counter
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// GTK Callbacks
|
||||
/* static */
|
||||
void CConfigurationEEGO::updateChannelNumCB(GtkWidget* /*widget*/, CConfigurationEEGO* pThis)
|
||||
{
|
||||
// get the values
|
||||
const gchar* maskBip = gtk_entry_get_text(pThis->m_bipEntryMask);
|
||||
const gchar* maskEEG = gtk_entry_get_text(pThis->m_eegEntryMask);
|
||||
|
||||
uint64_t bip;
|
||||
uint64_t eeg;
|
||||
|
||||
const bool bipSuccess = CHeaderEEGO::convertMask(maskBip, bip);
|
||||
const bool eegSuccess = CHeaderEEGO::convertMask(maskEEG, eeg);
|
||||
|
||||
const std::bitset<64> bitsetEEG(eeg);
|
||||
const std::bitset<24> bitsetBip(bip);
|
||||
|
||||
// format them
|
||||
std::stringstream ss;
|
||||
if (eegSuccess) { ss << bitsetEEG.count(); }
|
||||
else { ss << "Error"; }
|
||||
|
||||
ss << " + ";
|
||||
|
||||
if (bipSuccess) { ss << bitsetBip.count(); }
|
||||
else { ss << "Error"; }
|
||||
|
||||
ss << " + 2; (EEG + BIP + STATUS)";
|
||||
|
||||
// set text
|
||||
gtk_entry_set_text(pThis->m_nChannelEntry, ss.str().c_str());
|
||||
|
||||
const uint32_t nChannel = bitsetEEG.count() + bitsetBip.count() + 2;
|
||||
|
||||
pThis->m_header->setChannelCount(nChannel);
|
||||
|
||||
// Workaround! The current channel number is not derived from the channel count. It is retrieved from the /here hidden/
|
||||
// widget when the channel editing window is opening. Thus we have to set the value there too.
|
||||
if (GTK_SPIN_BUTTON(pThis->m_nChannels)) { gtk_spin_button_set_value(GTK_SPIN_BUTTON(pThis->m_nChannels), nChannel); }
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyEEGOAPI
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyEEGOAPI)
|
||||
|
||||
#include <ovasIDriver.h>
|
||||
#include <../ovasCConfigurationBuilder.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "ovasCHeaderEEGO.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CConfigurationEEGO
|
||||
* \author Steffen Heimes (Eemagine GmbH)
|
||||
* \date Fri May 27 21:48:42 2011
|
||||
* \brief The CConfigurationEEGO handles the configuration dialog for setting specific for EEGO.
|
||||
* \sa CDriverEEGO
|
||||
*/
|
||||
class CConfigurationEEGO final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
CConfigurationEEGO(IDriverContext& ctx, const char* gtkBuilderFilename, CHeaderEEGO& eegoHeader);
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
|
||||
// Data
|
||||
protected:
|
||||
|
||||
IDriverContext& m_driverCtx;
|
||||
|
||||
// Methods
|
||||
private:
|
||||
|
||||
static void updateChannelNumCB(GtkWidget* widget, CConfigurationEEGO* pThis);
|
||||
|
||||
// Data
|
||||
CHeaderEEGO& m_eegoHeader;
|
||||
|
||||
GtkComboBox* m_eegRangeComboBox = nullptr;
|
||||
GtkComboBox* m_bipRangeComboBox = nullptr;
|
||||
GtkEntry* m_eegEntryMask = nullptr;
|
||||
GtkEntry* m_bipEntryMask = nullptr;
|
||||
GtkEntry* m_nChannelEntry = nullptr;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif
|
||||
+471
@@ -0,0 +1,471 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyEEGOAPI)
|
||||
|
||||
// stl includes
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
|
||||
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
// OV includes
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
// auxilliary classes for this implementation
|
||||
#include "ovasCDriverEEGO.h"
|
||||
#include "ovasCConfigurationEEGO.h"
|
||||
|
||||
// The interface to the driver
|
||||
// We have to setup the binding method and the unicode support first
|
||||
#define _UNICODE
|
||||
#if defined(_MBCS) // Only unicode should be supported. Better stay away from MBCS methods!
|
||||
#undef _MBCS
|
||||
#endif
|
||||
#include <eemagine/sdk/factory.h> // Where it all starts
|
||||
|
||||
// Namespaces
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
namespace es = eemagine::sdk;
|
||||
|
||||
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
|
||||
namespace std {
|
||||
template<class T> struct _Unique_if {
|
||||
typedef unique_ptr<T> _Single_object;
|
||||
};
|
||||
|
||||
template<class T> struct _Unique_if<T[]> {
|
||||
typedef unique_ptr<T[]> _Unknown_bound;
|
||||
};
|
||||
|
||||
template<class T, size_t N> struct _Unique_if<T[N]> {
|
||||
typedef void _Known_bound;
|
||||
};
|
||||
|
||||
template<class T, class... Args>
|
||||
typename _Unique_if<T>::_Single_object
|
||||
make_unique(Args&&... args) {
|
||||
return unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename _Unique_if<T>::_Unknown_bound
|
||||
make_unique(size_t n) {
|
||||
typedef typename remove_extent<T>::type U;
|
||||
return unique_ptr<T>(new U[n]());
|
||||
}
|
||||
|
||||
template<class T, class... Args>
|
||||
typename _Unique_if<T>::_Known_bound
|
||||
make_unique(Args&&...) = delete;
|
||||
}
|
||||
#endif
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
CDriverEEGO::CDriverEEGO(IDriverContext& ctx)
|
||||
: IDriver(ctx)
|
||||
, m_settings("AcquisitionServer_Driver_EEGO", m_driverCtx.getConfigurationManager()), m_triggerChannel(-1) // == Nonexistent
|
||||
, m_lastTriggerValue(~0) // Set every bit to 1
|
||||
, m_iBIPRange(1500)
|
||||
, m_iEEGRange(1000)
|
||||
, m_sEEGMask("0xFFFFFFFFFFFFFFFF") // 64 channels
|
||||
, m_sBIPMask("0xFFFFFF") // 24 channels
|
||||
{
|
||||
m_header.setSamplingFrequency(500);
|
||||
m_header.setChannelCount(88);
|
||||
|
||||
// The following class allows saving and loading driver settings from the acquisition server .conf file
|
||||
m_settings.add("Header", &m_header);
|
||||
|
||||
// To save your custom driver settings, register each variable to the SettingsHelper
|
||||
//m_settings.add("SettingName", &variable);
|
||||
m_settings.add("BIPRange", &m_iBIPRange);
|
||||
m_settings.add("EEGRange", &m_iEEGRange);
|
||||
|
||||
m_settings.add("EEGMask", &m_sEEGMask);
|
||||
m_settings.add("BIPMask", &m_sBIPMask);
|
||||
|
||||
m_settings.load();
|
||||
}
|
||||
|
||||
CDriverEEGO::~CDriverEEGO() {}
|
||||
|
||||
const char* CDriverEEGO::getName() { return "EEGO"; }
|
||||
|
||||
bool CDriverEEGO::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected()
|
||||
|| !m_header.isChannelCountSet()
|
||||
|| !m_header.isSamplingFrequencySet()) { return false; }
|
||||
|
||||
try
|
||||
{
|
||||
// Builds up a buffer to store
|
||||
// acquired samples. This buffer
|
||||
// will be sent to the acquisition
|
||||
// server later...
|
||||
m_sample = std::make_unique<float[]>(m_header.getChannelCount() * nSamplePerSentBlock);
|
||||
m_samplesInBuffer = 0;
|
||||
|
||||
// Get the amplifier. If none is connected an exception will be thrown
|
||||
try { m_pAmplifier.reset(factory().getAmplifier()); }
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Warning << "Failure to get an amplifier! Reason: " << ex.what() << "\n";
|
||||
throw;
|
||||
}
|
||||
|
||||
if (m_driverCtx.isImpedanceCheckRequested())
|
||||
{
|
||||
// end streaming first, if started
|
||||
m_pStream.reset();
|
||||
|
||||
// After init we are in impedance mode until the recording is started
|
||||
m_pStream.reset(m_pAmplifier->OpenImpedanceStream(getRefChannelMask()));
|
||||
}
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Failed to initialize the driver. Exception: " << ex.what() << "\n";
|
||||
|
||||
m_sample.reset();
|
||||
m_pAmplifier.reset();
|
||||
m_pStream.reset();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save parameters
|
||||
m_callback = &callback;
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverEEGO::check_configuration()
|
||||
{
|
||||
// get masks from configuration
|
||||
const uint64_t maskBip = getBipChannelMask();
|
||||
const uint64_t maskEEG = getRefChannelMask();
|
||||
|
||||
const std::bitset<64> bitsetEEG(maskEEG);
|
||||
const std::bitset<24> bitsetBip(maskBip);
|
||||
|
||||
const size_t allChannels = bitsetBip.count() + bitsetEEG.count() + 2; // trigger and sample count as additional channels
|
||||
if (allChannels < m_header.getChannelCount())
|
||||
{
|
||||
// Not enough channels, we have to reduce them
|
||||
GtkWidget* dialog = gtk_message_dialog_new(nullptr, // parent
|
||||
GTK_DIALOG_MODAL, // Behavoir
|
||||
GTK_MESSAGE_QUESTION, // Type
|
||||
GTK_BUTTONS_OK_CANCEL, // buttons
|
||||
"The channels masks are set to only stream %ld channels, but %d channels should be streamed.\n"
|
||||
"Change the amount of channels to %ld?", allChannels, m_header.getChannelCount(), allChannels);
|
||||
const gint res = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
dialog = nullptr;
|
||||
switch (res)
|
||||
{
|
||||
case GTK_RESPONSE_OK:
|
||||
// update the channel count to contain only the selected masks
|
||||
m_header.setChannelCount(allChannels);
|
||||
break;
|
||||
default:
|
||||
// Nothing can be done here
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t CDriverEEGO::getRefChannelMask() const
|
||||
{
|
||||
uint64_t maskEEG(0);
|
||||
if (!CHeaderEEGO::convertMask(m_sEEGMask, maskEEG))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Warning << "Error converting mask: m_sEEGMask: " << m_sEEGMask << "\n";
|
||||
}
|
||||
return maskEEG;
|
||||
}
|
||||
|
||||
uint64_t CDriverEEGO::getBipChannelMask() const
|
||||
{
|
||||
uint64_t maskBip(0);
|
||||
if (!CHeaderEEGO::convertMask(m_sBIPMask, maskBip))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Warning << "Error converting mask: maskBip: " << maskBip << "\n";
|
||||
}
|
||||
return maskBip;
|
||||
}
|
||||
|
||||
eemagine::sdk::factory& CDriverEEGO::factory()
|
||||
{
|
||||
if (m_pFactory == nullptr)
|
||||
{
|
||||
// create the amplifier factory
|
||||
// To initialize we need to locate the path of the DLL
|
||||
// Create path to the dll
|
||||
#ifdef _WIN32
|
||||
const CString libDir = Directories::getBinDir() + "\\eego-SDK.dll";
|
||||
const std::string path(libDir.toASCIIString());
|
||||
#else
|
||||
const std::string path("libeego-SDK.so");
|
||||
#endif // _WIN32
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Debug << "SDK dll/so path: " << path << "\n";
|
||||
m_pFactory = std::make_unique<es::factory>(path);
|
||||
|
||||
// to check what is going on case of error; Log version
|
||||
const auto version = m_pFactory->getVersion();
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "EEGO SDK Version: " << version.major << "." << version.minor << "." << version.micro << "." <<
|
||||
version.build << "\n";
|
||||
}
|
||||
|
||||
return *m_pFactory;
|
||||
}
|
||||
|
||||
bool CDriverEEGO::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted() || !m_pAmplifier) { return false; }
|
||||
|
||||
// Check configuration
|
||||
if (!check_configuration()) { return false; }
|
||||
|
||||
// ...
|
||||
// request hardware to start
|
||||
// sending data
|
||||
// ..
|
||||
const double bipRange = m_iBIPRange / 1000.;
|
||||
const double eegRange = m_iEEGRange / 1000.;
|
||||
|
||||
try
|
||||
{
|
||||
// stop old streams, if existing
|
||||
m_pStream.reset();
|
||||
// Create the eeg stream
|
||||
m_pStream.reset(m_pAmplifier->OpenEegStream(m_header.getSamplingFrequency(), eegRange, bipRange, getRefChannelMask(), getBipChannelMask()));
|
||||
|
||||
// Error check
|
||||
if (!m_pStream)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "The stream returned is NULL!" << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// find trigger channel
|
||||
auto list = m_pStream->getChannelList();
|
||||
const auto triggerIterator = std::find_if(list.begin(), list.end(), [](eemagine::sdk::channel& chan)
|
||||
{
|
||||
return chan.getType() == eemagine::sdk::channel::trigger;
|
||||
});
|
||||
|
||||
if (triggerIterator == list.end())
|
||||
{
|
||||
m_triggerChannel = -1; // Unkown
|
||||
}
|
||||
else { m_triggerChannel = (*triggerIterator).getIndex(); }
|
||||
|
||||
// Wait till we are really getting data.
|
||||
while (m_pStream->getData().getSampleCount() == 0)
|
||||
{
|
||||
System::Time::sleep(5); // Do Nothing
|
||||
}
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not open EEG stream: " << ex.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverEEGO::loop()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
try { result = loop_wrapped(); }
|
||||
catch (const std::exception& ex) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error in data update: " << ex.what() << "\n"; }
|
||||
catch (...) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unknown error in data update." << "\n"; }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CDriverEEGO::loop_wrapped()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (!m_driverCtx.isStarted() && !m_driverCtx.isImpedanceCheckRequested()) { return true; } // Nothing to be done here!
|
||||
if (!m_pStream) { return false; }
|
||||
|
||||
// Check if we really provide enough channels
|
||||
// When doing impedance only the normal EEG channels are tested. This is fine and handled.
|
||||
if (m_pStream->getChannelList().size() < m_header.getChannelCount() && m_driverCtx.isStarted()) // !started -> impedance
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "The amplifier got asked for more channels than it could provide" << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_driverCtx.isStarted()) // Normal operation
|
||||
{
|
||||
eemagine::sdk::buffer data;
|
||||
|
||||
try { data = m_pStream->getData(); }
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error fetching data: " << ex.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t nSample = data.getSampleCount();
|
||||
|
||||
// For EEGO the with every index increment, the channel is incremented.
|
||||
// For OpenVibe it means next sample. Therefor we have to transpose the data
|
||||
for (size_t sample = 0; sample < nSample; ++sample)
|
||||
{
|
||||
for (size_t channel = 0; channel < m_header.getChannelCount(); ++channel)
|
||||
{
|
||||
const int ovIdx = int(m_samplesInBuffer + channel * m_nSamplePerSentBlock);
|
||||
|
||||
const double& sampleVal = data.getSample(channel, sample);
|
||||
m_sample[ovIdx] = float(sampleVal);
|
||||
}
|
||||
|
||||
// Add potential triggers to stimulation set
|
||||
// check for triggers
|
||||
if (m_triggerChannel >= 0) // Only try to find triggers when the channel exists
|
||||
{
|
||||
// A trigger is detected when the level changes in positive direction, all additional bits are seen as trigger code
|
||||
// a change from 1 to 0 is ignored
|
||||
const uint32_t currentTriggers = uint32_t(data.getSample(m_triggerChannel, sample));
|
||||
const uint32_t currentNewTriggers = currentTriggers & ~m_lastTriggerValue;
|
||||
m_lastTriggerValue = currentTriggers;
|
||||
|
||||
if (currentNewTriggers != 0)
|
||||
{
|
||||
const uint64_t currentTime = CTime(m_header.getSamplingFrequency(), m_samplesInBuffer).time();
|
||||
m_stimSet.appendStimulation(OVTK_StimulationId_Label(currentNewTriggers), currentTime, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Send buffer counter
|
||||
m_samplesInBuffer++;
|
||||
|
||||
// Send buffer is full, so send it
|
||||
if (m_samplesInBuffer == m_nSamplePerSentBlock)
|
||||
{
|
||||
m_callback->setSamples(m_sample.get());
|
||||
m_callback->setStimulationSet(m_stimSet);
|
||||
|
||||
// When your sample buffer is fully loaded,
|
||||
// it is advised to ask the acquisition server
|
||||
// to correct any drift in the acquisition automatically.
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
|
||||
m_samplesInBuffer = 0;
|
||||
m_stimSet.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Impedance
|
||||
{
|
||||
// Get the impedance data, here the data is always the most current state.
|
||||
// The method can block if impedance still needs to be calculated.
|
||||
eemagine::sdk::buffer data;
|
||||
try { data = m_pStream->getData(); }
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error fetching data: " << ex.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We have to take care not to r/w over any boundary.
|
||||
const size_t minChannels = std::min(size_t(data.getChannelCount()), m_header.getChannelCount());
|
||||
for (size_t channel = 0; channel < minChannels; ++channel) { m_driverCtx.updateImpedance(channel, data.getSample(channel, 0)); }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverEEGO::stop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// request the hardware to stop
|
||||
// sending data
|
||||
// ...
|
||||
if (m_driverCtx.isImpedanceCheckRequested())
|
||||
{
|
||||
try
|
||||
{
|
||||
m_pStream.reset();
|
||||
m_pStream.reset(m_pAmplifier->OpenImpedanceStream(getRefChannelMask())); // And we can stream Impedances once more.
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error restarting impedance: " << ex.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverEEGO::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// uninitialize hardware here
|
||||
// ...
|
||||
m_pStream.reset();
|
||||
m_pAmplifier.reset();
|
||||
m_sample.reset();
|
||||
|
||||
m_callback = nullptr;
|
||||
m_samplesInBuffer = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDriverEEGO::isConfigurable()
|
||||
{
|
||||
return true; // change to false if your device is not configurable
|
||||
}
|
||||
|
||||
bool CDriverEEGO::configure()
|
||||
{
|
||||
// Change this line if you need to specify some references to your driver attribute that need configuration, e.g. the connection ID.
|
||||
CConfigurationEEGO config(m_driverCtx,
|
||||
Directories::getDataDir() + "/applications/acquisition-server/interface-EEGO.ui",
|
||||
m_header);
|
||||
|
||||
m_header.setBIPRange(m_iBIPRange);
|
||||
m_header.setEEGRange(m_iEEGRange);
|
||||
m_header.setBIPMask(m_sBIPMask);
|
||||
m_header.setEEGMask(m_sEEGMask);
|
||||
if (!config.configure(m_header)) { return false; }
|
||||
|
||||
m_iBIPRange = m_header.getBIPRange();
|
||||
m_iEEGRange = m_header.getEEGRange();
|
||||
m_sBIPMask = m_header.getBIPMask();
|
||||
m_sEEGMask = m_header.getEEGMask();
|
||||
|
||||
m_settings.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyEEGOAPI)
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "ovasCHeaderEEGO.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
// forward declarations
|
||||
namespace eemagine {
|
||||
namespace sdk {
|
||||
class amplifier;
|
||||
class stream;
|
||||
class factory;
|
||||
}
|
||||
}
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CDriverEEGO
|
||||
* \author Steffen Heimes (eemagine GmbH)
|
||||
* \date Mon Oct 20 14:40:33 2014
|
||||
* \brief The CDriverEEGO allows the acquisition server to acquire data from an EEGO device.
|
||||
*
|
||||
* \sa CConfigurationEEGO
|
||||
*/
|
||||
class CDriverEEGO final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CDriverEEGO(IDriverContext& ctx);
|
||||
~CDriverEEGO() override;
|
||||
const char* getName() override;
|
||||
|
||||
bool initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback) override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool start() override;
|
||||
bool stop() override;
|
||||
bool loop() override;
|
||||
|
||||
bool isConfigurable() override;
|
||||
bool configure() override;
|
||||
const IHeader* getHeader() override { return &m_header; }
|
||||
|
||||
bool isFlagSet(const EDriverFlag /*flag*/) const
|
||||
override { return false; } // The only currently used flag is for checking for unstability. eego is stable now.
|
||||
|
||||
private:
|
||||
|
||||
bool loop_wrapped();
|
||||
|
||||
/**
|
||||
* Check if the configuration makes sense and tries to fix it, informing the user.
|
||||
*/
|
||||
bool check_configuration();
|
||||
uint64_t getRefChannelMask() const;
|
||||
uint64_t getBipChannelMask() const;
|
||||
eemagine::sdk::factory& factory();
|
||||
|
||||
protected:
|
||||
|
||||
SettingsHelper m_settings;
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
CHeaderEEGO m_header;
|
||||
|
||||
uint32_t m_nSamplePerSentBlock = 0;
|
||||
std::unique_ptr<float[]> m_sample;
|
||||
|
||||
std::unique_ptr<eemagine::sdk::factory> m_pFactory;
|
||||
std::unique_ptr<eemagine::sdk::amplifier> m_pAmplifier;
|
||||
std::unique_ptr<eemagine::sdk::stream> m_pStream;
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_samplesInBuffer = 0;
|
||||
uint32_t m_triggerChannel = 0;
|
||||
CStimulationSet m_stimSet; // Storing the samples over time
|
||||
|
||||
// To detect flanks in the trigger signal. The last state on the trigger input.
|
||||
uint32_t m_lastTriggerValue = 0;
|
||||
|
||||
// For setting store/load
|
||||
uint32_t m_iBIPRange = 0; // [mV]
|
||||
uint32_t m_iEEGRange = 0; // [mV]
|
||||
CString m_sEEGMask; // String interpreted as value to be interpreted as bitfield
|
||||
CString m_sBIPMask; // String interpreted as value to be interpreted as bitfield
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
|
||||
#endif
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
#if defined TARGET_HAS_ThirdPartyEEGOAPI
|
||||
|
||||
#include <algorithm>
|
||||
#include <locale>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "ovasCHeaderEEGO.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
void CHeaderEEGO::setEEGRange(const uint32_t range)
|
||||
{
|
||||
m_iEEGRange = range;
|
||||
m_bEEGRangeSet = true;
|
||||
}
|
||||
|
||||
void CHeaderEEGO::setEEGMask(const CString& mask)
|
||||
{
|
||||
m_sEEGMask = mask;
|
||||
m_bEEGMaskSet = true;
|
||||
}
|
||||
|
||||
void CHeaderEEGO::setBIPRange(const uint32_t range)
|
||||
{
|
||||
m_iBIPRange = range;
|
||||
m_bBIPRangeSet = true;
|
||||
}
|
||||
|
||||
void CHeaderEEGO::setBIPMask(const CString& mask)
|
||||
{
|
||||
m_sBIPMask = mask;
|
||||
m_bBIPMaskSet = true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool CHeaderEEGO::convertMask(char const* str, uint64_t& out)
|
||||
{
|
||||
bool error = false;
|
||||
|
||||
// init r_outValue anyway
|
||||
out = 0;
|
||||
|
||||
std::string input(str); //easier substring handling etc. Minor performance penalty which should not matter.
|
||||
boost::algorithm::trim(input); // Make sure to handle whitespace correctly
|
||||
|
||||
// check prefixes
|
||||
if (boost::algorithm::istarts_with(input, "0b"))
|
||||
{
|
||||
// binary
|
||||
const auto substring = input.substr(2);
|
||||
|
||||
// check for valid string members
|
||||
if (!std::all_of(substring.begin(), substring.end(), [&](const char& chr) { return chr == '0' || chr == '1'; })) { error = true; }
|
||||
else
|
||||
{
|
||||
// use the substring for string to number conversion as base 2
|
||||
out = strtoull(substring.c_str(), nullptr, 2);
|
||||
}
|
||||
}
|
||||
else if (boost::algorithm::istarts_with(input, "0x"))
|
||||
{
|
||||
// hex
|
||||
const auto substring = input.substr(2);
|
||||
std::locale loc;
|
||||
|
||||
if (!std::all_of(substring.begin(), substring.end(), [&](const char& chr) { return std::isxdigit(chr, loc); })) { error = true; }
|
||||
else { out = strtoull(substring.c_str(), nullptr, 16); }
|
||||
}
|
||||
else if (boost::algorithm::istarts_with(input, "0"))
|
||||
{
|
||||
// octal
|
||||
const auto substring = input.substr(1);
|
||||
|
||||
if (!std::all_of(substring.begin(), substring.end(), [&](const char& chr) { return chr >= '0' && chr < '8'; })) { error = true; }
|
||||
else { out = strtoull(substring.c_str(), nullptr, 8); }
|
||||
}
|
||||
else
|
||||
{
|
||||
// decimal
|
||||
//const auto substring = input;
|
||||
std::locale loc;
|
||||
|
||||
if (!std::all_of(input.begin(), input.end(), [&](const char& chr) { return std::isdigit(chr, loc); })) { error = true; }
|
||||
else { out = strtoull(input.c_str(), nullptr, 10); }
|
||||
}
|
||||
|
||||
// if no special handling for the base 2 case is neccessary we can just use the std::stroull implementation and do not mess with that any further.
|
||||
return !error;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyEEGOAPI
|
||||
|
||||
#include "../ovasCHeader.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
class CHeaderEEGO final : public CHeader
|
||||
{
|
||||
public:
|
||||
|
||||
CHeaderEEGO() { }
|
||||
|
||||
// EEG, referential channels
|
||||
// range
|
||||
uint32_t getEEGRange() const { return m_iEEGRange; }
|
||||
|
||||
void setEEGRange(uint32_t range);
|
||||
bool isEEGRangeSet() const { return m_bEEGRangeSet; }
|
||||
|
||||
// mask
|
||||
CString getEEGMask() const { return m_sEEGMask; }
|
||||
uint64_t getEEGMaskInt() const { return strtoull(m_sEEGMask, nullptr, 0); } // Same as method above. Only string parsing has been done
|
||||
void setEEGMask(const CString& mask);
|
||||
bool isEEGMaskSet() const { return m_bEEGMaskSet; }
|
||||
|
||||
// Bipolar channels
|
||||
// range
|
||||
uint32_t getBIPRange() const { return m_iBIPRange; }
|
||||
uint64_t getBIPMaskInt() const { return strtoull(m_sBIPMask, nullptr, 0); } // Same as method above. Only string parsing has been done
|
||||
void setBIPRange(uint32_t range);
|
||||
bool isBIPRangeSet() const { return m_bBIPRangeSet; }
|
||||
|
||||
// mask
|
||||
CString getBIPMask() const { return m_sBIPMask; }
|
||||
void setBIPMask(const CString& mask);
|
||||
bool isBIPMaskSet() const { return m_bBIPMaskSet; }
|
||||
|
||||
// Converts a string representing a number to this number as unsigned 64 bit value.
|
||||
// Accepts 0x, 0b and 0 notation for hexadecimal, binary and octal notation.
|
||||
// Otherwise it is interpreted as decimal.
|
||||
// Returns true if the conversion was successfull, false on error.
|
||||
// Please note that the error checking goes beyond the parsing strtoull etc.:
|
||||
// The strto* methods stop parsing at the first character which could not be interpreted.
|
||||
// Here the string is checked against all invalid chars and an error will be returned.
|
||||
static bool convertMask(char const* str, uint64_t& out);
|
||||
|
||||
// data
|
||||
protected:
|
||||
uint32_t m_iEEGRange = 1000;
|
||||
uint32_t m_iBIPRange = 1500;
|
||||
CString m_sEEGMask = "0xFFFFFFFFFFFFFFFF";
|
||||
CString m_sBIPMask = "0xFFFFFF";
|
||||
bool m_bEEGRangeSet = false;
|
||||
bool m_bBIPRangeSet = false;
|
||||
bool m_bEEGMaskSet = false;
|
||||
bool m_bBIPMaskSet = false;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 2.6 -->
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="value">18</property>
|
||||
<property name="upper">100</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="value">64</property>
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">64</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment4">
|
||||
<property name="value">120</property>
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">65536</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model1">
|
||||
<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="model2">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">250</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
|
||||
<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>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox">
|
||||
<property name="visible">True</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="orientation">vertical</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_title">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Encephalan</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">8</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">7</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_gender">
|
||||
<property name="visible">True</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">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_age">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_identifier">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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="GtkLabel" id="label_sampling_frequency">
|
||||
<property name="visible">True</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">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="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">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_gender">
|
||||
<property name="visible">True</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="GtkLabel" id="label_age">
|
||||
<property name="visible">True</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_identifier">
|
||||
<property name="visible">True</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="GtkComboBox" id="combobox_sampling_frequency">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">model2</property>
|
||||
<property name="active">0</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">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_connection_ip">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Connection IP :</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="GtkLabel" id="label_connection_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Connection Port :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment4</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">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry_ip">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="max_length">30</property>
|
||||
</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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</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">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area">
|
||||
<property name="visible">True</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_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_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="pack_type">end</property>
|
||||
<property name="position">0</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>
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
#include "ovasCConfigurationEncephalan.h"
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
CConfigurationEncephalan::CConfigurationEncephalan(IDriverContext& driverContext, const char* gtkFileName, uint32_t& connectionPort, char* connectionIp)
|
||||
: CConfigurationBuilder(gtkFileName), m_driverContext(driverContext), m_connectionPort(connectionPort), m_connectionIp(connectionIp) { }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CConfigurationEncephalan::preConfigure()
|
||||
{
|
||||
if (!CConfigurationBuilder::preConfigure()) { return false; }
|
||||
|
||||
GtkSpinButton* port = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder, "spinbutton_port"));
|
||||
gtk_spin_button_set_value(port, m_connectionPort);
|
||||
GtkEntry* ip = GTK_ENTRY(gtk_builder_get_object(m_builder, "entry_ip"));
|
||||
gtk_entry_set_text(ip, m_connectionIp);
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CConfigurationEncephalan::postConfigure()
|
||||
{
|
||||
if (m_applyConfig)
|
||||
{
|
||||
GtkSpinButton* port = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder, "spinbutton_port"));
|
||||
m_connectionPort = uint32_t(gtk_spin_button_get_value_as_int(port));
|
||||
GtkEntry* ip = GTK_ENTRY(gtk_builder_get_object(m_builder, "entry_ip"));
|
||||
m_connectionIp = const_cast<char*>(gtk_entry_get_text(ip));
|
||||
}
|
||||
|
||||
return CConfigurationBuilder::postConfigure();
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file ovasCConfigurationEncephalan.h
|
||||
/// \brief The CConfigurationEncephalan handles the configuration dialog specific to the Encephalan device.
|
||||
/// \author Alexey Minin (UrFU)
|
||||
/// \version 1.0.
|
||||
/// \date 02/01/2019.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
#include "ovasIDriver.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/// <summary> The CConfigurationEncephalan handles the configuration dialog specific to the Encephalan device. </summary>
|
||||
/// <seealso cref="CDriverEncephalan" />
|
||||
class CConfigurationEncephalan final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
CConfigurationEncephalan(IDriverContext& driverContext, const char* gtkFileName, uint32_t& connectionPort, char* connectionIp);
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
char* getConnectionIp() const { return m_connectionIp; }
|
||||
|
||||
protected:
|
||||
IDriverContext& m_driverContext;
|
||||
uint32_t& m_connectionPort;
|
||||
char* m_connectionIp = nullptr;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
#if defined TARGET_OS_Windows
|
||||
|
||||
#include "ovasCDriverEncephalan.h"
|
||||
#include "ovasCConfigurationEncephalan.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
CDriverEncephalan::CDriverEncephalan(IDriverContext& driverContext)
|
||||
: IDriver(driverContext), m_settings("AcquisitionServer_Driver_Encephalan", m_driverCtx.getConfigurationManager())
|
||||
{
|
||||
m_connectionIp = static_cast<char*>("127.0.0.1");
|
||||
m_header.setSamplingFrequency(250);
|
||||
m_header.setChannelCount(8);
|
||||
|
||||
m_settings.add("Header", &m_header);
|
||||
m_settings.load();
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CDriverEncephalan::initialize(const uint32_t sampleCountPerBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected() || !m_header.isChannelCountSet() || !m_header.isSamplingFrequencySet()) { return false; }
|
||||
|
||||
m_sample = new float[m_header.getChannelCount() * sampleCountPerBlock];
|
||||
if (!m_sample)
|
||||
{
|
||||
delete[] m_sample;
|
||||
m_sample = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Saves parameters
|
||||
m_callback = &callback;
|
||||
m_nSamplePerBlock = sampleCountPerBlock;
|
||||
return connectEncephalan();
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CDriverEncephalan::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// request hardware to start
|
||||
return sendRequestForData();
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CDriverEncephalan::loop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (!m_driverCtx.isStarted()) { return true; }
|
||||
|
||||
m_currentPoint = 0;
|
||||
ZeroMemory(m_sample, sizeof(float)*m_nSamplePerBlock*m_header.getChannelCount());
|
||||
while (m_currentPoint < m_nSamplePerBlock) { receiveData(); }
|
||||
m_callback->setSamples(m_sample);
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CDriverEncephalan::stop() { return m_driverCtx.isConnected() && m_driverCtx.isStarted(); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CDriverEncephalan::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
delete[] m_sample;
|
||||
m_sample = nullptr;
|
||||
m_callback = nullptr;
|
||||
|
||||
closesocket(m_client);
|
||||
WSACleanup();
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CDriverEncephalan::connectEncephalan()
|
||||
{
|
||||
WSADATA wsaData;
|
||||
const int wsaret = WSAStartup(0x101, &wsaData);
|
||||
if (wsaret != 0)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error WSAStartup (initialization windows socket api): " << WSAGetLastError() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (m_client == INVALID_SOCKET)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error socket (creation a socket): " << WSAGetLastError() << "\n";
|
||||
WSACleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
sockaddr_in sockaddrIn;
|
||||
sockaddrIn.sin_family = AF_INET;
|
||||
sockaddrIn.sin_addr.s_addr = inet_addr(m_connectionIp);
|
||||
sockaddrIn.sin_port = htons(u_short(m_connectionPort));
|
||||
const int connectError = connect(m_client, reinterpret_cast<sockaddr*>(&sockaddrIn), sizeof(sockaddrIn));
|
||||
if (connectError != 0)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error connect (connection to a specified socket): " << WSAGetLastError() << "\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CDriverEncephalan::sendRequestForData() const
|
||||
{
|
||||
BYTE* sendDataBuffer = new BYTE;
|
||||
|
||||
unsigned long ID = 0x0001; //EEG_ONLY_MODE
|
||||
//unsigned long ID = 0x000C; //ALL_CHANNELS_MODE
|
||||
//unsigned long ID = 0x000E; //HD_CHANNELS_MODE
|
||||
|
||||
CopyMemory(sendDataBuffer, &ID, sizeof(ID));
|
||||
if (!sendData(sendDataBuffer, sizeof(ID)))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error sendRequestForData: " << WSAGetLastError() << "\n";
|
||||
return false;
|
||||
}
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "sendRequestForData\n";
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CDriverEncephalan::sendData(BYTE* data, int dataSize) const
|
||||
{
|
||||
if (m_client == INVALID_SOCKET) { return false; }
|
||||
|
||||
const long fullLen = dataSize + long(sizeof(int));
|
||||
char* outData = new char[fullLen];
|
||||
|
||||
CopyMemory(&outData[0], &dataSize, sizeof(int));
|
||||
CopyMemory(&outData[sizeof(int)], &data[0], dataSize);
|
||||
|
||||
if (send(m_client, outData, fullLen, 0) != fullLen)
|
||||
{
|
||||
delete[] outData;
|
||||
return false;
|
||||
}
|
||||
|
||||
delete[]outData;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CDriverEncephalan::receiveData()
|
||||
{
|
||||
int inpSize = 0;
|
||||
char* inpData = new char[sizeof(inpSize) * sizeof(BYTE)];
|
||||
const int nReadSize = recv(m_client, inpData, sizeof(inpSize), 0);
|
||||
if (nReadSize <= 0) { return false; }
|
||||
|
||||
CopyMemory(&inpSize, &inpData[0], sizeof(inpSize));
|
||||
delete[]inpData;
|
||||
|
||||
if ((inpSize > 0) && (inpSize < 1024 * 512))
|
||||
{
|
||||
//unsigned long ID = 0;
|
||||
inpData = new char[inpSize * sizeof(BYTE)];
|
||||
if (recv(m_client, inpData, inpSize, 0) == inpSize) { readData(reinterpret_cast<BYTE*>(inpData), inpSize); }
|
||||
delete[]inpData;
|
||||
inpData = nullptr;
|
||||
}
|
||||
else { return false; }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
void CDriverEncephalan::readData(BYTE* data, const int dataSize)
|
||||
{
|
||||
BYTE* pCurData = data;
|
||||
int curDataSize = dataSize;
|
||||
|
||||
unsigned long ID = 0;
|
||||
getData(pCurData, curDataSize, &ID, sizeof(ID));
|
||||
switch (ID)
|
||||
{
|
||||
case 0x0002: // Information on research
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Recieved information on research\n";
|
||||
break;
|
||||
case 0x00E2: // This prompted all the data including the kilohertz
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Recieved all the data including the kilohertz\n";
|
||||
break;
|
||||
case 0x0003: m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "0x0003 data package\n";
|
||||
break;
|
||||
case 0x0006: m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "0x0006 data package\n";
|
||||
break;
|
||||
case 0x0008: // We came signal data
|
||||
receiveEEGData(pCurData, curDataSize);
|
||||
break;
|
||||
case 0x00E8: // Packet came with HD data
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Packet came with HD data\n";
|
||||
break;
|
||||
case 0x0009: m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "0x0009 data package\n";
|
||||
break;
|
||||
case 0x000A: m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "0x000A data package\n";
|
||||
break;
|
||||
case 0x0101: m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "0x0101 data package\n";
|
||||
break;
|
||||
default: m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Undefined data package\n";
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
// Parsing package with EEG data
|
||||
void CDriverEncephalan::receiveEEGData(BYTE* curData, int curDataSize)
|
||||
{
|
||||
int sliceNum = 0;
|
||||
int pointsNumber = 0;
|
||||
getData(curData, curDataSize, &sliceNum, sizeof(sliceNum));
|
||||
getData(curData, curDataSize, &pointsNumber, sizeof(pointsNumber));
|
||||
|
||||
short* dstData = new short[pointsNumber];
|
||||
ZeroMemory(dstData, sizeof(short)*pointsNumber);
|
||||
getData(curData, curDataSize, dstData, pointsNumber * int(sizeof(short)));
|
||||
|
||||
for (size_t iPos = 0; iPos < m_header.getChannelCount(); ++iPos)
|
||||
{
|
||||
float val = float(dstData[iPos]);
|
||||
CopyMemory(m_sample + (m_currentPoint + iPos * m_nSamplePerBlock), &val, sizeof(float));
|
||||
}
|
||||
m_currentPoint++;
|
||||
|
||||
delete[] dstData;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
void CDriverEncephalan::getData(BYTE* & data, int& dataSize, void* dstData, const int dstSize)
|
||||
{
|
||||
if (dataSize < dstSize) { return; }
|
||||
CopyMemory(dstData, data, dstSize);
|
||||
data += dstSize;
|
||||
dataSize -= dstSize;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CDriverEncephalan::configure()
|
||||
{
|
||||
// Change this line if you need to specify some references to your driver attribute that need configuration, e.g. the connection ID.
|
||||
CConfigurationEncephalan configuration(m_driverCtx, Directories::getDataDir() + "/applications/acquisition-server/interface-Encephalan.ui",
|
||||
m_connectionPort, m_connectionIp);
|
||||
|
||||
if (!configuration.configure(m_header)) { return false; }
|
||||
m_connectionIp = configuration.getConnectionIp();
|
||||
m_settings.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_OS_Windows
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file ovasCDriverEncephalan.h
|
||||
/// \brief The CDriverEncephalan allows the acquisition server to acquire data from a Encephalan device.
|
||||
/// \author Alexey Minin (UrFU)
|
||||
/// \version 1.0.
|
||||
/// \date 02/01/2019.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_OS_Windows
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
#include "winsock.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/// <summary> The CDriverEncephalan allows the acquisition server to acquire data from a Encephalan device. </summary>
|
||||
/// <seealso cref="CConfigurationEncephalan" />
|
||||
class CDriverEncephalan final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CDriverEncephalan(IDriverContext& driverContext);
|
||||
~CDriverEncephalan() override = default;
|
||||
const char* getName() override { return "Encephalan"; }
|
||||
|
||||
bool initialize(const uint32_t sampleCountPerBlock, IDriverCallback& callback) override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool start() override;
|
||||
bool stop() override;
|
||||
bool loop() override;
|
||||
|
||||
bool isConfigurable() override { return true; } // change to false if your device is not configurable
|
||||
bool configure() override;
|
||||
const IHeader* getHeader() override { return &m_header; }
|
||||
|
||||
bool isFlagSet(const EDriverFlag flag) const override { return flag == EDriverFlag::IsUnstable; }
|
||||
|
||||
protected:
|
||||
bool connectEncephalan();
|
||||
bool sendRequestForData() const;
|
||||
bool sendData(BYTE* data, int dataSize) const;
|
||||
bool receiveData();
|
||||
void readData(BYTE* data, int dataSize);
|
||||
static void getData(BYTE* & data, int& dataSize, void* dstData, int dstSize);
|
||||
void receiveEEGData(BYTE* curData, int curDataSize);
|
||||
|
||||
SettingsHelper m_settings;
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
CHeader m_header;
|
||||
uint32_t m_nSamplePerBlock = 0;
|
||||
float* m_sample = nullptr;
|
||||
|
||||
char* m_connectionIp = nullptr;
|
||||
uint32_t m_connectionPort = 120;
|
||||
uint32_t m_currentPoint = 0;
|
||||
SOCKET m_client = 0;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_OS_Windows
|
||||
+702
@@ -0,0 +1,702 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 2.12 -->
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="value">1025</property>
|
||||
<property name="upper">49151</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="GtkAdjustment" id="adjustment4">
|
||||
<property name="value">1</property>
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">1024</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model1">
|
||||
<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="model2">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<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">1024</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">2048</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">4096</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
|
||||
<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>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox">
|
||||
<property name="visible">True</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>
|
||||
<object class="GtkLabel" id="label_title">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Fieldtrip</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox">
|
||||
<property name="visible">True</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="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">7</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_host_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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="GtkEntry" id="entry_host_name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="max_length">15</property>
|
||||
<property name="invisible_char">•</property>
|
||||
</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="GtkLabel" id="label_host_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Buffer port number</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_host_name">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Buffer host name</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_identifier">
|
||||
<property name="visible">True</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="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="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="GtkSpinButton" id="spinbutton_identifier">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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="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="GtkComboBox" id="combobox_gender">
|
||||
<property name="visible">True</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">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_minSamples">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Min nb of samples in buffer</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_minSamples">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="adjustment">adjustment5</property>
|
||||
</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="GtkCheckButton" id="checkbutton_SRCorrection">
|
||||
<property name="label" translatable="yes">Correct manually drift due to non-integer sampling rate</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area">
|
||||
<property name="visible">True</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_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_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="pack_type">end</property>
|
||||
<property name="position">0</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>
|
||||
<object class="GtkWindow" id="dummy-widgets">
|
||||
<child>
|
||||
<object class="GtkTable" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">10</property>
|
||||
<property name="n_columns">10</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment4</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_sampling_frequency">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">model2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer2"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</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">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment5">
|
||||
<property name="value">1</property>
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">10000</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
</interface>
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "windows.h"
|
||||
#include "ov_defines.h"
|
||||
#include "ovCString.h"
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
// Get Cpu Time //
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
namespace OpenViBE
|
||||
{
|
||||
OV_API bool GetEnvVar( CString& sVar, CString& sValue );
|
||||
OV_API bool PutEnvVar( CString& sVar, CString& sValue );
|
||||
|
||||
OV_API inline double GetCPUTimeInMilliseconds()
|
||||
{
|
||||
static bool bGotCPUFreq = false;
|
||||
static double timerFrequency = 1;
|
||||
if ( !bGotCPUFreq )
|
||||
{
|
||||
unsigned __int64 cpufreq = 1;
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&cpufreq);
|
||||
timerFrequency = (1000.0/cpufreq);
|
||||
}
|
||||
unsigned __int64 curTime = 0;
|
||||
QueryPerformanceCounter((LARGE_INTEGER *)&curTime);
|
||||
return timerFrequency * curTime;
|
||||
};
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld & Christian Hesse
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
/* prevent double include */
|
||||
#ifndef BUFFER_H
|
||||
#define BUFFER_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "platform_includes.h"
|
||||
#include "message.h"
|
||||
|
||||
#ifndef POLLRDNORM
|
||||
#define POLLRDNORM POLLIN
|
||||
#endif
|
||||
|
||||
#ifndef POLLRDBAND
|
||||
#define POLLRDBAND POLLPRI
|
||||
#endif
|
||||
|
||||
#ifndef POLLWRNORM
|
||||
#define POLLWRNORM POLLOUT
|
||||
#endif
|
||||
|
||||
#ifndef POLLWRBAND
|
||||
#define POLLWRBAND POLLOUT
|
||||
#endif
|
||||
|
||||
#define BACKLOG 16
|
||||
#define DEFAULT_HOSTNAME "localhost"
|
||||
#define DEFAULT_PORT 1972
|
||||
|
||||
#define SO_RCVBUF_SIZE 16384
|
||||
#define SO_SNDBUF_SIZE 16384
|
||||
|
||||
/* this is because the function has been renamed, but is perhaps already in use in other software */
|
||||
#define open_remotehost open_connection
|
||||
|
||||
/* FIXME these should be variable */
|
||||
#define MAXNUMBYTE (512*1024*1024)
|
||||
#define MAXNUMSAMPLE 600000
|
||||
#define MAXNUMEVENT 100
|
||||
|
||||
#define WRAP(x,y) ((x) - (int(float(x)/(y)))*(y))
|
||||
#define FREE(x) {if (x) {free(x); x= nullptr;}}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
#endif
|
||||
|
||||
/* declaration of "public" buffer API functions */
|
||||
/* SK: where are these, and what are they for ? */
|
||||
int read_header( const char *hostname, int port, void **ppw);
|
||||
int read_data( const char *hostname, int port, int *pnw, void **ppw);
|
||||
int read_event( const char *hostname, int port, int *pnw, void **ppw);
|
||||
int write_header(const char *hostname, int port, void **ppw);
|
||||
int write_data( const char *hostname, int port, int *pnw, void **ppw);
|
||||
int write_event( const char *hostname, int port, int *pnw, void **ppw);
|
||||
int flush_header(const char *hostname, int port);
|
||||
int flush_data( const char *hostname, int port);
|
||||
int flush_event( const char *hostname, int port);
|
||||
void cleanup_buffer();
|
||||
|
||||
/* definition of the functions that implement the network transparent server */
|
||||
void *tcpserver(void *);
|
||||
void *tcpsocket(void *);
|
||||
|
||||
/* definition of test functions that emulate an acquisition system */
|
||||
void *sinewave_thread(void *);
|
||||
void *event_thread(void *);
|
||||
|
||||
/* definition of the functions used in thread cancelation, see cleanup.c */
|
||||
void cleanup_message(void **arg);
|
||||
void cleanup_header(void **arg);
|
||||
void cleanup_data(void **arg);
|
||||
void cleanup_event(void **arg);
|
||||
void cleanup_buf(void **arg);
|
||||
void cleanup_socket(int *);
|
||||
|
||||
/* definition of helper functions for debugging and printing the content of various structures */
|
||||
void print_request(messagedef_t *);
|
||||
void print_response(messagedef_t *);
|
||||
void print_headerdef(headerdef_t *);
|
||||
void print_datadef(datadef_t *);
|
||||
void print_eventdef(eventdef_t *);
|
||||
void print_datasel(datasel_t *);
|
||||
void print_eventsel(eventsel_t *);
|
||||
void print_buf(void *, int);
|
||||
|
||||
/* definition of even more helper functions, see util.c */
|
||||
int open_connection(const char*, int);
|
||||
int open_unix_connection(const char *name);
|
||||
int close_connection(int);
|
||||
unsigned int append(void **, unsigned int, void *, unsigned int);
|
||||
unsigned int bufread(int, void *, unsigned int);
|
||||
unsigned int bufwrite(int, const void *, unsigned int);
|
||||
int clientrequest(int, const message_t *, message_t**);
|
||||
int dmarequest(const message_t *, message_t**);
|
||||
int tcprequest(int, const message_t *, message_t**);
|
||||
unsigned int wordsize_from_type(UINT32_T data_type);
|
||||
void check_datatypes();
|
||||
int check_event_array(unsigned int size, const void *buf);
|
||||
const ft_chunk_t *find_chunk(const void *buf, unsigned int offset0, unsigned int size, UINT32_T chunk_type);
|
||||
|
||||
void ft_swap16(unsigned int numel, void *data);
|
||||
void ft_swap32(unsigned int numel, void *data);
|
||||
void ft_swap64(unsigned int numel, void *data);
|
||||
int ft_swap_buf_to_native(UINT16_T command, UINT32_T bufsize, void *buf);
|
||||
int ft_convert_chunks_from_native(UINT32_T size, UINT32_T nchans, void *buf);
|
||||
int ft_swap_from_native(UINT16_T orgCommand, message_t *msg);
|
||||
|
||||
typedef struct {
|
||||
char name[256];
|
||||
int port;
|
||||
} host_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "buffer.h"
|
||||
|
||||
/* this is used for debugging */
|
||||
int verbose = 0;
|
||||
|
||||
void cleanup_socket(int *arg) {
|
||||
if (verbose>0) fprintf(stderr, "cleanup_socket: s = %d\n", *arg);
|
||||
if ((*arg)>0) {
|
||||
close_connection(*arg);
|
||||
}
|
||||
*arg = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_message(void **arg) {
|
||||
message_t *message = (message_t *)*arg;
|
||||
if (verbose>0) fprintf(stderr, "cleanup_message()\n");
|
||||
if (message) {
|
||||
FREE(message->def);
|
||||
FREE(message->buf);
|
||||
FREE(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_header(void **arg) {
|
||||
header_t *header = (header_t *)*arg;
|
||||
if (verbose>0) fprintf(stderr, "cleanup_header()\n");
|
||||
if (header) {
|
||||
FREE(header->def);
|
||||
FREE(header->buf);
|
||||
FREE(header);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_data(void **arg) {
|
||||
data_t *data = (data_t *)*arg;
|
||||
if (verbose>0) fprintf(stderr, "cleanup_data()\n");
|
||||
if (data) {
|
||||
FREE(data->def);
|
||||
FREE(data->buf);
|
||||
FREE(data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_event(void **arg) {
|
||||
event_t *event = (event_t *)*arg;
|
||||
if (verbose>0) fprintf(stderr, "cleanup_event()\n");
|
||||
if (event) {
|
||||
FREE(event->def);
|
||||
FREE(event->buf);
|
||||
FREE(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_buf(void **arg) {
|
||||
if (verbose>0) fprintf(stderr, "cleanup_buf()\n");
|
||||
if (*arg) {
|
||||
FREE(*arg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "buffer.h"
|
||||
|
||||
/*******************************************************************************
|
||||
* this function is called by the client
|
||||
* it takes care that the request is processed by the buffer
|
||||
*******************************************************************************/
|
||||
int clientrequest(int server, const message_t *request, message_t **response_ptr) {
|
||||
int verbose = 0;
|
||||
|
||||
if (verbose>0) fprintf(stderr, "clientrequest: server = %d\n", server);
|
||||
if (verbose>0) print_request(request->def);
|
||||
|
||||
if (server<0) {
|
||||
fprintf(stderr, "clientrequest: invalid value for server (%d)\n", server);
|
||||
return -1;
|
||||
}
|
||||
|
||||
else if (server==0) {
|
||||
/* use direct memory acces to the buffer */
|
||||
if (dmarequest(request, response_ptr)!=0)
|
||||
return -2;
|
||||
}
|
||||
|
||||
else if (server>0) {
|
||||
/* use TCP connection to the buffer */
|
||||
if (tcprequest(server, request, response_ptr)!=0)
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (verbose>0) print_response((*response_ptr)->def);
|
||||
|
||||
/* everything went fine */
|
||||
return 0;
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/* prevent double include */
|
||||
#pragma once
|
||||
|
||||
#if defined (__BORLANDC__)
|
||||
#define COMPILER_BORLAND
|
||||
|
||||
#elif defined (_MSC_VER)
|
||||
#define COMPILER_MSVC
|
||||
|
||||
#elif defined (__CYGWIN32__)
|
||||
#define COMPILER_CYGWIN
|
||||
|
||||
#elif defined (__MINGW32__)
|
||||
#define COMPILER_MINGW
|
||||
|
||||
#elif defined (__LCC__)
|
||||
#define COMPILER_LCC
|
||||
|
||||
#endif
|
||||
+735
@@ -0,0 +1,735 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "buffer.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef TARGET_OS_Linux
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
/* FIXME should these be static? */
|
||||
static header_t *header = NULL;
|
||||
static data_t *data = NULL;
|
||||
static event_t *event = NULL;
|
||||
|
||||
static unsigned int current_max_num_sample = 0;
|
||||
|
||||
static int thissample = 0; /* points at the buffer */
|
||||
static int thisevent = 0; /* points at the buffer */
|
||||
|
||||
/* Note that there have been problems with the order of the mutexes (e.g.
|
||||
* http://bugzilla.fcdonders.nl/show_bug.cgi?id=933).
|
||||
* I have attempted to make the order of locking consistent, but can't give
|
||||
* guarantees. A more long term solution could be:
|
||||
* - find the dependencies between modifications of volatile data (e.g. events
|
||||
* depend on header),
|
||||
* - keep locks as shortly as possible (get info, release again).
|
||||
*
|
||||
* This could results in a global lock (robust, probably not optimal in terms
|
||||
* of speed), or a series of locks sandwiching modification code in dedicated
|
||||
* functions.
|
||||
*
|
||||
* -- Boris
|
||||
*/
|
||||
pthread_mutex_t mutexheader = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_t mutexdata = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_t mutexevent = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
pthread_cond_t getData_cond = PTHREAD_COND_INITIALIZER;
|
||||
pthread_mutex_t getData_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
#define DIE_BAD_MALLOC(ptr) if ((ptr) == nullptr) { fprintf(stderr,"Out of memory with unchecked malloc in line %d",__LINE__); exit(1); }
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void free_header() {
|
||||
int verbose = 0;
|
||||
if (verbose>0) fprintf(stderr, "free_header: freeing header buffer\n");
|
||||
if (header) {
|
||||
FREE(header->def);
|
||||
FREE(header->buf);
|
||||
FREE(header);
|
||||
}
|
||||
}
|
||||
|
||||
void free_data() {
|
||||
int verbose = 0;
|
||||
if (verbose>0) fprintf(stderr, "free_data: freeing data buffer\n");
|
||||
if (data) {
|
||||
FREE(data->def);
|
||||
FREE(data->buf);
|
||||
FREE(data);
|
||||
}
|
||||
thissample = 0;
|
||||
if (header) header->def->nsamples = 0;
|
||||
}
|
||||
|
||||
void free_event() {
|
||||
int verbose = 0;
|
||||
int i;
|
||||
if (verbose>0) fprintf(stderr, "free_event: freeing event buffer\n");
|
||||
if (event) {
|
||||
for (i=0; i<MAXNUMEVENT; ++i) {
|
||||
FREE(event[i].def);
|
||||
FREE(event[i].buf);
|
||||
}
|
||||
FREE(event);
|
||||
}
|
||||
thisevent = 0;
|
||||
if (header) header->def->nevents = 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void init_data() {
|
||||
int verbose = 0;
|
||||
if (verbose>0) fprintf(stderr, "init_data: creating data buffer\n");
|
||||
if (header) {
|
||||
unsigned int wordsize = wordsize_from_type(header->def->data_type);
|
||||
|
||||
if (wordsize==0) {
|
||||
fprintf(stderr, "init_data: unsupported data type (%u)\n", header->def->data_type);
|
||||
return;
|
||||
}
|
||||
/* heuristic of choosing size of buffer:
|
||||
set current_max_num_sample to MAXNUMSAMPLE if nchans <= 256
|
||||
otherwise, allocate about MAXNUMBYTE and calculate current_max_num_sample from nchans + wordsize
|
||||
*/
|
||||
if (header->def->nchans <= 256) {
|
||||
current_max_num_sample = MAXNUMSAMPLE;
|
||||
} else {
|
||||
current_max_num_sample = MAXNUMBYTE / (wordsize * header->def->nchans);
|
||||
}
|
||||
data = (data_t*)malloc(sizeof(data_t));
|
||||
|
||||
DIE_BAD_MALLOC(data);
|
||||
|
||||
data->def = (datadef_t*)malloc(sizeof(datadef_t));
|
||||
|
||||
DIE_BAD_MALLOC(data->def);
|
||||
|
||||
data->def->nchans = header->def->nchans;
|
||||
data->def->nsamples = current_max_num_sample;
|
||||
data->def->data_type = header->def->data_type;
|
||||
data->buf = malloc(header->def->nchans*current_max_num_sample*wordsize);
|
||||
|
||||
DIE_BAD_MALLOC(data->buf);
|
||||
}
|
||||
}
|
||||
|
||||
void init_event() {
|
||||
int verbose = 0;
|
||||
int i;
|
||||
if (verbose>0) fprintf(stderr, "init_event: creating event buffer\n");
|
||||
if (header) {
|
||||
event = (event_t*)malloc(MAXNUMEVENT*sizeof(event_t));
|
||||
DIE_BAD_MALLOC(event);
|
||||
for (i=0; i<MAXNUMEVENT; ++i) {
|
||||
event[i].def = NULL;
|
||||
event[i].buf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* this function handles the direct memory access to the buffer
|
||||
* and copies objects to and from memory
|
||||
*****************************************************************************/
|
||||
int dmarequest(const message_t *request, message_t **response_ptr) {
|
||||
unsigned int offset;
|
||||
/*
|
||||
int blockrequest = 0;
|
||||
*/
|
||||
int verbose = 0;
|
||||
|
||||
/* these are used for blocking the read requests */
|
||||
struct timeval tp;
|
||||
struct timespec ts;
|
||||
|
||||
/* use a local variable for datasel (in GET_DAT) */
|
||||
datasel_t datasel;
|
||||
|
||||
/* these are for typecasting */
|
||||
headerdef_t *headerdef;
|
||||
datadef_t *datadef;
|
||||
eventdef_t *eventdef;
|
||||
eventsel_t *eventsel;
|
||||
|
||||
/* this will hold the response */
|
||||
message_t *response;
|
||||
response = (message_t*)malloc(sizeof(message_t));
|
||||
|
||||
/* check for "out of memory" problems */
|
||||
if (response == nullptr) {
|
||||
*response_ptr = NULL;
|
||||
return -1;
|
||||
}
|
||||
response->def = (messagedef_t*)malloc(sizeof(messagedef_t));
|
||||
|
||||
/* check for "out of memory" problems */
|
||||
if (response->def == nullptr) {
|
||||
*response_ptr = NULL;
|
||||
free(response);
|
||||
return -1;
|
||||
}
|
||||
response->buf = NULL;
|
||||
/* the response should be passed to the calling function, where it should be freed */
|
||||
*response_ptr = response;
|
||||
|
||||
if (verbose>1) print_request(request->def);
|
||||
|
||||
switch (request->def->command) {
|
||||
|
||||
case PUT_HDR:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: PUT_HDR\n");
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
pthread_mutex_lock(&mutexevent);
|
||||
|
||||
headerdef = (headerdef_t*)request->buf;
|
||||
if (verbose>1) print_headerdef(headerdef);
|
||||
|
||||
/* delete the old header, data and events */
|
||||
free_header();
|
||||
free_data();
|
||||
free_event();
|
||||
|
||||
/* store the header and re-initialize */
|
||||
header = (header_t*)malloc(sizeof(header_t));
|
||||
DIE_BAD_MALLOC(header);
|
||||
header->def = (headerdef_t*)malloc(sizeof(headerdef_t));
|
||||
DIE_BAD_MALLOC(header->def);
|
||||
header->buf = malloc(headerdef->bufsize);
|
||||
DIE_BAD_MALLOC(header->buf);
|
||||
memcpy(header->def, request->buf, sizeof(headerdef_t));
|
||||
memcpy(header->buf, (char*)request->buf+sizeof(headerdef_t), headerdef->bufsize);
|
||||
header->def->nsamples = 0;
|
||||
header->def->nevents = 0;
|
||||
|
||||
init_data();
|
||||
init_event();
|
||||
|
||||
response->def->version = VERSION;
|
||||
response->def->bufsize = 0;
|
||||
/* check whether memory could indeed be allocated */
|
||||
if (data!= NULL && data->buf != nullptr && data->def != nullptr) {
|
||||
response->def->command = PUT_OK;
|
||||
} else {
|
||||
/* let's at least tell the client that something's wrong */
|
||||
response->def->command = PUT_ERR;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutexevent);
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case PUT_DAT:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: PUT_DAT\n");
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
|
||||
datadef = (datadef_t*)request->buf;
|
||||
if (verbose>1) print_datadef(datadef);
|
||||
if (verbose>2) print_buf(request->buf, request->def->bufsize);
|
||||
|
||||
response->def->version = VERSION;
|
||||
response->def->bufsize = 0;
|
||||
if (request->def->bufsize < sizeof(datadef_t))
|
||||
response->def->command = PUT_ERR;
|
||||
else if (header == nullptr || data == nullptr)
|
||||
response->def->command = PUT_ERR;
|
||||
else if (header->def->nchans != datadef->nchans)
|
||||
response->def->command = PUT_ERR;
|
||||
else if (header->def->data_type != datadef->data_type)
|
||||
response->def->command = PUT_ERR;
|
||||
else if (datadef->nsamples > current_max_num_sample)
|
||||
response->def->command = PUT_ERR;
|
||||
else {
|
||||
unsigned int i;
|
||||
unsigned int wordsize = wordsize_from_type(header->def->data_type);
|
||||
unsigned int datasize = wordsize * datadef->nsamples * datadef->nchans;
|
||||
|
||||
response->def->command = PUT_OK;
|
||||
|
||||
if (wordsize == 0) {
|
||||
fprintf(stderr, "dmarequest: unsupported data type (%d)\n", datadef->data_type);
|
||||
response->def->command = PUT_ERR;
|
||||
} else if (datasize > datadef->bufsize || (datadef->bufsize + sizeof(datadef_t)) > request->def->bufsize) {
|
||||
fprintf(stderr, "dmarequest: invalid size definitions in PUT_DAT request\n");
|
||||
response->def->command = PUT_ERR;
|
||||
} else {
|
||||
/* number of bytes per sample (all channels) is given by wordsize x number of channels */
|
||||
unsigned int chansize = wordsize * data->def->nchans;
|
||||
/* request_data points to actual data samples within the request, use char* for convenience */
|
||||
const char *request_data = (const char *) request->buf + sizeof(datadef_t);
|
||||
char *buffer_data = (char *)data->buf;
|
||||
|
||||
for (i=0; i<datadef->nsamples; ++i) {
|
||||
memcpy(buffer_data+(thissample*chansize), request_data+(i*chansize), chansize);
|
||||
header->def->nsamples++;
|
||||
thissample++;
|
||||
thissample = WRAP(thissample, current_max_num_sample);
|
||||
}
|
||||
/* Signal possibly waiting threads that we have received data */
|
||||
pthread_cond_broadcast(&getData_cond);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case PUT_EVT:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: PUT_EVT\n");
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexevent);
|
||||
|
||||
/* Give an error message if there is no header, or if the given event array is defined badly */
|
||||
if (header == nullptr || event == nullptr || check_event_array(request->def->bufsize, request->buf) < 0) {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = PUT_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else { /* go over all events and store them one by one */
|
||||
response->def->version = VERSION;
|
||||
response->def->command = PUT_OK;
|
||||
response->def->bufsize = 0;
|
||||
|
||||
offset = 0; /* this represents the offset of the event in the buffer */
|
||||
while (offset<request->def->bufsize) {
|
||||
FREE(event[thisevent].def);
|
||||
FREE(event[thisevent].buf);
|
||||
|
||||
eventdef = (eventdef_t*)((char*)request->buf+offset);
|
||||
if (verbose>1) print_eventdef(eventdef);
|
||||
|
||||
event[thisevent].def = (eventdef_t*)malloc(sizeof(eventdef_t));
|
||||
DIE_BAD_MALLOC(event[thisevent].def);
|
||||
memcpy(event[thisevent].def, (char*)request->buf+offset, sizeof(eventdef_t));
|
||||
|
||||
/* automatically convert event->def->sample to current sample number
|
||||
(thus this event "belongs" to the first sample of the next block from PUT_DAT)
|
||||
*/
|
||||
if (event[thisevent].def->sample == EVENT_AUTO_SAMPLE) {
|
||||
event[thisevent].def->sample = header->def->nsamples;
|
||||
}
|
||||
|
||||
offset += sizeof(eventdef_t);
|
||||
event[thisevent].buf = malloc(eventdef->bufsize);
|
||||
DIE_BAD_MALLOC(event[thisevent].buf);
|
||||
memcpy(event[thisevent].buf, (char*)request->buf+offset, eventdef->bufsize);
|
||||
offset += eventdef->bufsize;
|
||||
if (verbose>1) print_eventdef(event[thisevent].def);
|
||||
thisevent++;
|
||||
thisevent = WRAP(thisevent, MAXNUMEVENT);
|
||||
header->def->nevents++;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutexevent);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case GET_HDR:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: GET_HDR\n");
|
||||
if (header == nullptr) {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_OK;
|
||||
response->def->bufsize = 0;
|
||||
response->def->bufsize = append(&response->buf, response->def->bufsize, header->def, sizeof(headerdef_t));
|
||||
response->def->bufsize = append(&response->buf, response->def->bufsize, header->buf, header->def->bufsize);
|
||||
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case GET_DAT:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: GET_DAT\n");
|
||||
if (header == nullptr || data == nullptr) {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
|
||||
if (request->def->bufsize) {
|
||||
/* the selection has been specified */
|
||||
memcpy(&datasel, request->buf, sizeof(datasel_t));
|
||||
/* If endsample is -1 read the buffer to the end */
|
||||
if(datasel.endsample == UINT32_T(-1))
|
||||
{
|
||||
datasel.endsample = header->def->nsamples - 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* determine a valid selection */
|
||||
if (header->def->nsamples>current_max_num_sample) {
|
||||
/* the ringbuffer is completely full */
|
||||
datasel.begsample = header->def->nsamples - current_max_num_sample;
|
||||
datasel.endsample = header->def->nsamples - 1;
|
||||
}
|
||||
else {
|
||||
/* the ringbuffer is not yet completely full */
|
||||
datasel.begsample = 0;
|
||||
datasel.endsample = header->def->nsamples - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// if the read should block...
|
||||
if(blockrequest == 1)
|
||||
{
|
||||
// check whether data is available
|
||||
while((datasel.begsample >= (datasel.endsample+1)) || (datasel.endsample > header->def->nsamples - 1))
|
||||
{
|
||||
// if not unlock all mutexes
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
|
||||
// wait for the condition to be signaled
|
||||
pthread_mutex_lock(&getData_mutex);
|
||||
gettimeofday(&tp, nullptr);
|
||||
ts.tv_sec = tp.tv_sec;
|
||||
ts.tv_nsec = tp.tv_usec * 1000;
|
||||
ts.tv_sec += 1;
|
||||
pthread_cond_timedwait(&getData_cond, &getData_mutex, &ts);
|
||||
pthread_mutex_unlock(&getData_mutex);
|
||||
|
||||
// Lock the mutexes again
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
if(datasel.begsample == (datasel.endsample+1))
|
||||
datasel.endsample = header->def->nsamples - 1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (verbose>1) print_headerdef(header->def);
|
||||
if (verbose>1) print_datasel(&datasel);
|
||||
|
||||
if (datasel.begsample < 0 || datasel.endsample < 0) {
|
||||
fprintf(stderr, "dmarequest: err1\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else if (datasel.begsample >= header->def->nsamples || datasel.endsample >= header->def->nsamples) {
|
||||
fprintf(stderr, "dmarequest: err2\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else if ((header->def->nsamples - datasel.begsample) > current_max_num_sample) {
|
||||
fprintf(stderr, "dmarequest: err3\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else {
|
||||
unsigned int wordsize = wordsize_from_type(data->def->data_type);
|
||||
if (wordsize==0) {
|
||||
fprintf(stderr, "dmarequest: unsupported data type (%d)\n", data->def->data_type);
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
} else {
|
||||
unsigned int n;
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_OK;
|
||||
response->def->bufsize = 0;
|
||||
|
||||
/* determine the number of samples to return */
|
||||
n = datasel.endsample - datasel.begsample + 1;
|
||||
|
||||
response->buf = malloc(sizeof(datadef_t) + n*data->def->nchans*wordsize);
|
||||
if (response->buf == nullptr) {
|
||||
/* not enough space for copying data into response */
|
||||
fprintf(stderr, "dmarequest: out of memory\n");
|
||||
response->def->command = GET_ERR;
|
||||
}
|
||||
else {
|
||||
/* number of bytes per sample (all channels) */
|
||||
unsigned int chansize = data->def->nchans * wordsize;
|
||||
|
||||
/* convenience pointer to start of actual data in response */
|
||||
char *resp_data = ((char *) response->buf) + sizeof(datadef_t);
|
||||
|
||||
/* this is the location of begsample within the ringbuffer */
|
||||
unsigned int start_index = WRAP(datasel.begsample, current_max_num_sample);
|
||||
|
||||
/* have datadef point into the freshly allocated response buffer and directly
|
||||
fill in the information */
|
||||
datadef = (datadef_t *) response->buf;
|
||||
datadef->nchans = data->def->nchans;
|
||||
datadef->data_type = data->def->data_type;
|
||||
datadef->nsamples = n;
|
||||
datadef->bufsize = n*chansize;
|
||||
|
||||
response->def->bufsize = sizeof(datadef_t) + datadef->bufsize;
|
||||
|
||||
if (start_index + n <= current_max_num_sample) {
|
||||
/* we can copy everything in one go */
|
||||
memcpy(resp_data, (char*)(data->buf) + start_index*chansize, n*chansize);
|
||||
} else {
|
||||
/* need to wrap around at current_max_num_sample */
|
||||
unsigned int na = current_max_num_sample - start_index;
|
||||
unsigned int nb = n - na;
|
||||
|
||||
memcpy(resp_data, (char*)(data->buf) + start_index*chansize, na*chansize);
|
||||
memcpy(resp_data + na*chansize, (char*)(data->buf), nb*chansize);
|
||||
|
||||
/* std::cout << "Wrapped around!\n"; */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case GET_EVT:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: GET_EVT\n");
|
||||
if (header == nullptr || event == nullptr || header->def->nevents==0) {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexevent);
|
||||
|
||||
eventsel = (eventsel_t*)malloc(sizeof(eventsel_t));
|
||||
DIE_BAD_MALLOC(eventsel);
|
||||
|
||||
/* determine the selection */
|
||||
if (request->def->bufsize) {
|
||||
/* the selection has been specified */
|
||||
memcpy(eventsel, request->buf, sizeof(eventsel_t));
|
||||
}
|
||||
else {
|
||||
/* determine a valid selection */
|
||||
if (header->def->nevents>MAXNUMEVENT) {
|
||||
/* the ringbuffer is completely full */
|
||||
eventsel->begevent = header->def->nevents - MAXNUMEVENT;
|
||||
eventsel->endevent = header->def->nevents - 1;
|
||||
}
|
||||
else {
|
||||
/* the ringbuffer is not yet completely full */
|
||||
eventsel->begevent = 0;
|
||||
eventsel->endevent = header->def->nevents - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose>1) print_headerdef(header->def);
|
||||
if (verbose>1) print_eventsel(eventsel);
|
||||
|
||||
if (eventsel == nullptr) {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else if (eventsel->begevent < 0 || eventsel->endevent < 0) {
|
||||
fprintf(stderr, "dmarequest: err1\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else if (eventsel->begevent >= header->def->nevents || eventsel->endevent >= header->def->nevents) {
|
||||
fprintf(stderr, "dmarequest: err2\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else if ((header->def->nevents-eventsel->begevent) > MAXNUMEVENT) {
|
||||
fprintf(stderr, "dmarequest: err3\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else {
|
||||
unsigned int j,n;
|
||||
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_OK;
|
||||
response->def->bufsize = 0;
|
||||
|
||||
/* determine the number of events to return */
|
||||
n = eventsel->endevent - eventsel->begevent + 1;
|
||||
|
||||
for (j=0; j<n; ++j) {
|
||||
if (verbose>1) print_eventdef(event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].def);
|
||||
response->def->bufsize = append(&response->buf, response->def->bufsize, event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].def, sizeof(eventdef_t));
|
||||
response->def->bufsize = append(&response->buf, response->def->bufsize, event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].buf, event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].def->bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
FREE(eventsel);
|
||||
pthread_mutex_unlock(&mutexevent);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case FLUSH_HDR:
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
pthread_mutex_lock(&mutexevent);
|
||||
if (header) {
|
||||
free_header();
|
||||
free_data();
|
||||
free_event();
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_OK;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutexevent);
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case FLUSH_DAT:
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
if (header && data) {
|
||||
header->def->nsamples = thissample = 0;
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_OK;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case FLUSH_EVT:
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexevent);
|
||||
if (header && event) {
|
||||
unsigned int i;
|
||||
|
||||
header->def->nevents = thisevent = 0;
|
||||
for (i=0; i<MAXNUMEVENT; ++i) {
|
||||
FREE(event[i].def);
|
||||
FREE(event[i].buf);
|
||||
}
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_OK;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutexevent);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case WAIT_DAT:
|
||||
/* SK: This request means that the client wants to wait until
|
||||
MORE than waitdef_t.threshold.nsamples samples OR
|
||||
MORE THAN waitdef_t.threshold.nevents events
|
||||
are in the buffer, BUT
|
||||
only for the time given in waitdef_t.milliseconds.
|
||||
The response is just the number of samples and events
|
||||
in the buffer as described by samples_events_t.
|
||||
*/
|
||||
response->def->version = VERSION;
|
||||
if (header == nullptr || request->def->bufsize!=sizeof(waitdef_t)) {
|
||||
response->def->command = WAIT_ERR;
|
||||
response->def->bufsize = 0;
|
||||
} else {
|
||||
int waiterr;
|
||||
waitdef_t *wd = (waitdef_t *) request->buf;
|
||||
samples_events_t *nret = (samples_events_t*)malloc(sizeof(samples_events_t));
|
||||
UINT32_T nsmp, nevt;
|
||||
|
||||
if (nret == nullptr) {
|
||||
/* highly unlikely, but we cannot allocate a sample_event_t - return an error */
|
||||
response->def->command = WAIT_ERR;
|
||||
response->def->bufsize = 0;
|
||||
break;
|
||||
}
|
||||
/* Let response->buf point to the new sample_event_t structure */
|
||||
response->def->command = WAIT_OK;
|
||||
response->def->bufsize = sizeof(samples_events_t);
|
||||
response->buf = nret;
|
||||
|
||||
/* get current number of samples */
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
nsmp = header->def->nsamples;
|
||||
nevt = header->def->nevents;
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
|
||||
if (wd->milliseconds == 0 || nsmp > wd->threshold.nsamples || nevt > wd->threshold.nevents) {
|
||||
/* the client doesn't want to wait, or
|
||||
we're already above the threshold:
|
||||
return immediately */
|
||||
nret->nsamples = nsmp;
|
||||
nret->nevents = nevt;
|
||||
break;
|
||||
}
|
||||
gettimeofday(&tp, nullptr);
|
||||
ts.tv_sec = tp.tv_sec + (wd->milliseconds/1000);
|
||||
ts.tv_nsec = 1000 * (tp.tv_usec + (wd->milliseconds % 1000)*1000);
|
||||
while (ts.tv_nsec >= 1000000000) {
|
||||
ts.tv_sec++;
|
||||
ts.tv_nsec-=1000000000;
|
||||
}
|
||||
|
||||
/* FIXME: The getData condition variable is only triggered by incoming data, not events */
|
||||
do {
|
||||
pthread_mutex_lock(&getData_mutex);
|
||||
waiterr = pthread_cond_timedwait(&getData_cond, &getData_mutex, &ts);
|
||||
pthread_mutex_unlock(&getData_mutex);
|
||||
|
||||
/* get current number of samples */
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
nsmp = header->def->nsamples;
|
||||
nevt = header->def->nevents;
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
} while (nsmp <= wd->threshold.nsamples && nevt <= wd->threshold.nevents && waiterr==0);
|
||||
nret->nsamples = nsmp;
|
||||
nret->nevents = nevt;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "dmarequest: unknown command\n");
|
||||
}
|
||||
|
||||
if (verbose>0) fprintf(stderr, "dmarequest: thissample = %u, thisevent = %u\n", thissample, thisevent);
|
||||
|
||||
/* everything went fine */
|
||||
return 0;
|
||||
}
|
||||
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2010, Stefan Klanke
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
#include <buffer.h>
|
||||
|
||||
/* TODO: see if these can be optimized using compiler intrinsics etc. */
|
||||
void ft_swap16(unsigned int numel, void *data) {
|
||||
unsigned int n;
|
||||
char *d = (char *) data;
|
||||
for (n=0;n<numel;n++) {
|
||||
char t = d[0];
|
||||
d[0] = d[1];
|
||||
d[1] = t;
|
||||
d+=2;
|
||||
}
|
||||
}
|
||||
|
||||
void ft_swap32(unsigned int numel, void *data) {
|
||||
unsigned int n;
|
||||
char *d = (char *) data;
|
||||
for (n=0;n<numel;n++) {
|
||||
char t0 = d[0];
|
||||
char t1 = d[1];
|
||||
d[0] = d[3];
|
||||
d[1] = d[2];
|
||||
d[2] = t1;
|
||||
d[3] = t0;
|
||||
d+=4;
|
||||
}
|
||||
}
|
||||
|
||||
void ft_swap64(unsigned int numel, void *data) {
|
||||
unsigned int n;
|
||||
char *d = (char *) data;
|
||||
for (n=0;n<numel;n++) {
|
||||
char t0 = d[0];
|
||||
char t1 = d[1];
|
||||
char t2 = d[2];
|
||||
char t3 = d[3];
|
||||
d[0] = d[7];
|
||||
d[1] = d[6];
|
||||
d[2] = d[5];
|
||||
d[3] = d[4];
|
||||
d[4] = t3;
|
||||
d[5] = t2;
|
||||
d[6] = t1;
|
||||
d[7] = t0;
|
||||
d+=8;
|
||||
}
|
||||
}
|
||||
|
||||
void ft_swap_data(UINT32_T numel, UINT32_T datatype, void *data) {
|
||||
switch(datatype) {
|
||||
case DATATYPE_CHAR:
|
||||
case DATATYPE_UINT8:
|
||||
case DATATYPE_INT8:
|
||||
return;
|
||||
case DATATYPE_UINT16:
|
||||
case DATATYPE_INT16:
|
||||
ft_swap16(numel, data);
|
||||
return;
|
||||
case DATATYPE_UINT32:
|
||||
case DATATYPE_INT32:
|
||||
case DATATYPE_FLOAT32:
|
||||
ft_swap32(numel, data);
|
||||
return;
|
||||
case DATATYPE_UINT64:
|
||||
case DATATYPE_INT64:
|
||||
case DATATYPE_FLOAT64:
|
||||
ft_swap64(numel, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int ft_swap_chunks_to_native(UINT32_T size, UINT32_T nchans, void *buf) {
|
||||
UINT32_T offset = 0;
|
||||
while (offset + sizeof(ft_chunkdef_t) <= size) {
|
||||
ft_chunk_t *chunk = (ft_chunk_t *) ((char *) buf + offset);
|
||||
|
||||
ft_swap32(2, &(chunk->def));
|
||||
|
||||
offset += sizeof(ft_chunkdef_t) + chunk->def.size;
|
||||
/* chunk definition fault (=too big) ? */
|
||||
if (offset > size) return -1;
|
||||
|
||||
switch(chunk->def.type) {
|
||||
case FT_CHUNK_RESOLUTIONS:
|
||||
if (chunk->def.size >= nchans*sizeof(FLOAT64_T)) {
|
||||
ft_swap64(nchans, chunk->data);
|
||||
}
|
||||
break;
|
||||
/* Add other cases here as needed */
|
||||
}
|
||||
|
||||
offset += sizeof(ft_chunkdef_t) + chunk->def.size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0 on success, -1 on error */
|
||||
int ft_swap_events_to_native(UINT32_T size, void *buf) {
|
||||
UINT32_T offset = 0;
|
||||
|
||||
while (offset + sizeof(eventdef_t) <= size) {
|
||||
unsigned int wst, wsv;
|
||||
|
||||
eventdef_t *edef = (eventdef_t *) ((char *) buf + offset);
|
||||
ft_swap32(8, edef); /* all fields are 32-bit */
|
||||
|
||||
/* Increase offset to beginning of next event */
|
||||
offset += sizeof(eventdef_t) + edef->bufsize;
|
||||
if (offset > size) return -1; /* this event is too big for "buf" */
|
||||
|
||||
wst = wordsize_from_type(edef->type_type);
|
||||
wsv = wordsize_from_type(edef->value_type);
|
||||
|
||||
/* check if type and value fit into this event's local buffer */
|
||||
if (wst*edef->type_numel + wsv*edef->value_numel > edef->bufsize) return -1;
|
||||
|
||||
ft_swap_data(edef->type_numel, edef->type_type, (char *) buf + offset);
|
||||
ft_swap_data(edef->value_numel, edef->value_type, (char *) buf + offset + wst*edef->type_numel);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* returns 0 on success, -1 on error */
|
||||
int ft_swap_buf_to_native(UINT16_T command, UINT32_T bufsize, void *buf) {
|
||||
datadef_t *ddef;
|
||||
|
||||
switch(command) {
|
||||
case GET_HDR:
|
||||
/* This should not have a buf attached */
|
||||
return 0;
|
||||
case GET_DAT:
|
||||
/* buf contains a datsel_t = 2x UINT32_T */
|
||||
if (bufsize == 8) ft_swap32(2, buf);
|
||||
return 0;
|
||||
case GET_EVT:
|
||||
/* buf contains a datsel_t = 2x UINT32_T */
|
||||
if (bufsize == 8) ft_swap32(2, buf);
|
||||
return 0;
|
||||
case WAIT_DAT:
|
||||
/* buf contains a waitdef_t = 3x UINT32_T */
|
||||
ft_swap32(3, buf);
|
||||
return 0;
|
||||
case PUT_DAT:
|
||||
/* buf contains a datadef_t and after that the data */
|
||||
ddef = (datadef_t *) buf;
|
||||
ft_swap32(4, ddef); /* this is for datadef_t */
|
||||
ft_swap_data(ddef->nchans*ddef->nsamples, ddef->data_type, ddef + 1); /* ddef+1 points to first data byte */
|
||||
return 0;
|
||||
case PUT_HDR:
|
||||
/* buf contains a headerdef_t and optionally chunks */
|
||||
ft_swap32(6, buf); /* all fields are 32-bit values */
|
||||
return ft_swap_chunks_to_native(bufsize - sizeof(headerdef_t), ((headerdef_t *) buf)->nchans, (char *) buf + sizeof(headerdef_t));
|
||||
case PUT_EVT:
|
||||
/* buf contains multiple eventdef_t and buf's */
|
||||
return ft_swap_events_to_native(bufsize, buf);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int ft_swap_chunks_from_native(UINT32_T size, UINT32_T nchans, void *buf) {
|
||||
UINT32_T offset = 0;
|
||||
while (offset + sizeof(ft_chunkdef_t) <= size) {
|
||||
ft_chunk_t *chunk = (ft_chunk_t *) ((char *) buf + offset);
|
||||
offset += sizeof(ft_chunkdef_t) + chunk->def.size;
|
||||
|
||||
/* chunk definition fault (=too big) ? */
|
||||
if (offset > size) return -1;
|
||||
|
||||
switch(chunk->def.type) {
|
||||
case FT_CHUNK_RESOLUTIONS:
|
||||
if (chunk->def.size >= nchans*sizeof(FLOAT64_T)) {
|
||||
ft_swap64(nchans, chunk->data);
|
||||
}
|
||||
break;
|
||||
/* Add other cases here as needed */
|
||||
}
|
||||
|
||||
ft_swap32(2, &(chunk->def));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ft_swap_events_from_native(UINT32_T size, void *buf) {
|
||||
UINT32_T offset = 0;
|
||||
|
||||
while (offset + sizeof(eventdef_t) <= size) {
|
||||
unsigned int wst;
|
||||
|
||||
eventdef_t *edef = (eventdef_t *) ((char *) buf + offset);
|
||||
offset += sizeof(eventdef_t) + edef->bufsize;
|
||||
|
||||
wst = wordsize_from_type(edef->type_type);
|
||||
|
||||
ft_swap_data(edef->type_numel, edef->type_type, (char *) buf + offset);
|
||||
ft_swap_data(edef->value_numel, edef->value_type, (char *) buf + offset + wst*edef->type_numel);
|
||||
ft_swap32(8, edef); /* all fields are 32-bit */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ft_swap_from_native(UINT16_T orgCommand, message_t *msg) {
|
||||
datadef_t *ddef;
|
||||
UINT32_T nchans;
|
||||
|
||||
UINT32_T bufsize = msg->def->bufsize;
|
||||
|
||||
ft_swap16(1, &msg->def->version);
|
||||
ft_swap16(1, &msg->def->command);
|
||||
ft_swap32(1, &msg->def->bufsize);
|
||||
|
||||
if (bufsize == 0) return 0;
|
||||
|
||||
switch(orgCommand) {
|
||||
case GET_HDR:
|
||||
nchans = ((headerdef_t *) msg->buf)->nchans;
|
||||
ft_swap32(6, msg->buf); /* all fields are 32-bit values */
|
||||
return ft_swap_chunks_from_native(bufsize - sizeof(headerdef_t), nchans, (char *) msg->buf + sizeof(headerdef_t));
|
||||
case GET_DAT:
|
||||
ddef = (datadef_t *) msg->buf;
|
||||
ft_swap_data(ddef->nchans*ddef->nsamples, ddef->data_type, ddef + 1); /* ddef+1 points to first data byte */
|
||||
ft_swap32(5, ddef); /* all fields are 32-bit */
|
||||
return 0;
|
||||
case GET_EVT:
|
||||
return ft_swap_events_from_native(bufsize, msg->buf);
|
||||
case WAIT_DAT:
|
||||
ft_swap32(2, msg->buf); /* nsamples + nevents = 32bit */
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include "buffer.h"
|
||||
#include <pthread.h>
|
||||
|
||||
pthread_mutex_t mutexstatus = PTHREAD_MUTEX_INITIALIZER;
|
||||
int tcpserverStatus = 0;
|
||||
|
||||
pthread_mutex_t mutexthreadcount = PTHREAD_MUTEX_INITIALIZER;
|
||||
int threadcount = 0;
|
||||
|
||||
pthread_mutex_t mutexsocketcount = PTHREAD_MUTEX_INITIALIZER;
|
||||
int socketcount = 0;
|
||||
|
||||
pthread_mutex_t mutexappendcount = PTHREAD_MUTEX_INITIALIZER;
|
||||
int appendcount = 0;
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#ifndef EXTERN_H
|
||||
#define EXTERN_H
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
extern pthread_mutex_t mutexstatus;
|
||||
extern int tcpserverStatus;
|
||||
|
||||
extern pthread_mutex_t mutexthreadcount;
|
||||
extern int threadcount;
|
||||
|
||||
extern pthread_mutex_t mutexsocketcount;
|
||||
extern int socketcount;
|
||||
|
||||
extern pthread_mutex_t mutexthreadcount;
|
||||
extern int threadcount;
|
||||
|
||||
extern pthread_mutex_t mutexappendcount;
|
||||
extern int appendcount;
|
||||
|
||||
#endif
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld & Christian Hesse
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
/* prevent double include */
|
||||
#ifndef MESSAGE_H
|
||||
#define MESSAGE_H
|
||||
|
||||
#include "platform_includes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
#endif
|
||||
|
||||
/* FIXME these are obvious at the moment, but should be formally defined */
|
||||
typedef char CHAR_T;
|
||||
typedef float FLOAT32_T;
|
||||
typedef double FLOAT64_T;
|
||||
|
||||
/* the following types should be according to "ISO C99: 7.18 Integer types" (see /usr/include/stdint.h on OSX and Linux) */
|
||||
/* FIXME different endianness between client/server is not supported at the moment */
|
||||
|
||||
#ifndef INT8_T
|
||||
typedef int8_t INT8_T;
|
||||
#endif
|
||||
|
||||
#ifndef INT16_T
|
||||
typedef int16_t INT16_T;
|
||||
#endif
|
||||
|
||||
#ifndef INT32_T
|
||||
typedef int INT32_T;
|
||||
#endif
|
||||
|
||||
#ifndef INT64_T
|
||||
typedef int64_t INT64_T;
|
||||
#endif
|
||||
|
||||
#ifndef UINT8_T
|
||||
typedef uint8_t UINT8_T;
|
||||
#endif
|
||||
|
||||
#ifndef UINT16_T
|
||||
typedef uint16_t UINT16_T;
|
||||
#endif
|
||||
|
||||
#ifndef UINT32_T
|
||||
typedef uint32_t UINT32_T;
|
||||
#endif
|
||||
|
||||
#ifndef UINT64_T
|
||||
typedef uint64_t UINT64_T;
|
||||
#endif
|
||||
|
||||
/* these can be used for indexing the buffer pointer as array */
|
||||
#define WORDSIZE_CHAR sizeof(CHAR_T )
|
||||
#define WORDSIZE_UINT8 sizeof(UINT8_T )
|
||||
#define WORDSIZE_UINT16 sizeof(UINT16_T )
|
||||
#define WORDSIZE_UINT32 sizeof(UINT32_T )
|
||||
#define WORDSIZE_UINT64 sizeof(UINT64_T )
|
||||
#define WORDSIZE_INT8 sizeof(INT8_T )
|
||||
#define WORDSIZE_INT16 sizeof(INT16_T )
|
||||
#define WORDSIZE_INT32 sizeof(INT32_T )
|
||||
#define WORDSIZE_INT64 sizeof(INT64_T )
|
||||
#define WORDSIZE_FLOAT32 sizeof(FLOAT32_T)
|
||||
#define WORDSIZE_FLOAT64 sizeof(FLOAT64_T)
|
||||
|
||||
/* define the version of the message packet */
|
||||
#define VERSION (UINT16_T)0x0001
|
||||
|
||||
/* the same version number in the "other" endianness */
|
||||
#define VERSION_OE (UINT16_T) (((VERSION & 0x00FF) << 8) | ((VERSION & 0xFF00) >> 8))
|
||||
|
||||
/* these define the commands that can be used, which are split over the two available bytes */
|
||||
#define PUT_HDR (UINT16_T)0x0101
|
||||
#define PUT_DAT (UINT16_T)0x0102
|
||||
#define PUT_EVT (UINT16_T)0x0103
|
||||
#define PUT_OK (UINT16_T)0x0104
|
||||
#define PUT_ERR (UINT16_T)0x0105
|
||||
|
||||
#define GET_HDR (UINT16_T)0x0201
|
||||
#define GET_DAT (UINT16_T)0x0202
|
||||
#define GET_EVT (UINT16_T)0x0203
|
||||
#define GET_OK (UINT16_T)0x0204
|
||||
#define GET_ERR (UINT16_T)0x0205
|
||||
|
||||
#define FLUSH_HDR (UINT16_T)0x0301
|
||||
#define FLUSH_DAT (UINT16_T)0x0302
|
||||
#define FLUSH_EVT (UINT16_T)0x0303
|
||||
#define FLUSH_OK (UINT16_T)0x0304
|
||||
#define FLUSH_ERR (UINT16_T)0x0305
|
||||
|
||||
#define WAIT_DAT (UINT16_T)0x0402
|
||||
#define WAIT_OK (UINT16_T)0x0404
|
||||
#define WAIT_ERR (UINT16_T)0x0405
|
||||
|
||||
/* these are used in the data_t and event_t structure */
|
||||
#define DATATYPE_CHAR (UINT32_T)0
|
||||
#define DATATYPE_UINT8 (UINT32_T)1
|
||||
#define DATATYPE_UINT16 (UINT32_T)2
|
||||
#define DATATYPE_UINT32 (UINT32_T)3
|
||||
#define DATATYPE_UINT64 (UINT32_T)4
|
||||
#define DATATYPE_INT8 (UINT32_T)5
|
||||
#define DATATYPE_INT16 (UINT32_T)6
|
||||
#define DATATYPE_INT32 (UINT32_T)7
|
||||
#define DATATYPE_INT64 (UINT32_T)8
|
||||
#define DATATYPE_FLOAT32 (UINT32_T)9
|
||||
#define DATATYPE_FLOAT64 (UINT32_T)10
|
||||
/* this should never be used to put data into the buffer,
|
||||
but is handy for handling conversions of other data types
|
||||
*/
|
||||
#define DATATYPE_UNKNOWN (UINT32_T)0xFFFFFFFF
|
||||
|
||||
/* these are used in the specification of the event selection criteria */
|
||||
#define EVENTSEL_TYPE 1
|
||||
#define EVENTSEL_VALUE 2
|
||||
#define EVENTSEL_SAMPLE 3 /* for an exact match */
|
||||
#define EVENTSEL_MINSAMPLE 4
|
||||
#define EVENTSEL_MAXSAMPLE 5
|
||||
|
||||
|
||||
/* if event->def->sample == EVENT_AUTO_SAMPLE, automatically insert
|
||||
current sample index instead
|
||||
*/
|
||||
#define EVENT_AUTO_SAMPLE -1
|
||||
|
||||
/** The following enumeration is for specifying types of chunks that may be present
|
||||
in the "buf" part of the Fieldtrip header.
|
||||
*/
|
||||
enum {
|
||||
/** FT_CHUNK_UNSPECIFIED refers to a binary blob of known length, but unknown contents.
|
||||
Clients encountering this can try to use auto-detection, or just ignore this chunk.
|
||||
Unknown chunk types should be treated in the same manner. */
|
||||
FT_CHUNK_UNSPECIFIED = 0,
|
||||
|
||||
/** FT_CHUNK_CHANNEL_NAMES contains the channel names in ASCII format. Each channel is
|
||||
represented as a 0-terminated string (includes the case of just a 0 for an empty string).
|
||||
Example: chunk_data = "Left\0Right\0" for stereo sound signals. */
|
||||
FT_CHUNK_CHANNEL_NAMES = 1,
|
||||
|
||||
/** FT_CHUNK_CHANNEL_FLAGS contains a 0-terminated string describing the type of flags,
|
||||
and after that N (=#channels) bytes describing each channel. This is useful for
|
||||
specifying that a channel can have a discrete number of different types, e.g.
|
||||
chunk_data = "meg_ad_eog\0\1\1\1\1\3\3\2\2" should be used for a system with 8 channels,
|
||||
the first four of which are for MEG, then 2 channels EOG, then 2 channels A/D. */
|
||||
FT_CHUNK_CHANNEL_FLAGS = 2,
|
||||
|
||||
/** FT_CHUNK_RESOLUTIONS contains N double precision values mapping from A/D values to physical
|
||||
quantities such as micro-Volts in EEG. */
|
||||
FT_CHUNK_RESOLUTIONS = 3,
|
||||
|
||||
/** FT_CHUNK_ASCII_KEYVAL contains an arbitrary number of key/value pairs, each of
|
||||
which is given as a 0-terminated string. An empty key (=double 0) indicates the
|
||||
end of the list. Example: "amplifier_gain\0high\0noise_reduction\0active\0\0". */
|
||||
FT_CHUNK_ASCII_KEYVAL = 4,
|
||||
|
||||
/** FT_CHUNK_NIFTI1 contains a NIFTI-1 header (348 bytes long) */
|
||||
FT_CHUNK_NIFTI1 = 5,
|
||||
|
||||
/** FT_CHUNK_SIEMENS_AP contains Siemens Protocol data in ASCII format (string) */
|
||||
FT_CHUNK_SIEMENS_AP = 6,
|
||||
|
||||
/** FT_CHUNK_CTF_RES4 contains a .res4 file as written by the CTF MEG acquisition software (binary) */
|
||||
FT_CHUNK_CTF_RES4 = 7
|
||||
};
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
/* a packet that is sent over the network (or to disk) should contain the following */
|
||||
typedef struct {
|
||||
UINT16_T version; /* see VERSION */
|
||||
UINT16_T command; /* see PUT_xxx, GET_xxx and FLUSH_xxx */
|
||||
UINT32_T bufsize; /* size of the buffer in bytes */
|
||||
} messagedef_t;
|
||||
|
||||
/* the header definition is fixed, except for the channel labels */
|
||||
typedef struct {
|
||||
UINT32_T nchans;
|
||||
UINT32_T nsamples;
|
||||
UINT32_T nevents;
|
||||
FLOAT32_T fsample;
|
||||
UINT32_T data_type;
|
||||
UINT32_T bufsize; /* size of the buffer in bytes */
|
||||
} headerdef_t;
|
||||
|
||||
/* the data definition is fixed */
|
||||
typedef struct {
|
||||
UINT32_T nchans;
|
||||
UINT32_T nsamples;
|
||||
UINT32_T data_type;
|
||||
UINT32_T bufsize; /* size of the buffer in bytes */
|
||||
} datadef_t;
|
||||
|
||||
/* the event definition is fixed */
|
||||
typedef struct {
|
||||
UINT32_T type_type; /* usual would be DATATYPE_CHAR */
|
||||
UINT32_T type_numel; /* length of the type string */
|
||||
UINT32_T value_type;
|
||||
UINT32_T value_numel;
|
||||
INT32_T sample;
|
||||
INT32_T offset;
|
||||
INT32_T duration;
|
||||
UINT32_T bufsize; /* size of the buffer in bytes */
|
||||
} eventdef_t;
|
||||
|
||||
typedef struct {
|
||||
messagedef_t *def;
|
||||
void *buf;
|
||||
} message_t;
|
||||
|
||||
typedef struct {
|
||||
headerdef_t *def;
|
||||
void *buf; /* FIXME this should contain the channel names */
|
||||
} header_t;
|
||||
|
||||
typedef struct {
|
||||
datadef_t *def;
|
||||
void *buf;
|
||||
} data_t;
|
||||
|
||||
typedef struct {
|
||||
eventdef_t *def;
|
||||
void *buf;
|
||||
} event_t;
|
||||
|
||||
typedef struct {
|
||||
UINT32_T begsample; /* indexing starts with 0, should be >=0 */
|
||||
UINT32_T endsample; /* indexing starts with 0, should be <header.nsamples */
|
||||
} datasel_t;
|
||||
|
||||
typedef struct {
|
||||
UINT32_T begevent;
|
||||
UINT32_T endevent;
|
||||
} eventsel_t;
|
||||
|
||||
typedef struct {
|
||||
UINT32_T nsamples;
|
||||
UINT32_T nevents;
|
||||
} samples_events_t;
|
||||
|
||||
typedef struct {
|
||||
samples_events_t threshold;
|
||||
UINT32_T milliseconds;
|
||||
} waitdef_t;
|
||||
|
||||
typedef struct {
|
||||
UINT32_T type; /* One of FT_CHUNK_** (see above) */
|
||||
UINT32_T size; /* Size of chunk.data, total size is given by adding sizeof(ft_chunkdef_t)=8 */
|
||||
} ft_chunkdef_t;
|
||||
|
||||
typedef struct {
|
||||
ft_chunkdef_t def; /* See above. Note that this is not a pointer! */
|
||||
char data[1]; /* Data contained in this chunk */
|
||||
} ft_chunk_t;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
|
||||
/* prevent double include */
|
||||
#pragma once
|
||||
|
||||
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)
|
||||
/* linux, also other platforms (Hurd etc) that use GLIBC */
|
||||
#define PLATFORM_LINUX
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
/* BSD */
|
||||
#define PLATFORM_BSD
|
||||
|
||||
#elif defined(sun) || defined(__sun)
|
||||
/* Solaris */
|
||||
#define PLATFORM_SUN
|
||||
|
||||
#elif defined(__sgi)
|
||||
/* SGI Irix */
|
||||
#define PLATFORM_SGI
|
||||
|
||||
#elif defined(__hpux)
|
||||
/* hp unix */
|
||||
#define PLATFORM_HP
|
||||
|
||||
#elif defined(__CYGWIN__)
|
||||
/* cygwin is not win32 */
|
||||
#define PLATFORM_CYGWIN
|
||||
|
||||
#elif defined(_WIN64) || defined(__WIN64__) || defined(WIN64)
|
||||
/* win64 */
|
||||
#define PLATFORM_WIN64
|
||||
#define PLATFORM_WINDOWS
|
||||
|
||||
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
|
||||
/* win32 */
|
||||
#define PLATFORM_WIN32
|
||||
#define PLATFORM_WINDOWS
|
||||
|
||||
#elif defined(__BEOS__)
|
||||
/* BeOS */
|
||||
#define PLATFORM_BEOS
|
||||
|
||||
#elif defined (__APPLE__) && defined (__MACH__)
|
||||
/* MacOSX */
|
||||
#define PLATFORM_OSX
|
||||
|
||||
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
|
||||
/* MacOS classic */
|
||||
#define PLATFORM_MAC
|
||||
|
||||
#elif defined(__IBMCPP__) || defined(_AIX)
|
||||
/* IBM */
|
||||
#define PLATFORM_AIX
|
||||
|
||||
#elif defined(__amigaos__)
|
||||
/* AmigaOS */
|
||||
#define PLATFORM_AMIGA
|
||||
|
||||
#elif defined(__QNXNTO__)
|
||||
/* QNX */
|
||||
#define PLATFORM_QNX
|
||||
|
||||
#elif defined(__VXWORKS__)
|
||||
/* vxWorks */
|
||||
#define PLATFORM_VXWORKS
|
||||
|
||||
#elif defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)
|
||||
/* generic unix platform */
|
||||
#define PLATFORM_UNIX
|
||||
|
||||
#else
|
||||
/* the platform cannot be determined at compile time */
|
||||
#error "Unknown platform - please report the platform details to http://fieldtrip.fcdonders.nl"
|
||||
|
||||
#endif
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
#include "platform.h"
|
||||
#include "compiler.h"
|
||||
|
||||
#if defined (COMPILER_BORLAND)
|
||||
|
||||
#endif
|
||||
|
||||
/* these platforms will always use a similar gcc compiler */
|
||||
#if defined (PLATFORM_LINUX) || defined (PLATFORM_OSX)
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define closesocket(s) (close(s))
|
||||
|
||||
#elif defined (PLATFORM_WIN64)
|
||||
|
||||
#if defined (COMPILER_MSVC)
|
||||
#include <winsock2.h> /* for timeval */
|
||||
#include "win32/gettimeofday.h"
|
||||
#include "win32/stdint.h"
|
||||
|
||||
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
#elif defined (COMPILER_MINGW)
|
||||
#include <winsock2.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define bzero(b,len) memset(b,0,len)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
|
||||
#ifndef strcasecmp
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
#endif
|
||||
#else
|
||||
#error "Unsupported compiler"
|
||||
#endif
|
||||
|
||||
#elif defined (PLATFORM_WIN32)
|
||||
/* there are various compiler options for windows */
|
||||
#if defined (COMPILER_BORLAND)
|
||||
#include <windows.h>
|
||||
#include "win32/gettimeofday.h"
|
||||
|
||||
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
/* without the following, compilation with the Borland command line tools fails -- SK */
|
||||
typedef __int8 int8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef __int32 int;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
#elif defined (COMPILER_MSVC)
|
||||
#include <winsock2.h> /* for timeval */
|
||||
#if (_MSC_VER >= 1600 )
|
||||
#include <stdint.h>
|
||||
#else
|
||||
typedef __int8 int8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef __int32 int;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#endif
|
||||
|
||||
|
||||
#include "win32/gettimeofday.h"
|
||||
|
||||
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
|
||||
#elif defined (COMPILER_MINGW)
|
||||
#include <winsock2.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
|
||||
#elif defined (COMPILER_CYGWIN)
|
||||
#include <winsock2.h>
|
||||
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
#elif defined (COMPILER_LCC)
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include "win32/gettimeofday.h"
|
||||
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
|
||||
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
|
||||
#ifndef UINT8_T
|
||||
#define UINT8_T unsigned char
|
||||
#endif
|
||||
|
||||
#ifndef INT8_T
|
||||
#define INT8_T char
|
||||
#endif
|
||||
|
||||
#ifndef UINT16_T
|
||||
#define UINT16_T unsigned short
|
||||
#endif
|
||||
|
||||
#ifndef INT16_T
|
||||
#define INT16_T short
|
||||
#endif
|
||||
|
||||
#ifndef UINT32_T
|
||||
#define UINT32_T unsigned int
|
||||
#endif
|
||||
|
||||
#ifndef INT32_T
|
||||
#define INT32_T int
|
||||
#endif
|
||||
|
||||
#ifndef UINT64_T
|
||||
#define UINT64_T unsigned long long
|
||||
#endif
|
||||
|
||||
#ifndef INT64_T
|
||||
#define INT64_T long long
|
||||
#endif
|
||||
|
||||
/* #define PTW32_STATIC_LIB
|
||||
#define __cdecl
|
||||
#define PTW_CDECL
|
||||
*/
|
||||
#endif /* compiler */
|
||||
|
||||
#endif /* platform */
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld & Christian Hesse
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "buffer.h"
|
||||
|
||||
void print_request(messagedef_t *request) {
|
||||
fprintf(stderr, "request.version = 0x%04x\n", request->version);
|
||||
fprintf(stderr, "request.command = 0x%04x\n", request->command);
|
||||
fprintf(stderr, "request.bufsize = %u\n", request->bufsize);
|
||||
}
|
||||
|
||||
void print_response(messagedef_t *response) {
|
||||
fprintf(stderr, "response.version = 0x%04x\n", response->version);
|
||||
fprintf(stderr, "response.command = 0x%04x\n", response->command);
|
||||
fprintf(stderr, "response.bufsize = %u\n", response->bufsize);
|
||||
}
|
||||
|
||||
void print_headerdef(headerdef_t *headerdef) {
|
||||
if (headerdef == nullptr)
|
||||
fprintf(stderr, "headerdef == nullptr\n");
|
||||
else {
|
||||
fprintf(stderr, "headerdef.nchans = %u\n", headerdef->nchans);
|
||||
fprintf(stderr, "headerdef.nsamples = %u\n", headerdef->nsamples);
|
||||
fprintf(stderr, "headerdef.nevents = %u\n", headerdef->nevents);
|
||||
fprintf(stderr, "headerdef.fsample = %f\n", headerdef->fsample);
|
||||
fprintf(stderr, "headerdef.data_type = %u\n", headerdef->data_type);
|
||||
fprintf(stderr, "headerdef.bufsize = %u\n", headerdef->bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
void print_datadef(datadef_t *datadef) {
|
||||
if (datadef == nullptr)
|
||||
fprintf(stderr, "datadef == nullptr\n");
|
||||
else {
|
||||
fprintf(stderr, "datadef.nchans = %u\n", datadef->nchans);
|
||||
fprintf(stderr, "datadef.nsamples = %u\n", datadef->nsamples);
|
||||
fprintf(stderr, "datadef.data_type = %u\n", datadef->data_type);
|
||||
fprintf(stderr, "datadef.bufsize = %u\n", datadef->bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
void print_eventdef(eventdef_t *eventdef) {
|
||||
if (eventdef == nullptr)
|
||||
fprintf(stderr, "eventdef == nullptr\n");
|
||||
else {
|
||||
fprintf(stderr, "eventdef.type_type = %u\n", eventdef->type_type );
|
||||
fprintf(stderr, "eventdef.type_numel = %u\n", eventdef->type_numel );
|
||||
fprintf(stderr, "eventdef.value_type = %u\n", eventdef->value_type );
|
||||
fprintf(stderr, "eventdef.value_numel = %u\n", eventdef->value_numel );
|
||||
fprintf(stderr, "eventdef.sample = %d\n", eventdef->sample );
|
||||
fprintf(stderr, "eventdef.offset = %d\n", eventdef->offset );
|
||||
fprintf(stderr, "eventdef.duration = %d\n", eventdef->duration );
|
||||
fprintf(stderr, "eventdef.bufsize = %u\n", eventdef->bufsize );
|
||||
}
|
||||
}
|
||||
|
||||
void print_datasel(datasel_t *datasel) {
|
||||
fprintf(stderr, "datasel.begsample = %u\n", datasel->begsample);
|
||||
fprintf(stderr, "datasel.endsample = %u\n", datasel->endsample);
|
||||
}
|
||||
|
||||
void print_eventsel(eventsel_t *eventsel) {
|
||||
fprintf(stderr, "eventsel.begevent = %u\n", eventsel->begevent);
|
||||
fprintf(stderr, "eventsel.endevent = %u\n", eventsel->endevent);
|
||||
}
|
||||
|
||||
void print_buf(void *buf, int bufsize) {
|
||||
int i;
|
||||
fprintf(stderr, "buf =");
|
||||
if (buf == nullptr)
|
||||
fprintf(stderr, " NULL");
|
||||
else
|
||||
for (i=0; i<bufsize; ++i)
|
||||
fprintf(stderr, " %02x", ((unsigned char*)buf)[i]);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2010 S. Klanke
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __rdadefs_h
|
||||
#define __rdadefs_h
|
||||
|
||||
#include "message.h" /* for the integer datytypes */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Message types as sent to RDA clients */
|
||||
#define RDA_START_MSG 1
|
||||
#define RDA_INT_MSG 2
|
||||
#define RDA_STOP_MSG 3
|
||||
#define RDA_FLOAT_MSG 4
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
/** Structure of the first 24 bytes of all RDA messages */
|
||||
typedef struct {
|
||||
UINT8_T guid[16];
|
||||
UINT32_T nSize; /* Size of the message block in bytes including this header */
|
||||
UINT32_T nType; /* 1:start 2:int16_t 3:stop 4:float */
|
||||
} rda_msg_hdr_t;
|
||||
|
||||
/** Describes the structure of RDA data messages (fixed part only) */
|
||||
typedef struct {
|
||||
rda_msg_hdr_t hdr;
|
||||
UINT32_T nBlock; /* Block number, i.e. acquired blocks since acquisition started. */
|
||||
UINT32_T nPoints; /* Number of data points (samples) in this block */
|
||||
UINT32_T nMarkers; /* Number of markers in this block */
|
||||
/* after this, you get the data, and then an array of markers */
|
||||
} rda_msg_data_t;
|
||||
|
||||
/** Describes the structure of an RDA start message (fixed part only) */
|
||||
typedef struct {
|
||||
rda_msg_hdr_t hdr;
|
||||
UINT32_T nChannels; /* Number of channels */
|
||||
double dSamplingInterval; /* Sampling interval in microseconds */
|
||||
/* after this, you have double dResolutions[] and
|
||||
channels names come after this as 0-terminated strings */
|
||||
} rda_msg_start_t;
|
||||
|
||||
/* TODO: we never send a STOP packet, because it's hard to tell when the buffer will
|
||||
stop receiving data. We should maybe try to detect if a new header is put into the
|
||||
buffer, and send a STOP and a START in that case.
|
||||
*/
|
||||
|
||||
/** Describes the structure of an RDA marker (fixed part only) */
|
||||
typedef struct {
|
||||
UINT32_T nSize; /* Size of this marker */
|
||||
UINT32_T nPosition; /* Relative position in the data block */
|
||||
UINT32_T nPoints; /* Number of points of this marker */
|
||||
INT32_T nChannel; /* Associated channel number (-1 = all channels) */
|
||||
/* char sTypeDesc[1]; Type description in ASCII delimited by '\0', variable length actually */
|
||||
} rda_marker_t;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __rdadefs_h */
|
||||
+1111
File diff suppressed because it is too large
Load Diff
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2010 S. Klanke
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __rdaserver_h
|
||||
#define __rdaserver_h
|
||||
|
||||
#include <pthread.h>
|
||||
#include "buffer.h"
|
||||
#include "rdadefs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* On Windows, sockets are not described by a plain int, but by the type SOCKET, which has
|
||||
the size of a pointer. On WIN64, sizeof(SOCKET) != sizeof(int), although some people still
|
||||
argue that it is safe to cast between those. To be sure, the RDA server implementation
|
||||
always uses SOCKET as the base type, and defines this as an 'int' on POSIX systems.
|
||||
*/
|
||||
#ifndef WIN32
|
||||
typedef int SOCKET;
|
||||
#define INVALID_SOCKET -1
|
||||
#endif
|
||||
|
||||
/** Error values as returned by rda_start_server */
|
||||
#define FT_NO_ERROR 0
|
||||
#define FT_ERR_OUT_OF_MEM 1
|
||||
#define FT_ERR_SOCKET 2
|
||||
#define FT_ERR_THREADING 3
|
||||
|
||||
/** 'select' cannot handle more than 64 elements on Windows, but this
|
||||
should really be enough for all practical purposes. Depending on
|
||||
the sampling rate and number of channels, you would probably hit
|
||||
other performance boundaries first. Since the server socket itself
|
||||
also needs listening to (taking 1 away from the available 64),
|
||||
NEVER set the following number to more than 63!!!
|
||||
*/
|
||||
#define RDA_MAX_NUM_CLIENTS 32
|
||||
|
||||
/** Number of blocks any client can lag behind before being disconnected */
|
||||
#define RDA_MAX_LAG 5
|
||||
|
||||
/** RDA server control structure for starting, inspecting, and stopping a server */
|
||||
typedef struct {
|
||||
pthread_t thread; ///< Thread handle
|
||||
pthread_mutex_t mutex; ///< Mutex for protecting num_clients (actually not really necessary)
|
||||
SOCKET server_socket; ///< The server socket that clients connect to
|
||||
int ft_buffer; ///< Connection to FieldTrip buffer (socket or 0 for dmarequests)
|
||||
volatile int num_clients; ///< Current number of clients
|
||||
volatile int should_exit; ///< Flag to notify the server thread that it should stop
|
||||
volatile int is_running; ///< Flag that indicates whether the thread is still running
|
||||
int blocksize; ///< Block size for streaming out samples, 0 => adapt to incoming data
|
||||
int use16bit; ///< Flag that indicates whether 16 bit data should be streamed
|
||||
int verbosity; ///< Option that determines how much status information is printed during operation
|
||||
} rda_server_ctrl_t;
|
||||
|
||||
/** Internally used data structure to keep a linked list of
|
||||
data packets that need to be sent out */
|
||||
typedef struct rda_buffer_item {
|
||||
void *data; ///< Points to complete RDA packet
|
||||
size_t size; ///< Size of the packet (=allocated memory block)
|
||||
int blockNumber; ///< Number of this data block (or -1 for start packet)
|
||||
unsigned int refCount; ///< Reference count (multiple clients get the same data)
|
||||
struct rda_buffer_item *next; ///< Next item in list or NULL
|
||||
} rda_buffer_item_t;
|
||||
|
||||
/** Internally used data structure to describe a client and its pending jobs */
|
||||
typedef struct {
|
||||
SOCKET sock; ///< Client socket
|
||||
rda_buffer_item_t *item; ///< Points to the current/next packet to be written
|
||||
size_t written; ///< Number of bytes that have been written (from item->data)
|
||||
} rda_client_job_t;
|
||||
|
||||
/** Helper function for converting any FieldTrip data type to single precision floats
|
||||
@param N number of values to convert
|
||||
@param dest destination, must point to an array of at least N floats
|
||||
@param data_type data type as described by FieldTrip DATATYPE_** constants
|
||||
@param src source buffer
|
||||
*/
|
||||
void rda_aux_convert_to_float(UINT32_T N, void *dest, UINT32_T data_type, const void *src);
|
||||
|
||||
/** Starts an RDA server with a given FieldTrip connection (usually 0 for DMA), serving
|
||||
either single precision or 16 bit integer data.
|
||||
@param ft_buffer FieldTrip connection (0 for DMA, or socket for TCP connection)
|
||||
@param use16bit pass 0 to serve single precision data (with conversion as necessary)
|
||||
pass non-zero to serve 16 bit integers (only works if the FieldTrip buffer
|
||||
also contains 16 bit data)
|
||||
@param port Port number to bind to, or 0 for default port (51244 for 16 bit, 51344 for single precision)
|
||||
@param blocksize Block size for streaming out samples (0=send out variable blocks depending on incoming data)
|
||||
@param errval Optional pointer to an integer error value. Will contain either
|
||||
FT_NO_ERROR, FT_ERR_SOCKET, FT_OUT_OF_MEM or FT_THREADING on exit.
|
||||
@return Pointer to RDA server control structure, or NULL if an error occurred
|
||||
*/
|
||||
rda_server_ctrl_t *rda_start_server(int ft_buffer, int use16bit, int port, int blocksize, int *errval);
|
||||
|
||||
/** Stops an RDA server by closing the associated connections, stopping the background thread, and
|
||||
deallocating its memory (including the control structure pointed to by the argument)
|
||||
@param SC Must point to an RDA server control structure as created by rda_start_server
|
||||
@return -1 if SC is NULL
|
||||
0 on success
|
||||
*/
|
||||
int rda_stop_server(rda_server_ctrl_t *SC);
|
||||
|
||||
/** Simple helper function for retrieving the current number of clients of an RDA server
|
||||
@param SC Must point to an RDA server control structure as created by rda_start_server
|
||||
@return The number of clients
|
||||
*/
|
||||
int rda_get_num_clients(rda_server_ctrl_t *SC);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __rdaserver_h */
|
||||
+474
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* Copyright (C) 2010, Stefan Klanke
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <socketserver.h>
|
||||
|
||||
/************************************************************************
|
||||
* This function deals with the incoming client requests in a loop until
|
||||
* the user requests to stop the server, or until the remote side closes
|
||||
* the connection. The implementation follows the idea of a state machine
|
||||
* with the four different states:
|
||||
* state = 0 means we are waiting for a request to come in, or we are
|
||||
* in the process of reading the first 8 bytes (the "def" part)
|
||||
* state = 1 means we are in the process of reading the remainder of
|
||||
* the request (the "buf" part")
|
||||
* state = 2 means we are in the process of writing the response (def)
|
||||
* state = 3 means ... writing the 2nd. part of the response ("buf")
|
||||
*
|
||||
* On top of those 4 states, we maintain two variables "bytesDone" and
|
||||
* "bytesTotal" that determine how many bytes we've read/written within
|
||||
* the current state, and how many bytes we need to process in total,
|
||||
* and a variable "curPtr" which points to the memory region we currently
|
||||
* need to read into, or write from. Whether any action is actually taken
|
||||
* inside the while loop also depends on the state of the socket which
|
||||
* we find out using "select" (and then set "canRead" + "canWrite" flags).
|
||||
*
|
||||
* Depending on the nature of the request, we might skip states 1 and 3.
|
||||
* This is the case if there is no "buf" attached to the message, or if
|
||||
* an outgoing message can be merged in to a single packet.
|
||||
*
|
||||
* The actual processing of the message happens before moving to state 2
|
||||
* and consists of
|
||||
* 1) possibly swapping the message to native endianness
|
||||
* 2) calling dmarequest or the user-supplied callback function
|
||||
* 3) possibly swapping back to remote endianness
|
||||
************************************************************************/
|
||||
void *_buffer_socket_func(void *arg) {
|
||||
SOCKET sock;
|
||||
ft_buffer_server_t *SC;
|
||||
int mergePackets;
|
||||
messagedef_t reqdef;
|
||||
message_t request;
|
||||
message_t *response = nullptr;
|
||||
int state=0; /* 0 = reading def, 1=reading buf, 2=writing def, 3=writing buf */
|
||||
int bytesDone, bytesTotal;
|
||||
char mergeBuffer[MERGE_THRESHOLD];
|
||||
char *curPtr; /* points at buffer that needs to be filled or written out */
|
||||
int swap = 0;
|
||||
int canRead, canWrite;
|
||||
UINT16_T reqCommand;
|
||||
UINT32_T respBufSize;
|
||||
fd_set readSet, writeSet;
|
||||
|
||||
if (arg == nullptr) return NULL;
|
||||
|
||||
/* copy over necessary variables and free the given structure */
|
||||
SC = ((ft_buffer_socket_t *) arg)->server;
|
||||
sock = ((ft_buffer_socket_t *) arg)->clientSocket;
|
||||
mergePackets = ((ft_buffer_socket_t *) arg)->mergePackets;
|
||||
free(arg);
|
||||
|
||||
if (SC->verbosity > 0) {
|
||||
printf("Started new client thread with packet merging = %i\n", mergePackets);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&SC->lock);
|
||||
SC->numClients++;
|
||||
pthread_mutex_unlock(&SC->lock);
|
||||
|
||||
request.def = &reqdef;
|
||||
request.buf = NULL;
|
||||
bytesDone = 0;
|
||||
bytesTotal = sizeof(messagedef_t);
|
||||
curPtr = (char *) request.def;
|
||||
|
||||
while (SC->keepRunning) {
|
||||
int sel, res, n;
|
||||
struct timeval tv = {0, 10000}; /* 10ms */
|
||||
|
||||
FD_ZERO(&readSet);
|
||||
FD_ZERO(&writeSet);
|
||||
if (state < 2) {
|
||||
FD_SET(sock, &readSet);
|
||||
} else {
|
||||
FD_SET(sock, &writeSet);
|
||||
}
|
||||
sel = select((int) sock+1, &readSet, &writeSet, nullptr, &tv);
|
||||
if (sel == 0) continue;
|
||||
if (sel < 0) {
|
||||
fprintf(stderr, "Error in 'select' operation - closing client connection.\n");
|
||||
break;
|
||||
}
|
||||
canRead = FD_ISSET(sock, &readSet);
|
||||
canWrite = FD_ISSET(sock, &writeSet);
|
||||
|
||||
if (canRead) {
|
||||
n = recv(sock, curPtr + bytesDone, bytesTotal - bytesDone, 0);
|
||||
if (n<=0) {
|
||||
/* socket was closed */
|
||||
if (SC->verbosity>0) {
|
||||
std::cout << "Remote side closed client connection\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
bytesDone+=n;
|
||||
if (bytesDone<bytesTotal) continue;
|
||||
|
||||
if (state == 0) {
|
||||
/* we've read the request.def completely */
|
||||
if (reqdef.version==VERSION_OE) {
|
||||
swap = 1;
|
||||
ft_swap16(2, &reqdef.version); /* version + command */
|
||||
ft_swap32(1, &reqdef.bufsize);
|
||||
reqCommand = reqdef.command;
|
||||
}
|
||||
if (reqdef.version!=VERSION) {
|
||||
fprintf(stderr,"Incorrect version requested - closing socket.\n");
|
||||
break;
|
||||
}
|
||||
if (reqdef.bufsize > 0) {
|
||||
request.buf = malloc(reqdef.bufsize);
|
||||
if (request.buf == nullptr) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
break;
|
||||
}
|
||||
curPtr = request.buf;
|
||||
bytesDone = 0;
|
||||
bytesTotal = reqdef.bufsize;
|
||||
state = 1;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
/* Reaching this point means that the state=1, and that we've
|
||||
read request.buf completely, so swap the endianness if
|
||||
necessary, and then move on to handling the request.
|
||||
*/
|
||||
if (swap) ft_swap_buf_to_native(reqCommand, reqdef.bufsize, request.buf);
|
||||
}
|
||||
|
||||
/* Request has been read completely, now deal with it */
|
||||
if (SC->callback != nullptr) {
|
||||
/* User supplied a callback function in ft_start_buffer_server */
|
||||
res = SC->callback(&request, &response, SC->user_data);
|
||||
if (res != 0 || response == nullptr || response->def == nullptr) {
|
||||
fprintf(stderr, "buffer_socket_func: an unexpected error occurred in user-defined request handler\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* No callback, use normal dmarequest */
|
||||
res = dmarequest(&request, &response);
|
||||
if (res != 0 || response == nullptr || response->def == nullptr) {
|
||||
fprintf(stderr, "buffer_socket_func: an unexpected error occurred in dmarequest\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ok, the request has been handled, results are in response.
|
||||
We can free the memory pointed to by request.buf ...
|
||||
*/
|
||||
if (request.buf != nullptr) {
|
||||
free(request.buf);
|
||||
request.buf = NULL;
|
||||
}
|
||||
|
||||
/* ... swap the response to the remote endianness, if necessary ... */
|
||||
respBufSize = response->def->bufsize;
|
||||
if (swap) ft_swap_from_native(reqCommand, response);
|
||||
|
||||
/* ... and then start writing back the response. To reduce latency,
|
||||
we try to merge response->def and response->buf if they are small,
|
||||
so we can send it in one go over TCP. To fit the merged packet into
|
||||
our state machine logic, we apply a trick and jump to state=3 directly,
|
||||
where "curPtr" points to the merged packet.
|
||||
Otherwise, we move to state=2, transmit response->def, move to state=3,
|
||||
and there transmit response->buf.
|
||||
*/
|
||||
if (mergePackets && respBufSize > 0 && respBufSize + sizeof(messagedef_t) <= MERGE_THRESHOLD) {
|
||||
memcpy(mergeBuffer, response->def, sizeof(messagedef_t));
|
||||
memcpy(mergeBuffer + sizeof(messagedef_t), response->buf, respBufSize);
|
||||
|
||||
curPtr = mergeBuffer;
|
||||
bytesDone = 0;
|
||||
bytesTotal = respBufSize + sizeof(messagedef_t);
|
||||
state = 3;
|
||||
} else {
|
||||
curPtr = (char *) response->def;
|
||||
bytesDone = 0;
|
||||
bytesTotal = sizeof(messagedef_t);
|
||||
state = 2;
|
||||
}
|
||||
canWrite = 1;
|
||||
}
|
||||
|
||||
if (state >= 2 && canWrite) {
|
||||
n = send(sock, curPtr + bytesDone, bytesTotal - bytesDone, 0);
|
||||
if (n<=0) {
|
||||
/* socket was closed */
|
||||
fprintf(stderr, "Cannot write to socket -- closing client connection.\n");
|
||||
break;
|
||||
}
|
||||
bytesDone+=n;
|
||||
if (bytesDone < bytesTotal) continue;
|
||||
if (state==2 && respBufSize > 0) {
|
||||
curPtr = (char *) response->buf;
|
||||
bytesDone = 0;
|
||||
bytesTotal = respBufSize;
|
||||
state = 3;
|
||||
continue;
|
||||
}
|
||||
/* Reaching this point means we are done with writing out the response,
|
||||
so we will now free the allocated memory, and reset to state=0.
|
||||
*/
|
||||
if (response->buf) free(response->buf);
|
||||
free(response->def);
|
||||
free(response);
|
||||
response = NULL;
|
||||
state = 0;
|
||||
curPtr = (char *) request.def;
|
||||
bytesDone = 0;
|
||||
bytesTotal = sizeof(messagedef_t);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&SC->lock);
|
||||
SC->numClients--;
|
||||
pthread_mutex_unlock(&SC->lock);
|
||||
|
||||
closesocket(sock);
|
||||
if (request.buf != nullptr) free(request.buf);
|
||||
if (response != nullptr) {
|
||||
if (response->buf != nullptr) free(response->buf);
|
||||
if (response->def != nullptr) free(response->def);
|
||||
free(response);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* this thread listens to incoming TCP/UNIX domain socket connections
|
||||
* if a connection is made by a client, it starts _buffer_socket_func
|
||||
***********************************************************************/
|
||||
void *_buffer_server_func(void *arg) {
|
||||
ft_buffer_server_t *SC = (ft_buffer_server_t *) arg;
|
||||
int n;
|
||||
|
||||
if (SC == nullptr) {
|
||||
fprintf(stderr, "FieldTrip buffer server thread started with invalid argument\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
while (SC->keepRunning) {
|
||||
SOCKET c;
|
||||
fd_set readSet;
|
||||
int sel, rc, merge;
|
||||
pthread_t tid;
|
||||
ft_buffer_socket_t *CC;
|
||||
struct timeval tv = {0,10000}; /* 10 ms for select timeout */
|
||||
|
||||
FD_ZERO(&readSet);
|
||||
FD_SET(SC->serverSocket, &readSet);
|
||||
|
||||
sel = select((int) SC->serverSocket + 1, &readSet, nullptr, nullptr, &tv);
|
||||
|
||||
if (sel == 0) continue;
|
||||
|
||||
/* The following code portion looks weird because of the preprocessor
|
||||
defines splitting an if/else clause, but it's just what we want:
|
||||
On Windows, there is no if/else clause, and the second (TCP) bit
|
||||
is always called. On POSIX systems, we branch depending on whether
|
||||
the server runs a local domain socket or TCP socket.
|
||||
*/
|
||||
#ifndef WIN32
|
||||
if (SC->isUnixDomain) {
|
||||
struct sockaddr_un sa;
|
||||
socklen_t size_sa = sizeof(sa);
|
||||
|
||||
c = accept(SC->serverSocket, (struct sockaddr *)&sa, &size_sa);
|
||||
|
||||
if (c == INVALID_SOCKET) {
|
||||
perror("buffer_server, accept");
|
||||
continue;
|
||||
}
|
||||
/* never merge packets for (local) UNIX sockets */
|
||||
merge = 0;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
socklen_t size_sa = sizeof(sa);
|
||||
|
||||
c = accept(SC->serverSocket, (struct sockaddr *)&sa, &size_sa);
|
||||
|
||||
if (c == INVALID_SOCKET) {
|
||||
perror("buffer_server, accept");
|
||||
continue;
|
||||
}
|
||||
/* enable packet merging only if it's not localhost */
|
||||
merge = (sa.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) ? 0 : 1;
|
||||
}
|
||||
|
||||
CC = (ft_buffer_socket_t *) malloc(sizeof(ft_buffer_socket_t));
|
||||
if (CC == nullptr) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
closesocket(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
CC->server = SC;
|
||||
CC->clientSocket = c;
|
||||
CC->mergePackets = merge;
|
||||
|
||||
rc = pthread_create(&tid, nullptr, _buffer_socket_func, CC);
|
||||
if (rc) {
|
||||
fprintf(stderr, "tcpserver: return code from pthread_create() is %d\n", rc);
|
||||
closesocket(c);
|
||||
free(CC);
|
||||
}
|
||||
}
|
||||
while ((n=SC->numClients)>0) {
|
||||
printf("Waiting for %i remaining client threads to stop...\n", n);
|
||||
usleep(10000);
|
||||
}
|
||||
pthread_mutex_lock(&SC->lock);
|
||||
SC->numClients--;
|
||||
pthread_mutex_unlock(&SC->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
ft_buffer_server_t *ft_start_buffer_server(int port, const char *name, ft_request_callback_t callback, void *user_data) {
|
||||
ft_buffer_server_t *SC;
|
||||
int optval;
|
||||
SOCKET s = INVALID_SOCKET;
|
||||
|
||||
SC = (ft_buffer_server_t *) malloc(sizeof(ft_buffer_server_t));
|
||||
if (SC == nullptr) return NULL;
|
||||
|
||||
SC->callback = callback;
|
||||
SC->user_data = user_data;
|
||||
|
||||
#ifdef WIN32
|
||||
{
|
||||
/* We only need to do this once ... and actually have a corresponding WSACleanup call somewhere */
|
||||
static WSADATA wsa = {0,0};
|
||||
if (wsa.wVersion == 0) {
|
||||
if(WSAStartup(MAKEWORD(1, 1), &wsa)) {
|
||||
fprintf(stderr, "ft_start_buffer_server: cannot start WIN32 sockets.\n");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* setup socket */
|
||||
if (port == 0) {
|
||||
#ifdef WIN32
|
||||
fprintf(stderr, "ft_start_buffer_server: invalid port number given.\n");
|
||||
goto cleanup;
|
||||
#else
|
||||
struct sockaddr_un sa;
|
||||
/* UNIX domain socket */
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET) {
|
||||
perror("ft_start_buffer_server, socket");
|
||||
goto cleanup;
|
||||
}
|
||||
sa.sun_family = AF_UNIX;
|
||||
strncpy(sa.sun_path, name, sizeof(sa.sun_path));
|
||||
if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
|
||||
perror("ft_start_buffer_server, bind");
|
||||
goto cleanup;
|
||||
}
|
||||
SC->isUnixDomain = 0;
|
||||
#endif
|
||||
} else {
|
||||
/* TCP socket */
|
||||
struct sockaddr_in sa;
|
||||
|
||||
s = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET) {
|
||||
perror("ft_start_buffer_server, socket");
|
||||
goto cleanup;
|
||||
}
|
||||
/* prevend "bind: address already in use" */
|
||||
optval = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&optval, sizeof(optval)) < 0) {
|
||||
perror("ft_start_buffer_server, setsockopt");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
|
||||
perror("ft_start_buffer_server, bind");
|
||||
goto cleanup;
|
||||
}
|
||||
SC->isUnixDomain = 0;
|
||||
}
|
||||
|
||||
/* place the socket in non-blocking mode, required to do thread cancelation */
|
||||
#ifdef WIN32
|
||||
{
|
||||
unsigned long enable = 0;
|
||||
ioctlsocket(s, FIONBIO, &enable);
|
||||
}
|
||||
#else
|
||||
optval = fcntl(s, F_GETFL, nullptr);
|
||||
optval = optval | O_NONBLOCK;
|
||||
if (fcntl(s, F_SETFL, optval)<0) {
|
||||
perror("ft_start_buffer_server, fcntl");
|
||||
goto cleanup;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (listen(s, BACKLOG)<0) {
|
||||
perror("ft_start_buffer_server, listen");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* set some control variables */
|
||||
SC->numClients = 0;
|
||||
SC->keepRunning = 1;
|
||||
SC->serverSocket = s;
|
||||
SC->verbosity = 10; /* TODO: specify proper values */
|
||||
|
||||
/* create the mutex */
|
||||
if (pthread_mutex_init(&SC->lock, nullptr) != 0) {
|
||||
fprintf(stderr,"start_tcp_server: mutex could not be initialised\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* create thread with default attributes */
|
||||
if (pthread_create(&SC->threadID, nullptr, _buffer_server_func, SC) == 0) {
|
||||
/* everything went fine - thread should be running now */
|
||||
return SC;
|
||||
}
|
||||
|
||||
fprintf(stderr,"start_tcp_server: could not spawn thread\n");
|
||||
pthread_mutex_destroy(&SC->lock);
|
||||
|
||||
cleanup:
|
||||
if (SC != nullptr) free(SC);
|
||||
if (s != INVALID_SOCKET) {
|
||||
#ifdef WIN32
|
||||
shutdown(s, SD_BOTH);
|
||||
#else
|
||||
shutdown(s, SHUT_RDWR);
|
||||
#endif
|
||||
closesocket(s);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ft_stop_buffer_server(ft_buffer_server_t *S) {
|
||||
if (S == nullptr) return;
|
||||
|
||||
S->keepRunning = 0;
|
||||
pthread_join(S->threadID, nullptr);
|
||||
pthread_detach(S->threadID);
|
||||
free(S);
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2010, Stefan Klanke
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*/
|
||||
#ifndef __socketserver_h
|
||||
#define __socketserver_h
|
||||
|
||||
#include "buffer.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#define MERGE_THRESHOLD 4096 /* TODO: optimize this value? Maybe look at MTU size */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef WIN32
|
||||
#define INVALID_SOCKET -1
|
||||
typedef int SOCKET;
|
||||
#include <sys/un.h>
|
||||
#else
|
||||
typedef int socklen_t;
|
||||
#endif
|
||||
|
||||
typedef int (*ft_request_callback_t)(const message_t *request, message_t **response, void *user_data);
|
||||
|
||||
/** The following structure is used for managing a server. The structure is
|
||||
allocated and filled in ft_start_buffer_server and then passed on to the
|
||||
actual server thread, as well as to all threads handling the client
|
||||
connections. Thread functions are written such that they monitor the
|
||||
"keepRunning" member of this structure, and once this is set to 0, the
|
||||
threads stop and exit.
|
||||
*/
|
||||
typedef struct {
|
||||
SOCKET serverSocket; ///< Socket the server listens on (TCP or UNIX domain)
|
||||
int keepRunning; ///< Flag=1: thread functions keep looping, 0: threads exit
|
||||
int numClients; ///< Current number of clients connected to the server
|
||||
int verbosity; ///< Determines how much information is being printed during operation
|
||||
int isUnixDomain; ///< 1: UNIX domain socket, 0: TCP socket
|
||||
pthread_t threadID; ///< POSIX thread identifier of the server thread, client threads are detached immediately
|
||||
pthread_mutex_t lock; ///< Mutex to protect the "numClients" member, commonly used by all threads
|
||||
ft_request_callback_t callback; ///< Callback function to be called *instead* of dmarequest
|
||||
void *user_data; ///< Pointer to user-defined data structure, passed on to callback
|
||||
} ft_buffer_server_t;
|
||||
|
||||
/** Small helper structure that is passed to client threads. Get's allocated
|
||||
using malloc() in the server thread, and disposed using free() in the client
|
||||
thread.
|
||||
*/
|
||||
typedef struct {
|
||||
ft_buffer_server_t *server; ///< Pointer to the common control structure
|
||||
SOCKET clientSocket; ///< The newly created socket (from "accept")
|
||||
int mergePackets; ///< 1: merge packets if total size below threshold, 0: never merge (=>local host)
|
||||
} ft_buffer_socket_t;
|
||||
|
||||
/** Creates a server socket, binds it to the specified UNIX domain name or port,
|
||||
starts listening on this socket, and spawns a background thread to serve
|
||||
requests on this socket.
|
||||
TCP sockets are created for positive port numbers (name is ignored),
|
||||
UNIX domain sockets are created for port=0 (name needs to be a UNIX pathname).
|
||||
|
||||
If callback != nullptr, that function is called like
|
||||
callback(request, &response, user_data)
|
||||
for every request coming in over the socket, *instead* of the normal dmarequest.
|
||||
Usually the user will call the latter function internally. Make sure your
|
||||
callback is re-entrant!!!
|
||||
|
||||
Returns allocated control structure, or NULL in case of errors.
|
||||
*/
|
||||
ft_buffer_server_t *ft_start_buffer_server(int port, const char *name, ft_request_callback_t callback, void *user_data);
|
||||
|
||||
/** Stops background thread(s), closes the sockets, and disposes the control structure S.
|
||||
S cannot be used anymore after this call.
|
||||
*/
|
||||
void ft_stop_buffer_server(ft_buffer_server_t *S);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#define MERGE_THRESHOLD 4096 /* TODO: optimize this value? Maybe look at MTU size */
|
||||
|
||||
/*******************************************************************************
|
||||
* communicate with the buffer through TCP
|
||||
*******************************************************************************/
|
||||
int tcprequest(int server, const message_t *request, message_t **response_ptr) {
|
||||
unsigned int n, total;
|
||||
|
||||
/* this will hold the response */
|
||||
message_t *response;
|
||||
response = (message_t*)malloc(sizeof(message_t));
|
||||
response->def = (messagedef_t*)malloc(sizeof(messagedef_t));
|
||||
response->buf = NULL;
|
||||
/* the response should be passed to the calling function, where it should be freed */
|
||||
*response_ptr = response;
|
||||
|
||||
total = sizeof(messagedef_t) + request->def->bufsize;
|
||||
|
||||
/* Check whether request->def and request->buf are already contiguous in memory,
|
||||
or whether request->buf is empty. If that's the case, we can write the request in one go.
|
||||
*/
|
||||
if (request->def->bufsize == 0 || (request->def+1) == (messagedef_t *) request->buf) {
|
||||
if ((n = bufwrite(server, request->def, total)) != total) {
|
||||
fprintf(stderr, "write size = %d, should be %d\n", n, total);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
/* Now check whether the total size is below the merge threshold, in which case
|
||||
we'll copy it to contiguous memory and again send it in one go
|
||||
*/
|
||||
else if (total <= MERGE_THRESHOLD) {
|
||||
char merged[MERGE_THRESHOLD];
|
||||
|
||||
memcpy(merged, request->def, sizeof(messagedef_t));
|
||||
memcpy(merged + sizeof(messagedef_t), request->buf, request->def->bufsize);
|
||||
|
||||
if ((n = bufwrite(server, merged, total)) != total) {
|
||||
fprintf(stderr, "write size = %d, should be %d\n", n, total);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
/* Otherwise, send "def" and "buf" in separate pieces. This might introduce latencies
|
||||
if the other end runs Windows :-(
|
||||
*/
|
||||
else {
|
||||
/* send the request to the server, first the message definition */
|
||||
if ((n = bufwrite(server, request->def, sizeof(messagedef_t)))!=sizeof(messagedef_t)) {
|
||||
fprintf(stderr, "write size = %d, should be %lu\n", n, (long unsigned int)sizeof(messagedef_t));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* send the request to the server, then the message payload */
|
||||
if ((n = bufwrite(server, request->buf, request->def->bufsize))!=request->def->bufsize) {
|
||||
fprintf(stderr, "write size = %d, should be %d\n", n, request->def->bufsize);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* read the response from the server, first the message definition */
|
||||
if ((n = bufread(server, response->def, sizeof(messagedef_t))) != sizeof(messagedef_t)) {
|
||||
fprintf(stderr, "packet size = %d, should be %lu\n", n, (long unsigned int)sizeof(messagedef_t));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (response->def->version!=VERSION) {
|
||||
fprintf(stderr, "incorrect version\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* read the response from the server, then the message payload */
|
||||
if (response->def->bufsize>0) {
|
||||
response->buf = malloc(response->def->bufsize);
|
||||
if ((n = bufread(server, response->buf, response->def->bufsize)) != response->def->bufsize) {
|
||||
fprintf(stderr, "read size = %d, should be %d\n", n, response->def->bufsize);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* everything went fine, return with the response */
|
||||
/* print_response(response->def); */
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
/* there was a problem, clear the response and return */
|
||||
FREE(response->def);
|
||||
FREE(response->buf);
|
||||
FREE(response);
|
||||
*response_ptr = NULL; /* SK: this was missing a "*", effectively never really returning 0 */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
* This will contain the implementation of the TiA server on top of the FieldTrip buffer, similar to the RDA server.
|
||||
*
|
||||
*/
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
* This will contain the implementation of the TiA server on top of the FieldTrip buffer, similar to the RDA server.
|
||||
*
|
||||
*/
|
||||
+482
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld & Christian Hesse
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h> /* for strerror */
|
||||
|
||||
#include "buffer.h"
|
||||
#include <pthread.h>
|
||||
#include "extern.h"
|
||||
|
||||
unsigned int bufread(int s, void* buf, unsigned int numel)
|
||||
{
|
||||
unsigned int numcall = 0, numread = 0, verbose = 0;
|
||||
int numthis = 0;
|
||||
|
||||
while (numread < numel)
|
||||
{
|
||||
numthis = recv(s, (char*)buf + numread, numel - numread, 0);
|
||||
if (numthis < 0)
|
||||
{
|
||||
perror("bufread");
|
||||
break;
|
||||
}
|
||||
else if (numthis == 0) { break; }
|
||||
|
||||
if (verbose > 0) { fprintf(stderr, "bufread: read %d bytes\n", numthis); }
|
||||
numread += numthis;
|
||||
numcall++;
|
||||
#ifndef PLATFORM_WIN32 /* SK: I think this shouldn't be necessary on any platform: the sockets are blocking */
|
||||
if (numread < numel) { usleep(1000); }
|
||||
#endif
|
||||
}
|
||||
if (verbose > 1) { fprintf(stderr, "bufread: reading the complete buffer required %d calls\n", numcall); }
|
||||
return numread;
|
||||
}
|
||||
|
||||
unsigned int bufwrite(int s, const void* buf, unsigned int numel)
|
||||
{
|
||||
int numthis = 0;
|
||||
unsigned int numcall = 0, numwrite = 0, verbose = 0;
|
||||
|
||||
while (numwrite < numel)
|
||||
{
|
||||
numthis = send(s, (char*)buf + numwrite, numel - numwrite, 0);
|
||||
if (numthis < 0)
|
||||
{
|
||||
perror("bufwrite");
|
||||
break;
|
||||
}
|
||||
else if (numthis == 0) { break; }
|
||||
|
||||
if (verbose) fprintf(stderr, "bufwrite: wrote %d bytes\n", numthis);
|
||||
numwrite += numthis;
|
||||
numcall++;
|
||||
#ifndef PLATFORM_WIN32 /* SK: I think this shouldn't be necessary on any platform: the sockets are blocking */
|
||||
if (numwrite < numel) { usleep(1000); }
|
||||
#endif
|
||||
}
|
||||
if (verbose > 1) { fprintf(stderr, "bufwrite: writing the complete buffer required %d calls\n", numcall); }
|
||||
return numwrite;
|
||||
}
|
||||
|
||||
unsigned int append(void** buf1, unsigned int bufsize1, void* buf2, unsigned int bufsize2)
|
||||
{
|
||||
int verbose = 0;
|
||||
|
||||
if (verbose > 1)
|
||||
{
|
||||
pthread_mutex_lock(&mutexappendcount);
|
||||
appendcount++;
|
||||
fprintf(stderr, "append: appendcount = %d\n", appendcount);
|
||||
pthread_mutex_unlock(&mutexappendcount);
|
||||
}
|
||||
|
||||
if (((*buf1) != nullptr) && (bufsize1 == 0))
|
||||
{
|
||||
perror("append err1");
|
||||
return 0; /* was -1, but this is never checked anyway */
|
||||
}
|
||||
else if (((*buf1) == nullptr) && (bufsize1 != 0))
|
||||
{
|
||||
perror("append err2");
|
||||
return 0; /* was -1, but this is never checked anyway */
|
||||
}
|
||||
|
||||
if ((*buf1) == nullptr)
|
||||
{
|
||||
if (verbose > 0) { fprintf(stderr, "append: allocating %d bytes\n", bufsize2); }
|
||||
(*buf1) = malloc(bufsize2);
|
||||
}
|
||||
else if ((*buf1) != nullptr)
|
||||
{
|
||||
if (verbose > 0) { fprintf(stderr, "append: reallocating from %d to %d bytes\n", bufsize1, bufsize1 + bufsize2); }
|
||||
(*buf1) = realloc((*buf1), bufsize1 + bufsize2);
|
||||
}
|
||||
|
||||
memcpy((char*)(*buf1) + bufsize1, buf2, bufsize2);
|
||||
return (bufsize1 + bufsize2);
|
||||
}
|
||||
|
||||
int close_connection(int s)
|
||||
{
|
||||
int status = 0, verbose = 0;
|
||||
if (verbose > 0) { fprintf(stderr, "close_connection: socket = %d\n", s); }
|
||||
if (s > 0) status = closesocket(s); /* it is a TCP connection */
|
||||
if (status != 0) { perror("close_connection"); }
|
||||
return status;
|
||||
}
|
||||
|
||||
int open_connection(const char* hostname, int port)
|
||||
{
|
||||
int verbose = 0;
|
||||
int s, retry;
|
||||
struct sockaddr_in sa;
|
||||
struct hostent* host;
|
||||
#ifdef WIN32
|
||||
static WSADATA wsa = { 0, 0 }; /* check version fields to only initialise once */
|
||||
#endif
|
||||
|
||||
if (port == 0)
|
||||
{
|
||||
if (verbose > 0) { fprintf(stderr, "open_connection: using direct memory copy\n"); }
|
||||
return 0;
|
||||
}
|
||||
else { if (verbose > 0) { fprintf(stderr, "open_connection: server = %s, port = %d\n", hostname, port); } }
|
||||
|
||||
#ifdef WIN32
|
||||
if (wsa.wVersion == 0)
|
||||
{
|
||||
/* We only need to do this once ... and actually have a corresponding WSACleanup call somewhere */
|
||||
if (WSAStartup(MAKEWORD(1, 1), &wsa))
|
||||
{
|
||||
fprintf(stderr, "open_connection: cannot start sockets\n");
|
||||
/* FIXME should this exception be handled more explicitely? */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((host = gethostbyname(hostname)) == nullptr)
|
||||
{
|
||||
fprintf(stderr, "open_connection: nslookup1 failed on '%s'\n", hostname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (host->h_length == 0)
|
||||
{
|
||||
fprintf(stderr, "open_connection: nslookup2 failed on '%s'\n", hostname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
|
||||
memcpy(&(sa.sin_addr.s_addr), host->h_addr_list[0], sizeof(sa.sin_addr.s_addr));
|
||||
|
||||
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
if (verbose > 0) { fprintf(stderr, "open_connection: socket = %d\n", s); }
|
||||
perror("open_connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
retry = 10;
|
||||
while (retry > 0)
|
||||
{
|
||||
if (connect(s, (struct sockaddr*)&sa, sizeof sa) < 0)
|
||||
{
|
||||
/* wait 5 miliseconds and try again */
|
||||
usleep(5000);
|
||||
retry--;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* this signals that the connection has been made */
|
||||
retry = -1;
|
||||
}
|
||||
}
|
||||
if (retry == 0)
|
||||
{
|
||||
/* close the socket */
|
||||
closesocket(s);
|
||||
/* it failed on mutliple attempts, give up */
|
||||
return -2;
|
||||
}
|
||||
|
||||
/*
|
||||
while (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
|
||||
perror("open_connection connect");
|
||||
usleep(1000000);
|
||||
}
|
||||
*/
|
||||
|
||||
if (verbose > 0) { fprintf(stderr, "open_connection: connected to %s:%d on socket %d\n", hostname, port, s); }
|
||||
|
||||
#ifdef DISABLE_NAGLE
|
||||
{
|
||||
int optval = 1;
|
||||
setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
||||
}
|
||||
#endif
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void check_datatypes()
|
||||
{
|
||||
/* check datatypes */
|
||||
if (WORDSIZE_CHAR != 1)
|
||||
{
|
||||
fprintf(stderr, "invalid size of CHAR (%d)\n", (int)WORDSIZE_CHAR);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_UINT8 != 1)
|
||||
{
|
||||
fprintf(stderr, "invalid size of UINT8 (%d)\n", (int)WORDSIZE_UINT8);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_UINT16 != 2)
|
||||
{
|
||||
fprintf(stderr, "invalid size of UINT16 (%d)\n", (int)WORDSIZE_UINT16);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_UINT32 != 4)
|
||||
{
|
||||
fprintf(stderr, "invalid size of UINT32 (%d)\n", (int)WORDSIZE_UINT32);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_UINT64 != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of UINT64 (%d)\n", (int)WORDSIZE_UINT64);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_INT8 != 1)
|
||||
{
|
||||
fprintf(stderr, "invalid size of INT8 (%d)\n", (int)WORDSIZE_INT8);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_INT16 != 2)
|
||||
{
|
||||
fprintf(stderr, "invalid size of INT16 (%d)\n", (int)WORDSIZE_INT16);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_INT32 != 4)
|
||||
{
|
||||
fprintf(stderr, "invalid size of INT32 (%d)\n", (int)WORDSIZE_INT32);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_INT64 != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of INT64 (%d)\n", (int)WORDSIZE_INT64);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_FLOAT32 != 4)
|
||||
{
|
||||
fprintf(stderr, "invalid size of FLOAT32 (%d)\n", (int)WORDSIZE_FLOAT32);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_FLOAT64 != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of FLOAT64 (%d)\n", (int)WORDSIZE_FLOAT64);
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(messagedef_t) != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of messagedef_t\n");
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(headerdef_t) != 24)
|
||||
{
|
||||
fprintf(stderr, "invalid size of headerdef_t \n");
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(datadef_t) != 16)
|
||||
{
|
||||
fprintf(stderr, "invalid size of datadef_t \n");
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(eventdef_t) != 32)
|
||||
{
|
||||
fprintf(stderr, "invalid size of eventdef_t \n");
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(datasel_t) != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of datasel_t \n");
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(eventsel_t) != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of eventsel_t \n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned int wordsize_from_type(UINT32_T data_type)
|
||||
{
|
||||
switch (data_type)
|
||||
{
|
||||
case DATATYPE_CHAR: return WORDSIZE_CHAR;
|
||||
case DATATYPE_UINT8:
|
||||
case DATATYPE_INT8: return WORDSIZE_INT8;
|
||||
case DATATYPE_UINT16:
|
||||
case DATATYPE_INT16: return WORDSIZE_INT16;
|
||||
case DATATYPE_UINT32:
|
||||
case DATATYPE_INT32: return WORDSIZE_INT32;
|
||||
case DATATYPE_UINT64:
|
||||
case DATATYPE_INT64: return WORDSIZE_INT64;
|
||||
case DATATYPE_FLOAT32: return WORDSIZE_FLOAT32;
|
||||
case DATATYPE_FLOAT64: return WORDSIZE_FLOAT64;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ft_chunk_t* find_chunk(const void* buf, unsigned int offset0, unsigned int size, UINT32_T chunk_type)
|
||||
{
|
||||
unsigned int bufpos = offset0;
|
||||
while (bufpos + sizeof(ft_chunkdef_t) <= size)
|
||||
{
|
||||
const ft_chunk_t* chunk = (ft_chunk_t*)((char*)buf + bufpos);
|
||||
if (chunk->def.type == chunk_type) { return chunk; }
|
||||
bufpos += sizeof(ft_chunkdef_t) + chunk->def.size;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/** Iterate through an array of events and check whether all of them are properly defined,
|
||||
that is, whether the "type" and "value" fields are of valid type and size, and whether the
|
||||
"bufsize" fields are correct (that is, fully contained in the passed buffer, and big enough
|
||||
to hold "type" and "value".
|
||||
Returns the number of events on success (might also be 0), or
|
||||
a negative number that indicates in which event definition an error happend,
|
||||
for example, a return value of -2 means that the first event was ok, but the second event
|
||||
definition was invalid. This function returns at the first error.
|
||||
*/
|
||||
int check_event_array(unsigned int size, const void* buf)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
int numEvents = 0;
|
||||
|
||||
while (offset + sizeof(eventdef_t) <= size)
|
||||
{
|
||||
const eventdef_t* E;
|
||||
unsigned int wsType, wsValue;
|
||||
|
||||
/* Set our event pointer to the current location within the array */
|
||||
E = (const eventdef_t*)((char*)buf + offset);
|
||||
|
||||
/* Increase the offset by the size of this event, and check whether it's fully
|
||||
contained within the given array.
|
||||
*/
|
||||
offset += sizeof(eventdef_t) + E->bufsize;
|
||||
if (offset > size) { goto error; }
|
||||
|
||||
/* Check whether "type" and "value" are of known type */
|
||||
wsType = wordsize_from_type(E->type_type);
|
||||
if (wsType == 0) { goto error; }
|
||||
wsValue = wordsize_from_type(E->value_type);
|
||||
if (wsValue == 0) { goto error; }
|
||||
|
||||
/* Check whether "type" and "value" are contained in this event's "buf" */
|
||||
if (wsType * E->type_numel + wsValue * E->value_numel > E->bufsize) { goto error; }
|
||||
|
||||
/* all checks passed, continue at next offset */
|
||||
++numEvents;
|
||||
}
|
||||
return numEvents;
|
||||
error:
|
||||
return -(1 + numEvents);
|
||||
}
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
int open_unix_connection(const char* name) { return -1; }
|
||||
#else
|
||||
int open_unix_connection(const char* name)
|
||||
{
|
||||
int verbose = 0;
|
||||
int s, retry;
|
||||
struct sockaddr_un sa;
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sun_family = AF_UNIX;
|
||||
strncpy(sa.sun_path, name, sizeof(sa.sun_path));
|
||||
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s < 0) {
|
||||
perror("open_unix_connection, socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
retry = 10;
|
||||
while (retry > 0) {
|
||||
if (connect(s, (struct sockaddr*) & sa, sizeof(sa)) < 0) {
|
||||
/* wait 5 miliseconds and try again */
|
||||
perror("open_connection");
|
||||
usleep(5000);
|
||||
retry--;
|
||||
}
|
||||
else {
|
||||
/* this signals that the connection has been made */
|
||||
retry = -1;
|
||||
}
|
||||
}
|
||||
if (retry == 0) {
|
||||
/* it failed on mutliple attempts, give up */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (verbose > 0)
|
||||
fprintf(stderr, "open_unix_connection: connected to %s on socket %d\n", name, s);
|
||||
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#ifndef COMPILER_MINGW
|
||||
|
||||
/*
|
||||
* timeval.h 1.0 01/12/19
|
||||
*
|
||||
* Defines gettimeofday, timeval, etc. for Win32
|
||||
*
|
||||
* By Wu Yongwei
|
||||
*
|
||||
*/
|
||||
|
||||
//#define EPOCHFILETIME (116444736000000000i64)
|
||||
#define EPOCHFILETIME ((INT64_T) 116444736000000000LL)
|
||||
|
||||
#ifdef COMPILER_LCC
|
||||
VOID STDCALL GetSystemTimeAsFileTime(LPFILETIME);
|
||||
#endif
|
||||
|
||||
int gettimeofday(struct timeval* tv, struct timezone* tz)
|
||||
{
|
||||
FILETIME ft;
|
||||
LARGE_INTEGER li;
|
||||
INT64_T t;
|
||||
static int tzflag;
|
||||
|
||||
if (tv)
|
||||
{
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
li.LowPart = ft.dwLowDateTime;
|
||||
li.HighPart = ft.dwHighDateTime;
|
||||
t = li.QuadPart; /* In 100-nanosecond intervals */
|
||||
t -= EPOCHFILETIME; /* Offset to the Epoch time */
|
||||
t /= 10; /* In microseconds */
|
||||
tv->tv_sec = (long)(t / 1000000);
|
||||
tv->tv_usec = (long)(t % 1000000);
|
||||
}
|
||||
|
||||
#ifndef COMPILER_LCC
|
||||
/* LCC that comes with Matlab has problems with _timezone and _daylight,
|
||||
and we don't need it anyway */
|
||||
if (tz)
|
||||
{
|
||||
if (!tzflag)
|
||||
{
|
||||
_tzset();
|
||||
tzflag++;
|
||||
}
|
||||
tz->tz_minuteswest = _timezone / 60;
|
||||
tz->tz_dsttime = _daylight;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/* SK: This has now been moved into util.c for simplification of compile scripts and Makefiles */
|
||||
|
||||
/*
|
||||
* timeval.h 1.0 01/12/19
|
||||
*
|
||||
* Defines gettimeofday, timeval, etc. for Win32
|
||||
*
|
||||
* By Wu Yongwei
|
||||
*
|
||||
*/
|
||||
#ifndef _TIMEVAL_H
|
||||
#define _TIMEVAL_H
|
||||
|
||||
#if TIME_WITH_SYS_TIME
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#elif HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#if ! HAVE_GETTIMEOFDAY || MINGW
|
||||
|
||||
#if __WIN32__
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifndef __GNUC__
|
||||
#define EPOCHFILETIME (116444736000000000i64)
|
||||
#else
|
||||
#define EPOCHFILETIME (116444736000000000LL)
|
||||
#endif
|
||||
|
||||
#include "gettimeofday.h"
|
||||
|
||||
/*!
|
||||
\brief A Windows gettimeofday implementation.
|
||||
*/
|
||||
|
||||
int gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
#if __WIN32__
|
||||
FILETIME ft;
|
||||
LARGE_INTEGER li;
|
||||
__int64 t;
|
||||
static int tzflag;
|
||||
|
||||
if (tv) {
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
li.LowPart = ft.dwLowDateTime;
|
||||
li.HighPart = ft.dwHighDateTime;
|
||||
t = li.QuadPart; /* In 100-nanosecond intervals */
|
||||
t -= EPOCHFILETIME; /* Offset to the Epoch time */
|
||||
t /= 10; /* In microseconds */
|
||||
tv->tv_sec = (long)(t / 1000000);
|
||||
tv->tv_usec = (long)(t % 1000000);
|
||||
}
|
||||
|
||||
if (tz) {
|
||||
if (!tzflag) {
|
||||
_tzset();
|
||||
tzflag++;
|
||||
}
|
||||
tz->tz_minuteswest = _timezone / 60;
|
||||
tz->tz_dsttime = _daylight;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#ifndef __gettimeofday_h
|
||||
#define __gettimeofday_h
|
||||
#include <time.h>
|
||||
|
||||
struct timezone {
|
||||
int tz_minuteswest; /* minutes W of Greenwich */
|
||||
int tz_dsttime; /* type of dst correction */
|
||||
};
|
||||
|
||||
int gettimeofday(struct timeval *tv, struct timezone *tz);
|
||||
|
||||
#endif
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
#include <winsock2.h>
|
||||
#include "poll.h"
|
||||
|
||||
int poll (struct pollfd *p, int num, int timeout)
|
||||
{
|
||||
struct timeval tv;
|
||||
fd_set read, write, except;
|
||||
int i, n, ret;
|
||||
char buf[1024];
|
||||
|
||||
FD_ZERO (&read);
|
||||
FD_ZERO (&write);
|
||||
FD_ZERO (&except);
|
||||
|
||||
n = -1;
|
||||
for (i = 0; i < num; ++i)
|
||||
{
|
||||
if (p[i].fd < 0)
|
||||
continue;
|
||||
if (p[i].events & POLLIN)
|
||||
FD_SET (p[i].fd, &read);
|
||||
if (p[i].events & POLLOUT)
|
||||
FD_SET (p[i].fd, &write);
|
||||
if (p[i].events & POLLERR)
|
||||
FD_SET (p[i].fd, &except);
|
||||
if (p[i].fd > n)
|
||||
n = p[i].fd;
|
||||
}
|
||||
|
||||
if (n == -1)
|
||||
return (0);
|
||||
|
||||
if (timeout < 0)
|
||||
ret = select (n+1, &read, &write, &except, nullptr);
|
||||
else
|
||||
{
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = 1000 * (timeout % 1000);
|
||||
ret = select (n+1, &read, &write, &except, &tv);
|
||||
}
|
||||
|
||||
for (i = 0; ret >= 0 && i < num; ++i)
|
||||
{
|
||||
p[i].revents = 0;
|
||||
if (FD_ISSET (p[i].fd, &read))
|
||||
{
|
||||
|
||||
int j = recv(p[i].fd, buf, 1024, MSG_PEEK);
|
||||
if(j>0)
|
||||
p[i].revents |= POLLIN;
|
||||
else
|
||||
p[i].revents |= POLLHUP;
|
||||
|
||||
}
|
||||
if (FD_ISSET (p[i].fd, &write))
|
||||
p[i].revents |= POLLOUT;
|
||||
if (FD_ISSET (p[i].fd, &except))
|
||||
p[i].revents |= POLLERR;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
#if defined(TARGET_HAS_PThread)
|
||||
|
||||
#include "ovasCConfigurationFieldtrip.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
bool CConfigurationFieldtrip::preConfigure()
|
||||
{
|
||||
const bool res = CConfigurationBuilder::preConfigure();
|
||||
|
||||
m_pHostName = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_host_name"));
|
||||
m_pHostPort = GTK_WIDGET(gtk_builder_get_object(m_builder, "spinbutton_host_port"));
|
||||
m_pMinSamples = GTK_WIDGET(gtk_builder_get_object(m_builder, "spinbutton_minSamples"));
|
||||
m_pSRCorrection = GTK_WIDGET(gtk_builder_get_object(m_builder, "checkbutton_SRCorrection"));
|
||||
|
||||
gtk_spin_button_set_range(GTK_SPIN_BUTTON(m_pMinSamples), 1.0, 10000.0);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(m_pMinSamples), m_minSamples);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(m_pHostPort), m_hostPort);
|
||||
gtk_entry_set_text(GTK_ENTRY(m_pHostName), m_hostName.toASCIIString());
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pSRCorrection), m_srCorrection);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool CConfigurationFieldtrip::postConfigure()
|
||||
{
|
||||
if (m_applyConfig)
|
||||
{
|
||||
gtk_spin_button_update(GTK_SPIN_BUTTON(m_pMinSamples));
|
||||
gtk_spin_button_update(GTK_SPIN_BUTTON(m_pHostPort));
|
||||
|
||||
m_minSamples = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(m_pMinSamples));
|
||||
m_hostPort = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(m_pHostPort));
|
||||
m_hostName = gtk_entry_get_text(GTK_ENTRY(m_pHostName));
|
||||
m_srCorrection = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_pSRCorrection)) > 0);
|
||||
}
|
||||
|
||||
return CConfigurationBuilder::postConfigure();
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif //#if defined(TARGET_HAS_PThread)
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_PThread)
|
||||
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
|
||||
namespace OpenViBE
|
||||
{
|
||||
namespace AcquisitionServer
|
||||
{
|
||||
/**
|
||||
* \class CConfigurationFieldtrip
|
||||
* \author Amelie Serpollet (CEA/LETI/CLINATEC)
|
||||
* \date Mon May 23 09:48:21 2011
|
||||
* \brief The CDriverFieldtrip allows the acquisition server to acquire data from a Fieldtrip buffer.
|
||||
*
|
||||
*/
|
||||
class CConfigurationFieldtrip final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
CConfigurationFieldtrip(const char* gtkBuilderFilename) : CConfigurationBuilder(gtkBuilderFilename) { }
|
||||
~CConfigurationFieldtrip() override { }
|
||||
|
||||
void setHostName(const CString& hostName) { m_hostName = hostName; }
|
||||
void setHostPort(const uint32_t hostPort) { m_hostPort = hostPort; }
|
||||
void setMinSamples(const uint32_t minSamples) { m_minSamples = minSamples; }
|
||||
void setSRCorrection(const bool bSRCorrection) { m_srCorrection = bSRCorrection; }
|
||||
|
||||
CString getHostName() const { return m_hostName; }
|
||||
uint32_t getHostPort() const { return m_hostPort; }
|
||||
uint32_t getMinSamples() const { return m_minSamples; }
|
||||
bool getSRCorrection() const { return m_srCorrection; }
|
||||
|
||||
protected:
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
|
||||
private:
|
||||
|
||||
CConfigurationFieldtrip();
|
||||
|
||||
protected:
|
||||
|
||||
GtkWidget* m_pHostName = nullptr;
|
||||
GtkWidget* m_pHostPort = nullptr;
|
||||
GtkWidget* m_pMinSamples = nullptr;
|
||||
GtkWidget* m_pSRCorrection = nullptr;
|
||||
|
||||
CString m_hostName = "localhost";
|
||||
uint32_t m_hostPort = 4000;
|
||||
uint32_t m_minSamples = 1;
|
||||
bool m_srCorrection = true;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // #if defined(TARGET_HAS_PThread)
|
||||
+533
@@ -0,0 +1,533 @@
|
||||
/* This driver uses the FieldTrip buffer open source library.
|
||||
* See http://www.ru.nl/fcdonders/fieldtrip for details.
|
||||
*/
|
||||
#if defined(TARGET_HAS_PThread)
|
||||
|
||||
#include "ovasCDriverFieldtrip.h"
|
||||
#include "ovasCConfigurationFieldtrip.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include "fieldtrip/buffer.h"
|
||||
#include "fieldtrip/extern.h"
|
||||
#include "fieldtrip/extern.c"
|
||||
#include "fieldtrip/util.c"
|
||||
#include "fieldtrip/printstruct.c"
|
||||
#include "fieldtrip/tcprequest.c"
|
||||
#include "fieldtrip/dmarequest.c"
|
||||
#include "fieldtrip/clientrequest.c"
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
//#include "GetCpuTime.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
CDriverFieldtrip::CDriverFieldtrip(IDriverContext& ctx)
|
||||
: IDriver(ctx), m_settings("AcquisitionServer_Driver_FieldTrip", m_driverCtx.getConfigurationManager())
|
||||
{
|
||||
m_header.setSamplingFrequency(0);
|
||||
m_header.setChannelCount(0);
|
||||
|
||||
m_waitDataRequest = new message_t();
|
||||
m_waitDataRequest->def = new messagedef_t();
|
||||
m_waitDataRequest->buf = nullptr;
|
||||
|
||||
m_getDataRequest = new message_t();
|
||||
m_getDataRequest->def = new messagedef_t();
|
||||
m_getDataRequest->buf = nullptr;
|
||||
|
||||
m_settings.add("Header", &m_header);
|
||||
m_settings.add("MinSamples", &m_minSamples);
|
||||
m_settings.add("PortNumber", &m_portNumber);
|
||||
m_settings.add("HostName", &m_hostName);
|
||||
m_settings.add("CorrectNonIntegerSR", &m_correctNonIntegerSR);
|
||||
m_settings.load();
|
||||
}
|
||||
|
||||
CDriverFieldtrip::~CDriverFieldtrip()
|
||||
{
|
||||
if (m_waitDataRequest)
|
||||
{
|
||||
//m_waitDataRequest->buf deleted with m_waitDataRequest->def
|
||||
if (m_waitDataRequest->def) { delete m_waitDataRequest->def; }
|
||||
delete m_waitDataRequest;
|
||||
}
|
||||
|
||||
if (m_getDataRequest)
|
||||
{
|
||||
//m_getDataRequest->buf deleted with m_getDataRequest->def
|
||||
if (m_getDataRequest->def) { delete m_getDataRequest->def; }
|
||||
delete m_getDataRequest;
|
||||
}
|
||||
}
|
||||
|
||||
const char* CDriverFieldtrip::getName() { return "Fieldtrip Driver"; }
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDriverFieldtrip::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected()) { return false; }
|
||||
|
||||
// ...
|
||||
// initialize hardware and get available header information
|
||||
// from it :
|
||||
|
||||
// connect to buffer
|
||||
if (m_connectionID != -1)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Already connected to Fieldtrip buffer " << m_hostName << ":" << m_portNumber << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connectionID = open_connection(m_hostName.toASCIIString(), int(m_portNumber));
|
||||
if (m_connectionID < 0)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Failed to connect to Fieldtrip buffer :\n" << m_hostName << ":" << m_portNumber << "\n";
|
||||
m_connectionID = -1;
|
||||
return false;
|
||||
}
|
||||
else { m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Connection to Fieldtrip buffer succeeded !\n"; }
|
||||
|
||||
// request header
|
||||
if (!requestHeader())
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Request header failed, disconnecting.\n";
|
||||
if (close_connection(m_connectionID) != 0)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Failed to disconnect correctly from Fieldtrip buffer\n";
|
||||
}
|
||||
m_connectionID = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!m_header.isChannelCountSet() || !m_header.isSamplingFrequencySet()) { return false; }
|
||||
|
||||
// Builds up a buffer to store acquired samples. This buffer
|
||||
// will be sent to the acquisition server later...
|
||||
m_sample = new float[m_header.getChannelCount() * nSamplePerSentBlock];
|
||||
if (!m_sample)
|
||||
{
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Saves parameters
|
||||
m_callback = &callback;
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
|
||||
if (m_minSamples < 1) { m_minSamples = 1; }
|
||||
if (m_minSamples > m_nSamplePerSentBlock) { m_minSamples = m_nSamplePerSentBlock; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverFieldtrip::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// request hardware to start
|
||||
// sending data
|
||||
// ...
|
||||
m_firstGetDataRequest = true;
|
||||
m_waitingTimeMs = (m_header.getSamplingFrequency() > 1000 ? 1 : (1000 / m_header.getSamplingFrequency())
|
||||
); //time for 1 sample if >= 1ms //(1000*m_nSamplePerSentBlock)
|
||||
m_nTotalSample = 0;
|
||||
|
||||
m_diffPerSample = (m_realSampling - m_header.getSamplingFrequency()) / m_realSampling;
|
||||
if (m_diffPerSample <= 0.0) { m_diffPerSample = 0.0; }
|
||||
m_driftSinceLastCorrection = 0.0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverFieldtrip::loop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (!m_driverCtx.isStarted()) { return true; }
|
||||
|
||||
CStimulationSet stimSet;
|
||||
stimSet.setStimulationCount(0);
|
||||
|
||||
// ...
|
||||
// receive samples from hardware
|
||||
// put them the correct way in the sample array
|
||||
// whether the buffer is full, send it to the acquisition server
|
||||
//...
|
||||
const int count = requestChunk(stimSet);
|
||||
if (count < 0) { return false; }
|
||||
if (count == 0) { return true; }
|
||||
m_callback->setSamples(m_sample, count);
|
||||
m_callback->setStimulationSet(stimSet);
|
||||
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverFieldtrip::stop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (!m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverFieldtrip::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
if (close_connection(m_connectionID) != 0) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Failed to disconnect correctly from Fieldtrip buffer\n"; }
|
||||
m_connectionID = -1;
|
||||
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
m_callback = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDriverFieldtrip::isConfigurable()
|
||||
{
|
||||
return true; // change to false if your device is not configurable
|
||||
}
|
||||
|
||||
bool CDriverFieldtrip::configure()
|
||||
{
|
||||
CConfigurationFieldtrip config(Directories::getDataDir() + "/applications/acquisition-server/interface-Fieldtrip.ui");
|
||||
config.setMinSamples(m_minSamples);
|
||||
config.setHostPort(m_portNumber);
|
||||
config.setHostName(m_hostName);
|
||||
config.setSRCorrection(m_correctNonIntegerSR);
|
||||
|
||||
if (config.configure(m_header))
|
||||
{
|
||||
m_minSamples = config.getMinSamples();
|
||||
m_portNumber = config.getHostPort();
|
||||
m_hostName = config.getHostName();
|
||||
m_correctNonIntegerSR = config.getSRCorrection();
|
||||
|
||||
m_settings.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDriverFieldtrip::requestHeader()
|
||||
{
|
||||
m_waitDataRequest->def->command = GET_HDR;
|
||||
m_waitDataRequest->def->version = VERSION;
|
||||
m_waitDataRequest->def->bufsize = 0;
|
||||
m_waitDataRequest->buf = nullptr;
|
||||
|
||||
message_t* response = nullptr;
|
||||
|
||||
const int res = clientrequest(m_connectionID, m_waitDataRequest, &response);
|
||||
|
||||
if (res != 0)
|
||||
{
|
||||
FreeResponse(response, "Error while asking for header. Buffer aborted ?");
|
||||
return false;
|
||||
}
|
||||
else if (response == nullptr || response->def == nullptr)
|
||||
{
|
||||
FreeResponse(response, "Error while asking for header");
|
||||
return false;
|
||||
}
|
||||
else if (response->def->command != GET_OK || response->def->bufsize == 0)
|
||||
{
|
||||
FreeResponse(response, "No header in the buffer");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned int size = response->def->bufsize;
|
||||
headerdef_t* headerDef = (headerdef_t*)response->buf;
|
||||
|
||||
if (size < sizeof(headerdef_t))
|
||||
{
|
||||
FreeResponse(response, "Header received has wrong format");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_header.setSamplingFrequency(uint32_t(headerDef->fsample));
|
||||
m_realSampling = headerDef->fsample;
|
||||
m_header.setChannelCount(headerDef->nchans);
|
||||
m_dataType = headerDef->data_type;
|
||||
|
||||
if (m_dataType != DATATYPE_FLOAT32 && m_dataType != DATATYPE_FLOAT64)
|
||||
{
|
||||
FreeResponse(response, "Data type is not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size == sizeof(headerdef_t)) //no chunk attached to the header
|
||||
{
|
||||
for (uint32_t i = 0; i < headerDef->nchans; i++) { m_header.setChannelName(i, ("Channel " + std::to_string(i)).c_str()); }
|
||||
}
|
||||
else //chunk(s) attached to the header, maybe channel names
|
||||
{
|
||||
int bytesInHeaderBuffer = headerDef->bufsize;
|
||||
void* chunk = (headerdef_t*)headerDef + 1;
|
||||
bool foundChannelNames = false;
|
||||
|
||||
while (bytesInHeaderBuffer > 0)
|
||||
{
|
||||
if (((ft_chunk_t*)chunk)->def.type == FT_CHUNK_CHANNEL_NAMES)
|
||||
{
|
||||
foundChannelNames = true;
|
||||
char* chunkdata = ((ft_chunk_t*)chunk)->data;
|
||||
for (uint32_t i = 0; i < headerDef->nchans; i++)
|
||||
{
|
||||
std::string name = chunkdata;
|
||||
m_header.setChannelName(i, name.c_str());
|
||||
chunkdata = (char*)chunkdata + name.size() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bytesInHeaderBuffer -= ((ft_chunk_t*)chunk)->def.size + sizeof(ft_chunkdef_t);
|
||||
if (bytesInHeaderBuffer > 0) { chunk = (char*)chunk + ((ft_chunk_t*)chunk)->def.size + sizeof(ft_chunkdef_t); }
|
||||
}
|
||||
|
||||
if (!foundChannelNames)
|
||||
{
|
||||
for (uint32_t i = 0; i < headerDef->nchans; i++) { m_header.setChannelName(i, ("Channel " + std::to_string(i)).c_str()); }
|
||||
}
|
||||
}
|
||||
} /* end valid header */
|
||||
|
||||
FreeResponse(response, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int CDriverFieldtrip::requestChunk(CStimulationSet& oStimulationSet)
|
||||
{
|
||||
//There are two basic opertations:
|
||||
// - configure m_getDataRequest and m_waitDataRequest
|
||||
// - use m_getDataRequest to call fieldtrip clientrequest() that gets the data from fieldtrip
|
||||
// - transpose the data to fill m_sample in the correct way expected by OpenVube
|
||||
//The rest are data validity checks and correction for non-integer sampling frequency
|
||||
|
||||
//configure "wait data" request
|
||||
m_waitDataRequest->def->command = WAIT_DAT;
|
||||
m_waitDataRequest->def->version = VERSION;
|
||||
|
||||
if (m_waitDataRequest->buf == nullptr)
|
||||
{
|
||||
m_waitDataRequest->def->bufsize = 0;
|
||||
waitdef_t* waitDef = new waitdef_t();
|
||||
unsigned int requestSize = 0;
|
||||
requestSize = append((void**)&m_waitDataRequest->def, sizeof(messagedef_t), waitDef, sizeof(waitdef_t));
|
||||
m_waitDataRequest->def->bufsize = requestSize - sizeof(messagedef_t);
|
||||
m_waitDataRequest->buf = (messagedef_t*)m_waitDataRequest->def + 1;
|
||||
}
|
||||
|
||||
waitdef_t* waitDef = (waitdef_t*)m_waitDataRequest->buf;
|
||||
waitDef->threshold.nevents = 0xFFFFFFFF;
|
||||
waitDef->threshold.nsamples = m_nSamplePerSentBlock;
|
||||
waitDef->milliseconds = m_waitingTimeMs;
|
||||
|
||||
message_t* response = nullptr;
|
||||
int res = clientrequest(m_connectionID, m_waitDataRequest, &response);
|
||||
|
||||
uint32_t nDataReceived = 0;
|
||||
uint32_t nDataToSend = 0;
|
||||
|
||||
if (res)
|
||||
{
|
||||
FreeResponse(response, "Error while asking for data. Buffer aborted ?");
|
||||
return -1;
|
||||
}
|
||||
else if (response == nullptr || response->def == nullptr)
|
||||
{
|
||||
FreeResponse(response, "Error while asking for data");
|
||||
return -1;
|
||||
}
|
||||
else if (response->def->command != WAIT_OK || response->def->bufsize != 8 || response->buf == nullptr)
|
||||
{
|
||||
FreeResponse(response, "No header in buffer anymore");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// new header received ? stop acquisition
|
||||
if (((samples_events_t*)response->buf)->nsamples < m_nTotalSample)
|
||||
{
|
||||
FreeResponse(response, "End of data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// no new data
|
||||
if (((samples_events_t*)response->buf)->nsamples <= m_nTotalSample + m_minSamples)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "No new data\n";
|
||||
FreeResponse(response, nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get data
|
||||
uint32_t lastSample = ((samples_events_t*)response->buf)->nsamples;
|
||||
if (lastSample > m_nTotalSample + m_nSamplePerSentBlock)
|
||||
{
|
||||
if (m_firstGetDataRequest) { m_nTotalSample = lastSample - m_nSamplePerSentBlock; }
|
||||
else { lastSample = m_nTotalSample + m_nSamplePerSentBlock; }
|
||||
}
|
||||
|
||||
if (m_firstGetDataRequest) { m_firstGetDataRequest = false; }
|
||||
|
||||
FreeResponse(response, nullptr); //prevents memory leak
|
||||
|
||||
// "get data" request
|
||||
m_getDataRequest->def->command = GET_DAT;
|
||||
m_getDataRequest->def->version = VERSION;
|
||||
|
||||
if (m_getDataRequest->buf == nullptr)
|
||||
{
|
||||
m_getDataRequest->def->bufsize = 0;
|
||||
datasel_t* dataSel = new datasel_t();
|
||||
unsigned int requestSize = 0;
|
||||
requestSize = append((void**)&m_getDataRequest->def, sizeof(messagedef_t), dataSel, sizeof(datasel_t));
|
||||
m_getDataRequest->def->bufsize = requestSize - sizeof(messagedef_t);
|
||||
m_getDataRequest->buf = (messagedef_t*)m_getDataRequest->def + 1;
|
||||
}
|
||||
|
||||
datasel_t* dataSel = (datasel_t*)m_getDataRequest->buf;
|
||||
dataSel->begsample = m_nTotalSample;
|
||||
dataSel->endsample = lastSample - 1;
|
||||
|
||||
//actual data acquisition
|
||||
res = clientrequest(m_connectionID, m_getDataRequest, &response);
|
||||
|
||||
if (res || !response || !response->def || response->def->version != VERSION)
|
||||
{
|
||||
FreeResponse(response, "Error while asking for data");
|
||||
return -1;
|
||||
}
|
||||
else if (response->def->command != GET_OK || response->def->bufsize == 0 || response->buf == nullptr)
|
||||
{
|
||||
FreeResponse(response, "Data are not available anymore");
|
||||
return -1;
|
||||
}
|
||||
else // data received
|
||||
{
|
||||
datadef_t* datadef = (datadef_t*)response->buf;
|
||||
void* databuf = (datadef_t*)response->buf + 1;
|
||||
if (datadef->bufsize / (wordsize_from_type(datadef->data_type) * datadef->nchans) != lastSample - m_nTotalSample)
|
||||
{
|
||||
FreeResponse(response, "Data received from buffer are invalid");
|
||||
return -1;
|
||||
}
|
||||
else // data correct
|
||||
{
|
||||
nDataReceived = lastSample - m_nTotalSample;
|
||||
|
||||
// Delete some samples if necessary.
|
||||
// Sampling rate is converted into integer in openvibe,
|
||||
// so we can have up to 1 sample too many per second.
|
||||
nDataToSend = nDataReceived;
|
||||
|
||||
if (m_correctNonIntegerSR)
|
||||
{
|
||||
m_driftSinceLastCorrection += (m_diffPerSample * nDataReceived);
|
||||
if (m_driftSinceLastCorrection >= 1.0)
|
||||
{
|
||||
// delete samples
|
||||
const uint32_t diffSamples = uint32_t(m_driftSinceLastCorrection);
|
||||
nDataToSend -= diffSamples;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Correction for non-integer sampling rate : " << diffSamples << " samples deleted\n";
|
||||
m_driftSinceLastCorrection -= double(diffSamples);
|
||||
//oStimulationSet.appendStimulation(OVTK_GDF_Missing, CTime(m_header.getSamplingFrequency(), nDataToSend).time(), CTime(m_header.getSamplingFrequency(), diffSamples)).time();
|
||||
}
|
||||
}
|
||||
|
||||
// set data in m_sample
|
||||
double* buffer64;
|
||||
float* buffer32;
|
||||
switch (m_dataType)
|
||||
{
|
||||
case DATATYPE_FLOAT64:
|
||||
buffer64 = (double*)databuf;
|
||||
for (size_t j = 0; j < m_header.getChannelCount(); j++)
|
||||
{
|
||||
for (uint32_t i = 0; i < nDataToSend; i++)
|
||||
{
|
||||
const double value = buffer64[i * m_header.getChannelCount() + j];
|
||||
/*if ( _isnan(value) || !_finite(value) || value==DBL_MAX )
|
||||
{
|
||||
m_sample[j*nDataToSend + i] = FLT_MAX;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "NaN or infinite sample received.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
//data from IHM implant are in volts, must be in uvolts in openvibe
|
||||
m_sample[j*nDataToSend + i] = (float) 1000000.0f*value;
|
||||
}*/
|
||||
m_sample[j * nDataToSend + i] = float(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DATATYPE_FLOAT32:
|
||||
|
||||
buffer32 = (float*)databuf;
|
||||
for (size_t j = 0; j < m_header.getChannelCount(); j++)
|
||||
{
|
||||
for (uint32_t i = 0; i < nDataToSend; i++)
|
||||
{
|
||||
const float value = buffer32[i * m_header.getChannelCount() + j];
|
||||
/*if ( _isnan(value) || !_finite(value) || value==FLT_MAX )
|
||||
{
|
||||
m_sample[j*nDataToSend + i] = FLT_MAX;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "NaN or infinite sample received.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
//data from IHM implant are in volts, must be in uvolts in openvibe
|
||||
m_sample[j*nDataToSend + i] = 1000000.0f*value;
|
||||
}*/
|
||||
m_sample[j * nDataToSend + i] = value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
FreeResponse(response, "DEV ERROR : data type not suppported");
|
||||
return -1;
|
||||
}//end switch
|
||||
}//end data correct
|
||||
}//end data received
|
||||
|
||||
FreeResponse(response, nullptr);//we copied the data from response, so now we need to release this memory to avoid memory leak
|
||||
|
||||
m_nTotalSample = lastSample;
|
||||
}
|
||||
|
||||
return nDataToSend; // no error
|
||||
}
|
||||
|
||||
void CDriverFieldtrip::FreeResponse(message_t* response, const char* message)
|
||||
{
|
||||
if (message != nullptr) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << message << "\n"; }
|
||||
|
||||
if (response->buf) { free(response->buf); }
|
||||
if (response->def) { free(response->def); }
|
||||
free(response);
|
||||
response = nullptr;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/* This driver uses the FieldTrip buffer open source library.
|
||||
* See http://www.ru.nl/fcdonders/fieldtrip for details.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_PThread)
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "fieldtrip/message.h"
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
// for GET_CPU_TIME
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace OpenViBE
|
||||
{
|
||||
namespace AcquisitionServer
|
||||
{
|
||||
/**
|
||||
* \class CDriverFieldtrip
|
||||
* \author Amelie Serpollet (CEA/LETI/CLINATEC)
|
||||
* \date Mon May 23 09:48:21 2011
|
||||
* \brief The CDriverFieldtrip allows the acquisition server to acquire data from a Fieldtrip buffer.
|
||||
*
|
||||
* TODO: details
|
||||
*
|
||||
*/
|
||||
class CDriverFieldtrip final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
CDriverFieldtrip(IDriverContext& ctx);
|
||||
~CDriverFieldtrip() override;
|
||||
const char* getName() override;
|
||||
|
||||
bool initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback) override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool start() override;
|
||||
bool stop() override;
|
||||
bool loop() override;
|
||||
|
||||
bool isConfigurable() override;
|
||||
bool configure() override;
|
||||
const IHeader* getHeader() override { return &m_header; }
|
||||
|
||||
void FreeResponse(message_t* response, const char* message);
|
||||
|
||||
protected:
|
||||
|
||||
bool requestHeader();
|
||||
int requestChunk(CStimulationSet& oStimulationSet);
|
||||
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
CHeader m_header;
|
||||
SettingsHelper m_settings;
|
||||
uint32_t m_nSamplePerSentBlock = 0;
|
||||
float* m_sample = nullptr;
|
||||
uint32_t m_dataType = DATATYPE_UNKNOWN;
|
||||
|
||||
// Connection to Fieldtrip buffer
|
||||
CString m_hostName = "localhost";
|
||||
uint32_t m_portNumber = 1979;
|
||||
int m_connectionID = -1;
|
||||
uint32_t m_minSamples = 1;
|
||||
|
||||
// Avoid frequent memory allocation
|
||||
message_t* m_waitDataRequest = nullptr;
|
||||
message_t* m_getDataRequest = nullptr;
|
||||
|
||||
uint32_t m_nTotalSample = 0;
|
||||
uint32_t m_waitingTimeMs = 0;
|
||||
|
||||
bool m_firstGetDataRequest = false;
|
||||
|
||||
bool m_correctNonIntegerSR = true; // ???
|
||||
double m_realSampling = 0;
|
||||
double m_diffPerSample = 0; // ???
|
||||
double m_driftSinceLastCorrection = 0;
|
||||
|
||||
// count time lost for "get cpu time" :
|
||||
//double m_mesureLostTime = 0;
|
||||
//uint32_t m_mesureNumber = 0;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // #if defined(TARGET_HAS_PThread)
|
||||
@@ -0,0 +1,88 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Thomas Stewart
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Acknowledgements:
|
||||
|
||||
This driver was written by Tom Stewart under the supervision of
|
||||
Dr. Tomasz M. Rutkowski in the University of Tsukuba's BCI Laboratory
|
||||
http://bci-lab.info/
|
||||
|
||||
Installation:
|
||||
|
||||
The driver was developed using gtec's Linux C API 1.14.02. For it build properly,
|
||||
the install.sh supplied with the gtec API needs to have been run which should
|
||||
have created the following files:
|
||||
|
||||
/usr/lib/libgusbampapiso.so.1.14.02
|
||||
/usr/include/gAPI.h
|
||||
/etc/gtec/filter_files/DSPfilter.bin
|
||||
/etc/gtec/filter_files/DSPNotchfilter.bin
|
||||
|
||||
To access the USB without being root, you'll need to add yourself to the
|
||||
plugdev group and write a udev rule.
|
||||
|
||||
To do that, first check if there is a plugdev group:
|
||||
|
||||
$ groups
|
||||
|
||||
If there isn't already a plugdev group, then add it using:
|
||||
|
||||
$ sudo groupadd plugdev
|
||||
|
||||
Now execute the following:
|
||||
|
||||
$ sudo usermod -a -G plugdev <your-username>
|
||||
|
||||
If you're not sure what your username is, check it with:
|
||||
|
||||
$ whoami
|
||||
|
||||
Next create a file named /etc/udev/rules.d/10-gusbamp-usb.rules
|
||||
|
||||
$ sudo touch /etc/udev/rules.d/10-gusbamp-usb.rules
|
||||
|
||||
And open it with nano editor
|
||||
|
||||
$ sudo nano /etc/udev/rules.d/10-gusbamp-usb.rules
|
||||
|
||||
Now paste the following line into the editor:
|
||||
|
||||
ATTRS{idProduct}=="0001", ATTRS{idVendor}=="153c", MODE="666", GROUP="plugdev"
|
||||
|
||||
The values for idProduct and idVendor should match the ones that show up just
|
||||
after you've plugged in the amplifier and executed:
|
||||
|
||||
$ dmesg
|
||||
|
||||
Lastly, be sure to log out and back in.
|
||||
|
||||
Useage:
|
||||
|
||||
The driver gives access to everything in the API with the exception of the channel
|
||||
calibration and asynchronous configuration for digital outputs.
|
||||
|
||||
To run multiple gUSBAmps in parallel, use multiple instances of the acquisition server
|
||||
and configure each instance as Master / Slave according to gtec's documentation. When
|
||||
starting the acquisition, first connect to all the devices then press play on each device
|
||||
starting with the master.
|
||||
|
||||
|
||||
+2529
File diff suppressed because it is too large
Load Diff
+120
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
class Queue
|
||||
{
|
||||
T* buffer = nullptr;
|
||||
int head = 0, tail = 0, size = 0;
|
||||
|
||||
public:
|
||||
Queue() { }
|
||||
|
||||
Queue(T* array, const int len)
|
||||
{
|
||||
size = len;
|
||||
buffer = array;
|
||||
}
|
||||
|
||||
void SetBuffer(T* array, const int len)
|
||||
{
|
||||
size = len;
|
||||
head = tail = 0;
|
||||
buffer = array;
|
||||
}
|
||||
|
||||
int Get(T* elements, int len)
|
||||
{
|
||||
// Trim the length if necessary to only as large as the number of available elements in the buffer
|
||||
len = MIN(len, Avail());
|
||||
|
||||
int nonwrapped = MIN((size - tail), len);
|
||||
const int wrapped = len - nonwrapped;
|
||||
|
||||
// memcpy the data starting at the head all the way up to the last element *(storage - 1)
|
||||
memcpy(elements, (buffer + tail), nonwrapped * sizeof(T));
|
||||
|
||||
// If there's still data to copy memcpy whatever remains, starting at the first element *(begin) until the end of data. The first step will have ensured
|
||||
// that we don't crash into the tail during this process.
|
||||
memcpy((elements + nonwrapped), buffer, wrapped * sizeof(T));
|
||||
|
||||
// Recalculate head
|
||||
tail = (tail + nonwrapped + wrapped) % size;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// Returns the number of bytes actually placed in the array
|
||||
int Put(const T* elements, int len)
|
||||
{
|
||||
// Trim the length if necessary to only as large as the nuber of free elements in the buffer
|
||||
len = MIN(len, Free());
|
||||
|
||||
// Figure out how much to append to the end of the buffer and how much will overlap onto the start
|
||||
int nonwrapped = MIN((size - head), len);
|
||||
const int wrapped = len - nonwrapped;
|
||||
|
||||
// memcpy the data starting at the head all the way up to the last element *(storage - 1)
|
||||
memcpy((buffer + head), elements, nonwrapped * sizeof(T));
|
||||
|
||||
// If there's still data to copy memcpy whatever remains onto the beginning of the array
|
||||
memcpy(buffer, (elements + nonwrapped), wrapped * sizeof(T));
|
||||
|
||||
// Re-recalculate head
|
||||
head = (head + nonwrapped + wrapped) % size;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// Expand the size of queue without actually modifying any of the contents - useful for copying directly onto the queu buffer
|
||||
int Pad(int len)
|
||||
{
|
||||
// Trim the length if necessary to only as large as the nuber of free elements in the buffer
|
||||
len = MIN(len, Free());
|
||||
|
||||
// Figure out how much to append to the end of the buffer and how much will overlap onto the start
|
||||
const int nonwrapped = MIN((size - head), len), wrapped = len - nonwrapped;
|
||||
|
||||
// Re-recalculate head
|
||||
head = (head + nonwrapped + wrapped) % size;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// Removes the oldest entry from the Queue
|
||||
void Pop() { if (Avail()) tail = (tail + 1) % size; }
|
||||
|
||||
// Returns the oldest element in the array (the one added before any other)
|
||||
T& Tail() { return buffer[tail]; }
|
||||
|
||||
// Returns the newest element in the array (the one added after every other)
|
||||
T& Head() { return buffer[(head + size - 1) % size]; }
|
||||
|
||||
T& operator[](int n) { return buffer[tail + n % size]; }
|
||||
|
||||
void Clear() { head = tail = 0; }
|
||||
|
||||
int Avail() const { return (size + head - tail) % size; }
|
||||
|
||||
int Free() const { return (size - 1 - Avail()); }
|
||||
|
||||
// Gets the number of free elements that can be stored contiguously
|
||||
int FreeContiguous() { return head < tail ? tail - head - 1 : MIN(size - head, Free()); }
|
||||
|
||||
// Gets a pointer to the next free address in the buffer
|
||||
T* NextFreeAddress() { return buffer + head; }
|
||||
};
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
+545
@@ -0,0 +1,545 @@
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
|
||||
#include "ovasCConfigurationGTecGUSBampLinux.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
/*_________________________________________________
|
||||
|
||||
Callbacks to specific widgets
|
||||
_________________________________________________*/
|
||||
|
||||
void button_apply_bipolar_pressed_cb(GtkButton* button, void* data)
|
||||
{
|
||||
CConfigurationGTecGUSBampLinux* config = static_cast<CConfigurationGTecGUSBampLinux*>(data);
|
||||
config->OnButtonApplyConfigPressed(CConfigurationGTecGUSBampLinux::BipolarColumn);
|
||||
}
|
||||
|
||||
void button_apply_bandpass_pressed_cb(GtkButton* button, void* data)
|
||||
{
|
||||
CConfigurationGTecGUSBampLinux* config = static_cast<CConfigurationGTecGUSBampLinux*>(data);
|
||||
config->OnButtonApplyConfigPressed(CConfigurationGTecGUSBampLinux::BandpassColumn);
|
||||
}
|
||||
|
||||
void button_apply_notch_pressed_cb(GtkButton* button, void* data)
|
||||
{
|
||||
CConfigurationGTecGUSBampLinux* config = static_cast<CConfigurationGTecGUSBampLinux*>(data);
|
||||
config->OnButtonApplyConfigPressed(CConfigurationGTecGUSBampLinux::NotchColumn);
|
||||
}
|
||||
|
||||
void button_check_impedance_pressed_cb(GtkButton* button, void* data) { gtk_button_set_label(button, "Checking..."); }
|
||||
|
||||
void button_check_impedance_clicked_cb(GtkButton* button, void* data)
|
||||
{
|
||||
CConfigurationGTecGUSBampLinux* config = static_cast<CConfigurationGTecGUSBampLinux*>(data);
|
||||
config->OnButtonCheckImpedanceClicked();
|
||||
}
|
||||
|
||||
void combobox_sampling_frequency_changed_cb(GtkComboBox* pCombobox, void* data)
|
||||
{
|
||||
CConfigurationGTecGUSBampLinux* config = static_cast<CConfigurationGTecGUSBampLinux*>(data);
|
||||
config->OnComboboxSamplingFrequencyChanged();
|
||||
}
|
||||
|
||||
void DeviceChangedCB(GtkComboBox* pCombobox, void* data)
|
||||
{
|
||||
CConfigurationGTecGUSBampLinux* config = static_cast<CConfigurationGTecGUSBampLinux*>(data);
|
||||
config->OnComboboxDeviceChanged();
|
||||
}
|
||||
|
||||
void entry_impedance_activate_cb(GtkEntry* pEntry, void* data)
|
||||
{
|
||||
// Reset the background colour and stick a question mark on the box so the impedance check function will check this channel
|
||||
GdkColor color;
|
||||
color.red = color.green = color.blue = 0xFFFF;
|
||||
gtk_entry_set_text(pEntry, "?");
|
||||
gtk_widget_modify_base(GTK_WIDGET(pEntry), GTK_STATE_NORMAL, &color);
|
||||
}
|
||||
|
||||
/*_________________________________________________*/
|
||||
|
||||
// If you added more reference attribute, initialize them here
|
||||
CConfigurationGTecGUSBampLinux::CConfigurationGTecGUSBampLinux(IDriverContext& ctx, const char* gtkBuilderFilename, std::string* deviceName,
|
||||
gt_usbamp_config* config)
|
||||
: CConfigurationBuilder(gtkBuilderFilename), m_driverCtx(ctx), m_deviceName(deviceName), m_config(config) {}
|
||||
|
||||
void CConfigurationGTecGUSBampLinux::UpdateFilters()
|
||||
{
|
||||
GtkListStore* listStore = GTK_LIST_STORE(gtk_builder_get_object(m_builder,"model_channel_config"));
|
||||
GtkTreeView* treeView = GTK_TREE_VIEW(gtk_builder_get_object(m_builder,"treeview_channel_config"));
|
||||
GtkTreeModel* treeModel = gtk_tree_view_get_model(treeView);
|
||||
GtkTreeIter iter;
|
||||
|
||||
// If the sampling frequency changes the filters may no longer be valid, so clear them all and have the user start again
|
||||
for (gboolean end = gtk_tree_model_get_iter_first(treeModel, &iter); end; end = gtk_tree_model_iter_next(treeModel, &iter))
|
||||
{
|
||||
gtk_list_store_set(listStore, &iter, NotchColumn, "none", -1);
|
||||
gtk_list_store_set(listStore, &iter, NotchIdColumn, GT_FILTER_NONE, -1);
|
||||
|
||||
gtk_list_store_set(listStore, &iter, BandpassColumn, "none", -1);
|
||||
gtk_list_store_set(listStore, &iter, BandpassIdColumn, GT_FILTER_NONE, -1);
|
||||
}
|
||||
|
||||
// Get pointers to the comboboxes
|
||||
GtkComboBox* comboBoxDevice = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_device"));
|
||||
GtkComboBox* comboBoxSampling = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_sampling_frequency"));
|
||||
|
||||
// Get the device name and the sample rate from the comboboxes
|
||||
char* deviceName = gtk_combo_box_get_active_text(comboBoxDevice);
|
||||
gt_size sampling = (gt_size)strtol(gtk_combo_box_get_active_text(comboBoxSampling), nullptr, 10);
|
||||
|
||||
// This takes a while so we'll keep the user informed via the console - might have to put this in a thread at some point though
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Opening device [" << deviceName << "] to query filters ...\n";
|
||||
|
||||
// Try opening the device
|
||||
if (GT_OpenDevice(deviceName))
|
||||
{
|
||||
GtkTreeIter iterBandpass, iterNotch;
|
||||
|
||||
GtkListStore* listStoreBandpass = GTK_LIST_STORE(gtk_builder_get_object(m_builder,"model_bandpass"));
|
||||
GtkListStore* listStoreNotch = GTK_LIST_STORE(gtk_builder_get_object(m_builder,"model_notch"));
|
||||
|
||||
// Clear all the entries from the filter comboboxes
|
||||
gtk_list_store_clear(listStoreBandpass);
|
||||
gtk_list_store_clear(listStoreNotch);
|
||||
|
||||
// Add the none and autoset configurations back in
|
||||
// none
|
||||
gtk_list_store_append(listStoreBandpass, &iterBandpass);
|
||||
gtk_list_store_set(listStoreBandpass, &iterBandpass, 0, "none", 1, GT_FILTER_NONE, -1);
|
||||
gtk_list_store_append(listStoreNotch, &iterNotch);
|
||||
gtk_list_store_set(listStoreNotch, &iterNotch, 0, "none", 1, GT_FILTER_NONE, -1);
|
||||
|
||||
// autoset
|
||||
gtk_list_store_append(listStoreBandpass, &iterBandpass);
|
||||
gtk_list_store_set(listStoreBandpass, &iterBandpass, 0, "autoset", 1, GT_FILTER_AUTOSET, -1);
|
||||
gtk_list_store_append(listStoreNotch, &iterNotch);
|
||||
gtk_list_store_set(listStoreNotch, &iterNotch, 0, "autoset", 1, GT_FILTER_AUTOSET, -1);
|
||||
|
||||
// Set the combo boxes to show none, it's a bit tidier that way
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_bandpass")), 0);
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_notch")), 0);
|
||||
|
||||
// Get the sizes of the lists
|
||||
gt_size bandpassListSize = GT_GetBandpassFilterListSize(deviceName, sampling);
|
||||
gt_size notchListSize = GT_GetNotchFilterListSize(deviceName, sampling);
|
||||
|
||||
// Allocate them
|
||||
gt_filter_specification* bandpassList = new gt_filter_specification[bandpassListSize];
|
||||
gt_filter_specification* notchList = new gt_filter_specification[notchListSize];
|
||||
|
||||
// Get the lists themselves - note the last parameter to these two should be specified in bytes
|
||||
GT_GetBandpassFilterList(deviceName, sampling, bandpassList, bandpassListSize * sizeof(gt_filter_specification));
|
||||
GT_GetNotchFilterList(deviceName, sampling, notchList, notchListSize * sizeof(gt_filter_specification));
|
||||
|
||||
// Repopulate the comboboxes - for each returned filter make a description and put it in the combobox
|
||||
// Bandpass
|
||||
for (uint32_t i = 0; i < bandpassListSize; ++i)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "HP: " << bandpassList[i].f_lower << " / LP: " << bandpassList[i].f_upper; // HP = High Pass, LP = Low Pass
|
||||
gtk_list_store_append(listStoreBandpass, &iterBandpass);
|
||||
gtk_list_store_set(listStoreBandpass, &iterBandpass, 0, ss.str().c_str(), 1, bandpassList[i].id, -1);
|
||||
}
|
||||
// Notch
|
||||
for (uint32_t i = 0; i < notchListSize; ++i)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "HS: " << notchList[i].f_lower << " / LS: " << notchList[i].f_upper; // HS = High Stop, LS = Low Stop
|
||||
gtk_list_store_append(listStoreNotch, &iterNotch);
|
||||
gtk_list_store_set(listStoreNotch, &iterNotch, 0, ss.str().c_str(), 1, notchList[i].id, -1);
|
||||
}
|
||||
|
||||
GT_CloseDevice(deviceName);
|
||||
}
|
||||
else { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not open device\n"; }
|
||||
}
|
||||
|
||||
bool CConfigurationGTecGUSBampLinux::preConfigure()
|
||||
{
|
||||
char** deviceList = nullptr;
|
||||
size_t size = 0;
|
||||
|
||||
if (!CConfigurationBuilder::preConfigure()) return false;
|
||||
|
||||
// Refresh and get the list of currently connnected devices
|
||||
GT_UpdateDevices();
|
||||
size = GT_GetDeviceListSize();
|
||||
deviceList = GT_GetDeviceList();
|
||||
|
||||
GtkComboBox* comboBox = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,"combobox_device"));
|
||||
|
||||
for (uint32_t i = 0; i < size; ++i) { gtk_combo_box_append_text(comboBox, deviceList[i]); }
|
||||
|
||||
GT_FreeDeviceList(deviceList, size);
|
||||
|
||||
// Connect all the callbacks
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "button_apply_bipolar"), "pressed", G_CALLBACK(button_apply_bipolar_pressed_cb), this);
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "button_apply_bandpass"), "pressed", G_CALLBACK(button_apply_bandpass_pressed_cb), this);
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "button_apply_notch"), "pressed", G_CALLBACK(button_apply_notch_pressed_cb), this);
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "button_check_impedance"), "pressed", G_CALLBACK(button_check_impedance_pressed_cb), this);
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "button_check_impedance"), "clicked", G_CALLBACK(button_check_impedance_clicked_cb), this);
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "combobox_sampling_frequency"), "changed", G_CALLBACK(combobox_sampling_frequency_changed_cb), this);
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "combobox_device"), "changed", G_CALLBACK(DeviceChangedCB), this);
|
||||
|
||||
// Connect up all the activate methods for the text entries so the user can select them and read the associated channel impedance
|
||||
for (int i = 0; i < (GT_USBAMP_NUM_ANALOG_IN + GT_USBAMP_NUM_REFERENCE); ++i)
|
||||
{
|
||||
std::stringstream ss;
|
||||
// Compile the string corresponding to the name of the widget displaying the impedance information
|
||||
ss << "entry_impedance" << i + 1;
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, ss.str().c_str()), "focus-in-event", G_CALLBACK(entry_impedance_activate_cb), this);
|
||||
}
|
||||
|
||||
// Couldn't work out how to do this in the designer, set the treeview box to be able to select multiple items at once
|
||||
GtkTreeView* treeView = GTK_TREE_VIEW(gtk_builder_get_object(m_builder,"treeview_channel_config"));
|
||||
GtkTreeSelection* selection = gtk_tree_view_get_selection(treeView);
|
||||
gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
|
||||
|
||||
// Now apply all the configs recovered from the settings helper to the GUI - don't have to worry about the sampling rate and number of channels though since they're already taken care of
|
||||
|
||||
// First look through all the device names returned from the API. If any match the one recovered from the settings manager set that as active
|
||||
GtkTreeModel* treeModelName = gtk_combo_box_get_model(comboBox);
|
||||
GtkTreeIter itName;
|
||||
for (gboolean end = gtk_tree_model_get_iter_first(treeModelName, &itName); end; end = gtk_tree_model_iter_next(treeModelName, &itName))
|
||||
{
|
||||
gchar* filterDesc;
|
||||
gtk_tree_model_get(treeModelName, &itName, 0, &filterDesc, -1);
|
||||
|
||||
// If the name in the combo box matches the one passed in then make that entry active
|
||||
if (*m_deviceName == filterDesc) // todo: check that this works with the API
|
||||
{
|
||||
gtk_combo_box_set_active_iter(comboBox, &itName);
|
||||
}
|
||||
|
||||
// Free the string now that we're finished with it
|
||||
g_free(filterDesc);
|
||||
}
|
||||
|
||||
// todo: when we change the devicename combo box we should refresh all the filters as per sampling frequency changed.
|
||||
|
||||
// And since we might have changed to a valid device name, let's update the filter combo boxes
|
||||
UpdateFilters();
|
||||
|
||||
// Fill out the analog output configs
|
||||
// Shape
|
||||
GtkComboBox* comboBoxShape = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_analog_out_shape"));
|
||||
gtk_combo_box_set_active(comboBoxShape, m_config->ao_config->shape);
|
||||
// Amplitude
|
||||
GtkSpinButton* buttonAmplitude = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder,"spinbutton_analog_out_amplitude"));
|
||||
gtk_spin_button_set_value(buttonAmplitude, m_config->ao_config->amplitude);
|
||||
// Offset
|
||||
GtkSpinButton* buttonOffset = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder,"spinbutton_analog_out_offset"));
|
||||
gtk_spin_button_set_value(buttonOffset, m_config->ao_config->offset);
|
||||
// Frequency
|
||||
GtkSpinButton* buttonFrequency = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder,"spinbutton_analog_out_frequency"));
|
||||
gtk_spin_button_set_value(buttonFrequency, m_config->ao_config->frequency);
|
||||
|
||||
// Fill out the options
|
||||
// Slave
|
||||
GtkToggleButton* checkButtonSlave = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_slave"));
|
||||
gtk_toggle_button_set_active(checkButtonSlave, m_config->slave_mode);
|
||||
// Shortcut
|
||||
GtkToggleButton* buttonShortcut = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_shortcut"));
|
||||
gtk_toggle_button_set_active(buttonShortcut, m_config->enable_sc);
|
||||
// Shortcut
|
||||
GtkToggleButton* buttonDio = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_dio"));
|
||||
gtk_toggle_button_set_active(buttonDio, m_config->scan_dio);
|
||||
// Trigger
|
||||
GtkToggleButton* buttonTrigger = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_trigger"));
|
||||
gtk_toggle_button_set_active(buttonTrigger, m_config->enable_trigger_line);
|
||||
// Mode
|
||||
GtkComboBox* comboBoxMode = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_mode"));
|
||||
gtk_combo_box_set_active(comboBoxMode, m_config->mode);
|
||||
|
||||
// Set all the blocks A-D to use the common ground and reference voltages
|
||||
for (uint32_t i = 0; i < GT_USBAMP_NUM_GROUND; ++i)
|
||||
{
|
||||
GtkToggleButton* buttonGnd = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, ("checkbutton_block_gnd" + std::to_string(i + 1)).c_str()));
|
||||
GtkToggleButton* buttonRef = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, ("checkbutton_block_ref" + std::to_string(i + 1)).c_str()));
|
||||
|
||||
gtk_toggle_button_set_active(buttonGnd, m_config->common_ground[i]);
|
||||
gtk_toggle_button_set_active(buttonRef, m_config->common_reference[i]);
|
||||
}
|
||||
|
||||
// Config each channel with the info from the configs, no need to check the filters and what not since they must have been correct to have been stored alongside eachother
|
||||
GtkListStore* listStore = GTK_LIST_STORE(gtk_builder_get_object(m_builder,"model_channel_config"));
|
||||
int i = 0;
|
||||
|
||||
GtkTreeModel* treeModelChannel = gtk_tree_view_get_model(treeView);
|
||||
GtkTreeIter itChannel;
|
||||
for (gboolean end = gtk_tree_model_get_iter_first(treeModelChannel, &itChannel); end; end = gtk_tree_model_iter_next(treeModelChannel, &itChannel), i++)
|
||||
{
|
||||
// Look through each value in the bandpass model and if there's one that has an id that matches, set it's text to the channel configs
|
||||
GtkTreeModel* treeModelBandpass = GTK_TREE_MODEL(gtk_builder_get_object(m_builder,"model_bandpass"));
|
||||
GtkTreeIter itBandpass;
|
||||
for (gboolean end2 = gtk_tree_model_get_iter_first(treeModelBandpass, &itBandpass); end2; end2 = gtk_tree_model_iter_next(treeModelBandpass, &itBandpass))
|
||||
{
|
||||
gchar* filterDesc;
|
||||
gint filterID;
|
||||
gtk_tree_model_get(treeModelBandpass, &itBandpass, 0, &filterDesc, 1, &filterID, -1);
|
||||
|
||||
// If the id is the same as the one in the config, we've found the filter that was set last time
|
||||
if (filterID == m_config->bandpass[i]) { gtk_list_store_set(listStore, &itChannel, BandpassColumn, filterDesc, BandpassIdColumn, filterID, -1); }
|
||||
|
||||
// Free the string now that we're finished with it
|
||||
g_free(filterDesc);
|
||||
}
|
||||
|
||||
// Look through each value in the notch model and if there's one that has an id that matches, set it's text to the channel configs
|
||||
GtkTreeModel* treeModelNotch = GTK_TREE_MODEL(gtk_builder_get_object(m_builder,"model_notch"));
|
||||
GtkTreeIter iterNotch;
|
||||
for (gboolean end2 = gtk_tree_model_get_iter_first(treeModelNotch, &iterNotch); end2; end2 = gtk_tree_model_iter_next(treeModelNotch, &iterNotch))
|
||||
{
|
||||
gchar* filterDesc;
|
||||
gint filterID;
|
||||
gtk_tree_model_get(treeModelNotch, &iterNotch, 0, &filterDesc, 1, &filterID, -1);
|
||||
|
||||
// If the id is the same as the one in the config, we've found the filter that was set last time
|
||||
if (filterID == m_config->notch[i]) { gtk_list_store_set(listStore, &itChannel, NotchColumn, filterDesc, NotchIdColumn, filterID, -1); }
|
||||
|
||||
// Free the string now that we're finished with it
|
||||
g_free(filterDesc);
|
||||
}
|
||||
|
||||
// And just straight out set the bipolar channel config
|
||||
gtk_list_store_set(listStore, &itChannel, BipolarColumn, gint(m_config->bipolar[i] == GT_BIPOLAR_DERIVATION_NONE ? 0 : m_config->bipolar[i]), -1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CConfigurationGTecGUSBampLinux::OnButtonApplyConfigPressed(ChannelTreeViewColumn type)
|
||||
{
|
||||
// Get the tree view widget
|
||||
GtkTreeView* treeView = GTK_TREE_VIEW(gtk_builder_get_object(m_builder,"treeview_channel_config"));
|
||||
|
||||
// Now get it's model, both as a list store so we can set the entries and it's "base class" tree model so we can iterate through it
|
||||
GtkTreeModel* treeModel = gtk_tree_view_get_model(treeView);
|
||||
GtkListStore* listStore = GTK_LIST_STORE(gtk_builder_get_object(m_builder,"model_channel_config"));
|
||||
|
||||
// Also get the subset of paths that are selected, if any
|
||||
GtkTreeSelection* selection = gtk_tree_view_get_selection(treeView);
|
||||
|
||||
// Iterate through them and set the fields
|
||||
GtkTreeIter iter;
|
||||
for (gboolean end = gtk_tree_model_get_iter_first(treeModel, &iter); end; end = gtk_tree_model_iter_next(treeModel, &iter))
|
||||
{
|
||||
// If the given row is selected
|
||||
if (gtk_tree_selection_iter_is_selected(selection, &iter))
|
||||
{
|
||||
// Fill in the bipolar field with the spin button contents if the bipolar apply button was pressed
|
||||
if (type == BipolarColumn)
|
||||
{
|
||||
GtkSpinButton* button = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder,"spinbutton_bipolar"));
|
||||
gtk_list_store_set(listStore, &iter, BipolarColumn, gint(gtk_spin_button_get_value(button)), -1);
|
||||
}
|
||||
|
||||
// Fill in the filter field with the combobox contents and fill in a hidden field id with another hidden field in the combobox model that stores the filter's id
|
||||
gint filterId;
|
||||
GtkTreeIter selectedComboIter;
|
||||
|
||||
if (type == NotchColumn)
|
||||
{
|
||||
GtkComboBox* comboBox = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_notch"));
|
||||
|
||||
// Get the iterator for the active row in the combo box's model
|
||||
gtk_combo_box_get_active_iter(comboBox, &selectedComboIter);
|
||||
// Get the value of the id column of that row
|
||||
gtk_tree_model_get(gtk_combo_box_get_model(comboBox), &selectedComboIter, 1, &filterId, -1);
|
||||
// Put the combo box text in the tree view and the id value in the hidden column of the tree view's model
|
||||
gtk_list_store_set(listStore, &iter, NotchColumn, gtk_combo_box_get_active_text(comboBox), NotchIdColumn, filterId, -1);
|
||||
}
|
||||
|
||||
if (type == BandpassColumn)
|
||||
{
|
||||
GtkComboBox* comboBox = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_bandpass"));
|
||||
|
||||
// Get the iterator for the active row in the combo box's model
|
||||
gtk_combo_box_get_active_iter(comboBox, &selectedComboIter);
|
||||
// Get the value of the id column of that row
|
||||
gtk_tree_model_get(gtk_combo_box_get_model(comboBox), &selectedComboIter, 1, &filterId, -1);
|
||||
// Put the combo box text in the tree view and the id value in the hidden column of the tree view's model
|
||||
gtk_list_store_set(listStore, &iter, BandpassColumn, gtk_combo_box_get_active_text(comboBox), BandpassIdColumn, filterId, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We'll make this just check the impedance of the selcected box.
|
||||
void CConfigurationGTecGUSBampLinux::OnButtonCheckImpedanceClicked()
|
||||
{
|
||||
int impedance;
|
||||
GdkColor color;
|
||||
|
||||
// Get the name of the selected device
|
||||
GtkComboBox* comboBoxDevice = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,"combobox_device"));
|
||||
char* deviceName = gtk_combo_box_get_active_text(comboBoxDevice);
|
||||
|
||||
// This takes a while so we'll keep the user informed via the console - might have to put this in a thread at some point though
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Opening device [" << deviceName << "] for impedance check ...\n";
|
||||
|
||||
// Try opening the device
|
||||
if (GT_OpenDevice(deviceName))
|
||||
{
|
||||
// If that worked then for each channel
|
||||
for (uint32_t i = 0; i < (GT_USBAMP_NUM_ANALOG_IN + GT_USBAMP_NUM_REFERENCE); ++i)
|
||||
{
|
||||
// Reset the color so we don't get one impedance's color bleeding into the next
|
||||
color.red = color.green = color.blue = color.pixel = 0;
|
||||
|
||||
// Compile the string corresponding to the name of the widget displaying the impedance information
|
||||
std::string name = "entry_impedance" + std::to_string(i + 1);
|
||||
// Get the relevant text entry
|
||||
GtkEntry* text = GTK_ENTRY(gtk_builder_get_object(m_builder, name.c_str()));
|
||||
|
||||
if (strcmp(gtk_entry_get_text(text), "?") == 0)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Reading from channel " << i + 1 << "...\n";
|
||||
|
||||
// Try to get the impedance for each channel (channels 17 - 20 correspond to references A through D)
|
||||
if (GT_GetImpedance(deviceName, i + 1, &impedance))
|
||||
{
|
||||
// Convert the impedance into kohms
|
||||
impedance /= 1000;
|
||||
|
||||
// impedance is good
|
||||
if (impedance < LowImpedance) { color.green = 0xFFFF; }
|
||||
// impedance is moderate
|
||||
else if (impedance < ModerateImpedance) { color.red = color.green = 0xFFFF; }
|
||||
// impedance is high
|
||||
else if (impedance < HighImpedance) { color.red = 0xFFFF; }
|
||||
// impedance is so high that the channel is probably not connected
|
||||
else { color.blue = 0xFFFF; }
|
||||
|
||||
// If the impedance is larger than 100kohm just write NC
|
||||
std::string tmp = (impedance < HighImpedance) ? std::to_string(impedance) : "NC";
|
||||
|
||||
// Set the text
|
||||
gtk_entry_set_text(text, tmp.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// If impedance reading fails, produce an error message and set the relevant cell to black
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Failed to read from channel " << i + 1 << "...\n";
|
||||
color.red = color.green = color.blue = 1;
|
||||
gtk_entry_set_text(text, "");
|
||||
}
|
||||
|
||||
// Then get it as just a widget so we can set the background colour
|
||||
GtkWidget* widget = GTK_WIDGET(gtk_builder_get_object(m_builder, name.c_str()));
|
||||
gtk_widget_modify_base(widget, GTK_STATE_NORMAL, &color);
|
||||
}
|
||||
}
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Closing device...\n";
|
||||
GT_CloseDevice(deviceName);
|
||||
}
|
||||
else { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not open device\n"; }
|
||||
|
||||
// Set the text back so the user knows the test is over
|
||||
GtkButton* buttonCheckImpedance = GTK_BUTTON(gtk_builder_get_object(m_builder,"button_check_impedance"));
|
||||
gtk_button_set_label(buttonCheckImpedance, "Check Impedance");
|
||||
}
|
||||
|
||||
// Open the device, get all the possible filters given the sampling frequency and we also blank out all the preset filters since they might not be valid or the same for the new frequency
|
||||
void CConfigurationGTecGUSBampLinux::OnComboboxSamplingFrequencyChanged()
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Warning << "Changing sampling frequency invalidates filters\n";
|
||||
UpdateFilters();
|
||||
}
|
||||
|
||||
// Same again
|
||||
void CConfigurationGTecGUSBampLinux::OnComboboxDeviceChanged()
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Warning << "Changing device invalidates filters\n";
|
||||
UpdateFilters();
|
||||
}
|
||||
|
||||
bool CConfigurationGTecGUSBampLinux::postConfigure()
|
||||
{
|
||||
if (m_applyConfig)
|
||||
{
|
||||
// Fill in all the parts of the config that differ from the default configuration we've set out in the CGTecGUSBampLinux constructor
|
||||
GtkComboBox* comboBoxDevice = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_device"));
|
||||
|
||||
// If there's any active text in the device combo box then set it to the
|
||||
if (char* deviceName = gtk_combo_box_get_active_text(comboBoxDevice)) { *m_deviceName = deviceName; }
|
||||
|
||||
// Get the sample rate and the number of channels
|
||||
GtkComboBox* comboBoxSampling = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_sampling_frequency"));
|
||||
m_config->sample_rate = (gt_size)strtol(gtk_combo_box_get_active_text(comboBoxSampling), nullptr, 10);
|
||||
|
||||
GtkSpinButton* buttonChannel = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder, "spinbutton_number_of_channels"));
|
||||
m_config->num_analog_in = gtk_spin_button_get_value(buttonChannel);
|
||||
|
||||
// Fill out the analog output configs
|
||||
// Shape
|
||||
GtkComboBox* comboBoxShape = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_analog_out_shape"));
|
||||
m_config->ao_config->shape = usbamp_analog_out_shape(gtk_combo_box_get_active(comboBoxShape));
|
||||
// Amplitude
|
||||
GtkSpinButton* buttonAmplitude = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder,"spinbutton_analog_out_amplitude"));
|
||||
m_config->ao_config->amplitude = gtk_spin_button_get_value(buttonAmplitude);
|
||||
// Offset
|
||||
GtkSpinButton* buttonOffset = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder,"spinbutton_analog_out_offset"));
|
||||
m_config->ao_config->offset = gtk_spin_button_get_value(buttonOffset);
|
||||
// Frequency
|
||||
GtkSpinButton* buttonFrequency = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder,"spinbutton_analog_out_frequency"));
|
||||
m_config->ao_config->frequency = gtk_spin_button_get_value(buttonFrequency);
|
||||
|
||||
// Fill out the options
|
||||
// Slave
|
||||
GtkToggleButton* buttonSlave = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_slave"));
|
||||
m_config->slave_mode = gtk_toggle_button_get_active(buttonSlave);
|
||||
// Shortcut
|
||||
GtkToggleButton* buttonShortcut = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_shortcut"));
|
||||
m_config->enable_sc = gtk_toggle_button_get_active(buttonShortcut);
|
||||
// Shortcut
|
||||
GtkToggleButton* buttonDio = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_dio"));
|
||||
m_config->scan_dio = gtk_toggle_button_get_active(buttonDio);
|
||||
// Trigger
|
||||
GtkToggleButton* buttonTrigger = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_trigger"));
|
||||
m_config->enable_trigger_line = gtk_toggle_button_get_active(buttonTrigger);
|
||||
// Mode
|
||||
GtkComboBox* comboBoxMode = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_mode"));
|
||||
m_config->mode = usbamp_device_mode(gtk_combo_box_get_active(comboBoxMode));
|
||||
|
||||
// Set all the blocks A-D to use the common ground and reference voltages
|
||||
for (uint32_t i = 0; i < GT_USBAMP_NUM_GROUND; ++i)
|
||||
{
|
||||
GtkToggleButton* buttonGnd = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, ("checkbutton_block_gnd" + std::to_string(i + 1)).c_str()));
|
||||
GtkToggleButton* buttonRef = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, ("checkbutton_block_ref" + std::to_string(i + 1)).c_str()));
|
||||
|
||||
m_config->common_ground[i] = gtk_toggle_button_get_active(buttonGnd);
|
||||
m_config->common_reference[i] = gtk_toggle_button_get_active(buttonRef);
|
||||
}
|
||||
|
||||
// Config each channel with the info
|
||||
GtkTreeView* treeView = GTK_TREE_VIEW(gtk_builder_get_object(m_builder,"treeview_channel_config"));
|
||||
GtkTreeModel* treeModel = gtk_tree_view_get_model(treeView);
|
||||
gint value;
|
||||
|
||||
GtkTreeIter iter;
|
||||
int i = 0;
|
||||
for (gboolean end = gtk_tree_model_get_iter_first(treeModel, &iter); end; end = gtk_tree_model_iter_next(treeModel, &iter), i++)
|
||||
{
|
||||
gtk_tree_model_get(treeModel, &iter, BipolarColumn, &value, -1);
|
||||
m_config->bipolar[i] = (value == 0 ? GT_BIPOLAR_DERIVATION_NONE : value);
|
||||
|
||||
gtk_tree_model_get(treeModel, &iter, NotchIdColumn, &value, -1);
|
||||
m_config->notch[i] = value;
|
||||
|
||||
gtk_tree_model_get(treeModel, &iter, BandpassIdColumn, &value, -1);
|
||||
m_config->bandpass[i] = value;
|
||||
}
|
||||
}
|
||||
if (!CConfigurationBuilder::postConfigure()) // normal header is filled (Subject ID, Age, Gender, channels, sampling frequency), ressources are realesed
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
#include "ovasIDriver.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdk.h>
|
||||
#include <gAPI.h>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CConfigurationGTecGUSBampLinux
|
||||
* \author Tom Stewart (University of Tsukuba)
|
||||
* \date Mon Feb 9 18:59:22 2015
|
||||
* \brief The CConfigurationGTecGUSBampLinux handles the configuration dialog specific to the g.tec g.USBamp for Linux device.
|
||||
*
|
||||
* TODO: details
|
||||
*
|
||||
* \sa CDriverGTecGUSBampLinux
|
||||
*/
|
||||
class CConfigurationGTecGUSBampLinux final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
// Thresholds for reporting on measured impedance, these are the same as the ones that the simulink driver uses
|
||||
static const int LowImpedance = 5, ModerateImpedance = 7, HighImpedance = 100;
|
||||
|
||||
enum ChannelTreeViewColumn { ChannelColumn = 0, BipolarColumn, NotchColumn, NotchIdColumn, BandpassColumn, BandpassIdColumn };
|
||||
|
||||
// you may have to add to your constructor some reference parameters
|
||||
// for example, a connection ID:
|
||||
CConfigurationGTecGUSBampLinux(IDriverContext& ctx, const char* gtkBuilderFilename, std::string* deviceName, gt_usbamp_config* config);
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
|
||||
void OnButtonApplyConfigPressed(ChannelTreeViewColumn type);
|
||||
void OnButtonCheckImpedanceClicked();
|
||||
void OnComboboxSamplingFrequencyChanged();
|
||||
void OnComboboxDeviceChanged();
|
||||
|
||||
protected:
|
||||
IDriverContext& m_driverCtx;
|
||||
|
||||
private:
|
||||
/*
|
||||
* Insert here all specific attributes, such as a connection ID.
|
||||
* use references to directly modify the corresponding attribute of the driver
|
||||
* Example:
|
||||
*/
|
||||
std::string* m_deviceName = nullptr;
|
||||
gt_usbamp_config* m_config;
|
||||
void UpdateFilters();
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
+351
@@ -0,0 +1,351 @@
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
|
||||
#include "ovasCDriverGTecGUSBampLinux.h"
|
||||
#include "ovasCConfigurationGTecGUSBampLinux.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
CDriverGTecGUSBampLinux::CDriverGTecGUSBampLinux(IDriverContext& ctx)
|
||||
: IDriver(ctx), m_settings("AcquisitionServer_Driver_GTecGUSBampLinux", m_driverCtx.getConfigurationManager()),
|
||||
m_nSamplePerSentBlock(0), m_sampleSend(nullptr), m_sampleReceive(nullptr), m_sampleBuffer(nullptr), m_currentSample(0), m_currentChannel(0)
|
||||
{
|
||||
// Default values
|
||||
m_header.setSamplingFrequency(512);
|
||||
m_header.setChannelCount(16);
|
||||
|
||||
m_config.ao_config = &m_analogOutConfig;
|
||||
|
||||
// Configure some defaults so the settings are reasonable as soon as the driver loads and the user can tweak them from there
|
||||
|
||||
// Configure the analog waveform to be created by the internal signal generator
|
||||
m_analogOutConfig.shape = GT_ANALOGOUT_SINE;
|
||||
m_analogOutConfig.frequency = 1;
|
||||
m_analogOutConfig.amplitude = 0;
|
||||
m_analogOutConfig.offset = 0;
|
||||
|
||||
// This pretty much has to be GT_NOS_AUTOSET, don't know why, so says the documentation
|
||||
m_config.number_of_scans = GT_NOS_AUTOSET;
|
||||
// Disable the trigger line, digital io scan, slave mode and the shortcut
|
||||
m_config.enable_trigger_line = m_config.scan_dio = m_config.slave_mode = m_config.enable_sc = GT_FALSE;
|
||||
|
||||
// Set the mode to just take readings
|
||||
m_config.mode = GT_MODE_NORMAL;
|
||||
|
||||
// Set all the blocks A-D to use the common ground and reference voltages
|
||||
for (uint32_t i = 0; i < GT_USBAMP_NUM_GROUND; ++i)
|
||||
{
|
||||
m_config.common_ground[i] = GT_TRUE;
|
||||
m_config.common_reference[i] = GT_TRUE;
|
||||
}
|
||||
|
||||
// Configure each input
|
||||
for (unsigned char i = 0; i < GT_USBAMP_NUM_ANALOG_IN; ++i)
|
||||
{
|
||||
// Should be from 1 - 16, specifies which channel to observe as input i
|
||||
m_config.analog_in_channel[i] = i + 1;
|
||||
// Don't use any of the filters on channel i
|
||||
m_config.bandpass[i] = GT_FILTER_NONE;
|
||||
// Don't use any of the notch filters on channel i
|
||||
m_config.notch[i] = GT_FILTER_NONE;
|
||||
// Don't use any of the other channels for bi-polar derivation
|
||||
m_config.bipolar[i] = GT_BIPOLAR_DERIVATION_NONE;
|
||||
}
|
||||
|
||||
// Now look for any connected devices. If any exist we'll set the name to the first one found
|
||||
char** devices = nullptr;
|
||||
size_t nDevice = 0;
|
||||
|
||||
// Refresh and get the list of currently connnected devices
|
||||
GT_UpdateDevices();
|
||||
nDevice = GT_GetDeviceListSize();
|
||||
devices = GT_GetDeviceList();
|
||||
|
||||
// If any devices were found at all, set the combo box to the first one listed
|
||||
if (nDevice) { m_deviceName = devices[0]; }
|
||||
|
||||
GT_FreeDeviceList(devices, nDevice);
|
||||
|
||||
// Now retrieve all those configs from the settings file if they are there to be found (don't need to worry about sample rate or channel number though since they're already in the header)
|
||||
m_settings.add("Header", &m_header);
|
||||
m_settings.add("DeviceName", static_cast<std::string*>(&m_deviceName));
|
||||
m_settings.add("Mode", static_cast<int*>(&m_config.mode));
|
||||
m_settings.add("EnableTrigger", static_cast<bool*>(&m_config.enable_trigger_line));
|
||||
m_settings.add("ScanDIO", static_cast<bool*>(&m_config.scan_dio));
|
||||
m_settings.add("SlaveMode", static_cast<bool*>(&m_config.slave_mode));
|
||||
m_settings.add("EnableShortcut", static_cast<bool*>(&m_config.enable_sc));
|
||||
m_settings.add("AnalogOutShape", static_cast<int*>(&m_analogOutConfig.shape));
|
||||
m_settings.add("AnalogOutFrequency", static_cast<int*>(&m_analogOutConfig.frequency));
|
||||
m_settings.add("AnalogOutAmplitude", static_cast<int*>(&m_analogOutConfig.amplitude));
|
||||
m_settings.add("AnalogOutOffset", static_cast<int*>(&m_analogOutConfig.offset));
|
||||
|
||||
// Set all the blocks A-D to use the common ground and reference voltages
|
||||
for (uint32_t i = 0; i < GT_USBAMP_NUM_GROUND; ++i)
|
||||
{
|
||||
std::stringstream gndConfigName, configName;
|
||||
gndConfigName << "CommonGround" << i;
|
||||
configName << "CommonReference" << i;
|
||||
m_settings.add(gndConfigName.str().c_str(), static_cast<bool*>(&m_config.common_ground[i]));
|
||||
m_settings.add(configName.str().c_str(), static_cast<bool*>(&m_config.common_reference[i]));
|
||||
}
|
||||
|
||||
// Configure each input
|
||||
for (uint32_t i = 0; i < GT_USBAMP_NUM_ANALOG_IN; ++i)
|
||||
{
|
||||
std::stringstream bandpassConfigName, notchConfigName, bipolarConfigName;
|
||||
bandpassConfigName << "Bandpass" << i;
|
||||
notchConfigName << "Notch" << i;
|
||||
bipolarConfigName << "Bipolar" << i;
|
||||
m_settings.add(bandpassConfigName.str().c_str(), static_cast<int*>(&m_config.bandpass[i]));
|
||||
m_settings.add(notchConfigName.str().c_str(), static_cast<int*>(&m_config.notch[i]));
|
||||
m_settings.add(bipolarConfigName.str().c_str(), static_cast<int*>(&m_config.bipolar[i]));
|
||||
}
|
||||
|
||||
// This restores saved settings if any, such as sampling rate
|
||||
m_settings.load();
|
||||
|
||||
// Set the sampling rate that may have been changed by load
|
||||
m_config.sample_rate = m_header.getSamplingFrequency();
|
||||
|
||||
// Number of channels that may have been changed by load
|
||||
m_config.num_analog_in = m_header.getChannelCount();
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDriverGTecGUSBampLinux::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected()) return false;
|
||||
if (!m_header.isChannelCountSet() || !m_header.isSamplingFrequencySet()) return false;
|
||||
|
||||
// If the scan digital inputs flag is set, the API will return one extra channel outside of the analog data requested, so we need to match that on the header
|
||||
if (m_config.scan_dio == GT_TRUE)
|
||||
{
|
||||
m_header.setChannelCount(m_config.num_analog_in + 1);
|
||||
m_header.setChannelName(m_config.num_analog_in, "Digital");
|
||||
}
|
||||
|
||||
// Allocate buffers for...
|
||||
|
||||
// Sending to OpenViBE
|
||||
m_sampleSend = new float[m_header.getChannelCount() * nSamplePerSentBlock];
|
||||
// Receiving from the hardware,
|
||||
m_sampleReceive = new float[m_header.getChannelCount() * nSamplePerSentBlock];
|
||||
// Storing the data so we pass it between the two threads - we're using the recommended buffer size put out by gtec, which is enormous
|
||||
m_sampleBuffer = new float[GT_USBAMP_RECOMMENDED_BUFFER_SIZE / sizeof(float)];
|
||||
|
||||
// Set up the queue to help pass the data out of the hardware thread
|
||||
m_sampleQueue.SetBuffer(m_sampleBuffer, m_header.getChannelCount() * m_header.getSamplingFrequency() / 8);
|
||||
|
||||
// If any of that allocation fails then give up. Not sure what setting it all to NULL is for, but we'll go with it.
|
||||
if (!m_sampleSend || !m_sampleReceive || !m_sampleBuffer)
|
||||
{
|
||||
delete[] m_sampleSend;
|
||||
delete[] m_sampleReceive;
|
||||
delete[] m_sampleBuffer;
|
||||
m_sampleSend = m_sampleReceive = m_sampleBuffer = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apparently this causes the API to print debug info to the console, I'm yet to see any though
|
||||
GT_ShowDebugInformation(GT_TRUE);
|
||||
|
||||
// Try to open the device with the configured name, let the user know how it goes
|
||||
if (!GT_OpenDevice(m_deviceName.c_str()))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not open device: " << m_deviceName << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GT_SetConfiguration(m_deviceName.c_str(), &m_config))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not apply configuration to device: " << m_deviceName << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
GT_SetDataReadyCallBack(m_deviceName.c_str(), &OnDataReady, static_cast<void*>(this));
|
||||
|
||||
// Saves parameters
|
||||
m_callback = &callback;
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGUSBampLinux::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) return false;
|
||||
if (m_driverCtx.isStarted()) return false;
|
||||
|
||||
// ...
|
||||
// request hardware to start
|
||||
// sending data
|
||||
// ...
|
||||
|
||||
// Need to reset these in case the device is stopped mid-sample and then started again
|
||||
m_currentChannel = m_currentSample = 0;
|
||||
|
||||
GT_StartAcquisition(m_deviceName.c_str());
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Acquisition Started\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// So when the gtec buffer grows larger than a send buffer, copy it all to a send buffer sized array, then copy it into the actual send buffer one by one.
|
||||
bool CDriverGTecGUSBampLinux::loop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) return false;
|
||||
if (!m_driverCtx.isStarted()) return true;
|
||||
|
||||
const CStimulationSet stimSet;
|
||||
|
||||
// while there's new data available on the queue
|
||||
while (m_sampleQueue.Avail())
|
||||
{
|
||||
// take it off and put it in the appropriate element in the outgoing buffer
|
||||
m_sampleQueue.Get(m_sampleSend + m_currentChannel * m_nSamplePerSentBlock + m_currentSample, 1);
|
||||
|
||||
// Increment the current channel
|
||||
m_currentChannel++;
|
||||
|
||||
// If the current channel reaches the channel count then move to the next sample
|
||||
if (m_currentChannel == m_header.getChannelCount())
|
||||
{
|
||||
m_currentChannel = 0;
|
||||
m_currentSample++;
|
||||
}
|
||||
|
||||
// If the sample count reaches the number per sent block, then send it and start again
|
||||
if (m_currentSample == m_nSamplePerSentBlock)
|
||||
{
|
||||
m_callback->setSamples(m_sampleSend); // it looks as if this copies the buffer, so we're free modify it as soon as it executes
|
||||
|
||||
// When your sample buffer is fully loaded,
|
||||
// it is advised to ask the acquisition server
|
||||
// to correct any drift in the acquisition automatically.
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
|
||||
// ...
|
||||
// receive events from hardware
|
||||
// and put them the correct way in a CStimulationSet object
|
||||
//...
|
||||
m_callback->setStimulationSet(stimSet);
|
||||
|
||||
m_currentSample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGUSBampLinux::stop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) return false;
|
||||
if (!m_driverCtx.isStarted()) return false;
|
||||
|
||||
// ...
|
||||
// request the hardware to stop
|
||||
// sending data
|
||||
// ...
|
||||
GT_StopAcquisition(m_deviceName.c_str());
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Acquisition Stopped";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGUSBampLinux::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) return false;
|
||||
if (m_driverCtx.isStarted()) return false;
|
||||
|
||||
GT_CloseDevice(m_deviceName.c_str());
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Closed Device: " << m_deviceName << "\n";
|
||||
|
||||
// ...
|
||||
// uninitialize hardware here
|
||||
// ...
|
||||
m_sampleQueue.SetBuffer(nullptr, 0);
|
||||
|
||||
delete[] m_sampleSend;
|
||||
delete[] m_sampleBuffer;
|
||||
delete[] m_sampleReceive;
|
||||
|
||||
m_sampleSend = m_sampleReceive = m_sampleBuffer = nullptr;
|
||||
|
||||
m_callback = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDriverGTecGUSBampLinux::isConfigurable()
|
||||
{
|
||||
return true; // change to false if your device is not configurable
|
||||
}
|
||||
|
||||
bool CDriverGTecGUSBampLinux::configure()
|
||||
{
|
||||
// Change this line if you need to specify some references to your driver attribute that need configuration, e.g. the connection ID.
|
||||
CConfigurationGTecGUSBampLinux config(m_driverCtx, Directories::getDataDir() + "/applications/acquisition-server/interface-GTecGUSBampLinux.ui", &m_deviceName, &m_config);
|
||||
|
||||
if (!config.configure(m_header)) { return false; }
|
||||
|
||||
m_header.setChannelCount(m_config.num_analog_in);
|
||||
m_header.setSamplingFrequency(m_config.sample_rate);
|
||||
|
||||
m_settings.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*void AcquisitionServer::OnDataReady(void *param)
|
||||
{
|
||||
// Like the 'this' pointer, but for a friend function
|
||||
CDriverGTecGUSBampLinux *that = (CDriverGTecGUSBampLinux*)param;
|
||||
|
||||
// This is pretty tricky to know in advance, the API decides how many values to spit out depnding on a few factors it seems.
|
||||
// We'll allocate a reasonble buffer and call GT_GetData as many times as is necessary
|
||||
while(size_t nSamplesToRead = GT_GetSamplesAvailable(that->m_deviceName.c_str()))
|
||||
{
|
||||
// If there are more samples than will fit in the buffer, just get as many as possible and we can get the rest next iteration
|
||||
if(nSamplesToRead > CDriverGTecGUSBampLinux::ReceiveBufferSize * sizeof(float))
|
||||
nSamplesToRead = CDriverGTecGUSBampLinux::ReceiveBufferSize * sizeof(float);
|
||||
|
||||
// Get the data -- TODO: rewrite this algorithm such that we can copy directly from GT_GetData into the buffer read in the loop() function - is this a bug?? Maybe, but probably not since the calibration mode was always perfect
|
||||
GT_GetData(that->m_deviceName.c_str(), reinterpret_cast<unsigned char*>(that->m_sampleReceive), nSamplesToRead);
|
||||
|
||||
// Put it on the sample queue
|
||||
that->m_sampleQueue.Put(that->m_sampleReceive, nSamplesToRead / sizeof(float));
|
||||
}
|
||||
}*/
|
||||
|
||||
void AcquisitionServer::OnDataReady(void* param)
|
||||
{
|
||||
// Like the 'this' pointer, but for a friend function
|
||||
CDriverGTecGUSBampLinux* that = static_cast<CDriverGTecGUSBampLinux*>(param);
|
||||
|
||||
// This is pretty tricky to know in advance, the API decides how many values to spit out depnding on a few factors it seems.
|
||||
// We'll allocate a reasonble buffer and call GT_GetData as many times as is necessary
|
||||
while (size_t samplesToRead = GT_GetSamplesAvailable(that->m_deviceName.c_str()))
|
||||
{
|
||||
// If there are more samples than will fit in the buffer, just get as many as possible and we can get the rest next iteration
|
||||
if (samplesToRead > that->m_sampleQueue.FreeContiguous() * sizeof(float)) samplesToRead = that->m_sampleQueue.FreeContiguous() * sizeof(float);
|
||||
|
||||
// Get the data and put it directly onto the queue
|
||||
GT_GetData(that->m_deviceName.c_str(), reinterpret_cast<unsigned char*>(that->m_sampleQueue.NextFreeAddress()), samplesToRead);
|
||||
|
||||
// Pad the queue so it recognises how much data was just added to it
|
||||
that->m_sampleQueue.Pad(samplesToRead / sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
#include <gAPI.h>
|
||||
#include "Queue.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
void OnDataReady(void* param);
|
||||
|
||||
/**
|
||||
* \class CDriverGTecGUSBampLinux
|
||||
* \author Tom Stewart (University of Tsukuba)
|
||||
* \date Mon Feb 9 18:59:22 2015
|
||||
* \brief The CDriverGTecGUSBampLinux allows the acquisition server to acquire data from a g.tec g.USBamp from Linux.
|
||||
*
|
||||
* \sa CConfigurationGTecGUSBampLinux
|
||||
*/
|
||||
class CDriverGTecGUSBampLinux final : public IDriver
|
||||
{
|
||||
static const int ReceiveBufferSize = 8192;
|
||||
public:
|
||||
friend void OnDataReady(void* param);
|
||||
|
||||
explicit CDriverGTecGUSBampLinux(IDriverContext& ctx);
|
||||
~CDriverGTecGUSBampLinux() override {}
|
||||
const char* getName() override { return "g.tec g.USBamp Linux BCI-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;
|
||||
bool configure() override;
|
||||
const IHeader* getHeader() override { return &m_header; }
|
||||
|
||||
bool isFlagSet(const EDriverFlag flag) const override { return flag == EDriverFlag::IsUnstable; }
|
||||
|
||||
protected:
|
||||
|
||||
SettingsHelper m_settings;
|
||||
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
|
||||
// Replace this generic Header with any specific header you might have written
|
||||
CHeader m_header;
|
||||
|
||||
uint32_t m_nSamplePerSentBlock;
|
||||
|
||||
float *m_sampleSend, *m_sampleReceive, *m_sampleBuffer;
|
||||
Queue<float> m_sampleQueue;
|
||||
private:
|
||||
|
||||
/*
|
||||
* Insert here all specific attributes, such as USB port number or device ID.
|
||||
*/
|
||||
std::string m_deviceName;
|
||||
gt_usbamp_config m_config;
|
||||
gt_usbamp_analog_out_config m_analogOutConfig;
|
||||
|
||||
// Keeps track of where we are with filling up the buffer
|
||||
uint32_t m_currentSample, m_currentChannel;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
|
||||
+151
@@ -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;
|
||||
};
|
||||
+999
@@ -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"><big>Connect to Common Ground</big></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"><big>Connect to Common Reference</big></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"><big>Notch filter</big>
|
||||
|
||||
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"><big>Band Pass filter</big>
|
||||
|
||||
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>
|
||||
+401
@@ -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
|
||||
+53
@@ -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
|
||||
+1089
File diff suppressed because it is too large
Load Diff
+183
@@ -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
|
||||
+458
@@ -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
|
||||
+144
@@ -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
|
||||
+410
@@ -0,0 +1,410 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy project-wide -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="value">1</property>
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">8</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="GtkListStore" id="model1">
|
||||
<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="model2">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">/dev/rfcomm0</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">/dev/rfcomm1</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model3">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">256</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
|
||||
<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>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox">
|
||||
<property name="visible">True</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>
|
||||
<object class="GtkLabel" id="label_title">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">g.Tec gMobi Lab+</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox">
|
||||
<property name="visible">True</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="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">6</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="width_chars">0</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="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="GtkComboBox" id="combobox_gender">
|
||||
<property name="visible">True</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">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_age">
|
||||
<property name="visible">True</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="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="GtkSpinButton" id="spinbutton_age">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_identifier">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_identifier">
|
||||
<property name="visible">True</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_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="xpad">5</property>
|
||||
<property name="ypad">5</property>
|
||||
<property name="label" translatable="yes">Port name :</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="selectable">True</property>
|
||||
<property name="width_chars">10</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="text" translatable="yes">/dev/rfcomm0</property>
|
||||
</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="GtkLabel" id="label_testmode">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Use test mode :</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="checkbutton_testmode">
|
||||
<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="GtkComboBoxEntry" id="combobox_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">model2</property>
|
||||
<property name="active">1</property>
|
||||
<property name="button_sensitivity">on</property>
|
||||
<property name="text_column">0</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">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="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">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="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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area">
|
||||
<property name="visible">True</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_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_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="pack_type">end</property>
|
||||
<property name="position">0</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>
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
To use the gTec gMOBIlab+ device, it is necessary to connect it via Blutooth to the computer.
|
||||
First the computer has to recognize the new peripheral (the gTec gMOBIlab+) and then the computer has to be connected to it.
|
||||
|
||||
The command on linux is :
|
||||
|
||||
rfcomm connect # xx:xx:xx:xx:xx:xx
|
||||
|
||||
Where # has to be replaced by a port number (0 if you want to connect to /dev/rfcomm0) and xx:xx:xx:xx:xx:xx is the mac address of the gTec module.
|
||||
Once the device is connected, the OpenViBE-aquisition-server file can be run. A pop up appears with a menu containing "gTec gMOBIlab+", that has to be selected.
|
||||
After checking in the properties that the port name is the same as the one you are connected to (default name is /dev/rfcomm0), it is possible to try to connect.
|
||||
|
||||
If the connection to the server fails, the state of the gMOBIlab+ should be checked : the diode of the gMOBIlab+ device should flash on and off.
|
||||
|
||||
The frequency of the gTecMobiLab+ is 256 Hz.
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* The gMobilab driver was contributed
|
||||
* by Lucie Daubigney from Supelec Metz
|
||||
*/
|
||||
|
||||
#include "ovasCConfigurationGTecGMobiLabPlus.h"
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
CConfigurationGTecGMobiLabPlus::CConfigurationGTecGMobiLabPlus(const char* gtkBuilderFilename, std::string& portName, bool& testMode)
|
||||
: CConfigurationBuilder(gtkBuilderFilename), m_portName(portName), m_testMode(testMode) {}
|
||||
|
||||
bool CConfigurationGTecGMobiLabPlus::preConfigure()
|
||||
{
|
||||
if (!CConfigurationBuilder::preConfigure()) { return false; }
|
||||
|
||||
GtkEntry* port = GTK_ENTRY(gtk_builder_get_object(m_builder, "entry_port"));
|
||||
gtk_entry_set_text(port, m_portName.c_str());
|
||||
|
||||
GtkToggleButton* testMode = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_testmode"));
|
||||
gtk_toggle_button_set_active(testMode, m_testMode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CConfigurationGTecGMobiLabPlus::postConfigure()
|
||||
{
|
||||
if (m_applyConfig)
|
||||
{
|
||||
GtkEntry* port = GTK_ENTRY(gtk_builder_get_object(m_builder, "entry_port"));
|
||||
m_portName = gtk_entry_get_text(port);
|
||||
|
||||
GtkToggleButton* testMode = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_testmode"));
|
||||
m_testMode = (gtk_toggle_button_get_active(testMode) > 0);
|
||||
}
|
||||
|
||||
return CConfigurationBuilder::postConfigure();
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* The gMobilab driver was contributed
|
||||
* by Lucie Daubigney from Supelec Metz
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
|
||||
// #ifdef TARGET_OS_Windows
|
||||
// #include <Windows.h>
|
||||
// #endif
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
class CConfigurationGTecGMobiLabPlus final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
CConfigurationGTecGMobiLabPlus(const char* gtkBuilderFilename, std::string& portName, bool& testMode);
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
|
||||
private:
|
||||
|
||||
std::string& m_portName;
|
||||
bool& m_testMode;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
/**
|
||||
* The gMobilab Linux driver was contributed by Lucie Daubigney from Supelec Metz
|
||||
*
|
||||
* Windows compatibility + gusbamp coexistence added by Jussi T. Lindgren / Inria
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ovasCDriverGTecGMobiLabPlus.h"
|
||||
#include "ovasCConfigurationGTecGMobiLabPlus.h"
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
|
||||
#include "ovasCDriverGTecGMobiLabPlusPrivate.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#if defined(TARGET_OS_Linux)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
static const uint32_t N_ACQUIRED_CHANNEL = 8;
|
||||
|
||||
//constructor
|
||||
CDriverGTecGMobiLabPlus::CDriverGTecGMobiLabPlus(IDriverContext& ctx)
|
||||
: IDriver(ctx)
|
||||
, m_settings("AcquisitionServer_Driver_GTecMobiLabPlus", m_driverCtx.getConfigurationManager())
|
||||
, m_library(nullptr)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "CDriverGTecGMobiLabPlus::CDriverGTecGMobiLabPlus\n";
|
||||
|
||||
m_gTec = new CDriverGTecGMobiLabPlusPrivate();
|
||||
|
||||
m_header = new CHeader();
|
||||
m_header->setSamplingFrequency(256);
|
||||
m_header->setChannelCount(8);
|
||||
|
||||
m_gTec->m_oBuffer.pBuffer = nullptr;
|
||||
m_gTec->m_oBuffer.size = 0;
|
||||
m_gTec->m_oBuffer.validPoints = 0;
|
||||
#if defined(TARGET_OS_Windows)
|
||||
m_portName = "//./COM1";
|
||||
#else
|
||||
m_portName="/dev/rfcomm0";
|
||||
#endif
|
||||
|
||||
//initialisation of the analog channels of the gTec module : by default no analog exchange are allowed
|
||||
m_gTec->m_analogIn.ain1 = false;
|
||||
m_gTec->m_analogIn.ain2 = false;
|
||||
m_gTec->m_analogIn.ain3 = false;
|
||||
m_gTec->m_analogIn.ain4 = false;
|
||||
m_gTec->m_analogIn.ain5 = false;
|
||||
m_gTec->m_analogIn.ain6 = false;
|
||||
m_gTec->m_analogIn.ain7 = false;
|
||||
m_gTec->m_analogIn.ain8 = false;
|
||||
|
||||
m_settings.add("Header", m_header);
|
||||
m_settings.add("PortName", &m_portName);
|
||||
m_settings.add("TestMode", &m_testMode);
|
||||
m_settings.load();
|
||||
}
|
||||
|
||||
CDriverGTecGMobiLabPlus::~CDriverGTecGMobiLabPlus()
|
||||
{
|
||||
delete m_header;
|
||||
delete m_gTec;
|
||||
}
|
||||
|
||||
void CDriverGTecGMobiLabPlus::release()
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "CDriverGTecGMobiLabPlus::release\n";
|
||||
delete this;
|
||||
}
|
||||
|
||||
const char* CDriverGTecGMobiLabPlus::getName()
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "CDriverGTecGMobiLabPlus::getName\n";
|
||||
return "g.tec gMOBIlab+";
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
/*
|
||||
* configuration
|
||||
*/
|
||||
|
||||
bool CDriverGTecGMobiLabPlus::isConfigurable()
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "CDriverGTecGMobiLabPlus::isConfigurable\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGMobiLabPlus::configure()
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "CDriverGTecGMobiLabPlus::configure\n";
|
||||
|
||||
// We use CConfigurationGTecMobilabPlus configuration which is a class that inheritate from the CConfigurationBuilder class
|
||||
// The difference between these two classes is the addition of a member of class. This member allows to change the port where is connected the device.
|
||||
CConfigurationGTecGMobiLabPlus config(Directories::getDataDir() + "/applications/acquisition-server/interface-GTec-GMobiLabPlus.ui", m_portName,
|
||||
m_testMode);
|
||||
|
||||
// We configure the Header with it...
|
||||
if (!config.configure(*m_header)) { return false; }
|
||||
|
||||
if (m_header->getChannelCount() > N_ACQUIRED_CHANNEL) { m_header->setChannelCount(N_ACQUIRED_CHANNEL); }
|
||||
|
||||
m_settings.save();
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Debug << "Port name after configuration " << m_portName << " \n";
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
#if defined(TARGET_OS_Linux)
|
||||
#define GetProcAddress dlsym
|
||||
#define FreeLibrary dlclose
|
||||
#endif
|
||||
|
||||
bool CDriverGTecGMobiLabPlus::registerLibraryFunctions()
|
||||
{
|
||||
// Lets open the DLL
|
||||
#if defined(TARGET_OS_Windows)
|
||||
m_library = LoadLibrary("gMOBIlabplus.dll");
|
||||
#else
|
||||
m_library = dlopen("libgmobilabplusapi.so", RTLD_LAZY);
|
||||
#endif
|
||||
if (!m_library)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "CDriverGTecGMobiLabPlus:: Unable to open gMOBIlabplus.dll\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_gTec->m_fOpenDevice = CDriverGTecGMobiLabPlusPrivate::OV_GT_OpenDevice(GetProcAddress(m_library, "GT_OpenDevice"));
|
||||
m_gTec->m_fCloseDevice = CDriverGTecGMobiLabPlusPrivate::OV_GT_CloseDevice(GetProcAddress(m_library, "GT_CloseDevice"));
|
||||
m_gTec->m_fSetTestmode = CDriverGTecGMobiLabPlusPrivate::OV_GT_SetTestmode(GetProcAddress(m_library, "GT_SetTestmode"));
|
||||
m_gTec->m_fStartAcquisition = CDriverGTecGMobiLabPlusPrivate::OV_GT_StartAcquisition(GetProcAddress(m_library, "GT_StartAcquisition"));
|
||||
m_gTec->m_fGetData = CDriverGTecGMobiLabPlusPrivate::OV_GT_GetData(GetProcAddress(m_library, "GT_GetData"));
|
||||
m_gTec->m_fInitChannels = CDriverGTecGMobiLabPlusPrivate::OV_GT_InitChannels(GetProcAddress(m_library, "GT_InitChannels"));
|
||||
m_gTec->m_fStopAcquisition = CDriverGTecGMobiLabPlusPrivate::OV_GT_StopAcquisition(GetProcAddress(m_library, "GT_StopAcquisition"));
|
||||
m_gTec->m_fGetLastError = CDriverGTecGMobiLabPlusPrivate::OV_GT_GetLastError(GetProcAddress(m_library, "GT_GetLastError"));
|
||||
m_gTec->m_fTranslateErrorCode = CDriverGTecGMobiLabPlusPrivate::OV_GT_TranslateErrorCode(GetProcAddress(m_library, "GT_TranslateErrorCode"));
|
||||
|
||||
if (!m_gTec->m_fOpenDevice || !m_gTec->m_fCloseDevice || !m_gTec->m_fSetTestmode
|
||||
|| !m_gTec->m_fStartAcquisition || !m_gTec->m_fGetData || !m_gTec->m_fInitChannels
|
||||
|| !m_gTec->m_fStopAcquisition || !m_gTec->m_fGetLastError || !m_gTec->m_fTranslateErrorCode)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "CDriverGTecGMobiLabPlus:: Unable to find all the required functions from the gMOBIlabplus.dll\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* initialisation
|
||||
*/
|
||||
bool CDriverGTecGMobiLabPlus::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "CDriverGTecGMobiLabPlus::initialize\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Debug << "Port name after initialisation " << m_portName << "\n";
|
||||
|
||||
if (m_driverCtx.isConnected()) { return false; }
|
||||
|
||||
if (!m_header->isChannelCountSet() || !m_header->isSamplingFrequencySet())
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Either channel count or sampling frequency is not set\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!registerLibraryFunctions()) { return false; }
|
||||
|
||||
const size_t nChannel = m_header->getChannelCount();
|
||||
|
||||
// analog exchanges allowed on the first "nChannel" channels:
|
||||
for (uint32_t i = 1; i <= nChannel; ++i) { allowAnalogInputs(i); }
|
||||
|
||||
// then buffer of type _BUFFER_ST built to store acquired samples.
|
||||
m_gTec->m_oBuffer.pBuffer = new short int[nChannel
|
||||
];//allocate enough space for the buffer m_oBuffer.pBuffer ; only one set of mesures is acquired (channel 1 to 8) in a row
|
||||
m_gTec->m_oBuffer.size = nChannel * sizeof(short int);
|
||||
m_gTec->m_oBuffer.validPoints = 0;
|
||||
|
||||
#if defined(TARGET_OS_Windows)
|
||||
m_gTec->m_oOverlap.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
m_gTec->m_oOverlap.Offset = 0;
|
||||
m_gTec->m_oOverlap.OffsetHigh = 0;
|
||||
#endif
|
||||
|
||||
// allocates enough space for m_sample
|
||||
m_sample = new float[nSamplePerSentBlock * nChannel];
|
||||
|
||||
// if there is a problem while creating the two arrays
|
||||
if (!m_gTec->m_oBuffer.pBuffer || !m_sample)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Memory allocation problem\n";
|
||||
delete [] m_gTec->m_oBuffer.pBuffer;
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
m_gTec->m_oBuffer.pBuffer = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// initializes hardware and get
|
||||
// available header information
|
||||
// from it
|
||||
#if defined(TARGET_OS_Windows)
|
||||
m_gTec->m_device = m_gTec->m_fOpenDevice(LPSTR(m_portName.c_str()));
|
||||
#else
|
||||
m_gTec->m_device = m_gTec->m_fOpenDevice(m_portName.c_str());
|
||||
#endif
|
||||
if (m_gTec->m_device == nullptr)
|
||||
{
|
||||
#if defined(TARGET_OS_Windows)
|
||||
UINT errorCode = 0;
|
||||
#else
|
||||
uint32_t errorCode = 0;
|
||||
#endif
|
||||
_ERRSTR error;
|
||||
m_gTec->m_fGetLastError(&errorCode);
|
||||
m_gTec->m_fTranslateErrorCode(&error, errorCode);
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unable to connect to [" << m_portName << "], error code " << errorCode << ": '" << error.Error
|
||||
<< "'\n";
|
||||
delete [] m_gTec->m_oBuffer.pBuffer;
|
||||
delete [] m_sample;
|
||||
return false;
|
||||
}
|
||||
|
||||
// saves parameters
|
||||
m_callback = &callback;
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGMobiLabPlus::uninitialize()
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "CDriverGTecGMobiLabPlus::uninitialize\n";
|
||||
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// uninitializes hardware here
|
||||
if (!m_gTec->m_fCloseDevice(m_gTec->m_device))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "GT_CloseDevice() failed\n";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
// frees memory
|
||||
delete [] m_sample;
|
||||
delete [] m_gTec->m_oBuffer.pBuffer;
|
||||
|
||||
m_sample = nullptr;
|
||||
m_gTec->m_oBuffer.pBuffer = nullptr;
|
||||
m_callback = nullptr;
|
||||
|
||||
// uninitialisation of the analog channels : set valus to default ones
|
||||
m_gTec->m_analogIn.ain1 = false;
|
||||
m_gTec->m_analogIn.ain2 = false;
|
||||
m_gTec->m_analogIn.ain3 = false;
|
||||
m_gTec->m_analogIn.ain4 = false;
|
||||
m_gTec->m_analogIn.ain5 = false;
|
||||
m_gTec->m_analogIn.ain6 = false;
|
||||
m_gTec->m_analogIn.ain7 = false;
|
||||
m_gTec->m_analogIn.ain8 = false;
|
||||
|
||||
if (m_library)
|
||||
{
|
||||
FreeLibrary(m_library);
|
||||
m_library = nullptr;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
const IHeader* CDriverGTecGMobiLabPlus::getHeader() { return m_header; }
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
/*
|
||||
* acquisition
|
||||
*/
|
||||
|
||||
bool CDriverGTecGMobiLabPlus::start()
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "CDriverGTecGMobiLabPlus::start\n";
|
||||
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// we use none of the digital inputs/outputs
|
||||
_DIO digitalInOut;
|
||||
digitalInOut.dio1_enable = false;
|
||||
digitalInOut.dio2_enable = false;
|
||||
digitalInOut.dio3_enable = false;
|
||||
digitalInOut.dio4_enable = false;
|
||||
digitalInOut.dio5_enable = false;
|
||||
digitalInOut.dio6_enable = false;
|
||||
digitalInOut.dio7_enable = false;
|
||||
digitalInOut.dio8_enable = false;
|
||||
|
||||
// channel initialisation
|
||||
if (!m_gTec->m_fInitChannels(m_gTec->m_device, m_gTec->m_analogIn, digitalInOut))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "GT_InitChannels failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// are we interested in test signal?
|
||||
m_gTec->m_fSetTestmode(m_gTec->m_device, m_testMode);
|
||||
|
||||
// requests hardware to start sending data
|
||||
if (!m_gTec->m_fStartAcquisition(m_gTec->m_device))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "GT_StartAcquisition failed\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGMobiLabPlus::loop()
|
||||
{
|
||||
// m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "CDriverGTecGMobiLabPlus::loop\n";
|
||||
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return true; }
|
||||
|
||||
const size_t nChannel = m_header->getChannelCount();
|
||||
|
||||
// only "l-nChannel" measures corresponding to one measure per channel are acquired in a row with the function GT_GetData()
|
||||
// these measures are stored in m_oBuffer.pBuffer[]
|
||||
// the acquisition is reapeted m_sampleCountPerSendBlock times to fill in the array "m_sample"
|
||||
for (uint32_t i = 0; i < m_nSamplePerSentBlock; ++i)
|
||||
{
|
||||
#if defined(TARGET_OS_Windows)
|
||||
if (!m_gTec->m_fGetData(m_gTec->m_device, &m_gTec->m_oBuffer, &m_gTec->m_oOverlap))// receive samples from hardware (one per channel)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "GT_GetData failed\n";
|
||||
return false;
|
||||
}
|
||||
if (WaitForSingleObject(m_gTec->m_oOverlap.hEvent, 1000) == WAIT_TIMEOUT)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Warning << "Timeout in reading from the device\n";
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (!m_gTec->m_fGetData(m_gTec->m_device, &m_gTec->m_oBuffer))// receive samples from hardware (one per channel)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "GT_GetData failed\n";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// here the "nChannel" measures just acquired are stored in m_sample not to be deleted by the next acquisition
|
||||
// m_driverCtx.getLogManager() << Kernel::LogLevel_Debug << "Here are the " << nChannel << " measures of the " << i << " th sample\n" << Kernel::LogLevel_Debug;
|
||||
for (uint32_t j = 0; j < nChannel; ++j)
|
||||
{
|
||||
// m_driverCtx.getLogManager() << (m_oBuffer.pBuffer[j]*0.5)/32768. << " ";
|
||||
//operation made to modify the short int in a number between 0 and 500mV (in Volt)
|
||||
m_sample[m_nSamplePerSentBlock * j + i] = float((m_gTec->m_oBuffer.pBuffer[j] * 0.5) / 32768.);
|
||||
}
|
||||
// m_driverCtx.getLogManager() << "\n";
|
||||
}
|
||||
|
||||
// the buffer is full : it is send to the acquisition server
|
||||
m_callback->setSamples(m_sample, m_nSamplePerSentBlock);
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGMobiLabPlus::stop()
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "CDriverGTecGMobiLabPlus::stop\n";
|
||||
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// requests the hardware to stop sending data
|
||||
if (!m_gTec->m_fStopAcquisition(m_gTec->m_device))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "GT_StopAcquisition failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// this function allows exchanges of data on the "index" channel
|
||||
// function used to initialize the analog inputs according to the number "nChannel"
|
||||
void CDriverGTecGMobiLabPlus::allowAnalogInputs(uint32_t index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 8: m_gTec->m_analogIn.ain8 = true;
|
||||
break;
|
||||
case 7: m_gTec->m_analogIn.ain7 = true;
|
||||
break;
|
||||
case 6: m_gTec->m_analogIn.ain6 = true;
|
||||
break;
|
||||
case 5: m_gTec->m_analogIn.ain5 = true;
|
||||
break;
|
||||
case 4: m_gTec->m_analogIn.ain4 = true;
|
||||
break;
|
||||
case 3: m_gTec->m_analogIn.ain3 = true;
|
||||
break;
|
||||
case 2: m_gTec->m_analogIn.ain2 = true;
|
||||
break;
|
||||
case 1: m_gTec->m_analogIn.ain1 = true;
|
||||
break;
|
||||
default: m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Unexpected value " << index << " in CDriverGTecGMobiLabPlus::allowAnalogInputs\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* The gMobilab driver was contributed
|
||||
* by Lucie Daubigney from Supelec Metz
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
#include <string>
|
||||
#ifdef TARGET_OS_Windows
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
class CDriverGTecGMobiLabPlusPrivate; // fwd declare
|
||||
|
||||
/**
|
||||
* \class CDriverGTecGMobiLabPlus
|
||||
* \author Lucie Daubigney (Supelec Metz)
|
||||
*/
|
||||
class CDriverGTecGMobiLabPlus final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CDriverGTecGMobiLabPlus(IDriverContext& ctx);
|
||||
~CDriverGTecGMobiLabPlus() override;
|
||||
void release();
|
||||
const char* getName() override;
|
||||
|
||||
bool isFlagSet(const EDriverFlag flag) const override { return flag == EDriverFlag::IsUnstable; }
|
||||
|
||||
//configuration
|
||||
bool isConfigurable() override;
|
||||
bool configure() override;
|
||||
//initialisation
|
||||
bool initialize(const uint32_t sampleCountPerChannel, IDriverCallback& callback) override;
|
||||
bool uninitialize() override;
|
||||
const IHeader* getHeader() override;
|
||||
|
||||
//acquisition
|
||||
bool start() override;
|
||||
bool stop() override;
|
||||
bool loop() override;
|
||||
|
||||
protected:
|
||||
|
||||
SettingsHelper m_settings;
|
||||
|
||||
//usefull data to communicate with OpenViBE
|
||||
IHeader* m_header = nullptr;
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
uint32_t m_nSamplePerSentBlock = 0;//number of sample you want to send in a row
|
||||
float* m_sample = nullptr;//array containing the data to sent to OpenViBE once they had been recovered from the gTec module
|
||||
|
||||
//params
|
||||
std::string m_portName;
|
||||
bool m_testMode = false;
|
||||
|
||||
// Pointers do gtec-specific data and function pointers
|
||||
CDriverGTecGMobiLabPlusPrivate* m_gTec = nullptr;
|
||||
|
||||
// Register the function pointers from the dll. (The dll approach
|
||||
// is used with gMobilab to avoid conflicts with the gUSBAmp lib)
|
||||
bool registerLibraryFunctions();
|
||||
|
||||
#if defined(TARGET_OS_Windows)
|
||||
HINSTANCE m_library;
|
||||
#elif defined(TARGET_OS_Linux)
|
||||
void* m_library = nullptr;
|
||||
#endif
|
||||
private:
|
||||
|
||||
void allowAnalogInputs(uint32_t index);
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* The only purpose of this class is to hide the gtec API
|
||||
* data types from the ov driver header. An approach resembling
|
||||
* this is sometimes called a 'd-pointer'.
|
||||
*
|
||||
* Previous situation:
|
||||
*
|
||||
* gUsbamp ov driver header depends on gtec gUSBAmp API types
|
||||
* gMobilab ov driver header depends on gtec gMobilab API types
|
||||
* Acquisition Server includes both these driver headers
|
||||
*
|
||||
* But the gtec types are declared differently in the two gtec APIs.
|
||||
* Hence we get a conflict if both headers are included by the
|
||||
* same compilation unit.
|
||||
*
|
||||
* With this class, the gmobilab API is not exposed just by
|
||||
* including ovasCDriverGtecMobiLabPlus.h to the Acquisition Server.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
|
||||
#include <gMOBIlabplus.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \author Jussi T. Lindgren (Inria)
|
||||
*
|
||||
* The class collects all members that used to be in
|
||||
* CDriverGtecMobiLabPlus header that depend on the gmobilab API.
|
||||
*
|
||||
*/
|
||||
class CDriverGTecGMobiLabPlusPrivate
|
||||
{
|
||||
public:
|
||||
|
||||
//useful data to communicate with the gTec module
|
||||
_BUFFER_ST m_oBuffer;
|
||||
HANDLE m_device;
|
||||
_AIN m_analogIn;
|
||||
|
||||
#if defined(TARGET_OS_Windows)
|
||||
OVERLAPPED m_oOverlap;
|
||||
#endif
|
||||
|
||||
// These functions are defined in the gmobilab library
|
||||
#if defined(TARGET_OS_Windows)
|
||||
typedef HANDLE (__stdcall *OV_GT_OpenDevice)(LPSTR lpPort);
|
||||
typedef BOOL (__stdcall *OV_GT_CloseDevice)(HANDLE hDevice);
|
||||
typedef BOOL (__stdcall *OV_GT_SetTestmode)(HANDLE hDevice, BOOL Testmode);
|
||||
typedef BOOL (__stdcall *OV_GT_StartAcquisition)(HANDLE hDevice);
|
||||
typedef BOOL (__stdcall *OV_GT_GetData)(HANDLE hDevice, _BUFFER_ST* buffer, LPOVERLAPPED lpOvl);
|
||||
typedef BOOL (__stdcall *OV_GT_InitChannels)(HANDLE hDevice, _AIN analogCh, _DIO digitalCh);
|
||||
typedef bool (__stdcall *OV_GT_StopAcquisition)(HANDLE hDevice);
|
||||
typedef bool (__stdcall *OV_GT_GetLastError)(UINT* LastError);
|
||||
typedef bool (__stdcall *OV_GT_TranslateErrorCode)(_ERRSTR* ErrorString, UINT ErrorCode);
|
||||
#else
|
||||
typedef HANDLE(*OV_GT_OpenDevice)(const char* lpPort);
|
||||
typedef bool(*OV_GT_CloseDevice)(HANDLE hDevice);
|
||||
typedef bool(*OV_GT_SetTestmode)(HANDLE hDevice, bool Testmode);
|
||||
typedef bool(*OV_GT_StartAcquisition)(HANDLE hDevice);
|
||||
typedef bool(*OV_GT_GetData)(HANDLE hDevice, _BUFFER_ST *buffer);
|
||||
typedef bool(*OV_GT_InitChannels)(HANDLE hDevice, _AIN analogCh, _DIO digitalCh);
|
||||
typedef bool(*OV_GT_StopAcquisition)(HANDLE hDevice);
|
||||
typedef bool(*OV_GT_GetLastError)(uint32_t* LastError);
|
||||
typedef bool(*OV_GT_TranslateErrorCode)(_ERRSTR *ErrorString, uint32_t ErrorCode);
|
||||
#endif
|
||||
|
||||
// Function pointers to the dll
|
||||
OV_GT_OpenDevice m_fOpenDevice;
|
||||
OV_GT_CloseDevice m_fCloseDevice;
|
||||
OV_GT_SetTestmode m_fSetTestmode;
|
||||
OV_GT_StartAcquisition m_fStartAcquisition;
|
||||
OV_GT_GetData m_fGetData;
|
||||
OV_GT_InitChannels m_fInitChannels;
|
||||
OV_GT_StopAcquisition m_fStopAcquisition;
|
||||
OV_GT_GetLastError m_fGetLastError;
|
||||
OV_GT_TranslateErrorCode m_fTranslateErrorCode;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGMobiLabPlusAPI
|
||||
+3669
File diff suppressed because it is too large
Load Diff
+969
@@ -0,0 +1,969 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyGNEEDaccessAPI)
|
||||
|
||||
#include "ovasCConfigurationgNautilusInterface.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
/*_________________________________________________
|
||||
|
||||
Insert callback to specific widget here
|
||||
Example with a button that launch a calibration of the device:
|
||||
*/
|
||||
|
||||
//Callback connected to a dedicated gtk button (button_select_channels_bipolar_car_noise):
|
||||
static void ChannelSettingsCB(GtkButton* button, void* data) { static_cast<CConfigurationgNautilusInterface*>(data)->buttonChannelSettingsPressedCB(); }
|
||||
|
||||
//Callback connected to a dedicated gtk button (button_select_sensitivity_filters):
|
||||
static void SensitivityFiltersCB(GtkButton* button, void* data) { static_cast<CConfigurationgNautilusInterface*>(data)->buttonSensitivityFiltersPressedCB(); }
|
||||
|
||||
//Callback connected to a dedicated gtk button (button_sensitivity_filters_apply):
|
||||
static void SensitivityFiltersApplyCB(GtkButton* button, void* data)
|
||||
{
|
||||
static_cast<CConfigurationgNautilusInterface*>(data)->buttonSensitivityFiltersApplyPressedCB();
|
||||
}
|
||||
|
||||
//Callback connected to a dedicated gtk button (button_channel_apply):
|
||||
static void ChannelSettingsApplyCB(GtkButton* button, void* data)
|
||||
{
|
||||
static_cast<CConfigurationgNautilusInterface*>(data)->buttonChannelSettingsApplyPressedCB();
|
||||
}
|
||||
|
||||
//Callback actually called:
|
||||
void CConfigurationgNautilusInterface::buttonChannelSettingsPressedCB()
|
||||
{
|
||||
// Connect to the hardware, ask for calibration, verify the return code, etc.
|
||||
|
||||
GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(m_builder,"dialog_select_channels_bipolar_car_noise"));
|
||||
gint resp = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
|
||||
gtk_widget_hide(dialog);
|
||||
}
|
||||
|
||||
//Callback actually called:
|
||||
void CConfigurationgNautilusInterface::buttonSensitivityFiltersPressedCB()
|
||||
{
|
||||
// get bandpass and notch filters for currenty selected sampling rate and set to filter list in corresponding dialog
|
||||
GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(m_builder,"dialog_sensitivity_filters"));
|
||||
gint resp = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
|
||||
gtk_widget_hide(dialog);
|
||||
}
|
||||
|
||||
|
||||
//Callback actually called:
|
||||
void CConfigurationgNautilusInterface::buttonSensitivityFiltersApplyPressedCB()
|
||||
{
|
||||
// get handle to sensitivity and filters dialog and close it
|
||||
GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(m_builder,"dialog_sensitivity_filters"));
|
||||
gtk_widget_hide(dialog);
|
||||
}
|
||||
|
||||
|
||||
//Callback actually called:
|
||||
void CConfigurationgNautilusInterface::buttonChannelSettingsApplyPressedCB()
|
||||
{
|
||||
// get handle to channel settings dialog and close it
|
||||
GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(m_builder,"dialog_select_channels_bipolar_car_noise"));
|
||||
gtk_widget_hide(dialog);
|
||||
|
||||
// get number of channels selected and set range as number of channels starting at number of channels
|
||||
uint16_t nChannels = 0;
|
||||
char tmp[30];
|
||||
for (uint32_t i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
// set electrode names as channel names in channel selection dialog
|
||||
sprintf_s(&tmp[0], 30, "checkbutton_channel_%d", (i + 1));
|
||||
GtkButton* checkButton = GTK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
if (checkButton)
|
||||
{
|
||||
if ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton))) && (gtk_widget_get_visible(GTK_WIDGET(checkButton)))) { nChannels += 1; }
|
||||
}
|
||||
}
|
||||
|
||||
GtkSpinButton* button = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder,"spinbutton_number_of_channels"));
|
||||
gtk_spin_button_set_range(button, nChannels, nChannels);
|
||||
gtk_spin_button_set_value(button, nChannels);
|
||||
}
|
||||
|
||||
// catch combobox sampling rate changed signal and call function which handles event
|
||||
static void sample_rate_changed_cb(GtkComboBox* comboBox, void* data) { static_cast<CConfigurationgNautilusInterface*>(data)->comboboxSampleRateChangedCB(); }
|
||||
|
||||
// Callback actually called
|
||||
void CConfigurationgNautilusInterface::comboboxSampleRateChangedCB()
|
||||
{
|
||||
// get hardware filters according to sampling frequency currently selected
|
||||
const bool res = getFiltersForNewSamplingRate();
|
||||
if (!res) { } // error logged in getFiltersForNewSamplingRate
|
||||
}
|
||||
|
||||
// catch noise reduction checkbox toggled signal and call function which handles event
|
||||
static void noise_reduction_changed_cb(GtkCheckButton* pCheckbutton, void* data)
|
||||
{
|
||||
static_cast<CConfigurationgNautilusInterface*>(data)->checkbuttonNoiseReductionChangedCB();
|
||||
}
|
||||
|
||||
// Callback actually called
|
||||
void CConfigurationgNautilusInterface::checkbuttonNoiseReductionChangedCB()
|
||||
{
|
||||
// activate/deactivate noise reduction checkboxes in dialog_select_channels_bipolar_car_noise
|
||||
GtkCheckButton* buttonNoise = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,"checkbutton_noise_reduction"));
|
||||
const gboolean buttonValue = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttonNoise));
|
||||
char tmp[45];
|
||||
for (uint16_t i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
sprintf_s(&tmp[0], 45, "checkbutton_noise_channel_%d", (i + 1));
|
||||
GtkCheckButton* buttonNoiseChannel = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
|
||||
if (buttonNoiseChannel)
|
||||
{
|
||||
if (gtk_widget_get_visible(GTK_WIDGET(buttonNoiseChannel))) { gtk_widget_set_sensitive(GTK_WIDGET(buttonNoiseChannel), buttonValue); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// catch car checkbox toggled signal and call function which handles event
|
||||
static void car_changed_cb(GtkCheckButton* /*button*/, void* data) { static_cast<CConfigurationgNautilusInterface*>(data)->checkbuttonCARChangedCB(); }
|
||||
|
||||
// Callback actually called
|
||||
void CConfigurationgNautilusInterface::checkbuttonCARChangedCB()
|
||||
{
|
||||
// activate/deactivate car checkboxes in dialog_select_channels_bipolar_car_noise
|
||||
GtkCheckButton* buttonCAR = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,"checkbutton_car"));
|
||||
const gboolean buttonValue = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttonCAR));
|
||||
char tmp[45];
|
||||
for (uint16_t i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
sprintf_s(&tmp[0], 45, "checkbutton_car_channel_%d", (i + 1));
|
||||
GtkCheckButton* buttonCarChannel = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
if (buttonCarChannel)
|
||||
{
|
||||
if (gtk_widget_get_visible(GTK_WIDGET(buttonCarChannel))) { gtk_widget_set_sensitive(GTK_WIDGET(buttonCarChannel), buttonValue); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get hardware related settings from GDS
|
||||
bool CConfigurationgNautilusInterface::getHardwareSettings()
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
uint32_t i;
|
||||
|
||||
// get network channel and set in dialog and set one as active in dialog
|
||||
size_t supportedNwChannelsCount;
|
||||
// get number of supported network channels to allocate memory to hold supported network channels
|
||||
m_gdsResult = GDS_GNAUTILUS_GetSupportedNetworkChannels(m_deviceHandle, m_deviceNames, nullptr, &supportedNwChannelsCount);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
uint32_t* supportedNwChannels = new uint32_t[supportedNwChannelsCount];
|
||||
m_gdsResult = GDS_GNAUTILUS_GetSupportedNetworkChannels(m_deviceHandle, m_deviceNames, supportedNwChannels, &supportedNwChannelsCount);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
// get network channel currently used between base station and headstage
|
||||
uint32_t val;
|
||||
m_gdsResult = GDS_GNAUTILUS_GetNetworkChannel(m_deviceHandle, m_deviceNames, &val);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// put network channels to combobox
|
||||
GtkComboBox* comboBoxNetworkChannel = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_network_channel"));
|
||||
GtkTreeModel* listStoreNetworkChannel = gtk_combo_box_get_model(comboBoxNetworkChannel);
|
||||
gtk_list_store_clear(GTK_LIST_STORE(listStoreNetworkChannel));
|
||||
std::stringstream networkChannel;
|
||||
if (m_comboBoxNetworkChannels.size() > 0) m_comboBoxNetworkChannels.clear();
|
||||
|
||||
// fill network channel combobox with available network channels
|
||||
for (i = 0; i < supportedNwChannelsCount; ++i)
|
||||
{
|
||||
networkChannel << supportedNwChannels[i];
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreNetworkChannel), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreNetworkChannel), &iter, 0, networkChannel.str().c_str(), -1);
|
||||
networkChannel.clear();
|
||||
networkChannel.str("");
|
||||
|
||||
m_comboBoxNetworkChannels.push_back(supportedNwChannels[i]);
|
||||
}
|
||||
|
||||
const auto it = find(m_comboBoxNetworkChannels.begin(), m_comboBoxNetworkChannels.end(), val);
|
||||
const size_t nwChIndex = distance(m_comboBoxNetworkChannels.begin(), it);
|
||||
gtk_combo_box_set_active(comboBoxNetworkChannel, nwChIndex);
|
||||
|
||||
delete [] supportedNwChannels;
|
||||
|
||||
// get supported sample rates
|
||||
size_t nSampling;
|
||||
|
||||
// get number of supported sample rates
|
||||
m_gdsResult = GDS_GNAUTILUS_GetSupportedSamplingRates(m_deviceHandle, m_deviceNames, nullptr, &nSampling);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t* sampling = new uint32_t[nSampling];
|
||||
// get supported sample rates
|
||||
m_gdsResult = GDS_GNAUTILUS_GetSupportedSamplingRates(m_deviceHandle, m_deviceNames, sampling, &nSampling);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// set sample rates as content of combo box in corresponding dialog
|
||||
GtkComboBox* comboBoxSamplings = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_sampling_frequency"));
|
||||
GtkTreeModel* listStoreSamplings = gtk_combo_box_get_model(comboBoxSamplings);
|
||||
gtk_list_store_clear(GTK_LIST_STORE(listStoreSamplings));
|
||||
for (i = 0; i < nSampling; ++i)
|
||||
{
|
||||
std::string str = std::to_string(sampling[i]);
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreSamplings), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreSamplings), &iter, 0, str.c_str(), -1);
|
||||
}
|
||||
gtk_combo_box_set_active(comboBoxSamplings, 0);
|
||||
|
||||
const bool functionReturn = getFiltersForNewSamplingRate();
|
||||
if (!functionReturn) { return false; }
|
||||
|
||||
size_t nSensitivities;
|
||||
// get number of sensitivities
|
||||
m_gdsResult = GDS_GNAUTILUS_GetSupportedSensitivities(m_deviceHandle, m_deviceNames, nullptr, &nSensitivities);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
// get sensitivities
|
||||
double* sensitivities = new double[nSensitivities];
|
||||
m_gdsResult = GDS_GNAUTILUS_GetSupportedSensitivities(m_deviceHandle, m_deviceNames, sensitivities, &nSensitivities);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
// set items in dialog item combobox_select_sensitivity
|
||||
GtkComboBox* comboBoxSensitivities = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_select_sensitivity"));
|
||||
GtkTreeModel* listStoreSensitivities = gtk_combo_box_get_model(comboBoxSensitivities);
|
||||
gtk_list_store_clear(GTK_LIST_STORE(listStoreSensitivities));
|
||||
for (i = 0; i < nSensitivities; ++i)
|
||||
{
|
||||
std::string str = std::to_string(sensitivities[i] / 1000);
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreSensitivities), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreSensitivities), &iter, 0, str.c_str(), -1);
|
||||
m_comboBoxSensitivityValues.push_back(sensitivities[i]);
|
||||
}
|
||||
gtk_combo_box_set_active(comboBoxSensitivities, 0);
|
||||
|
||||
delete [] sensitivities;
|
||||
|
||||
size_t nInputSources;
|
||||
// first get number of supported input sources (call with third parameter set to NULL)
|
||||
m_gdsResult = GDS_GNAUTILUS_GetSupportedInputSources(m_deviceHandle, m_deviceNames, nullptr, &nInputSources);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
// allocate memory to hold input sources
|
||||
GDS_GNAUTILUS_INPUT_SIGNAL* inputSources = new GDS_GNAUTILUS_INPUT_SIGNAL[nInputSources];
|
||||
// now get input sources
|
||||
m_gdsResult = GDS_GNAUTILUS_GetSupportedInputSources(m_deviceHandle, m_deviceNames, inputSources, &nInputSources);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// set values to combobox_input_source (there are only three allowed at the moment, see code below)
|
||||
GtkComboBox* comboBoxInputSources = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_input_source"));
|
||||
GtkTreeModel* listStoreInputSources = gtk_combo_box_get_model(comboBoxInputSources);
|
||||
gtk_list_store_clear(GTK_LIST_STORE(listStoreInputSources));
|
||||
for (i = 0; i < nInputSources; ++i)
|
||||
{
|
||||
if (inputSources[i] == 0)
|
||||
{
|
||||
// electrode input
|
||||
m_comboBoxInputSources.push_back(inputSources[i]);
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreInputSources), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreInputSources), &iter, 0, "Electrode", -1);
|
||||
}
|
||||
else if (inputSources[i] == 1)
|
||||
{
|
||||
// shortcut
|
||||
m_comboBoxInputSources.push_back(inputSources[i]);
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreInputSources), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreInputSources), &iter, 0, "Shortcut", -1);
|
||||
}
|
||||
else if (inputSources[i] == 5)
|
||||
{
|
||||
// test signal
|
||||
m_comboBoxInputSources.push_back(inputSources[i]);
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreInputSources), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreInputSources), &iter, 0, "Test Signal", -1);
|
||||
}
|
||||
}
|
||||
gtk_combo_box_set_active(comboBoxInputSources, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// get channel names for hardware currently connected (cannot be changed as electrode grid is fixed)
|
||||
bool CConfigurationgNautilusInterface::getChannelNames()
|
||||
{
|
||||
uint32_t nMountedModules;
|
||||
size_t nElectrodeNames;
|
||||
char tmp[30];
|
||||
std::stringstream bipolarEntry;
|
||||
|
||||
// get number of mounted modules and electrode names currently available
|
||||
nMountedModules = 0;
|
||||
nElectrodeNames = 0;
|
||||
m_gdsResult = GDS_GNAUTILUS_GetChannelNames(m_deviceHandle, m_deviceNames, &nMountedModules,NULL, &nElectrodeNames);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
// set array of electrode names according to names currently available
|
||||
char (*electrodeNames)[GDS_GNAUTILUS_ELECTRODE_NAME_LENGTH_MAX] = new char[nElectrodeNames][GDS_GNAUTILUS_ELECTRODE_NAME_LENGTH_MAX];
|
||||
// get electrode names
|
||||
m_gdsResult = GDS_GNAUTILUS_GetChannelNames(m_deviceHandle, m_deviceNames, &nMountedModules, electrodeNames, &nElectrodeNames);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
GtkComboBox* comboBoxBipolarChannels = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,"combobox_channel_1"));
|
||||
GtkTreeModel* listStoreBipolarChannels = gtk_combo_box_get_model(comboBoxBipolarChannels);
|
||||
gtk_list_store_clear(GTK_LIST_STORE(listStoreBipolarChannels));
|
||||
|
||||
bipolarEntry << "none";
|
||||
GtkTreeIter it;
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreBipolarChannels), &it);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreBipolarChannels), &it, 0, bipolarEntry.str().c_str(), -1);
|
||||
for (uint16_t i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
// set electrode names as channel names in channel selection dialog
|
||||
sprintf_s(&tmp[0], 30, "checkbutton_channel_%d", (i + 1));
|
||||
GtkButton* buttonChannel = GTK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
if ((buttonChannel) && (i < nElectrodeNames))
|
||||
{
|
||||
gtk_button_set_label(buttonChannel, &electrodeNames[i][0]);
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreBipolarChannels), &it);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreBipolarChannels), &it, 0, &electrodeNames[i][0], -1);
|
||||
}
|
||||
else if ((buttonChannel) && (i >= nElectrodeNames))
|
||||
{
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(buttonChannel), false);
|
||||
gtk_button_set_label(buttonChannel, "");
|
||||
}
|
||||
}
|
||||
for (uint16_t i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
sprintf_s(&tmp[0], 30, "combobox_channel_%d", (i + 1));
|
||||
comboBoxBipolarChannels = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,tmp));
|
||||
if (comboBoxBipolarChannels)
|
||||
{
|
||||
gtk_combo_box_set_model(comboBoxBipolarChannels, listStoreBipolarChannels);
|
||||
gtk_combo_box_set_active(comboBoxBipolarChannels, 0);
|
||||
}
|
||||
}
|
||||
// adapt number of recorded channels in main configuration dialog according to number of actual selected channels
|
||||
GtkSpinButton* buttonNChannels = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder,"spinbutton_number_of_channels"));
|
||||
gtk_spin_button_set_range(buttonNChannels, nElectrodeNames, nElectrodeNames);
|
||||
gtk_spin_button_set_value(buttonNChannels, nElectrodeNames);
|
||||
|
||||
// set electrode names as channel names
|
||||
m_channelNames.clear();
|
||||
for (size_t i = 0; i < nElectrodeNames; ++i) { m_channelNames[i] = electrodeNames[i]; }
|
||||
|
||||
delete [] electrodeNames;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// get channels currenly available for g.Nautilus used
|
||||
bool CConfigurationgNautilusInterface::getAvailableChannels()
|
||||
{
|
||||
char tmp[30];
|
||||
|
||||
// get channels currently available on connected device
|
||||
BOOL availableChannels[GDS_GNAUTILUS_CHANNELS_MAX];
|
||||
m_gdsResult = GDS_GNAUTILUS_GetAvailableChannels(m_deviceHandle, m_deviceNames, &availableChannels);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_availableChannels.resize(GDS_GNAUTILUS_CHANNELS_MAX);
|
||||
|
||||
for (uint16_t i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
sprintf_s(&tmp[0], 30, "checkbutton_channel_%d", (i + 1));
|
||||
GtkCheckButton* buttonChannel = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
sprintf_s(&tmp[0], 30, "label_channel_%d", (i + 1));
|
||||
GtkLabel* labelChannel = GTK_LABEL(gtk_builder_get_object(m_builder,tmp));
|
||||
sprintf_s(&tmp[0], 30, "combobox_channel_%d", (i + 1));
|
||||
GtkComboBox* comboBoxChannel = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,tmp));
|
||||
sprintf_s(&tmp[0], 30, "checkbutton_car_channel_%d", (i + 1));
|
||||
GtkCheckButton* buttonCARChannel = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
sprintf_s(&tmp[0], 30, "checkbutton_noise_channel_%d", (i + 1));
|
||||
GtkCheckButton* buttonNoiseChannel = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
if (availableChannels[i] == 1)
|
||||
{
|
||||
if (buttonChannel)
|
||||
{
|
||||
gtk_widget_set_visible(GTK_WIDGET(buttonChannel), true);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(buttonChannel), true);
|
||||
gtk_widget_set_visible(GTK_WIDGET(labelChannel), true);
|
||||
gtk_widget_set_visible(GTK_WIDGET(comboBoxChannel), true);
|
||||
gtk_widget_set_visible(GTK_WIDGET(buttonCARChannel), true);
|
||||
gtk_widget_set_visible(GTK_WIDGET(buttonNoiseChannel), true);
|
||||
}
|
||||
m_availableChannels[i] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (buttonChannel)
|
||||
{
|
||||
gtk_widget_set_visible(GTK_WIDGET(buttonChannel), false);
|
||||
gtk_widget_set_visible(GTK_WIDGET(labelChannel), false);
|
||||
gtk_widget_set_visible(GTK_WIDGET(comboBoxChannel), false);
|
||||
gtk_widget_set_visible(GTK_WIDGET(buttonCARChannel), false);
|
||||
gtk_widget_set_visible(GTK_WIDGET(buttonNoiseChannel), false);
|
||||
}
|
||||
m_availableChannels[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// if sampling rate changed filters in filter settings dialog have to be updated according to new sampling rate
|
||||
bool CConfigurationgNautilusInterface::getFiltersForNewSamplingRate()
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
// get current sample rate
|
||||
GtkComboBox* comboBoxSampling = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_sampling_frequency"));
|
||||
const char* sSampling = gtk_combo_box_get_active_text(comboBoxSampling);
|
||||
double sampling = atof(sSampling);
|
||||
|
||||
size_t nBandPassFilters, nNotchFilters;
|
||||
std::stringstream filterDesc;
|
||||
filterDesc << "no bandpass filter";
|
||||
|
||||
// get number of bandpass filters and allocate filter array correspondingly
|
||||
m_gdsResult = GDS_GNAUTILUS_GetBandpassFilters(m_deviceHandle, m_deviceNames, nullptr, &nBandPassFilters);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
GDS_FILTER_INFO* bandPassFilters = new GDS_FILTER_INFO[nBandPassFilters];
|
||||
// get bandpass filters
|
||||
m_gdsResult = GDS_GNAUTILUS_GetBandpassFilters(m_deviceHandle, m_deviceNames, bandPassFilters, &nBandPassFilters);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage;
|
||||
return false;
|
||||
}
|
||||
// get number of notch filters and allocate filter array correspondingly
|
||||
m_gdsResult = GDS_GNAUTILUS_GetNotchFilters(m_deviceHandle, m_deviceNames, nullptr, &nNotchFilters);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
GDS_FILTER_INFO* notchFilters = new GDS_FILTER_INFO[nNotchFilters];
|
||||
// get notch filters
|
||||
m_gdsResult = GDS_GNAUTILUS_GetNotchFilters(m_deviceHandle, m_deviceNames, notchFilters, &nNotchFilters);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// set filters as combobox entries depending on the current sample rate selected
|
||||
GtkComboBox* comboBoxBandPass = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,"combobox_select_bandpass_filter"));
|
||||
GtkTreeModel* listStoreBandPass = gtk_combo_box_get_model(comboBoxBandPass);
|
||||
gtk_list_store_clear(GTK_LIST_STORE(listStoreBandPass));
|
||||
|
||||
GtkComboBox* comboBoxNotch = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,"combobox_select_notch_filter"));
|
||||
GtkTreeModel* listStoreNotch = gtk_combo_box_get_model(comboBoxNotch);
|
||||
gtk_list_store_clear(GTK_LIST_STORE(listStoreNotch));
|
||||
|
||||
if (m_comboBoxBandpassFilterIdx.size() > 0) m_comboBoxBandpassFilterIdx.clear();
|
||||
if (m_comboBoxNotchFilterIdx.size() > 0) m_comboBoxNotchFilterIdx.clear();
|
||||
if (m_comboBoxSensitivityValues.size() > 0) m_comboBoxSensitivityValues.clear();
|
||||
|
||||
// fill bandpass filter combobox with available filters
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreBandPass), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreBandPass), &iter, 0, filterDesc.str().c_str(), -1);
|
||||
filterDesc.clear();
|
||||
filterDesc.str("");
|
||||
|
||||
m_comboBoxBandpassFilterIdx.push_back(-1);
|
||||
for (size_t i = 0; i < nBandPassFilters; ++i)
|
||||
{
|
||||
if (sampling == bandPassFilters[i].SamplingRate)
|
||||
{
|
||||
if (bandPassFilters[i].TypeId == 1) filterDesc << "Butterworth - ";
|
||||
if (bandPassFilters[i].TypeId == 2) filterDesc << "Chebyshev - ";
|
||||
|
||||
filterDesc << bandPassFilters[i].Order << " - [";
|
||||
filterDesc << bandPassFilters[i].LowerCutoffFrequency << "; ";
|
||||
filterDesc << bandPassFilters[i].UpperCutoffFrequency << "] - ";
|
||||
filterDesc << bandPassFilters[i].SamplingRate;
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreBandPass), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreBandPass), &iter, 0, filterDesc.str().c_str(), -1);
|
||||
m_comboBoxBandpassFilterIdx.push_back(i);
|
||||
}
|
||||
filterDesc.clear();
|
||||
filterDesc.str("");
|
||||
}
|
||||
gtk_combo_box_set_active(comboBoxBandPass, m_bandpassFilterIdx + 1); // +1 because -1 is for "no filter".
|
||||
|
||||
// fill notch filter combobox with available fliters
|
||||
filterDesc << "no notch filter";
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreNotch), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreNotch), &iter, 0, filterDesc.str().c_str(), -1);
|
||||
filterDesc.clear();
|
||||
filterDesc.str("");
|
||||
|
||||
m_comboBoxNotchFilterIdx.push_back(-1);
|
||||
for (size_t i = 0; i < nNotchFilters; ++i)
|
||||
{
|
||||
if (sampling == notchFilters[i].SamplingRate)
|
||||
{
|
||||
if (notchFilters[i].TypeId == 1) filterDesc << "Butterworth - ";
|
||||
if (notchFilters[i].TypeId == 2) filterDesc << "Chebyshev - ";
|
||||
|
||||
filterDesc << notchFilters[i].Order << " - [";
|
||||
filterDesc << notchFilters[i].LowerCutoffFrequency << "; ";
|
||||
filterDesc << notchFilters[i].UpperCutoffFrequency << "] - ";
|
||||
filterDesc << notchFilters[i].SamplingRate;
|
||||
gtk_list_store_append(GTK_LIST_STORE(listStoreNotch), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(listStoreNotch), &iter, 0, filterDesc.str().c_str(), -1);
|
||||
m_comboBoxNotchFilterIdx.push_back(i);
|
||||
}
|
||||
filterDesc.clear();
|
||||
filterDesc.str("");
|
||||
}
|
||||
gtk_combo_box_set_active(comboBoxNotch, m_notchFilterIdx + 1); // +1 because -1 is for "no filter".
|
||||
|
||||
delete [] bandPassFilters;
|
||||
delete [] notchFilters;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// If you added more reference attribute, initialize them here
|
||||
CConfigurationgNautilusInterface::CConfigurationgNautilusInterface(IDriverContext& ctx, const char* gtkBuilderFilename, std::string& deviceSerial,
|
||||
int& inputSource, uint32_t& networkChannel, int& bandpassFilterIdx, int& notchFilterIdx,
|
||||
double& sensitivity, bool& digitalInputEnabled, bool& noiseReductionEnabled,
|
||||
bool& carEnabled, bool& accelerationDataEnabled, bool& counterEnabled,
|
||||
bool& linkQualityEnabled, bool& batteryLevelEnabled, bool& validationIndicatorEnabled,
|
||||
std::vector<uint16_t>& selectedChannels, std::vector<int>& bipolarChannels,
|
||||
std::vector<bool>& cars, std::vector<bool>& noiseReduction)
|
||||
: CConfigurationBuilder(gtkBuilderFilename), m_driverCtx(ctx), m_deviceSerial(deviceSerial), m_inputSource(inputSource), m_networkChannel(networkChannel),
|
||||
m_bandpassFilterIdx(bandpassFilterIdx), m_notchFilterIdx(notchFilterIdx), m_sensitivity(sensitivity), m_digitalInputEnabled(digitalInputEnabled),
|
||||
m_noiseReductionEnabled(noiseReductionEnabled), m_carEnabled(carEnabled), m_accelerationDataEnabled(accelerationDataEnabled),
|
||||
m_counterEnabled(counterEnabled), m_linkQualityEnabled(linkQualityEnabled), m_batteryLevelEnabled(batteryLevelEnabled),
|
||||
m_validationIndicatorEnabled(validationIndicatorEnabled), m_selectedChannels(selectedChannels), m_bipolarChannels(bipolarChannels), m_cars(cars),
|
||||
m_noiseReduction(noiseReduction) { m_connectionOpen = false; }
|
||||
|
||||
bool CConfigurationgNautilusInterface::preConfigure()
|
||||
{
|
||||
if (! CConfigurationBuilder::preConfigure()) { return false; }
|
||||
|
||||
// Connect here all callbacks
|
||||
// Example:
|
||||
// g_signal_connect(gtk_builder_get_object(m_builder, "button_calibrate"), "pressed", G_CALLBACK(button_calibrate_pressed_cb), this);
|
||||
|
||||
// Insert here the pre-configure code.
|
||||
// For example, you may want to check if a device is currently connected
|
||||
// and if more than one are connected. Then you can list in a dedicated combo-box
|
||||
// the device currently connected so the user can choose which one he wants to acquire from.
|
||||
|
||||
bool functionReturn;
|
||||
|
||||
// open connection to device if not done yet before reading filters, channel names etc.
|
||||
if (!m_connectionOpen)
|
||||
{
|
||||
// open device handle and get connected device
|
||||
functionReturn = openDevice();
|
||||
if (!functionReturn) { return false; } // error logged in openDevice;
|
||||
}
|
||||
|
||||
// get settings for connected hardware
|
||||
functionReturn = getHardwareSettings();
|
||||
if (!functionReturn) { return false; }
|
||||
// error logged in getHardwareSettings
|
||||
|
||||
// get available channels
|
||||
functionReturn = getAvailableChannels();
|
||||
if (!functionReturn) { return false; } // error logged in getAvailableChannels
|
||||
|
||||
functionReturn = getChannelNames();
|
||||
if (!functionReturn) { return false; } // error logged in getChannelNames
|
||||
|
||||
// activate checkboxes in g.Nautilus Configuration dialog
|
||||
GtkCheckButton* buttonEventChannel = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_event_channel"));
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(buttonEventChannel), m_digitalInputEnabled);
|
||||
|
||||
GtkCheckButton* buttonNoiseReduction = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_noise_reduction"));
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(buttonNoiseReduction), m_noiseReductionEnabled);
|
||||
|
||||
GtkCheckButton* buttonCAR = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_car"));
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(buttonCAR), m_carEnabled);
|
||||
|
||||
// activate/deactivate channel, noise reduction and CAR checkboxes in dialog_select_channels_bipolar_car_noise
|
||||
// as well as bipolar combo boxes according to available channels
|
||||
const gboolean noiseReduction = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttonNoiseReduction));
|
||||
const gboolean car = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttonCAR));
|
||||
char tmp[45];
|
||||
for (uint16_t i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
sprintf_s(&tmp[0], 45, "checkbutton_channel_%d", (i + 1));
|
||||
GtkCheckButton* button = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
if ((button) && (m_availableChannels[i])) gtk_widget_set_sensitive(GTK_WIDGET(button), true);
|
||||
else if (button) gtk_widget_set_sensitive(GTK_WIDGET(button), false);
|
||||
|
||||
sprintf_s(&tmp[0], 45, "checkbutton_noise_channel_%d", (i + 1));
|
||||
button = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
if ((button) && (m_availableChannels[i])) gtk_widget_set_sensitive(GTK_WIDGET(button), noiseReduction);
|
||||
else if (button) gtk_widget_set_sensitive(GTK_WIDGET(button), false);
|
||||
|
||||
sprintf_s(&tmp[0], 45, "checkbutton_car_channel_%d", (i + 1));
|
||||
button = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
if ((button) && (m_availableChannels[i])) gtk_widget_set_sensitive(GTK_WIDGET(button), car);
|
||||
else if (button) gtk_widget_set_sensitive(GTK_WIDGET(button), false);
|
||||
|
||||
sprintf_s(&tmp[0], 45, "combobox_channel_%d", (i + 1));
|
||||
GtkComboBox* comboBox = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,tmp));
|
||||
if ((comboBox) && (m_availableChannels[i])) gtk_widget_set_sensitive(GTK_WIDGET(comboBox), true);
|
||||
else if (comboBox) gtk_widget_set_sensitive(GTK_WIDGET(comboBox), false);
|
||||
}
|
||||
|
||||
GtkCheckButton* accelerationData = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_acceleration_data"));
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(accelerationData), m_accelerationDataEnabled);
|
||||
|
||||
GtkCheckButton* counter = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_counter"));
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(counter), m_counterEnabled);
|
||||
|
||||
GtkCheckButton* linkQuality = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_link_quality"));
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linkQuality), m_linkQualityEnabled);
|
||||
|
||||
GtkCheckButton* batteryLevel = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder, "checkbutton_battery_level"));
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(batteryLevel), m_batteryLevelEnabled);
|
||||
|
||||
GtkCheckButton* validationIndicator = GTK_CHECK_BUTTON(
|
||||
gtk_builder_get_object(m_builder, "checkbutton_validation_indicator"));
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(validationIndicator), m_validationIndicatorEnabled);
|
||||
|
||||
g_signal_connect(gtk_builder_get_object(m_builder,"button_select_channels_bipolar_car_noise"), "pressed", G_CALLBACK(ChannelSettingsCB), this);
|
||||
g_signal_connect(gtk_builder_get_object(m_builder,"button_select_sensitivity_filters"), "pressed", G_CALLBACK(SensitivityFiltersCB), this);
|
||||
|
||||
g_signal_connect(gtk_builder_get_object(m_builder,"button_select_sensitivity_filters_apply"), "pressed", G_CALLBACK(SensitivityFiltersApplyCB), this);
|
||||
g_signal_connect(gtk_builder_get_object(m_builder,"button_channel_apply"), "pressed", G_CALLBACK(ChannelSettingsApplyCB), this);
|
||||
|
||||
// set device serial in device serial text entry in g.Nautilus Configuration dialog
|
||||
GtkEntry* deviceSerial = GTK_ENTRY(gtk_builder_get_object(m_builder, "entry_device_serial"));
|
||||
gtk_entry_set_text(deviceSerial, m_deviceSerial.c_str());
|
||||
gtk_entry_set_editable(deviceSerial, false);
|
||||
|
||||
// deactivate buttons if no device is detected
|
||||
if (gtk_entry_get_text_length(deviceSerial) < 13)
|
||||
{
|
||||
GtkWidget* button = GTK_WIDGET(gtk_builder_get_object(m_builder,"button_select_channels_bipolar_car_noise"));
|
||||
gtk_widget_set_sensitive(button, false);
|
||||
|
||||
button = GTK_WIDGET(gtk_builder_get_object(m_builder,"button_select_sensitivity_filters"));
|
||||
gtk_widget_set_sensitive(button, false);
|
||||
}
|
||||
|
||||
// catch event when sample rate is changed to adjust available filters
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "combobox_sampling_frequency"), "changed", G_CALLBACK(sample_rate_changed_cb), this);
|
||||
|
||||
// catch events when car and noise reduction checkboxes are toggled in main config dialog to enable/disable corresponding checkboxes in related dialog
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "checkbutton_noise_reduction"), "toggled", G_CALLBACK(noise_reduction_changed_cb), this);
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "checkbutton_car"), "toggled", G_CALLBACK(car_changed_cb), this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfigurationgNautilusInterface::postConfigure()
|
||||
{
|
||||
if (m_applyConfig)
|
||||
{
|
||||
// If the user pressed the "apply" button, you need to save the changes made in the configuration.
|
||||
// For example, you can save the connection ID of the selected device:
|
||||
// m_connectionID = <value-from-gtk-widget>
|
||||
GtkCheckButton* checkButton;
|
||||
uint16_t i;
|
||||
gboolean buttonValue = false;
|
||||
gdouble nChannels = 0;
|
||||
|
||||
// get serial number from configuration dialog
|
||||
GtkEntry* entrySerial = GTK_ENTRY(gtk_builder_get_object(m_builder, "entry_device_serial"));
|
||||
m_deviceSerial = gtk_entry_get_text(entrySerial);
|
||||
|
||||
// get selected input source from dialog
|
||||
GtkComboBox* inputSources = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_input_source"));
|
||||
size_t idx = gtk_combo_box_get_active(inputSources);
|
||||
m_inputSource = m_comboBoxInputSources[idx];
|
||||
|
||||
// get selected channels
|
||||
char tmp[45];
|
||||
m_selectedChannels.clear();
|
||||
for (i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
sprintf_s(&tmp[0], 45, "checkbutton_channel_%d", (i + 1));
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
if (checkButton)
|
||||
{
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)))
|
||||
{
|
||||
m_selectedChannels.push_back(i + 1);
|
||||
nChannels += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_selectedChannels.push_back(0);
|
||||
m_channelNames.erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get bipolar channels
|
||||
m_bipolarChannels.clear();
|
||||
for (i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
sprintf_s(&tmp[0], 45, "combobox_channel_%d", (i + 1));
|
||||
GtkComboBox* bipolarChannels = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,tmp));
|
||||
if (bipolarChannels)
|
||||
{
|
||||
// bipolar is 0 based for the channels, -1 indicates no bipolar derivation
|
||||
m_bipolarChannels.push_back(gtk_combo_box_get_active(bipolarChannels) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// set bandpass and notch filter indices to correponding variables
|
||||
GtkComboBox* bandpassFilters = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,"combobox_select_bandpass_filter"));
|
||||
idx = gtk_combo_box_get_active(bandpassFilters);
|
||||
m_bandpassFilterIdx = m_comboBoxBandpassFilterIdx.at(idx);
|
||||
|
||||
GtkComboBox* notchFilters = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,"combobox_select_notch_filter"));
|
||||
idx = gtk_combo_box_get_active(notchFilters);
|
||||
m_notchFilterIdx = m_comboBoxNotchFilterIdx.at(idx);
|
||||
|
||||
// sensitivity
|
||||
GtkComboBox* sensitivities = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,"combobox_select_sensitivity"));
|
||||
idx = gtk_combo_box_get_active(sensitivities);
|
||||
m_sensitivity = m_comboBoxSensitivityValues.at(idx);
|
||||
|
||||
// digital inputs
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,"checkbutton_event_channel"));
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)) == 1) { m_digitalInputEnabled = true; }
|
||||
else { m_digitalInputEnabled = false; }
|
||||
|
||||
// noise reduction
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,"checkbutton_noise_reduction"));
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)) == 1) { m_noiseReductionEnabled = true; }
|
||||
else { m_noiseReductionEnabled = false; }
|
||||
|
||||
// if noise reduction is active, check for which channels noise reduction is enabled
|
||||
m_noiseReduction.clear();
|
||||
for (i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
sprintf_s(&tmp[0], 45, "checkbutton_noise_channel_%d", (i + 1));
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
if (checkButton)
|
||||
{
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)) == 1) { m_noiseReduction.push_back(true); }
|
||||
else { m_noiseReduction.push_back(false); }
|
||||
}
|
||||
}
|
||||
|
||||
// CAR
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,"checkbutton_car"));
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)) == 1) m_carEnabled = true;
|
||||
else m_carEnabled = false;
|
||||
|
||||
// if CAR is active, check for which channels CAR is enabled
|
||||
m_cars.clear();
|
||||
for (i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
sprintf_s(&tmp[0], 45, "checkbutton_car_channel_%d", (i + 1));
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,tmp));
|
||||
if (checkButton)
|
||||
{
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)) == 1) m_cars.push_back(true);
|
||||
else m_cars.push_back(false);
|
||||
}
|
||||
}
|
||||
|
||||
// acceleration data
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,"checkbutton_acceleration_data"));
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)) == 1) { m_accelerationDataEnabled = true; }
|
||||
else { m_accelerationDataEnabled = false; }
|
||||
|
||||
// counter
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,"checkbutton_counter"));
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)) == 1) { m_counterEnabled = true; }
|
||||
else { m_counterEnabled = false; }
|
||||
|
||||
// link quality
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,"checkbutton_link_quality"));
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)) == 1) { m_linkQualityEnabled = true; }
|
||||
else { m_linkQualityEnabled = false; }
|
||||
|
||||
// battery level
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,"checkbutton_battery_level"));
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)) == 1) { m_batteryLevelEnabled = true; }
|
||||
else { m_batteryLevelEnabled = false; }
|
||||
|
||||
// validation indicator
|
||||
checkButton = GTK_CHECK_BUTTON(gtk_builder_get_object(m_builder,"checkbutton_validation_indicator"));
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton)) == 1) { m_validationIndicatorEnabled = true; }
|
||||
else { m_validationIndicatorEnabled = false; }
|
||||
|
||||
// network channel
|
||||
GtkComboBox* comboBoxNetworkChannel = GTK_COMBO_BOX(gtk_builder_get_object(m_builder,"combobox_network_channel"));
|
||||
const char* networkChannel = gtk_combo_box_get_active_text(comboBoxNetworkChannel);
|
||||
m_networkChannel = uint32_t(atoi(networkChannel));
|
||||
|
||||
// set number of channels in main configuration dialog (spinbutton still remains disabled, user cannot change value there)
|
||||
GtkSpinButton* buttonChannels = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder, "spinbutton_number_of_channels"));
|
||||
gtk_spin_button_set_value(buttonChannels, nChannels);
|
||||
gtk_spin_button_set_range(buttonChannels, nChannels, nChannels);
|
||||
}
|
||||
if (m_connectionOpen)
|
||||
{
|
||||
// close connection handle
|
||||
const bool functionReturn = closeDevice();
|
||||
if (!functionReturn)
|
||||
{
|
||||
// error logged in closeDevice
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (! CConfigurationBuilder::postConfigure()
|
||||
) { return false; } // normal header is filled (Subject ID, Age, Gender, channels, sampling frequency), ressources are realesed
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfigurationgNautilusInterface::openDevice()
|
||||
{
|
||||
GDS_ENDPOINT hostEp, localEp;
|
||||
GDS_DEVICE_CONNECTION_INFO* connectedDevicesInfo;
|
||||
size_t nDevices;
|
||||
std::string deviceSerial;
|
||||
BOOL openExclusively = true;
|
||||
BOOL isCreator;
|
||||
m_deviceNames = new char[1][DEVICE_NAME_LENGTH_MAX];
|
||||
|
||||
uint8_t byteIP[4];
|
||||
byteIP[0] = 1;
|
||||
byteIP[1] = 0;
|
||||
byteIP[2] = 0;
|
||||
byteIP[3] = 127;
|
||||
char tmpIP[16];
|
||||
|
||||
_snprintf_s(tmpIP, IP_ADDRESS_LENGTH_MAX, "%d.%d.%d.%d", byteIP[3], byteIP[2], byteIP[1], byteIP[0]);
|
||||
|
||||
for (size_t i = 0; i < IP_ADDRESS_LENGTH_MAX; ++i)
|
||||
{
|
||||
hostEp.IpAddress[i] = tmpIP[i];
|
||||
localEp.IpAddress[i] = tmpIP[i];
|
||||
}
|
||||
|
||||
hostEp.Port = 50223;
|
||||
localEp.Port = 50224;
|
||||
|
||||
// get connected device
|
||||
m_gdsResult = GDS_GetConnectedDevices(hostEp, localEp, &connectedDevicesInfo, &nDevices);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nDevices; ++i)
|
||||
{
|
||||
// if devices are in use they cannot be used for a new acquisition
|
||||
if ((connectedDevicesInfo[i].InUse) && (i < nDevices)) continue;
|
||||
// only one device can be used for data acquisition, as g.Nautilus cannot be synchronized
|
||||
if ((connectedDevicesInfo[i].InUse) && (connectedDevicesInfo[i].ConnectedDevicesLength > 1)) continue;
|
||||
GDS_DEVICE_INFO* deviceInfo = connectedDevicesInfo[i].ConnectedDevices;
|
||||
if (deviceInfo[0].DeviceType == GDS_DEVICE_TYPE_GNAUTILUS)
|
||||
{
|
||||
deviceSerial = deviceInfo[0].Name;
|
||||
nDevices = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nDevices == 0)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "No g.Nautilus connected\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
strncpy_s(m_deviceNames[0], deviceSerial.c_str(), DEVICE_NAME_LENGTH_MAX);
|
||||
m_deviceSerial = deviceSerial;
|
||||
|
||||
// connect to device
|
||||
m_gdsResult = GDS_Connect(hostEp, localEp, m_deviceNames, nDevices, openExclusively, &m_deviceHandle, &isCreator);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// free connected devices list allocated by GDS_GetConnectedDevices
|
||||
m_gdsResult = GDS_FreeConnectedDevicesList(&connectedDevicesInfo, nDevices);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connectionOpen = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfigurationgNautilusInterface::closeDevice()
|
||||
{
|
||||
// disconnect device
|
||||
m_gdsResult = GDS_Disconnect(&m_deviceHandle);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connectionOpen = false;
|
||||
delete [] m_deviceNames;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyGNEEDaccessAPI
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyGNEEDaccessAPI)
|
||||
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
#include "ovasIDriver.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <GDSClientAPI.h>
|
||||
#include <GDSClientAPI_gNautilus.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CConfigurationgNautilusInterface
|
||||
* \author g.tec medical engineering GmbH (g.tec medical engineering GmbH)
|
||||
* \date Wed Aug 12 16:37:18 2015
|
||||
* \brief The CConfigurationgNautilusInterface handles the configuration dialog specific to the g.NEEDaccess device.
|
||||
*
|
||||
* TODO: details
|
||||
*
|
||||
* \sa CDrivergNautilusInterface
|
||||
*/
|
||||
class CConfigurationgNautilusInterface final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
// you may have to add to your constructor some reference parameters
|
||||
// for example, a connection ID:
|
||||
//CConfigurationgNautilusInterface(IDriverContext& ctx, const char* gtkBuilderFilename, uint32_t& rConnectionId);
|
||||
CConfigurationgNautilusInterface(IDriverContext& ctx, const char* gtkBuilderFilename, std::string& deviceSerial, int& inputSource, uint32_t& networkChannel,
|
||||
int& bandpassFilterIdx, int& notchFilterIdx, double& sensitivity, bool& digitalInputEnabled, bool& noiseReductionEnabled,
|
||||
bool& carEnabled, bool& accelerationDataEnabled, bool& counterEnabled, bool& linkQualityEnabled, bool& batteryLevelEnabled,
|
||||
bool& validationIndicatorEnabled, std::vector<uint16_t>& selectedChannels, std::vector<int>& bipolarChannels,
|
||||
std::vector<bool>& cars, std::vector<bool>& noiseReduction);
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
|
||||
//button callback functions
|
||||
void buttonChannelSettingsPressedCB();
|
||||
void buttonChannelSettingsApplyPressedCB();
|
||||
void buttonSensitivityFiltersPressedCB();
|
||||
void buttonSensitivityFiltersApplyPressedCB();
|
||||
bool getHardwareSettings();
|
||||
bool getChannelNames();
|
||||
bool getAvailableChannels();
|
||||
bool getFiltersForNewSamplingRate();
|
||||
void comboboxSampleRateChangedCB();
|
||||
void checkbuttonNoiseReductionChangedCB();
|
||||
void checkbuttonCARChangedCB();
|
||||
|
||||
protected:
|
||||
|
||||
IDriverContext& m_driverCtx;
|
||||
std::string& m_deviceSerial;
|
||||
int& m_inputSource;
|
||||
uint32_t& m_networkChannel;
|
||||
int& m_bandpassFilterIdx;
|
||||
int& m_notchFilterIdx;
|
||||
double& m_sensitivity;
|
||||
bool& m_digitalInputEnabled;
|
||||
bool& m_noiseReductionEnabled;
|
||||
bool& m_carEnabled;
|
||||
bool& m_accelerationDataEnabled;
|
||||
bool& m_counterEnabled;
|
||||
bool& m_linkQualityEnabled;
|
||||
bool& m_batteryLevelEnabled;
|
||||
bool& m_validationIndicatorEnabled;
|
||||
std::vector<uint16_t>& m_selectedChannels;
|
||||
std::vector<int>& m_bipolarChannels;
|
||||
std::vector<bool>& m_cars;
|
||||
std::vector<bool>& m_noiseReduction;
|
||||
std::vector<int> m_comboBoxBandpassFilterIdx;
|
||||
std::vector<int> m_comboBoxNotchFilterIdx;
|
||||
std::vector<double> m_comboBoxSensitivityValues;
|
||||
std::vector<int> m_comboBoxInputSources;
|
||||
std::vector<size_t> m_comboBoxNetworkChannels;
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Insert here all specific attributes, such as a connection ID.
|
||||
* use references to directly modify the corresponding attribute of the driver
|
||||
* Example:
|
||||
*/
|
||||
// uint32_t& m_connectionID;
|
||||
bool openDevice();
|
||||
bool closeDevice();
|
||||
GDS_HANDLE m_deviceHandle;
|
||||
GDS_RESULT m_gdsResult;
|
||||
std::vector<bool> m_availableChannels;
|
||||
char (*m_deviceNames)[DEVICE_NAME_LENGTH_MAX];
|
||||
bool m_connectionOpen = false;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGNEEDaccessAPI
|
||||
+462
@@ -0,0 +1,462 @@
|
||||
#if defined(TARGET_HAS_ThirdPartyGNEEDaccessAPI)
|
||||
|
||||
#include "ovasCDrivergNautilusInterface.h"
|
||||
#include "ovasCConfigurationgNautilusInterface.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
// global functions, event handles and variables
|
||||
void OnDataReadyEventHandler(GDS_HANDLE handle, void* data);
|
||||
HANDLE dataReadyEventHandle;
|
||||
|
||||
CDrivergNautilusInterface::CDrivergNautilusInterface(IDriverContext& ctx)
|
||||
: IDriver(ctx), m_settings("AcquisitionServer_Driver_gNautilusInterface", m_driverCtx.getConfigurationManager()), m_Device(NULL)
|
||||
{
|
||||
m_header.setSamplingFrequency(250);
|
||||
m_header.setChannelCount(32);
|
||||
|
||||
// The following class allows saving and loading driver settings from the acquisition server .conf file
|
||||
m_settings.add("Header", &m_header);
|
||||
// To save your custom driver settings, register each variable to the SettingsHelper
|
||||
//m_settings.add("SettingName", &variable);
|
||||
m_settings.add("DeviceIndex", &m_deviceIdx);
|
||||
m_settings.add("InputSource", &m_inputSource);
|
||||
m_settings.add("NetworkChannel", &m_networkChannel);
|
||||
m_settings.add("Sensitivity", &m_sensitivity);
|
||||
m_settings.add("NotchFilterIndex", &m_notchFilterIdx);
|
||||
m_settings.add("BandPassFilterIndex", &m_bandPassFilterIdx);
|
||||
m_settings.add("DigitalInputEnabled", &m_digitalInputEnabled);
|
||||
m_settings.add("NoiseReductionEnabled", &m_noiseReductionEnabled);
|
||||
m_settings.add("CAREnabled", &m_carEnabled);
|
||||
m_settings.add("AccelerationEnabled", &m_accelerationDataEnabled);
|
||||
m_settings.add("CounterEnabled", &m_counterEnabled);
|
||||
m_settings.add("LinqQualityEnabled", &m_linkQualityEnabled);
|
||||
m_settings.add("BatteryLevelEnabled", &m_batteryLevelEnabled);
|
||||
m_settings.add("ValidationIndicatorEnabled", &m_validationIndicatorEnabled);
|
||||
|
||||
m_settings.load();
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDrivergNautilusInterface::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected() || !m_header.isChannelCountSet() || !m_header.isSamplingFrequencySet()) { return false; }
|
||||
|
||||
if (nSamplePerSentBlock > 250)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Sample count per sent block cannot be higher than 250 samples for g.Nautilus\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// initialize basic GDS functions before the first GDS function itself is called
|
||||
GDS_Initialize();
|
||||
|
||||
// get number of channels actually acquired
|
||||
m_nAcquiredChannel = 0;
|
||||
for (size_t i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
if (find(m_selectedChannels.begin(), m_selectedChannels.end(), (i + 1)) != m_selectedChannels.end())
|
||||
{
|
||||
m_header.setChannelUnits(m_nAcquiredChannel, OVTK_UNIT_Volts, OVTK_FACTOR_Micro);
|
||||
m_nAcquiredChannel += 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_header.setChannelCount(m_nAcquiredChannel);
|
||||
|
||||
// g.Nautilus provides some extra channel, add them to the channels count
|
||||
// and provide corresponding names
|
||||
uint32_t nAcquiredChannel = m_nAcquiredChannel;
|
||||
if (m_accelerationDataEnabled)
|
||||
{
|
||||
m_header.setChannelCount(nAcquiredChannel + 3);
|
||||
m_header.setChannelName(nAcquiredChannel, "CH_Accel_x");
|
||||
m_header.setChannelName(nAcquiredChannel + 1, "CH_Accel_y");
|
||||
m_header.setChannelName(nAcquiredChannel + 2, "CH_Accel_z");
|
||||
m_header.setChannelUnits(nAcquiredChannel, OVTK_UNIT_Meter_Per_Second_Squared, OVTK_FACTOR_Base);
|
||||
m_header.setChannelUnits(nAcquiredChannel + 1, OVTK_UNIT_Meter_Per_Second_Squared, OVTK_FACTOR_Base);
|
||||
m_header.setChannelUnits(nAcquiredChannel + 2, OVTK_UNIT_Meter_Per_Second_Squared, OVTK_FACTOR_Base);
|
||||
nAcquiredChannel = m_header.getChannelCount();
|
||||
}
|
||||
if (m_counterEnabled)
|
||||
{
|
||||
m_header.setChannelCount(nAcquiredChannel + 1);
|
||||
m_header.setChannelName(nAcquiredChannel, "CH_Counter");
|
||||
m_header.setChannelUnits(nAcquiredChannel,OVTK_UNIT_Dimensionless,OVTK_FACTOR_Base);
|
||||
nAcquiredChannel = m_header.getChannelCount();
|
||||
}
|
||||
if (m_linkQualityEnabled)
|
||||
{
|
||||
m_header.setChannelCount(nAcquiredChannel + 1);
|
||||
m_header.setChannelName(nAcquiredChannel, "CH_LQ");
|
||||
m_header.setChannelUnits(nAcquiredChannel,OVTK_UNIT_10_2_Percent,OVTK_FACTOR_Base);
|
||||
nAcquiredChannel = m_header.getChannelCount();
|
||||
}
|
||||
if (m_batteryLevelEnabled)
|
||||
{
|
||||
m_header.setChannelCount(nAcquiredChannel + 1);
|
||||
m_header.setChannelName(nAcquiredChannel, "CH_Battery");
|
||||
m_header.setChannelUnits(nAcquiredChannel,OVTK_UNIT_10_2_Percent,OVTK_FACTOR_Base);
|
||||
nAcquiredChannel = m_header.getChannelCount();
|
||||
}
|
||||
if (m_digitalInputEnabled)
|
||||
{
|
||||
m_header.setChannelCount(nAcquiredChannel + 1);
|
||||
m_header.setChannelName(nAcquiredChannel, "CH_Event");
|
||||
m_header.setChannelUnits(nAcquiredChannel,OVTK_UNIT_Dimensionless,OVTK_FACTOR_Base);
|
||||
nAcquiredChannel = m_header.getChannelCount();
|
||||
}
|
||||
if (m_validationIndicatorEnabled)
|
||||
{
|
||||
m_header.setChannelCount(nAcquiredChannel + 1);
|
||||
m_header.setChannelName(nAcquiredChannel, "CH_Valid");
|
||||
m_header.setChannelUnits(nAcquiredChannel,OVTK_UNIT_Dimensionless,OVTK_FACTOR_Base);
|
||||
nAcquiredChannel = m_header.getChannelCount();
|
||||
}
|
||||
|
||||
// initialize connection and open connection handle and get serial of connected g.Nautilus
|
||||
GDS_DEVICE_CONNECTION_INFO* devicesInfo;
|
||||
size_t nDevices = 0;
|
||||
GDS_ENDPOINT hostEp;
|
||||
GDS_ENDPOINT localEp;
|
||||
uint8_t byteIP[4];
|
||||
char tempIP[16];
|
||||
|
||||
byteIP[0] = 1;
|
||||
byteIP[1] = 0;
|
||||
byteIP[2] = 0;
|
||||
byteIP[3] = 127;
|
||||
|
||||
_snprintf_s(tempIP, IP_ADDRESS_LENGTH_MAX, "%d.%d.%d.%d", byteIP[3], byteIP[2], byteIP[1], byteIP[0]);
|
||||
|
||||
for (size_t i = 0; i < IP_ADDRESS_LENGTH_MAX; ++i)
|
||||
{
|
||||
hostEp.IpAddress[i] = tempIP[i];
|
||||
localEp.IpAddress[i] = tempIP[i];
|
||||
}
|
||||
|
||||
hostEp.Port = 50223;
|
||||
localEp.Port = 50224;
|
||||
|
||||
bool found = false;
|
||||
m_gdsResult = GDS_GetConnectedDevices(hostEp, localEp, &devicesInfo, &nDevices);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < nDevices; ++i)
|
||||
{
|
||||
// if devices are in use they cannot be used for a new acquisition
|
||||
if ((devicesInfo[i].InUse) && (i < nDevices)) { continue; }
|
||||
// only one device can be used for data acquisition, as g.Nautilus cannot be synchronized
|
||||
if ((devicesInfo[i].InUse) && (devicesInfo[i].ConnectedDevicesLength > 1)) { continue; }
|
||||
GDS_DEVICE_INFO* info = devicesInfo[i].ConnectedDevices;
|
||||
if (info[0].DeviceType == GDS_DEVICE_TYPE_GNAUTILUS)
|
||||
{
|
||||
// check if device used for configuration is still available
|
||||
if (!strcmp(info[0].Name, m_deviceSerial.c_str()))
|
||||
{
|
||||
m_nDevice = 1;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found == false)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "No g.Nautilus device found\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
char (*names)[DEVICE_NAME_LENGTH_MAX] = new char[1][DEVICE_NAME_LENGTH_MAX];
|
||||
bool openExclusively = true;
|
||||
BOOL isCreator;
|
||||
|
||||
strncpy_s(names[0], m_deviceSerial.c_str(), DEVICE_NAME_LENGTH_MAX);
|
||||
|
||||
// connect to device
|
||||
m_gdsResult = GDS_Connect(hostEp, localEp, names, m_nDevice, openExclusively, &m_Device, &isCreator);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Connected to device : " << m_deviceSerial << "\n";
|
||||
|
||||
m_deviceCfg.SamplingRate = m_header.getSamplingFrequency();
|
||||
m_deviceCfg.NumberOfScans = nSamplePerSentBlock;
|
||||
m_deviceCfg.NetworkChannel = m_networkChannel;
|
||||
m_deviceCfg.AccelerationData = m_accelerationDataEnabled;
|
||||
m_deviceCfg.BatteryLevel = m_batteryLevelEnabled;
|
||||
m_deviceCfg.CAR = m_carEnabled;
|
||||
m_deviceCfg.Counter = m_counterEnabled;
|
||||
m_deviceCfg.DigitalIOs = m_digitalInputEnabled;
|
||||
m_deviceCfg.LinkQualityInformation = m_linkQualityEnabled;
|
||||
m_deviceCfg.NoiseReduction = m_noiseReductionEnabled;
|
||||
m_deviceCfg.Slave = 0;
|
||||
m_deviceCfg.ValidationIndicator = m_validationIndicatorEnabled;
|
||||
m_deviceCfg.InputSignal = GDS_GNAUTILUS_INPUT_SIGNAL(m_inputSource);
|
||||
|
||||
for (size_t i = 0; i < GDS_GNAUTILUS_CHANNELS_MAX; ++i)
|
||||
{
|
||||
if (find(m_selectedChannels.begin(), m_selectedChannels.end(), (i + 1)) != m_selectedChannels.end()) { m_deviceCfg.Channels[i].Enabled = true; }
|
||||
else { m_deviceCfg.Channels[i].Enabled = false; }
|
||||
m_deviceCfg.Channels[i].Sensitivity = m_sensitivity;
|
||||
if (i < m_cars.size()) { m_deviceCfg.Channels[i].UsedForCar = m_cars[i]; }
|
||||
else { m_deviceCfg.Channels[i].UsedForCar = false; }
|
||||
if (i < m_noiseReduction.size()) { m_deviceCfg.Channels[i].UsedForNoiseReduction = m_noiseReduction[i]; }
|
||||
else { m_deviceCfg.Channels[i].UsedForNoiseReduction = false; }
|
||||
m_deviceCfg.Channels[i].BandpassFilterIndex = m_bandPassFilterIdx;
|
||||
m_deviceCfg.Channels[i].NotchFilterIndex = m_notchFilterIdx;
|
||||
if (i < m_bipolarChannels.size()) { m_deviceCfg.Channels[i].BipolarChannel = m_bipolarChannels[i]; }
|
||||
else { m_deviceCfg.Channels[i].BipolarChannel = false; }
|
||||
}
|
||||
|
||||
// Free memory allocated for list of connected devices by GDS_GetConnectedDevices
|
||||
m_gdsResult = GDS_FreeConnectedDevicesList(&devicesInfo, nDevices);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Builds up a buffer to store acquired samples. This buffer
|
||||
// will be sent to the acquisition server later...
|
||||
m_sample = new float[nAcquiredChannel * m_deviceCfg.NumberOfScans];
|
||||
if (!m_sample)
|
||||
{
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
return false;
|
||||
}
|
||||
// set up data buffer for gds getdata function
|
||||
m_Buffer = new float[nAcquiredChannel * m_deviceCfg.NumberOfScans];
|
||||
if (!m_Buffer)
|
||||
{
|
||||
delete [] m_Buffer;
|
||||
m_Buffer = nullptr;
|
||||
return false;
|
||||
}
|
||||
m_bufferSize = nAcquiredChannel * m_deviceCfg.NumberOfScans;
|
||||
|
||||
// Saves parameters
|
||||
m_callback = &callback;
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDrivergNautilusInterface::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// if no device was found return
|
||||
if (m_nDevice != 1) { return false; }
|
||||
|
||||
GDS_CONFIGURATION_BASE* config = new GDS_CONFIGURATION_BASE[m_nDevice];
|
||||
config[0].Configuration = &m_deviceCfg;
|
||||
config[0].DeviceInfo.DeviceType = GDS_DEVICE_TYPE_GNAUTILUS;
|
||||
strcpy_s(config[0].DeviceInfo.Name, m_deviceSerial.c_str());
|
||||
size_t nScan = 0;
|
||||
size_t channelsPerDevice = 0;
|
||||
size_t bufferSizeInSamples = 0;
|
||||
|
||||
// set device configuration
|
||||
m_gdsResult = GDS_SetConfiguration(m_Device, config, m_nDevice);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// initialize data ready event and set data ready event handle
|
||||
dataReadyEventHandle = nullptr;
|
||||
dataReadyEventHandle = CreateEvent(nullptr, false, false, nullptr);
|
||||
m_gdsResult = GDS_SetDataReadyCallback(m_Device, (GDS_Callback)OnDataReadyEventHandler, m_deviceCfg.NumberOfScans,NULL);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// set number of scans getdata function will return
|
||||
m_availableScans = m_deviceCfg.NumberOfScans;
|
||||
|
||||
// ...
|
||||
// request hardware to start
|
||||
// sending data
|
||||
// ...
|
||||
// start acquisition
|
||||
m_gdsResult = GDS_StartAcquisition(m_Device);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
// start data stream from server
|
||||
m_gdsResult = GDS_StartStreaming(m_Device);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_gdsResult = GDS_GetDataInfo(m_Device, &nScan, nullptr, &channelsPerDevice, &bufferSizeInSamples);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDrivergNautilusInterface::loop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return true; }
|
||||
|
||||
const CStimulationSet stimSet;
|
||||
|
||||
if (m_driverCtx.isStarted())
|
||||
{
|
||||
const DWORD ret = WaitForSingleObject(dataReadyEventHandle, 5000);
|
||||
if (ret == WAIT_TIMEOUT)
|
||||
{
|
||||
// if data ready event is not triggered in 5000ms add a timeout handler
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "No data received in 5 seconds\n";
|
||||
return false;
|
||||
}
|
||||
// when data is ready call get data function with amount of scans to return
|
||||
m_gdsResult = GDS_GetData(m_Device, &m_availableScans, m_Buffer, m_bufferSize);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// put data from receiving buffer to application buffer
|
||||
for (size_t i = 0; i < m_header.getChannelCount(); ++i)
|
||||
{
|
||||
for (size_t j = 0; j < m_nSamplePerSentBlock; ++j) { m_sample[i * m_nSamplePerSentBlock + j] = m_Buffer[j * (m_header.getChannelCount()) + i]; }
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
// receive samples from hardware
|
||||
// put them the correct way in the sample array
|
||||
// whether the buffer is full, send it to the acquisition server
|
||||
//...
|
||||
m_callback->setSamples(m_sample);
|
||||
|
||||
// When your sample buffer is fully loaded,
|
||||
// it is advised to ask the acquisition server
|
||||
// to correct any drift in the acquisition automatically.
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
|
||||
// ...
|
||||
// receive events from hardware
|
||||
// and put them the correct way in a CStimulationSet object
|
||||
//...
|
||||
m_callback->setStimulationSet(stimSet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDrivergNautilusInterface::stop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) return false;
|
||||
if (!m_driverCtx.isStarted()) return false;
|
||||
|
||||
// ...
|
||||
// request the hardware to stop
|
||||
// sending data
|
||||
// ...
|
||||
|
||||
|
||||
// stop streaming
|
||||
m_gdsResult = GDS_StopStreaming(m_Device);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// stop data acquisiton
|
||||
m_gdsResult = GDS_StopAcquisition(m_Device);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDrivergNautilusInterface::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// uninitialize hardware here
|
||||
// ...
|
||||
|
||||
m_gdsResult = GDS_Disconnect(&m_Device);
|
||||
if (m_gdsResult.ErrorCode != GDS_ERROR_SUCCESS)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << m_gdsResult.ErrorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
m_callback = nullptr;
|
||||
|
||||
// uninitialize basic GDS functions after last GDS function is called
|
||||
GDS_Uninitialize();
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Disconnected from device : " << m_deviceSerial << "\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDrivergNautilusInterface::isConfigurable()
|
||||
{
|
||||
return true; // change to false if your device is not configurable
|
||||
}
|
||||
|
||||
bool CDrivergNautilusInterface::configure()
|
||||
{
|
||||
// Change this line if you need to specify some references to your driver attribute that need configuration, e.g. the connection ID.
|
||||
CConfigurationgNautilusInterface config(m_driverCtx, Directories::getDataDir() + "/applications/acquisition-server/interface-gNautilusInterface.ui",
|
||||
m_deviceSerial, m_inputSource, m_networkChannel, m_bandPassFilterIdx, m_notchFilterIdx, m_sensitivity,
|
||||
m_digitalInputEnabled, m_noiseReductionEnabled, m_carEnabled, m_accelerationDataEnabled, m_counterEnabled,
|
||||
m_linkQualityEnabled, m_batteryLevelEnabled, m_validationIndicatorEnabled, m_selectedChannels, m_bipolarChannels,
|
||||
m_cars, m_noiseReduction);
|
||||
|
||||
if (!config.configure(m_header)) { return false; }
|
||||
m_settings.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnDataReadyEventHandler(GDS_HANDLE handle, void* data)
|
||||
{
|
||||
// signals data is ready on GDS acquisition buffer
|
||||
if (!SetEvent(dataReadyEventHandle))
|
||||
{
|
||||
// insert error handling if necessary
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGNEEDaccessAPI
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_ThirdPartyGNEEDaccessAPI)
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
#include <GDSClientAPI.h>
|
||||
#include <GDSClientAPI_gNautilus.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <Windows.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CDrivergNautilusInterface
|
||||
* \author g.tec medical engineering GmbH (g.tec medical engineering GmbH)
|
||||
* \date Wed Aug 12 16:37:18 2015
|
||||
* \brief The CDrivergNautilusInterface allows the acquisition server to acquire data from a g.NEEDaccess device.
|
||||
*
|
||||
* TODO: details
|
||||
*
|
||||
* \sa CConfigurationgNautilusInterface
|
||||
*/
|
||||
class CDrivergNautilusInterface final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CDrivergNautilusInterface(IDriverContext& ctx);
|
||||
~CDrivergNautilusInterface() override { }
|
||||
const char* getName() override { return "g.tec g.Nautilus using g.NEEDaccess"; }
|
||||
|
||||
bool initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback) override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool start() override;
|
||||
bool stop() override;
|
||||
bool loop() override;
|
||||
|
||||
bool isConfigurable() override;
|
||||
bool configure() override;
|
||||
const IHeader* getHeader() override { return &m_header; }
|
||||
|
||||
bool isFlagSet(const EDriverFlag flag) const override { return flag == EDriverFlag::IsUnstable; }
|
||||
|
||||
protected:
|
||||
|
||||
SettingsHelper m_settings;
|
||||
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
|
||||
// Replace this generic Header with any specific header you might have written
|
||||
CHeader m_header;
|
||||
|
||||
uint32_t m_nSamplePerSentBlock = 4;
|
||||
float* m_sample = nullptr;
|
||||
uint32_t m_deviceIdx = uint32_t(-1);
|
||||
uint32_t m_actualDeviceIdx = 0;
|
||||
uint32_t m_bufferSize = 0;
|
||||
size_t m_availableScans = 0;
|
||||
float* m_Buffer = nullptr;
|
||||
|
||||
int m_notchFilterIdx = -1;
|
||||
int m_bandPassFilterIdx = -1;
|
||||
|
||||
double m_sensitivity = 0;
|
||||
int m_inputSource = 0;
|
||||
uint32_t m_networkChannel = 11;
|
||||
bool m_digitalInputEnabled = true;
|
||||
bool m_noiseReductionEnabled = false;
|
||||
bool m_carEnabled = false;
|
||||
bool m_accelerationDataEnabled = true;
|
||||
bool m_counterEnabled = true;
|
||||
bool m_linkQualityEnabled = true;
|
||||
bool m_batteryLevelEnabled = true;
|
||||
bool m_validationIndicatorEnabled = true;
|
||||
std::vector<uint16_t> m_selectedChannels;
|
||||
std::vector<int> m_bipolarChannels;
|
||||
std::vector<bool> m_noiseReduction;
|
||||
std::vector<bool> m_cars;
|
||||
uint32_t m_nAcquiredChannel = 32;
|
||||
|
||||
GDS_HANDLE m_Device;
|
||||
GDS_RESULT m_gdsResult;
|
||||
GDS_GNAUTILUS_CONFIGURATION m_deviceCfg;
|
||||
std::string m_deviceSerial;
|
||||
uint32_t m_nDevice = 0;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGNEEDaccessAPI
|
||||
+812
@@ -0,0 +1,812 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy project-wide -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="value">4</property>
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">16</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="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="GtkDialog" id="openvibe-acquisition-server-settings">
|
||||
<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>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox">
|
||||
<property name="visible">True</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>
|
||||
<object class="GtkLabel" id="label_title">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">g.Tec gUSBamp</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox">
|
||||
<property name="visible">True</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="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">8</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="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="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_device">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">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="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="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="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_device">
|
||||
<property name="visible">True</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="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="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="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="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="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="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="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="label6">
|
||||
<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">7</property>
|
||||
<property name="bottom_attach">8</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="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>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator2">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</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>
|
||||
</object>
|
||||
<packing>
|
||||
<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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator3">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area">
|
||||
<property name="visible">True</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_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_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="pack_type">end</property>
|
||||
<property name="position">0</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>
|
||||
<object class="GtkListStore" id="model3">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkDialog" id="dialog-common-gnd-ref">
|
||||
<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>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table1">
|
||||
<property name="visible">True</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="label" translatable="yes"><big>Connect to Common Ground</big></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="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="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="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="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="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator4">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTable" id="table3">
|
||||
<property name="visible">True</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="label" translatable="yes"><big>Connect to Common Reference</big></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="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="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="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="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="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area2">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkDialog" id="dialog-filters">
|
||||
<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>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><big>Notch filter</big>
|
||||
|
||||
Type - Order - [Low Pass; High Pass] - Frequency</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox-notch">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">liststore1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext1"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator5">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><big>Band Pass filter</big>
|
||||
|
||||
Type - Order - [Low Cut; High Cut] - Frequency</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox-band-pass">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">liststore2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext2"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area3">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</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>
|
||||
</interface>
|
||||
+282
@@ -0,0 +1,282 @@
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI
|
||||
|
||||
#include "ovasCConfigurationGTecGUSBampLegacy.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <gUSBamp.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
static void CalibratePressedCB(GtkButton* /*button*/, void* data) { static_cast<CConfigurationGTecGUSBampLegacy*>(data)->buttonCalibratePressedCB(); }
|
||||
|
||||
static gboolean idle_calibrate_cb(void* data)
|
||||
{
|
||||
static_cast<CConfigurationGTecGUSBampLegacy*>(data)->idleCalibrateCB();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void button_common_gnd_ref_pressed_cb(GtkButton* /*button*/, void* data)
|
||||
{
|
||||
static_cast<CConfigurationGTecGUSBampLegacy*>(data)->buttonCommonGndRefPressedCB();
|
||||
}
|
||||
|
||||
static void FiltersPressedCB(GtkButton* /*button*/, void* data) { static_cast<CConfigurationGTecGUSBampLegacy*>(data)->buttonFiltersPressedCB(); }
|
||||
|
||||
CConfigurationGTecGUSBampLegacy::CConfigurationGTecGUSBampLegacy(const char* gtkBuilderFilename, uint32_t& usbIdx, uint8_t& commonGndAndRefBitmap,
|
||||
int& notchFilterIdx, int& bandPassFilterIdx, bool& triggerInput)
|
||||
: CConfigurationBuilder(gtkBuilderFilename), m_usbIdx(usbIdx), m_commonGndAndRefBitmap(commonGndAndRefBitmap), m_notchFilterIdx(notchFilterIdx),
|
||||
m_bandPassFilterIdx(bandPassFilterIdx), m_triggerInput(triggerInput) {}
|
||||
|
||||
bool CConfigurationGTecGUSBampLegacy::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);
|
||||
|
||||
GtkComboBox* comboBox = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_device"));
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "button_calibrate"), "pressed", G_CALLBACK(CalibratePressedCB), this);
|
||||
|
||||
if (!selected && count != 0) { gtk_combo_box_set_active(comboBox, 0); }
|
||||
|
||||
g_signal_connect(gtk_builder_get_object(m_builder, "button-common-gnd-ref"), "pressed", G_CALLBACK(button_common_gnd_ref_pressed_cb), 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"));
|
||||
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);
|
||||
|
||||
|
||||
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)));
|
||||
|
||||
// To check for available filters in the amplifier, we must connect to it.
|
||||
if (count != 0)
|
||||
{
|
||||
HANDLE handle = GT_OpenDevice(count);
|
||||
|
||||
int nBandPassFilters, nNotchFilters;
|
||||
if (!GT_GetNumberOfFilter(&nBandPassFilters)) { std::cout << "err GT_GetNumberOfFilter\n"; }
|
||||
if (!GT_GetNumberOfNotch(&nNotchFilters)) { std::cout << "err GT_GetNumberOfNotch\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"; }
|
||||
|
||||
GtkComboBox* comboBoxBandPass = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox-band-pass"));
|
||||
GtkTreeModel* bandPassListStore = gtk_combo_box_get_model(comboBoxBandPass);
|
||||
std::stringstream desc;
|
||||
desc << "no band pass filter.";
|
||||
GtkTreeIter lIter;
|
||||
gtk_list_store_append(GTK_LIST_STORE(bandPassListStore), &lIter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(bandPassListStore), &lIter, 0, desc.str().c_str(), -1);
|
||||
desc.clear();
|
||||
desc.str("");
|
||||
for (int i = 0; i < nBandPassFilters; ++i)
|
||||
{
|
||||
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("");
|
||||
}
|
||||
gtk_combo_box_set_active(comboBoxBandPass, m_bandPassFilterIdx + 1); // +1 because -1 is for "no filter".
|
||||
|
||||
GtkComboBox* comboBoxNotch = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox-notch"));
|
||||
GtkTreeModel* notchListStore = gtk_combo_box_get_model(comboBoxNotch);
|
||||
desc << "no notch filter.";
|
||||
gtk_list_store_append(GTK_LIST_STORE(notchListStore), &lIter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(notchListStore), &lIter, 0, desc.str().c_str(), -1);
|
||||
desc.clear();
|
||||
desc.str("");
|
||||
for (int i = 0; i < nNotchFilters; ++i)
|
||||
{
|
||||
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("");
|
||||
}
|
||||
gtk_combo_box_set_active(comboBoxNotch, m_notchFilterIdx + 1);
|
||||
|
||||
delete bpFilterSpec;
|
||||
delete notchFilterSpec;
|
||||
GT_CloseDevice(&handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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 CConfigurationGTecGUSBampLegacy::postConfigure()
|
||||
{
|
||||
GtkComboBox* comboBox = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_device"));
|
||||
|
||||
if (m_applyConfig)
|
||||
{
|
||||
int iUsbIdx = 0;
|
||||
const char* usbIdx = gtk_combo_box_get_active_text(comboBox);
|
||||
if (usbIdx) { if (sscanf(usbIdx, "USB port %i", &iUsbIdx) == 1) { m_usbIdx = uint32_t(iUsbIdx); } }
|
||||
|
||||
GtkToggleButton* button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockA"));
|
||||
m_commonGndAndRefBitmap = (gtk_toggle_button_get_active(button) ? 1 : 0);
|
||||
button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockB"));
|
||||
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(button) ? (1 << 1) : 0);
|
||||
button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockC"));
|
||||
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(button) ? (1 << 2) : 0);
|
||||
button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-gnd-blockD"));
|
||||
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(button) ? (1 << 3) : 0);
|
||||
button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockA"));
|
||||
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(button) ? (1 << 4) : 0);
|
||||
button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockB"));
|
||||
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(button) ? (1 << 5) : 0);
|
||||
button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockC"));
|
||||
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(button) ? (1 << 6) : 0);
|
||||
button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "checkbutton-ref-blockD"));
|
||||
m_commonGndAndRefBitmap = m_commonGndAndRefBitmap + (gtk_toggle_button_get_active(button) ? (1 << 7) : 0);
|
||||
|
||||
GtkComboBox* notch = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox-notch"));
|
||||
m_notchFilterIdx = gtk_combo_box_get_active(notch) - 1;
|
||||
GtkComboBox* bandPass = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox-band-pass"));
|
||||
m_bandPassFilterIdx = 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);
|
||||
}
|
||||
|
||||
if (!CConfigurationBuilder::postConfigure()) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
void CConfigurationGTecGUSBampLegacy::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 CConfigurationGTecGUSBampLegacy::idleCalibrateCB()
|
||||
{
|
||||
GtkComboBox* comboBox = GTK_COMBO_BOX(gtk_builder_get_object(m_builder, "combobox_device"));
|
||||
|
||||
m_calibrationDone = false;
|
||||
int iUsbIdx = 0;
|
||||
const char* usbIdx = gtk_combo_box_get_active_text(comboBox);
|
||||
|
||||
if (usbIdx)
|
||||
{
|
||||
if (sscanf(usbIdx, "USB port %i", &iUsbIdx) == 1)
|
||||
{
|
||||
HANDLE handle = GT_OpenDevice(iUsbIdx);
|
||||
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 CConfigurationGTecGUSBampLegacy::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 CConfigurationGTecGUSBampLegacy::buttonFiltersPressedCB()
|
||||
{
|
||||
GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(m_builder, "dialog-filters"));
|
||||
gint resp = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_hide(dialog);
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI
|
||||
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
class CConfigurationGTecGUSBampLegacy final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
CConfigurationGTecGUSBampLegacy(const char* gtkBuilderFilename, uint32_t& usbIdx, uint8_t& commonGndAndRefBitmap, int& notchFilterIdx,
|
||||
int& bandPassFilterIdx, bool& triggerInput);
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
|
||||
void buttonCalibratePressedCB();
|
||||
void idleCalibrateCB();
|
||||
|
||||
void buttonCommonGndRefPressedCB();
|
||||
void buttonFiltersPressedCB();
|
||||
|
||||
protected:
|
||||
uint32_t& m_usbIdx;
|
||||
uint8_t& m_commonGndAndRefBitmap;
|
||||
|
||||
int& m_notchFilterIdx;
|
||||
int& m_bandPassFilterIdx;
|
||||
bool& m_triggerInput;
|
||||
|
||||
private:
|
||||
GtkWidget* m_calibrateDialog = nullptr;
|
||||
bool m_calibrationDone = false;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI
|
||||
+351
@@ -0,0 +1,351 @@
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI
|
||||
|
||||
#include "ovasCDriverGTecGUSBampLegacy.h"
|
||||
#include "ovasCConfigurationGTecGUSBampLegacy.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <windows.h>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <gUSBamp.h>
|
||||
|
||||
#define GTEC_NUM_CHANNELS 16
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
/*
|
||||
This driver always reads 17 channels: 16 + 1
|
||||
16 are EEG channels
|
||||
1 is the last channel that provides triggers from the parallel port of the GTEC
|
||||
Although 17 channels are read only "m_nAcquiredChannel" + 1 (if m_pTriggerInputEnabled==true) are displayed.
|
||||
If m_nAcquiredChannel=6 and m_pTriggerInputEnabled=true then the output in OpenVibe is 7 channels. If m_pTriggerInputEnabled=false then 6.
|
||||
"m_nAcquiredChannel" is a user modifiable variable with default value 16
|
||||
*/
|
||||
|
||||
CDriverGTecGUSBampLegacy::CDriverGTecGUSBampLegacy(IDriverContext& ctx)
|
||||
: IDriver(ctx)
|
||||
, m_settings("AcquisitionServer_Driver_GTecGUSBampLegacy", m_driverCtx.getConfigurationManager())
|
||||
, m_deviceIdx(uint32_t(-1))
|
||||
, m_notchFilterIdx(-1)
|
||||
, m_bandPassFilterIdx(-1)
|
||||
, m_nAcquiredChannel(GTEC_NUM_CHANNELS)
|
||||
{
|
||||
m_header.setSamplingFrequency(512);
|
||||
m_header.setChannelCount(GTEC_NUM_CHANNELS);
|
||||
|
||||
m_settings.add("Header", &m_header);
|
||||
m_settings.add("DeviceIndex", &m_deviceIdx);
|
||||
m_settings.add("CommonGndAndRefBitmap", &m_commonGndAndRefBitmap);
|
||||
m_settings.add("NotchFilterIndex", &m_notchFilterIdx);
|
||||
m_settings.add("BandPassFilterIndex", &m_bandPassFilterIdx);
|
||||
m_settings.add("TriggerInputEnabled", &m_bTriggerInputEnabled);
|
||||
m_settings.load();
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDriverGTecGUSBampLegacy::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected() || !m_header.isChannelCountSet() || !m_header.isSamplingFrequencySet()) { return false; }
|
||||
|
||||
if (m_bTriggerInputEnabled)
|
||||
{
|
||||
m_header.setChannelCount(m_nAcquiredChannel + 1);
|
||||
m_header.setChannelName(m_nAcquiredChannel, "CH_Event");
|
||||
}
|
||||
else { m_header.setChannelCount(m_nAcquiredChannel); }
|
||||
|
||||
// If device has not been selected, try to find a device
|
||||
uint32_t i = 0;
|
||||
m_actualDeviceIdx = m_deviceIdx;
|
||||
while (i < 11 && m_actualDeviceIdx == uint32_t(-1))
|
||||
{
|
||||
HANDLE handle = GT_OpenDevice(i);
|
||||
if (handle)
|
||||
{
|
||||
GT_CloseDevice(&handle);
|
||||
m_actualDeviceIdx = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (m_actualDeviceIdx == uint32_t(-1)) { return false; }
|
||||
|
||||
m_pEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
if (!m_pEvent) { return false; }
|
||||
|
||||
//allocate buffers
|
||||
m_bufferSize = (GTEC_NUM_CHANNELS + 1) * nSamplePerSentBlock * sizeof(float) + HEADER_SIZE;//+1 channel for trigger
|
||||
m_Buffer = new uint8_t[m_bufferSize];
|
||||
|
||||
if (m_bTriggerInputEnabled) { m_sample = new float[(m_nAcquiredChannel + 1) * nSamplePerSentBlock]; }
|
||||
else { m_sample = new float[m_nAcquiredChannel * nSamplePerSentBlock]; }
|
||||
|
||||
if (!m_Buffer || !m_sample)
|
||||
{
|
||||
delete [] m_Buffer;
|
||||
delete [] m_sample;
|
||||
CloseHandle(m_pEvent);
|
||||
return false;
|
||||
}
|
||||
memset(m_Buffer, 0, m_bufferSize);
|
||||
m_sampleTranspose = reinterpret_cast<float*>(m_Buffer + HEADER_SIZE);
|
||||
m_pOverlapped = new OVERLAPPED;
|
||||
|
||||
if (!m_pOverlapped)
|
||||
{
|
||||
delete [] m_Buffer;
|
||||
delete [] m_sample;
|
||||
CloseHandle(m_pEvent);
|
||||
return false;
|
||||
}
|
||||
|
||||
#define m_pOverlapped ((OVERLAPPED*)m_pOverlapped)
|
||||
|
||||
memset(m_pOverlapped, 0, sizeof(OVERLAPPED));
|
||||
m_pOverlapped->hEvent = m_pEvent;
|
||||
|
||||
m_Device = GT_OpenDevice(m_actualDeviceIdx);
|
||||
if (!m_Device)
|
||||
{
|
||||
delete m_pOverlapped;
|
||||
delete [] m_Buffer;
|
||||
delete [] m_sample;
|
||||
CloseHandle(m_pEvent);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_actualImpedanceIdx = 0;
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
m_callback = &callback;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGUSBampLegacy::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
UCHAR channels[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
|
||||
|
||||
// The amplifier is divided in 4 blocks, A to D
|
||||
// each one has its own Ref/gnd connections,
|
||||
// user can specify whether or not to connect the block to the common ground and reference of the amplifier.
|
||||
GND gnd;
|
||||
gnd.GND1 = (m_commonGndAndRefBitmap & 1);
|
||||
gnd.GND2 = (m_commonGndAndRefBitmap & (1 << 1));
|
||||
gnd.GND3 = (m_commonGndAndRefBitmap & (1 << 2));
|
||||
gnd.GND4 = (m_commonGndAndRefBitmap & (1 << 3));
|
||||
|
||||
REF ref;
|
||||
ref.ref1 = (m_commonGndAndRefBitmap & (1 << 4));
|
||||
ref.ref2 = (m_commonGndAndRefBitmap & (1 << 5));
|
||||
ref.ref3 = (m_commonGndAndRefBitmap & (1 << 6));
|
||||
ref.ref4 = (m_commonGndAndRefBitmap & (1 << 7));
|
||||
|
||||
if (!GT_SetMode(m_Device, M_NORMAL)) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unexpected error while calling GT_SetMode\n"; }
|
||||
if (!GT_SetBufferSize(m_Device, m_nSamplePerSentBlock))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unexpected error while calling GT_SetBufferSize\n";
|
||||
}
|
||||
if (!GT_SetChannels(m_Device, channels, sizeof(channels) / sizeof(UCHAR)))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unexpected error while calling GT_SetChannels\n";
|
||||
}
|
||||
if (!GT_SetSlave(m_Device, FALSE)) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unexpected error while calling GT_SetSlave\n"; }
|
||||
if (!GT_EnableTriggerLine(m_Device, TRUE))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unexpected error while calling GT_EnableTriggerLine - the extra input trigger channel is disabled\n";
|
||||
}
|
||||
// GT_EnableSC
|
||||
// GT_SetBipolar
|
||||
|
||||
for (uint32_t i = 0; i < m_nAcquiredChannel; ++i)
|
||||
{
|
||||
if (!GT_SetBandPass(m_Device, i + 1, m_bandPassFilterIdx))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unexpected error while calling GT_SetBandPass for channel " << i << "\n";
|
||||
}
|
||||
if (!GT_SetNotch(m_Device, i + 1, m_notchFilterIdx))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unexpected error while calling GT_SetNotch for channel " << i << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!GT_SetSampleRate(m_Device, m_header.getSamplingFrequency()))
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unexpected error while calling GT_SetSampleRate\n";
|
||||
}
|
||||
|
||||
if (!GT_SetReference(m_Device, ref)) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unexpected error while calling GT_SetReference\n"; }
|
||||
if (!GT_SetGround(m_Device, gnd)) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unexpected error while calling GT_SetGround\n"; }
|
||||
|
||||
m_lastStimulation = STIMULATION_0;
|
||||
m_totalHardwareStimulations = 0;
|
||||
m_totalDriverChunksLost = 0;
|
||||
|
||||
GT_Start(m_Device);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGUSBampLegacy::loop()
|
||||
{
|
||||
CStimulationSet stimSet;
|
||||
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (m_driverCtx.isStarted())
|
||||
{
|
||||
if (GT_GetData(m_Device, m_Buffer, m_bufferSize, m_pOverlapped))
|
||||
{
|
||||
if (WaitForSingleObject(m_pOverlapped->hEvent, 1000) == WAIT_OBJECT_0)
|
||||
{
|
||||
DWORD nByte = 0;
|
||||
|
||||
GetOverlappedResult(m_Device, m_pOverlapped, &nByte, FALSE);
|
||||
|
||||
if (nByte == m_bufferSize)
|
||||
{
|
||||
for (uint32_t i = 0; i < m_nAcquiredChannel; ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < m_nSamplePerSentBlock; ++j)
|
||||
{
|
||||
m_sample[i * m_nSamplePerSentBlock + j] = m_sampleTranspose[j * (GTEC_NUM_CHANNELS + 1) + i];
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bTriggerInputEnabled)
|
||||
{
|
||||
for (uint32_t iSample = 0; iSample < m_nSamplePerSentBlock; ++iSample)
|
||||
{
|
||||
const uint32_t stimCode = uint32_t(
|
||||
m_sampleTranspose[iSample * (GTEC_NUM_CHANNELS + 1) + GTEC_NUM_CHANNELS]);
|
||||
m_sample[m_nAcquiredChannel * m_nSamplePerSentBlock + iSample] = float(stimCode);
|
||||
|
||||
//this means that the user sends 0 after each stimulatuion and in the beginning
|
||||
if ((stimCode != STIMULATION_0) && (stimCode != m_lastStimulation)
|
||||
)
|
||||
{
|
||||
uint64_t identifier;
|
||||
switch (stimCode)
|
||||
{
|
||||
case STIMULATION_64: identifier = OVTK_StimulationId_Label_01;
|
||||
break;
|
||||
case STIMULATION_128: identifier = OVTK_StimulationId_Label_02;
|
||||
break;
|
||||
case STIMULATION_192: identifier = OVTK_StimulationId_Label_03;
|
||||
break;
|
||||
default: identifier = OVTK_StimulationId_Label_07;
|
||||
}
|
||||
|
||||
const uint64_t time = CTime(m_header.getSamplingFrequency(), uint64_t(iSample)).time();
|
||||
stimSet.appendStimulation(identifier, time, 0);
|
||||
m_totalHardwareStimulations++;
|
||||
}
|
||||
|
||||
m_lastStimulation = stimCode;
|
||||
}
|
||||
}
|
||||
|
||||
m_callback->setSamples(m_sample);
|
||||
m_callback->setStimulationSet(stimSet);
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_totalDriverChunksLost++;
|
||||
//m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "nByte and m_bufferSize differs : " << nByte << "/" << m_bufferSize << "(header size is " << HEADER_SIZE << ")\n";
|
||||
/*m_driverCtx.getLogManager() << Kernel::LogLevel_Warning
|
||||
<< "Returned data is different than expected. Total chunks lost: " << m_totalDriverChunksLost
|
||||
<< ", Total samples lost: " << m_nSamplePerSentBlock * m_totalDriverChunksLost
|
||||
<< "\n";*/
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TIMEOUT
|
||||
//m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "timeout 1\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "tError on GT_GetData.\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_driverCtx.isImpedanceCheckRequested())
|
||||
{
|
||||
double impedance = 0;
|
||||
GT_GetImpedance(m_Device, m_actualImpedanceIdx + 1, &impedance);
|
||||
if (impedance < 0) { impedance *= -1; }
|
||||
|
||||
m_driverCtx.updateImpedance(m_actualImpedanceIdx, impedance);
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Channel " << m_actualImpedanceIdx << " - "
|
||||
<< m_header.getChannelName(m_actualImpedanceIdx) << " : " << impedance << "\n";
|
||||
|
||||
m_actualImpedanceIdx++;
|
||||
m_actualImpedanceIdx %= m_header.getChannelCount();
|
||||
|
||||
m_driverCtx.updateImpedance(m_actualImpedanceIdx, -1);
|
||||
}
|
||||
else { System::Time::sleep(20); }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGUSBampLegacy::stop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
//stop device
|
||||
GT_Stop(m_Device);
|
||||
GT_ResetTransfer(m_Device);
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Debug << "Total number of hardware stimulations acquired: " << m_totalHardwareStimulations << "\n";
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Total chunks lost: " << m_totalDriverChunksLost << "\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGUSBampLegacy::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
GT_CloseDevice(&m_Device);
|
||||
CloseHandle(m_pEvent);
|
||||
delete [] m_Buffer;
|
||||
delete [] m_sample;
|
||||
delete m_pOverlapped;
|
||||
m_Device = nullptr;
|
||||
m_pEvent = nullptr;
|
||||
m_Buffer = nullptr;
|
||||
m_sample = nullptr;
|
||||
m_callback = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverGTecGUSBampLegacy::configure()
|
||||
{
|
||||
CConfigurationGTecGUSBampLegacy config(Directories::getDataDir() + "/applications/acquisition-server/interface-GTec-GUSBampLegacy.ui",
|
||||
m_deviceIdx, m_commonGndAndRefBitmap, m_notchFilterIdx, m_bandPassFilterIdx,
|
||||
m_bTriggerInputEnabled);
|
||||
|
||||
if (!config.configure(m_header)) { return false; }
|
||||
this->m_nAcquiredChannel = m_header.getChannelCount();
|
||||
m_settings.save();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CDriverGTecGUSBampLegacy
|
||||
* \author Yann Renard (Inria)
|
||||
* \date unknown
|
||||
* \brief GTEC driver
|
||||
*
|
||||
*/
|
||||
class CDriverGTecGUSBampLegacy final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CDriverGTecGUSBampLegacy(IDriverContext& ctx);
|
||||
void release() { delete this; }
|
||||
const char* getName() override { return "g.tec gUSBamp Legacy"; }
|
||||
|
||||
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 isFlagSet(const EDriverFlag flag) const override { return flag == EDriverFlag::IsDeprecated; }
|
||||
|
||||
protected:
|
||||
|
||||
SettingsHelper m_settings;
|
||||
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
CHeader m_header;
|
||||
|
||||
uint32_t m_nSamplePerSentBlock = 0;
|
||||
uint32_t m_deviceIdx = 0;
|
||||
uint32_t m_actualDeviceIdx = 0;
|
||||
uint32_t m_bufferSize = 0;
|
||||
uint8_t* m_Buffer = nullptr;
|
||||
float* m_sampleTranspose = nullptr;
|
||||
float* m_sample = nullptr;
|
||||
void* m_Device = nullptr;
|
||||
void* m_pEvent = nullptr;
|
||||
void* m_pOverlapped = nullptr;
|
||||
|
||||
uint32_t m_actualImpedanceIdx = 0;
|
||||
|
||||
uint8_t m_commonGndAndRefBitmap = 0;
|
||||
|
||||
int m_notchFilterIdx = 0;
|
||||
int m_bandPassFilterIdx = 0;
|
||||
|
||||
bool m_bTriggerInputEnabled = false;
|
||||
uint32_t m_lastStimulation = 0;
|
||||
|
||||
// EVENT CHANNEL : contribution Anton Andreev (Gipsa-lab) - 0.14.0
|
||||
typedef enum
|
||||
{
|
||||
STIMULATION_0 = 0,
|
||||
STIMULATION_64 = 64,
|
||||
STIMULATION_128 = 128,
|
||||
STIMULATION_192 = 192
|
||||
} gtec_triggers_t;
|
||||
|
||||
uint32_t m_totalHardwareStimulations = 0; //since start button clicked
|
||||
uint32_t m_totalDriverChunksLost = 0; //since start button clicked
|
||||
uint32_t m_nAcquiredChannel = 0; //number of channels 1..16 specified bu user
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI
|
||||
+403
@@ -0,0 +1,403 @@
|
||||
<?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">24</property>
|
||||
<property name="upper">27</property>
|
||||
<property name="value">24</property>
|
||||
<property name="step_increment">3</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment2">
|
||||
<property name="upper">100</property>
|
||||
<property name="value">18</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="GtkAdjustment" id="adjustment4">
|
||||
<property name="upper">255</property>
|
||||
<property name="value">0</property>
|
||||
<property name="lower">0</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
|
||||
|
||||
<object class="GtkListStore" id="model1">
|
||||
<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="model2">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">500</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">250</col>
|
||||
</row>
|
||||
</data>
|
||||
</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_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_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">mBrainTrain configuration (24 channels)</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">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">6</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">False</property>
|
||||
<property name="sensitive">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">3</property>
|
||||
<property name="bottom_attach">4</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="use_markup">True</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_port_number">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Port number :</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="GtkSpinButton" id="spinbutton_port_number">
|
||||
<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>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_gender">
|
||||
<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">2</property>
|
||||
<property name="bottom_attach">3</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">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>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</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">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>
|
||||
</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="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_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="GtkComboBox" id="combobox_sampling_frequency">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">model2</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">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>
|
||||
</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_change_channel_names">
|
||||
<property name="label" translatable="yes">Change channel names</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</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>
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
#include "ovasCConfigurationMBTSmarting.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
/*_________________________________________________
|
||||
|
||||
Insert callback to specific widget here
|
||||
Example with a button that launch a calibration of the device:
|
||||
|
||||
//Callback connected to a dedicated gtk button:
|
||||
static void button_calibrate_pressed_cb(GtkButton* button, void* data)
|
||||
{
|
||||
CConfigurationMBTSmarting* config=static_cast<CConfigurationMBTSmarting*>(data);
|
||||
config->buttonCalibratePressedCB();
|
||||
}
|
||||
|
||||
//Callback actually called:
|
||||
void CConfigurationGTecGUSBamp::buttonCalibratePressedCB()
|
||||
{
|
||||
// Connect to the hardware, ask for calibration, verify the return code, etc.
|
||||
}
|
||||
_________________________________________________*/
|
||||
|
||||
// If you added more reference attribute, initialize them here
|
||||
CConfigurationMBTSmarting::CConfigurationMBTSmarting(IDriverContext& ctx, const char* gtkBuilderFilename, uint32_t& rConnectionId)
|
||||
: CConfigurationBuilder(gtkBuilderFilename), m_driverCtx(ctx), m_connectionID(rConnectionId)
|
||||
{
|
||||
//m_listStore = gtk_list_store_new(1, G_TYPE_STRING);
|
||||
}
|
||||
|
||||
CConfigurationMBTSmarting::~CConfigurationMBTSmarting()
|
||||
{
|
||||
//g_object_unref(m_listStore);
|
||||
}
|
||||
|
||||
bool CConfigurationMBTSmarting::preConfigure()
|
||||
{
|
||||
if (! CConfigurationBuilder::preConfigure()) { return false; }
|
||||
|
||||
// Apply previous port number
|
||||
GtkSpinButton* buttonPortNumber = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder, "spinbutton_port_number"));
|
||||
gtk_spin_button_set_value(buttonPortNumber, m_connectionID);
|
||||
|
||||
// Connect here all callbacks
|
||||
// Example:
|
||||
// g_signal_connect(gtk_builder_get_object(m_builder, "button_calibrate"), "pressed", G_CALLBACK(button_calibrate_pressed_cb), this);
|
||||
|
||||
// Insert here the pre-configure code.
|
||||
// For example, you may want to check if a device is currently connected
|
||||
// and if more than one are connected. Then you can list in a dedicated combo-box
|
||||
// the device currently connected so the user can choose which one he wants to acquire from.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfigurationMBTSmarting::postConfigure()
|
||||
{
|
||||
if (m_applyConfig)
|
||||
{
|
||||
// If the user pressed the "apply" button, you need to save the changes made in the configuration.
|
||||
// For example, you can save the connection ID of the selected device:
|
||||
// m_connectionID = <value-from-gtk-widget>
|
||||
GtkSpinButton* buttonPortNumber = GTK_SPIN_BUTTON(gtk_builder_get_object(m_builder, "spinbutton_port_number"));
|
||||
gtk_spin_button_update(buttonPortNumber);
|
||||
m_connectionID = gtk_spin_button_get_value_as_int(buttonPortNumber);
|
||||
}
|
||||
|
||||
// normal header is filled (Subject ID, Age, Gender, channels, sampling frequency), ressources are realesed
|
||||
return CConfigurationBuilder::postConfigure();
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
#include "ovasIDriver.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CConfigurationMBTSmarting
|
||||
* \author mBrainTrain dev team (mBrainTrain)
|
||||
* \date Tue Oct 14 16:09:43 2014
|
||||
* \brief The CConfigurationMBTSmarting handles the configuration dialog specific to the MBTSmarting device.
|
||||
*
|
||||
* TODO: details
|
||||
*
|
||||
* \sa CDriverMBTSmarting
|
||||
*/
|
||||
class CConfigurationMBTSmarting final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
// you may have to add to your constructor some reference parameters
|
||||
// for example, a connection ID:
|
||||
CConfigurationMBTSmarting(IDriverContext& ctx, const char* gtkBuilderFilename, uint32_t& rConnectionId);
|
||||
//CConfigurationMBTSmarting(IDriverContext& ctx, const char* gtkBuilderFilename);
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
~CConfigurationMBTSmarting() override;
|
||||
|
||||
protected:
|
||||
|
||||
IDriverContext& m_driverCtx;
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Insert here all specific attributes, such as a connection ID.
|
||||
* use references to directly modify the corresponding attribute of the driver
|
||||
* Example:
|
||||
*/
|
||||
uint32_t& m_connectionID;
|
||||
//::GtkListStore* m_listStore;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
#include "ovasCDriverMBTSmarting.h"
|
||||
#include "ovasCConfigurationMBTSmarting.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
CDriverMBTSmarting::CDriverMBTSmarting(IDriverContext& ctx)
|
||||
: IDriver(ctx)
|
||||
, m_settings("AcquisitionServer_Driver_MBTSmarting", m_driverCtx.getConfigurationManager())
|
||||
{
|
||||
m_header.setSamplingFrequency(500);
|
||||
m_header.setChannelCount(27);
|
||||
|
||||
// The following class allows saving and loading driver settings from the acquisition server .conf file
|
||||
m_settings.add("Header", &m_header);
|
||||
// To save your custom driver settings, register each variable to the SettingsHelper
|
||||
m_settings.add("ConnectionID", &m_connectionID);
|
||||
m_settings.load();
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDriverMBTSmarting::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected() || !m_header.isChannelCountSet() || !m_header.isSamplingFrequencySet()) { return false; }
|
||||
|
||||
// Builds up a buffer to store
|
||||
// acquired samples. This buffer
|
||||
// will be sent to the acquisition
|
||||
// server later...
|
||||
m_sample = new float[m_header.getChannelCount()];
|
||||
if (!m_sample)
|
||||
{
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ...
|
||||
// initialize hardware and get
|
||||
// available header information
|
||||
// from it
|
||||
// Using for example the connection ID provided by the configuration (m_connectionID)
|
||||
// ...
|
||||
|
||||
m_pSmartingAmp.reset(new SmartingAmp);
|
||||
|
||||
std::stringstream port_ss;
|
||||
#ifdef TARGET_OS_Windows
|
||||
port_ss << "COM" << m_connectionID;
|
||||
#elif defined TARGET_OS_Linux
|
||||
port_ss << "/dev/rfcomm" << m_connectionID;
|
||||
#endif
|
||||
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Attempting to Connect to Device at : " << port_ss.str() << "\n";
|
||||
|
||||
std::string port(port_ss.str());
|
||||
const bool connected = m_pSmartingAmp->connect(port);
|
||||
if (connected)
|
||||
{
|
||||
// set sampling frequency
|
||||
switch (m_header.getSamplingFrequency())
|
||||
{
|
||||
case 250: m_pSmartingAmp->send_command(FREQUENCY_250);
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Setting the sampling frequency at " << 250 << "\n";
|
||||
break;
|
||||
case 500: m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Setting the sampling frequency at " << 500 << "\n";
|
||||
m_pSmartingAmp->send_command(FREQUENCY_500);
|
||||
break;
|
||||
default: m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Only sampling frequencies 250 and 500 are supported\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Declare channel units
|
||||
for (uint32_t c = 0; c < 24; ++c)
|
||||
{
|
||||
m_header.setChannelUnits(c, OVTK_UNIT_Volts, OVTK_FACTOR_Micro); // signal channels
|
||||
}
|
||||
m_header.setChannelUnits(24, OVTK_UNIT_Degree_Per_Second, OVTK_FACTOR_Base); // gyroscope outputs
|
||||
m_header.setChannelUnits(25, OVTK_UNIT_Degree_Per_Second, OVTK_FACTOR_Base);
|
||||
m_header.setChannelUnits(26, OVTK_UNIT_Degree_Per_Second, OVTK_FACTOR_Base);
|
||||
|
||||
m_header.setChannelName(24, "Gyro 1");
|
||||
m_header.setChannelName(25, "Gyro 2");
|
||||
m_header.setChannelName(26, "Gyro 3");
|
||||
|
||||
// Saves parameters
|
||||
m_callback = &callback;
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDriverMBTSmarting::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// request hardware to start
|
||||
// sending data
|
||||
// ...
|
||||
|
||||
m_pSmartingAmp->send_command(ON);
|
||||
m_byteArray.clear();
|
||||
|
||||
sample_number = 1;
|
||||
latency = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverMBTSmarting::loop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (!m_driverCtx.isStarted()) { return true; }
|
||||
|
||||
const CStimulationSet stimSet;
|
||||
|
||||
// ...
|
||||
// receive samples from hardware
|
||||
// put them the correct way in the sample array
|
||||
// whether the buffer is full, send it to the acquisition server
|
||||
//...
|
||||
|
||||
unsigned char* receiveBuffer = new unsigned char[MAX_PACKAGE_SIZE];
|
||||
|
||||
const int readed = m_pSmartingAmp->read(receiveBuffer, MAX_PACKAGE_SIZE);
|
||||
|
||||
for (int i = 0; i < readed; ++i)
|
||||
{
|
||||
if (!m_byteArray.empty())
|
||||
{
|
||||
m_byteArray.push_back(receiveBuffer[i]);
|
||||
if (m_byteArray.size() == 83)
|
||||
{
|
||||
if (m_byteArray[82] == '<')
|
||||
{
|
||||
if (sample_number % 5000 == 0)
|
||||
{
|
||||
sample_number = 1;
|
||||
|
||||
if (m_driverCtx.getDriftSampleCount() < 2)
|
||||
{
|
||||
m_sample = m_pSmartingAmp->convert_data(m_byteArray);
|
||||
m_callback->setSamples(m_sample, 1);
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sample_number++;
|
||||
|
||||
m_sample = m_pSmartingAmp->convert_data(m_byteArray);
|
||||
m_callback->setSamples(m_sample, 1);
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
}
|
||||
}
|
||||
|
||||
m_byteArray.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_byteArray.empty() && receiveBuffer[i] == '>') { m_byteArray.push_back(receiveBuffer[i]); }
|
||||
}
|
||||
|
||||
if (latency == 300)
|
||||
{
|
||||
latency = 0;
|
||||
m_driverCtx.setInnerLatencySampleCount(-m_driverCtx.getDriftSampleCount());
|
||||
}
|
||||
else { if (latency != 0) { latency++; } }
|
||||
|
||||
// ...
|
||||
// receive events from hardware
|
||||
// and put them the correct way in a CStimulationSet object
|
||||
//...
|
||||
m_callback->setStimulationSet(stimSet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverMBTSmarting::stop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || !m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// request the hardware to stop
|
||||
// sending data
|
||||
// ...
|
||||
m_driverCtx.setInnerLatencySampleCount(0);
|
||||
m_pSmartingAmp->off();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverMBTSmarting::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// uninitialize hardware here
|
||||
// ...
|
||||
|
||||
m_pSmartingAmp->disconnect();
|
||||
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
m_callback = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDriverMBTSmarting::configure()
|
||||
{
|
||||
// Change this line if you need to specify some references to your driver attribute that need configuration, e.g. the connection ID.
|
||||
CConfigurationMBTSmarting config(m_driverCtx, Directories::getDataDir() + "/applications/acquisition-server/interface-MBTSmarting.ui",
|
||||
m_connectionID);
|
||||
|
||||
if (!config.configure(m_header)) { return false; }
|
||||
m_settings.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
#include "ovasCSmartingAmp.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
/**
|
||||
* \class CDriverMBTSmarting
|
||||
* \author mBrainTrain dev team (mBrainTrain)
|
||||
* \date Tue Oct 14 16:09:43 2014
|
||||
* \brief The CDriverMBTSmarting allows the acquisition server to acquire data from a MBTSmarting device.
|
||||
*
|
||||
* TODO: details
|
||||
*
|
||||
* \sa CConfigurationMBTSmarting
|
||||
*/
|
||||
class CDriverMBTSmarting final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CDriverMBTSmarting(IDriverContext& ctx);
|
||||
~CDriverMBTSmarting() override {}
|
||||
const char* getName() override { return "mBrainTrain Smarting"; }
|
||||
|
||||
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; } // change to false if your device is not configurable
|
||||
bool configure() override;
|
||||
const IHeader* getHeader() override { return &m_header; }
|
||||
|
||||
#ifdef TARGET_OS_Windows
|
||||
bool isFlagSet(const EDriverFlag /*flag*/) const override { return false; }
|
||||
#elif defined TARGET_OS_Linux
|
||||
bool isFlagSet(const EDriverFlag /*flag*/) const override { return true; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
SettingsHelper m_settings;
|
||||
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
|
||||
// Replace this generic Header with any specific header you might have written
|
||||
CHeader m_header;
|
||||
|
||||
uint32_t m_nSamplePerSentBlock = 0;
|
||||
float* m_sample = nullptr;
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Insert here all specific attributes, such as USB port number or device ID.
|
||||
* Example :
|
||||
*/
|
||||
uint32_t m_connectionID = 1;
|
||||
std::shared_ptr<SmartingAmp> m_pSmartingAmp;
|
||||
std::vector<unsigned char> m_byteArray;
|
||||
int sample_number = 0;
|
||||
int latency = 0;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
+431
@@ -0,0 +1,431 @@
|
||||
#include "ovasCSmartingAmp.h"
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
#define CHANNEL_SCALAR_FACTOR 2.235174445530706e-2
|
||||
#define GYRO_SCALAR_FACTOR 0.00875
|
||||
|
||||
SmartingAmp::SmartingAmp()
|
||||
{
|
||||
m_port.reset(new boost::asio::serial_port(m_io));
|
||||
m_timer.reset(new boost::asio::deadline_timer(m_io));
|
||||
}
|
||||
|
||||
void SmartingAmp::disconnect()
|
||||
{
|
||||
if (!m_port->is_open()) { return; }
|
||||
m_port->close();
|
||||
}
|
||||
|
||||
void SmartingAmp::write(const unsigned char* data, const size_t& size) const
|
||||
{
|
||||
boost::system::error_code code;
|
||||
m_port->write_some(boost::asio::buffer(data, size), code);
|
||||
}
|
||||
|
||||
int SmartingAmp::read(unsigned char* data, const size_t size) const
|
||||
{
|
||||
boost::system::error_code code;
|
||||
return m_port->read_some(boost::asio::buffer(data, size), code);
|
||||
}
|
||||
|
||||
void SmartingAmp::acquire()
|
||||
{
|
||||
if (m_port.get() == nullptr || !m_port->is_open()) { return; }
|
||||
|
||||
m_port->async_read_some(boost::asio::buffer(m_receiveBuffer, MAX_PACKAGE_SIZE),
|
||||
boost::bind(&SmartingAmp::onReceive, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void SmartingAmp::onReceive(const boost::system::error_code& ec, const size_t bytesTransferred)
|
||||
{
|
||||
std::lock_guard<std::mutex> m(m_on_receive_lock);
|
||||
if (m_port.get() == nullptr || !m_port->is_open()) { return; }
|
||||
|
||||
if (ec)
|
||||
{
|
||||
//acquire();
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < bytesTransferred; ++i)
|
||||
{
|
||||
if (!m_byteArray.empty())
|
||||
{
|
||||
m_byteArray.push_back(m_receiveBuffer[i]);
|
||||
if (m_byteArray.size() == 83)
|
||||
{
|
||||
if (m_byteArray[82] == '<')
|
||||
{
|
||||
float* sample = convert_data(m_byteArray);
|
||||
m_byteArray.clear();
|
||||
m_receivedSamples++;
|
||||
|
||||
m_samples_lock.lock();
|
||||
m_samplesBuffer.push(sample);
|
||||
m_samples_lock.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_failedSamples++;
|
||||
m_byteArray.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_byteArray.empty() && m_receiveBuffer[i] == '>') { m_byteArray.push_back(m_receiveBuffer[i]); }
|
||||
}
|
||||
acquire();
|
||||
}
|
||||
|
||||
std::pair<unsigned char*, int> SmartingAmp::make_command(const Command command)
|
||||
{
|
||||
unsigned char* cmd;
|
||||
std::pair<unsigned char*, size_t> out;
|
||||
switch (command)
|
||||
{
|
||||
case ON: cmd = new unsigned char[4];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = 'O';
|
||||
cmd[2] = 'N';
|
||||
cmd[3] = '<';
|
||||
out = std::make_pair(cmd, 4);
|
||||
break;
|
||||
case OFF: cmd = new unsigned char[5];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = 'O';
|
||||
cmd[2] = 'F';
|
||||
cmd[3] = 'F';
|
||||
cmd[4] = '<';
|
||||
out = std::make_pair(cmd, 5);
|
||||
break;
|
||||
case TEST: cmd = new unsigned char[6];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = 'T';
|
||||
cmd[2] = 'E';
|
||||
cmd[3] = 'S';
|
||||
cmd[4] = 'T';
|
||||
cmd[5] = '<';
|
||||
out = std::make_pair(cmd, 6);
|
||||
break;
|
||||
case NORMAL: cmd = new unsigned char[8];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = 'N';
|
||||
cmd[2] = 'O';
|
||||
cmd[3] = 'R';
|
||||
cmd[4] = 'M';
|
||||
cmd[5] = 'A';
|
||||
cmd[6] = 'L';
|
||||
cmd[7] = '<';
|
||||
out = std::make_pair(cmd, 8);
|
||||
break;
|
||||
case SELECT_CHANNELS: cmd = new unsigned char[8];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = 'S';
|
||||
cmd[2] = 'C';
|
||||
cmd[3] = ';';
|
||||
cmd[4] = 0xFF;
|
||||
cmd[5] = 0xFF;
|
||||
cmd[6] = 0xFF;
|
||||
cmd[7] = '<';
|
||||
out = std::make_pair(cmd, 8);
|
||||
break;
|
||||
case REFON: cmd = new unsigned char[7];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = 'R';
|
||||
cmd[2] = 'E';
|
||||
cmd[3] = 'F';
|
||||
cmd[4] = 'O';
|
||||
cmd[5] = 'N';
|
||||
cmd[6] = '<';
|
||||
out = std::make_pair(cmd, 7);
|
||||
break;
|
||||
case REFOFF: cmd = new unsigned char[8];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = 'R';
|
||||
cmd[2] = 'E';
|
||||
cmd[3] = 'F';
|
||||
cmd[4] = 'O';
|
||||
cmd[5] = 'F';
|
||||
cmd[6] = 'F';
|
||||
cmd[7] = '<';
|
||||
out = std::make_pair(cmd, 8);
|
||||
break;
|
||||
case IMPON: cmd = new unsigned char[7];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = 'I';
|
||||
cmd[2] = 'M';
|
||||
cmd[3] = 'P';
|
||||
cmd[4] = 'O';
|
||||
cmd[5] = 'N';
|
||||
cmd[6] = '<';
|
||||
out = std::make_pair(cmd, 7);
|
||||
break;
|
||||
case IMPOFF: cmd = new unsigned char[8];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = 'I';
|
||||
cmd[2] = 'M';
|
||||
cmd[3] = 'P';
|
||||
cmd[4] = 'O';
|
||||
cmd[5] = 'F';
|
||||
cmd[6] = 'F';
|
||||
cmd[7] = '<';
|
||||
out = std::make_pair(cmd, 8);
|
||||
break;
|
||||
case FREQUENCY_250: cmd = new unsigned char[5];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = '2';
|
||||
cmd[2] = '5';
|
||||
cmd[3] = '0';
|
||||
cmd[4] = '<';
|
||||
out = std::make_pair(cmd, 5);
|
||||
break;
|
||||
case FREQUENCY_500: cmd = new unsigned char[5];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = '5';
|
||||
cmd[2] = '0';
|
||||
cmd[3] = '0';
|
||||
cmd[4] = '<';
|
||||
out = std::make_pair(cmd, 5);
|
||||
break;
|
||||
case NOISE: cmd = new unsigned char[7];
|
||||
cmd[0] = '>';
|
||||
cmd[1] = 'N';
|
||||
cmd[2] = 'O';
|
||||
cmd[3] = 'I';
|
||||
cmd[4] = 'S';
|
||||
cmd[5] = 'E';
|
||||
cmd[6] = '<';
|
||||
out = std::make_pair(cmd, 7);
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
CommandStatus SmartingAmp::send_command(const Command cmd)
|
||||
{
|
||||
const std::pair<unsigned char*, size_t> newCmd = make_command(cmd);
|
||||
write(newCmd.first, newCmd.second);
|
||||
|
||||
unsigned char* response = new unsigned char[OK_RESPONSE_SIZE];
|
||||
const int readed = read(response, OK_RESPONSE_SIZE);
|
||||
return command_status(response, readed);
|
||||
}
|
||||
|
||||
bool SmartingAmp::connect(std::string& portName)
|
||||
{
|
||||
boost::system::error_code errorCode;
|
||||
m_port->open(portName, errorCode);
|
||||
if (errorCode)
|
||||
{
|
||||
std::cout << "error : port->open() failed...com_port_name = " << portName << ", e=" << errorCode.message() << std::endl;
|
||||
disconnect();
|
||||
// m_port->open(portName, error_code);
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
m_port->set_option(boost::asio::serial_port_base::baud_rate(921600));
|
||||
m_port->set_option(boost::asio::serial_port_base::character_size(8));
|
||||
m_port->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
|
||||
m_port->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
|
||||
m_port->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware));
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: After connection is established wait 3s and then you can start using the amp
|
||||
System::Time::sleep(3000);
|
||||
|
||||
// NOTE: Leaving MAX_PORT_SIZE bytes to read for the first time because we are not sure
|
||||
// how many bytes are in serial port buffer
|
||||
read_with_timeout(MAX_PORT_SIZE, 3000);
|
||||
if (m_bytes_readed > 0) { if (m_commandReceiveBuffer[m_bytes_readed - 1] == 'g') { if (send_command(SELECT_CHANNELS) == Success) { return true; } } }
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
CommandStatus SmartingAmp::command_status(const unsigned char* response, const int readed)
|
||||
{
|
||||
if (readed < 4) { return Failure; }
|
||||
|
||||
char* msg = new char[5];
|
||||
msg[0] = response[readed - 4];
|
||||
msg[1] = response[readed - 3];
|
||||
msg[2] = response[readed - 2];
|
||||
msg[3] = response[readed - 1];
|
||||
msg[4] = '\0';
|
||||
|
||||
if (!strcmp(msg, ">OK<")) { return Success; }
|
||||
|
||||
// TODO: Implement timeout expired method
|
||||
return Failure;
|
||||
}
|
||||
|
||||
bool SmartingAmp::start()
|
||||
{
|
||||
send_command(ON);
|
||||
|
||||
m_samplesBuffer = std::queue<float*>();
|
||||
|
||||
m_failedSamples = 0;
|
||||
m_receivedSamples = 0;
|
||||
|
||||
// start reading
|
||||
|
||||
acquire();
|
||||
|
||||
auto f = [this]() { this->m_io.run(); };
|
||||
// auto f = boost::bind(&boost::asio::io_service::run, &m_io);
|
||||
|
||||
acquire_t.reset(new std::thread(f));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float* SmartingAmp::get_sample()
|
||||
{
|
||||
if (!m_samplesBuffer.empty())
|
||||
{
|
||||
m_samples_lock.lock();
|
||||
float* sample = m_samplesBuffer.front();
|
||||
m_samplesBuffer.pop();
|
||||
m_samples_lock.unlock();
|
||||
return sample;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool SmartingAmp::stop()
|
||||
{
|
||||
// NOTE: Stop would rarely return SUCCESS
|
||||
off();
|
||||
|
||||
try
|
||||
{
|
||||
// cancel all async operations
|
||||
m_io.stop();
|
||||
m_io.reset();
|
||||
|
||||
// TODO: Implement proper stopping of the acquiring thread
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
// to do
|
||||
}
|
||||
|
||||
// NOTE: Currently, I am very unhappy with flushing
|
||||
// It slows down everything and after 6 play/stops open vibe is blocked
|
||||
// flush();
|
||||
|
||||
//std::cout << "Failed " << m_failedSamples << std::endl;
|
||||
//std::cout << "Received " << m_receivedSamples << std::endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float* SmartingAmp::convert_data(std::vector<unsigned char> in)
|
||||
{
|
||||
float* converted = new float[30];
|
||||
|
||||
// channel data
|
||||
for (int i = 0; i < 24; ++i) { converted[i] = get_channel_value(in[3 * i + 1], in[3 * i + 2], in[3 * i + 3]); }
|
||||
|
||||
// gyroX
|
||||
converted[24] = get_gyro_value(in[74], in[75]);
|
||||
// gyroY
|
||||
converted[25] = get_gyro_value(in[76], in[77]);
|
||||
// gyroZ
|
||||
converted[26] = get_gyro_value(in[78], in[79]);
|
||||
|
||||
// counter
|
||||
converted[27] = float(in[73]);
|
||||
|
||||
// battery
|
||||
converted[28] = float(in[80] & 0x7F);//add baterry data, removing highest bit, because it's used for impedance measurement
|
||||
converted[29] = in[81]; //add checksum data
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
float SmartingAmp::get_channel_value(const unsigned char first, const unsigned char second, const unsigned char third)
|
||||
{
|
||||
const int firstByte = int(first) & 0xff;
|
||||
const int secondByte = int(second) & 0xff;
|
||||
const int thirdByte = int(third) & 0xff;
|
||||
|
||||
int channelValue = (firstByte << 16) + (secondByte << 8) + thirdByte;
|
||||
|
||||
// Checking if value is positive or negative
|
||||
if (channelValue > 0x007FFFFF) { channelValue = channelValue - 0x01000000; }
|
||||
|
||||
return float(channelValue) * float(CHANNEL_SCALAR_FACTOR);
|
||||
}
|
||||
|
||||
float SmartingAmp::get_gyro_value(const unsigned char first, const unsigned char second)
|
||||
{
|
||||
const int firstByte = int(first) & 0xff;
|
||||
const int secondByte = int(second) & 0xff;
|
||||
|
||||
int out = (firstByte << 8) + secondByte;
|
||||
|
||||
if (out > 0x00007FFF) { out = out - 0x00008000; }
|
||||
else { out = out + 0x00008000; }
|
||||
|
||||
return float(out) * float(GYRO_SCALAR_FACTOR);
|
||||
}
|
||||
|
||||
void SmartingAmp::read_with_timeout(const int size, const size_t timeout)
|
||||
{
|
||||
// Asynchronously read.
|
||||
if (m_port.get() == nullptr || !m_port->is_open()) { return; }
|
||||
|
||||
m_bytes_readed = 0;
|
||||
|
||||
m_port->async_read_some(boost::asio::buffer(m_commandReceiveBuffer, size),
|
||||
boost::bind(&SmartingAmp::read_complete, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
|
||||
|
||||
// Setup a deadline time to implement our timeout.
|
||||
m_timer->expires_from_now(boost::posix_time::milliseconds(timeout));
|
||||
m_timer->async_wait(boost::bind(&SmartingAmp::timeout_expired, this, boost::asio::placeholders::error));
|
||||
|
||||
// This will block until a character is read
|
||||
// or until it is cancelled.
|
||||
m_io.run();
|
||||
|
||||
m_io.stop();
|
||||
// After a timeout & cancel it seems we need
|
||||
// to do a reset for subsequent reads to work.
|
||||
m_port->get_io_service().reset();
|
||||
}
|
||||
|
||||
// Called when the timer's deadline expires.
|
||||
void SmartingAmp::timeout_expired(const boost::system::error_code& /*error*/) { m_io.stop(); }
|
||||
|
||||
// Called when an async read completes or has been cancelled
|
||||
void SmartingAmp::read_complete(const boost::system::error_code& error, const size_t bytesTransferred)
|
||||
{
|
||||
// IMPORTANT NOTE: when timer expires, and port is canceled, read complete is
|
||||
// called for the last time. Its extremly important to cancel the timer even though
|
||||
// it has already expired. So, here it goes
|
||||
if (error) { return; }
|
||||
|
||||
m_bytes_readed = bytesTransferred;
|
||||
m_timer->cancel();
|
||||
}
|
||||
|
||||
void SmartingAmp::off()
|
||||
{
|
||||
const std::pair<unsigned char*, size_t> transformedCmd = make_command(OFF);
|
||||
write(transformedCmd.first, transformedCmd.second);
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
#ifndef SMARTINGAMP_H
|
||||
#define SMARTINGAMP_H
|
||||
|
||||
#ifndef _WIN32_WINNT // Minimum platform is Windows 7
|
||||
#define _WIN32_WINNT 0x0601
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/serial_port.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include <memory> // shared_ptr
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#define OK_RESPONSE_SIZE 4
|
||||
#define MAX_PACKAGE_SIZE 4096
|
||||
#define MAX_PORT_SIZE 1000000
|
||||
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
|
||||
|
||||
enum Command { SELECT_CHANNELS, ON, OFF, TEST, NORMAL, REFON, REFOFF, IMPON, IMPOFF, FREQUENCY_250, FREQUENCY_500, NOISE };
|
||||
|
||||
enum CommandStatus { Success, Failure, TimeoutExpired };
|
||||
|
||||
/**
|
||||
* \class SmartingAmp
|
||||
* \author mBrainTrain dev team (mBrainTrain)
|
||||
* \brief Communicates with a MBTSmarting device.
|
||||
*/
|
||||
|
||||
class SmartingAmp : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
SmartingAmp();
|
||||
~SmartingAmp() {}
|
||||
|
||||
bool connect(std::string& portName);
|
||||
void disconnect();
|
||||
bool start();
|
||||
bool stop();
|
||||
|
||||
/* NOTE: off doesn't wait for response */
|
||||
void off();
|
||||
CommandStatus send_command(Command cmd);
|
||||
CommandStatus command_status(const unsigned char* response, int readed);
|
||||
void write(const unsigned char* data, const size_t& size) const;
|
||||
int read(unsigned char* data, size_t size) const;
|
||||
void acquire();
|
||||
void onReceive(const boost::system::error_code& ec, size_t bytesTransferred);
|
||||
float* get_sample();
|
||||
float* convert_data(std::vector<unsigned char> in);
|
||||
void read_with_timeout(int size, size_t timeout);
|
||||
void read_complete(const boost::system::error_code& error, size_t bytesTransferred);
|
||||
void timeout_expired(const boost::system::error_code& error);
|
||||
float get_channel_value(unsigned char first, unsigned char second, unsigned char third);
|
||||
float get_gyro_value(unsigned char first, unsigned char second);
|
||||
|
||||
private:
|
||||
|
||||
std::pair<unsigned char*, int> make_command(Command command);
|
||||
boost::asio::io_service m_io;
|
||||
std::shared_ptr<boost::asio::serial_port> m_port;
|
||||
unsigned char m_receiveBuffer[MAX_PACKAGE_SIZE];
|
||||
unsigned char m_commandReceiveBuffer[MAX_PORT_SIZE];
|
||||
std::queue<float*> m_samplesBuffer;
|
||||
std::vector<unsigned char> m_byteArray;
|
||||
std::mutex m_on_receive_lock;
|
||||
std::mutex m_read_complete_lock;
|
||||
std::mutex m_samples_lock;
|
||||
std::mutex m_bytes_readed_lock;
|
||||
std::mutex m_timer_expired_lock;
|
||||
int m_failedSamples = 0;
|
||||
int m_receivedSamples = 0;
|
||||
std::shared_ptr<std::thread> acquire_t;
|
||||
std::shared_ptr<boost::asio::deadline_timer> m_timer;
|
||||
|
||||
// read with timeout
|
||||
int m_bytes_readed = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
|
||||
<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>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_title">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Mitsar EEG202</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox">
|
||||
<property name="visible">True</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="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">6</property>
|
||||
<property name="n_columns">2</property>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_gender">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">modelGender</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext1"/>
|
||||
<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="GtkSpinButton" id="spinbutton_age">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment2</property>
|
||||
<property name="climb_rate">1</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="GtkSpinButton" id="spinbutton_identifier">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment1</property>
|
||||
<property name="climb_rate">1</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="GtkLabel" id="label_age">
|
||||
<property name="visible">True</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_identifier">
|
||||
<property name="visible">True</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="label1">
|
||||
<property name="visible">True</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="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Reference :</property>
|
||||
<property name="justify">right</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_ref">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">modelRef</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext2"/>
|
||||
<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>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_number_of_channels">
|
||||
<property name="visible">True</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">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_sampling_frequency">
|
||||
<property name="visible">True</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">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="adjustment">adjustment3</property>
|
||||
</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="GtkComboBox" id="combobox_sampling_frequency">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">modelSamplingFreq</property>
|
||||
<property name="button_sensitivity">off</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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</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="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="checkbutton_EventAndBioChannels">
|
||||
<property name="label" translatable="yes">Event and Bio channels</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label-info">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><span size="smaller" foreground="#602020">All channels are acquired
|
||||
The signal is sampled at 500 Hz</span></property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area">
|
||||
<property name="visible">True</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_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_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="pack_type">end</property>
|
||||
<property name="position">0</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>
|
||||
<object class="GtkListStore" id="modelGender">
|
||||
<columns>
|
||||
<!-- column-name gchararray1 -->
|
||||
<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="modelChan">
|
||||
<columns>
|
||||
<!-- column-name gchararray1 -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">31 EEG + 1 Event + 1 Bio</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">31 EEG + 1 Event + 1 Bio + 3 Synchro</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="modelRef">
|
||||
<columns>
|
||||
<!-- column-name gchararray1 -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Common (A1 et A2)</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Left A1 - Right A2</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="value">1025</property>
|
||||
<property name="upper">65535</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="GtkListStore" id="modelSamplingFreq">
|
||||
<columns>
|
||||
<!-- column-name gchararray1 -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">500</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment3">
|
||||
<property name="lower">33</property>
|
||||
<property name="upper">33</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
</interface>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user