sensor-sdk 0.0.3__tar.gz → 0.0.4__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sensor-sdk
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: Python sdk for Synchroni
5
5
  Home-page: https://github.com/oymotion/SynchroniSDKPython
6
6
  Author: Martin Ye
@@ -13,16 +13,19 @@ from bleak import (
13
13
  BleakScanner,
14
14
  AdvertisementData,
15
15
  )
16
+
16
17
  SERVICE_GUID = "0000ffd0-0000-1000-8000-00805f9b34fb"
17
18
  RFSTAR_SERVICE_GUID = "00001812-0000-1000-8000-00805f9b34fb"
18
19
 
20
+
19
21
  class SensorController:
20
22
  _instance_lock = threading.Lock()
23
+
21
24
  def __new__(cls, *args, **kwargs):
22
25
  if not hasattr(SensorController, "_instance"):
23
26
  with SensorController._instance_lock:
24
27
  if not hasattr(SensorController, "_instance"):
25
- SensorController._instance = object.__new__(cls)
28
+ SensorController._instance = object.__new__(cls)
26
29
  return SensorController._instance
27
30
 
28
31
  """
@@ -34,16 +37,23 @@ class SensorController:
34
37
  初始化 SensorController 实例。
35
38
  """
36
39
  self._event_loop = asyncio.new_event_loop()
37
- self._event_thread = threading.Thread(target=start_loop, args=(self._event_loop,))
40
+ self._event_thread = threading.Thread(
41
+ target=start_loop, args=(self._event_loop,)
42
+ )
38
43
  self._event_thread.daemon = True
39
44
  self._event_thread.name = "SensorController event"
40
45
  self._event_thread.start()
41
46
  self._gforce_event_loop = asyncio.new_event_loop()
42
- self._gforce_event_thread = threading.Thread(target=start_loop, args=(self._gforce_event_loop,))
47
+ self._gforce_event_thread = threading.Thread(
48
+ target=start_loop, args=(self._gforce_event_loop,)
49
+ )
43
50
  self._gforce_event_thread.daemon = True
44
51
  self._gforce_event_thread.name = "BLE operation"
45
52
  self._gforce_event_thread.start()
46
- self._scanner = BleakScanner(detection_callback=self._match_device,service_uuids=[SERVICE_GUID,RFSTAR_SERVICE_GUID])
53
+ self._scanner = BleakScanner(
54
+ detection_callback=self._match_device,
55
+ service_uuids=[SERVICE_GUID, RFSTAR_SERVICE_GUID],
56
+ )
47
57
  self._is_scanning = False
48
58
  self._device_callback: Callable[[List[sensor_profile.BLEDevice]], None] = None
49
59
  self._device_callback_period = 0
@@ -60,17 +70,17 @@ class SensorController:
60
70
 
61
71
  def terminate(self):
62
72
  for sensor in self._sensor_profiles.values():
63
- if sensor.deviceState == DeviceStateEx.Connected or sensor.deviceState == DeviceStateEx.Ready:
73
+ if (
74
+ sensor.deviceState == DeviceStateEx.Connected
75
+ or sensor.deviceState == DeviceStateEx.Ready
76
+ ):
64
77
  sensor._destroy()
65
-
66
78
 
67
79
  def _match_device(self, _device: bleak.BLEDevice, adv: AdvertisementData):
68
80
  if _device.name == None:
69
81
  return False
70
82
 
71
- if (
72
- SERVICE_GUID in adv.service_uuids
73
- ):
83
+ if SERVICE_GUID in adv.service_uuids:
74
84
  print("Device found: {0}, RSSI: {1}".format(_device.name, adv.rssi))
75
85
  return True
76
86
 
@@ -113,7 +123,9 @@ class SensorController:
113
123
  return self._device_callback != None
114
124
 
115
125
  @hasDeviceFoundCallback.setter
116
- def onDeviceFoundCallback(self, callback: Callable[[List[sensor_profile.BLEDevice]], None]):
126
+ def onDeviceFoundCallback(
127
+ self, callback: Callable[[List[sensor_profile.BLEDevice]], None]
128
+ ):
117
129
  """
118
130
  设置扫描设备回调。
119
131
 
@@ -121,10 +133,12 @@ class SensorController:
121
133
  """
122
134
  self._device_callback = callback
123
135
 
124
- async def _device_scan_callback(self, found_devices: Dict[str, Tuple[bleak.BLEDevice, AdvertisementData]]):
136
+ async def _device_scan_callback(
137
+ self, found_devices: Dict[str, Tuple[bleak.BLEDevice, AdvertisementData]]
138
+ ):
125
139
  if self._device_callback:
