biosignal-device-interface 0.2.1a1__py3-none-any.whl → 0.2.1a2__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.
Files changed (41) hide show
  1. biosignal_device_interface/constants/devices/__init__.py +3 -3
  2. biosignal_device_interface/constants/devices/core/base_device_constants.py +61 -61
  3. biosignal_device_interface/constants/devices/otb/otb_muovi_constants.py +129 -129
  4. biosignal_device_interface/constants/devices/otb/otb_quattrocento_constants.py +313 -313
  5. biosignal_device_interface/constants/devices/otb/otb_quattrocento_light_constants.py +59 -59
  6. biosignal_device_interface/constants/devices/otb/otb_syncstation_constants.py +233 -233
  7. biosignal_device_interface/constants/plots/color_palette.py +59 -59
  8. biosignal_device_interface/devices/__init__.py +17 -17
  9. biosignal_device_interface/devices/core/base_device.py +424 -412
  10. biosignal_device_interface/devices/otb/__init__.py +29 -29
  11. biosignal_device_interface/devices/otb/otb_muovi.py +290 -290
  12. biosignal_device_interface/devices/otb/otb_quattrocento.py +332 -332
  13. biosignal_device_interface/devices/otb/otb_quattrocento_light.py +210 -210
  14. biosignal_device_interface/devices/otb/otb_syncstation.py +407 -407
  15. biosignal_device_interface/gui/device_template_widgets/all_devices_widget.py +51 -51
  16. biosignal_device_interface/gui/device_template_widgets/core/base_device_widget.py +130 -130
  17. biosignal_device_interface/gui/device_template_widgets/core/base_multiple_devices_widget.py +108 -108
  18. biosignal_device_interface/gui/device_template_widgets/otb/otb_devices_widget.py +44 -44
  19. biosignal_device_interface/gui/device_template_widgets/otb/otb_muovi_plus_widget.py +158 -158
  20. biosignal_device_interface/gui/device_template_widgets/otb/otb_muovi_widget.py +158 -158
  21. biosignal_device_interface/gui/device_template_widgets/otb/otb_quattrocento_light_widget.py +174 -174
  22. biosignal_device_interface/gui/device_template_widgets/otb/otb_quattrocento_widget.py +260 -260
  23. biosignal_device_interface/gui/device_template_widgets/otb/otb_syncstation_widget.py +262 -262
  24. biosignal_device_interface/gui/plot_widgets/biosignal_plot_widget.py +501 -501
  25. biosignal_device_interface/gui/ui/devices_template_widget.ui +38 -38
  26. biosignal_device_interface/gui/ui/otb_muovi_plus_template_widget.ui +171 -171
  27. biosignal_device_interface/gui/ui/otb_muovi_template_widget.ui +171 -171
  28. biosignal_device_interface/gui/ui/otb_quattrocento_light_template_widget.ui +266 -266
  29. biosignal_device_interface/gui/ui/otb_quattrocento_template_widget.ui +415 -415
  30. biosignal_device_interface/gui/ui/otb_syncstation_template_widget.ui +732 -732
  31. biosignal_device_interface/gui/ui_compiled/devices_template_widget.py +56 -56
  32. biosignal_device_interface/gui/ui_compiled/otb_muovi_plus_template_widget.py +153 -153
  33. biosignal_device_interface/gui/ui_compiled/otb_muovi_template_widget.py +153 -153
  34. biosignal_device_interface/gui/ui_compiled/otb_quattrocento_light_template_widget.py +217 -217
  35. biosignal_device_interface/gui/ui_compiled/otb_quattrocento_template_widget.py +318 -318
  36. biosignal_device_interface/gui/ui_compiled/otb_syncstation_template_widget.py +495 -495
  37. {biosignal_device_interface-0.2.1a1.dist-info → biosignal_device_interface-0.2.1a2.dist-info}/LICENSE +675 -675
  38. {biosignal_device_interface-0.2.1a1.dist-info → biosignal_device_interface-0.2.1a2.dist-info}/METADATA +2 -2
  39. biosignal_device_interface-0.2.1a2.dist-info/RECORD +46 -0
  40. {biosignal_device_interface-0.2.1a1.dist-info → biosignal_device_interface-0.2.1a2.dist-info}/WHEEL +1 -1
  41. biosignal_device_interface-0.2.1a1.dist-info/RECORD +0 -46
