sensor-sdk 0.0.65__tar.gz → 0.0.67__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.
- {sensor_sdk-0.0.65/sensor_sdk.egg-info → sensor_sdk-0.0.67}/PKG-INFO +1 -1
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_data.py +1 -1
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_data_context.py +205 -507
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67/sensor_sdk.egg-info}/PKG-INFO +1 -1
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/setup.py +1 -1
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/LICENSE.txt +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/README.md +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/__init__.py +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/bleak_host.py +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/bleak_process.py +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/gforce.py +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_controller.py +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_device.py +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_profile.py +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_utils.py +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor_sdk.egg-info/SOURCES.txt +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor_sdk.egg-info/dependency_links.txt +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor_sdk.egg-info/requires.txt +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor_sdk.egg-info/top_level.txt +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor_sdk.egg-info/zip-safe +0 -0
- {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/setup.cfg +0 -0
|
@@ -30,7 +30,6 @@ class Sample:
|
|
|
30
30
|
self.isLost = False
|
|
31
31
|
self.timeStampInMs = 0
|
|
32
32
|
self.channelIndex = 0
|
|
33
|
-
self.sampleIndex = 0
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
|
|
@@ -94,6 +93,7 @@ class SensorData:
|
|
|
94
93
|
self.lastPackageIndex = 0
|
|
95
94
|
self.lostPackageCount = 0
|
|
96
95
|
self.resolutionBits = 0
|
|
96
|
+
self.resolutionSigned = 0
|
|
97
97
|
self.channelMask = 0
|
|
98
98
|
self.minPackageSampleCount = 0
|
|
99
99
|
self.K = 0
|
|
@@ -16,6 +16,7 @@ from enum import Enum, IntEnum
|
|
|
16
16
|
|
|
17
17
|
from sensor.sensor_device import DeviceInfo
|
|
18
18
|
|
|
19
|
+
QUAT_SCALE = 1 / 1073741824.0 # 2^30
|
|
19
20
|
|
|
20
21
|
class SensorDataType(IntEnum):
|
|
21
22
|
DATA_TYPE_EEG = 0
|
|
@@ -76,12 +77,8 @@ class SensorProfileDataCtx:
|
|
|
76
77
|
|
|
77
78
|
# Quaternion support
|
|
78
79
|
self.isContainQAT6 = False
|
|
79
|
-
self.lastQuatData: List[float] = [0.0, 0.0, 0.0, 0.0]
|
|
80
|
-
self.lastEulerData: List[float] = [0.0, 0.0, 0.0]
|
|
81
|
-
self.lastAccData: List[float] = [0.0, 0.0, 0.0]
|
|
82
|
-
self.lastGyroData: List[float] = [0.0, 0.0, 0.0]
|
|
83
80
|
# PPG configuration
|
|
84
|
-
self.
|
|
81
|
+
self.ppgModel = PPGDataMode.PPG_AND_SPO2
|
|
85
82
|
|
|
86
83
|
self.sensorDatas: List[SensorData] = list()
|
|
87
84
|
for idx in range(0, SensorDataType.DATA_TYPE_COUNT):
|
|
@@ -177,6 +174,7 @@ class SensorProfileDataCtx:
|
|
|
177
174
|
data.dataType = DataType.NTF_EMG
|
|
178
175
|
data.sampleRate = 500
|
|
179
176
|
data.resolutionBits = 0
|
|
177
|
+
data.resolutionSigned = 0
|
|
180
178
|
data.channelCount = 8
|
|
181
179
|
data.channelMask = config.channel_mask
|
|
182
180
|
data.minPackageSampleCount = packageCount
|
|
@@ -198,6 +196,7 @@ class SensorProfileDataCtx:
|
|
|
198
196
|
# new emg
|
|
199
197
|
data.packageIndexLength = 2
|
|
200
198
|
data.resolutionBits = 0
|
|
199
|
+
data.resolutionSigned = 1
|
|
201
200
|
gain = 6
|
|
202
201
|
data.K = 4000000.0 / 8388607.0 / gain
|
|
203
202
|
config.resolution = 8
|
|
@@ -205,6 +204,7 @@ class SensorProfileDataCtx:
|
|
|
205
204
|
# old emg
|
|
206
205
|
data.packageIndexLength = 1
|
|
207
206
|
data.resolutionBits = 7
|
|
207
|
+
data.resolutionSigned = 1
|
|
208
208
|
gain = 1200
|
|
209
209
|
min_voltage = -1.25 * 1000000
|
|
210
210
|
max_voltage = 1.25 * 100000
|
|
@@ -242,6 +242,7 @@ class SensorProfileDataCtx:
|
|
|
242
242
|
data.dataType = DataType.NTF_EEG
|
|
243
243
|
data.sampleRate = config.fs
|
|
244
244
|
data.resolutionBits = config.resolution
|
|
245
|
+
data.resolutionSigned = 1
|
|
245
246
|
data.channelCount = cap.channel_count
|
|
246
247
|
data.channelMask = config.channel_mask
|
|
247
248
|
data.minPackageSampleCount = packageCount
|
|
@@ -259,6 +260,7 @@ class SensorProfileDataCtx:
|
|
|
259
260
|
data.dataType = DataType.NTF_ECG
|
|
260
261
|
data.sampleRate = config.fs
|
|
261
262
|
data.resolutionBits = config.resolution
|
|
263
|
+
data.resolutionSigned = 1
|
|
262
264
|
data.channelCount = 1
|
|
263
265
|
data.channelMask = config.channel_mask
|
|
264
266
|
data.minPackageSampleCount = packageCount
|
|
@@ -270,63 +272,54 @@ class SensorProfileDataCtx:
|
|
|
270
272
|
return data.channelCount
|
|
271
273
|
async def initPPG(self, packageCount: int) -> int:
|
|
272
274
|
|
|
273
|
-
|
|
274
275
|
config = await self.gForce.get_ppg_raw_data_config()
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
config.fs =
|
|
278
|
-
|
|
279
|
-
|
|
276
|
+
config.mode = self.ppgModel
|
|
277
|
+
config.period = 1
|
|
278
|
+
config.fs = 50
|
|
279
|
+
await self.gForce.set_ppg_raw_data_config(config)
|
|
280
280
|
|
|
281
281
|
data = SensorData()
|
|
282
|
+
data.dataType = DataType.NTF_PPG
|
|
282
283
|
data.deviceMac = self.deviceMac
|
|
283
284
|
data.sampleRate = config.fs
|
|
284
285
|
data.channelMask = 255
|
|
285
286
|
data.minPackageSampleCount = packageCount
|
|
286
287
|
data.packageSampleCount = config.batch_len
|
|
287
288
|
data.K = 1.0
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
self.sensorDatas[SensorDataType.DATA_TYPE_PPG] = data
|
|
294
|
-
elif self.model == PPGDataMode.SPO2_AND_HR:
|
|
295
|
-
data.dataType = DataType.NTF_SPO2
|
|
296
|
-
config.period = self.period
|
|
297
|
-
config.fs = SamplingRate.HZ_100
|
|
298
|
-
data.resolutionBits = 16
|
|
299
|
-
data.channelCount = 2
|
|
300
|
-
self.sensorDatas[SensorDataType.DATA_TYPE_SPO2] = data
|
|
301
|
-
else:
|
|
302
|
-
|
|
303
|
-
data.dataType = DataType.NTF_PPG
|
|
304
|
-
data.resolutionBits = 24
|
|
305
|
-
data.channelCount = 2
|
|
306
|
-
self.sensorDatas[SensorDataType.DATA_TYPE_PPG] = data
|
|
307
|
-
data.dataType = DataType.NTF_SPO2
|
|
308
|
-
config.period = self.period
|
|
309
|
-
config.fs = self.sampleRate
|
|
310
|
-
data.resolutionBits = 16
|
|
311
|
-
data.channelCount = 1
|
|
312
|
-
self.sensorDatas[SensorDataType.DATA_TYPE_SPO2] = data
|
|
289
|
+
data.resolutionBits = 24
|
|
290
|
+
data.resolutionSigned = 0
|
|
291
|
+
data.channelCount = 2
|
|
292
|
+
self.sensorDatas[SensorDataType.DATA_TYPE_PPG] = data
|
|
293
|
+
data.clear()
|
|
313
294
|
|
|
314
|
-
|
|
315
|
-
|
|
295
|
+
data = SensorData()
|
|
296
|
+
data.dataType = DataType.NTF_SPO2
|
|
297
|
+
data.deviceMac = self.deviceMac
|
|
298
|
+
data.sampleRate = config.period
|
|
299
|
+
data.channelMask = 255
|
|
300
|
+
data.minPackageSampleCount = 1
|
|
301
|
+
data.packageSampleCount = config.batch_len
|
|
302
|
+
data.K = 1.0
|
|
303
|
+
data.resolutionBits = 16
|
|
304
|
+
data.resolutionSigned = 1
|
|
305
|
+
data.channelCount = 2
|
|
306
|
+
self.sensorDatas[SensorDataType.DATA_TYPE_SPO2] = data
|
|
316
307
|
data.clear()
|
|
308
|
+
|
|
309
|
+
self.notifyDataFlag |= DataSubscription.DNF_PPG;
|
|
317
310
|
|
|
318
311
|
return data.channelCount
|
|
319
312
|
|
|
320
|
-
def setPPGMode(self, mode: int, sampleRate: int = None, period: int = None):
|
|
313
|
+
# def setPPGMode(self, mode: int, sampleRate: int = None, period: int = None):
|
|
321
314
|
|
|
322
|
-
|
|
323
|
-
|
|
315
|
+
# if mode not in [PPGDataMode.SPO2_AND_HR, PPGDataMode.PPG_RAW]:
|
|
316
|
+
# raise ValueError(f"Invalid PPG mode: {mode}. Must be 0 (SPO2AndHR) or 1 (PPGRaw)")
|
|
324
317
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
318
|
+
# self.ppgModel = mode
|
|
319
|
+
# self.period = period
|
|
320
|
+
# self.sampleRate = sampleRate
|
|
328
321
|
|
|
329
|
-
|
|
322
|
+
# print(f"PPG mode set to: {'SPO2AndHR' if mode == 0 else 'PPGRaw'}, Sample rate: {self.sampleRate}Hz")
|
|
330
323
|
|
|
331
324
|
|
|
332
325
|
async def initIMU(self, packageCount: int) -> int:
|
|
@@ -346,6 +339,7 @@ class SensorProfileDataCtx:
|
|
|
346
339
|
data.dataType = DataType.NTF_ACC
|
|
347
340
|
data.sampleRate = config.fs
|
|
348
341
|
data.resolutionBits = 16
|
|
342
|
+
data.resolutionSigned = 1
|
|
349
343
|
data.channelCount = 3
|
|
350
344
|
data.channelMask = 255
|
|
351
345
|
data.minPackageSampleCount = min_package_sample_count
|
|
@@ -359,6 +353,7 @@ class SensorProfileDataCtx:
|
|
|
359
353
|
data.dataType = DataType.NTF_GYRO
|
|
360
354
|
data.sampleRate = config.fs
|
|
361
355
|
data.resolutionBits = 16
|
|
356
|
+
data.resolutionSigned = 1
|
|
362
357
|
data.channelCount = 3
|
|
363
358
|
data.channelMask = 255
|
|
364
359
|
data.minPackageSampleCount = min_package_sample_count
|
|
@@ -373,9 +368,10 @@ class SensorProfileDataCtx:
|
|
|
373
368
|
data.deviceMac = self.deviceMac
|
|
374
369
|
data.dataType = DataType.NTF_QUATERNION
|
|
375
370
|
data.sampleRate = config.fs
|
|
376
|
-
data.resolutionBits = 32
|
|
371
|
+
data.resolutionBits = 31 # 32-bit signed integer
|
|
372
|
+
data.resolutionSigned = 1
|
|
377
373
|
data.channelCount = 4 # w, x, y, z
|
|
378
|
-
data.channelMask =
|
|
374
|
+
data.channelMask = 0b1110 # we don't read the first channel for quaternion data
|
|
379
375
|
data.minPackageSampleCount = min_package_sample_count
|
|
380
376
|
data.packageSampleCount = config.batch_len
|
|
381
377
|
data.K = 1.0 / 1073741824.0 # 1 / 2^30
|
|
@@ -386,7 +382,8 @@ class SensorProfileDataCtx:
|
|
|
386
382
|
data.deviceMac = self.deviceMac
|
|
387
383
|
data.dataType = DataType.NTF_EULER_DATA
|
|
388
384
|
data.sampleRate = config.fs
|
|
389
|
-
data.resolutionBits =
|
|
385
|
+
data.resolutionBits = 0
|
|
386
|
+
data.resolutionSigned = 1
|
|
390
387
|
data.channelCount = 3 # pitch, roll, yaw
|
|
391
388
|
data.channelMask = 0b0111
|
|
392
389
|
data.packageIndexLength = 0
|
|
@@ -411,8 +408,8 @@ class SensorProfileDataCtx:
|
|
|
411
408
|
data.resolutionBits = 32 # float32
|
|
412
409
|
data.channelCount = 3 # roll, pitch, yaw
|
|
413
410
|
data.channelMask = 0b0111
|
|
414
|
-
data.packageIndexLength =
|
|
415
|
-
data.minPackageSampleCount =
|
|
411
|
+
data.packageIndexLength = 1
|
|
412
|
+
data.minPackageSampleCount = 1
|
|
416
413
|
data.packageSampleCount = 1
|
|
417
414
|
data.K = 1.0
|
|
418
415
|
data.clear()
|
|
@@ -431,8 +428,8 @@ class SensorProfileDataCtx:
|
|
|
431
428
|
data.resolutionBits = 32 # float32
|
|
432
429
|
data.channelCount = 4 # w, x, y, z
|
|
433
430
|
data.channelMask = 0b1111
|
|
434
|
-
data.packageIndexLength =
|
|
435
|
-
data.minPackageSampleCount =
|
|
431
|
+
data.packageIndexLength = 1
|
|
432
|
+
data.minPackageSampleCount = 1
|
|
436
433
|
data.packageSampleCount = 1
|
|
437
434
|
data.K = 1.0
|
|
438
435
|
data.clear()
|
|
@@ -446,13 +443,14 @@ class SensorProfileDataCtx:
|
|
|
446
443
|
data.deviceMac = self.deviceMac
|
|
447
444
|
data.dataType = DataType.NTF_ACC
|
|
448
445
|
data.sampleRate = 40
|
|
449
|
-
data.resolutionBits =
|
|
446
|
+
data.resolutionBits = 31 # int32
|
|
447
|
+
data.resolutionSigned = 1
|
|
450
448
|
data.channelCount = 3 # x, y, z
|
|
451
449
|
data.channelMask = 0b0111
|
|
452
450
|
data.packageIndexLength = 1
|
|
453
|
-
data.minPackageSampleCount =
|
|
451
|
+
data.minPackageSampleCount = 1
|
|
454
452
|
data.packageSampleCount = 1
|
|
455
|
-
data.K = 1.0
|
|
453
|
+
data.K = 1.0 / 65536.0
|
|
456
454
|
data.clear()
|
|
457
455
|
self.sensorDatas[SensorDataType.DATA_TYPE_ACC] = data
|
|
458
456
|
self.notifyDataFlag |= DataSubscription.ACCELERATE
|
|
@@ -464,13 +462,14 @@ class SensorProfileDataCtx:
|
|
|
464
462
|
data.deviceMac = self.deviceMac
|
|
465
463
|
data.dataType = DataType.NTF_GYRO
|
|
466
464
|
data.sampleRate = 40
|
|
467
|
-
data.resolutionBits =
|
|
465
|
+
data.resolutionBits = 31 # int32
|
|
466
|
+
data.resolutionSigned = 1
|
|
468
467
|
data.channelCount = 3 # x, y, z
|
|
469
468
|
data.channelMask = 0b0111
|
|
470
469
|
data.packageIndexLength = 1
|
|
471
|
-
data.minPackageSampleCount =
|
|
470
|
+
data.minPackageSampleCount = 1
|
|
472
471
|
data.packageSampleCount = 1
|
|
473
|
-
data.K = 1.0
|
|
472
|
+
data.K = 1.0 / 65536.0
|
|
474
473
|
data.clear()
|
|
475
474
|
self.sensorDatas[SensorDataType.DATA_TYPE_GYRO] = data
|
|
476
475
|
self.notifyDataFlag |= DataSubscription.GYROSCOPE
|
|
@@ -484,9 +483,10 @@ class SensorProfileDataCtx:
|
|
|
484
483
|
data.dataType = DataType.NTF_BRTH
|
|
485
484
|
data.sampleRate = config.fs
|
|
486
485
|
data.resolutionBits = config.resolution
|
|
486
|
+
data.resolutionSigned = 1
|
|
487
487
|
data.channelCount = 1
|
|
488
488
|
data.channelMask = config.channel_mask
|
|
489
|
-
data.minPackageSampleCount =
|
|
489
|
+
data.minPackageSampleCount = 1
|
|
490
490
|
data.packageSampleCount = config.batch_len
|
|
491
491
|
data.K = config.K
|
|
492
492
|
data.clear()
|
|
@@ -500,9 +500,10 @@ class SensorProfileDataCtx:
|
|
|
500
500
|
data.dataType = DataType.NTF_MAG_ANGLE_DATA
|
|
501
501
|
data.sampleRate = 40
|
|
502
502
|
data.resolutionBits = 8
|
|
503
|
+
data.resolutionSigned = 0
|
|
503
504
|
data.channelCount = 1
|
|
504
505
|
data.channelMask = 1
|
|
505
|
-
data.minPackageSampleCount =
|
|
506
|
+
data.minPackageSampleCount = 1
|
|
506
507
|
data.packageSampleCount = 1
|
|
507
508
|
data.K = 1
|
|
508
509
|
data.packageIndexLength = 2
|
|
@@ -599,15 +600,16 @@ class SensorProfileDataCtx:
|
|
|
599
600
|
if self.hasPPG() and (self.init_map["NTF_PPG_RAW"] == "ON"):
|
|
600
601
|
info.PpgChannelCount = await self.initPPG(packageCount)
|
|
601
602
|
|
|
602
|
-
if self.
|
|
603
|
+
if self.ppgModel == PPGDataMode.PPG_RAW:
|
|
603
604
|
|
|
604
605
|
info.Spo2SampleRate = self.sensorDatas[SensorDataType.DATA_TYPE_SPO2].sampleRate
|
|
605
|
-
if self.
|
|
606
|
+
if self.ppgModel == PPGDataMode.SPO2_AND_HR:
|
|
606
607
|
|
|
607
608
|
info.PpgSampleRate = self.sensorDatas[SensorDataType.DATA_TYPE_SPO2].sampleRate
|
|
608
609
|
else:
|
|
609
610
|
info.PpgSampleRate = self.sensorDatas[SensorDataType.DATA_TYPE_PPG].sampleRate
|
|
610
611
|
info.Spo2SampleRate = self.sensorDatas[SensorDataType.DATA_TYPE_SPO2].sampleRate
|
|
612
|
+
|
|
611
613
|
if self.hasMagAngle() and (self.init_map["NTF_MAG_ANGLE"] == "ON"):
|
|
612
614
|
magAngleChannelCount = await self.initMagAngle(packageCount)
|
|
613
615
|
info.MagAngleChannelCount = magAngleChannelCount
|
|
@@ -848,277 +850,22 @@ class SensorProfileDataCtx:
|
|
|
848
850
|
elif v == DataType.NTF_SPO2 and self.hasPPG():
|
|
849
851
|
self._process_spo2_data(data, buf)
|
|
850
852
|
elif v == DataType.NTF_EULER_DATA and self.hasEuler():
|
|
851
|
-
self.
|
|
853
|
+
sensor_data = self.sensorDatas[SensorDataType.DATA_TYPE_EULER]
|
|
854
|
+
if self.checkReadSamples(data, sensor_data, sensor_data.packageIndexLength + 1, 0, on_error_callback):
|
|
855
|
+
self.sendSensorData(sensor_data, buf)
|
|
852
856
|
elif v == DataType.NTF_QUATERNION and self.hasQuat():
|
|
853
|
-
self.
|
|
857
|
+
sensor_data = self.sensorDatas[SensorDataType.DATA_TYPE_GFORCE_QUAT]
|
|
858
|
+
if self.checkReadSamples(data, sensor_data, sensor_data.packageIndexLength + 1, 0, on_error_callback):
|
|
859
|
+
self.sendSensorData(sensor_data, buf)
|
|
854
860
|
elif v == DataType.NTF_ACC and self.hasAcc():
|
|
855
|
-
self.
|
|
861
|
+
sensor_data = self.sensorDatas[SensorDataType.DATA_TYPE_ACC]
|
|
862
|
+
if self.checkReadSamples(data, sensor_data, sensor_data.packageIndexLength + 1, 0, on_error_callback):
|
|
863
|
+
self.sendSensorData(sensor_data, buf)
|
|
856
864
|
elif v == DataType.NTF_GYRO and self.hasGyro():
|
|
857
|
-
self.
|
|
858
|
-
|
|
859
|
-
def _process_gforce_float_data(self, data: bytes, buf: Queue[SensorData],
|
|
860
|
-
data_type_index: int, channel_count: int):
|
|
861
|
-
|
|
862
|
-
HEADER_SIZE = 2
|
|
863
|
-
FLOAT_SIZE = 4
|
|
864
|
-
|
|
865
|
-
EXPECTED_SIZE = HEADER_SIZE + channel_count * FLOAT_SIZE
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
if len(data) != EXPECTED_SIZE:
|
|
869
|
-
print(f"[DataTask] 异常: len(data){len(data)} ")
|
|
870
|
-
return
|
|
871
|
-
|
|
872
|
-
sensor_data = self.sensorDatas[data_type_index]
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
if not sensor_data.channelSamples:
|
|
876
|
-
sensor_data.channelSamples = [[] for _ in range(channel_count)]
|
|
877
|
-
|
|
878
|
-
if sensor_data.lastPackageCounter < 0:
|
|
879
|
-
sensor_data.lastPackageCounter = 0
|
|
880
|
-
|
|
881
|
-
offset = HEADER_SIZE
|
|
882
|
-
sample_idx = sensor_data.lastPackageCounter
|
|
883
|
-
interval_ms = (1000 // sensor_data.sampleRate) if sensor_data.sampleRate > 0 else 0
|
|
884
|
-
|
|
885
|
-
for ch in range(channel_count):
|
|
886
|
-
raw_float = struct.unpack_from("<f", data, offset)[0]
|
|
887
|
-
offset += FLOAT_SIZE
|
|
888
|
-
val = raw_float if math.isfinite(raw_float) else 0.0
|
|
889
|
-
|
|
890
|
-
sample = Sample()
|
|
891
|
-
sample.channelIndex = ch
|
|
892
|
-
sample.sampleIndex = sample_idx
|
|
893
|
-
sample.timeStampInMs = sample_idx * interval_ms
|
|
894
|
-
sample.rawData = raw_float
|
|
895
|
-
sample.data = val
|
|
896
|
-
sample.impedance = 0.0
|
|
897
|
-
sample.saturation = 0.0
|
|
898
|
-
sample.isLost = False
|
|
899
|
-
sensor_data.channelSamples[ch].append(sample)
|
|
900
|
-
|
|
901
|
-
sensor_data.lastPackageCounter += 1
|
|
902
|
-
self.sendSensorData(sensor_data, buf)
|
|
903
|
-
|
|
904
|
-
def _process_gforce_int_data(self, data: bytes, buf: Queue[SensorData],
|
|
905
|
-
data_type_index: int, channel_count: int):
|
|
906
|
-
|
|
907
|
-
HEADER_SIZE = 1
|
|
908
|
-
PKG_IDX_SIZE = 1
|
|
909
|
-
INT_SIZE = 4
|
|
910
|
-
EXPECTED_SIZE = HEADER_SIZE + PKG_IDX_SIZE + channel_count * INT_SIZE
|
|
911
|
-
|
|
912
|
-
if len(data) != EXPECTED_SIZE:
|
|
913
|
-
print(f"[INT_DATA] type=0x{data[0]:02X} 包长不匹配 expected={EXPECTED_SIZE} actual={len(data)}")
|
|
914
|
-
return
|
|
915
|
-
|
|
916
|
-
sensor_data = self.sensorDatas[data_type_index]
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
pkg_index = data[HEADER_SIZE] & 0xFF
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
if not sensor_data.channelSamples:
|
|
923
|
-
sensor_data.channelSamples = [[] for _ in range(channel_count)]
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
if sensor_data.lastPackageCounter < 0:
|
|
927
|
-
sensor_data.lastPackageCounter = 0
|
|
928
|
-
sensor_data.lastPackageIndex = pkg_index
|
|
929
|
-
else:
|
|
930
|
-
if pkg_index == sensor_data.lastPackageIndex:
|
|
931
|
-
return
|
|
932
|
-
sensor_data.lastPackageIndex = pkg_index
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
offset = HEADER_SIZE + PKG_IDX_SIZE
|
|
936
|
-
sample_idx = sensor_data.lastPackageCounter
|
|
937
|
-
interval_ms = (1000 // sensor_data.sampleRate) if sensor_data.sampleRate > 0 else 0
|
|
938
|
-
|
|
939
|
-
channel_values = []
|
|
940
|
-
for ch in range(channel_count):
|
|
941
|
-
raw_int = struct.unpack_from("<i", data, offset)[0]
|
|
942
|
-
offset += INT_SIZE
|
|
943
|
-
channel_values.append(raw_int)
|
|
944
|
-
|
|
945
|
-
sample = Sample()
|
|
946
|
-
sample.channelIndex = ch
|
|
947
|
-
sample.sampleIndex = sample_idx
|
|
948
|
-
sample.timeStampInMs = sample_idx * interval_ms
|
|
949
|
-
sample.rawData = raw_int
|
|
950
|
-
sample.data = float(raw_int)
|
|
951
|
-
sample.impedance = 0.0
|
|
952
|
-
sample.saturation = 0.0
|
|
953
|
-
sample.isLost = False
|
|
954
|
-
sensor_data.channelSamples[ch].append(sample)
|
|
955
|
-
|
|
956
|
-
# type_name = "ACC" if data[0] & 0x7F == DataType.NTF_ACC else "GYRO"
|
|
957
|
-
# print(f"[{type_name}] pkgIdx={pkg_index} x={channel_values[0]} y={channel_values[1]} z={channel_values[2]}")
|
|
958
|
-
|
|
959
|
-
sensor_data.lastPackageCounter += 1
|
|
960
|
-
self.sendSensorData(sensor_data, buf)
|
|
961
|
-
|
|
962
|
-
def _process_spo2_data(self, data: bytes, buf: Queue[SensorData]):
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
DATA_HEADER_SIZE = 3
|
|
966
|
-
SPO2_CHANNEL_INDEX = 0
|
|
967
|
-
HEART_RATE_CHANNEL_INDEX = 1
|
|
968
|
-
REQUIRED_DATA_SIZE = 4
|
|
969
|
-
|
|
970
|
-
offset = DATA_HEADER_SIZE
|
|
971
|
-
|
|
972
|
-
if len(data) < offset + REQUIRED_DATA_SIZE:
|
|
973
|
-
return
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
spo2_value = struct.unpack_from(">H", data, offset)[0]
|
|
977
|
-
offset += 2
|
|
978
|
-
heart_rate = struct.unpack_from(">H", data, offset)[0]
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
sensor_data = self.sensorDatas[SensorDataType.DATA_TYPE_SPO2]
|
|
982
|
-
if not sensor_data.channelSamples:
|
|
983
|
-
sensor_data.channelSamples = [[], []]
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
self._append_spo2_sample(sensor_data, SPO2_CHANNEL_INDEX, spo2_value)
|
|
987
|
-
self._append_spo2_sample(sensor_data, HEART_RATE_CHANNEL_INDEX, heart_rate)
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
sensor_data.lastPackageCounter += 1
|
|
991
|
-
self.sendSensorData(sensor_data, buf)
|
|
992
|
-
|
|
993
|
-
def _append_spo2_sample(self, sensor_data: SensorData, channel_index: int, raw_value: int):
|
|
994
|
-
|
|
995
|
-
sample = Sample()
|
|
996
|
-
sample.channelIndex = channel_index
|
|
997
|
-
sample.sampleIndex = sensor_data.lastPackageCounter
|
|
998
|
-
sample.timeStampInMs = self._calculate_timestamp(sensor_data)
|
|
999
|
-
sample.rawData = raw_value
|
|
1000
|
-
sample.data = float(raw_value)
|
|
1001
|
-
sample.impedance = 0.0
|
|
1002
|
-
sample.saturation = 0.0
|
|
1003
|
-
sample.isLost = False
|
|
1004
|
-
|
|
1005
|
-
sensor_data.channelSamples[channel_index].append(sample)
|
|
1006
|
-
|
|
1007
|
-
def _calculate_timestamp(self, sensor_data: SensorData) -> int:
|
|
1008
|
-
|
|
1009
|
-
if sensor_data.sampleRate > 0:
|
|
1010
|
-
return sensor_data.lastPackageCounter * (1000 // sensor_data.sampleRate)
|
|
1011
|
-
return 0
|
|
1012
|
-
|
|
1013
|
-
def _process_imu_samples(self, data: bytes, buf: Queue[SensorData], on_error_callback=None):
|
|
1014
|
-
sensor_data_acc = self.sensorDatas[SensorDataType.DATA_TYPE_ACC]
|
|
1015
|
-
sensor_data_gyro = self.sensorDatas[SensorDataType.DATA_TYPE_GYRO]
|
|
1016
|
-
sensor_data_quat = None
|
|
1017
|
-
sensor_data_euler = None
|
|
1018
|
-
|
|
1019
|
-
sensor_datas = [sensor_data_acc, sensor_data_gyro]
|
|
1020
|
-
if self.isContainQAT6:
|
|
1021
|
-
sensor_data_quat = self.sensorDatas[SensorDataType.DATA_TYPE_QUATERNION]
|
|
1022
|
-
sensor_data_euler = self.sensorDatas[SensorDataType.DATA_TYPE_EULER]
|
|
1023
|
-
sensor_datas.append(sensor_data_quat)
|
|
1024
|
-
sensor_datas.append(sensor_data_euler)
|
|
1025
|
-
|
|
1026
|
-
if self._check_read_imu_samples(data, sensor_datas, sensor_data_quat, sensor_data_euler, on_error_callback):
|
|
1027
|
-
for sensor_data in sensor_datas:
|
|
865
|
+
sensor_data = self.sensorDatas[SensorDataType.DATA_TYPE_GYRO]
|
|
866
|
+
if self.checkReadSamples(data, sensor_data, sensor_data.packageIndexLength + 1, 0, on_error_callback):
|
|
1028
867
|
self.sendSensorData(sensor_data, buf)
|
|
1029
|
-
|
|
1030
|
-
def _check_read_imu_samples(self, data: bytes, sensor_datas: List[SensorData], sensor_data_quat, sensor_data_euler,on_error_callback=None):
|
|
1031
|
-
if not self._is_data_transfering or not sensor_datas:
|
|
1032
|
-
return False
|
|
1033
|
-
|
|
1034
|
-
try:
|
|
1035
|
-
sensor_data_ref = sensor_datas[0]
|
|
1036
|
-
offset = 1
|
|
1037
|
-
packageIndex = 0
|
|
1038
|
-
maxPackageIndex = 0
|
|
1039
|
-
|
|
1040
|
-
if len(data) < offset + sensor_data_ref.packageIndexLength:
|
|
1041
|
-
return False
|
|
1042
|
-
|
|
1043
|
-
if sensor_data_ref.packageIndexLength == 2:
|
|
1044
|
-
packageIndex = ((data[offset + 1] & 0xFF) << 8) | (data[offset] & 0xFF)
|
|
1045
|
-
maxPackageIndex = 65535
|
|
1046
|
-
elif sensor_data_ref.packageIndexLength == 1:
|
|
1047
|
-
packageIndex = data[offset] & 0xFF
|
|
1048
|
-
maxPackageIndex = 255
|
|
1049
|
-
|
|
1050
|
-
offset += sensor_data_ref.packageIndexLength
|
|
1051
|
-
if not self._has_complete_imu_packet(data, sensor_data_ref, sensor_data_quat, offset):
|
|
1052
|
-
return False
|
|
1053
|
-
|
|
1054
|
-
if sensor_data_ref.packageIndexLength <= 0:
|
|
1055
|
-
for sensor_data in sensor_datas:
|
|
1056
|
-
if sensor_data.lastPackageCounter < 0:
|
|
1057
|
-
sensor_data.lastPackageIndex = 0
|
|
1058
|
-
sensor_data.lastPackageCounter = 0
|
|
1059
|
-
else:
|
|
1060
|
-
newPackageIndex = packageIndex
|
|
1061
|
-
lastPackageIndex = sensor_data_ref.lastPackageIndex
|
|
1062
|
-
|
|
1063
|
-
if sensor_data_ref.lastPackageCounter < 0:
|
|
1064
|
-
if newPackageIndex == 0:
|
|
1065
|
-
lastPackageIndex = maxPackageIndex
|
|
1066
|
-
else:
|
|
1067
|
-
lastPackageIndex = newPackageIndex - 1
|
|
1068
|
-
|
|
1069
|
-
for sensor_data in sensor_datas:
|
|
1070
|
-
sensor_data.lastPackageIndex = lastPackageIndex
|
|
1071
|
-
sensor_data.lastPackageCounter = 0
|
|
1072
|
-
|
|
1073
|
-
if packageIndex < lastPackageIndex:
|
|
1074
|
-
packageIndex += maxPackageIndex + 1
|
|
1075
|
-
elif packageIndex == lastPackageIndex:
|
|
1076
|
-
return False
|
|
1077
|
-
|
|
1078
|
-
deltaPackageIndex = packageIndex - lastPackageIndex
|
|
1079
|
-
if deltaPackageIndex > 1:
|
|
1080
|
-
lostSampleCount = sensor_data_ref.packageSampleCount * (deltaPackageIndex - 1)
|
|
1081
|
-
|
|
1082
|
-
if lostSampleCount < 100:
|
|
1083
|
-
self._read_imu_frame_samples(data, sensor_datas, sensor_data_quat, 0, lostSampleCount)
|
|
1084
|
-
|
|
1085
|
-
if newPackageIndex == 0:
|
|
1086
|
-
lastPackageIndex = maxPackageIndex
|
|
1087
|
-
else:
|
|
1088
|
-
lastPackageIndex = newPackageIndex - 1
|
|
1089
|
-
|
|
1090
|
-
for sensor_data in sensor_datas:
|
|
1091
|
-
sensor_data.lastPackageIndex = lastPackageIndex
|
|
1092
|
-
sensor_data.lastPackageCounter += deltaPackageIndex - 1
|
|
1093
|
-
|
|
1094
|
-
lostLog = (
|
|
1095
|
-
"MSG|LOST SAMPLE|MAC|"
|
|
1096
|
-
+ str(sensor_data_ref.deviceMac)
|
|
1097
|
-
+ "|TYPE|"
|
|
1098
|
-
+ str(DataType.NTF_IMU)
|
|
1099
|
-
+ "|COUNT|"
|
|
1100
|
-
+ str(lostSampleCount)
|
|
1101
|
-
)
|
|
1102
|
-
if on_error_callback is not None:
|
|
1103
|
-
try:
|
|
1104
|
-
asyncio.get_event_loop().run_in_executor(None, on_error_callback, lostLog)
|
|
1105
|
-
except Exception as e:
|
|
1106
|
-
import traceback
|
|
1107
|
-
traceback.print_exc()
|
|
1108
|
-
|
|
1109
|
-
for sensor_data in sensor_datas:
|
|
1110
|
-
sensor_data.lastPackageIndex = newPackageIndex
|
|
1111
|
-
|
|
1112
|
-
if not self._read_imu_frame_samples(data, sensor_datas, sensor_data_quat, sensor_data_euler, offset, 0):
|
|
1113
|
-
return False
|
|
1114
|
-
|
|
1115
|
-
for sensor_data in sensor_datas:
|
|
1116
|
-
sensor_data.lastPackageCounter += 1
|
|
1117
|
-
|
|
1118
|
-
return True
|
|
1119
|
-
except Exception as e:
|
|
1120
|
-
return False
|
|
1121
|
-
|
|
868
|
+
|
|
1122
869
|
def _has_complete_imu_packet(
|
|
1123
870
|
self,
|
|
1124
871
|
data: bytes,
|
|
@@ -1131,158 +878,93 @@ class SensorProfileDataCtx:
|
|
|
1131
878
|
frameSize += 12
|
|
1132
879
|
|
|
1133
880
|
return len(data) >= dataOffset + sensor_data_ref.packageSampleCount * frameSize
|
|
881
|
+
|
|
882
|
+
def _process_imu_samples(self, data: bytes, buf: Queue[SensorData], on_error_callback=None):
|
|
883
|
+
sensor_data_acc = self.sensorDatas[SensorDataType.DATA_TYPE_ACC]
|
|
884
|
+
sensor_data_gyro = self.sensorDatas[SensorDataType.DATA_TYPE_GYRO]
|
|
885
|
+
sensor_data_quat = None
|
|
886
|
+
sensor_data_euler = None
|
|
1134
887
|
|
|
1135
|
-
|
|
1136
|
-
self
|
|
1137
|
-
|
|
1138
|
-
sensor_datas: List[SensorData],
|
|
1139
|
-
sensor_data_quat: SensorData,
|
|
1140
|
-
sensor_data_euler: SensorData,
|
|
1141
|
-
dataOffset: int,
|
|
1142
|
-
lostSampleCount: int,
|
|
1143
|
-
) -> bool:
|
|
1144
|
-
sensor_data_acc = sensor_datas[0]
|
|
1145
|
-
sensor_data_gyro = sensor_datas[1]
|
|
1146
|
-
sampleCount = sensor_data_acc.packageSampleCount
|
|
1147
|
-
if lostSampleCount > 0:
|
|
1148
|
-
sampleCount = lostSampleCount
|
|
1149
|
-
|
|
1150
|
-
frameSize = 12
|
|
1151
|
-
if sensor_data_quat is not None:
|
|
1152
|
-
frameSize += 12
|
|
1153
|
-
|
|
1154
|
-
if lostSampleCount <= 0 and len(data) < dataOffset + sampleCount * frameSize:
|
|
1155
|
-
return False
|
|
1156
|
-
|
|
1157
|
-
for sensor_data in sensor_datas:
|
|
1158
|
-
if not sensor_data.channelSamples:
|
|
1159
|
-
sensor_data.channelSamples = [[] for _ in range(sensor_data.channelCount)]
|
|
1160
|
-
|
|
1161
|
-
lastSampleIndex = sensor_data_acc.lastPackageCounter * sensor_data_acc.packageSampleCount
|
|
1162
|
-
offset = dataOffset
|
|
1163
|
-
|
|
1164
|
-
for sampleOffset in range(sampleCount):
|
|
1165
|
-
sampleIndex = lastSampleIndex + sampleOffset
|
|
1166
|
-
|
|
1167
|
-
if lostSampleCount > 0:
|
|
1168
|
-
self._append_imu_vector_sample(sensor_data_acc, sampleIndex, self.lastAccData, True)
|
|
1169
|
-
self._append_imu_vector_sample(sensor_data_gyro, sampleIndex, self.lastGyroData, True)
|
|
1170
|
-
if sensor_data_quat is not None:
|
|
1171
|
-
self._append_imu_quaternion_sample(sensor_data_quat, sensor_data_euler, sampleIndex, self.lastQuatData, True)
|
|
1172
|
-
|
|
1173
|
-
continue
|
|
1174
|
-
|
|
1175
|
-
accRaw = [
|
|
1176
|
-
struct.unpack_from("<h", data, offset)[0],
|
|
1177
|
-
struct.unpack_from("<h", data, offset + 2)[0],
|
|
1178
|
-
struct.unpack_from("<h", data, offset + 4)[0],
|
|
1179
|
-
]
|
|
1180
|
-
self.lastAccData = accRaw.copy()
|
|
1181
|
-
gyroOffset = offset + 6
|
|
1182
|
-
gyroRaw = [
|
|
1183
|
-
struct.unpack_from("<h", data, gyroOffset)[0],
|
|
1184
|
-
struct.unpack_from("<h", data, gyroOffset + 2)[0],
|
|
1185
|
-
struct.unpack_from("<h", data, gyroOffset + 4)[0],
|
|
1186
|
-
]
|
|
1187
|
-
self.lastGyroData = gyroRaw.copy()
|
|
1188
|
-
|
|
1189
|
-
self._append_imu_vector_sample(sensor_data_acc, sampleIndex, self.lastAccData, False)
|
|
1190
|
-
self._append_imu_vector_sample(sensor_data_gyro, sampleIndex, self.lastGyroData, False)
|
|
1191
|
-
|
|
1192
|
-
if sensor_data_quat is not None:
|
|
1193
|
-
quatOffset = offset + 12
|
|
1194
|
-
quatRaw = [
|
|
1195
|
-
struct.unpack_from("<i", data, quatOffset)[0],
|
|
1196
|
-
struct.unpack_from("<i", data, quatOffset + 4)[0],
|
|
1197
|
-
struct.unpack_from("<i", data, quatOffset + 8)[0],
|
|
1198
|
-
]
|
|
1199
|
-
self._append_imu_quaternion_sample(sensor_data_quat, sensor_data_euler, sampleIndex, quatRaw, False)
|
|
1200
|
-
|
|
1201
|
-
offset += frameSize
|
|
1202
|
-
|
|
1203
|
-
return True
|
|
1204
|
-
|
|
1205
|
-
def _append_imu_vector_sample(self, sensor_data: SensorData, sampleIndex: int, rawValues: List[int], isLost: bool):
|
|
1206
|
-
sampleInterval = 1000 // sensor_data.sampleRate if sensor_data.sampleRate > 0 else 0
|
|
888
|
+
if self.isContainQAT6:
|
|
889
|
+
sensor_data_quat = self.sensorDatas[SensorDataType.DATA_TYPE_QUATERNION]
|
|
890
|
+
sensor_data_euler = self.sensorDatas[SensorDataType.DATA_TYPE_EULER]
|
|
1207
891
|
|
|
1208
|
-
|
|
1209
|
-
if
|
|
1210
|
-
|
|
892
|
+
if not self._has_complete_imu_packet(data, sensor_data_acc, sensor_data_quat, 3):
|
|
893
|
+
if on_error_callback:
|
|
894
|
+
on_error_callback("Incomplete IMU packet received")
|
|
895
|
+
return
|
|
1211
896
|
|
|
1212
|
-
|
|
897
|
+
if not self.isContainQAT6:
|
|
898
|
+
if self.checkReadSamples(data, sensor_data_acc, 3, 6, on_error_callback):
|
|
899
|
+
self.sendSensorData(sensor_data_acc, buf)
|
|
1213
900
|
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
sample.data = 0.0 if isLost else rawData * sensor_data.K
|
|
1220
|
-
sample.impedance = 0.0
|
|
1221
|
-
sample.saturation = 0.0
|
|
1222
|
-
sample.isLost = isLost
|
|
1223
|
-
sensor_data.channelSamples[channelIndex].append(sample)
|
|
901
|
+
if self.checkReadSamples(data, sensor_data_gyro, 9, 6, on_error_callback):
|
|
902
|
+
self.sendSensorData(sensor_data_gyro, buf)
|
|
903
|
+
else:
|
|
904
|
+
if self.checkReadSamples(data, sensor_data_acc, 3, 18, on_error_callback):
|
|
905
|
+
self.sendSensorData(sensor_data_acc, buf)
|
|
1224
906
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
sensor_data_quat: SensorData,
|
|
1228
|
-
sensor_data_euler: SensorData,
|
|
1229
|
-
sampleIndex: int,
|
|
1230
|
-
rawValues: List[int],
|
|
1231
|
-
isLost: bool,
|
|
1232
|
-
):
|
|
1233
|
-
scale = 1073741824.0 # 2^30
|
|
1234
|
-
sampleInterval = 1000 // sensor_data_quat.sampleRate if sensor_data_quat.sampleRate > 0 else 0
|
|
1235
|
-
|
|
1236
|
-
raw = [0, 0, 0, 0]
|
|
1237
|
-
value = [0.0, 0.0, 0.0, 0.0]
|
|
1238
|
-
euler = [0.0, 0.0, 0.0]
|
|
1239
|
-
|
|
1240
|
-
if not isLost:
|
|
1241
|
-
raw[1] = rawValues[0]
|
|
1242
|
-
raw[2] = rawValues[1]
|
|
1243
|
-
raw[3] = rawValues[2]
|
|
1244
|
-
|
|
1245
|
-
value[1] = raw[1] / scale
|
|
1246
|
-
value[2] = raw[2] / scale
|
|
1247
|
-
value[3] = raw[3] / scale
|
|
1248
|
-
value[0] = math.sqrt(max(0.0, 1.0 - value[1] ** 2 - value[2] ** 2 - value[3] ** 2))
|
|
1249
|
-
|
|
1250
|
-
euler[0] = math.atan2(2 * (value[0] * value[1] + value[2] * value[3]), 1 - 2 * (value[1] ** 2 + value[2] ** 2)) * 180 / math.pi
|
|
1251
|
-
euler[1] = math.asin(max(-1.0, min(1.0, 2 * (value[0] * value[2] - value[3] * value[1])))) * 180 / math.pi
|
|
1252
|
-
euler[2] = math.atan2(2 * (value[0] * value[3] + value[1] * value[2]), 1 - 2 * (value[2] ** 2 + value[3] ** 2)) * 180 / math.pi
|
|
1253
|
-
|
|
1254
|
-
self.lastQuatData = value.copy()
|
|
1255
|
-
self.lastEulerData = euler.copy()
|
|
1256
|
-
|
|
1257
|
-
for channelIndex in range(sensor_data_quat.channelCount):
|
|
1258
|
-
if (sensor_data_quat.channelMask & (1 << channelIndex)) == 0:
|
|
1259
|
-
continue
|
|
907
|
+
if self.checkReadSamples(data, sensor_data_gyro, 9, 18, on_error_callback):
|
|
908
|
+
self.sendSensorData(sensor_data_gyro, buf)
|
|
1260
909
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
910
|
+
if sensor_data_euler.channelSamples is None or len(sensor_data_euler.channelSamples) == 0:
|
|
911
|
+
sensor_data_euler.channelSamples = [[], [], []]
|
|
912
|
+
|
|
913
|
+
if self.checkReadSamples(data, sensor_data_quat, 15, 12, on_error_callback):
|
|
914
|
+
#add w
|
|
915
|
+
sampleCount = sensor_data_quat.packageSampleCount
|
|
916
|
+
for sampleIndex in range(sampleCount):
|
|
917
|
+
try:
|
|
918
|
+
x_sample = sensor_data_quat.channelSamples[1][sampleIndex]
|
|
919
|
+
y_sample = sensor_data_quat.channelSamples[2][sampleIndex]
|
|
920
|
+
z_sample = sensor_data_quat.channelSamples[3][sampleIndex]
|
|
921
|
+
x = x_sample.data
|
|
922
|
+
y = y_sample.data
|
|
923
|
+
z = z_sample.data
|
|
924
|
+
w = math.sqrt(max(0.0, 1.0 - x ** 2 - y ** 2 - z ** 2))
|
|
925
|
+
dataItem = Sample()
|
|
926
|
+
dataItem.channelIndex = 0
|
|
927
|
+
dataItem.sampleIndex = x_sample.sampleIndex
|
|
928
|
+
dataItem.timeStampInMs = x_sample.timeStampInMs
|
|
929
|
+
dataItem.rawData = 0
|
|
930
|
+
dataItem.data = w
|
|
931
|
+
dataItem.isLost = x_sample.isLost
|
|
932
|
+
sensor_data_quat.channelSamples[0].append(dataItem)
|
|
933
|
+
|
|
934
|
+
#add euler
|
|
935
|
+
R = math.atan2(2 * (w * x + y * z), 1 - 2 * (x ** 2 + y ** 2)) * 180 / math.pi
|
|
936
|
+
R_sample = Sample()
|
|
937
|
+
R_sample.channelIndex = 0
|
|
938
|
+
R_sample.sampleIndex = x_sample.sampleIndex
|
|
939
|
+
R_sample.timeStampInMs = x_sample.timeStampInMs
|
|
940
|
+
R_sample.rawData = 0
|
|
941
|
+
R_sample.data = R
|
|
942
|
+
R_sample.isLost = x_sample.isLost
|
|
943
|
+
sensor_data_euler.channelSamples[0].append(R_sample)
|
|
944
|
+
P = math.asin(max(-1.0, min(1.0, 2 * (w * y - z * x)))) * 180 / math.pi
|
|
945
|
+
P_sample = Sample()
|
|
946
|
+
P_sample.channelIndex = 1
|
|
947
|
+
P_sample.sampleIndex = x_sample.sampleIndex
|
|
948
|
+
P_sample.timeStampInMs = x_sample.timeStampInMs
|
|
949
|
+
P_sample.rawData = 0
|
|
950
|
+
P_sample.data = P
|
|
951
|
+
P_sample.isLost = x_sample.isLost
|
|
952
|
+
sensor_data_euler.channelSamples[1].append(P_sample)
|
|
953
|
+
Y = math.atan2(2 * (w * z + x * y), 1 - 2 * (y ** 2 + z ** 2)) * 180 / math.pi
|
|
954
|
+
Y_sample = Sample()
|
|
955
|
+
Y_sample.channelIndex = 2
|
|
956
|
+
Y_sample.sampleIndex = x_sample.sampleIndex
|
|
957
|
+
Y_sample.timeStampInMs = x_sample.timeStampInMs
|
|
958
|
+
Y_sample.rawData = 0
|
|
959
|
+
Y_sample.data = Y
|
|
960
|
+
Y_sample.isLost = x_sample.isLost
|
|
961
|
+
sensor_data_euler.channelSamples[2].append(Y_sample)
|
|
962
|
+
except Exception as e:
|
|
963
|
+
import traceback
|
|
964
|
+
traceback.print_exc()
|
|
1275
965
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
sample.sampleIndex = sampleIndex
|
|
1279
|
-
sample.timeStampInMs = sampleIndex * sampleInterval
|
|
1280
|
-
sample.rawData = raw[channelIndex]
|
|
1281
|
-
sample.data = self.lastEulerData[channelIndex]
|
|
1282
|
-
sample.impedance = 0.0
|
|
1283
|
-
sample.saturation = 0.0
|
|
1284
|
-
sample.isLost = isLost
|
|
1285
|
-
sensor_data_euler.channelSamples[channelIndex].append(sample)
|
|
966
|
+
self.sendSensorData(sensor_data_quat, buf)
|
|
967
|
+
self.sendSensorData(sensor_data_euler, buf)
|
|
1286
968
|
|
|
1287
969
|
def checkReadSamples(self, data: bytes, sensorData: SensorData, dataOffset: int, dataGap: int, on_error_callback=None):
|
|
1288
970
|
offset = 1
|
|
@@ -1361,7 +1043,8 @@ class SensorProfileDataCtx:
|
|
|
1361
1043
|
|
|
1362
1044
|
sensorData.lastPackageCounter += 1
|
|
1363
1045
|
except Exception as e:
|
|
1364
|
-
|
|
1046
|
+
import traceback
|
|
1047
|
+
traceback.print_exc()
|
|
1365
1048
|
return False
|
|
1366
1049
|
return True
|
|
1367
1050
|
|
|
@@ -1379,11 +1062,33 @@ class SensorProfileDataCtx:
|
|
|
1379
1062
|
dataGap: int,
|
|
1380
1063
|
lostSampleCount: int,
|
|
1381
1064
|
):
|
|
1382
|
-
# 基本越界保护:offset 必须在 data 范围内
|
|
1383
|
-
if data is None or offset < 0 or offset > len(data):
|
|
1384
|
-
return
|
|
1385
|
-
|
|
1386
1065
|
sampleCount = sensorData.packageSampleCount
|
|
1066
|
+
if lostSampleCount <= 0:
|
|
1067
|
+
if data is None or offset < 0 or offset > len(data):
|
|
1068
|
+
raise ValueError("Invalid data or offset")
|
|
1069
|
+
|
|
1070
|
+
if sensorData.resolutionBits in (7, 8):
|
|
1071
|
+
bytesPerChannel = 1
|
|
1072
|
+
elif sensorData.resolutionBits in (12, 16, 0):
|
|
1073
|
+
bytesPerChannel = 2
|
|
1074
|
+
elif sensorData.resolutionBits == 24:
|
|
1075
|
+
bytesPerChannel = 3
|
|
1076
|
+
elif sensorData.resolutionBits in (31, 32):
|
|
1077
|
+
bytesPerChannel = 4
|
|
1078
|
+
else:
|
|
1079
|
+
bytesPerChannel = 2
|
|
1080
|
+
|
|
1081
|
+
dataLength = len(data)
|
|
1082
|
+
|
|
1083
|
+
realChannelCount = 0
|
|
1084
|
+
for channelIndex, impedanceChannelIndex in enumerate(range(sensorData.channelCount)):
|
|
1085
|
+
if (sensorData.channelMask & (1 << channelIndex)) != 0:
|
|
1086
|
+
++realChannelCount
|
|
1087
|
+
|
|
1088
|
+
if offset + ((bytesPerChannel + dataGap) * realChannelCount * sampleCount) > dataLength:
|
|
1089
|
+
raise ValueError(f"Invalid dataLength:{dataLength}")
|
|
1090
|
+
|
|
1091
|
+
|
|
1387
1092
|
sampleInterval = (
|
|
1388
1093
|
1000 // sensorData.sampleRate if sensorData.sampleRate > 0 else 0
|
|
1389
1094
|
)
|
|
@@ -1401,18 +1106,7 @@ class SensorProfileDataCtx:
|
|
|
1401
1106
|
for channelIndex in range(sensorData.channelCount):
|
|
1402
1107
|
channelSamples.append([])
|
|
1403
1108
|
|
|
1404
|
-
|
|
1405
|
-
if sensorData.resolutionBits in (7, 8):
|
|
1406
|
-
bytesPerChannel = 1
|
|
1407
|
-
elif sensorData.resolutionBits in (12, 16, 0):
|
|
1408
|
-
bytesPerChannel = 2
|
|
1409
|
-
elif sensorData.resolutionBits == 24:
|
|
1410
|
-
bytesPerChannel = 3
|
|
1411
|
-
else:
|
|
1412
|
-
bytesPerChannel = 2
|
|
1413
|
-
|
|
1414
|
-
dataLength = len(data)
|
|
1415
|
-
|
|
1109
|
+
|
|
1416
1110
|
for sampleIndex in range(sampleCount):
|
|
1417
1111
|
for channelIndex, impedanceChannelIndex in enumerate(range(sensorData.channelCount)):
|
|
1418
1112
|
if (sensorData.channelMask & (1 << channelIndex)) != 0:
|
|
@@ -1440,10 +1134,6 @@ class SensorProfileDataCtx:
|
|
|
1440
1134
|
dataItem.saturation = saturation
|
|
1441
1135
|
dataItem.isLost = True
|
|
1442
1136
|
else:
|
|
1443
|
-
# 在读取任何数据前先检查剩余字节是否足够
|
|
1444
|
-
if offset + bytesPerChannel > dataLength:
|
|
1445
|
-
return
|
|
1446
|
-
|
|
1447
1137
|
rawData = 0
|
|
1448
1138
|
if sensorData.resolutionBits == 7:
|
|
1449
1139
|
rawData = data[offset]
|
|
@@ -1453,24 +1143,29 @@ class SensorProfileDataCtx:
|
|
|
1453
1143
|
rawData = data[offset] & 0xFF
|
|
1454
1144
|
offset += 1
|
|
1455
1145
|
elif sensorData.resolutionBits == 12:
|
|
1456
|
-
rawData =
|
|
1457
|
-
|
|
1458
|
-
byteorder="little",
|
|
1459
|
-
signed=True,
|
|
1460
|
-
)
|
|
1146
|
+
rawData = struct.unpack_from("<h", data, offset)[0]
|
|
1147
|
+
rawData -= 2000
|
|
1461
1148
|
offset += 2
|
|
1462
1149
|
elif sensorData.resolutionBits == 16:
|
|
1463
|
-
|
|
1464
|
-
data
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
)
|
|
1150
|
+
if (sensorData.resolutionSigned):
|
|
1151
|
+
rawData = struct.unpack_from("<h", data, offset)[0]
|
|
1152
|
+
else:
|
|
1153
|
+
rawData = struct.unpack_from("<H", data, offset)[0]
|
|
1468
1154
|
offset += 2
|
|
1469
1155
|
elif sensorData.resolutionBits == 24:
|
|
1470
1156
|
rawData = (data[offset] << 16) | (data[offset + 1] << 8) | data[offset + 2]
|
|
1471
|
-
if sensorData.
|
|
1157
|
+
if (sensorData.resolutionSigned):
|
|
1472
1158
|
rawData -= 8388608
|
|
1473
1159
|
offset += 3
|
|
1160
|
+
elif sensorData.resolutionBits == 31:
|
|
1161
|
+
if (sensorData.resolutionSigned):
|
|
1162
|
+
rawData = struct.unpack_from("<i", data, offset)[0]
|
|
1163
|
+
else:
|
|
1164
|
+
rawData = struct.unpack_from("<I", data, offset)[0]
|
|
1165
|
+
offset += 4
|
|
1166
|
+
elif sensorData.resolutionBits == 32:
|
|
1167
|
+
rawData = struct.unpack_from("f", data, offset)[0]
|
|
1168
|
+
offset += 4
|
|
1474
1169
|
elif sensorData.resolutionBits == 0:
|
|
1475
1170
|
rawData = struct.unpack_from("<h", data, offset)[0]
|
|
1476
1171
|
offset += 2
|
|
@@ -1513,7 +1208,10 @@ class SensorProfileDataCtx:
|
|
|
1513
1208
|
oldSamples = oldChannelSamples[channelIndex]
|
|
1514
1209
|
newSamples = []
|
|
1515
1210
|
for sampleIndex in range(sensorData.minPackageSampleCount):
|
|
1516
|
-
|
|
1211
|
+
try:
|
|
1212
|
+
newSamples.append(oldSamples[startIndex + sampleIndex])
|
|
1213
|
+
except IndexError:
|
|
1214
|
+
pass
|
|
1517
1215
|
resultChannelSamples.append(newSamples)
|
|
1518
1216
|
|
|
1519
1217
|
sensorDataResult = SensorData()
|
|
@@ -8,7 +8,7 @@ with open(os.path.join(this_directory, "README.md"), "r", encoding="utf-8") as f
|
|
|
8
8
|
|
|
9
9
|
setup(
|
|
10
10
|
name="sensor-sdk",
|
|
11
|
-
version="0.0.
|
|
11
|
+
version="0.0.67",
|
|
12
12
|
description="Python sdk for Synchroni",
|
|
13
13
|
long_description=long_description,
|
|
14
14
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|