init
This commit is contained in:
+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
|
||||
Reference in New Issue
Block a user