@@ -1,29 +1,29 @@
1
- from biosignal_device_interface.devices.otb.otb_quattrocento import OTBQuattrocento
2
- from biosignal_device_interface.devices.otb.otb_quattrocento_light import (
3
- OTBQuattrocentoLight,
4
- )
5
- from biosignal_device_interface.devices.otb.otb_muovi import OTBMuovi
6
- from biosignal_device_interface.devices.otb.otb_syncstation import OTBSyncStation
7
-
8
- # Widgets
9
- # All OTB Devices Widget
10
- from biosignal_device_interface.gui.device_template_widgets.otb.otb_devices_widget import (
11
- OTBDevicesWidget,
12
- )
13
-
14
- # Individual OTB Device Widgets
15
- from biosignal_device_interface.gui.device_template_widgets.otb.otb_muovi_widget import (
16
- OTBMuoviWidget,
17
- )
18
- from biosignal_device_interface.gui.device_template_widgets.otb.otb_muovi_plus_widget import (
19
- OTBMuoviPlusWidget,
20
- )
21
- from biosignal_device_interface.gui.device_template_widgets.otb.otb_quattrocento_widget import (
22
- OTBQuattrocentoWidget,
23
- )
24
- from biosignal_device_interface.gui.device_template_widgets.otb.otb_quattrocento_light_widget import (
25
- OTBQuattrocentoLightWidget,
26
- )
27
- from biosignal_device_interface.gui.device_template_widgets.otb.otb_syncstation_widget import (
28
- OTBSyncStationWidget,
29
- )
1
+ from biosignal_device_interface.devices.otb.otb_quattrocento import OTBQuattrocento
2
+ from biosignal_device_interface.devices.otb.otb_quattrocento_light import (
3
+ OTBQuattrocentoLight,
4
+ )
5
+ from biosignal_device_interface.devices.otb.otb_muovi import OTBMuovi
6
+ from biosignal_device_interface.devices.otb.otb_syncstation import OTBSyncStation
7
+
8
+ # Widgets
9
+ # All OTB Devices Widget
10
+ from biosignal_device_interface.gui.device_template_widgets.otb.otb_devices_widget import (
11
+ OTBDevicesWidget,
12
+ )
13
+
14
+ # Individual OTB Device Widgets
15
+ from biosignal_device_interface.gui.device_template_widgets.otb.otb_muovi_widget import (
16
+ OTBMuoviWidget,
17
+ )
18
+ from biosignal_device_interface.gui.device_template_widgets.otb.otb_muovi_plus_widget import (
19
+ OTBMuoviPlusWidget,
20
+ )
21
+ from biosignal_device_interface.gui.device_template_widgets.otb.otb_quattrocento_widget import (
22
+ OTBQuattrocentoWidget,
23
+ )
24
+ from biosignal_device_interface.gui.device_template_widgets.otb.otb_quattrocento_light_widget import (
25
+ OTBQuattrocentoLightWidget,
26
+ )
27
+ from biosignal_device_interface.gui.device_template_widgets.otb.otb_syncstation_widget import (
28
+ OTBSyncStationWidget,
29
+ )
@@ -1,290 +1,290 @@
1
- """
2
- Device class for real-time interfacing the Muovi device.
3
- Developer: Dominik I. Braun
4
- Contact: dome.braun@fau.de
5
- Last Update: 2024-06-05
6
- """
7
-
8
- from __future__ import annotations
9
- from typing import TYPE_CHECKING, Union, Dict
10
- from PySide6.QtNetwork import QTcpSocket, QTcpServer, QHostAddress
11
- import numpy as np
12
-
13
- # Local Libraries
14
- from biosignal_device_interface.devices.core.base_device import BaseDevice
15
- from biosignal_device_interface.constants.devices.core.base_device_constants import (
16
- DeviceType,
17
- DeviceChannelTypes,
18
- )
19
-
20
- # Constants
21
- from biosignal_device_interface.constants.devices.otb.otb_muovi_constants import (
22
- MUOVI_CONVERSION_FACTOR_DICT,
23
- MuoviWorkingMode,
24
- MuoviDetectionMode,
25
- MUOVI_WORKING_MODE_CHARACTERISTICS_DICT,
26
- MUOVI_SAMPLES_PER_FRAME_DICT,
27
- MUOVI_AVAILABLE_CHANNELS_DICT,
28
- )
29
-
30
- if TYPE_CHECKING:
31
- from PySide6.QtWidgets import QMainWindow, QWidget
32
- from aenum import Enum
33
-
34
-
35
- class OTBMuovi(BaseDevice):
36
- """
37
- Muovi device class derived from BaseDevice class.
38
-
39
- Args:
40
- parent (Union[QMainWindow, QWidget], optional):
41
- Parent widget to which the device is assigned to.
42
- Defaults to None.
43
-
44
- is_muovi_plus (bool):
45
- True if the device is a Muovi Plus, False if not.
46
-
47
- The Muovi class is using a TCP/IP protocol to communicate with the device.
48
- """
49
-
50
- def __init__(
51
- self,
52
- parent: Union[QMainWindow, QWidget] = None,
53
- is_muovi_plus: bool = False,
54
- ) -> None:
55
- """
56
- Initialize the Muovi device.
57
-
58
- Args:
59
- parent (Union[QMainWindow, QWidget], optional): Parent widget. Defaults to None.
60
- is_muovi_plus (bool, optional): Boolean to initialize the Muovi device as Muovi+ (64 biosignal channels) or Muovi (32 biosignal channels). Defaults to False (Muovi).
61
- """
62
- super().__init__(parent)
63
-
64
- # Device Parameters
65
- self._device_type: DeviceType = (
66
- DeviceType.OTB_MUOVI_PLUS if is_muovi_plus else DeviceType.OTB_MUOVI
67
- )
68
-
69
- # Connection Parameters
70
- self._interface: QTcpServer = None
71
- self._client_socket: QTcpSocket | None = None
72
-
73
- # Configuration Parameters
74
- self._working_mode: MuoviWorkingMode = MuoviWorkingMode.NONE
75
- self._detection_mode: MuoviDetectionMode = MuoviDetectionMode.NONE
76
- self._configuration_command: int | None = None
77
-
78
- def _connect_to_device(self) -> bool:
79
- super()._connect_to_device()
80
-
81
- self._interface = QTcpServer(self)
82
- self._received_bytes: bytearray = bytearray()
83
-
84
- if not self._interface.listen(
85
- QHostAddress(self._connection_settings[0]), self._connection_settings[1]
86
- ):
87
- return False
88
-
89
- self._interface.newConnection.connect(self._make_request)
90
-
91
- self._connection_timeout_timer.start()
92
-
93
- return True
94
-
95
- def _make_request(self) -> bool:
96
- super()._make_request()
97
- self._client_socket = self._interface.nextPendingConnection()
98
-
99
- if self._client_socket:
100
-
101
- self._client_socket.readyRead.connect(self._read_data)
102
-
103
- if not self.is_connected:
104
- self.is_connected = True
105
- self.connect_toggled.emit(self.is_connected)
106
- self._connection_timeout_timer.stop()
107
- return True
108
-
109
- elif not self._is_configured:
110
- self._is_configured = True
111
- self.configure_toggled.emit(self._is_configured)
112
- return True
113
-
114
- def _disconnect_from_device(self) -> bool:
115
- super()._disconnect_from_device()
116
-
117
- if self._client_socket is not None:
118
- self._client_socket.readyRead.disconnect(self._read_data)
119
- self._client_socket.disconnectFromHost()
120
- self._client_socket.close()
121
-
122
- if self._interface is not None:
123
- self._interface.close()
124
-
125
- return True
126
-
127
- def configure_device(
128
- self, params: Dict[str, Union[Enum, Dict[str, Enum]]] # type: ignore
129
- ) -> None:
130
- super().configure_device(params)
131
-
132
- if not self.is_connected or self._client_socket is None:
133
- return
134
-
135
- # Check if detection mode is valid for working mode (Case EEG -> MONOPOLAR_GAIN_4 => MONOPOLAR_GAIN_8)
136
- if self._working_mode == MuoviWorkingMode.EEG:
137
- if self._detection_mode == MuoviDetectionMode.MONOPOLAR_GAIN_4:
138
- self._detection_mode = MuoviDetectionMode.MONOPOLAR_GAIN_8
139
-
140
- self._conversion_factor_biosignal = MUOVI_CONVERSION_FACTOR_DICT[
141
- self._detection_mode
142
- ]
143
- self._conversion_factor_auxiliary = self._conversion_factor_biosignal
144
-
145
- # Set configuration parameters for data transfer
146
- working_mode_characteristics = MUOVI_WORKING_MODE_CHARACTERISTICS_DICT[
147
- self._working_mode
148
- ]
149
- self._sampling_frequency = working_mode_characteristics["sampling_frequency"]
150
- self._bytes_per_sample = working_mode_characteristics["bytes_per_sample"]
151
- self._samples_per_frame = MUOVI_SAMPLES_PER_FRAME_DICT[self._device_type][
152
- self._working_mode
153
- ]
154
-
155
- self._number_of_channels = MUOVI_AVAILABLE_CHANNELS_DICT[self._device_type][
156
- DeviceChannelTypes.ALL
157
- ]
158
- self._number_of_biosignal_channels = MUOVI_AVAILABLE_CHANNELS_DICT[
159
- self._device_type
160
- ][DeviceChannelTypes.BIOSIGNAL]
161
- self._biosignal_channel_indices = np.arange(self._number_of_biosignal_channels)
162
-
163
- self._number_of_auxiliary_channels = MUOVI_AVAILABLE_CHANNELS_DICT[
164
- self._device_type
165
- ][DeviceChannelTypes.AUXILIARY]
166
- self._auxiliary_channel_indices = np.arange(
167
- self._number_of_biosignal_channels,
168
- self._number_of_biosignal_channels + self._number_of_auxiliary_channels,
169
- )
170
-
171
- self._buffer_size = (
172
- self._number_of_channels * self._samples_per_frame * self._bytes_per_sample
173
- )
174
-
175
- self._received_bytes = bytearray()
176
-
177
- self._configure_command()
178
- self._send_configuration_to_device()
179
-
180
- def _send_configuration_to_device(self) -> None:
181
- configuration_bytes = int(self._configuration_command).to_bytes(
182
- 1, byteorder="big"
183
- )
184
-
185
- success = self._client_socket.write(configuration_bytes)
186
-
187
- if success == -1:
188
- self._disconnect_from_device()
189
-
190
- def _configure_command(self) -> None:
191
- self._configuration_command = self._working_mode.value << 2
192
- self._configuration_command += self._detection_mode.value
193
-
194
- def _start_streaming(self) -> None:
195
- super()._start_streaming()
196
-
197
- if self._configuration_command is None:
198
- return
199
-
200
- self._configuration_command += 1
201
- self._send_configuration_to_device()
202
-
203
- def _stop_streaming(self) -> None:
204
- super()._stop_streaming()
205
-
206
- if self._configuration_command is None:
207
- return
208
-
209
- self._configuration_command -= 1
210
- self._send_configuration_to_device()
211
-
212
- def clear_socket(self) -> None:
213
- if self._client_socket is not None:
214
- self._client_socket.readAll()
215
-
216
- def _read_data(self) -> None:
217
- super()._read_data()
218
-
219
- if not self._is_streaming:
220
- self.clear_socket()
221
- return
222
-
223
- while self._client_socket.bytesAvailable() > self._buffer_size:
224
- packet = self._client_socket.read(self._buffer_size)
225
- if not packet:
226
- continue
227
-
228
- self._received_bytes.extend(packet)
229
-
230
- while len(self._received_bytes) >= self._buffer_size:
231
- data_to_process = self._received_bytes[: self._buffer_size]
232
- self._process_data(data_to_process)
233
- self._received_bytes = self._received_bytes[self._buffer_size :]
234
-
235
- def _process_data(self, input: bytearray) -> None:
236
- super()._process_data(input)
237
-
238
- decoded_data = self._bytes_to_integers(input)
239
-
240
- processed_data = decoded_data.reshape(
241
- self._number_of_channels, -1, order="F"
242
- ).astype(np.float32)
243
-
244
- # Emit the data
245
- self.data_available.emit(processed_data)
246
- self.biosignal_data_available.emit(self._extract_biosignal_data(processed_data))
247
- self.auxiliary_data_available.emit(self._extract_auxiliary_data(processed_data))
248
-
249
- # Convert channels from bytes to integers
250
- def _bytes_to_integers(
251
- self,
252
- data: bytearray,
253
- ) -> np.ndarray:
254
- channel_values = []
255
- # Separate channels from byte-string. One channel has
256
- # "bytes_in_sample" many bytes in it.
257
- for channel_index in range(len(data) // 2):
258
- channel_start = channel_index * self._bytes_per_sample
259
- channel_end = (channel_index + 1) * self._bytes_per_sample
260
- channel = data[channel_start:channel_end]
261
-
262
- # Convert channel's byte value to integer
263
- match self._working_mode:
264
- case MuoviWorkingMode.EMG:
265
- value = self._decode_int16(channel)
266
- case MuoviWorkingMode.EEG:
267
- value = self._decode_int24(channel)
268
-
269
- channel_values.append(value)
270
-
271
- return np.array(channel_values)
272
-
273
- def _decode_int16(self, bytes_value: bytearray) -> int:
274
- value = None
275
- # Combine 2 bytes to a 16 bit integer value
276
- value = bytes_value[0] * 2**8 + bytes_value[1]
277
- # See if the value is negative and make the two's complement
278
- if value >= 2**15:
279
- value -= 2**16
280
- return value
281
-
282
- # Convert byte-array value to an integer value and apply two's complement
283
- def _decode_int24(self, bytes_value: bytearray) -> int:
284
- value = None
285
- # Combine 3 bytes to a 24 bit integer value
286
- value = bytes_value[0] * 2**16 + bytes_value[1] * 2**8 + bytes_value[2]
287
- # See if the value is negative and make the two's complement
288
- if value >= 2**23:
289
- value -= 2**24
290
- return value
1
+ """
2
+ Device class for real-time interfacing the Muovi device.
3
+ Developer: Dominik I. Braun
4
+ Contact: dome.braun@fau.de
5
+ Last Update: 2024-06-05
6
+ """
7
+
8
+ from __future__ import annotations
9
+ from typing import TYPE_CHECKING, Union, Dict
10
+ from PySide6.QtNetwork import QTcpSocket, QTcpServer, QHostAddress
11
+ import numpy as np
12
+
13
+ # Local Libraries
14
+ from biosignal_device_interface.devices.core.base_device import BaseDevice
15
+ from biosignal_device_interface.constants.devices.core.base_device_constants import (
16
+ DeviceType,
17
+ DeviceChannelTypes,
18
+ )
19
+
20
+ # Constants
21
+ from biosignal_device_interface.constants.devices.otb.otb_muovi_constants import (
22
+ MUOVI_CONVERSION_FACTOR_DICT,
23
+ MuoviWorkingMode,
24
+ MuoviDetectionMode,
25
+ MUOVI_WORKING_MODE_CHARACTERISTICS_DICT,
26
+ MUOVI_SAMPLES_PER_FRAME_DICT,
27
+ MUOVI_AVAILABLE_CHANNELS_DICT,
28
+ )
29
+
30
+ if TYPE_CHECKING:
31
+ from PySide6.QtWidgets import QMainWindow, QWidget
32
+ from aenum import Enum
33
+
34
+
35
+ class OTBMuovi(BaseDevice):
36
+ """
37
+ Muovi device class derived from BaseDevice class.
38
+
39
+ Args:
40
+ parent (Union[QMainWindow, QWidget], optional):
41
+ Parent widget to which the device is assigned to.
42
+ Defaults to None.
43
+
44
+ is_muovi_plus (bool):
45
+ True if the device is a Muovi Plus, False if not.
46
+
47
+ The Muovi class is using a TCP/IP protocol to communicate with the device.
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ parent: Union[QMainWindow, QWidget] = None,
53
+ is_muovi_plus: bool = False,
54
+ ) -> None:
55
+ """
56
+ Initialize the Muovi device.
57
+
58
+ Args:
59
+ parent (Union[QMainWindow, QWidget], optional): Parent widget. Defaults to None.
60
+ is_muovi_plus (bool, optional): Boolean to initialize the Muovi device as Muovi+ (64 biosignal channels) or Muovi (32 biosignal channels). Defaults to False (Muovi).
61
+ """
62
+ super().__init__(parent)
63
+
64
+ # Device Parameters
65
+ self._device_type: DeviceType = (
66
+ DeviceType.OTB_MUOVI_PLUS if is_muovi_plus else DeviceType.OTB_MUOVI
67
+ )
68
+
69
+ # Connection Parameters
70
+ self._interface: QTcpServer = None
71
+ self._client_socket: QTcpSocket | None = None
72
+
73
+ # Configuration Parameters
74
+ self._working_mode: MuoviWorkingMode = MuoviWorkingMode.NONE
75
+ self._detection_mode: MuoviDetectionMode = MuoviDetectionMode.NONE
76
+ self._configuration_command: int | None = None
77
+
78
+ def _connect_to_device(self) -> bool:
79
+ super()._connect_to_device()
80
+
81
+ self._interface = QTcpServer(self)
82
+ self._received_bytes: bytearray = bytearray()
83
+
84
+ if not self._interface.listen(
85
+ QHostAddress(self._connection_settings[0]), self._connection_settings[1]
86
+ ):
87
+ return False
88
+
89
+ self._interface.newConnection.connect(self._make_request)
90
+
91
+ self._connection_timeout_timer.start()
92
+
93
+ return True
94
+
95
+ def _make_request(self) -> bool:
96
+ super()._make_request()
97
+ self._client_socket = self._interface.nextPendingConnection()
98
+
99
+ if self._client_socket:
100
+
101
+ self._client_socket.readyRead.connect(self._read_data)
102
+
103
+ if not self.is_connected:
104
+ self.is_connected = True
105
+ self.connect_toggled.emit(self.is_connected)
106
+ self._connection_timeout_timer.stop()
107
+ return True
108
+
109
+ elif not self._is_configured:
110
+ self._is_configured = True
111
+ self.configure_toggled.emit(self._is_configured)
112
+ return True
113
+
114
+ def _disconnect_from_device(self) -> bool:
115
+ super()._disconnect_from_device()
116
+
117
+ if self._client_socket is not None:
118
+ self._client_socket.readyRead.disconnect(self._read_data)
119
+ self._client_socket.disconnectFromHost()
120
+ self._client_socket.close()
121
+
122
+ if self._interface is not None:
123
+ self._interface.close()
124
+
125
+ return True
126
+
127
+ def configure_device(
128
+ self, params: Dict[str, Union[Enum, Dict[str, Enum]]] # type: ignore
129
+ ) -> None:
130
+ super().configure_device(params)
131
+
132
+ if not self.is_connected or self._client_socket is None:
133
+ return
134
+
135
+ # Check if detection mode is valid for working mode (Case EEG -> MONOPOLAR_GAIN_4 => MONOPOLAR_GAIN_8)
136
+ if self._working_mode == MuoviWorkingMode.EEG:
137
+ if self._detection_mode == MuoviDetectionMode.MONOPOLAR_GAIN_4:
138
+ self._detection_mode = MuoviDetectionMode.MONOPOLAR_GAIN_8
139
+
140
+ self._conversion_factor_biosignal = MUOVI_CONVERSION_FACTOR_DICT[
141
+ self._detection_mode
142
+ ]
143
+ self._conversion_factor_auxiliary = self._conversion_factor_biosignal
144
+
145
+ # Set configuration parameters for data transfer
146
+ working_mode_characteristics = MUOVI_WORKING_MODE_CHARACTERISTICS_DICT[
147
+ self._working_mode
148
+ ]
149
+ self._sampling_frequency = working_mode_characteristics["sampling_frequency"]
150
+ self._bytes_per_sample = working_mode_characteristics["bytes_per_sample"]
151
+ self._samples_per_frame = MUOVI_SAMPLES_PER_FRAME_DICT[self._device_type][
152
+ self._working_mode
153
+ ]
154
+
155
+ self._number_of_channels = MUOVI_AVAILABLE_CHANNELS_DICT[self._device_type][
156
+ DeviceChannelTypes.ALL
157
+ ]
158
+ self._number_of_biosignal_channels = MUOVI_AVAILABLE_CHANNELS_DICT[
159
+ self._device_type
160
+ ][DeviceChannelTypes.BIOSIGNAL]
161
+ self._biosignal_channel_indices = np.arange(self._number_of_biosignal_channels)
162
+
163
+ self._number_of_auxiliary_channels = MUOVI_AVAILABLE_CHANNELS_DICT[
164
+ self._device_type
165
+ ][DeviceChannelTypes.AUXILIARY]
166
+ self._auxiliary_channel_indices = np.arange(
167
+ self._number_of_biosignal_channels,
168
+ self._number_of_biosignal_channels + self._number_of_auxiliary_channels,
169
+ )
170
+
171
+ self._buffer_size = (
172
+ self._number_of_channels * self._samples_per_frame * self._bytes_per_sample
173
+ )
174
+
175
+ self._received_bytes = bytearray()
176
+
177
+ self._configure_command()
178
+ self._send_configuration_to_device()
179
+
180
+ def _send_configuration_to_device(self) -> None:
181
+ configuration_bytes = int(self._configuration_command).to_bytes(
182
+ 1, byteorder="big"
183
+ )
184
+
185
+ success = self._client_socket.write(configuration_bytes)
186
+
187
+ if success == -1:
188
+ self._disconnect_from_device()
189
+
190
+ def _configure_command(self) -> None:
191
+ self._configuration_command = self._working_mode.value << 2
192
+ self._configuration_command += self._detection_mode.value
193
+
194
+ def _start_streaming(self) -> None:
195
+ super()._start_streaming()
196
+
197
+ if self._configuration_command is None:
198
+ return
199
+
200
+ self._configuration_command += 1
201
+ self._send_configuration_to_device()
202
+
203
+ def _stop_streaming(self) -> None:
204
+ super()._stop_streaming()
205
+
206
+ if self._configuration_command is None:
207
+ return
208
+
209
+ self._configuration_command -= 1
210
+ self._send_configuration_to_device()
211
+
212
+ def clear_socket(self) -> None:
213
+ if self._client_socket is not None:
214
+ self._client_socket.readAll()
215
+
216
+ def _read_data(self) -> None:
217
+ super()._read_data()
218
+
219
+ if not self._is_streaming:
220
+ self.clear_socket()
221
+ return
222
+
223
+ while self._client_socket.bytesAvailable() > self._buffer_size:
224
+ packet = self._client_socket.read(self._buffer_size)
225
+ if not packet:
226
+ continue
227
+
228
+ self._received_bytes.extend(packet)
229
+
230
+ while len(self._received_bytes) >= self._buffer_size:
231
+ data_to_process = self._received_bytes[: self._buffer_size]
232
+ self._process_data(data_to_process)
233
+ self._received_bytes = self._received_bytes[self._buffer_size :]
234
+
235
+ def _process_data(self, input: bytearray) -> None:
236
+ super()._process_data(input)
237
+
238
+ decoded_data = self._bytes_to_integers(input)
239
+
240
+ processed_data = decoded_data.reshape(
241
+ self._number_of_channels, -1, order="F"
242
+ ).astype(np.float32)
243
+
244
+ # Emit the data
245
+ self.data_available.emit(processed_data)
246
+ self.biosignal_data_available.emit(self._extract_biosignal_data(processed_data))
247
+ self.auxiliary_data_available.emit(self._extract_auxiliary_data(processed_data))
248
+
249
+ # Convert channels from bytes to integers
250
+ def _bytes_to_integers(
251
+ self,
252
+ data: bytearray,
253
+ ) -> np.ndarray:
254
+ channel_values = []
255
+ # Separate channels from byte-string. One channel has
256
+ # "bytes_in_sample" many bytes in it.
257
+ for channel_index in range(len(data) // 2):
258
+ channel_start = channel_index * self._bytes_per_sample
259
+ channel_end = (channel_index + 1) * self._bytes_per_sample
260
+ channel = data[channel_start:channel_end]
261
+
262
+ # Convert channel's byte value to integer
263
+ match self._working_mode:
264
+ case MuoviWorkingMode.EMG:
265
+ value = self._decode_int16(channel)
266
+ case MuoviWorkingMode.EEG:
267
+ value = self._decode_int24(channel)
268
+
269
+ channel_values.append(value)
270
+
271
+ return np.array(channel_values)
272
+
273
+ def _decode_int16(self, bytes_value: bytearray) -> int:
274
+ value = None
275
+ # Combine 2 bytes to a 16 bit integer value
276
+ value = bytes_value[0] * 2**8 + bytes_value[1]
277
+ # See if the value is negative and make the two's complement
278
+ if value >= 2**15:
279
+ value -= 2**16
280
+ return value
281
+
282
+ # Convert byte-array value to an integer value and apply two's complement
283
+ def _decode_int24(self, bytes_value: bytearray) -> int:
284
+ value = None
285
+ # Combine 3 bytes to a 24 bit integer value
286
+ value = bytes_value[0] * 2**16 + bytes_value[1] * 2**8 + bytes_value[2]
287
+ # See if the value is negative and make the two's complement
288
+ if value >= 2**23:
289
+ value -= 2**24
290
+ return value