sensor-sdk 0.0.27__py3-none-any.whl → 0.0.32__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.

Potentially problematic release.


This version of sensor-sdk might be problematic. Click here for more details.

sensor/gforce.py CHANGED
@@ -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__(self, device: BLEDevice, cmd_char: str, data_char: str, isUniversalStream: bool):
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 asyncio.wait_for(client.connect(), sensor_utils._TIMEOUT)
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 asyncio.wait_for(
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 asyncio.wait_for(
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 asyncio.wait_for(
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
- # try:
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 asyncio.wait_for(self.client.stop_notify(self.data_char), sensor_utils._TIMEOUT)
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 asyncio.wait_for(self.client.disconnect(), sensor_utils._TIMEOUT)
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 None
893
-
894
- q = Queue()
895
- self.responses[cmd] = q
896
- return q
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
- while self.current_request != None:
908
- # print("wait")
909
- await asyncio.sleep(0.1)
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
- await asyncio.wait_for(self.client.write_gatt_char(self.cmd_char, bs, response=False), 0.1)
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.current_request = None
902
+ self.responses[req.cmd] = None
921
903
  return None
922
904
 
923
905
  try:
924
- ret = await asyncio.wait_for(q.get(), 0.5)
925
- self.current_request = None
926
- self.responses[req.cmd] = None
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
sensor/sensor_data.py CHANGED
@@ -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 = (0x12,) # 阻抗数据
45
- NTF_IMU = (0x13,) # 包含ACC和GYRO数据
46
- NTF_ADS = (0x14,) # 无单位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 = {"FILTER_50Hz": "ON", "FILTER_60Hz": "ON", "FILTER_HPF": "ON", "FILTER_LPF": "ON"}
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 == "FILTER_50Hz":
343
+ if filter == "FILTER_50HZ":
341
344
  if value == "ON":
342
345
  switch |= 1
343
- elif filter == "FILTER_60Hz":
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
- # await asyncio.sleep(0.1)
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 and self._rawDataBuffer.empty():
376
- await asyncio.sleep(0.1)
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
- while self._is_running and self.isDataTransfering and not buf.empty():
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
- await asyncio.sleep(0.1)
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 += 1
738
+ index = data_size
698
739
  continue
699
740
  n = self._concatDataBuffer[index + 1]
700
- if (index + 1 + n + 1) >= data_size:
701
- index += 1
741
+ if (index + 1 + n + 2) >= data_size:
742
+ index = data_size
702
743
  continue
703
- crc = self._concatDataBuffer[index + 1 + n + 1]
704
- calc_crc = sensor_utils.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
705
- if crc != calc_crc:
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
- while self._is_running and self.isDataTransfering and not buf.empty():
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 += 1
755
+ index = data_size
729
756
  continue
730
757
  n = self._concatDataBuffer[index + 1]
731
- if (index + 1 + n + 1) >= data_size:
732
- index += 1
758
+ if (index + 1 + n + 2) >= data_size:
759
+ index = data_size
733
760
  continue
734
- crc = self._concatDataBuffer[index + 1 + n + 1]
735
- calc_crc = sensor_utils.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
736
- if crc != calc_crc:
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
 
sensor/sensor_profile.py CHANGED
@@ -62,6 +62,8 @@ class SensorProfile:
62
62
  self._gforce: GForce = None
63
63
  self._data_event_loop: asyncio.AbstractEventLoop = None
64
64
  self._data_event_thread: threading.Thread = None
65
+ self._gforce_event_loop: asyncio.AbstractEventLoop = None
66
+ self._gforce_event_thread: threading.Thread = None
65
67
  self._event_loop: asyncio.AbstractEventLoop = None
66
68
  self._event_thread: threading.Thread = None
67
69
  self._is_starting = False
@@ -92,6 +94,17 @@ class SensorProfile:
92
94
  except Exception as e:
93
95
  pass
94
96
 
97
+ if self._gforce_event_loop != None:
98
+ try:
99
+ self._gforce_event_loop.stop()
100
+ self._gforce_event_loop.close()
101
+ self._gforce_event_loop = None
102
+ except Exception as e:
103
+ pass
104
+
105
+ self._is_starting = False
106
+ self._is_setting_param = False
107
+
95
108
  @property
96
109
  def deviceState(self) -> DeviceStateEx:
97
110
  """
@@ -215,6 +228,13 @@ class SensorProfile:
215
228
  self._on_power_changed = callback
216
229
 
217
230
  async def _initGforce(self):
231
+
232
+ self._gforce_event_loop = asyncio.new_event_loop()
233
+ self._gforce_event_thread = threading.Thread(target=sensor_utils.start_loop, args=(self._gforce_event_loop,))
234
+ self._gforce_event_thread.daemon = True
235
+ self._gforce_event_thread.name = self._detail_device.name + "data event"
236
+ self._gforce_event_thread.start()
237
+
218
238
  if self._gforce == None:
219
239
  if self._adv.service_data.get(SERVICE_GUID) != None:
220
240
  # print("OYM_SERVICE:" + self._detail_device.name)
@@ -223,21 +243,31 @@ class SensorProfile:
223
243
  OYM_CMD_NOTIFY_CHAR_UUID,
224
244
  OYM_DATA_NOTIFY_CHAR_UUID,
225
245
  False,
246
+ self._event_loop,
247
+ self._gforce_event_loop,
226
248
  )
227
249
  elif self._adv.service_data.get(RFSTAR_SERVICE_GUID) != None:
228
250
  # print("RFSTAR_SERVICE:" + self._detail_device.name)
229
- self._gforce = GForce(self._detail_device, RFSTAR_CMD_UUID, RFSTAR_DATA_UUID, True)
230
- self._data_event_loop = asyncio.new_event_loop()
251
+ self._gforce = GForce(
252
+ self._detail_device, RFSTAR_CMD_UUID, RFSTAR_DATA_UUID, True, self._event_loop, self._gforce_event_loop
253
+ )
254
+
231
255
  else:
232
256
  print("Invalid device service uuid:" + self._detail_device.name + str(self._adv))
233
257
  return False
234
258
 
259
+ self._data_event_loop = asyncio.new_event_loop()
260
+ self._data_event_thread = threading.Thread(target=sensor_utils.start_loop, args=(self._data_event_loop,))
261
+ self._data_event_thread.daemon = True
262
+ self._data_event_thread.name = self._detail_device.name + "data event"
263
+ self._data_event_thread.start()
264
+
235
265
  if self._data_ctx == None and self._gforce != None:
236
266
  self._data_ctx = SensorProfileDataCtx(self._gforce, self._device.Address, self._raw_data_buf)
237
267
  if self._data_ctx.isUniversalStream:
238
- async_exec(self._process_universal_data(), self._event_loop)
268
+ async_exec(self._process_universal_data(), self._data_event_loop)
239
269
  else:
240
- async_exec(self._process_data(), self._event_loop)
270
+ async_exec(self._process_data(), self._data_event_loop)
241
271
 
242
272
  async def _connect(self) -> bool:
243
273
  if sensor_utils._terminated:
@@ -249,6 +279,7 @@ class SensorProfile:
249
279
  self._event_thread.daemon = True
250
280
  self._event_thread.name = self._detail_device.name + "event"
251
281
  self._event_thread.start()
282
+
252
283
  self._data_buffer: Queue[SensorData] = Queue()
253
284
  self._raw_data_buf: Queue[bytes] = Queue()
254
285
 
@@ -269,7 +300,7 @@ class SensorProfile:
269
300
  self._set_device_state(DeviceStateEx.Disconnected)
270
301
  pass
271
302
 
272
- await async_call(self._gforce.connect(handle_disconnect, self._raw_data_buf), runloop=self._event_loop)
303
+ await self._gforce.connect(handle_disconnect, self._raw_data_buf)
273
304
 
274
305
  if self._gforce != None and self._gforce.client.is_connected:
275
306
  self._set_device_state(DeviceStateEx.Connected)
@@ -313,7 +344,7 @@ class SensorProfile:
313
344
  if self._data_ctx == None:
314
345
  return False
315
346
  self._set_device_state(DeviceStateEx.Disconnecting)
316
- async_exec(self._gforce.disconnect(), self._event_loop)
347
+ await self._gforce.disconnect()
317
348
  await asyncio.wait_for(self._waitForDisconnect(), sensor_utils._TIMEOUT)
318
349
 
319
350
  return True
@@ -341,6 +372,7 @@ class SensorProfile:
341
372
 
342
373
  async def _process_universal_data(self):
343
374
  await self._data_ctx.processUniversalData(self._data_buffer, self, self._on_data_callback)
375
+ print("finished")
344
376
 
345
377
  async def _startDataNotification(self) -> bool:
346
378
  if self.deviceState != DeviceStateEx.Ready:
@@ -353,13 +385,10 @@ class SensorProfile:
353
385
  if self._data_ctx.isDataTransfering:
354
386
  return True
355
387
 
356
- if self._data_event_loop == None:
357
- self._data_event_loop = asyncio.new_event_loop()
358
-
359
388
  self._raw_data_buf.queue.clear()
360
389
  self._data_buffer.queue.clear()
361
390
 
362
- result = await async_call(self._data_ctx.start_streaming(), runloop=self._event_loop)
391
+ result = await async_call(self._data_ctx.start_streaming(), runloop=None)
363
392
  await asyncio.sleep(0.2)
364
393
 
365
394
  return result
@@ -413,7 +442,7 @@ class SensorProfile:
413
442
  if not self._data_ctx.isDataTransfering:
414
443
  return True
415
444
 
416
- result = await async_call(self._data_ctx.stop_streaming(), runloop=self._event_loop)
445
+ result = await async_call(self._data_ctx.stop_streaming(), runloop=None)
417
446
  return result
418
447
 
419
448
  def stopDataNotification(self) -> bool:
@@ -553,19 +582,18 @@ class SensorProfile:
553
582
  if self.deviceState != DeviceStateEx.Ready:
554
583
  result = "Error: Please connect first"
555
584
 
556
- if key in ["NTF_EMG", "NTF_EEG", "NTF_ECG", "NTF_IMU", "NTF_BRTH"]:
585
+ if key in ["NTF_EMG", "NTF_EEG", "NTF_ECG", "NTF_IMU", "NTF_BRTH", "NTF_IMPEDANCE"]:
557
586
  if value in ["ON", "OFF"]:
558
587
  self._data_ctx.init_map[key] = value
559
588
  result = "OK"
560
589
 
561
- if key in ["FILTER_50Hz", "FILTER_60Hz", "FILTER_HPF", "FILTER_LPF"]:
590
+ if key in ["FILTER_50HZ", "FILTER_60HZ", "FILTER_HPF", "FILTER_LPF"]:
562
591
  if value in ["ON", "OFF"]:
563
592
  result = await self._data_ctx.setFilter(key, value)
564
593
 
565
594
  if key == "DEBUG_BLE_DATA_PATH":
566
595
  result = await self._data_ctx.setDebugCSV(value)
567
596
 
568
- self._is_setting_param = False
569
597
  return result
570
598
 
571
599
  def setParam(self, key: str, value: str) -> str:
@@ -581,11 +609,17 @@ class SensorProfile:
581
609
  if self._is_setting_param:
582
610
  return "Error: Please wait for the previous operation to complete"
583
611
 
584
- self._is_setting_param = True
585
- return sync_call(
586
- self._setParam(key, value),
587
- 20,
588
- )
612
+ try:
613
+ self._is_setting_param = True
614
+ ret = sync_call(
615
+ self._setParam(key, value),
616
+ 1,
617
+ )
618
+ self._is_setting_param = False
619
+ return ret
620
+ except Exception as e:
621
+ self._is_setting_param = False
622
+ print(e)
589
623
 
590
624
  async def asyncSetParam(self, key: str, value: str) -> str:
591
625
  """
@@ -600,8 +634,14 @@ class SensorProfile:
600
634
  if self._is_setting_param:
601
635
  return "Error: Please wait for the previous operation to complete"
602
636
 
603
- self._is_setting_param = True
604
- return await async_call(
605
- self._setParam(key, value),
606
- 20,
607
- )
637
+ try:
638
+ self._is_setting_param = True
639
+ ret = await async_call(
640
+ self._setParam(key, value),
641
+ 1,
642
+ )
643
+ self._is_setting_param = False
644
+ return ret
645
+ except Exception as e:
646
+ self._is_setting_param = False
647
+ print(e)
sensor/sensor_utils.py CHANGED
@@ -371,3 +371,536 @@ def calc_crc8(data):
371
371
  crc8 = crc8Table[crc8]
372
372
 
373
373
  return crc8
374
+
375
+
376
+ def crc16_cal(data, iLen):
377
+ auchCRCHi = [
378
+ 0x00,
379
+ 0xC1,
380
+ 0x81,
381
+ 0x40,
382
+ 0x01,
383
+ 0xC0,
384
+ 0x80,
385
+ 0x41,
386
+ 0x01,
387
+ 0xC0,
388
+ 0x80,
389
+ 0x41,
390
+ 0x00,
391
+ 0xC1,
392
+ 0x81,
393
+ 0x40,
394
+ 0x01,
395
+ 0xC0,
396
+ 0x80,
397
+ 0x41,
398
+ 0x00,
399
+ 0xC1,
400
+ 0x81,
401
+ 0x40,
402
+ 0x00,
403
+ 0xC1,
404
+ 0x81,
405
+ 0x40,
406
+ 0x01,
407
+ 0xC0,
408
+ 0x80,
409
+ 0x41,
410
+ 0x01,
411
+ 0xC0,
412
+ 0x80,
413
+ 0x41,
414
+ 0x00,
415
+ 0xC1,
416
+ 0x81,
417
+ 0x40,
418
+ 0x00,
419
+ 0xC1,
420
+ 0x81,
421
+ 0x40,
422
+ 0x01,
423
+ 0xC0,
424
+ 0x80,
425
+ 0x41,
426
+ 0x00,
427
+ 0xC1,
428
+ 0x81,
429
+ 0x40,
430
+ 0x01,
431
+ 0xC0,
432
+ 0x80,
433
+ 0x41,
434
+ 0x01,
435
+ 0xC0,
436
+ 0x80,
437
+ 0x41,
438
+ 0x00,
439
+ 0xC1,
440
+ 0x81,
441
+ 0x40,
442
+ 0x01,
443
+ 0xC0,
444
+ 0x80,
445
+ 0x41,
446
+ 0x00,
447
+ 0xC1,
448
+ 0x81,
449
+ 0x40,
450
+ 0x00,
451
+ 0xC1,
452
+ 0x81,
453
+ 0x40,
454
+ 0x01,
455
+ 0xC0,
456
+ 0x80,
457
+ 0x41,
458
+ 0x00,
459
+ 0xC1,
460
+ 0x81,
461
+ 0x40,
462
+ 0x01,
463
+ 0xC0,
464
+ 0x80,
465
+ 0x41,
466
+ 0x01,
467
+ 0xC0,
468
+ 0x80,
469
+ 0x41,
470
+ 0x00,
471
+ 0xC1,
472
+ 0x81,
473
+ 0x40,
474
+ 0x00,
475
+ 0xC1,
476
+ 0x81,
477
+ 0x40,
478
+ 0x01,
479
+ 0xC0,
480
+ 0x80,
481
+ 0x41,
482
+ 0x01,
483
+ 0xC0,
484
+ 0x80,
485
+ 0x41,
486
+ 0x00,
487
+ 0xC1,
488
+ 0x81,
489
+ 0x40,
490
+ 0x01,
491
+ 0xC0,
492
+ 0x80,
493
+ 0x41,
494
+ 0x00,
495
+ 0xC1,
496
+ 0x81,
497
+ 0x40,
498
+ 0x00,
499
+ 0xC1,
500
+ 0x81,
501
+ 0x40,
502
+ 0x01,
503
+ 0xC0,
504
+ 0x80,
505
+ 0x41,
506
+ 0x01,
507
+ 0xC0,
508
+ 0x80,
509
+ 0x41,
510
+ 0x00,
511
+ 0xC1,
512
+ 0x81,
513
+ 0x40,
514
+ 0x00,
515
+ 0xC1,
516
+ 0x81,
517
+ 0x40,
518
+ 0x01,
519
+ 0xC0,
520
+ 0x80,
521
+ 0x41,
522
+ 0x00,
523
+ 0xC1,
524
+ 0x81,
525
+ 0x40,
526
+ 0x01,
527
+ 0xC0,
528
+ 0x80,
529
+ 0x41,
530
+ 0x01,
531
+ 0xC0,
532
+ 0x80,
533
+ 0x41,
534
+ 0x00,
535
+ 0xC1,
536
+ 0x81,
537
+ 0x40,
538
+ 0x00,
539
+ 0xC1,
540
+ 0x81,
541
+ 0x40,
542
+ 0x01,
543
+ 0xC0,
544
+ 0x80,
545
+ 0x41,
546
+ 0x01,
547
+ 0xC0,
548
+ 0x80,
549
+ 0x41,
550
+ 0x00,
551
+ 0xC1,
552
+ 0x81,
553
+ 0x40,
554
+ 0x01,
555
+ 0xC0,
556
+ 0x80,
557
+ 0x41,
558
+ 0x00,
559
+ 0xC1,
560
+ 0x81,
561
+ 0x40,
562
+ 0x00,
563
+ 0xC1,
564
+ 0x81,
565
+ 0x40,
566
+ 0x01,
567
+ 0xC0,
568
+ 0x80,
569
+ 0x41,
570
+ 0x00,
571
+ 0xC1,
572
+ 0x81,
573
+ 0x40,
574
+ 0x01,
575
+ 0xC0,
576
+ 0x80,
577
+ 0x41,
578
+ 0x01,
579
+ 0xC0,
580
+ 0x80,
581
+ 0x41,
582
+ 0x00,
583
+ 0xC1,
584
+ 0x81,
585
+ 0x40,
586
+ 0x01,
587
+ 0xC0,
588
+ 0x80,
589
+ 0x41,
590
+ 0x00,
591
+ 0xC1,
592
+ 0x81,
593
+ 0x40,
594
+ 0x00,
595
+ 0xC1,
596
+ 0x81,
597
+ 0x40,
598
+ 0x01,
599
+ 0xC0,
600
+ 0x80,
601
+ 0x41,
602
+ 0x01,
603
+ 0xC0,
604
+ 0x80,
605
+ 0x41,
606
+ 0x00,
607
+ 0xC1,
608
+ 0x81,
609
+ 0x40,
610
+ 0x00,
611
+ 0xC1,
612
+ 0x81,
613
+ 0x40,
614
+ 0x01,
615
+ 0xC0,
616
+ 0x80,
617
+ 0x41,
618
+ 0x00,
619
+ 0xC1,
620
+ 0x81,
621
+ 0x40,
622
+ 0x01,
623
+ 0xC0,
624
+ 0x80,
625
+ 0x41,
626
+ 0x01,
627
+ 0xC0,
628
+ 0x80,
629
+ 0x41,
630
+ 0x00,
631
+ 0xC1,
632
+ 0x81,
633
+ 0x40,
634
+ ]
635
+
636
+ auchCRCLo = [
637
+ 0x00,
638
+ 0xC0,
639
+ 0xC1,
640
+ 0x01,
641
+ 0xC3,
642
+ 0x03,
643
+ 0x02,
644
+ 0xC2,
645
+ 0xC6,
646
+ 0x06,
647
+ 0x07,
648
+ 0xC7,
649
+ 0x05,
650
+ 0xC5,
651
+ 0xC4,
652
+ 0x04,
653
+ 0xCC,
654
+ 0x0C,
655
+ 0x0D,
656
+ 0xCD,
657
+ 0x0F,
658
+ 0xCF,
659
+ 0xCE,
660
+ 0x0E,
661
+ 0x0A,
662
+ 0xCA,
663
+ 0xCB,
664
+ 0x0B,
665
+ 0xC9,
666
+ 0x09,
667
+ 0x08,
668
+ 0xC8,
669
+ 0xD8,
670
+ 0x18,
671
+ 0x19,
672
+ 0xD9,
673
+ 0x1B,
674
+ 0xDB,
675
+ 0xDA,
676
+ 0x1A,
677
+ 0x1E,
678
+ 0xDE,
679
+ 0xDF,
680
+ 0x1F,
681
+ 0xDD,
682
+ 0x1D,
683
+ 0x1C,
684
+ 0xDC,
685
+ 0x14,
686
+ 0xD4,
687
+ 0xD5,
688
+ 0x15,
689
+ 0xD7,
690
+ 0x17,
691
+ 0x16,
692
+ 0xD6,
693
+ 0xD2,
694
+ 0x12,
695
+ 0x13,
696
+ 0xD3,
697
+ 0x11,
698
+ 0xD1,
699
+ 0xD0,
700
+ 0x10,
701
+ 0xF0,
702
+ 0x30,
703
+ 0x31,
704
+ 0xF1,
705
+ 0x33,
706
+ 0xF3,
707
+ 0xF2,
708
+ 0x32,
709
+ 0x36,
710
+ 0xF6,
711
+ 0xF7,
712
+ 0x37,
713
+ 0xF5,
714
+ 0x35,
715
+ 0x34,
716
+ 0xF4,
717
+ 0x3C,
718
+ 0xFC,
719
+ 0xFD,
720
+ 0x3D,
721
+ 0xFF,
722
+ 0x3F,
723
+ 0x3E,
724
+ 0xFE,
725
+ 0xFA,
726
+ 0x3A,
727
+ 0x3B,
728
+ 0xFB,
729
+ 0x39,
730
+ 0xF9,
731
+ 0xF8,
732
+ 0x38,
733
+ 0x28,
734
+ 0xE8,
735
+ 0xE9,
736
+ 0x29,
737
+ 0xEB,
738
+ 0x2B,
739
+ 0x2A,
740
+ 0xEA,
741
+ 0xEE,
742
+ 0x2E,
743
+ 0x2F,
744
+ 0xEF,
745
+ 0x2D,
746
+ 0xED,
747
+ 0xEC,
748
+ 0x2C,
749
+ 0xE4,
750
+ 0x24,
751
+ 0x25,
752
+ 0xE5,
753
+ 0x27,
754
+ 0xE7,
755
+ 0xE6,
756
+ 0x26,
757
+ 0x22,
758
+ 0xE2,
759
+ 0xE3,
760
+ 0x23,
761
+ 0xE1,
762
+ 0x21,
763
+ 0x20,
764
+ 0xE0,
765
+ 0xA0,
766
+ 0x60,
767
+ 0x61,
768
+ 0xA1,
769
+ 0x63,
770
+ 0xA3,
771
+ 0xA2,
772
+ 0x62,
773
+ 0x66,
774
+ 0xA6,
775
+ 0xA7,
776
+ 0x67,
777
+ 0xA5,
778
+ 0x65,
779
+ 0x64,
780
+ 0xA4,
781
+ 0x6C,
782
+ 0xAC,
783
+ 0xAD,
784
+ 0x6D,
785
+ 0xAF,
786
+ 0x6F,
787
+ 0x6E,
788
+ 0xAE,
789
+ 0xAA,
790
+ 0x6A,
791
+ 0x6B,
792
+ 0xAB,
793
+ 0x69,
794
+ 0xA9,
795
+ 0xA8,
796
+ 0x68,
797
+ 0x78,
798
+ 0xB8,
799
+ 0xB9,
800
+ 0x79,
801
+ 0xBB,
802
+ 0x7B,
803
+ 0x7A,
804
+ 0xBA,
805
+ 0xBE,
806
+ 0x7E,
807
+ 0x7F,
808
+ 0xBF,
809
+ 0x7D,
810
+ 0xBD,
811
+ 0xBC,
812
+ 0x7C,
813
+ 0xB4,
814
+ 0x74,
815
+ 0x75,
816
+ 0xB5,
817
+ 0x77,
818
+ 0xB7,
819
+ 0xB6,
820
+ 0x76,
821
+ 0x72,
822
+ 0xB2,
823
+ 0xB3,
824
+ 0x73,
825
+ 0xB1,
826
+ 0x71,
827
+ 0x70,
828
+ 0xB0,
829
+ 0x50,
830
+ 0x90,
831
+ 0x91,
832
+ 0x51,
833
+ 0x93,
834
+ 0x53,
835
+ 0x52,
836
+ 0x92,
837
+ 0x96,
838
+ 0x56,
839
+ 0x57,
840
+ 0x97,
841
+ 0x55,
842
+ 0x95,
843
+ 0x94,
844
+ 0x54,
845
+ 0x9C,
846
+ 0x5C,
847
+ 0x5D,
848
+ 0x9D,
849
+ 0x5F,
850
+ 0x9F,
851
+ 0x9E,
852
+ 0x5E,
853
+ 0x5A,
854
+ 0x9A,
855
+ 0x9B,
856
+ 0x5B,
857
+ 0x99,
858
+ 0x59,
859
+ 0x58,
860
+ 0x98,
861
+ 0x88,
862
+ 0x48,
863
+ 0x49,
864
+ 0x89,
865
+ 0x4B,
866
+ 0x8B,
867
+ 0x8A,
868
+ 0x4A,
869
+ 0x4E,
870
+ 0x8E,
871
+ 0x8F,
872
+ 0x4F,
873
+ 0x8D,
874
+ 0x4D,
875
+ 0x4C,
876
+ 0x8C,
877
+ 0x44,
878
+ 0x84,
879
+ 0x85,
880
+ 0x45,
881
+ 0x87,
882
+ 0x47,
883
+ 0x46,
884
+ 0x86,
885
+ 0x82,
886
+ 0x42,
887
+ 0x43,
888
+ 0x83,
889
+ 0x41,
890
+ 0x81,
891
+ 0x80,
892
+ 0x40,
893
+ ]
894
+
895
+ uchCRCHi = 0xFF
896
+ uchCRCLo = 0xFF
897
+ index = 0
898
+
899
+ iLenCount = range(iLen)
900
+
901
+ for i in iLenCount:
902
+ index = uchCRCHi ^ data[i]
903
+ uchCRCHi = uchCRCLo ^ auchCRCHi[index]
904
+ uchCRCLo = auchCRCLo[index]
905
+
906
+ return (uchCRCHi << 8) | uchCRCLo
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sensor-sdk
3
- Version: 0.0.27
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
@@ -378,10 +378,10 @@ result = sensorProfile.setParam("NTF_IMU", "ON")
378
378
  result = sensorProfile.setParam("NTF_BRTH", "ON")
379
379
  # set BRTH data to ON or OFF, result is "OK" if succeed
380
380
 
381
- result = sensorProfile.setParam("FILTER_50Hz", "ON")
381
+ result = sensorProfile.setParam("FILTER_50HZ", "ON")
382
382
  # set 50Hz notch filter to ON or OFF, result is "OK" if succeed
383
383
 
384
- result = sensorProfile.setParam("FILTER_60Hz", "ON")
384
+ result = sensorProfile.setParam("FILTER_60HZ", "ON")
385
385
  # set 60Hz notch filter to ON or OFF, result is "OK" if succeed
386
386
 
387
387
  result = sensorProfile.setParam("FILTER_HPF", "ON")
@@ -0,0 +1,14 @@
1
+ sensor/__init__.py,sha256=L1VyAP0EDEnJIMeMTzp4iXHSRUUHyHScF_GIl3iYKRI,123
2
+ sensor/gforce.py,sha256=k6nHsNV_IM8ymGYrZXXD3oHC5Ed0qODlvk8W6dhtMMk,26291
3
+ sensor/sensor_controller.py,sha256=cjVOcGaU9_8_eWFamtOpXsqSZMzD7lGU24gC_rsm2FQ,9546
4
+ sensor/sensor_data.py,sha256=RRj5k3F7Cwv_sFvOPN3HqtklntUHFCFMIqzp8QHIhxM,4078
5
+ sensor/sensor_data_context.py,sha256=Y13eTdCKPHAptuMLH8AYozWjxBz2BIEPfcilba5nJX0,31888
6
+ sensor/sensor_device.py,sha256=eO1vaqjxCc2UCPBoKXqlk6o498uRyWt6IYs7r7wXSD0,3042
7
+ sensor/sensor_profile.py,sha256=cCAw8jDSp-EN7eznLhrbJo6dFhOLIOfgTunj47e3qT0,22671
8
+ sensor/sensor_utils.py,sha256=Laj9rJG_6e7mfrXBlw5_NKJC93mOKogati_YQso11Tw,15043
9
+ sensor_sdk-0.0.32.dist-info/LICENSE.txt,sha256=8CSivOpub3IuXODTyqBRI91AxouJZk02YrcKuOAkWu8,1111
10
+ sensor_sdk-0.0.32.dist-info/METADATA,sha256=Os3_yXfiRlJJRSBo9BBULzDpez7sVughbflcN2u2-Ic,9821
11
+ sensor_sdk-0.0.32.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
12
+ sensor_sdk-0.0.32.dist-info/top_level.txt,sha256=Ftq49B6bH0Ffdc7c8LkcyakHo6lsg_snlBbpEUoILSk,7
13
+ sensor_sdk-0.0.32.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
14
+ sensor_sdk-0.0.32.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,14 +0,0 @@
1
- sensor/__init__.py,sha256=L1VyAP0EDEnJIMeMTzp4iXHSRUUHyHScF_GIl3iYKRI,123
2
- sensor/gforce.py,sha256=0Jpou51yJxg7B8NjFG_A1j43Rtj95rk8YDDI2FEStR4,26892
3
- sensor/sensor_controller.py,sha256=cjVOcGaU9_8_eWFamtOpXsqSZMzD7lGU24gC_rsm2FQ,9546
4
- sensor/sensor_data.py,sha256=vKreLHZs7WbQcnfqHiLLfHQ3cCmvD1K2xl2WUQg8vv0,4005
5
- sensor/sensor_data_context.py,sha256=3EaCXuLNLQ7OgU2FkpbM5nQvhVyhA68cuw-yX_LpA78,30832
6
- sensor/sensor_device.py,sha256=eO1vaqjxCc2UCPBoKXqlk6o498uRyWt6IYs7r7wXSD0,3042
7
- sensor/sensor_profile.py,sha256=sLkOautCTfV-SdOhLXEcZwmjBk2lGx11-JKnkOPP2NM,21176
8
- sensor/sensor_utils.py,sha256=7JIZF6uhOcHuAPXwwrQi5FwGU5BVLc6Aml-n9QXzupM,6998
9
- sensor_sdk-0.0.27.dist-info/LICENSE.txt,sha256=8CSivOpub3IuXODTyqBRI91AxouJZk02YrcKuOAkWu8,1111
10
- sensor_sdk-0.0.27.dist-info/METADATA,sha256=Ut2upEYe7-oFiyEtLeL6Ft65RSTC7Vm5lN3ddTZ8lqU,9821
11
- sensor_sdk-0.0.27.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
12
- sensor_sdk-0.0.27.dist-info/top_level.txt,sha256=Ftq49B6bH0Ffdc7c8LkcyakHo6lsg_snlBbpEUoILSk,7
13
- sensor_sdk-0.0.27.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
14
- sensor_sdk-0.0.27.dist-info/RECORD,,