pyForceDAQ 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyforcedaq/__init__.py +43 -0
- pyforcedaq/__main__.py +42 -0
- pyforcedaq/_lib/__init__.py +1 -0
- pyforcedaq/_lib/lsl.py +56 -0
- pyforcedaq/_lib/misc.py +126 -0
- pyforcedaq/_lib/polling_time_profile.py +52 -0
- pyforcedaq/_lib/process_priority_manager.py +148 -0
- pyforcedaq/_lib/timer.py +45 -0
- pyforcedaq/_lib/types.py +400 -0
- pyforcedaq/_lib/udp_connection.py +326 -0
- pyforcedaq/daq/__init__.py +19 -0
- pyforcedaq/daq/_daq_read_Analog_pydaqmx.py +114 -0
- pyforcedaq/daq/_daq_read_analog_nidaqmx.py +84 -0
- pyforcedaq/daq/_mock_sensor.py +80 -0
- pyforcedaq/daq/_pyATIDAQ.py +306 -0
- pyforcedaq/daq/config.py +13 -0
- pyforcedaq/extras/__init__.py +0 -0
- pyforcedaq/extras/convert.py +275 -0
- pyforcedaq/extras/expyriment_daq_control.py +246 -0
- pyforcedaq/extras/opensesame_daq_control.py +280 -0
- pyforcedaq/extras/read_force_data.py +89 -0
- pyforcedaq/extras/remote_control.py +93 -0
- pyforcedaq/force/__init__.py +13 -0
- pyforcedaq/force/_log.py +18 -0
- pyforcedaq/force/data_recorder.py +400 -0
- pyforcedaq/force/sensor.py +200 -0
- pyforcedaq/force/sensor_process.py +251 -0
- pyforcedaq/gui/__init__.py +6 -0
- pyforcedaq/gui/_gui_status.py +306 -0
- pyforcedaq/gui/_layout.py +104 -0
- pyforcedaq/gui/_level_indicator.py +59 -0
- pyforcedaq/gui/_pg_surface.py +100 -0
- pyforcedaq/gui/_plotter.py +234 -0
- pyforcedaq/gui/_run.py +522 -0
- pyforcedaq/gui/_scaling.py +71 -0
- pyforcedaq/gui/_settings.py +98 -0
- pyforcedaq/gui/forceDAQ_logo.png +0 -0
- pyforcedaq/gui/launcher.py +249 -0
- pyforcedaq-2.0.0.dist-info/METADATA +15 -0
- pyforcedaq-2.0.0.dist-info/RECORD +42 -0
- pyforcedaq-2.0.0.dist-info/WHEEL +4 -0
- pyforcedaq-2.0.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Expyriment convinient functions for the force DAQ remote control
|
|
5
|
+
|
|
6
|
+
(c) O. Lindemann
|
|
7
|
+
|
|
8
|
+
for two sensor setups
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from expyriment import control, io, misc, stimuli
|
|
13
|
+
|
|
14
|
+
from . import remote_control as rc
|
|
15
|
+
|
|
16
|
+
FORCE_SERVER_IP = "192.168.1.2"
|
|
17
|
+
WEAK, FINE, STRONG = [0, 1, 2]
|
|
18
|
+
STR_FULL_FORCE = u"kräftig"
|
|
19
|
+
STR_LESS_FORCE = u"sacht"
|
|
20
|
+
|
|
21
|
+
stopwatch = misc.Clock()
|
|
22
|
+
udp = rc.init_udp_connection()
|
|
23
|
+
print(udp)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def runtimeerror(exp, text):
|
|
27
|
+
stimuli.TextScreen("ERROR", text).present()
|
|
28
|
+
exp.keyboard.wait()
|
|
29
|
+
control.end()
|
|
30
|
+
udp.send(rc.Command.QUIT)
|
|
31
|
+
exit()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def start(exp, time_for_feedback=10):
|
|
35
|
+
""" returns true if feedback is OK
|
|
36
|
+
waits a particular time (in sec) for feedback and
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
udp.send(rc.Command.START)
|
|
40
|
+
stopwatch.reset_stopwatch()
|
|
41
|
+
while True:
|
|
42
|
+
rtn = udp.poll()
|
|
43
|
+
exp.keyboard.check()
|
|
44
|
+
if rtn == rc.Command.FEEDBACK_STARTED:
|
|
45
|
+
break
|
|
46
|
+
if stopwatch.stopwatch_time > time_for_feedback * 1000:
|
|
47
|
+
return False
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def pause(exp, time_for_feedback=60 * 2):
|
|
52
|
+
"""returns true if feedback is OK
|
|
53
|
+
waits a particular for feedback and
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
udp.send(rc.Command.PAUSE)
|
|
57
|
+
stopwatch.reset_stopwatch()
|
|
58
|
+
while True:
|
|
59
|
+
rtn = udp.poll()
|
|
60
|
+
exp.keyboard.check()
|
|
61
|
+
if rtn == rc.Command.FEEDBACK_PAUSED:
|
|
62
|
+
break
|
|
63
|
+
if stopwatch.stopwatch_time > time_for_feedback * 1000:
|
|
64
|
+
return False
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# make connection #
|
|
69
|
+
def make_connection(exp, experiment_name="force_daq"):
|
|
70
|
+
"""hand shake and filename,
|
|
71
|
+
returns pyforcedaq version
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
stimuli.TextScreen("Prepare force recording", "press key if ready").present()
|
|
75
|
+
exp.keyboard.wait()
|
|
76
|
+
stimuli.BlankScreen().present()
|
|
77
|
+
while not udp.connect_peer(FORCE_SERVER_IP):
|
|
78
|
+
stimuli.TextScreen("ERROR while connecting to server",
|
|
79
|
+
"try again or <ESC> to quit").present()
|
|
80
|
+
exp.keyboard.wait()
|
|
81
|
+
stimuli.BlankScreen().present()
|
|
82
|
+
exp.clock.wait(300)
|
|
83
|
+
|
|
84
|
+
stimuli.TextScreen("Connected", "").present()
|
|
85
|
+
exp.clock.wait(500)
|
|
86
|
+
udp.send(rc.Command.FILENAME.decode('utf-8', 'replace') + "{0}_{1}.csv".format(experiment_name, exp.subject))
|
|
87
|
+
rtn = udp.receive(5) # paused
|
|
88
|
+
if rtn is None:
|
|
89
|
+
runtimeerror(exp, "Force server not responding")
|
|
90
|
+
version = rc.get_data(rc.Command.GET_VERSION)
|
|
91
|
+
stimuli.TextScreen("Connected", "Version " + version).present()
|
|
92
|
+
exp.clock.wait(1000)
|
|
93
|
+
return version
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def force_button_box_prepare(n_sensors=1):
|
|
97
|
+
udp.clear_receive_buffer()
|
|
98
|
+
udp.send(rc.Command.SET_LEVEL_CHANGE_DETECTION)
|
|
99
|
+
if n_sensors>1:
|
|
100
|
+
udp.send(rc.Command.SET_LEVEL_CHANGE_DETECTION2)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def force_button_box_check():
|
|
104
|
+
"""
|
|
105
|
+
changes to level
|
|
106
|
+
"""
|
|
107
|
+
evt, level = rc.poll_multiple_events([rc.Command.CHANGED_LEVEL, rc.Command.CHANGED_LEVEL2])
|
|
108
|
+
if evt is not None:
|
|
109
|
+
if evt == rc.Command.CHANGED_LEVEL:
|
|
110
|
+
sensor = 1
|
|
111
|
+
else:
|
|
112
|
+
sensor = 2
|
|
113
|
+
return (sensor, level)
|
|
114
|
+
return (None, None)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def force_button_box_wait(exp, duration=None, minimum_level=-1):
|
|
118
|
+
"""
|
|
119
|
+
returns if one of two sensors changes its level
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
stopwatch.reset_stopwatch()
|
|
123
|
+
last_key = None
|
|
124
|
+
rt = None
|
|
125
|
+
force_button_box_prepare()
|
|
126
|
+
while not(duration is not None and stopwatch.stopwatch_time > duration):
|
|
127
|
+
sensor, level = force_button_box_check()
|
|
128
|
+
if sensor is not None:
|
|
129
|
+
if level>=minimum_level:
|
|
130
|
+
rt = stopwatch.stopwatch_time
|
|
131
|
+
break
|
|
132
|
+
else:
|
|
133
|
+
force_button_box_prepare()
|
|
134
|
+
sensor = None
|
|
135
|
+
last_key = exp.keyboard.check()
|
|
136
|
+
if last_key is not None:
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
return sensor, level, rt, last_key
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def wait_no_button_pressed(exp, feedback_stimulus=None, polling_intervall=500):
|
|
143
|
+
"""level detection needs to be switch on
|
|
144
|
+
display feedback_stimulus (optional) if one button pressed
|
|
145
|
+
"""
|
|
146
|
+
if rc.get_data(rc.Command.GET_THRESHOLD_LEVEL) > 0 or \
|
|
147
|
+
rc.get_data(rc.Command.GET_THRESHOLD_LEVEL) > 0:
|
|
148
|
+
if feedback_stimulus is not None:
|
|
149
|
+
feedback_stimulus.present()
|
|
150
|
+
while rc.get_data(rc.Command.GET_THRESHOLD_LEVEL) > 0 or \
|
|
151
|
+
rc.get_data(rc.Command.GET_THRESHOLD_LEVEL) > 0:
|
|
152
|
+
exp.keyboard.wait(duration=polling_intervall)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
############ further convenient functions
|
|
156
|
+
def hold_check(exp, holding_time, background_stimulus=None,
|
|
157
|
+
n_sensors=2): # FIXME not checked for sensor=1
|
|
158
|
+
if background_stimulus is None:
|
|
159
|
+
background_stimulus = stimuli.BlankScreen()
|
|
160
|
+
|
|
161
|
+
background_stimulus.present()
|
|
162
|
+
background_stimulus.present()
|
|
163
|
+
udp.send("hold:test")
|
|
164
|
+
|
|
165
|
+
fine = stimuli.Circle(radius=20, colour=misc.constants.C_GREY, line_width=0)
|
|
166
|
+
too_low = stimuli.Circle(radius=20, colour=misc.constants.C_GREEN, line_width=0)
|
|
167
|
+
too_strong = stimuli.Circle(radius=20, colour=misc.constants.C_RED, line_width=0)
|
|
168
|
+
bkg = [stimuli.Circle(position=(-200, 0), radius=24, colour=misc.constants.C_BLACK, line_width=0),
|
|
169
|
+
stimuli.Circle(position=(200, 0), radius=24, colour=misc.constants.C_BLACK, line_width=0)]
|
|
170
|
+
|
|
171
|
+
key = None
|
|
172
|
+
stopwatch.reset_stopwatch()
|
|
173
|
+
|
|
174
|
+
while key is None and stopwatch.stopwatch_time < holding_time:
|
|
175
|
+
key = exp.keyboard.check()
|
|
176
|
+
udp.clear_receive_buffer()
|
|
177
|
+
lv = [rc.get_data(rc.Command.GET_THRESHOLD_LEVEL),
|
|
178
|
+
rc.get_data(rc.Command.GET_THRESHOLD_LEVEL2)]
|
|
179
|
+
for i in range(n_sensors):
|
|
180
|
+
bkg[i].clear_surface()
|
|
181
|
+
if lv[i] == WEAK:
|
|
182
|
+
too_low.plot(bkg[i])
|
|
183
|
+
stopwatch.reset_stopwatch()
|
|
184
|
+
elif lv[i] == STRONG:
|
|
185
|
+
too_strong.plot(bkg[i])
|
|
186
|
+
stopwatch.reset_stopwatch()
|
|
187
|
+
elif lv[i] == FINE:
|
|
188
|
+
fine.plot(bkg[i])
|
|
189
|
+
bkg[i].present(clear=False, update=False)
|
|
190
|
+
bkg[i].present(clear=False, update=False)
|
|
191
|
+
exp.screen.update()
|
|
192
|
+
|
|
193
|
+
background_stimulus.present()
|
|
194
|
+
|
|
195
|
+
def _text2number_array(txt):
|
|
196
|
+
"""helper function for textinput_thesholds """
|
|
197
|
+
rtn = []
|
|
198
|
+
try:
|
|
199
|
+
for x in txt.split(","):
|
|
200
|
+
rtn.append(float(x))
|
|
201
|
+
return rtn
|
|
202
|
+
except:
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def textinput_thesholds(message="Enter thresholds"):
|
|
207
|
+
thresholds = _text2number_array(io.TextInput(message=message).get())
|
|
208
|
+
stimuli.BlankScreen().present()
|
|
209
|
+
if thresholds is not None:
|
|
210
|
+
if len(thresholds) != 2:
|
|
211
|
+
thresholds = None
|
|
212
|
+
return thresholds
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def threshold_menu(exp, thresholds, last_item="Ende"):
|
|
216
|
+
while True:
|
|
217
|
+
select = io.TextMenu(heading=u"Schwellen: " + str(thresholds),
|
|
218
|
+
menu_items=["Schwellen Anpassen ", # 0
|
|
219
|
+
"Halten", # 1
|
|
220
|
+
last_item],
|
|
221
|
+
background_colour=misc.constants.C_GREY,
|
|
222
|
+
text_colour=misc.constants.C_BLACK,
|
|
223
|
+
width=500)
|
|
224
|
+
x = select.get()
|
|
225
|
+
blank = stimuli.BlankScreen()
|
|
226
|
+
blank.present()
|
|
227
|
+
|
|
228
|
+
if x == 0:
|
|
229
|
+
new = textinput_thesholds()
|
|
230
|
+
if new is not None:
|
|
231
|
+
thresholds = new
|
|
232
|
+
elif x == 1:
|
|
233
|
+
if not start(exp):
|
|
234
|
+
stimuli.TextScreen("ERROR: Could not start recording",
|
|
235
|
+
"Press key to quit").present()
|
|
236
|
+
exp.keyboard.wait()
|
|
237
|
+
exit()
|
|
238
|
+
exp.clock.wait(500)
|
|
239
|
+
rc.set_force_thresholds(lower=thresholds[0], upper=thresholds[1])
|
|
240
|
+
hold_check(exp, holding_time=10000)
|
|
241
|
+
blank.present()
|
|
242
|
+
pause(exp)
|
|
243
|
+
else:
|
|
244
|
+
break
|
|
245
|
+
|
|
246
|
+
return thresholds
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Openseame Object or the force DAQ remote control
|
|
5
|
+
|
|
6
|
+
(c) O. Lindemann
|
|
7
|
+
|
|
8
|
+
v0.9
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from libopensesame.exceptions import osexception
|
|
13
|
+
from libopensesame.experiment import experiment
|
|
14
|
+
from openexp.canvas import canvas
|
|
15
|
+
from openexp.keyboard import keyboard
|
|
16
|
+
|
|
17
|
+
from . import remote_control as rc
|
|
18
|
+
|
|
19
|
+
FORCE_SERVER_IP = "192.168.1.1"
|
|
20
|
+
WEAK, FINE, STRONG = [0, 1, 2]
|
|
21
|
+
|
|
22
|
+
class OpensesameDAQControl():
|
|
23
|
+
|
|
24
|
+
def __init__(self, opensesame_experiment):
|
|
25
|
+
""" OpenSesame clock and var"""
|
|
26
|
+
|
|
27
|
+
if isinstance(opensesame_experiment, experiment):
|
|
28
|
+
self._exp = opensesame_experiment
|
|
29
|
+
else:
|
|
30
|
+
raise osexception("opensesame_experiment needs to be an instance of " +\
|
|
31
|
+
"opensesame.experiment.experiment")
|
|
32
|
+
|
|
33
|
+
self.clock = ExpyClock(self._exp.clock)
|
|
34
|
+
self.subject_number = self._exp.var.get(u'subject_nr')
|
|
35
|
+
self.experiment_name = self._exp.var.get(u'experiment_file').split('.')[0]
|
|
36
|
+
self.udp = rc.init_udp_connection()
|
|
37
|
+
self._exp.cleanup_functions.append(self.quit_recording)
|
|
38
|
+
|
|
39
|
+
def __del__(self):
|
|
40
|
+
self.quit_recording()
|
|
41
|
+
|
|
42
|
+
def start(self, time_for_feedback=10):
|
|
43
|
+
""" returns true if feedback is OK
|
|
44
|
+
waits a particular time (in sec) for feedback and
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
self.udp.send(rc.Command.START)
|
|
48
|
+
self.clock.reset_stopwatch()
|
|
49
|
+
kbd = keyboard(self._exp)
|
|
50
|
+
while True:
|
|
51
|
+
rtn = self.udp.poll()
|
|
52
|
+
kbd.get_key(timeout=0) # just for keyboard processing
|
|
53
|
+
if rtn == rc.Command.FEEDBACK_STARTED:
|
|
54
|
+
break
|
|
55
|
+
if self.clock.stopwatch_time > time_for_feedback*1000:
|
|
56
|
+
msg = "ERROR: Could not start recording <br/> Press key to quit"
|
|
57
|
+
cnv = canvas(self._exp)
|
|
58
|
+
cnv.text(msg)
|
|
59
|
+
cnv.show()
|
|
60
|
+
kbd.get_key()
|
|
61
|
+
self._exp.end()
|
|
62
|
+
exit()
|
|
63
|
+
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
def stop(self):
|
|
67
|
+
self.udp.send(rc.Command.QUIT)
|
|
68
|
+
|
|
69
|
+
def quit_recording(self):
|
|
70
|
+
if self.udp is not None:
|
|
71
|
+
self.udp.send(rc.Command.QUIT)
|
|
72
|
+
self.udp = None
|
|
73
|
+
|
|
74
|
+
def pause(self, time_for_feedback=60 * 2, text_saving_time ="Please wait..."):
|
|
75
|
+
"""returns true if feedback is OK (that means data are saved)
|
|
76
|
+
waits for a particular for feedback
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
self.udp.send(rc.Command.PAUSE)
|
|
80
|
+
self.clock.reset_stopwatch()
|
|
81
|
+
kbd = keyboard(self._exp)
|
|
82
|
+
if text_saving_time != None:
|
|
83
|
+
cnv = canvas(self._exp)
|
|
84
|
+
cnv.text(text_saving_time)
|
|
85
|
+
cnv.show()
|
|
86
|
+
while True:
|
|
87
|
+
rtn = self.udp.poll()
|
|
88
|
+
kbd.get_key(timeout=1)
|
|
89
|
+
if rtn == rc.Command.FEEDBACK_PAUSED:
|
|
90
|
+
break
|
|
91
|
+
if self.clock.stopwatch_time > time_for_feedback * 1000:
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
if text_saving_time != None:
|
|
95
|
+
canvas(self._exp).show()
|
|
96
|
+
|
|
97
|
+
return True
|
|
98
|
+
|
|
99
|
+
# make connection #
|
|
100
|
+
def make_connection(self, ip=FORCE_SERVER_IP):
|
|
101
|
+
"""hand shake and filename,
|
|
102
|
+
returns pyforcedaq version
|
|
103
|
+
"""
|
|
104
|
+
kbd = keyboard(self._exp)
|
|
105
|
+
cnv = canvas(self._exp)
|
|
106
|
+
|
|
107
|
+
cnv.text("Prepare force recording <br> press key if ready")
|
|
108
|
+
cnv.show()
|
|
109
|
+
kbd.get_key()
|
|
110
|
+
canvas(self._exp).show()
|
|
111
|
+
|
|
112
|
+
while not self.udp.connect_peer(ip):
|
|
113
|
+
cnv = canvas(self._exp)
|
|
114
|
+
cnv.text("ERROR while connecting to server <br> try again or Q to quit")
|
|
115
|
+
cnv.show()
|
|
116
|
+
key = kbd.get_key()
|
|
117
|
+
if key[0] == u'q':
|
|
118
|
+
msg = "Experiment quitted by user!"
|
|
119
|
+
self.udp.send(rc.Command.QUIT)
|
|
120
|
+
print(msg)
|
|
121
|
+
self._exp.end()
|
|
122
|
+
exit()
|
|
123
|
+
canvas(self._exp).show()
|
|
124
|
+
self.clock.wait(300)
|
|
125
|
+
|
|
126
|
+
cnv = canvas(self._exp)
|
|
127
|
+
cnv.text("Connected")
|
|
128
|
+
cnv.show()
|
|
129
|
+
self.clock.wait(500)
|
|
130
|
+
self.udp.send(rc.Command.FILENAME.decode('utf-8', 'replace') + "{0}_{1}.csv".format(self.experiment_name,
|
|
131
|
+
self.subject_number))
|
|
132
|
+
rtn = self.udp.receive(5) # paused
|
|
133
|
+
if rtn is None:
|
|
134
|
+
msg = "Force server not responding"
|
|
135
|
+
cnv = canvas(self._exp)
|
|
136
|
+
cnv.text(msg)
|
|
137
|
+
cnv.show()
|
|
138
|
+
kbd.get_key()
|
|
139
|
+
self.udp.send(rc.Command.QUIT)
|
|
140
|
+
print(msg)
|
|
141
|
+
self._exp.end()
|
|
142
|
+
exit()
|
|
143
|
+
version = rc.get_data(rc.Command.GET_VERSION)
|
|
144
|
+
if version is None:
|
|
145
|
+
version = "" # FIXME Why is version somethimes None
|
|
146
|
+
cnv = canvas(self._exp)
|
|
147
|
+
cnv.text("Connected <br> Version " + version)
|
|
148
|
+
cnv.show()
|
|
149
|
+
self.clock.wait(1000)
|
|
150
|
+
return version
|
|
151
|
+
|
|
152
|
+
def force_button_box_prepare(self, n_sensors=1):
|
|
153
|
+
self.udp.clear_receive_buffer()
|
|
154
|
+
self.udp.send(rc.Command.SET_LEVEL_CHANGE_DETECTION)
|
|
155
|
+
if n_sensors>1:
|
|
156
|
+
self.udp.send(rc.Command.SET_LEVEL_CHANGE_DETECTION2)
|
|
157
|
+
|
|
158
|
+
def force_button_box_check(self):
|
|
159
|
+
"""
|
|
160
|
+
changes to level
|
|
161
|
+
"""
|
|
162
|
+
evt, level = rc.poll_multiple_events([rc.Command.CHANGED_LEVEL,
|
|
163
|
+
rc.Command.CHANGED_LEVEL2])
|
|
164
|
+
if evt is not None:
|
|
165
|
+
if evt == rc.Command.CHANGED_LEVEL:
|
|
166
|
+
sensor = 1
|
|
167
|
+
else:
|
|
168
|
+
sensor = 2
|
|
169
|
+
return (sensor, level)
|
|
170
|
+
return (None, None)
|
|
171
|
+
|
|
172
|
+
def force_button_box_wait(self, duration=None, minimum_level=-1):
|
|
173
|
+
"""
|
|
174
|
+
returns if one of two sensors changes its level
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
self.clock.reset_stopwatch()
|
|
178
|
+
kbd = keyboard(self._exp)
|
|
179
|
+
last_key = None
|
|
180
|
+
rt = None
|
|
181
|
+
self.force_button_box_prepare()
|
|
182
|
+
while not (duration is not None and self.clock.stopwatch_time > duration):
|
|
183
|
+
sensor, level = self.force_button_box_check()
|
|
184
|
+
if sensor is not None:
|
|
185
|
+
if level >= minimum_level:
|
|
186
|
+
rt = self.clock.stopwatch_time
|
|
187
|
+
break
|
|
188
|
+
else:
|
|
189
|
+
self.force_button_box_prepare()
|
|
190
|
+
sensor = None
|
|
191
|
+
last_key, _ = kbd.get_key(timeout=0)
|
|
192
|
+
if last_key is not None:
|
|
193
|
+
break
|
|
194
|
+
|
|
195
|
+
return sensor, level, rt, last_key
|
|
196
|
+
|
|
197
|
+
def wait_no_button_pressed(self, feedback_stimulus_text=None, polling_intervall=500):
|
|
198
|
+
"""level detection needs to be switch on
|
|
199
|
+
display feedback_stimulus (optional) if one button pressed
|
|
200
|
+
"""
|
|
201
|
+
if rc.get_data(rc.Command.GET_THRESHOLD_LEVEL) > 0 or \
|
|
202
|
+
rc.get_data(rc.Command.GET_THRESHOLD_LEVEL) > 0:
|
|
203
|
+
if feedback_stimulus_text is not None:
|
|
204
|
+
cnv = canvas(self._exp)
|
|
205
|
+
cnv.text(feedback_stimulus_text)
|
|
206
|
+
cnv.show()
|
|
207
|
+
kbd = keyboard(self._exp)
|
|
208
|
+
while rc.get_data(rc.Command.GET_THRESHOLD_LEVEL) > 0 or \
|
|
209
|
+
rc.get_data(rc.Command.GET_THRESHOLD_LEVEL) > 0:
|
|
210
|
+
kbd.get_key(timeout=polling_intervall)
|
|
211
|
+
|
|
212
|
+
def set_thresholds(self, lower, upper):
|
|
213
|
+
rc.set_force_thresholds(lower=lower, upper=upper)
|
|
214
|
+
|
|
215
|
+
def hold_check(self, holding_time=3000,
|
|
216
|
+
left_pos=-200, right_pos=200, radius=50,
|
|
217
|
+
col_fine='gray',
|
|
218
|
+
col_too_low='green',
|
|
219
|
+
col_too_strong='red',
|
|
220
|
+
n_sensors=2):
|
|
221
|
+
kbd = keyboard(self._exp)
|
|
222
|
+
blank = canvas(self._exp)
|
|
223
|
+
blank.show()
|
|
224
|
+
|
|
225
|
+
self.udp.send("hold:test")
|
|
226
|
+
self.clock.reset_stopwatch()
|
|
227
|
+
prev_lv = None
|
|
228
|
+
while True:
|
|
229
|
+
self.udp.clear_receive_buffer()
|
|
230
|
+
lv = [rc.get_data(rc.Command.GET_THRESHOLD_LEVEL)]
|
|
231
|
+
if n_sensors>1:
|
|
232
|
+
lv.append(rc.get_data(rc.Command.GET_THRESHOLD_LEVEL2))
|
|
233
|
+
else:
|
|
234
|
+
lv.append(lv[0]) # just double, if one sensor
|
|
235
|
+
|
|
236
|
+
if prev_lv != lv:
|
|
237
|
+
# level has changes
|
|
238
|
+
self.clock.reset_stopwatch()
|
|
239
|
+
prev_lv = lv
|
|
240
|
+
cnv = canvas(self._exp)
|
|
241
|
+
for i, pos in enumerate([left_pos, right_pos]):
|
|
242
|
+
if lv[i] == WEAK:
|
|
243
|
+
cnv.circle(x=pos, y=0, r=radius, fill=True,
|
|
244
|
+
color=col_too_low)
|
|
245
|
+
elif lv[i] == STRONG:
|
|
246
|
+
cnv.circle(x=pos, y=0, r=radius, fill=True,
|
|
247
|
+
color=col_too_strong)
|
|
248
|
+
elif lv[i] == FINE:
|
|
249
|
+
cnv.circle(x=pos, y=0, r=radius, fill=True,
|
|
250
|
+
color=col_fine)
|
|
251
|
+
cnv.show()
|
|
252
|
+
|
|
253
|
+
key, _ = kbd.get_key(timeout=0)
|
|
254
|
+
if (lv == [FINE, FINE] and self.clock.stopwatch_time > holding_time) or\
|
|
255
|
+
(key is not None):
|
|
256
|
+
break
|
|
257
|
+
|
|
258
|
+
blank.show()
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class ExpyClock():
|
|
262
|
+
"""Expyriment-like stopwatch based on Opensesame clock"""
|
|
263
|
+
|
|
264
|
+
def __init__(self, opensesame_clock):
|
|
265
|
+
self._clock = opensesame_clock
|
|
266
|
+
self.reset_stopwatch()
|
|
267
|
+
|
|
268
|
+
@property
|
|
269
|
+
def time(self):
|
|
270
|
+
return self._clock.time()
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def stopwatch_time(self):
|
|
274
|
+
return self._clock.time() - self._start
|
|
275
|
+
|
|
276
|
+
def reset_stopwatch(self):
|
|
277
|
+
self._start = self._clock.time()
|
|
278
|
+
|
|
279
|
+
def wait(self, waiting_time):
|
|
280
|
+
return self._clock.sleep(waiting_time)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Functions to read your force and event data
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__author__ = 'Oliver Lindemann'
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import gzip
|
|
10
|
+
from collections import OrderedDict
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
TAG_COMMENTS = "#"
|
|
14
|
+
TAG_UDPDATA = TAG_COMMENTS + "UDP"
|
|
15
|
+
TAG_DAQEVENTS = TAG_COMMENTS + "T"
|
|
16
|
+
|
|
17
|
+
def _csv(line):
|
|
18
|
+
return list(map(lambda x: x.strip(), line.split(",")))
|
|
19
|
+
|
|
20
|
+
def DataFrameDict(data, varnames):
|
|
21
|
+
"""data frame: Dict of numpy arrays
|
|
22
|
+
|
|
23
|
+
does not require Pandas, but can be easily converted to pandas dataframe
|
|
24
|
+
via pandas.DataFrame(data_frame_dict)
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
rtn = OrderedDict()
|
|
29
|
+
for v in varnames:
|
|
30
|
+
rtn[v] = []
|
|
31
|
+
|
|
32
|
+
for row in data:
|
|
33
|
+
for v, d in zip(varnames, row):
|
|
34
|
+
rtn[v].append(d)
|
|
35
|
+
|
|
36
|
+
return rtn
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def data_frame_to_text(data_frame):
|
|
40
|
+
rtn = ",".join(data_frame.keys())
|
|
41
|
+
rtn += "\n"
|
|
42
|
+
for x in np.array(list(data_frame.values())).T:
|
|
43
|
+
rtn += ",".join(x) + "\n"
|
|
44
|
+
return rtn
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def read_raw_data(path):
|
|
48
|
+
"""reading trigger and udp data
|
|
49
|
+
|
|
50
|
+
Returns: data, udp_event, daq_events and comments
|
|
51
|
+
|
|
52
|
+
data, udp_event, daq_events: DataFrameDict
|
|
53
|
+
comments: text string
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
daq_events = []
|
|
57
|
+
udp_events = []
|
|
58
|
+
comments = ""
|
|
59
|
+
data = []
|
|
60
|
+
varnames = None
|
|
61
|
+
app_dir = os.path.split(sys.argv[0])[0]
|
|
62
|
+
path = os.path.abspath(os.path.join(app_dir, path))
|
|
63
|
+
|
|
64
|
+
if path.endswith("gz"):
|
|
65
|
+
fl = gzip.open(path, "rt")
|
|
66
|
+
else:
|
|
67
|
+
fl = open(path, "rt")
|
|
68
|
+
|
|
69
|
+
for ln in fl:
|
|
70
|
+
if ln.startswith(TAG_COMMENTS):
|
|
71
|
+
comments += ln
|
|
72
|
+
if ln.startswith(TAG_UDPDATA + ","):
|
|
73
|
+
udp_events.append(_csv(ln[len(TAG_UDPDATA) + 1:]))
|
|
74
|
+
elif ln.startswith(TAG_DAQEVENTS):
|
|
75
|
+
daq_events.append(_csv(ln[len(TAG_DAQEVENTS) + 1:]))
|
|
76
|
+
else:
|
|
77
|
+
# data
|
|
78
|
+
if varnames is None:
|
|
79
|
+
# first row contains varnames
|
|
80
|
+
varnames = _csv(ln)
|
|
81
|
+
else:
|
|
82
|
+
data.append(_csv(ln))
|
|
83
|
+
fl.close()
|
|
84
|
+
|
|
85
|
+
return (DataFrameDict(data, varnames),
|
|
86
|
+
DataFrameDict(udp_events, ["time", "value"]),
|
|
87
|
+
DataFrameDict(daq_events, ["time", "value"]),
|
|
88
|
+
comments)
|
|
89
|
+
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
import this module to have all relevant classes and function for the remote_control on your remote maschine
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__author__ = 'Oliver Lindemann'
|
|
6
|
+
|
|
7
|
+
import atexit
|
|
8
|
+
from pickle import dumps, loads
|
|
9
|
+
|
|
10
|
+
from .._lib.types import Thresholds, bytes_startswith
|
|
11
|
+
from .._lib.types import GUIRemoteControlCommands as Command
|
|
12
|
+
from .._lib.udp_connection import UDPConnection
|
|
13
|
+
|
|
14
|
+
udp = None
|
|
15
|
+
|
|
16
|
+
def init_udp_connection():
|
|
17
|
+
"""init udp connecting afterwards udp connection is available via
|
|
18
|
+
remote_control.udp
|
|
19
|
+
|
|
20
|
+
REQUIRED BEFORE USING OTHER FUNCTIONS
|
|
21
|
+
|
|
22
|
+
returns udp connection
|
|
23
|
+
"""
|
|
24
|
+
global udp
|
|
25
|
+
udp = UDPConnection()
|
|
26
|
+
return udp
|
|
27
|
+
|
|
28
|
+
def quit():
|
|
29
|
+
global udp
|
|
30
|
+
if isinstance(udp, UDPConnection):
|
|
31
|
+
udp.send(Command.QUIT)
|
|
32
|
+
udp = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
atexit.register(quit)
|
|
36
|
+
|
|
37
|
+
def get_data(get_command):
|
|
38
|
+
"""Get data from the recording PC
|
|
39
|
+
get_commands e.g.
|
|
40
|
+
Command.GET_FZ or
|
|
41
|
+
Command.GET_VERSION
|
|
42
|
+
....
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
udp.send(get_command)
|
|
46
|
+
d = udp.receive(1)
|
|
47
|
+
try:
|
|
48
|
+
return loads(d[len(Command.VALUE):])
|
|
49
|
+
except:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
def poll_event(event_type):
|
|
53
|
+
"""polling response minmax level
|
|
54
|
+
event_tag:
|
|
55
|
+
Command.RESPONSE_MINMAX or
|
|
56
|
+
Command.CHANGED_LEVEL
|
|
57
|
+
....
|
|
58
|
+
"""
|
|
59
|
+
rcv = udp.poll()
|
|
60
|
+
if rcv is not None and bytes_startswith(rcv, event_type):
|
|
61
|
+
x = loads(rcv[len(event_type):])
|
|
62
|
+
return x
|
|
63
|
+
else:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def poll_multiple_events(event_type_list):
|
|
68
|
+
"""polling for multiple events
|
|
69
|
+
e.g.
|
|
70
|
+
[Command.CHANGED_LEVEL, Command.CHANGED_LEVEL2]
|
|
71
|
+
|
|
72
|
+
returns tuple (event_type, event_type_data) or (None, None)
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
rcv = udp.poll()
|
|
76
|
+
if rcv is not None:
|
|
77
|
+
for event_type in event_type_list:
|
|
78
|
+
if bytes_startswith(rcv, event_type):
|
|
79
|
+
x = loads(rcv[len(event_type):])
|
|
80
|
+
return (event_type, x)
|
|
81
|
+
return (None, None)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def set_force_thresholds(lower, upper):
|
|
85
|
+
thr = Thresholds([lower, upper])
|
|
86
|
+
return udp.send(Command.SET_THRESHOLDS + dumps(thr))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def set_level_change_detection(sensor=1):
|
|
90
|
+
if sensor==2:
|
|
91
|
+
return udp.send(Command.SET_LEVEL_CHANGE_DETECTION2)
|
|
92
|
+
else:
|
|
93
|
+
return udp.send(Command.SET_LEVEL_CHANGE_DETECTION)
|