qlsdk2 0.6.0a3__tar.gz → 0.6.0a5__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.
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/PKG-INFO +1 -1
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/setup.py +1 -1
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/entity/__init__.py +57 -11
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/command/__init__.py +2 -3
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/device/base.py +8 -4
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/device/c256_rs.py +3 -9
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/device/c64_rs.py +1 -29
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/paradigm.py +124 -4
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk2.egg-info/PKG-INFO +1 -1
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/README.md +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/setup.cfg +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/ar4/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/ar4m/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/crc/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/crc/crctools.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/device.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/exception.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/filter/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/filter/norch.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/local.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/message/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/message/command.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/message/tcp.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/message/udp.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/network/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/network/monitor.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/core/utils.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/entity/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/entity/message.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/entity/signal.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/interface/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/interface/analyzer.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/interface/collector.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/interface/device.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/interface/parser.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/interface/stimulator.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/interface/store.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/persist/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/persist/ars_edf.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/persist/edf.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/persist/rsc_edf.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/persist/stream.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/device/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/device/arskindling.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/device/c16_rs.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/device/c64s1.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/device/device_factory.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/eegion.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/entity.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/interface/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/interface/command.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/interface/device.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/interface/handler.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/interface/parser.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/manager/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/manager/container.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/manager/search.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/network/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/network/discover.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/parser/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/parser/base-new.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/parser/base.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/parser/rsc.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/rsc/proxy.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/sdk/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/sdk/ar4sdk.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/sdk/hub.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/sdk/libs/libAr4SDK.dll +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/sdk/libs/libwinpthread-1.dll +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/x8/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk/x8m/__init__.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk2.egg-info/SOURCES.txt +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk2.egg-info/dependency_links.txt +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk2.egg-info/requires.txt +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/src/qlsdk2.egg-info/top_level.txt +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/test/test.222.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/test/test.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/test/test_ar4m.py +0 -0
- {qlsdk2-0.6.0a3 → qlsdk2-0.6.0a5}/test/test_bdf.py +0 -0
|
@@ -72,24 +72,34 @@ class RscPacket(Packet):
|
|
|
72
72
|
class ImpedancePacket(Packet):
|
|
73
73
|
def __init__(self):
|
|
74
74
|
super().__init__()
|
|
75
|
-
self.data_len = None
|
|
76
75
|
self.impedance = None
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
@staticmethod
|
|
78
|
+
def transfer(body:bytes) -> 'ImpedancePacket':
|
|
79
|
+
packet = ImpedancePacket()
|
|
80
|
+
packet.time_stamp = int.from_bytes(body[0:8], 'little')
|
|
81
|
+
# packet.result = body[8]
|
|
82
|
+
packet.pkg_id = int.from_bytes(body[9: 13], 'little')
|
|
83
|
+
packet.channels = to_channels(body[13: 45])
|
|
84
|
+
# packet.sample_rate = int.from_bytes(body[45: 49], 'little')
|
|
85
|
+
# packet.sample_len = int.from_bytes(body[49: 53], 'little')
|
|
86
|
+
# packet.resolution = int(int(body[53]) / 8)
|
|
87
|
+
# packet.filter = int(int(body[54]) / 8)
|
|
88
|
+
# packet.wave_type = int(int(body[55]) / 8)
|
|
89
|
+
# packet.wave_freq = int.from_bytes(body[56: 60], 'little')
|
|
90
|
+
# packet.data_len = int.from_bytes(body[60: 64], 'little')
|
|
91
|
+
b_impedance = body[64:]
|
|
92
|
+
packet.impedance = [int.from_bytes(b_impedance[j * 4 : j * 4 + 4], 'little', signed=False) for j in range(len(packet.channels))]
|
|
93
|
+
|
|
94
|
+
logger.trace(f"impedance: {packet}")
|
|
85
95
|
|
|
96
|
+
return packet
|
|
97
|
+
|
|
86
98
|
def __str__(self):
|
|
87
99
|
return f"""
|
|
88
100
|
time_stamp: {self.time_stamp}
|
|
89
101
|
pkg_id: {self.pkg_id}
|
|
90
|
-
result: {self.result}
|
|
91
102
|
channels: {self.channels}
|
|
92
|
-
data len: {self.data_len}
|
|
93
103
|
impedance: {self.impedance}
|
|
94
104
|
"""
|
|
95
105
|
|
|
@@ -162,4 +172,40 @@ class C256RSPacket(Packet):
|
|
|
162
172
|
offset += 4 + channel_size * packet.resolution
|
|
163
173
|
|
|
164
174
|
logger.trace(packet)
|
|
165
|
-
return packet
|
|
175
|
+
return packet
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class C256ImpedancePacket(Packet):
|
|
179
|
+
def __init__(self):
|
|
180
|
+
super().__init__()
|
|
181
|
+
self.impedance = None
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
def transfer(body:bytes) -> 'ImpedancePacket':
|
|
185
|
+
packet = ImpedancePacket()
|
|
186
|
+
packet.time_stamp = int.from_bytes(body[0:8], 'little')
|
|
187
|
+
# packet.result = body[8]
|
|
188
|
+
packet.pkg_id = int.from_bytes(body[9: 13], 'little')
|
|
189
|
+
packet.channels = to_channels(body[13: 45])
|
|
190
|
+
# packet.sample_rate = int.from_bytes(body[45: 49], 'little')
|
|
191
|
+
# packet.sample_len = int.from_bytes(body[49: 53], 'little')
|
|
192
|
+
# packet.resolution = int(int(body[53]) / 8)
|
|
193
|
+
# packet.filter = int(int(body[54]) / 8)
|
|
194
|
+
# packet.wave_type = int(int(body[55]) / 8)
|
|
195
|
+
# packet.wave_freq = int.from_bytes(body[56: 60], 'little')
|
|
196
|
+
# packet.data_len = int.from_bytes(body[60: 64], 'little')
|
|
197
|
+
b_impedance = body[64:]
|
|
198
|
+
packet.impedance = [int.from_bytes(b_impedance[j : j + 4], 'little', signed=False) for j in range(len(packet.channels))]
|
|
199
|
+
|
|
200
|
+
logger.trace(f"impedance: {packet}")
|
|
201
|
+
|
|
202
|
+
def __str__(self):
|
|
203
|
+
return f"""
|
|
204
|
+
time_stamp: {self.time_stamp}
|
|
205
|
+
pkg_id: {self.pkg_id}
|
|
206
|
+
result: {self.result}
|
|
207
|
+
channels: {self.channels}
|
|
208
|
+
data len: {self.data_len}
|
|
209
|
+
impedance: {self.impedance}
|
|
210
|
+
"""
|
|
211
|
+
|
|
@@ -233,7 +233,7 @@ class StartStimulationCommand(DeviceCommand):
|
|
|
233
233
|
cmd_code = 0x48C
|
|
234
234
|
cmd_desc = "启动刺激"
|
|
235
235
|
def pack_body(self):
|
|
236
|
-
return self.device.
|
|
236
|
+
return self.device.get_stim_param()
|
|
237
237
|
# return bytes.fromhex('01000000000000008813000000000000010000000000000000000140420f00640064000000803f0000010000000000000000000000000000000000000000008813000000000000')
|
|
238
238
|
def parse_body(self, body: bytes):
|
|
239
239
|
# time - 8B
|
|
@@ -297,8 +297,7 @@ class ImpedanceDataCommand(DeviceCommand):
|
|
|
297
297
|
cmd_desc = "阻抗数据"
|
|
298
298
|
|
|
299
299
|
def parse_body(self, body: bytes):
|
|
300
|
-
|
|
301
|
-
packet = ImpedancePacket().transfer(body)
|
|
300
|
+
self.device.produce(body, type="impedance")
|
|
302
301
|
|
|
303
302
|
# 信号数据
|
|
304
303
|
class SignalDataCommand(DeviceCommand):
|
|
@@ -9,6 +9,7 @@ from qlsdk.core.entity import RscPacket, ImpedancePacket
|
|
|
9
9
|
from qlsdk.core.utils import to_bytes
|
|
10
10
|
from qlsdk.rsc.interface import IDevice, IParser
|
|
11
11
|
from qlsdk.rsc.command import StartImpedanceCommand, StopImpedanceCommand, StartStimulationCommand, StopStimulationCommand, SetAcquisitionParamCommand, StartAcquisitionCommand, StopAcquisitionCommand
|
|
12
|
+
from qlsdk.rsc.paradigm import StimulationParadigm
|
|
12
13
|
from qlsdk.rsc.parser.base import TcpMessageParser
|
|
13
14
|
|
|
14
15
|
class QLBaseDevice(IDevice):
|
|
@@ -91,7 +92,7 @@ class QLBaseDevice(IDevice):
|
|
|
91
92
|
]
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
self.stim_paradigm = None
|
|
95
|
+
self.stim_paradigm: StimulationParadigm = None
|
|
95
96
|
|
|
96
97
|
signal_info = {
|
|
97
98
|
"param" : None,
|
|
@@ -144,7 +145,7 @@ class QLBaseDevice(IDevice):
|
|
|
144
145
|
self._produce_impedance(body)
|
|
145
146
|
|
|
146
147
|
def _produce_impedance(self, body: bytes):
|
|
147
|
-
packet = ImpedancePacket
|
|
148
|
+
packet = ImpedancePacket.transfer(body)
|
|
148
149
|
# 分发阻抗数据包给订阅者
|
|
149
150
|
if len(self._impedance_consumer) > 0:
|
|
150
151
|
for topic, q in self._impedance_consumer.items():
|
|
@@ -354,7 +355,7 @@ class QLBaseDevice(IDevice):
|
|
|
354
355
|
self.stim_paradigm = param
|
|
355
356
|
|
|
356
357
|
# 设置采集参数
|
|
357
|
-
def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
|
|
358
|
+
def set_acq_param(self, channels, sample_rate:Literal[188, 375, 563, 750, 1125, 2250, 4500] = 500, sample_range:Literal[250, 500, 1000, 2000, 4000, 8000, 16000, 32000] = 188):
|
|
358
359
|
self._acq_param["channels"] = channels
|
|
359
360
|
self._acq_param["sample_rate"] = sample_rate
|
|
360
361
|
self._acq_param["sample_range"] = sample_range
|
|
@@ -389,6 +390,9 @@ class QLBaseDevice(IDevice):
|
|
|
389
390
|
t = Thread(target=self._stop_stimulation_trigger, args=(self.stim_paradigm.duration,), daemon=True)
|
|
390
391
|
t.start()
|
|
391
392
|
|
|
393
|
+
def get_stim_param(self) -> bytes:
|
|
394
|
+
return self.stim_paradigm.to_bytes()
|
|
395
|
+
|
|
392
396
|
def _stop_stimulation_trigger(self, duration):
|
|
393
397
|
delay = duration
|
|
394
398
|
while delay > 0:
|
|
@@ -446,7 +450,7 @@ class QLBaseDevice(IDevice):
|
|
|
446
450
|
|
|
447
451
|
# 数据队列
|
|
448
452
|
if q is None:
|
|
449
|
-
q = Queue(maxsize=
|
|
453
|
+
q = Queue(maxsize=1024 * 1024)
|
|
450
454
|
|
|
451
455
|
# 订阅生理电信号数据
|
|
452
456
|
if type == "signal":
|
|
@@ -199,15 +199,6 @@ class C256RS(QLBaseDevice):
|
|
|
199
199
|
def set_stim_param(self, param):
|
|
200
200
|
self.stim_paradigm = param
|
|
201
201
|
|
|
202
|
-
# 设置采集参数
|
|
203
|
-
def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
|
|
204
|
-
self._acq_param["channels"] = channels
|
|
205
|
-
self._acq_param["sample_rate"] = sample_rate
|
|
206
|
-
self._acq_param["sample_range"] = sample_range
|
|
207
|
-
self._acq_channels = channels
|
|
208
|
-
self._sample_rate = sample_rate
|
|
209
|
-
self._sample_range = sample_range
|
|
210
|
-
|
|
211
202
|
def _signal_wrapper(self, body: bytes):
|
|
212
203
|
return C256RSPacket().transfer(body)
|
|
213
204
|
|
|
@@ -234,6 +225,9 @@ class C256RS(QLBaseDevice):
|
|
|
234
225
|
t = Thread(target=self._stop_stimulation_trigger, args=(self.stim_paradigm.duration,))
|
|
235
226
|
t.start()
|
|
236
227
|
|
|
228
|
+
def get_stim_param(self) -> bytes:
|
|
229
|
+
return self.stim_paradigm.to_bytes_c256()
|
|
230
|
+
|
|
237
231
|
def _stop_stimulation_trigger(self, duration):
|
|
238
232
|
delay = duration
|
|
239
233
|
while delay > 0:
|
|
@@ -285,35 +285,7 @@ class C64RS(QLBaseDevice):
|
|
|
285
285
|
if self._edf_handler:
|
|
286
286
|
# 发送结束标识
|
|
287
287
|
self.edf_handler.write(None)
|
|
288
|
-
|
|
289
|
-
# 订阅实时数据
|
|
290
|
-
def subscribe(self, topic:str=None, q : Queue=None, type : Literal["signal","impedance"]="signal"):
|
|
291
|
-
|
|
292
|
-
# 数据队列
|
|
293
|
-
if q is None:
|
|
294
|
-
q = Queue(maxsize=1000)
|
|
295
|
-
|
|
296
|
-
# 队列名称
|
|
297
|
-
if topic is None:
|
|
298
|
-
topic = f"{type}_{time_ns()}"
|
|
299
|
-
|
|
300
|
-
# 订阅生理电信号数据
|
|
301
|
-
if type == "signal":
|
|
302
|
-
# topic唯一,用来区分不同的订阅队列(下同)
|
|
303
|
-
if topic in list(self.__signal_consumer.keys()):
|
|
304
|
-
logger.warning(f"exists {type} subscribe of {topic}")
|
|
305
|
-
else:
|
|
306
|
-
self.__signal_consumer[topic] = q
|
|
307
|
-
|
|
308
|
-
# 订阅阻抗数据
|
|
309
|
-
if type == "impedance":
|
|
310
|
-
if topic in list(self.__signal_consumer.keys()):
|
|
311
|
-
logger.warning(f"exists {type} subscribe of {topic}")
|
|
312
|
-
else:
|
|
313
|
-
self.__impedance_consumer[topic] = q
|
|
314
|
-
|
|
315
|
-
return topic, q
|
|
316
|
-
|
|
288
|
+
|
|
317
289
|
def trigger(self, desc):
|
|
318
290
|
if self._edf_handler:
|
|
319
291
|
self.edf_handler.trigger(desc)
|
|
@@ -61,6 +61,22 @@ class StimulationChannel(ABC):
|
|
|
61
61
|
|
|
62
62
|
return result
|
|
63
63
|
|
|
64
|
+
def to_bytes_c256(self):
|
|
65
|
+
result = self.channel_id.to_bytes(1, 'little')
|
|
66
|
+
result += self.wave_form.to_bytes(1, 'little')
|
|
67
|
+
|
|
68
|
+
result += self._to_bytes_c256()
|
|
69
|
+
|
|
70
|
+
result += int(self.delay_time).to_bytes(4, 'little')
|
|
71
|
+
result += int(self.ramp_up * 1000).to_bytes(4, 'little')
|
|
72
|
+
result += int((self.duration + self.ramp_up) * 1000).to_bytes(4, 'little')
|
|
73
|
+
result += int(self.ramp_down * 1000).to_bytes(4, 'little')
|
|
74
|
+
|
|
75
|
+
return result
|
|
76
|
+
|
|
77
|
+
def _to_bytes_c256(self):
|
|
78
|
+
return bytes.fromhex("00000000000000000000000000000000000000000000000000000000")
|
|
79
|
+
|
|
64
80
|
def _ext_bytes(self):
|
|
65
81
|
return bytes.fromhex("00000000000000000000000000000000")
|
|
66
82
|
|
|
@@ -99,6 +115,9 @@ class DCStimulation(StimulationChannel):
|
|
|
99
115
|
"delay_time": self.delay_time
|
|
100
116
|
}
|
|
101
117
|
|
|
118
|
+
def _to_bytes_c256(self):
|
|
119
|
+
return int(self.current_max * 1000 ).to_bytes(2, 'little') + bytes.fromhex("0000000000000000000000000000000000000000000000000000")
|
|
120
|
+
|
|
102
121
|
def from_json(self, param):
|
|
103
122
|
pass
|
|
104
123
|
def __str__(self):
|
|
@@ -140,9 +159,19 @@ class SquareWaveStimulation(StimulationChannel):
|
|
|
140
159
|
result += int((self.duration + self.ramp_up) * 1000).to_bytes(4, 'little')
|
|
141
160
|
result += int(self.ramp_down * 1000).to_bytes(4, 'little')
|
|
142
161
|
|
|
143
|
-
return result
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
return result
|
|
163
|
+
|
|
164
|
+
def _to_bytes_c256(self):
|
|
165
|
+
result = int(self.current_max * 1000000 ).to_bytes(4, 'little')
|
|
166
|
+
result += int(self.frequency).to_bytes(2, 'little')
|
|
167
|
+
result += bytes.fromhex("0000")
|
|
168
|
+
result += struct.pack('<f', self.duty_cycle)
|
|
169
|
+
result = int(self.current_min * 1000000 ).to_bytes(4, 'little')
|
|
170
|
+
result += self._ext_bytes()
|
|
171
|
+
return result
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# 刺激模式-正弦
|
|
146
175
|
class ACStimulation(StimulationChannel):
|
|
147
176
|
'''
|
|
148
177
|
channel_id: int, 通道编号,从0开始
|
|
@@ -177,6 +206,15 @@ class ACStimulation(StimulationChannel):
|
|
|
177
206
|
|
|
178
207
|
def from_json(self, param):
|
|
179
208
|
pass
|
|
209
|
+
|
|
210
|
+
def _to_bytes_c256(self):
|
|
211
|
+
result = int(self.current_max * 1000 ).to_bytes(2, 'little')
|
|
212
|
+
result += bytes.fromhex("0000")
|
|
213
|
+
result += struct.pack('<f', self.frequency)
|
|
214
|
+
result += struct.pack('<f', self.phase_position)
|
|
215
|
+
result += self._ext_bytes()
|
|
216
|
+
return result
|
|
217
|
+
|
|
180
218
|
def __str__(self):
|
|
181
219
|
return f"ACStimulation(channel_id={self.channel_id}, waveform={self.waveform}, current={self.current_max}, duration={self.duration}, ramp_up={self.ramp_up}, ramp_down={self.ramp_down}, frequency={self.frequency}, phase_position={self.phase_position}, duration_delay={self.duration_delay})"
|
|
182
220
|
|
|
@@ -230,6 +268,15 @@ class PulseStimulation(StimulationChannel):
|
|
|
230
268
|
|
|
231
269
|
return result
|
|
232
270
|
|
|
271
|
+
def _to_bytes_c256(self):
|
|
272
|
+
result = int(self.current_max * 1000000 ).to_bytes(4, 'little')
|
|
273
|
+
result += int(self.frequency).to_bytes(2, 'little')
|
|
274
|
+
result += bytes.fromhex("0000")
|
|
275
|
+
result += struct.pack('<f', self.duty_cycle)
|
|
276
|
+
result = int(self.current_min * 1000000 ).to_bytes(4, 'little')
|
|
277
|
+
result += self._ext_bytes()
|
|
278
|
+
return result
|
|
279
|
+
|
|
233
280
|
def to_json(self):
|
|
234
281
|
return {
|
|
235
282
|
"channel_id": self.channel_id,
|
|
@@ -287,6 +334,17 @@ class StimulationParadigm(object):
|
|
|
287
334
|
result += channel.to_bytes()
|
|
288
335
|
return result
|
|
289
336
|
|
|
337
|
+
def to_bytes_c256(self):
|
|
338
|
+
result = to_bytes(list(self.channels.keys()), 256)
|
|
339
|
+
result += int(self.duration * 1000).to_bytes(4, 'little')
|
|
340
|
+
result += int(self.interval_time).to_bytes(4, 'little')
|
|
341
|
+
result += int(self.characteristic).to_bytes(4, 'little')
|
|
342
|
+
result += int(self.mode).to_bytes(1, 'little')
|
|
343
|
+
result += int(self.repeats).to_bytes(4, 'little')
|
|
344
|
+
for channel in self.channels.values():
|
|
345
|
+
result += channel.to_bytes()
|
|
346
|
+
return result
|
|
347
|
+
|
|
290
348
|
def to_json(self):
|
|
291
349
|
# Convert the object to JSON for transmission
|
|
292
350
|
return {
|
|
@@ -306,8 +364,70 @@ class StimulationParadigm(object):
|
|
|
306
364
|
def clear(self):
|
|
307
365
|
self.channels = None
|
|
308
366
|
self.duration = None
|
|
309
|
-
self.interval_time =
|
|
367
|
+
self.interval_time = 25000
|
|
310
368
|
self.characteristic = 1
|
|
311
369
|
self.mode = 0
|
|
312
370
|
self.repeats = 0
|
|
313
371
|
|
|
372
|
+
|
|
373
|
+
# 刺激范式
|
|
374
|
+
class C256StimulationParadigm(object):
|
|
375
|
+
def __init__(self):
|
|
376
|
+
self.channels = None
|
|
377
|
+
self.duration = None
|
|
378
|
+
self.interval_time = 0
|
|
379
|
+
self.characteristic = 1
|
|
380
|
+
self.mode = 0
|
|
381
|
+
self.repeats = 0
|
|
382
|
+
|
|
383
|
+
def add_channel(self, channel: StimulationChannel, update=False):
|
|
384
|
+
if self.channels is None:
|
|
385
|
+
self.channels = {}
|
|
386
|
+
channel_id = channel.channel_id + 1
|
|
387
|
+
if channel_id in self.channels.keys():
|
|
388
|
+
logger.warning(f"Channel {channel_id} already exists")
|
|
389
|
+
if update:
|
|
390
|
+
self.channels[channel_id] = channel
|
|
391
|
+
else:
|
|
392
|
+
self.channels[channel_id] = channel
|
|
393
|
+
|
|
394
|
+
# 计算刺激时间
|
|
395
|
+
duration = channel.duration + channel.ramp_up + channel.ramp_down
|
|
396
|
+
if self.duration is None or duration > self.duration:
|
|
397
|
+
self.duration = duration
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def to_bytes(self):
|
|
401
|
+
result = to_bytes(list(self.channels.keys()), 64)
|
|
402
|
+
result += int(self.duration * 1000).to_bytes(4, 'little')
|
|
403
|
+
result += int(self.interval_time).to_bytes(4, 'little')
|
|
404
|
+
result += int(self.characteristic).to_bytes(4, 'little')
|
|
405
|
+
result += int(self.mode).to_bytes(1, 'little')
|
|
406
|
+
result += int(self.repeats).to_bytes(4, 'little')
|
|
407
|
+
for channel in self.channels.values():
|
|
408
|
+
result += channel.to_bytes()
|
|
409
|
+
return result
|
|
410
|
+
|
|
411
|
+
def to_json(self):
|
|
412
|
+
# Convert the object to JSON for transmission
|
|
413
|
+
return {
|
|
414
|
+
"channels": list(self.channels.keys()),
|
|
415
|
+
"duration": self.duration,
|
|
416
|
+
"interval_time": self.interval_time,
|
|
417
|
+
"characteristic": self.characteristic,
|
|
418
|
+
"mode": self.mode,
|
|
419
|
+
"repeats": self.repeats,
|
|
420
|
+
"stim": [channel.to_json() for channel in self.channels.values()]
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
# @staticmethod
|
|
424
|
+
# def from_json(param: Dict[str, Any]):
|
|
425
|
+
# pass
|
|
426
|
+
|
|
427
|
+
def clear(self):
|
|
428
|
+
self.channels = None
|
|
429
|
+
self.duration = None
|
|
430
|
+
self.interval_time = 0
|
|
431
|
+
self.characteristic = 1
|
|
432
|
+
self.mode = 0
|
|
433
|
+
self.repeats = 0
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|