Skip to content

Commit 7b46def

Browse files
committed
Auditory (untested) and haptic blining
1 parent 422ef2c commit 7b46def

7 files changed

Lines changed: 389 additions & 1 deletion

File tree

obci/control/gui/presets/budzik.ini

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,9 @@ info=run interactive synthetic test target and nontarget epochs from real EEG si
5050
launch_file=scenarios/budzik/prototypes/p300_online_synthetic_real_data_test.ini
5151
public_params=
5252
category=Prototypes P300
53+
54+
[P300 online labirynth all modalities]
55+
info=run interactive labirynth P300 on dummy amp with 3 modalities
56+
launch_file=scenarios/budzik/prototypes/modal_p300/p300_labyrinth_dummy.ini
57+
public_params=
58+
category=Prototypes P300

obci/gui/ugm/blinking/ugm_blinking_engine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def _schedule_blink(self, start_time):
6666
self._curr_blink_id = self.id_mgr.get_id()
6767
self._curr_blink_ugm = self.ugm_mgr.get_blink_ugm(self._curr_blink_id)
6868
self._curr_unblink_ugm = self.ugm_mgr.get_unblink_ugm(self._curr_blink_id)
69-
curr_time = self.time_mgr.get_time()
69+
curr_time = self.time_mgr.get_time() #time if next blink I guess?
7070
t = 1000*(curr_time - (time.time()-start_time))
7171
if t < 0:
7272
t = 0.0
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# Author:
4+
# Marian Dovgialo <marian.dowgialo@gmail.com>
5+
6+
from ugm_blinking_engine import UgmBlinkingEngine
7+
from PyQt4.QtGui import QSound
8+
from obci.devices.haptics.HapticsControl import HapticStimulator
9+
from obci.utils import context as ctx
10+
from obci.gui.ugm import ugm_engine
11+
from PyQt4 import QtCore
12+
import time
13+
14+
class UgmModalBlinkingEngine(UgmBlinkingEngine):
15+
"""A class representing ugm application. It is supposed to fire ugm,
16+
receive messages from outside (UGM_UPDATE_MESSAGES) and send`em to
17+
ugm pyqt structure so that it can refresh.
18+
Can be used to evoke P300 of different modalities
19+
(visual, haptic, auditory, combinations).
20+
"""
21+
def __init__(self, p_config_manager, p_connection,
22+
context=ctx.get_dummy_context('UgmBlinkingEngine'),
23+
modalities=['visual',]):
24+
"""Store config manager.
25+
Args:
26+
Modalities: modalities used for stimulation, list of strings.
27+
available options: 'visual', 'auditory', 'haptic'
28+
"""
29+
30+
super(UgmModalBlinkingEngine, self).__init__(p_config_manager,
31+
p_connection,
32+
context)
33+
self.visual = 'visual' in modalities
34+
self.haptic = 'haptic' in modalities
35+
self.auditory = 'auditory' in modalities
36+
37+
#must be here or else they connect to upper class methods
38+
self._blink_timer = QtCore.QTimer(self)
39+
self._blink_timer.setSingleShot(True)
40+
self._blink_timer.connect(self._blink_timer, QtCore.SIGNAL("timeout()"), self._blink)
41+
42+
self._unblink_timer = QtCore.QTimer(self)
43+
self._unblink_timer.setSingleShot(True)
44+
self._unblink_timer.connect(self._unblink_timer, QtCore.SIGNAL("timeout()"), self._unblink)
45+
46+
self._stop_timer = QtCore.QTimer(self)
47+
self._stop_timer.setSingleShot(True)
48+
self._stop_timer.connect(self._stop_timer, QtCore.SIGNAL("timeout()"), self._stop)
49+
50+
51+
if self._run_on_start:
52+
self.start_blinking()
53+
54+
def _init_auditory(self, configs):
55+
soundfiles = configs.get_param('soundfiles').split(';')
56+
sounds = [QSound(f) for f in soundfiles]
57+
assert len(soundfiles) == len(self._active_ids)
58+
assert len(sounds) == len(self._active_ids)
59+
self._sounds = dict(zip(self._active_ids, sounds))
60+
61+
def _init_haptic(self, configs):
62+
ids = configs.get_param("haptic_device").split(":")
63+
vid, pid = [int(i, base=16) for i in ids]
64+
self.stimulator = HapticStimulator(vid, pid)
65+
channel_map = (int(i) for i in configs.get_param('haptic_channels_map').split(';'))
66+
self._haptic_map = dict(zip(self._active_ids, channel_map))
67+
self._haptic_duration = float(configs.get_param('haptic_duration'))
68+
69+
70+
def set_configs(self, configs):
71+
for m in self.mgrs:
72+
m.set_configs(configs)
73+
self._active_ids = [int(i) for i in configs.get_param('active_field_ids').split(';')]
74+
self._blink_duration = float(configs.get_param('blink_duration'))
75+
if self.auditory:
76+
self._init_auditory(configs)
77+
if self.haptic:
78+
self._init_haptic(configs)
79+
self._run_on_start = int(configs.get_param('running_on_start'))
80+
81+
82+
def _blink(self):
83+
'''Do blinking of configured modality'''
84+
curr_blink_global_id = self._active_ids[self._curr_blink_id]
85+
start_time = time.time()
86+
if self.visual:
87+
self.update_from(self._curr_blink_ugm)
88+
if self.auditory:
89+
self._sounds[curr_blink_global_id].play()
90+
if self.haptic:
91+
self.stimulator.stimulate(
92+
self._haptic_map[curr_blink_global_id],
93+
self._haptic_duration)
94+
update_time = time.time()
95+
if self._blinks_count >= 0:
96+
self._blinks_count -= 1
97+
self.connection.send_blink(self._curr_blink_id, update_time)
98+
t = 1000*(self._blink_duration - (time.time() - start_time))
99+
if t < 0:
100+
t = 0.0
101+
self.context['logger'].warning("BLINKER WARNING: blink duration to short for that computer ...")
102+
self._unblink_timer.start(t)
103+
104+
105+
def _unblink(self):
106+
start_time = time.time()
107+
if self.visual:
108+
self.update_from(self._curr_unblink_ugm)
109+
update_time = time.time()
110+
if self._blinks_count == 0 or self.STOP:
111+
self.STOP = False
112+
curr_time = self.time_mgr.get_time()
113+
for m in self.mgrs:
114+
m.reset()
115+
self._stop_timer.start(1000*(curr_time - (time.time()-start_time)))
116+
else:
117+
self._schedule_blink(start_time)
118+
119+
120+
if __name__ == '__main__':
121+
ugm_engine.run()
122+
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
[local_params]
2+
; engin params *************************
3+
internal_ip=127.0.0.1
4+
internal_port=
5+
use_tagger=0
6+
ugm_config=speller_config_8
7+
8+
9+
; blinking engine params ***************
10+
;common to all modalities
11+
modalities=visual;auditory;haptic
12+
;length of visual stimulation, also time of trial epoch before break kicks in
13+
blink_duration=0.1
14+
running_on_start=1
15+
16+
;auditory
17+
;should be in order of active_field_ids
18+
soundfiles=~/dataset/sounds/1.wav;~/dataset/sounds/2.wav;~/dataset/sounds/3.wav
19+
20+
;haptic
21+
haptic_duration=0.1
22+
haptic_device=0403:6010
23+
;in order of active_field_ids
24+
haptic_channels_map=1;2;3
25+
26+
27+
; time manager
28+
blink_min_break=0.1
29+
blink_max_break=0.1
30+
31+
; count manager
32+
blink_count_type=inf
33+
; inf, random, random_sequential, sequential
34+
blink_count_min=10
35+
blink_count_max=15
36+
37+
; id manager
38+
blink_id_type=random_sequential
39+
; sequential, random, random_sequential
40+
blink_id_count=8
41+
42+
; ugm manager
43+
blink_ugm_id_start=101
44+
blink_ugm_id_count=8
45+
blink_id_count=8
46+
blink_ugm_key=color
47+
blink_ugm_value=#00cb21
48+
blink_ugm_type=single
49+
active_field_ids = 0;1;2;3;4;5;6;7
50+
; single, classic
51+
52+
; classic...
53+
blink_ugm_row_count=2
54+
blink_ugm_col_count=4
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
import sys
5+
import thread, os
6+
7+
from multiplexer.multiplexer_constants import peers, types
8+
from obci.control.peer.configured_client import ConfiguredClient
9+
10+
from obci.configs import settings, variables_pb2
11+
from obci.utils import context as ctx
12+
from obci.gui.ugm import ugm_internal_server
13+
from obci.gui.ugm import ugm_config_manager
14+
from obci.gui.ugm.blinking import ugm_modal_blinking_engine
15+
from obci.gui.ugm.blinking import ugm_blinking_connection
16+
from obci.utils.openbci_logging import log_crash
17+
18+
class DummyClient(object):
19+
def __init__(self, params):
20+
self.params = params
21+
def get_param(self, key):
22+
return self.params[key]
23+
24+
class UgmModalBlinkingEnginePeer(ConfiguredClient):
25+
@log_crash
26+
def __init__(self, addresses):
27+
super(UgmModalBlinkingEnginePeer, self).__init__(addresses=addresses, type=peers.UGM_ENGINE_PEER)
28+
context = ctx.get_new_context()
29+
context['logger'] = self.logger
30+
connection = ugm_blinking_connection.UgmBlinkingConnection(settings.MULTIPLEXER_ADDRESSES,
31+
context)
32+
33+
_modalities = self.config.get_param('modalities').split(';')
34+
ENG = ugm_modal_blinking_engine.UgmModalBlinkingEngine(
35+
ugm_config_manager.UgmConfigManager(self.config.get_param('ugm_config')),
36+
connection,
37+
context,
38+
_modalities)
39+
ENG.set_configs(DummyClient(self.config.param_values()))
40+
srv = ugm_internal_server.UdpServer(
41+
ENG,
42+
self.config.get_param('internal_ip'),
43+
int(self.config.get_param('use_tagger')),
44+
context
45+
)
46+
self.set_param('internal_port', str(srv.socket.getsockname()[1]))
47+
thread.start_new_thread(
48+
srv.run,
49+
()
50+
)
51+
self.ready()
52+
ENG.run()
53+
54+
if __name__ == "__main__":
55+
UgmModalBlinkingEnginePeer(settings.MULTIPLEXER_ADDRESSES)
56+
#assume closing ugm should stop all other peers...
57+
sys.exit(1)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
[local_params]
2+
ugm_config=brain2013_config_8_fields_tablet
3+
4+
modalities=visual;auditory;haptic
5+
; blinking engine params ***************
6+
; visual
7+
blink_duration=1
8+
running_on_start=1
9+
10+
;auditory
11+
;should be in order of active_field_ids
12+
soundfiles=~/dataset/sounds/1.wav;~/dataset/sounds/2.wav;~/dataset/sounds/3.wav
13+
14+
;hapitc
15+
haptic_duration=0.8
16+
haptic_device=0403:6010
17+
;in order of active_field_ids
18+
haptic_channels_map=1;2;3
19+
20+
21+
; time manager
22+
blink_min_break=0.08
23+
blink_max_break=0.12
24+
25+
; count manager
26+
blink_count_type=inf
27+
; inf, random, random_sequential, sequential
28+
blink_count_min=64
29+
blink_count_max=80
30+
31+
; id manager
32+
blink_id_type=random_sequential
33+
; sequential, random, random_sequential
34+
blink_id_count=3
35+
36+
; ugm manager
37+
blink_ugm_id_start=1001
38+
blink_ugm_id_count=3
39+
blink_id_count=3
40+
active_field_ids=0;2;5
41+
blink_ugm_key=font_color
42+
blink_ugm_value=#E42525
43+
;blink_ugm_value=#4c05ef
44+
45+
blink_ugm_type=single
46+
; single, classic
47+
48+
; classic...
49+
blink_ugm_row_count=2
50+
blink_ugm_col_count=4
51+
52+
53+

0 commit comments

Comments
 (0)