sensor-sdk 0.0.1__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.

@@ -0,0 +1,569 @@
1
+ import asyncio
2
+ from collections import deque
3
+ from queue import Queue
4
+ from typing import Deque, List
5
+
6
+ from sensor.gforce import DataSubscription, GForce
7
+ from sensor.sensor_data import DataType, Sample, SensorData
8
+
9
+ from enum import Enum, IntEnum
10
+
11
+ from sensor.sensor_device import DeviceInfo
12
+ from sensor.utils import timer
13
+
14
+ class SensorDataType(IntEnum):
15
+ DATA_TYPE_EEG = 0
16
+ DATA_TYPE_ECG = 1
17
+ DATA_TYPE_ACC = 2
18
+ DATA_TYPE_GYRO = 3
19
+ DATA_TYPE_BRTH = 4
20
+ DATA_TYPE_COUNT = 5
21
+
22
+ # 枚举 FeatureMaps 的 Python 实现
23
+ class FeatureMaps(Enum):
24
+ GFD_FEAT_EMG = 0x000002000
25
+ GFD_FEAT_EEG = 0x000400000
26
+ GFD_FEAT_ECG = 0x000800000
27
+ GFD_FEAT_IMPEDANCE = 0x001000000
28
+ GFD_FEAT_IMU = 0x002000000
29
+ GFD_FEAT_ADS = 0x004000000
30
+ GFD_FEAT_BRTH = 0x008000000
31
+ GFD_FEAT_CONCAT_BLE = 0x80000000
32
+
33
+
34
+ class SensorProfileDataCtx:
35
+ def __init__(self, gForce: GForce, deviceMac: str, buf: Queue[bytes]):
36
+ self.featureMap = 0
37
+ self.notifyDataFlag:DataSubscription = 0
38
+
39
+ self.gForce = gForce
40
+ self.deviceMac = deviceMac
41
+ self._device_info: DeviceInfo = None
42
+
43
+ self._is_running = True
44
+ self._is_data_transfering = False
45
+ self.isUniversalStream: bool = gForce._is_universal_stream
46
+ self._rawDataBuffer: Queue[bytes] = buf
47
+ self._concatDataBuffer = bytearray()
48
+
49
+ self.sensorDatas: List[SensorData] = list()
50
+ for idx in range(0, SensorDataType.DATA_TYPE_COUNT):
51
+ self.sensorDatas.append(SensorData())
52
+ self.impedanceData: List[float] = list()
53
+ self.saturationData: List[float] = list()
54
+
55
+ def close(self):
56
+ self._is_running = False
57
+
58
+ def clear(self):
59
+ for sensorData in self.sensorDatas:
60
+ sensorData.clear()
61
+ self.impedanceData.clear()
62
+ self.saturationData.clear()
63
+ self._concatDataBuffer.clear()
64
+ self._rawDataBuffer.queue.clear()
65
+
66
+ def reset(self):
67
+ self.notifyDataFlag = 0
68
+ self.clear()
69
+
70
+ @property
71
+ def isDataTransfering(self) -> bool:
72
+ """
73
+ 检查传感器是否正在进行数据传输。
74
+
75
+ :return: bool: 如果传感器正在进行数据传输,返回 True;否则返回 False。
76
+ """
77
+ return self._is_data_transfering
78
+ def hasInit(self):
79
+ return self.featureMap != 0 and self.notifyDataFlag != 0
80
+ def hasEMG(self):
81
+ return (self.featureMap & FeatureMaps.GFD_FEAT_EMG.value) != 0
82
+
83
+ def hasEEG(self):
84
+ return (self.featureMap & FeatureMaps.GFD_FEAT_EEG.value) != 0
85
+
86
+ def hasECG(self):
87
+ return (self.featureMap & FeatureMaps.GFD_FEAT_ECG.value) != 0
88
+
89
+ def hasImpedance(self):
90
+ return (self.featureMap & FeatureMaps.GFD_FEAT_IMPEDANCE.value) != 0
91
+
92
+ def hasIMU(self):
93
+ return (self.featureMap & FeatureMaps.GFD_FEAT_IMU.value) != 0
94
+
95
+ def hasBrth(self):
96
+ return (self.featureMap & FeatureMaps.GFD_FEAT_BRTH.value) != 0
97
+
98
+ def hasConcatBLE(self):
99
+ return (self.featureMap & FeatureMaps.GFD_FEAT_CONCAT_BLE.value) != 0
100
+
101
+ async def initEEG(self,packageCount:int)-> int:
102
+ config = await self.gForce.get_eeg_raw_data_config()
103
+ cap = await self.gForce.get_eeg_raw_data_cap()
104
+ data = SensorData()
105
+ data.deviceMac = self.deviceMac
106
+ data.dataType = DataType.NTF_EEG
107
+ data.sampleRate = config.fs
108
+ data.resolutionBits = config.resolution
109
+ data.channelCount = cap.channel_count
110
+ data.channelMask = config.channel_mask
111
+ data.minPackageSampleCount = packageCount
112
+ data.packageSampleCount = config.batch_len
113
+ data.K = config.K
114
+ data.clear()
115
+ self.sensorDatas[SensorDataType.DATA_TYPE_EEG] = data
116
+ self.notifyDataFlag |= DataSubscription.DNF_EEG
117
+ return data.channelCount
118
+
119
+ async def initECG(self,packageCount:int)-> int:
120
+ config = await self.gForce.get_ecg_raw_data_config()
121
+ data = SensorData()
122
+ data.deviceMac = self.deviceMac
123
+ data.dataType = DataType.NTF_ECG
124
+ data.sampleRate = config.fs
125
+ data.resolutionBits = config.resolution
126
+ data.channelCount = 1
127
+ data.channelMask = config.channel_mask
128
+ data.minPackageSampleCount = packageCount
129
+ data.packageSampleCount = config.batch_len
130
+ data.K = config.K
131
+ data.clear()
132
+ self.sensorDatas[SensorDataType.DATA_TYPE_ECG] = data
133
+ self.notifyDataFlag |= DataSubscription.DNF_ECG
134
+ return data.channelCount
135
+
136
+ async def initIMU(self,packageCount:int)-> int:
137
+ config = await self.gForce.get_imu_raw_data_config()
138
+ data = SensorData()
139
+ data.deviceMac = self.deviceMac
140
+ data.dataType = DataType.NTF_ACC
141
+ data.sampleRate = config.fs
142
+ data.resolutionBits = 16
143
+ data.channelCount = config.channel_count
144
+ data.channelMask = 255
145
+ data.minPackageSampleCount = packageCount
146
+ data.packageSampleCount = config.batch_len
147
+ data.K = config.accK
148
+ data.clear()
149
+ self.sensorDatas[SensorDataType.DATA_TYPE_ACC] = data
150
+
151
+ data = SensorData()
152
+ data.deviceMac = self.deviceMac
153
+ data.dataType = DataType.NTF_GYRO
154
+ data.sampleRate = config.fs
155
+ data.resolutionBits = 16
156
+ data.channelCount = config.channel_count
157
+ data.channelMask = 255
158
+ data.minPackageSampleCount = packageCount
159
+ data.packageSampleCount = config.batch_len
160
+ data.K = config.gyroK
161
+ data.clear()
162
+ self.sensorDatas[SensorDataType.DATA_TYPE_GYRO] = data
163
+
164
+ self.notifyDataFlag |= DataSubscription.DNF_IMU
165
+
166
+ return data.channelCount
167
+
168
+ async def initBrth(self,packageCount:int)-> int:
169
+ config = await self.gForce.get_brth_raw_data_config()
170
+ data = SensorData()
171
+ data.deviceMac = self.deviceMac
172
+ data.dataType = DataType.NTF_BRTH
173
+ data.sampleRate = config.fs
174
+ data.resolutionBits = config.resolution
175
+ data.channelCount = 1
176
+ data.channelMask = config.channel_mask
177
+ data.minPackageSampleCount = packageCount
178
+ data.packageSampleCount = config.batch_len
179
+ data.K = config.K
180
+ data.clear()
181
+ self.sensorDatas[SensorDataType.DATA_TYPE_BRTH] = data
182
+ self.notifyDataFlag |= DataSubscription.DNF_ECG
183
+ return data.channelCount
184
+
185
+ async def initDataTransfer(self,isGetFeature:bool)-> int:
186
+ if (isGetFeature):
187
+ self.featureMap = await self.gForce.get_feature_map()
188
+ if (self.hasImpedance()):
189
+ self.notifyDataFlag |= DataSubscription.DNF_IMPEDANCE
190
+ return self.featureMap
191
+ else:
192
+ await self.gForce.set_subscription(self.notifyDataFlag)
193
+ return self.notifyDataFlag
194
+
195
+ async def fetchDeviceInfo(self)->DeviceInfo:
196
+ info = DeviceInfo()
197
+ info.MTUSize = self.gForce.client.mtu_size
198
+ info.DeviceName = await self.gForce.get_device_name()
199
+ info.ModelName = await self.gForce.get_model_number()
200
+ info.HardwareVersion = await self.gForce.get_hardware_revision()
201
+ info.FirmwareVersion = await self.gForce.get_firmware_revision()
202
+ return info
203
+
204
+ async def init(self, packageCount:int)->bool:
205
+ try:
206
+ info = await self.fetchDeviceInfo()
207
+
208
+ await self.initDataTransfer(True)
209
+
210
+ if (self.hasImpedance()):
211
+ self.notifyDataFlag |= DataSubscription.DNF_IMPEDANCE
212
+
213
+ if (self.hasEEG()):
214
+ info.EegChannelCount = await self.initEEG(packageCount)
215
+
216
+ if (self.hasECG()):
217
+ info.EcgChannelCount = await self.initECG(packageCount)
218
+
219
+ if (self.hasBrth()):
220
+ info.BrthChannelCount = await self.initBrth(packageCount)
221
+
222
+ if (self.hasIMU()):
223
+ imuChannelCount = await self.initIMU(packageCount)
224
+ info.AccChannelCount = imuChannelCount
225
+ info.GyroChannelCount = imuChannelCount
226
+
227
+ self._device_info = info
228
+
229
+ if (not self.isUniversalStream):
230
+ await self.initDataTransfer(False)
231
+
232
+ return True
233
+ except Exception as e:
234
+ print(e)
235
+ return False
236
+
237
+ async def start_streaming(self) -> bool:
238
+ if (self._is_data_transfering):
239
+ return True
240
+ self._is_data_transfering = True
241
+ self._rawDataBuffer.queue.clear()
242
+ if (not self.isUniversalStream):
243
+ await self.gForce.start_streaming(self._rawDataBuffer)
244
+ return True
245
+ else:
246
+ await self.gForce.set_subscription(self.notifyDataFlag)
247
+ return True
248
+
249
+ async def stop_streaming(self) -> bool:
250
+ if (not self._is_data_transfering):
251
+ return True
252
+
253
+ self._is_data_transfering = False
254
+
255
+ if (not self.isUniversalStream):
256
+ await self.gForce.stop_streaming()
257
+ return True
258
+ else:
259
+ await self.gForce.set_subscription(0)
260
+ return True
261
+
262
+ def process_data(self, buf: Queue[SensorData]):
263
+ try:
264
+ data: bytes = self._rawDataBuffer.get_nowait()
265
+ except Exception as e:
266
+ return
267
+
268
+ self._processDataPackage(data, buf)
269
+ self._rawDataBuffer.task_done()
270
+
271
+ def _processDataPackage(self,data: bytes, buf: Queue[SensorData]):
272
+ v = data[0]
273
+ if v == DataType.NTF_IMPEDANCE:
274
+ offset = 1
275
+ # packageIndex = ((data[offset + 1] & 0xff) << 8) | (data[offset] & 0xff)
276
+ offset += 2
277
+
278
+ impedanceData = []
279
+ saturationData = []
280
+
281
+ dataCount = (len(data) - 3) // 4 // 2
282
+ for index in range(dataCount):
283
+ impedance_bytes = data[offset:offset + 4]
284
+ impedance = int.from_bytes(impedance_bytes, byteorder='little')
285
+ offset += 4
286
+ impedanceData.append(impedance)
287
+
288
+ for index in range(dataCount):
289
+ saturation_bytes = data[offset:offset + 4]
290
+ saturation = int.from_bytes(saturation_bytes, byteorder='little')
291
+ offset += 4
292
+ saturationData.append(saturation / 10) # firmware value range 0 - 1000
293
+
294
+ self.impedanceData = impedanceData
295
+ self.saturationData = saturationData
296
+
297
+ elif v == DataType.NTF_EEG:
298
+ sensor_data = self.sensorDatas[SensorDataType.DATA_TYPE_EEG]
299
+ if self.checkReadSamples(data, sensor_data, 3, 0):
300
+ self.sendSensorData(sensor_data, buf)
301
+ elif v == DataType.NTF_ECG:
302
+ sensor_data = self.sensorDatas[SensorDataType.DATA_TYPE_ECG]
303
+ if self.checkReadSamples(data, sensor_data, 3, 0):
304
+ self.sendSensorData(sensor_data, buf)
305
+ elif v == DataType.NTF_BRTH:
306
+ sensor_data = self.sensorDatas[SensorDataType.DATA_TYPE_BRTH]
307
+ if self.checkReadSamples(data, sensor_data, 3, 0):
308
+ self.sendSensorData(sensor_data, buf)
309
+ elif v == DataType.NTF_IMU:
310
+ sensor_data_acc = self.sensorDatas[SensorDataType.DATA_TYPE_ACC]
311
+ if self.checkReadSamples(data, sensor_data_acc, 3, 6):
312
+ self.sendSensorData(sensor_data_acc, buf)
313
+
314
+ sensor_data_gyro = self.sensorDatas[SensorDataType.DATA_TYPE_GYRO]
315
+ if self.checkReadSamples(data, sensor_data_gyro, 9, 6):
316
+ self.sendSensorData(sensor_data_gyro, buf)
317
+
318
+ def checkReadSamples(self, data: bytes, sensorData: SensorData, dataOffset: int, dataGap: int):
319
+ offset = 1
320
+ v = data[0]
321
+ if not self._is_data_transfering:
322
+ return False
323
+ try:
324
+
325
+ packageIndex = ((data[offset + 1] & 0xff) << 8) | (data[offset] & 0xff)
326
+ offset += 2
327
+ newPackageIndex = packageIndex
328
+ lastPackageIndex = sensorData.lastPackageIndex
329
+
330
+ if packageIndex < lastPackageIndex:
331
+ packageIndex += 65536 # 包索引是 U16 类型
332
+ elif packageIndex == lastPackageIndex:
333
+ return False
334
+
335
+ deltaPackageIndex = packageIndex - lastPackageIndex
336
+ if deltaPackageIndex > 1:
337
+ lostSampleCount = sensorData.packageSampleCount * (deltaPackageIndex - 1)
338
+ print(f"lost dataType {sensorData.dataType} -> data {sensorData.deviceMac} {lostSampleCount}")
339
+ self.readSamples(data, sensorData, 0, dataGap, lostSampleCount)
340
+ if newPackageIndex == 0:
341
+ sensorData.lastPackageIndex = 65535
342
+ else:
343
+ sensorData.lastPackageIndex = newPackageIndex - 1
344
+ sensorData.lastPackageCounter += (deltaPackageIndex - 1)
345
+
346
+ self.readSamples(data, sensorData, dataOffset, dataGap, 0)
347
+ sensorData.lastPackageIndex = newPackageIndex
348
+ sensorData.lastPackageCounter += 1
349
+ except Exception:
350
+ return False
351
+ return True
352
+
353
+ def readSamples(self, data: bytes, sensorData: SensorData, offset: int, dataGap: int, lostSampleCount: int):
354
+ sampleCount = sensorData.packageSampleCount
355
+ sampleInterval = 1000 // sensorData.sampleRate
356
+ if lostSampleCount > 0:
357
+ sampleCount = lostSampleCount
358
+
359
+ K = sensorData.K
360
+ lastSampleIndex = sensorData.lastPackageCounter * sensorData.packageSampleCount
361
+
362
+ _impedanceData = self.impedanceData.copy()
363
+ _saturationData = self.saturationData.copy()
364
+
365
+ channelSamples = sensorData.channelSamples
366
+ if not channelSamples:
367
+ for channelIndex in range(sensorData.channelCount):
368
+ channelSamples.append([])
369
+
370
+ for sampleIndex in range(sampleCount):
371
+ for channelIndex, impedanceChannelIndex in enumerate(range(sensorData.channelCount)):
372
+ if (sensorData.channelMask & (1 << channelIndex)) != 0:
373
+ samples = channelSamples[channelIndex]
374
+ impedance = 0.0
375
+ saturation = 0.0
376
+ if sensorData.dataType == DataType.NTF_ECG:
377
+ impedanceChannelIndex = self.sensorDatas[SensorDataType.DATA_TYPE_EEG].channelCount
378
+ impedance = _impedanceData[impedanceChannelIndex]
379
+ saturation = _saturationData[impedanceChannelIndex]
380
+ impedanceChannelIndex += 1
381
+
382
+ dataItem = Sample()
383
+ dataItem.channelIndex = channelIndex
384
+ dataItem.sampleIndex = lastSampleIndex
385
+ dataItem.timeStampInMs = lastSampleIndex * sampleInterval
386
+ if lostSampleCount > 0:
387
+ dataItem.rawData = 0
388
+ dataItem.data = 0.0
389
+ dataItem.impedance = impedance
390
+ dataItem.saturation = saturation
391
+ dataItem.isLost = True
392
+ else:
393
+ rawData = 0
394
+ if sensorData.resolutionBits == 8:
395
+ rawData = data[offset]
396
+ rawData -= 128
397
+ offset += 1
398
+ elif sensorData.resolutionBits == 16:
399
+ rawData = int.from_bytes(data[offset:offset + 2], byteorder='little', signed=True)
400
+ offset += 2
401
+ elif sensorData.resolutionBits == 24:
402
+ rawData = (data[offset] << 16) | (data[offset + 1] << 8) | data[offset + 2]
403
+ rawData -= 8388608
404
+ offset += 3
405
+
406
+ converted = rawData * K
407
+ dataItem.rawData = rawData
408
+ dataItem.data = converted
409
+ dataItem.impedance = impedance
410
+ dataItem.saturation = saturation
411
+ dataItem.isLost = False
412
+
413
+ samples.append(dataItem)
414
+
415
+ lastSampleIndex += 1
416
+ offset += dataGap
417
+
418
+ def sendSensorData(self, sensorData: SensorData, buf: Queue[SensorData]):
419
+ oldChannelSamples = sensorData.channelSamples
420
+
421
+ if not self.isDataTransfering or len(oldChannelSamples) == 0:
422
+ return
423
+
424
+ realSampleCount = 0
425
+ if len(oldChannelSamples) > 0:
426
+ realSampleCount = len(oldChannelSamples[0])
427
+
428
+ if realSampleCount < sensorData.minPackageSampleCount:
429
+ return
430
+
431
+ sensorData.channelSamples = []
432
+ batchCount = realSampleCount // sensorData.minPackageSampleCount
433
+ # leftSampleSize = realSampleCount - sensorData.minPackageSampleCount * batchCount
434
+
435
+ sensorDataList = []
436
+ startIndex = 0
437
+ for batchIndex in range(batchCount):
438
+ resultChannelSamples = []
439
+ for channelIndex in range(sensorData.channelCount):
440
+ oldSamples = oldChannelSamples[channelIndex]
441
+ newSamples = []
442
+ for sampleIndex in range(sensorData.minPackageSampleCount):
443
+ newSamples.append(oldSamples[startIndex + sampleIndex])
444
+ resultChannelSamples.append(newSamples)
445
+
446
+ sensorDataResult = SensorData()
447
+ sensorDataResult.channelSamples = resultChannelSamples
448
+ sensorDataResult.dataType = sensorData.dataType
449
+ sensorDataResult.deviceMac = sensorData.deviceMac
450
+ sensorDataResult.sampleRate = sensorData.sampleRate
451
+ sensorDataResult.channelCount = sensorData.channelCount
452
+ sensorDataResult.minPackageSampleCount = sensorData.minPackageSampleCount
453
+ sensorDataList.append(sensorDataResult)
454
+
455
+ startIndex += sensorData.minPackageSampleCount
456
+
457
+ leftChannelSamples = []
458
+ for channelIndex in range(sensorData.channelCount):
459
+ oldSamples = oldChannelSamples[channelIndex]
460
+ newSamples = []
461
+ for sampleIndex in range(startIndex, len(oldSamples)):
462
+ newSamples.append(oldSamples[sampleIndex])
463
+
464
+ leftChannelSamples.append(newSamples)
465
+
466
+ sensorData.channelSamples = leftChannelSamples
467
+
468
+ for sensorDataResult in sensorDataList:
469
+ buf.put(sensorDataResult)
470
+
471
+
472
+ def calc_crc8(self,data):
473
+ crc8Table = [
474
+ 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
475
+ 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
476
+ 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
477
+ 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
478
+ 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
479
+ 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
480
+ 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
481
+ 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
482
+ 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
483
+ 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
484
+ 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
485
+ 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
486
+ 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
487
+ 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
488
+ 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
489
+ 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
490
+ ]
491
+ crc8 = 0
492
+ len_data = len(data)
493
+
494
+ for i in range(len_data):
495
+ crc8 ^= data[i]
496
+ crc8 = crc8Table[crc8]
497
+
498
+ return crc8
499
+ def processUniversalData(self, buf: Queue[SensorData],loop:asyncio.AbstractEventLoop, sensor, callback):
500
+
501
+ while(self._is_running):
502
+ try:
503
+ while(self._is_running and not self._rawDataBuffer.empty()):
504
+ data = self._rawDataBuffer.get_nowait()
505
+ self._concatDataBuffer.extend(data)
506
+ self._rawDataBuffer.task_done()
507
+ except Exception as e:
508
+ pass
509
+
510
+ index = 0
511
+ last_cut = -1
512
+
513
+ while self._is_running:
514
+ data_size = len(self._concatDataBuffer)
515
+ if index >= data_size:
516
+ break
517
+
518
+ if self._concatDataBuffer[index] == 0x55:
519
+ if (index + 1) >= data_size:
520
+ index += 1
521
+ continue
522
+ n = self._concatDataBuffer[index + 1]
523
+ if (index + 1 + n + 1) >= data_size:
524
+ index += 1
525
+ continue
526
+ crc = self._concatDataBuffer[index + 1 + n + 1]
527
+ calc_crc = self.calc_crc8(self._concatDataBuffer[index + 2:index + 2 + n])
528
+ if crc != calc_crc:
529
+ index += 1
530
+ continue
531
+ if self._is_data_transfering:
532
+ data_package = bytes(self._concatDataBuffer[index + 2:index + 2 + n])
533
+ self._processDataPackage(data_package, buf)
534
+ while(self._is_running and self.isDataTransfering):
535
+ sensorData: SensorData = None
536
+ try:
537
+ sensorData = buf.get_nowait()
538
+ except Exception as e:
539
+ break
540
+ if (sensorData != None and callback != None):
541
+ try:
542
+ loop.call_soon_threadsafe(callback,sensor, sensorData)
543
+ except Exception as e:
544
+ print(e)
545
+
546
+ buf.task_done()
547
+
548
+ last_cut = index = index + 2 + n
549
+ elif self._concatDataBuffer[index] == 0xAA:
550
+ if (index + 1) >= data_size:
551
+ index += 1
552
+ continue
553
+ n = self._concatDataBuffer[index + 1]
554
+ if (index + 1 + n + 1) >= data_size:
555
+ index += 1
556
+ continue
557
+ crc = self._concatDataBuffer[index + 1 + n + 1]
558
+ calc_crc = self.calc_crc8(self._concatDataBuffer[index + 2:index + 2 + n])
559
+ if crc != calc_crc:
560
+ index += 1
561
+ continue
562
+ data_package = bytes(self._concatDataBuffer[index + 2:index + 2 + n])
563
+ loop.call_soon_threadsafe(self.gForce._on_cmd_response,None, data_package)
564
+ last_cut = index = index + 2 + n
565
+ else:
566
+ index += 1
567
+
568
+ if last_cut > 0:
569
+ self._concatDataBuffer = self._concatDataBuffer[last_cut + 1:]
@@ -0,0 +1,75 @@
1
+
2
+
3
+ # 详细设备信息
4
+ # 该类用于存储设备的详细信息,包括设备名称、型号、硬件和固件版本、各种通道数量以及 MTU 大小
5
+ from enum import Enum
6
+
7
+
8
+ class DeviceInfo:
9
+ # """
10
+ # Initialize a DeviceInfo instance.
11
+
12
+ # :param device_name (str): 设备名称
13
+ # :param model_name (str): 设备型号
14
+ # :param hardware_version (str): 设备硬件版本
15
+ # :param firmware_version (str): 设备固件版本
16
+ # :param emg_channel_count (int): EMG 通道数量
17
+ # :param eeg_channel_count (int): EEG 通道数量
18
+ # :param ecg_channel_count (int): ECG 通道数量
19
+ # :param acc_channel_count (int): 加速度通道数量
20
+ # :param gyro_channel_count (int): 陀螺仪通道数量
21
+ # :param brth_channel_count (int): 呼吸通道数量
22
+ # :param mtu_size (int): MTU 大小
23
+ # """
24
+ # def __init__(self, device_name: str, model_name: str, hardware_version: str, firmware_version: str,
25
+ # emg_channel_count: int, eeg_channel_count: int, ecg_channel_count: int,
26
+ # acc_channel_count: int, gyro_channel_count: int, brth_channel_count: int, mtu_size: int):
27
+ # self.DeviceName = device_name
28
+ # self.ModelName = model_name
29
+ # self.HardwareVersion = hardware_version
30
+ # self.FirmwareVersion = firmware_version
31
+ # self.EmgChannelCount = emg_channel_count
32
+ # self.EegChannelCount = eeg_channel_count
33
+ # self.EcgChannelCount = ecg_channel_count
34
+ # self.AccChannelCount = acc_channel_count
35
+ # self.GyroChannelCount = gyro_channel_count
36
+ # self.BrthChannelCount = brth_channel_count
37
+ # self.MTUSize = mtu_size
38
+
39
+ def __init__(self):
40
+ self.DeviceName = ""
41
+ self.ModelName = ""
42
+ self.HardwareVersion = ""
43
+ self.FirmwareVersion = ""
44
+ self.EmgChannelCount = 0
45
+ self.EegChannelCount = 0
46
+ self.EcgChannelCount = 0
47
+ self.AccChannelCount = 0
48
+ self.GyroChannelCount = 0
49
+ self.BrthChannelCount = 0
50
+ self.MTUSize = 0
51
+
52
+ class DeviceStateEx(Enum):
53
+ Disconnected = 0
54
+ Connecting = 1
55
+ Connected = 2
56
+ Ready = 3
57
+ Disconnecting = 4
58
+ Invalid = 5
59
+
60
+
61
+ # 蓝牙设备信息
62
+ # 该类用于存储蓝牙设备的基本信息,包括设备名称、地址和信号强度
63
+ class BLEDevice:
64
+ """
65
+ Initialize a BLEDevice instance.
66
+ :param name (str): 设备名称
67
+ :param address (str): 设备地址
68
+ :param rssi (int): 信号强度
69
+ """
70
+ def __init__(self, name: str, address: str, rssi: int):
71
+
72
+ # 初始化函数,用于创建一个Beacon对象
73
+ self.Name = name # 设置Beacon的名称
74
+ self.Address = address # 设置Beacon的地址
75
+ self.RSSI = rssi