sensor-sdk 0.0.27__tar.gz → 0.0.32__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.
Potentially problematic release.
This version of sensor-sdk might be problematic. Click here for more details.
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/PKG-INFO +6 -3
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/README.md +2 -2
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor/gforce.py +70 -88
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor/sensor_data.py +5 -3
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor/sensor_data_context.py +68 -41
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor/sensor_profile.py +64 -24
- sensor_sdk-0.0.32/sensor/sensor_utils.py +906 -0
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor_sdk.egg-info/PKG-INFO +6 -3
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/setup.py +1 -1
- sensor-sdk-0.0.27/sensor/sensor_utils.py +0 -373
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/LICENSE.txt +0 -0
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor/__init__.py +0 -0
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor/sensor_controller.py +0 -0
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor/sensor_device.py +0 -0
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor_sdk.egg-info/SOURCES.txt +0 -0
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor_sdk.egg-info/dependency_links.txt +0 -0
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor_sdk.egg-info/requires.txt +0 -0
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor_sdk.egg-info/top_level.txt +0 -0
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/sensor_sdk.egg-info/zip-safe +0 -0
- {sensor-sdk-0.0.27 → sensor_sdk-0.0.32}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sensor-sdk
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.32
|
|
4
4
|
Summary: Python sdk for Synchroni
|
|
5
5
|
Home-page: https://github.com/oymotion/SynchroniSDKPython
|
|
6
6
|
Author: Martin Ye
|
|
@@ -8,6 +8,9 @@ Author-email: yecq_82@hotmail.com
|
|
|
8
8
|
Requires-Python: >=3.9.0
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE.txt
|
|
11
|
+
Requires-Dist: numpy
|
|
12
|
+
Requires-Dist: setuptools
|
|
13
|
+
Requires-Dist: bleak
|
|
11
14
|
|
|
12
15
|
# sensor-sdk
|
|
13
16
|
|
|
@@ -375,10 +378,10 @@ result = sensorProfile.setParam("NTF_IMU", "ON")
|
|
|
375
378
|
result = sensorProfile.setParam("NTF_BRTH", "ON")
|
|
376
379
|
# set BRTH data to ON or OFF, result is "OK" if succeed
|
|
377
380
|
|
|
378
|
-
result = sensorProfile.setParam("
|
|
381
|
+
result = sensorProfile.setParam("FILTER_50HZ", "ON")
|
|
379
382
|
# set 50Hz notch filter to ON or OFF, result is "OK" if succeed
|
|
380
383
|
|
|
381
|
-
result = sensorProfile.setParam("
|
|
384
|
+
result = sensorProfile.setParam("FILTER_60HZ", "ON")
|
|
382
385
|
# set 60Hz notch filter to ON or OFF, result is "OK" if succeed
|
|
383
386
|
|
|
384
387
|
result = sensorProfile.setParam("FILTER_HPF", "ON")
|
|
@@ -364,10 +364,10 @@ result = sensorProfile.setParam("NTF_IMU", "ON")
|
|
|
364
364
|
result = sensorProfile.setParam("NTF_BRTH", "ON")
|
|
365
365
|
# set BRTH data to ON or OFF, result is "OK" if succeed
|
|
366
366
|
|
|
367
|
-
result = sensorProfile.setParam("
|
|
367
|
+
result = sensorProfile.setParam("FILTER_50HZ", "ON")
|
|
368
368
|
# set 50Hz notch filter to ON or OFF, result is "OK" if succeed
|
|
369
369
|
|
|
370
|
-
result = sensorProfile.setParam("
|
|
370
|
+
result = sensorProfile.setParam("FILTER_60HZ", "ON")
|
|
371
371
|
# set 60Hz notch filter to ON or OFF, result is "OK" if succeed
|
|
372
372
|
|
|
373
373
|
result = sensorProfile.setParam("FILTER_HPF", "ON")
|
|
@@ -3,6 +3,7 @@ import queue
|
|
|
3
3
|
import struct
|
|
4
4
|
from asyncio import Queue
|
|
5
5
|
from contextlib import suppress
|
|
6
|
+
from datetime import datetime
|
|
6
7
|
from dataclasses import dataclass
|
|
7
8
|
from enum import IntEnum
|
|
8
9
|
from typing import Optional, Dict, List
|
|
@@ -88,6 +89,7 @@ class Command(IntEnum):
|
|
|
88
89
|
GET_EMG_RAWDATA_CONFIG = (0x46,)
|
|
89
90
|
|
|
90
91
|
SET_DATA_NOTIF_SWITCH = (0x4F,)
|
|
92
|
+
SET_FUNCTION_SWITCH = (0x85,)
|
|
91
93
|
|
|
92
94
|
CMD_GET_EEG_CONFIG = (0xA0,)
|
|
93
95
|
CMD_SET_EEG_CONFIG = (0xA1,)
|
|
@@ -372,12 +374,21 @@ class Response:
|
|
|
372
374
|
|
|
373
375
|
|
|
374
376
|
class GForce:
|
|
375
|
-
def __init__(
|
|
377
|
+
def __init__(
|
|
378
|
+
self,
|
|
379
|
+
device: BLEDevice,
|
|
380
|
+
cmd_char: str,
|
|
381
|
+
data_char: str,
|
|
382
|
+
isUniversalStream: bool,
|
|
383
|
+
event_loop: asyncio.AbstractEventLoop,
|
|
384
|
+
gforce_event_loop: asyncio.AbstractEventLoop,
|
|
385
|
+
):
|
|
376
386
|
self.device_name = ""
|
|
377
387
|
self.client = None
|
|
388
|
+
self.event_loop = event_loop
|
|
389
|
+
self.gforce_event_loop = gforce_event_loop
|
|
378
390
|
self.cmd_char = cmd_char
|
|
379
391
|
self.data_char = data_char
|
|
380
|
-
self.current_request: Request = None
|
|
381
392
|
self.responses: Dict[Command, Queue] = {}
|
|
382
393
|
self.resolution = SampleResolution.BITS_8
|
|
383
394
|
self._num_channels = 8
|
|
@@ -394,7 +405,7 @@ class GForce:
|
|
|
394
405
|
self._raw_data_buf = buf
|
|
395
406
|
|
|
396
407
|
try:
|
|
397
|
-
await
|
|
408
|
+
await sensor_utils.async_call(client.connect(), sensor_utils._TIMEOUT, self.gforce_event_loop)
|
|
398
409
|
except Exception as e:
|
|
399
410
|
return
|
|
400
411
|
|
|
@@ -403,19 +414,22 @@ class GForce:
|
|
|
403
414
|
|
|
404
415
|
try:
|
|
405
416
|
if not self._is_universal_stream:
|
|
406
|
-
await
|
|
417
|
+
await sensor_utils.async_call(
|
|
407
418
|
client.start_notify(self.cmd_char, self._on_cmd_response),
|
|
408
419
|
sensor_utils._TIMEOUT,
|
|
420
|
+
self.gforce_event_loop,
|
|
409
421
|
)
|
|
422
|
+
|
|
410
423
|
else:
|
|
411
|
-
await
|
|
424
|
+
await sensor_utils.async_call(
|
|
412
425
|
client.start_notify(self.data_char, self._on_universal_response),
|
|
413
426
|
sensor_utils._TIMEOUT,
|
|
427
|
+
self.gforce_event_loop,
|
|
414
428
|
)
|
|
415
429
|
except Exception as e:
|
|
416
430
|
return
|
|
417
431
|
|
|
418
|
-
def _on_data_response(self, q: Queue[bytes], bs: bytearray):
|
|
432
|
+
def _on_data_response(self, q: queue.Queue[bytes], bs: bytearray):
|
|
419
433
|
bs = bytes(bs)
|
|
420
434
|
|
|
421
435
|
full_packet = []
|
|
@@ -444,63 +458,6 @@ class GForce:
|
|
|
444
458
|
return
|
|
445
459
|
|
|
446
460
|
q.put_nowait(bytes(full_packet))
|
|
447
|
-
# data = None
|
|
448
|
-
# data_type = DataType(full_packet[0])
|
|
449
|
-
# packet = full_packet[1:]
|
|
450
|
-
# match data_type:
|
|
451
|
-
# case DataType.EMG_ADC:
|
|
452
|
-
# data = self._convert_emg_to_raw(packet)
|
|
453
|
-
|
|
454
|
-
# case DataType.ACC:
|
|
455
|
-
# data = self._convert_acceleration_to_g(packet)
|
|
456
|
-
|
|
457
|
-
# case DataType.GYO:
|
|
458
|
-
# data = self._convert_gyro_to_dps(packet)
|
|
459
|
-
|
|
460
|
-
# case DataType.MAG:
|
|
461
|
-
# data = self._convert_magnetometer_to_ut(packet)
|
|
462
|
-
|
|
463
|
-
# case DataType.EULER:
|
|
464
|
-
# data = self._convert_euler(packet)
|
|
465
|
-
|
|
466
|
-
# case DataType.QUAT:
|
|
467
|
-
# data = self._convert_quaternion(packet)
|
|
468
|
-
|
|
469
|
-
# case DataType.ROTA:
|
|
470
|
-
# data = self._convert_rotation_matrix(packet)
|
|
471
|
-
|
|
472
|
-
# case DataType.EMG_GEST: # It is not supported by the device (?)
|
|
473
|
-
# data = self._convert_emg_gesture(packet)
|
|
474
|
-
|
|
475
|
-
# case DataType.HID_MOUSE: # It is not supported by the device
|
|
476
|
-
# pass
|
|
477
|
-
|
|
478
|
-
# case DataType.HID_JOYSTICK: # It is not supported by the device
|
|
479
|
-
# pass
|
|
480
|
-
|
|
481
|
-
# case DataType.PARTIAL:
|
|
482
|
-
# pass
|
|
483
|
-
# case _:
|
|
484
|
-
# raise Exception(
|
|
485
|
-
# f"Unknown data type {data_type}, full packet: {full_packet}"
|
|
486
|
-
# )
|
|
487
|
-
|
|
488
|
-
# q.put_nowait(data)
|
|
489
|
-
|
|
490
|
-
# def _convert_emg_to_raw(self, data: bytes) -> np.ndarray[np.integer]:
|
|
491
|
-
# match self.resolution:
|
|
492
|
-
# case SampleResolution.BITS_8:
|
|
493
|
-
# dtype = np.uint8
|
|
494
|
-
|
|
495
|
-
# case SampleResolution.BITS_12:
|
|
496
|
-
# dtype = np.uint16
|
|
497
|
-
|
|
498
|
-
# case _:
|
|
499
|
-
# raise Exception(f"Unsupported resolution {self.resolution}")
|
|
500
|
-
|
|
501
|
-
# emg_data = np.frombuffer(data, dtype=dtype)
|
|
502
|
-
|
|
503
|
-
# return emg_data.reshape(-1, self._num_channels)
|
|
504
461
|
|
|
505
462
|
@staticmethod
|
|
506
463
|
def _convert_acceleration_to_g(data: bytes) -> np.ndarray[np.float32]:
|
|
@@ -565,6 +522,9 @@ class GForce:
|
|
|
565
522
|
self._raw_data_buf.put_nowait(bytes(bs))
|
|
566
523
|
|
|
567
524
|
def _on_cmd_response(self, _: BleakGATTCharacteristic, bs: bytearray):
|
|
525
|
+
sensor_utils.async_exec(self.async_on_cmd_response(bs), self.event_loop)
|
|
526
|
+
|
|
527
|
+
async def async_on_cmd_response(self, bs: bytearray):
|
|
568
528
|
try:
|
|
569
529
|
# print(bytes(bs))
|
|
570
530
|
response = self._parse_response(bytes(bs))
|
|
@@ -754,6 +714,17 @@ class GForce:
|
|
|
754
714
|
)
|
|
755
715
|
)
|
|
756
716
|
|
|
717
|
+
async def set_function_switch(self, funcSwitch):
|
|
718
|
+
body = [0xFF & funcSwitch]
|
|
719
|
+
body = bytes(body)
|
|
720
|
+
ret = await self._send_request(
|
|
721
|
+
Request(
|
|
722
|
+
cmd=Command.SET_FUNCTION_SWITCH,
|
|
723
|
+
body=body,
|
|
724
|
+
has_res=True,
|
|
725
|
+
)
|
|
726
|
+
)
|
|
727
|
+
|
|
757
728
|
async def set_firmware_filter_switch(self, switchStatus: int):
|
|
758
729
|
body = [0xFF & switchStatus]
|
|
759
730
|
body = bytes(body)
|
|
@@ -858,22 +829,20 @@ class GForce:
|
|
|
858
829
|
)
|
|
859
830
|
|
|
860
831
|
async def start_streaming(self, q: queue.Queue):
|
|
861
|
-
await
|
|
832
|
+
await sensor_utils.async_call(
|
|
862
833
|
self.client.start_notify(
|
|
863
834
|
self.data_char,
|
|
864
835
|
lambda _, data: self._on_data_response(q, data),
|
|
865
836
|
),
|
|
866
837
|
sensor_utils._TIMEOUT,
|
|
838
|
+
self.gforce_event_loop,
|
|
867
839
|
)
|
|
868
840
|
|
|
869
841
|
async def stop_streaming(self):
|
|
870
842
|
exceptions = []
|
|
871
|
-
|
|
872
|
-
# await asyncio.wait_for(self.set_subscription(DataSubscription.OFF), sensor_utils._TIMEOUT)
|
|
873
|
-
# except Exception as e:
|
|
874
|
-
# exceptions.append(e)
|
|
843
|
+
|
|
875
844
|
try:
|
|
876
|
-
await
|
|
845
|
+
await sensor_utils.async_call(self.client.stop_notify(self.data_char), sensor_utils._TIMEOUT, self.gforce_event_loop)
|
|
877
846
|
except Exception as e:
|
|
878
847
|
exceptions.append(e)
|
|
879
848
|
|
|
@@ -883,49 +852,62 @@ class GForce:
|
|
|
883
852
|
async def disconnect(self):
|
|
884
853
|
with suppress(asyncio.CancelledError):
|
|
885
854
|
try:
|
|
886
|
-
await
|
|
855
|
+
await sensor_utils.async_call(self.client.disconnect(), sensor_utils._TIMEOUT, self.gforce_event_loop)
|
|
887
856
|
except Exception as e:
|
|
888
857
|
pass
|
|
889
858
|
|
|
890
859
|
def _get_response_channel(self, cmd: Command) -> Queue:
|
|
891
860
|
if self.responses.get(cmd) != None:
|
|
892
|
-
return
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
861
|
+
return self.responses[cmd]
|
|
862
|
+
else:
|
|
863
|
+
q = Queue()
|
|
864
|
+
self.responses[cmd] = q
|
|
865
|
+
return q
|
|
897
866
|
|
|
898
867
|
async def _send_request(self, req: Request) -> Optional[bytes]:
|
|
868
|
+
return await sensor_utils.async_call(self._send_request_internal(req=req), runloop=self.event_loop)
|
|
899
869
|
|
|
870
|
+
async def _send_request_internal(self, req: Request) -> Optional[bytes]:
|
|
900
871
|
q = None
|
|
901
872
|
if req.has_res:
|
|
902
873
|
q = self._get_response_channel(req.cmd)
|
|
903
|
-
if q == None:
|
|
904
|
-
# print("duplicate")
|
|
905
|
-
return None
|
|
906
874
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
875
|
+
timeStamp_old = -1
|
|
876
|
+
while not q.empty():
|
|
877
|
+
timeStamp_old = q.get_nowait()
|
|
878
|
+
|
|
879
|
+
now = datetime.now()
|
|
880
|
+
timestamp_now = now.timestamp()
|
|
881
|
+
if (timeStamp_old > -1) and ((timestamp_now - timeStamp_old) < 3):
|
|
882
|
+
print("send request too fast")
|
|
883
|
+
q.put_nowait(timeStamp_old)
|
|
884
|
+
return None
|
|
910
885
|
|
|
911
|
-
self.current_request = req
|
|
912
886
|
bs = bytes([req.cmd])
|
|
913
887
|
if req.body is not None:
|
|
914
888
|
bs += req.body
|
|
915
889
|
|
|
916
890
|
# print(str(req.cmd) + str(req.body))
|
|
917
|
-
|
|
891
|
+
try:
|
|
892
|
+
await sensor_utils.async_call(
|
|
893
|
+
self.client.write_gatt_char(self.cmd_char, bs),
|
|
894
|
+
1,
|
|
895
|
+
runloop=self.gforce_event_loop,
|
|
896
|
+
)
|
|
897
|
+
except Exception as e:
|
|
898
|
+
self.responses[req.cmd] = None
|
|
899
|
+
return None
|
|
918
900
|
|
|
919
901
|
if not req.has_res:
|
|
920
|
-
self.
|
|
902
|
+
self.responses[req.cmd] = None
|
|
921
903
|
return None
|
|
922
904
|
|
|
923
905
|
try:
|
|
924
|
-
ret = await asyncio.wait_for(q.get(),
|
|
925
|
-
|
|
926
|
-
|
|
906
|
+
ret = await asyncio.wait_for(q.get(), 2)
|
|
907
|
+
now = datetime.now()
|
|
908
|
+
timestamp_now = now.timestamp()
|
|
909
|
+
q.put_nowait(timestamp_now)
|
|
927
910
|
return ret
|
|
928
911
|
except Exception as e:
|
|
929
|
-
self.current_request = None
|
|
930
912
|
self.responses[req.cmd] = None
|
|
931
913
|
return None
|
|
@@ -41,10 +41,12 @@ class DataType(IntEnum):
|
|
|
41
41
|
NTF_EMG = 0x8 # EMG,用于标识肌电传感器采集的数据
|
|
42
42
|
NTF_EEG = 0x10 # EEG,用于标识脑电传感器采集的数据
|
|
43
43
|
NTF_ECG = 0x11 # ECG,用于标识心电传感器采集的数据
|
|
44
|
-
NTF_IMPEDANCE =
|
|
45
|
-
NTF_IMU =
|
|
46
|
-
NTF_ADS =
|
|
44
|
+
NTF_IMPEDANCE = 0x12 # 阻抗数据
|
|
45
|
+
NTF_IMU = 0x13 # 包含ACC和GYRO数据
|
|
46
|
+
NTF_ADS = 0x14 # 无单位ads数据
|
|
47
47
|
NTF_BRTH = 0x15 # 呼吸,用于标识呼吸传感器采集的数据
|
|
48
|
+
NTF_IMPEDANCE_EXT = 0x16 # 阻抗数据扩展
|
|
49
|
+
NTF_DATA_TYPE_MAX = 0x17
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
# 一次采样的数据,包含多个通道的数据,channal_samples 为一个二维数组, 第一个维度为通道索引,第二个维度为采样索引
|
|
@@ -61,7 +61,7 @@ class SensorProfileDataCtx:
|
|
|
61
61
|
self.saturationData: List[float] = list()
|
|
62
62
|
self.dataPool = ThreadPoolExecutor(1, "data")
|
|
63
63
|
self.init_map = {"NTF_EMG": "ON", "NTF_EEG": "ON", "NTF_ECG": "ON", "NTF_IMU": "ON", "NTF_BRTH": "ON"}
|
|
64
|
-
self.filter_map = {"
|
|
64
|
+
self.filter_map = {"FILTER_50HZ": "ON", "FILTER_60HZ": "ON", "FILTER_HPF": "ON", "FILTER_LPF": "ON"}
|
|
65
65
|
self.debugCSVWriter = None
|
|
66
66
|
self.debugCSVPath = None
|
|
67
67
|
|
|
@@ -138,6 +138,9 @@ class SensorProfileDataCtx:
|
|
|
138
138
|
await self.gForce.set_emg_raw_data_config(config)
|
|
139
139
|
|
|
140
140
|
await self.gForce.set_package_id(True)
|
|
141
|
+
|
|
142
|
+
await self.gForce.set_function_switch(2)
|
|
143
|
+
|
|
141
144
|
return data.channelCount
|
|
142
145
|
|
|
143
146
|
async def initEEG(self, packageCount: int) -> int:
|
|
@@ -337,10 +340,10 @@ class SensorProfileDataCtx:
|
|
|
337
340
|
switch = 0
|
|
338
341
|
for filter in self.filter_map.keys():
|
|
339
342
|
value = self.filter_map[filter]
|
|
340
|
-
if filter == "
|
|
343
|
+
if filter == "FILTER_50HZ":
|
|
341
344
|
if value == "ON":
|
|
342
345
|
switch |= 1
|
|
343
|
-
elif filter == "
|
|
346
|
+
elif filter == "FILTER_60HZ":
|
|
344
347
|
if value == "ON":
|
|
345
348
|
switch |= 2
|
|
346
349
|
elif filter == "FILTER_HPF":
|
|
@@ -350,8 +353,9 @@ class SensorProfileDataCtx:
|
|
|
350
353
|
if value == "ON":
|
|
351
354
|
switch |= 8
|
|
352
355
|
try:
|
|
353
|
-
await self.gForce.set_firmware_filter_switch(switch)
|
|
354
|
-
|
|
356
|
+
ret = await self.gForce.set_firmware_filter_switch(switch)
|
|
357
|
+
if ret == None:
|
|
358
|
+
return "ERROR: not success"
|
|
355
359
|
return "OK"
|
|
356
360
|
except Exception as e:
|
|
357
361
|
return "ERROR: " + str(e)
|
|
@@ -372,10 +376,8 @@ class SensorProfileDataCtx:
|
|
|
372
376
|
####################################################################################
|
|
373
377
|
|
|
374
378
|
async def process_data(self, buf: Queue[SensorData], sensor, callback):
|
|
375
|
-
while self._is_running
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
while self._is_running and not self._rawDataBuffer.empty():
|
|
379
|
+
while self._is_running:
|
|
380
|
+
while not self._rawDataBuffer.empty():
|
|
379
381
|
try:
|
|
380
382
|
data: bytes = self._rawDataBuffer.get_nowait()
|
|
381
383
|
except Exception as e:
|
|
@@ -385,7 +387,7 @@ class SensorProfileDataCtx:
|
|
|
385
387
|
self._processDataPackage(data, buf, sensor)
|
|
386
388
|
self._rawDataBuffer.task_done()
|
|
387
389
|
|
|
388
|
-
|
|
390
|
+
if self.isDataTransfering and not buf.empty():
|
|
389
391
|
sensorData: SensorData = None
|
|
390
392
|
try:
|
|
391
393
|
sensorData = buf.get_nowait()
|
|
@@ -398,6 +400,8 @@ class SensorProfileDataCtx:
|
|
|
398
400
|
print(e)
|
|
399
401
|
|
|
400
402
|
buf.task_done()
|
|
403
|
+
else:
|
|
404
|
+
await asyncio.sleep(0.01)
|
|
401
405
|
|
|
402
406
|
def _processDataPackage(self, data: bytes, buf: Queue[SensorData], sensor):
|
|
403
407
|
v = data[0] & 0x7F
|
|
@@ -423,6 +427,29 @@ class SensorProfileDataCtx:
|
|
|
423
427
|
|
|
424
428
|
self.impedanceData = impedanceData
|
|
425
429
|
self.saturationData = saturationData
|
|
430
|
+
elif v == DataType.NTF_IMPEDANCE_EXT:
|
|
431
|
+
offset = 1
|
|
432
|
+
# packageIndex = ((data[offset + 1] & 0xff) << 8) | (data[offset] & 0xff)
|
|
433
|
+
offset += 2
|
|
434
|
+
|
|
435
|
+
impedanceData = []
|
|
436
|
+
saturationData = []
|
|
437
|
+
|
|
438
|
+
dataCount = self._device_info.EegChannelCount + self._device_info.EcgChannelCount
|
|
439
|
+
|
|
440
|
+
for index in range(dataCount):
|
|
441
|
+
impedance = struct.unpack_from("<f", data, offset)[0]
|
|
442
|
+
offset += 4
|
|
443
|
+
impedanceData.append(impedance)
|
|
444
|
+
|
|
445
|
+
for index in range(dataCount):
|
|
446
|
+
saturation = struct.unpack_from("<H", data, offset)[0]
|
|
447
|
+
offset += 2
|
|
448
|
+
saturationData.append(saturation / 10) # firmware value range 0 - 1000
|
|
449
|
+
|
|
450
|
+
self.impedanceData = impedanceData
|
|
451
|
+
self.saturationData = saturationData
|
|
452
|
+
|
|
426
453
|
elif v == DataType.NTF_EMG:
|
|
427
454
|
sensor_data = self.sensorDatas[SensorDataType.DATA_TYPE_EMG]
|
|
428
455
|
if self.checkReadSamples(sensor, data, sensor_data, 3, 0):
|
|
@@ -673,7 +700,21 @@ class SensorProfileDataCtx:
|
|
|
673
700
|
|
|
674
701
|
while self._is_running:
|
|
675
702
|
while self._is_running and self._rawDataBuffer.empty():
|
|
676
|
-
|
|
703
|
+
if self._is_running and self.isDataTransfering and not buf.empty():
|
|
704
|
+
sensorData: SensorData = None
|
|
705
|
+
try:
|
|
706
|
+
sensorData = buf.get_nowait()
|
|
707
|
+
except Exception as e:
|
|
708
|
+
break
|
|
709
|
+
if not sensor_utils._terminated and sensorData != None and callback != None:
|
|
710
|
+
try:
|
|
711
|
+
asyncio.get_event_loop().run_in_executor(self.dataPool, callback, sensor, sensorData)
|
|
712
|
+
except Exception as e:
|
|
713
|
+
print(e)
|
|
714
|
+
|
|
715
|
+
buf.task_done()
|
|
716
|
+
else:
|
|
717
|
+
await asyncio.sleep(0.01)
|
|
677
718
|
continue
|
|
678
719
|
|
|
679
720
|
try:
|
|
@@ -694,53 +735,39 @@ class SensorProfileDataCtx:
|
|
|
694
735
|
|
|
695
736
|
if self._concatDataBuffer[index] == 0x55:
|
|
696
737
|
if (index + 1) >= data_size:
|
|
697
|
-
index
|
|
738
|
+
index = data_size
|
|
698
739
|
continue
|
|
699
740
|
n = self._concatDataBuffer[index + 1]
|
|
700
|
-
if (index + 1 + n +
|
|
701
|
-
index
|
|
741
|
+
if (index + 1 + n + 2) >= data_size:
|
|
742
|
+
index = data_size
|
|
702
743
|
continue
|
|
703
|
-
|
|
704
|
-
calc_crc = sensor_utils.
|
|
705
|
-
if
|
|
744
|
+
crc16 = (self._concatDataBuffer[index + 1 + n + 2] << 8) | self._concatDataBuffer[index + 1 + n + 1]
|
|
745
|
+
calc_crc = sensor_utils.crc16_cal(self._concatDataBuffer[index + 2 : index + 2 + n], n)
|
|
746
|
+
if crc16 != calc_crc:
|
|
706
747
|
index += 1
|
|
707
748
|
continue
|
|
708
749
|
if self._is_data_transfering:
|
|
709
750
|
data_package = bytes(self._concatDataBuffer[index + 2 : index + 2 + n])
|
|
710
751
|
self._processDataPackage(data_package, buf, sensor)
|
|
711
|
-
|
|
712
|
-
sensorData: SensorData = None
|
|
713
|
-
try:
|
|
714
|
-
sensorData = buf.get_nowait()
|
|
715
|
-
except Exception as e:
|
|
716
|
-
break
|
|
717
|
-
if not sensor_utils._terminated and sensorData != None and callback != None:
|
|
718
|
-
try:
|
|
719
|
-
asyncio.get_event_loop().run_in_executor(self.dataPool, callback, sensor, sensorData)
|
|
720
|
-
except Exception as e:
|
|
721
|
-
print(e)
|
|
722
|
-
|
|
723
|
-
buf.task_done()
|
|
724
|
-
last_cut = index = index + 2 + n
|
|
725
|
-
|
|
752
|
+
last_cut = index = index + 2 + n + 1
|
|
726
753
|
elif self._concatDataBuffer[index] == 0xAA:
|
|
727
754
|
if (index + 1) >= data_size:
|
|
728
|
-
index
|
|
755
|
+
index = data_size
|
|
729
756
|
continue
|
|
730
757
|
n = self._concatDataBuffer[index + 1]
|
|
731
|
-
if (index + 1 + n +
|
|
732
|
-
index
|
|
758
|
+
if (index + 1 + n + 2) >= data_size:
|
|
759
|
+
index = data_size
|
|
733
760
|
continue
|
|
734
|
-
|
|
735
|
-
calc_crc = sensor_utils.
|
|
736
|
-
if
|
|
761
|
+
crc16 = (self._concatDataBuffer[index + 1 + n + 2] << 8) | self._concatDataBuffer[index + 1 + n + 1]
|
|
762
|
+
calc_crc = sensor_utils.crc16_cal(self._concatDataBuffer[index + 2 : index + 2 + n], n)
|
|
763
|
+
if crc16 != calc_crc:
|
|
737
764
|
index += 1
|
|
738
765
|
continue
|
|
739
766
|
data_package = bytes(self._concatDataBuffer[index + 2 : index + 2 + n])
|
|
740
|
-
if not sensor_utils._terminated:
|
|
741
|
-
self.gForce._on_cmd_response(None, data_package)
|
|
742
|
-
last_cut = index = index + 2 + n
|
|
743
767
|
|
|
768
|
+
if not sensor_utils._terminated:
|
|
769
|
+
await sensor_utils.async_call(self.gForce.async_on_cmd_response(data_package), runloop=sensor._event_loop)
|
|
770
|
+
last_cut = index = index + 2 + n + 1
|
|
744
771
|
else:
|
|
745
772
|
index += 1
|
|
746
773
|
|