pyForceDAQ 2.0.1.dev0__tar.gz → 2.0.1.dev2__tar.gz
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-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/PKG-INFO +1 -1
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/pyproject.toml +1 -1
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/__init__.py +1 -1
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/__main__.py +2 -2
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/_lib/misc.py +25 -14
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/_lib/types.py +8 -7
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/daq/__init__.py +5 -4
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/daq/_mock_sensor.py +1 -1
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/daq/_pyATIDAQ.py +1 -3
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/force/data_recorder.py +30 -34
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/force/sensor.py +18 -23
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/force/sensor_process.py +10 -5
- pyforcedaq-2.0.1.dev2/src/pyforcedaq/gui/__init__.py +6 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/gui/_run.py +40 -38
- pyforcedaq-2.0.1.dev2/src/pyforcedaq/gui/_settings.py +101 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/gui/launcher.py +23 -22
- pyforcedaq-2.0.1.dev0/src/pyforcedaq/gui/__init__.py +0 -6
- pyforcedaq-2.0.1.dev0/src/pyforcedaq/gui/_settings.py +0 -98
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/_lib/__init__.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/_lib/lsl.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/_lib/polling_time_profile.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/_lib/process_priority_manager.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/_lib/timer.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/_lib/udp_connection.py +0 -0
- /pyforcedaq-2.0.1.dev0/src/pyforcedaq/daq/_daq_read_analog_nidaqmx.py → /pyforcedaq-2.0.1.dev2/src/pyforcedaq/daq/_use_nidaqmx.py +0 -0
- /pyforcedaq-2.0.1.dev0/src/pyforcedaq/daq/_daq_read_Analog_pydaqmx.py → /pyforcedaq-2.0.1.dev2/src/pyforcedaq/daq/_use_pydaqmx.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/daq/config.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/extras/__init__.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/extras/convert.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/extras/expyriment_daq_control.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/extras/opensesame_daq_control.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/extras/read_force_data.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/extras/remote_control.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/force/__init__.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/force/_log.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/gui/_gui_status.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/gui/_layout.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/gui/_level_indicator.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/gui/_pg_surface.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/gui/_plotter.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/gui/_scaling.py +0 -0
- {pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/gui/forceDAQ_logo.png +0 -0
|
@@ -34,9 +34,9 @@ def cli():
|
|
|
34
34
|
from .gui import launcher
|
|
35
35
|
return launcher.run()
|
|
36
36
|
else:
|
|
37
|
-
gui.
|
|
37
|
+
gui.run_settings(args.SETTINGS_FILE)
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
if __name__ == "__main__": # required because of threading
|
|
42
|
-
cli()
|
|
42
|
+
cli()
|
|
@@ -39,20 +39,31 @@ class MinMaxDetector(object):
|
|
|
39
39
|
return (self._level_change_time is not None) and \
|
|
40
40
|
(get_time_ms() - self._level_change_time) < self._duration
|
|
41
41
|
|
|
42
|
-
def find_calibration_file(calibration_folder, sensor_name,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
42
|
+
# def find_calibration_file(calibration_folder: str, sensor_name: str,
|
|
43
|
+
# calibration_suffix=".cal") -> str:
|
|
44
|
+
|
|
45
|
+
# needle = 'Serial="{0}"'.format(sensor_name)
|
|
46
|
+
# calibration_files = []
|
|
47
|
+
# for x in listdir(path.abspath(calibration_folder)):
|
|
48
|
+
# filename = path.join(calibration_folder, x)
|
|
49
|
+
# if path.isfile(filename) and filename.endswith(calibration_suffix):
|
|
50
|
+
# with open(filename, "r") as fl:
|
|
51
|
+
# for l in fl:
|
|
52
|
+
# if l.find(needle)>0:
|
|
53
|
+
# print("Found calibration file for sensor '{0}' : {1}.".format(
|
|
54
|
+
# sensor_name, filename))
|
|
55
|
+
# calibration_files.append(filename)
|
|
56
|
+
|
|
57
|
+
# if len(calibration_files) == 1:
|
|
58
|
+
# return calibration_files[0]
|
|
59
|
+
# elif len(calibration_files) > 1:
|
|
60
|
+
# print("Multiple calibration files found for sensor '{0}'".format(sensor_name))
|
|
61
|
+
# for f in calibration_files:
|
|
62
|
+
# print(" - {0}".format(f))
|
|
63
|
+
# print("Please ensure that only one calibration file exists for each sensor")
|
|
64
|
+
# else:
|
|
65
|
+
# print("No calibration file found for sensor '{0}'.".format(sensor_name))
|
|
66
|
+
# exit()
|
|
56
67
|
|
|
57
68
|
#Sensor History with moving average filtering and distance, velocity
|
|
58
69
|
class SensorHistory(object):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
__author__ = 'Oliver Lindemann'
|
|
2
2
|
|
|
3
3
|
import ctypes as ct
|
|
4
|
-
from typing import List
|
|
4
|
+
from typing import Iterator, List, Tuple
|
|
5
5
|
|
|
6
6
|
from .misc import MinMaxDetector as _MinMaxDetector
|
|
7
7
|
|
|
@@ -13,7 +13,7 @@ TAG_UDPDATA = TAG_COMMENTS + "UDP"
|
|
|
13
13
|
CTYPE_FORCES = ct.c_float * 600
|
|
14
14
|
CTYPE_TRIGGER = ct.c_float * 2
|
|
15
15
|
|
|
16
|
-
class PollingPriority(object):
|
|
16
|
+
class PollingPriority(object): # TODO needed?
|
|
17
17
|
|
|
18
18
|
NORMAL = 'normal'
|
|
19
19
|
HIGH = 'high'
|
|
@@ -160,12 +160,13 @@ class ForceSensorData(object):
|
|
|
160
160
|
self.force = struct.forces
|
|
161
161
|
self.trigger = struct.trigger
|
|
162
162
|
|
|
163
|
-
def selected_forces(self, select: List[bool]):
|
|
164
|
-
"""Return
|
|
165
|
-
return
|
|
163
|
+
def selected_forces(self, select: List[bool]) -> List[float]:
|
|
164
|
+
"""Return list of selected force values."""
|
|
165
|
+
return [force for i, force in enumerate(self.forces) if select[i]] # FIXME simplify not all force have to save in ForceClass
|
|
166
166
|
|
|
167
|
-
def selected_trigger(self, select: List[bool]):
|
|
168
|
-
|
|
167
|
+
def selected_trigger(self, select: List[bool]) -> List[float]:
|
|
168
|
+
"""Return list of selected trigger values."""
|
|
169
|
+
return [trigger for i, trigger in enumerate(self.trigger) if select[i]]
|
|
169
170
|
|
|
170
171
|
|
|
171
172
|
class UDPData(object):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
__author__ = "Oliver Lindemann"
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.5"
|
|
3
3
|
|
|
4
4
|
from .. import USE_MOCK_SENSOR
|
|
5
5
|
from ._pyATIDAQ import ATI_CDLL
|
|
@@ -11,9 +11,10 @@ if USE_MOCK_SENSOR:
|
|
|
11
11
|
else:
|
|
12
12
|
#### change import here if you want to use nidaqmx instead of pydaymx ####
|
|
13
13
|
try:
|
|
14
|
-
from .
|
|
15
|
-
#from .
|
|
16
|
-
except (ImportError, ModuleNotFoundError):
|
|
14
|
+
from ._use_pydaqmx import DAQReadAnalog
|
|
15
|
+
#from ._use_nidaqmx import DAQReadAnalog
|
|
16
|
+
except (ImportError, ModuleNotFoundError, NotImplementedError) as e:
|
|
17
|
+
print("Error importing DAQReadAnalog: {0}".format(e))
|
|
17
18
|
print("Warning: PyDAQmx or nidaqmx not found. Using mock sensor instead.")
|
|
18
19
|
from ._mock_sensor import DAQReadAnalog
|
|
19
20
|
|
|
@@ -75,6 +75,6 @@ class DAQReadAnalog(object):
|
|
|
75
75
|
n_new_samples = self._simulation_timer.time - self._sample_cnt
|
|
76
76
|
|
|
77
77
|
self._sample_cnt += 1
|
|
78
|
-
x = self._sample_cnt /
|
|
78
|
+
x = self._sample_cnt / 1000
|
|
79
79
|
y = 10 + np.array((np.sin(x/2), np.cos(x/5), np.sin(x)))*10
|
|
80
80
|
return np.append(y, np.array((0, 0 , 0, 0, 0))), 1
|
|
@@ -12,8 +12,6 @@ __author__ = "Oliver Lindemann"
|
|
|
12
12
|
from ctypes import *
|
|
13
13
|
from sys import platform
|
|
14
14
|
|
|
15
|
-
from .._lib.misc import find_calibration_file
|
|
16
|
-
|
|
17
15
|
# ### DATA TYPES ####
|
|
18
16
|
VOLTAGE_SAMPLE_TYPE = c_float * 7
|
|
19
17
|
FT_SAMPLE_TYPE = c_float * 6
|
|
@@ -292,7 +290,7 @@ if __name__ == "__main__":
|
|
|
292
290
|
# -0.065867 0.123803 111.156731 0.039974 0.040417 0.079049
|
|
293
291
|
|
|
294
292
|
#filename = raw_input("Calibration file: ")
|
|
295
|
-
filename =
|
|
293
|
+
filename = "calibration/FT30436.cal"
|
|
296
294
|
atidaq = ATI_CDLL()
|
|
297
295
|
# get calibration
|
|
298
296
|
index = c_short(1)
|
|
@@ -7,8 +7,7 @@ __author__ = "Oliver Lindemann"
|
|
|
7
7
|
import atexit
|
|
8
8
|
import gzip
|
|
9
9
|
import logging
|
|
10
|
-
import
|
|
11
|
-
import sys
|
|
10
|
+
from pathlib import Path
|
|
12
11
|
from time import asctime, localtime, strftime
|
|
13
12
|
|
|
14
13
|
from pyforcedaq._lib import timer
|
|
@@ -38,7 +37,7 @@ class DataRecorder(object):
|
|
|
38
37
|
def __init__(self, force_sensor_settings:SensorSettings | list,
|
|
39
38
|
poll_udp_connection=False,
|
|
40
39
|
write_deviceid = False,
|
|
41
|
-
polling_priority=None):
|
|
40
|
+
polling_priority:str | None = None):
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
"""queue_data will be saved
|
|
@@ -82,18 +81,18 @@ class DataRecorder(object):
|
|
|
82
81
|
self._proc_manager.add_subprocess(self.udp)
|
|
83
82
|
self._proc_manager.add_subprocess(self._force_sensor_processes)
|
|
84
83
|
if polling_priority is not None:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
level = PollingPriority.NORMAL
|
|
85
|
+
else:
|
|
86
|
+
level = PollingPriority.get_priority(polling_priority)
|
|
87
|
+
self._proc_manager.set_subprocess_priorities(level=level, disable_gc=False)
|
|
88
88
|
|
|
89
|
-
logging.info("Main process priority:
|
|
90
|
-
self._proc_manager.get_main_priority()))
|
|
89
|
+
logging.info("Main process priority: %s", self._proc_manager.get_main_priority())
|
|
91
90
|
#logging.info("Subprocess priorities: {}".format(self._proc_manager.get_subprocess_priorities()))
|
|
92
91
|
|
|
93
92
|
self._is_recording = False
|
|
94
93
|
self._file = None
|
|
95
94
|
self._daq_event = []
|
|
96
|
-
self.filename = None
|
|
95
|
+
self.filename: str | None = None
|
|
97
96
|
atexit.register(self.quit)
|
|
98
97
|
|
|
99
98
|
|
|
@@ -102,7 +101,7 @@ class DataRecorder(object):
|
|
|
102
101
|
"""Property indicates whether the recording processes are alive"""
|
|
103
102
|
try:
|
|
104
103
|
return self._force_sensor_processes[0].is_alive()
|
|
105
|
-
except:
|
|
104
|
+
except Exception:
|
|
106
105
|
return False
|
|
107
106
|
|
|
108
107
|
@property
|
|
@@ -119,7 +118,7 @@ class DataRecorder(object):
|
|
|
119
118
|
return list(map(lambda x:x.sensor_settings,
|
|
120
119
|
self._force_sensor_processes))
|
|
121
120
|
|
|
122
|
-
def quit(self):
|
|
121
|
+
def quit(self) -> list | None:
|
|
123
122
|
"""Stop all recording processes, close data file and quit recording
|
|
124
123
|
|
|
125
124
|
Notes
|
|
@@ -145,7 +144,7 @@ class DataRecorder(object):
|
|
|
145
144
|
|
|
146
145
|
return buffer
|
|
147
146
|
|
|
148
|
-
def process_and_write_udp_events(self):
|
|
147
|
+
def process_and_write_udp_events(self) -> list:
|
|
149
148
|
"""process udp events and return them"""
|
|
150
149
|
buffer = []
|
|
151
150
|
while True:
|
|
@@ -159,9 +158,9 @@ class DataRecorder(object):
|
|
|
159
158
|
self._save_data(buffer)
|
|
160
159
|
return buffer
|
|
161
160
|
|
|
162
|
-
def _save_data(self, data_buffer,
|
|
161
|
+
def _save_data(self, data_buffer: list,
|
|
163
162
|
recording_screen=None,
|
|
164
|
-
float_decimal_places=4):
|
|
163
|
+
float_decimal_places: int = 4) -> None:
|
|
165
164
|
""" writes data to disk and set counters
|
|
166
165
|
|
|
167
166
|
ignores UDP remote control commands
|
|
@@ -199,10 +198,10 @@ class DataRecorder(object):
|
|
|
199
198
|
"Writing {0} of {1} blocks".format(c//BLOCKSIZE,
|
|
200
199
|
buffer_len//BLOCKSIZE)).present()
|
|
201
200
|
|
|
202
|
-
def _file_write(self, str):
|
|
203
|
-
self._file.write(
|
|
201
|
+
def _file_write(self, s: str) -> None:
|
|
202
|
+
self._file.write(s.encode())
|
|
204
203
|
|
|
205
|
-
def save_daq_event(self, code, time=None):
|
|
204
|
+
def save_daq_event(self, code, time: float | None = None) -> None:
|
|
206
205
|
"""Set marker code in file
|
|
207
206
|
|
|
208
207
|
DAQEvent will be timestamps and occur in the data output
|
|
@@ -213,7 +212,7 @@ class DataRecorder(object):
|
|
|
213
212
|
self._daq_event.append(DAQEvents(time = time, code = code))
|
|
214
213
|
|
|
215
214
|
|
|
216
|
-
def start_recording(self, determine_bias=False):
|
|
215
|
+
def start_recording(self, determine_bias: bool = False) -> None:
|
|
217
216
|
"""Start polling process and record
|
|
218
217
|
|
|
219
218
|
See Also
|
|
@@ -233,7 +232,7 @@ class DataRecorder(object):
|
|
|
233
232
|
list(map(lambda x:x.start_polling(), self._force_sensor_processes))
|
|
234
233
|
self._is_recording = True
|
|
235
234
|
|
|
236
|
-
def pause_recording(self, recording_screen=None):
|
|
235
|
+
def pause_recording(self, recording_screen=None) -> list:
|
|
237
236
|
"""Pauses all polling processes and process data
|
|
238
237
|
|
|
239
238
|
returns
|
|
@@ -268,7 +267,7 @@ class DataRecorder(object):
|
|
|
268
267
|
self._daq_event = []
|
|
269
268
|
return data
|
|
270
269
|
|
|
271
|
-
def determine_biases(self, n_samples):
|
|
270
|
+
def determine_biases(self, n_samples: int) -> None:
|
|
272
271
|
"""Record n data samples (n_samples) to determine bias.
|
|
273
272
|
Afterwards recording is in pause mode
|
|
274
273
|
|
|
@@ -289,12 +288,12 @@ class DataRecorder(object):
|
|
|
289
288
|
for x in self._force_sensor_processes:
|
|
290
289
|
x.event_bias_is_available.wait()
|
|
291
290
|
|
|
292
|
-
def open_data_file(self, filename,
|
|
293
|
-
subdirectory="data",
|
|
294
|
-
time_stamp_filename=False,
|
|
295
|
-
varnames = True,
|
|
296
|
-
comment_line="",
|
|
297
|
-
zipped=False):
|
|
291
|
+
def open_data_file(self, filename: str,
|
|
292
|
+
subdirectory: str = "data",
|
|
293
|
+
time_stamp_filename: bool = False,
|
|
294
|
+
varnames: bool = True,
|
|
295
|
+
comment_line: str = "",
|
|
296
|
+
zipped: bool = False) -> Path:
|
|
298
297
|
"""Create a data file
|
|
299
298
|
|
|
300
299
|
Only if data file has been opened, data will be saved!
|
|
@@ -322,11 +321,8 @@ class DataRecorder(object):
|
|
|
322
321
|
full path the actually used file (incl. timestamp)
|
|
323
322
|
|
|
324
323
|
"""
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
data_dir = os.path.join(base_dir, subdirectory)
|
|
328
|
-
if not os.path.isdir(data_dir):
|
|
329
|
-
os.mkdir(data_dir)
|
|
324
|
+
data_dir = Path.cwd() / subdirectory
|
|
325
|
+
data_dir.mkdir(exist_ok=True)
|
|
330
326
|
self.close_data_file()
|
|
331
327
|
|
|
332
328
|
if filename is None or len(filename) == 0:
|
|
@@ -352,8 +348,8 @@ class DataRecorder(object):
|
|
|
352
348
|
else:
|
|
353
349
|
self.filename = flname + suffix
|
|
354
350
|
|
|
355
|
-
full_path_file =
|
|
356
|
-
if
|
|
351
|
+
full_path_file = data_dir / self.filename
|
|
352
|
+
if full_path_file.is_file():
|
|
357
353
|
# print "data file already exists, adding counter"
|
|
358
354
|
cnt += 1
|
|
359
355
|
else:
|
|
@@ -388,7 +384,7 @@ class DataRecorder(object):
|
|
|
388
384
|
|
|
389
385
|
return full_path_file
|
|
390
386
|
|
|
391
|
-
def close_data_file(self):
|
|
387
|
+
def close_data_file(self) -> None:
|
|
392
388
|
"""Close the data file
|
|
393
389
|
|
|
394
390
|
Afterwards data will not be saved anymore.
|
|
@@ -7,11 +7,11 @@ __author__ = 'Oliver Lindemann'
|
|
|
7
7
|
|
|
8
8
|
import ctypes as ct
|
|
9
9
|
from copy import copy
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import List, Tuple
|
|
10
12
|
|
|
11
13
|
import numpy as np
|
|
12
14
|
|
|
13
|
-
#from .._lib import lsl
|
|
14
|
-
from .._lib.misc import find_calibration_file
|
|
15
15
|
from .._lib.timer import Timer, app_clock
|
|
16
16
|
from .._lib.types import ForceSensorData
|
|
17
17
|
from ..daq import ATI_CDLL, DAQConfiguration, DAQReadAnalog
|
|
@@ -22,15 +22,15 @@ class SensorSettings(DAQConfiguration):
|
|
|
22
22
|
def __init__(self,
|
|
23
23
|
device_id:int,
|
|
24
24
|
sensor_name:str,
|
|
25
|
-
|
|
25
|
+
calibration_file:str | Path,
|
|
26
26
|
channels="ai0:7",
|
|
27
27
|
device_name_prefix = "Dev",
|
|
28
28
|
rate:int=1000,
|
|
29
|
-
minVal=-10,
|
|
30
|
-
maxVal=10,
|
|
31
|
-
reverse_parameter_names=
|
|
29
|
+
minVal:float=-10,
|
|
30
|
+
maxVal:float=10,
|
|
31
|
+
reverse_parameter_names: str | Tuple[str] | List[str] | None = None,
|
|
32
32
|
convert_to_FT:bool=True,
|
|
33
|
-
|
|
33
|
+
lsl_stream:bool=False,
|
|
34
34
|
write_Fx:bool=True,
|
|
35
35
|
write_Fy:bool=True,
|
|
36
36
|
write_Fz:bool=True,
|
|
@@ -53,7 +53,7 @@ class SensorSettings(DAQConfiguration):
|
|
|
53
53
|
self.device_id = device_id
|
|
54
54
|
self.sensor_name = sensor_name
|
|
55
55
|
self.convert_to_FT = convert_to_FT
|
|
56
|
-
self.
|
|
56
|
+
self.lsl_stream = lsl_stream
|
|
57
57
|
self.write_Fx = write_Fx
|
|
58
58
|
self.write_Fy = write_Fy
|
|
59
59
|
self.write_Fz = write_Fz
|
|
@@ -62,22 +62,17 @@ class SensorSettings(DAQConfiguration):
|
|
|
62
62
|
self.write_Tz = write_Tz
|
|
63
63
|
self.write_trigger1 = write_trigger1
|
|
64
64
|
self.write_trigger2 = write_trigger2
|
|
65
|
-
|
|
66
|
-
if self.convert_to_FT:
|
|
67
|
-
self.calibration_file = find_calibration_file(
|
|
68
|
-
calibration_folder=calibration_folder,
|
|
69
|
-
sensor_name=sensor_name)
|
|
70
|
-
else:
|
|
71
|
-
self.calibration_file = None
|
|
65
|
+
self.calibration_file = calibration_file
|
|
72
66
|
|
|
73
67
|
self.reverse_parameters = []
|
|
74
|
-
if
|
|
75
|
-
reverse_parameter_names
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
68
|
+
if reverse_parameter_names is not None:
|
|
69
|
+
if isinstance(reverse_parameter_names, str):
|
|
70
|
+
reverse_parameter_names = [reverse_parameter_names]
|
|
71
|
+
for para in reverse_parameter_names:
|
|
72
|
+
try:
|
|
73
|
+
self.reverse_parameters.append(ForceSensorData.forces_names.index(para))
|
|
74
|
+
except Exception:
|
|
75
|
+
pass
|
|
81
76
|
|
|
82
77
|
def array_write_forces(self):
|
|
83
78
|
return [self.write_Fx, self.write_Fy, self.write_Fz, self.write_Tx, self.write_Ty, self.write_Tz]
|
|
@@ -105,7 +100,7 @@ class Sensor(DAQReadAnalog):
|
|
|
105
100
|
self.name = settings.sensor_name
|
|
106
101
|
self.convert_to_FT = settings.convert_to_FT
|
|
107
102
|
self.timer = Timer(sync_timer=app_clock) # own timer, because this class is used in own process
|
|
108
|
-
if self.DAQ_TYPE == "
|
|
103
|
+
if self.DAQ_TYPE == "mock_sensor":
|
|
109
104
|
self._atidaq = None
|
|
110
105
|
self.convert_to_FT = False
|
|
111
106
|
else:
|
|
@@ -157,19 +157,21 @@ class SensorProcess(Process):
|
|
|
157
157
|
## create init LSL
|
|
158
158
|
lsl_data_steam = None
|
|
159
159
|
lsl_hardware_trigger_stream = None
|
|
160
|
-
if self.sensor_settings.
|
|
160
|
+
if self.sensor_settings.lsl_stream:
|
|
161
161
|
lsl_data_steam = lsl.init(
|
|
162
|
-
name=f"
|
|
162
|
+
name=f"Force_{self.sensor_settings.device_name}",
|
|
163
163
|
n_channels=sum(stream_forces),
|
|
164
164
|
stream_id=f"RF_{self.sensor_settings.device_name}",
|
|
165
165
|
freq=self.sensor_settings.rate,
|
|
166
|
+
channel_format= lsl.cf_float32,
|
|
166
167
|
metadata={"sensor_name": self.sensor_settings.sensor_name})
|
|
167
168
|
n_hardware_trigger = sum(stream_trigger)
|
|
168
169
|
if n_hardware_trigger > 0:
|
|
169
170
|
lsl_hardware_trigger_stream = lsl.init(
|
|
170
|
-
name=f"
|
|
171
|
+
name=f"Trigger_{self.sensor_settings.device_name}",
|
|
171
172
|
n_channels=n_hardware_trigger,
|
|
172
173
|
stream_id=f"Tr_{self.sensor_settings.device_name}",
|
|
174
|
+
channel_format=lsl.cf_float32,
|
|
173
175
|
freq=self.sensor_settings.rate)
|
|
174
176
|
|
|
175
177
|
|
|
@@ -189,9 +191,12 @@ class SensorProcess(Process):
|
|
|
189
191
|
d = sensor.poll_data()
|
|
190
192
|
## LSL
|
|
191
193
|
if lsl_data_steam is not None:
|
|
192
|
-
lsl_data_steam.push_sample(
|
|
194
|
+
lsl_data_steam.push_sample(d.selected_forces(stream_forces)) # steam only select forces
|
|
195
|
+
|
|
193
196
|
if lsl_hardware_trigger_stream is not None:
|
|
194
|
-
|
|
197
|
+
tr = d.selected_trigger(stream_trigger)
|
|
198
|
+
if any(tr): # only stream if at least one trigger is active
|
|
199
|
+
lsl_hardware_trigger_stream.push_sample(tr)
|
|
195
200
|
|
|
196
201
|
ptp.update(d.time) # needed? TODO
|
|
197
202
|
self._last_Fx.value, self._last_Fy.value, self._last_Fz.value, \
|
|
@@ -5,7 +5,9 @@ See COPYING file distributed along with the pyForceDAQ copyright and license ter
|
|
|
5
5
|
__author__ = "Oliver Lindemann"
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
|
+
from os import path
|
|
8
9
|
from pickle import dumps
|
|
10
|
+
from typing import List
|
|
9
11
|
|
|
10
12
|
import numpy as np
|
|
11
13
|
import pygame
|
|
@@ -342,16 +344,16 @@ def _main_loop(exp, recorder, remote_control=False):
|
|
|
342
344
|
plotter_thread.join()
|
|
343
345
|
recorder.pause_recording(s.background)
|
|
344
346
|
|
|
345
|
-
def
|
|
347
|
+
def run_settings(settings_file: str | None = None):
|
|
346
348
|
|
|
347
349
|
if settings_file is not None and len(settings_file) > 0:
|
|
348
350
|
# load different settings file if specified
|
|
349
351
|
settings.load(settings_file)
|
|
350
352
|
|
|
351
|
-
return
|
|
353
|
+
return run(remote_control = settings.recording.remote_control,
|
|
352
354
|
ask_filename = settings.recording.ask_filename,
|
|
353
355
|
device_ids = settings.recording.device_ids,
|
|
354
|
-
|
|
356
|
+
calibration_files = settings.recording.calibration_files,
|
|
355
357
|
calibration_folder = settings.recording.calibration_folder,
|
|
356
358
|
device_name_prefix = settings.recording.device_name_prefix,
|
|
357
359
|
write_Fx = settings.recording.write_Fx,
|
|
@@ -365,28 +367,28 @@ def run(settings_file: str | None = None):
|
|
|
365
367
|
zip_data=settings.recording.zip_data,
|
|
366
368
|
reverse_scaling = settings.recording.reverse_scaling,
|
|
367
369
|
convert_to_forces=settings.recording.convert_to_forces,
|
|
368
|
-
|
|
370
|
+
lsl_stream=settings.recording.lsl_stream,
|
|
369
371
|
polling_priority=settings.recording.priority)
|
|
370
372
|
|
|
371
|
-
def
|
|
373
|
+
def run(remote_control,
|
|
372
374
|
ask_filename,
|
|
373
|
-
device_ids,
|
|
374
|
-
|
|
375
|
-
calibration_folder,
|
|
376
|
-
device_name_prefix="Dev",
|
|
377
|
-
write_Fx = True,
|
|
378
|
-
write_Fy = True,
|
|
379
|
-
write_Fz = True,
|
|
380
|
-
write_Tx = False,
|
|
381
|
-
write_Ty = False,
|
|
382
|
-
write_Tz = False,
|
|
383
|
-
write_trigger1 = True,
|
|
384
|
-
write_trigger2 = False,
|
|
385
|
-
zip_data=False,
|
|
386
|
-
reverse_scaling = None,
|
|
387
|
-
convert_to_forces = True,
|
|
388
|
-
|
|
389
|
-
polling_priority = None):
|
|
375
|
+
device_ids : int | List[int],
|
|
376
|
+
calibration_files : str | List[str],
|
|
377
|
+
calibration_folder: str,
|
|
378
|
+
device_name_prefix: str="Dev",
|
|
379
|
+
write_Fx: bool = True,
|
|
380
|
+
write_Fy: bool = True,
|
|
381
|
+
write_Fz: bool = True,
|
|
382
|
+
write_Tx: bool = False,
|
|
383
|
+
write_Ty: bool = False,
|
|
384
|
+
write_Tz: bool = False,
|
|
385
|
+
write_trigger1 : bool = True,
|
|
386
|
+
write_trigger2: bool = False,
|
|
387
|
+
zip_data: bool = False,
|
|
388
|
+
reverse_scaling: dict | None = None,
|
|
389
|
+
convert_to_forces: bool = True,
|
|
390
|
+
lsl_stream: bool = False,
|
|
391
|
+
polling_priority: str | None = None):
|
|
390
392
|
|
|
391
393
|
"""start gui
|
|
392
394
|
remote_control should be None (ask) or True or False
|
|
@@ -401,31 +403,34 @@ def run_with_options(remote_control,
|
|
|
401
403
|
"""
|
|
402
404
|
#
|
|
403
405
|
|
|
404
|
-
logging.info("New Recording with forceDAQ
|
|
405
|
-
logging.info("Sensors
|
|
406
|
-
logging.info("Settings "
|
|
406
|
+
logging.info("New Recording with forceDAQ %s", forceDAQVersion)
|
|
407
|
+
logging.info("Sensors %s", calibration_files)
|
|
408
|
+
logging.info("Settings %s", settings.recording_as_json())
|
|
407
409
|
|
|
408
410
|
|
|
409
411
|
if not isinstance(device_ids, (list, tuple)):
|
|
410
412
|
device_ids = [device_ids]
|
|
411
|
-
if not isinstance(
|
|
412
|
-
|
|
413
|
+
if not isinstance(calibration_files, (list, tuple)):
|
|
414
|
+
calibration_files = [calibration_files]
|
|
413
415
|
|
|
414
416
|
sensors = []
|
|
415
|
-
for d_id,
|
|
416
|
-
|
|
417
|
-
reverse_parameter_names = reverse_scaling[str(d_id)]
|
|
418
|
-
except:
|
|
417
|
+
for d_id, cal_file in zip(device_ids, calibration_files):
|
|
418
|
+
if reverse_scaling is None:
|
|
419
419
|
reverse_parameter_names = []
|
|
420
|
+
else:
|
|
421
|
+
try:
|
|
422
|
+
reverse_parameter_names = reverse_scaling[str(d_id)]
|
|
423
|
+
except KeyError:
|
|
424
|
+
reverse_parameter_names = []
|
|
420
425
|
|
|
421
426
|
ss = SensorSettings(device_id = d_id,
|
|
422
427
|
device_name_prefix=device_name_prefix,
|
|
423
|
-
sensor_name =
|
|
424
|
-
|
|
428
|
+
sensor_name = cal_file.split(".")[0],
|
|
429
|
+
calibration_file=path.join(calibration_folder, cal_file),
|
|
425
430
|
reverse_parameter_names=reverse_parameter_names,
|
|
426
431
|
rate = settings.gui.sampling_rate,
|
|
427
432
|
convert_to_FT=convert_to_forces,
|
|
428
|
-
|
|
433
|
+
lsl_stream=lsl_stream,
|
|
429
434
|
write_Fx = write_Fx,
|
|
430
435
|
write_Fy = write_Fy,
|
|
431
436
|
write_Fz = write_Fz,
|
|
@@ -436,11 +441,8 @@ def run_with_options(remote_control,
|
|
|
436
441
|
write_trigger2= write_trigger2)
|
|
437
442
|
sensors.append(ss)
|
|
438
443
|
|
|
439
|
-
|
|
440
|
-
|
|
441
444
|
# expyriment
|
|
442
|
-
control.defaults.
|
|
443
|
-
control.defaults.pause_key = None
|
|
445
|
+
control.defaults.initialise_delay = 0
|
|
444
446
|
control.defaults.window_mode = True
|
|
445
447
|
control.defaults.window_size = (1000, 700)
|
|
446
448
|
control.defaults.fast_quit = True
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import tomlkit
|
|
7
|
+
from icecream import ic
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class _GUISettings:
|
|
12
|
+
sampling_rate: int = 1000
|
|
13
|
+
level_detection_parameter: str = "Fz"
|
|
14
|
+
window_font: str = "freemono"
|
|
15
|
+
moving_average_size: int = 5
|
|
16
|
+
screen_refresh_interval_indicator: int = 300
|
|
17
|
+
gui_screen_refresh_interval_plotter: int = 50
|
|
18
|
+
data_min_max: list = field(default_factory=lambda: [-5, 30])
|
|
19
|
+
plotter_pixel_min_max: list = field(default_factory=lambda: [-250, 250])
|
|
20
|
+
indicator_pixel_min_max: list = field(default_factory=lambda: [-150, 150])
|
|
21
|
+
plot_axis: bool = False
|
|
22
|
+
plot_data_indicator_for_single_sensor: list = field(
|
|
23
|
+
default_factory=lambda: [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5)])
|
|
24
|
+
plot_data_plotter_for_single_sensor: list = field(
|
|
25
|
+
default_factory=lambda: [(0, 0), (0, 1), (0, 2)])
|
|
26
|
+
plot_data_indicator_for_two_sensors: list = field(
|
|
27
|
+
default_factory=lambda: [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)])
|
|
28
|
+
plot_data_plotter_for_two_sensors: list = field(
|
|
29
|
+
default_factory=lambda: [(0, 2), (1, 2)])
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class _RecordingSetting:
|
|
33
|
+
device_name_prefix: str = "Dev"
|
|
34
|
+
device_ids: List[int] = field(default_factory=lambda: [1])
|
|
35
|
+
ask_filename: bool = False
|
|
36
|
+
remote_control: bool = False
|
|
37
|
+
calibration_folder: str = "calibration"
|
|
38
|
+
calibration_files: List[str] = field(default_factory=lambda: ["FT9334.cal"])
|
|
39
|
+
zip_data: bool = True
|
|
40
|
+
write_Fx: bool = True
|
|
41
|
+
write_Fy: bool = True
|
|
42
|
+
write_Fz: bool = True
|
|
43
|
+
write_Tx: bool = False
|
|
44
|
+
write_Ty: bool = False
|
|
45
|
+
write_Tz: bool = False
|
|
46
|
+
write_trigger1: bool = False
|
|
47
|
+
write_trigger2: bool = False
|
|
48
|
+
lsl_stream: bool = False
|
|
49
|
+
reverse_scaling: dict = field(default_factory=lambda: {"1": ["Fz"], "2": ["Fz"]})
|
|
50
|
+
convert_to_forces: bool = True
|
|
51
|
+
priority: str = "normal"
|
|
52
|
+
|
|
53
|
+
def __post_init__(self):
|
|
54
|
+
if isinstance(self.device_ids, int):
|
|
55
|
+
self.device_ids = [self.device_ids]
|
|
56
|
+
if isinstance(self.calibration_files, str):
|
|
57
|
+
self.calibration_files = [self.calibration_files]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class GUISettings(object):
|
|
61
|
+
|
|
62
|
+
def __init__(self, filename):
|
|
63
|
+
# defaults
|
|
64
|
+
self.gui = _GUISettings()
|
|
65
|
+
self.gui_section = "GUI"
|
|
66
|
+
|
|
67
|
+
self.recording = _RecordingSetting()
|
|
68
|
+
self.recording_section = "Recording"
|
|
69
|
+
|
|
70
|
+
self.filename = filename
|
|
71
|
+
if os.path.isfile(self.filename):
|
|
72
|
+
self.load()
|
|
73
|
+
else:
|
|
74
|
+
self.save() # defaults
|
|
75
|
+
|
|
76
|
+
def _asdict(self):
|
|
77
|
+
return {self.recording_section: self.recording.__dict__,
|
|
78
|
+
self.gui_section: self.gui.__dict__}
|
|
79
|
+
|
|
80
|
+
def set_gui_settings(self, gui_setting_dict):
|
|
81
|
+
self.gui = _GUISettings(**gui_setting_dict)
|
|
82
|
+
|
|
83
|
+
def set_recording_setting(self, recording_setting_dict):
|
|
84
|
+
self.recording = _RecordingSetting(**recording_setting_dict)
|
|
85
|
+
|
|
86
|
+
def load(self, filename=None):
|
|
87
|
+
if filename is not None:
|
|
88
|
+
self.filename = filename
|
|
89
|
+
with open(self.filename, 'r', encoding='utf-8') as fl:
|
|
90
|
+
d = tomlkit.load(fl)
|
|
91
|
+
self.set_gui_settings(d[self.gui_section])
|
|
92
|
+
self.set_recording_setting(d[self.recording_section])
|
|
93
|
+
|
|
94
|
+
def save(self):
|
|
95
|
+
with open(self.filename, 'w', encoding='utf-8') as fl:
|
|
96
|
+
tomlkit.dump(self._asdict(), fl)
|
|
97
|
+
|
|
98
|
+
def recording_as_json(self):
|
|
99
|
+
return json.dumps(self.recording.__dict__)
|
|
100
|
+
|
|
101
|
+
settings = GUISettings(filename="pyForceDAQ.defaults.settings.toml")
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from os import path
|
|
3
|
+
from typing import List
|
|
3
4
|
|
|
4
5
|
import FreeSimpleGUI as _sg
|
|
5
6
|
|
|
6
7
|
from .. import USE_MOCK_SENSOR, __version__
|
|
7
|
-
from .._lib.misc import find_calibration_file
|
|
8
8
|
from .._lib.types import PollingPriority
|
|
9
9
|
from .._lib.udp_connection import UDPConnection
|
|
10
|
-
from ._run import
|
|
10
|
+
from ._run import run_settings as _gui_run
|
|
11
11
|
from ._settings import settings
|
|
12
12
|
|
|
13
13
|
|
|
@@ -37,23 +37,25 @@ def _s2l(csv_string, is_integer=False, is_float=False): # convert csv string to
|
|
|
37
37
|
return rtn
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
def _check_sensor_calibration_settings(device_ids
|
|
41
|
-
|
|
40
|
+
def _check_sensor_calibration_settings(device_ids: List[int],
|
|
41
|
+
calibrations_files : List[str],
|
|
42
|
+
calibration_folder :str):
|
|
42
43
|
rtn = []
|
|
43
44
|
for x, d_id in enumerate(device_ids):
|
|
44
45
|
error = False
|
|
45
46
|
try:
|
|
46
|
-
|
|
47
|
-
except:
|
|
48
|
-
|
|
49
|
-
error = True
|
|
50
|
-
try:
|
|
51
|
-
cal = find_calibration_file(calibration_folder=calibration_folder,
|
|
52
|
-
sensor_name=sensor_name)
|
|
53
|
-
except:
|
|
54
|
-
cal = "NO CALIBRATION"
|
|
47
|
+
cal_file = calibrations_files[x]
|
|
48
|
+
except KeyError:
|
|
49
|
+
cal_file = "??"
|
|
55
50
|
error = True
|
|
56
|
-
|
|
51
|
+
|
|
52
|
+
if not error:
|
|
53
|
+
cal = path.join(calibration_folder, cal_file)
|
|
54
|
+
if not path.isfile(cal):
|
|
55
|
+
cal = "NOT FOUND"
|
|
56
|
+
error = True
|
|
57
|
+
|
|
58
|
+
rtn.append([d_id, cal_file, cal, error])
|
|
57
59
|
|
|
58
60
|
return rtn
|
|
59
61
|
|
|
@@ -68,7 +70,7 @@ def _windows_run():
|
|
|
68
70
|
|
|
69
71
|
for d_id, name, cal, error in _check_sensor_calibration_settings(
|
|
70
72
|
s.device_ids,
|
|
71
|
-
s.
|
|
73
|
+
s.calibration_files,
|
|
72
74
|
s.calibration_folder):
|
|
73
75
|
if error:
|
|
74
76
|
col = "red"
|
|
@@ -135,8 +137,8 @@ def _window_settings():
|
|
|
135
137
|
[[_sg.Text("Folder:", size=(5, 1)), _sg.InputText(
|
|
136
138
|
s.calibration_folder, size=(23, 1), key="cal_dir"),
|
|
137
139
|
_sg.FolderBrowse(size=(6, 1))],
|
|
138
|
-
_input_text_list("
|
|
139
|
-
key="
|
|
140
|
+
_input_text_list("Cal-files:", s.calibration_files,
|
|
141
|
+
key="calibration_files")
|
|
140
142
|
])])
|
|
141
143
|
|
|
142
144
|
layout.append([_sg.Frame('Record Forces & Torques',
|
|
@@ -190,7 +192,7 @@ def _window_settings():
|
|
|
190
192
|
except:
|
|
191
193
|
event = "Error"
|
|
192
194
|
|
|
193
|
-
key = "
|
|
195
|
+
key = "calibration_files"
|
|
194
196
|
try:
|
|
195
197
|
d[key] = _s2l(values[key])
|
|
196
198
|
except:
|
|
@@ -207,7 +209,7 @@ def _window_settings():
|
|
|
207
209
|
main_path = path.split(sys.modules['__main__'].__file__)[0] + path.sep
|
|
208
210
|
d["calibration_folder"] = values["cal_dir"].replace(main_path, "")
|
|
209
211
|
|
|
210
|
-
settings.
|
|
212
|
+
settings.set_recording_setting(d)
|
|
211
213
|
settings.save()
|
|
212
214
|
|
|
213
215
|
window.close()
|
|
@@ -220,9 +222,8 @@ def run():
|
|
|
220
222
|
s = settings.recording
|
|
221
223
|
settings_error = False
|
|
222
224
|
n_sensor = len(s.device_ids)
|
|
223
|
-
if n_sensor != len(s.
|
|
224
|
-
_sg.PopupError("Number of devices IDs and
|
|
225
|
-
"not equal.")
|
|
225
|
+
if n_sensor != len(s.calibration_files):
|
|
226
|
+
_sg.PopupError("Number of devices IDs and calibration files are not equal.")
|
|
226
227
|
settings_error = True
|
|
227
228
|
|
|
228
229
|
if not path.isdir(s.calibration_folder):
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import collections
|
|
2
|
-
import json
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
import tomlkit
|
|
6
|
-
|
|
7
|
-
_GUISettings = collections.namedtuple('GUISettings', 'sampling_rate '
|
|
8
|
-
'level_detection_parameter window_font moving_average_size '
|
|
9
|
-
'screen_refresh_interval_indicator gui_screen_refresh_interval_plotter '
|
|
10
|
-
'data_min_max plotter_pixel_min_max indicator_pixel_min_max plot_axis '
|
|
11
|
-
'plot_data_indicator_for_single_sensor plot_data_plotter_for_single_sensor '
|
|
12
|
-
'plot_data_indicator_for_two_sensors plot_data_plotter_for_two_sensors')
|
|
13
|
-
|
|
14
|
-
_RecordingSetting = collections.namedtuple('RecordingSetting',
|
|
15
|
-
'device_name_prefix device_ids sensor_names remote_control '
|
|
16
|
-
'ask_filename calibration_folder '
|
|
17
|
-
' zip_data write_Fx write_Fy '
|
|
18
|
-
'write_Fz write_Tx write_Ty write_Tz write_trigger1 '
|
|
19
|
-
'write_trigger2 has_lsl_stream reverse_scaling convert_to_forces priority')
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class GUISettings(object):
|
|
23
|
-
|
|
24
|
-
def __init__(self, filename):
|
|
25
|
-
|
|
26
|
-
# defaults
|
|
27
|
-
self.gui = _GUISettings(
|
|
28
|
-
sampling_rate = 1000,
|
|
29
|
-
level_detection_parameter = "Fz",
|
|
30
|
-
window_font = "freemono",
|
|
31
|
-
moving_average_size = 5,
|
|
32
|
-
screen_refresh_interval_indicator = 300,
|
|
33
|
-
gui_screen_refresh_interval_plotter = 50,
|
|
34
|
-
data_min_max = [-5, 30],
|
|
35
|
-
plotter_pixel_min_max = [-250, 250],
|
|
36
|
-
indicator_pixel_min_max = [-150, 150],
|
|
37
|
-
plot_axis = False,
|
|
38
|
-
plot_data_indicator_for_single_sensor = [(0, 0), (0, 1), (0, 2), (0, 3),
|
|
39
|
-
(0, 4), (0, 5)], # sensor, parameter
|
|
40
|
-
plot_data_plotter_for_single_sensor = [(0, 0), (0, 1), (0, 2)],
|
|
41
|
-
# plotter can't plot torques # TODO
|
|
42
|
-
|
|
43
|
-
plot_data_indicator_for_two_sensors = [(0, 0), (0, 1), (0, 2), (1, 0),
|
|
44
|
-
(1, 1), (1, 2)], # sensor,
|
|
45
|
-
# parameter
|
|
46
|
-
plot_data_plotter_for_two_sensors = [(0, 2),
|
|
47
|
-
(1, 2)],
|
|
48
|
-
# plotter can't plot torques
|
|
49
|
-
)
|
|
50
|
-
self.gui_section = "GUI"
|
|
51
|
-
|
|
52
|
-
self.recording = _RecordingSetting(device_name_prefix="Dev",
|
|
53
|
-
device_ids = [1],
|
|
54
|
-
sensor_names = ["FT9334"],
|
|
55
|
-
calibration_folder="calibration",
|
|
56
|
-
reverse_scaling = {"1": ["Fz"], "2":["Fz"]}, # key: device_id, parameter. E.g.:if x & z dimension of sensor 1 and z dimension of sensor 2 has to be flipped use {1: ["Fx", "Fz"], 2: ["Fz"]}
|
|
57
|
-
remote_control=False, ask_filename= False, write_Fx=True,
|
|
58
|
-
write_Fy=True, write_Fz=True, write_Tx=False, write_Ty=False,
|
|
59
|
-
write_Tz=False, write_trigger1=True, write_trigger2=False,
|
|
60
|
-
has_lsl_stream=False,
|
|
61
|
-
zip_data=True, convert_to_forces=True,
|
|
62
|
-
priority='normal') # default recording settings
|
|
63
|
-
self.recording_section = "Recording"
|
|
64
|
-
|
|
65
|
-
self.filename = filename
|
|
66
|
-
if os.path.isfile(self.filename):
|
|
67
|
-
self.load()
|
|
68
|
-
else:
|
|
69
|
-
self.save() # defaults
|
|
70
|
-
|
|
71
|
-
def _asdict(self):
|
|
72
|
-
return {self.recording_section: self.recording._asdict(),
|
|
73
|
-
self.gui_section: self.gui._asdict()}
|
|
74
|
-
|
|
75
|
-
def set_gui_settings(self, gui_setting_dict):
|
|
76
|
-
self.gui = _GUISettings(**gui_setting_dict)
|
|
77
|
-
|
|
78
|
-
def set_recoding_setting(self, recording_setting_dict):
|
|
79
|
-
self.recording = _RecordingSetting(**recording_setting_dict)
|
|
80
|
-
|
|
81
|
-
def load(self, filename=None):
|
|
82
|
-
if filename is not None:
|
|
83
|
-
self.filename = filename
|
|
84
|
-
with open(self.filename, 'r') as fl:
|
|
85
|
-
d = tomlkit.load(fl)
|
|
86
|
-
self.set_gui_settings(d[self.gui_section])
|
|
87
|
-
self.set_recoding_setting(d[self.recording_section])
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def save(self):
|
|
91
|
-
with open(self.filename, 'w') as fl:
|
|
92
|
-
tomlkit.dump(self._asdict(), fl)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def recording_as_json(self):
|
|
96
|
-
return json.dumps(self.recording._asdict())
|
|
97
|
-
|
|
98
|
-
settings = GUISettings(filename="pyForceDAQ.defaults.settings.toml")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/_lib/process_priority_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/extras/expyriment_daq_control.py
RENAMED
|
File without changes
|
{pyforcedaq-2.0.1.dev0 → pyforcedaq-2.0.1.dev2}/src/pyforcedaq/extras/opensesame_daq_control.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|