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.
Files changed (21) hide show
  1. {sensor_sdk-0.0.65/sensor_sdk.egg-info → sensor_sdk-0.0.67}/PKG-INFO +1 -1
  2. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_data.py +1 -1
  3. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_data_context.py +205 -507
  4. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67/sensor_sdk.egg-info}/PKG-INFO +1 -1
  5. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/setup.py +1 -1
  6. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/LICENSE.txt +0 -0
  7. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/README.md +0 -0
  8. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/__init__.py +0 -0
  9. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/bleak_host.py +0 -0
  10. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/bleak_process.py +0 -0
  11. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/gforce.py +0 -0
  12. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_controller.py +0 -0
  13. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_device.py +0 -0
  14. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_profile.py +0 -0
  15. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor/sensor_utils.py +0 -0
  16. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor_sdk.egg-info/SOURCES.txt +0 -0
  17. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor_sdk.egg-info/dependency_links.txt +0 -0
  18. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor_sdk.egg-info/requires.txt +0 -0
  19. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor_sdk.egg-info/top_level.txt +0 -0
  20. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/sensor_sdk.egg-info/zip-safe +0 -0
  21. {sensor_sdk-0.0.65 → sensor_sdk-0.0.67}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sensor-sdk
3
- Version: 0.0.65
3
+ Version: 0.0.67
4
4
  Summary: Python sdk for Synchroni
5
5
  Home-page: https://github.com/oymotion/SynchroniSDKPython
6
6
  Author: Martin Ye
@@ -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.model = PPGDataMode.PPG_RAW
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 = self.sampleRate
278
- config.mode = self.model
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
- if self.model == PPGDataMode.PPG_RAW:
289
-
290
- data.dataType = DataType.NTF_PPG
291
- data.resolutionBits = 24
292
- data.channelCount = 2
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
- await self.gForce.set_ppg_raw_data_config(config)
315
- self.notifyDataFlag=DataSubscription.DNF_PPG;
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
- if mode not in [PPGDataMode.SPO2_AND_HR, PPGDataMode.PPG_RAW]:
323
- raise ValueError(f"Invalid PPG mode: {mode}. Must be 0 (SPO2AndHR) or 1 (PPGRaw)")
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
- self.model = mode
326
- self.period = period
327
- self.sampleRate = sampleRate
318
+ # self.ppgModel = mode
319
+ # self.period = period
320
+ # self.sampleRate = sampleRate
328
321
 
329
- print(f"PPG mode set to: {'SPO2AndHR' if mode == 0 else 'PPGRaw'}, Sample rate: {self.sampleRate}Hz")
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 = 15 # 0b1111
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 = 32 # float32
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 = 0
415
- data.minPackageSampleCount = 2
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 = 0
435
- data.minPackageSampleCount = 2
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 = 32 # int32
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 = 2
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 = 32 # int32
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 = 2
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 = packageCount
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 = 2
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.model == PPGDataMode.PPG_RAW:
603
+ if self.ppgModel == PPGDataMode.PPG_RAW:
603
604
 
604
605
  info.Spo2SampleRate = self.sensorDatas[SensorDataType.DATA_TYPE_SPO2].sampleRate
605
- if self.model == PPGDataMode.SPO2_AND_HR:
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._process_gforce_float_data(data, buf, SensorDataType.DATA_TYPE_EULER, 3)
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._process_gforce_float_data(data, buf, SensorDataType.DATA_TYPE_GFORCE_QUAT, 4)
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._process_gforce_int_data(data, buf, SensorDataType.DATA_TYPE_ACC, 3)
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._process_gforce_int_data(data, buf, SensorDataType.DATA_TYPE_GYRO, 3)
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
- def _read_imu_frame_samples(
1136
- self,
1137
- data: bytes,
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
- for channelIndex in range(sensor_data.channelCount):
1209
- if (sensor_data.channelMask & (1 << channelIndex)) == 0:
1210
- continue
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
- rawData = 0 if isLost else rawValues[channelIndex]
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
- sample = Sample()
1215
- sample.channelIndex = channelIndex
1216
- sample.sampleIndex = sampleIndex
1217
- sample.timeStampInMs = sampleIndex * sampleInterval
1218
- sample.rawData = rawData
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
- def _append_imu_quaternion_sample(
1226
- self,
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
- sample = Sample()
1262
- sample.channelIndex = channelIndex
1263
- sample.sampleIndex = sampleIndex
1264
- sample.timeStampInMs = sampleIndex * sampleInterval
1265
- sample.rawData = raw[channelIndex]
1266
- sample.data = self.lastQuatData[channelIndex]
1267
- sample.impedance = 0.0
1268
- sample.saturation = 0.0
1269
- sample.isLost = isLost
1270
- sensor_data_quat.channelSamples[channelIndex].append(sample)
1271
-
1272
- for channelIndex in range(sensor_data_euler.channelCount):
1273
- if (sensor_data_euler.channelMask & (1 << channelIndex)) == 0:
1274
- continue
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
- sample = Sample()
1277
- sample.channelIndex = channelIndex
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
- # print(e)
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 = int.from_bytes(
1457
- data[offset: offset + 2],
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
- rawData = int.from_bytes(
1464
- data[offset: offset + 2],
1465
- byteorder="little",
1466
- signed=True,
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.dataType != DataType.NTF_PPG:
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
- newSamples.append(oldSamples[startIndex + sampleIndex])
1211
+ try:
1212
+ newSamples.append(oldSamples[startIndex + sampleIndex])
1213
+ except IndexError:
1214
+ pass
1517
1215
  resultChannelSamples.append(newSamples)
1518
1216
 
1519
1217
  sensorDataResult = SensorData()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sensor-sdk
3
- Version: 0.0.65
3
+ Version: 0.0.67
4
4
  Summary: Python sdk for Synchroni
5
5
  Home-page: https://github.com/oymotion/SynchroniSDKPython
6
6
  Author: Martin Ye
@@ -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.65",
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