126
- devices:List[sensor_profile.BLEDevice] = list()
127
- deviceMap :Dict[str, SensorProfile] = self._sensor_profiles.copy()
140
+ devices: List[sensor_profile.BLEDevice] = list()
141
+ deviceMap: Dict[str, SensorProfile] = self._sensor_profiles.copy()
128
142
  for mac in found_devices:
129
143
  device = found_devices[mac][0]
130
144
  adv = found_devices[mac][1]
@@ -132,7 +146,7 @@ class SensorController:
132
146
  if deviceMap.get(mac) != None:
133
147
  devices.append(self._sensor_profiles[mac].BLEDevice)
134
148
  else:
135
- newSensor = SensorProfile(device, adv,self._gforce_event_loop)
149
+ newSensor = SensorProfile(device, adv, self._gforce_event_loop)
136
150
  deviceMap[mac] = newSensor
137
151
  devices.append(newSensor.BLEDevice)
138
152
  self._sensor_profiles = deviceMap
@@ -142,8 +156,11 @@ class SensorController:
142
156
  timer(self._gforce_event_loop, 0, self._startScan())
143
157
 
144
158
  async def _startScan(self) -> bool:
145
- devices = await self._scanner.discover(timeout=self._device_callback_period / 1000, return_adv=True)
159
+ devices = await self._scanner.discover(
160
+ timeout=self._device_callback_period / 1000, return_adv=True
161
+ )
146
162
  timer(self._event_loop, 0, self._device_scan_callback(devices))
163
+
147
164
  def startScan(self, periodInMs: int) -> bool:
148
165
  """
149
166
  开始扫描。
@@ -152,12 +169,12 @@ class SensorController:
152
169
 
153
170
  :return: bool: 扫描是否成功启动
154
171
  """
155
- if (self._is_scanning):
172
+ if self._is_scanning:
156
173
  return True
157
-
174
+
158
175
  self._is_scanning = True
159
176
  self._device_callback_period = periodInMs
160
-
177
+
161
178
  timer(self._gforce_event_loop, 0, self._startScan())
162
179
  return True
163
180
 
@@ -165,12 +182,14 @@ class SensorController:
165
182
  """
166
183
  停止扫描。
167
184
  """
168
- if (not self._is_scanning):
169
- return
170
-
185
+ if not self._is_scanning:
186
+ return
187
+
171
188
  self._is_scanning = False
172
189
 
173
- def requireSensor(self, device: sensor_profile.BLEDevice) -> Optional[SensorProfile]:
190
+ def requireSensor(
191
+ self, device: sensor_profile.BLEDevice
192
+ ) -> Optional[SensorProfile]:
174
193
  """
175
194
  根据设备信息获取或创建SensorProfile。
176
195
 
@@ -200,9 +219,12 @@ class SensorController:
200
219
 
201
220
  :return: List[SensorProfile]: 已连接的SensorProfile列表
202
221
  """
203
- sensors:List[SensorProfile] = list()
222
+ sensors: List[SensorProfile] = list()
204
223
  for sensor in self._sensor_profiles.values():
205
- if sensor.deviceState == DeviceStateEx.Connected or sensor.deviceState == DeviceStateEx.Ready:
224
+ if (
225
+ sensor.deviceState == DeviceStateEx.Connected
226
+ or sensor.deviceState == DeviceStateEx.Ready
227
+ ):
206
228
  sensors.append(sensor)
207
229
 
208
230
  return sensors
@@ -213,11 +235,15 @@ class SensorController:
213
235
 
214
236
  :return: List[BLEDevice]: 已连接的蓝牙设备列表
215
237
  """
216
- devices:List[sensor_profile.BLEDevice] = list()
238
+ devices: List[sensor_profile.BLEDevice] = list()
217
239
  for sensor in self._sensor_profiles.values():
218
- if sensor.deviceState == DeviceStateEx.Connected or sensor.deviceState == DeviceStateEx.Ready:
240
+ if (
241
+ sensor.deviceState == DeviceStateEx.Connected
242
+ or sensor.deviceState == DeviceStateEx.Ready
243
+ ):
219
244
  devices.append(sensor.BLEDevice)
220
245
 
221
246
  return devices
222
247
 
248
+
223
249
  SensorControllerInstance = SensorController()
@@ -0,0 +1,94 @@
1
+ from enum import Enum, IntEnum
2
+ from typing import Dict, List
3
+
4
+
5
+ # 一个采样数据
6
+ # 该类用于存储单个采样数据的相关信息,包括数据值、阻抗、饱和度、采样索引和是否丢包的标志
7
+ class Sample:
8
+ # """
9
+ # Initialize a Sample instance.
10
+
11
+ # :param data: 数据值,单位为 uV
12
+ # :param impedance: 阻抗值,单位为 Ω
13
+ # :param saturation: 饱和度值,单位为 % ,值 0-100
14
+ # :param sample_index: 采样索引,用于标识采样的顺序
15
+ # :param is_lost: 是否丢包的标志,True 表示丢包,False 表示正常
16
+ # """
17
+ # def __init__(self, data: int, impedance: int, saturation: int, sample_index: int, is_lost: bool):
18
+ # self.data = data
19
+ # self.impedance = impedance
20
+ # self.saturation = saturation
21
+ # self.sampleIndex = sample_index
22
+ # self.isLost = is_lost
23
+
24
+ def __init__(self):
25
+ self.rawData = 0
26
+ self.data = 0
27
+ self.impedance = 0
28
+ self.saturation = 0
29
+ self.sampleIndex = 0
30
+ self.isLost = False
31
+ self.timeStampInMs = 0
32
+ self.channelIndex = 0
33
+ self.sampleIndex = 0
34
+
35
+
36
+ # 对应 DataType 枚举
37
+ # 该枚举类定义了不同类型的数据,用于区分传感器采集的不同类型的数据
38
+ class DataType(IntEnum):
39
+ NTF_ACC = 0x1 # 加速度,用于标识加速度传感器采集的数据
40
+ NTF_GYRO = 0x2 # 陀螺仪,用于标识陀螺仪传感器采集的数据
41
+ NTF_EEG = 0x10 # EEG,用于标识脑电传感器采集的数据
42
+ NTF_ECG = 0x11 # ECG,用于标识心电传感器采集的数据
43
+ NTF_IMPEDANCE = (0x12,) # 阻抗数据
44
+ NTF_IMU = (0x13,) # 包含ACC和GYRO数据
45
+ NTF_ADS = (0x14,) # 无单位ads数据
46
+ NTF_BRTH = 0x15 # 呼吸,用于标识呼吸传感器采集的数据
47
+
48
+
49
+ # 一次采样的数据,包含多个通道的数据,channal_samples 为一个二维数组, 第一个维度为通道索引,第二个维度为采样索引
50
+ # 该类用于存储一次采样的完整数据,包括设备 MAC 地址、数据类型、采样率、通道数量、包中采样数量以及通道采样数据
51
+ class SensorData:
52
+ # """
53
+ # Initialize a SensorData instance.
54
+
55
+ # :param device_mac: The MAC address of the device.
56
+ # :param data_type: The type of data being collected.
57
+ # :param sample_rate: The rate at which samples are collected.
58
+ # :param channel_count: The number of channels in the data.
59
+ # :param package_sample_count: The number of samples in the package.
60
+ # :param channel_samples: A list of lists containing the sample data for each channel.
61
+ # """
62
+ # def __init__(self, device_mac: str, data_type: DataType, sample_rate: int, channel_count: int,
63
+ # package_sample_count: int, channel_samples: List[List[Sample]]):
64
+ # self.deviceMac = device_mac
65
+ # self.dataType = data_type
66
+ # self.sampleRate = sample_rate
67
+ # self.channelCount = channel_count
68
+ # self.packageSampleCount = package_sample_count
69
+ # self.channelSamples = channel_samples
70
+ # self.lastPackageCounter = 0
71
+ # self.lastPackageIndex = 0
72
+ # self.resolutionBits = 0
73
+ # self.channelMask = 0
74
+ # self.minPackageSampleCount = 0
75
+ # self.K = 0
76
+
77
+ def __init__(self):
78
+ self.deviceMac = ""
79
+ self.dataType = DataType.NTF_EEG
80
+ self.sampleRate = 0
81
+ self.channelCount = 0
82
+ self.packageSampleCount = 0
83
+ self.channelSamples: List[List[Sample]] = list()
84
+ self.lastPackageCounter = 0
85
+ self.lastPackageIndex = 0
86
+ self.resolutionBits = 0
87
+ self.channelMask = 0
88
+ self.minPackageSampleCount = 0
89
+ self.K = 0
90
+
91
+ def clear(self):
92
+ self.channelSamples.clear()
93
+ self.lastPackageCounter = 0
94
+ self.lastPackageIndex = 0