Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
The Openvibe Group
extras
Commits
08009065
Commit
08009065
authored
Apr 20, 2016
by
Jussi Lindgren
Browse files
Plugins: P300 Visualisation should now send stimuli to AS after rendering
- Updated documentation - Small clean up
parent
0bed7265
Changes
4
Hide whitespace changes
Inline
Side-by-side
plugins/processing/simple-visualisation/CMakeLists.txt
View file @
08009065
...
...
@@ -23,7 +23,9 @@ INCLUDE("FindOpenViBEToolkit")
INCLUDE
(
"FindOpenViBEModuleEBML"
)
INCLUDE
(
"FindOpenViBEModuleSystem"
)
INCLUDE
(
"FindThirdPartyGTK"
)
INCLUDE
(
"FindThirdPartyBoost"
)
#need boost for erp plot
INCLUDE
(
"FindThirdPartyBoost"
)
# need boost for erp plot
INCLUDE
(
"FindThirdPartyBoost_System"
)
# P300 visualizer ASIO part needs this
# ---------------------------------
# Target macros
# Defines target operating system
...
...
plugins/processing/simple-visualisation/doc/Doc_BoxAlgorithm_P300SpellerVisualisation.dox-part
View file @
08009065
...
...
@@ -7,9 +7,9 @@ __________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_P300SpellerVisualisation_Description|
This box can be used with the \ref Doc_BoxAlgorithm_P300SpellerStimulator box in order to
establish
a P300 speller application. The visualisation consists in a matrix of 6 lines and
implement
a P300 speller application. The visualisation consists in a matrix of 6 lines and
columns containing 26 characters and 10 numbers. The lines and columns can be flashed sequentially
resulting in
an evoked potential in the user's brain activity. This evoked potential can be
to cause
an evoked potential in the user's brain activity. This evoked potential can be
detected and used to find which line and which column the user was focused on, thus resulting
in the ability to write text.
...
...
@@ -69,10 +69,12 @@ __________________________________________________________________
* |OVP_DocEnd_BoxAlgorithm_P300SpellerVisualisation_Outputs|
* |OVP_DocBegin_BoxAlgorithm_P300SpellerVisualisation_Output1|
DEPRECATED. This output should not be used anymore. See Miscellaneous Description below.
This output is used to translate incoming flash stimulations depending if they are target or not.
The actual target is determined with the second input. As soon as the target is known, each flash
can be considered as a target flash or not. This can be later use for selecting evoked response
potentials against other responses.
potentials against other responses.
* |OVP_DocEnd_BoxAlgorithm_P300SpellerVisualisation_Output1|
__________________________________________________________________
...
...
@@ -170,5 +172,10 @@ Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_P300SpellerVisualisation_Miscellaneous|
The box relies on the TCP Tagging plugin of the OpenViBE Acquisition Server. A stimulation stream including target/nontarget
stimulations will be sent to the Acquisition Server directly after rendering. This 'software tagging' approach tries to
align the stimulations as well as possible with relation to the underlying EEG stream. It should be more time-accurate than using
the stimulations from the output socket of the box (deprecated). An even better option would be 'hardware tagging', i.e. to
send the stimulations to the amplifier. Hardware tagging is currently not supported due to a lack of a standard for such.
* |OVP_DocEnd_BoxAlgorithm_P300SpellerVisualisation_Miscellaneous|
*/
plugins/processing/simple-visualisation/src/box-algorithms/ovpCBoxAlgorithmP300SpellerVisualisation.cpp
View file @
08009065
...
...
@@ -141,6 +141,15 @@ namespace
}
};
// This callback flushes all accumulated stimulations to the TCP Tagging
// after the rendering has completed.
gboolean
flush_callback
(
gpointer
pUserData
)
{
((
CBoxAlgorithmP300SpellerVisualisation
*
)
pUserData
)
->
flushQueue
();
return
false
;
// Only run once
}
boolean
CBoxAlgorithmP300SpellerVisualisation
::
initialize
(
void
)
{
IBox
&
l_rStaticBoxContext
=
this
->
getStaticBoxContext
();
...
...
@@ -199,6 +208,11 @@ boolean CBoxAlgorithmP300SpellerVisualisation::initialize(void)
m_ui64LastTime
=
0
;
m_pStimulusSender
=
NULL
;
m_uiIdleFuncTag
=
0
;
m_vStimuliQueue
.
clear
();
m_pMainWidgetInterface
=
gtk_builder_new
();
// glade_xml_new(m_sInterfaceFilename.toASCIIString(), "p300-speller-main", NULL);
if
(
!
gtk_builder_add_from_file
(
m_pMainWidgetInterface
,
m_sInterfaceFilename
.
toASCIIString
(),
NULL
))
{
...
...
@@ -274,7 +288,6 @@ boolean CBoxAlgorithmP300SpellerVisualisation::initialize(void)
this
->
getLogManager
()
<<
LogLevel_Warning
<<
"Unable to connect to AS TCP Tagging, stimuli wont be forwarded.
\n
"
;
}
m_bTableInitialized
=
false
;
return
true
;
...
...
@@ -282,6 +295,16 @@ boolean CBoxAlgorithmP300SpellerVisualisation::initialize(void)
boolean
CBoxAlgorithmP300SpellerVisualisation
::
uninitialize
(
void
)
{
{
boost
::
mutex
::
scoped_lock
lock
(
m_oIdleFuncMutex
);
if
(
m_uiIdleFuncTag
)
{
m_vStimuliQueue
.
clear
();
g_source_remove
(
m_uiIdleFuncTag
);
m_uiIdleFuncTag
=
0
;
}
}
if
(
m_pStimulusSender
)
{
delete
m_pStimulusSender
;
...
...
@@ -388,6 +411,17 @@ boolean CBoxAlgorithmP300SpellerVisualisation::processInput(uint32 ui32Index)
boolean
CBoxAlgorithmP300SpellerVisualisation
::
process
(
void
)
{
// Remove possibly dangling idle func, this construct makes sure only one idle func is registered at a time.
{
boost
::
mutex
::
scoped_lock
lock
(
m_oIdleFuncMutex
);
if
(
m_uiIdleFuncTag
)
{
g_source_remove
(
m_uiIdleFuncTag
);
m_uiIdleFuncTag
=
0
;
}
}
// IBox& l_rStaticBoxContext=this->getStaticBoxContext();
IBoxIO
&
l_rDynamicBoxContext
=
this
->
getDynamicBoxContext
();
...
...
@@ -423,9 +457,6 @@ boolean CBoxAlgorithmP300SpellerVisualisation::process(void)
int
l_iColumn
=-
1
;
boolean
l_bIsTarget
=
false
;
// Pass the stimulation to AS in any case
m_pStimulusSender
->
sendStimuli
(
l_ui64StimulationIdentifier
);
if
(
l_ui64StimulationIdentifier
>=
m_ui64RowStimulationBase
&&
l_ui64StimulationIdentifier
<
m_ui64RowStimulationBase
+
m_ui64RowCount
)
{
l_iRow
=
(
int
)(
l_ui64StimulationIdentifier
-
m_ui64RowStimulationBase
);
...
...
@@ -475,17 +506,26 @@ boolean CBoxAlgorithmP300SpellerVisualisation::process(void)
m_pFlashFontDescription
,
m_pNoFlashFontDescription
);
// We now know if this flash corresponds to the current target or not, merge this to the outgoing stimulation stream
if
(
l_bIsTarget
)
{
m_pStimulusSender
->
sendStimuli
(
OVTK_StimulationId_Target
);
boost
::
mutex
::
scoped_lock
lock
(
m_oIdleFuncMutex
);
m_vStimuliQueue
.
push_back
(
OVTK_StimulationId_Target
);
l_oFlaggingStimulationSet
.
appendStimulation
(
OVTK_StimulationId_Target
,
l_pStimulationSet
->
getStimulationDate
(
j
),
0
);
}
else
{
m_pStimulusSender
->
sendStimuli
(
OVTK_StimulationId_NonTarget
);
boost
::
mutex
::
scoped_lock
lock
(
m_oIdleFuncMutex
);
m_vStimuliQueue
.
push_back
(
OVTK_StimulationId_NonTarget
);
l_oFlaggingStimulationSet
.
appendStimulation
(
OVTK_StimulationId_NonTarget
,
l_pStimulationSet
->
getStimulationDate
(
j
),
0
);
}
}
// Pass the stimulation to the server also as-is. If its a flash, it can be differentiated from a 'target' spec because
// its NOT between OVTK_StimulationId_RestStart and OVTK_StimulationId_RestStop stimuli in the generated P300 timeline.
{
boost
::
mutex
::
scoped_lock
lock
(
m_oIdleFuncMutex
);
m_vStimuliQueue
.
push_back
(
l_ui64StimulationIdentifier
);
}
}
m_pTargetFlaggingStimulationEncoder
->
process
(
OVP_GD_Algorithm_StimulationStreamEncoder_InputTriggerId_EncodeBuffer
);
...
...
@@ -567,10 +607,14 @@ boolean CBoxAlgorithmP300SpellerVisualisation::process(void)
&
l_vWidgets
,
NULL
);
m_pStimulusSender
->
sendStimuli
(
OVTK_StimulationId_BaselineStart
);
m_pStimulusSender
->
sendStimuli
(
m_iTargetRow
+
m_ui64RowStimulationBase
);
m_pStimulusSender
->
sendStimuli
(
m_iTargetColumn
+
m_ui64ColumnStimulationBase
);
m_pStimulusSender
->
sendStimuli
(
OVTK_StimulationId_BaselineStop
);
// Merge the current target into the stimulation stream. It can be differentiated
// from a 'flash' spec because it IS between OVTK_StimulationId_RestStart and
// OVTK_StimulationId_RestStop stimulations in the P300 timeline.
{
boost
::
mutex
::
scoped_lock
lock
(
m_oIdleFuncMutex
);
m_vStimuliQueue
.
push_back
(
m_iTargetRow
+
m_ui64RowStimulationBase
);
m_vStimuliQueue
.
push_back
(
m_iTargetColumn
+
m_ui64ColumnStimulationBase
);
}
if
(
l_vWidgets
.
size
()
==
1
)
{
...
...
@@ -708,6 +752,10 @@ boolean CBoxAlgorithmP300SpellerVisualisation::process(void)
{
l_sString
=
"<span color=
\"
darkorange
\"
>"
+
l_sString
+
"</span>"
;
}
else
{
l_sString
=
"<span color=
\"
darkred
\"
>"
+
l_sString
+
"</span>"
;
}
}
l_sString
=
std
::
string
(
gtk_label_get_label
(
m_pResult
))
+
l_sString
;
gtk_label_set_markup
(
m_pResult
,
l_sString
.
c_str
());
...
...
@@ -746,6 +794,12 @@ boolean CBoxAlgorithmP300SpellerVisualisation::process(void)
}
}
// After any possible rendering, we flush the accumulated stimuli. The default idle func is low priority, so it should be run after rendering by gtk.
{
boost
::
mutex
::
scoped_lock
lock
(
m_oIdleFuncMutex
);
m_uiIdleFuncTag
=
g_idle_add
(
flush_callback
,
this
);
}
return
true
;
}
...
...
@@ -892,3 +946,17 @@ void CBoxAlgorithmP300SpellerVisualisation::_cache_collect_child_widget_cb_(CBox
((
std
::
vector
<
::
GtkWidget
*
>*
)
pUserData
)
->
push_back
(
rWidgetStyle
.
pChildWidget
);
}
}
void
CBoxAlgorithmP300SpellerVisualisation
::
flushQueue
(
void
)
{
boost
::
mutex
::
scoped_lock
lock
(
m_oIdleFuncMutex
);
for
(
size_t
i
=
0
;
i
<
m_vStimuliQueue
.
size
();
i
++
)
{
m_pStimulusSender
->
sendStimuli
(
m_vStimuliQueue
[
i
]);
}
m_vStimuliQueue
.
clear
();
// This function will be automatically removed after completion, so set to 0
m_uiIdleFuncTag
=
0
;
}
\ No newline at end of file
plugins/processing/simple-visualisation/src/box-algorithms/ovpCBoxAlgorithmP300SpellerVisualisation.h
View file @
08009065
...
...
@@ -9,6 +9,10 @@
#include <map>
#include <list>
#include <boost/thread.hpp>
#include <boost/thread/condition.hpp>
#include <boost/version.hpp>
// TODO:
// - please move the identifier definitions in ovp_defines.h
// - please include your desciptor in ovp_main.cpp
...
...
@@ -58,6 +62,10 @@ namespace OpenViBEPlugins
void
_cache_collect_widget_cb_
(
CBoxAlgorithmP300SpellerVisualisation
::
SWidgetStyle
&
rWidgetStyle
,
void
*
pUserData
);
void
_cache_collect_child_widget_cb_
(
CBoxAlgorithmP300SpellerVisualisation
::
SWidgetStyle
&
rWidgetStyle
,
void
*
pUserData
);
public:
void
flushQueue
(
void
);
// Sends all accumulated stimuli to the TCP Tagging
protected:
OpenViBE
::
CString
m_sInterfaceFilename
;
...
...
@@ -115,9 +123,14 @@ namespace OpenViBEPlugins
OpenViBE
::
boolean
m_bTableInitialized
;
// @todo refactor to std::pair<long,long> ?
std
::
map
<
unsigned
long
,
std
::
map
<
unsigned
long
,
CBoxAlgorithmP300SpellerVisualisation
::
SWidgetStyle
>
>
m_vCache
;
std
::
list
<
std
::
pair
<
int
,
int
>
>
m_vTargetHistory
;
std
::
vector
<
OpenViBE
::
uint64
>
m_vStimuliQueue
;
guint
m_uiIdleFuncTag
;
boost
::
mutex
m_oIdleFuncMutex
;
StimulusSender
*
m_pStimulusSender
;
};
...
...
@@ -148,7 +161,7 @@ namespace OpenViBEPlugins
rBoxAlgorithmPrototype
.
addInput
(
"Row selection stimulations"
,
OV_TypeId_Stimulations
);
rBoxAlgorithmPrototype
.
addInput
(
"Column selection stimulations"
,
OV_TypeId_Stimulations
);
rBoxAlgorithmPrototype
.
addOutput
(
"Target / Non target flagging"
,
OV_TypeId_Stimulations
);
rBoxAlgorithmPrototype
.
addOutput
(
"Target / Non target flagging
(deprecated)
"
,
OV_TypeId_Stimulations
);
rBoxAlgorithmPrototype
.
addSetting
(
"Interface filename"
,
OV_TypeId_Filename
,
"${Path_Data}/plugins/simple-visualisation/p300-speller.ui"
);
rBoxAlgorithmPrototype
.
addSetting
(
"Row stimulation base"
,
OV_TypeId_Stimulation
,
"OVTK_StimulationId_Label_01"
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment