muse-api 2.0.0__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.
Muse_Utils.py ADDED
@@ -0,0 +1,2882 @@
1
+ """ Muse_Utils.py: Muse utilities for encoding and decoding of command and messages.
2
+
3
+ This program is free software: you can redistribute it and/or modify it under
4
+ the terms of the GNU General Public License as published by the Free Software
5
+ Foundation, either version 3 of the License, or (at your option) any later
6
+ version.
7
+
8
+ This program is distributed in the hope that it will be useful, but WITHOUT
9
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
11
+
12
+ You should have received a copy of the GNU General Public License along with
13
+ this program. If not, see <http://www.gnu.org/licenses/>.
14
+ """
15
+
16
+ __authors__ = ["Luigi Mattiello", "Francesca Palazzo", "Roberto Bortoletto"]
17
+ __contact__ = "info@221e.com"
18
+ __copyright__ = "Copyright (c) 2020 by 221e srl."
19
+ __credits__ = ["Luigi Mattiello", "Francesca Palazzo", "Roberto Bortoletto"]
20
+ __deprecated__ = False
21
+ __email__ = "roberto.bortoletto@221e.com"
22
+ __license__ = "GNU General Public License"
23
+ __maintainer__ = "Roberto Bortoletto"
24
+ __status__ = "Production"
25
+ __version__ = "2.0.0"
26
+
27
+ import copy
28
+ import ctypes
29
+ import datetime
30
+ import struct
31
+ import math
32
+
33
+ from Muse_HW import Muse_HW as MH
34
+ from Muse_Data import *
35
+ from typing import Optional
36
+
37
+ class Muse_Utils:
38
+ """Utilities related to communication protocol specifications.
39
+ """
40
+
41
+ # USB WRAPPER FUNCTIONS
42
+
43
+ @staticmethod
44
+ def _WrapMessage(buffer):
45
+ """
46
+ Adds header and trailer to a standard command.
47
+ .. code-block:: python
48
+
49
+ Args:
50
+ buffer:
51
+ Input byte array to be wrapped.
52
+ """
53
+ wrapped_buffer = None
54
+ if (buffer != None):
55
+ #Define wrapped message buffer
56
+ wrapped_buffer = [0]* (len(buffer)+4)
57
+
58
+ #Add header to input buffer
59
+ wrapped_buffer[0] = 0x3f # ?
60
+ wrapped_buffer[1] = 0x21 # !
61
+
62
+ #Copy main content
63
+ buffer_copy = copy.copy(buffer)
64
+ wrapped_buffer[2:] = buffer_copy
65
+
66
+ #Add trailer to input buffer
67
+ wrapped_buffer.append(0x21) # !
68
+ wrapped_buffer.append(0x3f) # ?
69
+
70
+ return wrapped_buffer
71
+
72
+ @staticmethod
73
+ def _ExtractMessage(buffer):
74
+ """
75
+ Removes header and trailer from a standard command.
76
+ .. code-block:: python
77
+
78
+ Args:
79
+ buffer:
80
+ Input byte array from which the header and trailer must be removed.
81
+ """
82
+ #Remove header and trailer from input buffer
83
+ dewrapped_buffer = None
84
+ if (buffer != None):
85
+ #copio dal secondo carattere in poi, poi tolgo il trailer
86
+ dewrapped_buffer = copy.copy(buffer[2:])[:-2]
87
+ return dewrapped_buffer
88
+
89
+ # end of USB WRAPPER FUNCTIONS
90
+
91
+ # ENCODING COMMAND IMPLEMENTATION
92
+
93
+ @staticmethod
94
+ def Cmd_Acknowledge(ack: MH.AcknowledgeType, channel = MH.CommunicationChannel.CHANNEL_BLE):
95
+ """
96
+ Builds command to send Acknowledge.
97
+ .. code-block:: python
98
+
99
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
100
+ cmd = Muse_Utils.Cmd_Acknowledge(ack=MH.AcnowledgeType.ACK_SUCCESS)
101
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
102
+
103
+ Args:
104
+ ack:
105
+ Acknowledge type (ACK_SUCCESS = 0x00, ACK_ERROR = 0x01).
106
+ channel:
107
+ Communication channel over which the command will be sent.
108
+ """
109
+ # Definition of message buffer
110
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_ACKNOWLEDGE)
111
+
112
+ buffer[0] = 0x00
113
+
114
+ #Set payload length
115
+ buffer[1] = MH.CommandLength.CMD_LENGTH_ACKNOWLEDGE.value - 2
116
+
117
+ buffer[2] = MH.Command.CMD_STATE.value
118
+ buffer[3] = ack
119
+ buffer[4] = MH.SystemState.SYS_CALIB.value
120
+
121
+ return buffer
122
+
123
+ @staticmethod
124
+ def Cmd_GetSystemState(channel = MH.CommunicationChannel.CHANNEL_BLE):
125
+ """
126
+ Builds command to retrieve current system state.
127
+ .. code-block:: python
128
+
129
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
130
+ cmd = Muse_Utils.Cmd_GetSystemState()
131
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
132
+
133
+ Args:
134
+ channel:
135
+ Communication channel over which the command will be sent.
136
+ """
137
+ #Definition of message buffer
138
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_STATE)
139
+
140
+ #Get state command
141
+ buffer[0] = MH.Command.CMD_STATE.value + MH.READ_BIT_MASK
142
+
143
+ #Wrap message with header and trailer in the case of USB communication
144
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
145
+ return Muse_Utils._WrapMessage(buffer)
146
+
147
+ return buffer
148
+
149
+ @staticmethod
150
+ def Cmd_StartStream(mode: MH.DataMode, frequency: MH.DataFrequency, enableDirect = True, channel = MH.CommunicationChannel.CHANNEL_BLE):
151
+ """
152
+ Builds command to start acquisition in stream mode.
153
+ .. code-block:: python
154
+
155
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
156
+ cmd = Muse_Utils.Cmd_StartStream(MH.DataMode.DATA_MODE_AXL, MH.DataFrequency.DATA_FREQ_50Hz, enableDirect=True)
157
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
158
+
159
+ Args:
160
+ mode:
161
+ Identifies the set of data to be acquired.
162
+ frequency:
163
+ Identifies the data acquisition frequency.
164
+ enableDirect:
165
+ Allows to select the stream type (i.e., direct=True or buffered=False).
166
+ channel:
167
+ Communication channel over which the command will be sent.
168
+ """
169
+ #Definition of message buffer
170
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_START_STREAM)
171
+
172
+ #Start stream acquisition using set state command
173
+ buffer[0] = MH.Command.CMD_STATE.value
174
+
175
+ #Set payload length
176
+ buffer[1] = MH.CommandLength.CMD_LENGTH_START_STREAM.value - 2
177
+
178
+ #Set tx type based on boolean flag value
179
+ if (enableDirect):
180
+ buffer[2] = MH.SystemState.SYS_TX_DIRECT.value
181
+ else:
182
+ buffer[2] = MH.SystemState.SYS_TX_BUFFERED.value
183
+
184
+ #Set acquisition mode
185
+ tmp = struct.pack("I", mode.value)
186
+ buffer[3:6] = tmp[:3]
187
+
188
+ #Set acquisition frequency
189
+ buffer[6] = frequency.value
190
+
191
+ #Wrap message with header and trailer in the case of USB communication
192
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
193
+ return Muse_Utils._WrapMessage(buffer)
194
+
195
+ return buffer
196
+
197
+ @staticmethod
198
+ def Cmd_StartLog(mode: MH.DataMode, frequency: MH.DataFrequency, channel = MH.CommunicationChannel.CHANNEL_BLE):
199
+ """
200
+ Builds command to stop any data acquisition procedure.
201
+ .. code-block:: python
202
+
203
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
204
+ cmd = Muse_Utils.Cmd_StartLog(MH.DataMode.DATA_MODE_AXL, MH.DataFrequency.DATA_FREQ_50Hz)
205
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
206
+
207
+ Args:
208
+ mode:
209
+ Identifies the set of data to be acquired.
210
+ frequency:
211
+ Identifies the data acquisition frequency.
212
+ channel:
213
+ Communication channel over which the command will be sent.
214
+ """
215
+ #Definition of message buffer
216
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_START_LOG)
217
+
218
+ #Start stream acquisition using set state command
219
+ buffer[0] = MH.Command.CMD_STATE.value
220
+
221
+ #Set payload length
222
+ buffer[1] = MH.CommandLength.CMD_LENGTH_START_LOG.value - 2
223
+
224
+ #Set state - LOG
225
+ buffer[2] = MH.SystemState.SYS_LOG.value
226
+
227
+ #Set acquisition mode
228
+ tmp = struct.pack("I", mode.value)
229
+ buffer[3:6] = tmp[:3]
230
+
231
+ #Set acquisition frequency
232
+ buffer[6] = frequency.value
233
+
234
+ #Wrap message with header and trailer in the case of USB communication
235
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
236
+ return Muse_Utils._WrapMessage(buffer)
237
+
238
+ return buffer
239
+
240
+ @staticmethod
241
+ def Cmd_StopAcquisition(channel = MH.CommunicationChannel.CHANNEL_BLE):
242
+ """
243
+ Builds command to stop any data acquisition procedure.
244
+ .. code-block:: python
245
+
246
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
247
+ cmd = Muse_Utils.Cmd_StopAcquisition()
248
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
249
+
250
+ Args:
251
+ channel:
252
+ Communication channel over which the command will be sent.
253
+ """
254
+ #Definition of message buffer
255
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_STOP_ACQUISITION)
256
+
257
+ #Set IDLE state to stop any acquisition procedure
258
+ buffer[0] = MH.Command.CMD_STATE.value
259
+ buffer[1] = MH.CommandLength.CMD_LENGTH_STOP_ACQUISITION.value - 2
260
+ buffer[2] = MH.SystemState.SYS_IDLE.value
261
+
262
+ #Wrap message with header and trailer in the case of USB communication
263
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
264
+ return Muse_Utils._WrapMessage(buffer)
265
+
266
+ return buffer
267
+
268
+ @staticmethod
269
+ def Cmd_Restart(restartMode : MH.RestartMode, channel = MH.CommunicationChannel.CHANNEL_BLE):
270
+ """
271
+ Builds command to set restart device.
272
+ .. code-block:: python
273
+
274
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
275
+ restart_mode = MH.RestartMode.APPLICATION
276
+ cmd = Muse_Utils.Cmd_Restart(restart_mode)
277
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
278
+
279
+ Args:
280
+ restartMode:
281
+ Allows to select the restart mode (i.e., APPLICATION, BOOT or RESET).
282
+ channel:
283
+ Communication channel over which the command will be sent.
284
+ """
285
+ #Definition of message buffer
286
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_RESTART)
287
+
288
+ #Set RESTART command with the specified restart mode (i.e., boot or app)
289
+ buffer[0] = MH.Command.CMD_RESTART.value
290
+ buffer[1] = MH.CommandLength.CMD_LENGTH_RESTART.value - 2
291
+ buffer[2] = restartMode.value
292
+
293
+ # Wrap message with header and trailer in the case of USB communication
294
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
295
+ return Muse_Utils._WrapMessage(buffer)
296
+
297
+ return buffer
298
+
299
+ @staticmethod
300
+ def Cmd_GetApplicationInfo(channel = MH.CommunicationChannel.CHANNEL_BLE):
301
+ """
302
+ Builds command to retrive application firmware information.
303
+ .. code-block:: python
304
+
305
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
306
+ cmd = Muse_Utils.Cmd_GetApplicationInfo()
307
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
308
+
309
+ Args:
310
+ channel:
311
+ Communication channel over which the command will be sent.
312
+ """
313
+ #Definition of message buffer
314
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_APP_INFO)
315
+
316
+ #Get firmware application info
317
+ buffer[0] = MH.Command.CMD_APP_INFO.value + MH.READ_BIT_MASK
318
+
319
+ #Wrap message with header and trailer in the case of USB communication
320
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
321
+ return Muse_Utils._WrapMessage(buffer)
322
+
323
+ return buffer
324
+
325
+ @staticmethod
326
+ def Cmd_GetBatteryCharge(channel = MH.CommunicationChannel.CHANNEL_BLE):
327
+ """
328
+ Builds command to retrieve battery charge level [%].
329
+ .. code-block:: python
330
+
331
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
332
+ cmd = Muse_Utils.Cmd_GetBatteryCharge()
333
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
334
+
335
+ Args:
336
+ channel:
337
+ Communication channel over which the command will be sent.
338
+ """
339
+ #Definition of message buffer
340
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_BATTERY_CHARGE)
341
+
342
+ #Get battery charge
343
+ buffer[0] = MH.Command.CMD_BATTERY_CHARGE.value + MH.READ_BIT_MASK
344
+
345
+ #Wrap message with header and trailer in the case of USB communication
346
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
347
+ return Muse_Utils._WrapMessage(buffer)
348
+
349
+ return buffer
350
+
351
+ @staticmethod
352
+ def Cmd_GetBatteryVoltage(channel = MH.CommunicationChannel.CHANNEL_BLE):
353
+ """
354
+ Builds command to retrieve battery voltage level [mV].
355
+ .. code-block:: python
356
+
357
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
358
+ cmd = Muse_Utils.Cmd_GetBatteryVoltage()
359
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
360
+
361
+ Args:
362
+ channel:
363
+ Communication channel over which the command will be sent.
364
+ """
365
+ #Definition of message buffer
366
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_BATTERY_VOLTAGE)
367
+
368
+ #Get battery voltage
369
+ buffer[0] = MH.Command.CMD_BATTERY_VOLTAGE.value + MH.READ_BIT_MASK
370
+
371
+ #Wrap message with header and trailer in the case of USB communication
372
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
373
+ return Muse_Utils._WrapMessage(buffer)
374
+
375
+ return buffer
376
+
377
+ @staticmethod
378
+ def Cmd_GetDeviceCheckup(channel = MH.CommunicationChannel.CHANNEL_BLE):
379
+ """
380
+ Builds command to retrieve system check-up register value.
381
+ .. code-block:: python
382
+
383
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
384
+ cmd = Muse_Utils.Cmd_GetDeviceCheckup()
385
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
386
+
387
+ Args:
388
+ channel:
389
+ Communication channel over which the command will be sent.
390
+ """
391
+ #Definition of message buffer
392
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_CHECK_UP)
393
+
394
+ #Get check up register value
395
+ buffer[0] = MH.Command.CMD_CHECK_UP.value + MH.READ_BIT_MASK
396
+
397
+ #Wrap message with header and trailer in the case of USB communication
398
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
399
+ return Muse_Utils._WrapMessage(buffer)
400
+
401
+ return buffer
402
+
403
+ @staticmethod
404
+ def Cmd_GetFirmwareVersion(channel = MH.CommunicationChannel.CHANNEL_BLE):
405
+ """
406
+ Builds command to retrieve current firmware versions (i.e., both bootloader and application).
407
+ .. code-block:: python
408
+
409
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
410
+ cmd = Muse_Utils.Cmd_GetFirmwareVersion()
411
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
412
+
413
+ Args:
414
+ channel:
415
+ Communication channel over which the command will be sent.
416
+ """
417
+ #Definition of message buffer
418
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_FW_VERSION)
419
+
420
+ #Get firmware version labels (i.e., bootloader and application firmware)
421
+ buffer[0] = MH.Command.CMD_FW_VERSION.value + MH.READ_BIT_MASK
422
+
423
+ #Wrap message with header and trailer in the case of USB communication
424
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
425
+ return Muse_Utils._WrapMessage(buffer)
426
+
427
+ return buffer
428
+
429
+ @staticmethod
430
+ def Cmd_SetTime(channel = MH.CommunicationChannel.CHANNEL_BLE):
431
+ """
432
+ Builds command to update date/time.
433
+ .. code-block:: python
434
+
435
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
436
+ cmd = Muse_Utils.Cmd_SetTime()
437
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
438
+
439
+ Args:
440
+ channel:
441
+ Communication channel over which the command will be sent.
442
+ """
443
+ #Definition of message buffer
444
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_SET_TIME)
445
+
446
+ #Set time using current timespan since 1970
447
+ buffer[0] = MH.Command.CMD_TIME.value
448
+ buffer[1] = MH.CommandLength.CMD_LENGTH_SET_TIME.value - 2
449
+
450
+ #Get current timespan since 1/1/1970
451
+ time_span = datetime.datetime.utcnow() - datetime.datetime(1970, 1, 1, 0, 0, 0)
452
+ seconds_since_epoch = ctypes.c_long(int(time_span.total_seconds())).value
453
+
454
+ #Set payload - seconds since epoch (4 bytes)
455
+ #pack takes values that aren't bytes and convert them into bytes
456
+ payload = struct.pack("<I", seconds_since_epoch)
457
+
458
+ buffer[2:] = payload[:]
459
+
460
+ #Wrap message with header and trailer in the case of USB communication
461
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
462
+ return Muse_Utils._WrapMessage(buffer)
463
+
464
+ return buffer
465
+
466
+ @staticmethod
467
+ def Cmd_GetTime(channel = MH.CommunicationChannel.CHANNEL_BLE):
468
+ """
469
+ Builds command to retrieve current date/time.
470
+ .. code-block:: python
471
+
472
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
473
+ cmd = Muse_Utils.Cmd_GetTime()
474
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
475
+
476
+ Args:
477
+ channel:
478
+ Communication channel over which the command will be sent.
479
+ """
480
+ #Definition of message buffer
481
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_TIME)
482
+
483
+ #Get current datetime
484
+ buffer[0] = MH.Command.CMD_TIME.value + MH.READ_BIT_MASK
485
+
486
+ #Wrap message with header and trailer in the case of USB communication
487
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
488
+ return Muse_Utils._WrapMessage(buffer)
489
+
490
+ return buffer
491
+
492
+ @staticmethod
493
+ def Cmd_GetDeviceName(channel = MH.CommunicationChannel.CHANNEL_BLE):
494
+ """
495
+ Builds command to retrieve device name (i.e., the name advertised by the Bluetooth module).
496
+ .. code-block:: python
497
+
498
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
499
+ cmd = Muse_Utils.Cmd_GetDeviceName()
500
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
501
+
502
+ Args:
503
+ channel:
504
+ Communication channel over which the command will be sent.
505
+ """
506
+
507
+ #Definition of message buffer
508
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_BLE_NAME)
509
+
510
+ #Get device name (i.e., ble name)
511
+ buffer[0] = MH.Command.CMD_BLE_NAME.value + MH.READ_BIT_MASK
512
+
513
+ #Wrap message with header and trailer in the case of USB communication
514
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
515
+ return Muse_Utils._WrapMessage(buffer)
516
+
517
+ return buffer
518
+
519
+ @staticmethod
520
+ def Cmd_SetDeviceName(bleName, channel = MH.CommunicationChannel.CHANNEL_BLE):
521
+ """
522
+ Builds command to change the device name (i.e., the name advertised by the Bluetooth module).
523
+ .. code-block:: python
524
+
525
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
526
+ cmd = Muse_Utils.Cmd_SetDeviceName(bleName="my_muse")
527
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
528
+
529
+ Args:
530
+ bleName:
531
+ New device name to be set.
532
+ channel:
533
+ Communication channel over which the command will be sent.
534
+ """
535
+ #Definition of message buffer
536
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_SET_BLE_NAME)
537
+
538
+ #Check bleName to be set consistency before proceeding
539
+ if (bleName != "" and len(bleName) < 16):
540
+
541
+ buffer[0] = MH.Command.CMD_BLE_NAME.value
542
+
543
+ respLen = len(bleName)
544
+ buffer[1:1] = respLen.to_bytes(1, 'big')
545
+
546
+ for i in range(len(bleName)):
547
+ buffer[2 + i:] = bytearray(bleName[i],'utf-8')
548
+
549
+
550
+ #Wrap message with header and trailer in the case of USB communication
551
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
552
+ return Muse_Utils._WrapMessage(buffer)
553
+
554
+ return buffer
555
+
556
+ @staticmethod
557
+ def Cmd_GetDeviceID(channel = MH.CommunicationChannel.CHANNEL_BLE):
558
+ """
559
+ Builds command to retrieve the device unique identifier.
560
+ .. code-block:: python
561
+
562
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
563
+ cmd = Muse_Utils.Cmd_GetDeviceID()
564
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
565
+
566
+ Args:
567
+ channel:
568
+ Communication channel over which the command will be sent.
569
+ """
570
+ #Definition of message buffer
571
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_DEVICE_ID)
572
+
573
+ #Get device unique identifier
574
+ buffer[0] = MH.Command.CMD_DEVICE_ID.value + MH.READ_BIT_MASK
575
+
576
+ #Wrap message with header and trailer in the case of USB communication
577
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
578
+ return Muse_Utils._WrapMessage(buffer)
579
+
580
+ return buffer
581
+
582
+ @staticmethod
583
+ def Cmd_GetDeviceSkills(channel = MH.CommunicationChannel.CHANNEL_BLE):
584
+ """
585
+ Builds command to retrieve the devices skills (i.e. hardware and software features provided by the device).
586
+ The response to this command will return a byte array containing 4 bytes corresponding to hardware skills and the next 4 bytes corresponding to software skills.
587
+ .. code-block:: python
588
+
589
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
590
+ cmd = Muse_Utils.Cmd_GetDeviceSkills()
591
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
592
+
593
+ Args:
594
+ channel:
595
+ Communication channel over which the command will be sent.
596
+ """
597
+ #Definition of message buffer
598
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_DEVICE_SKILLS)
599
+
600
+ #Get device skills (i.e., hardware or software based on specified flag)
601
+ buffer[0] = MH.Command.CMD_DEVICE_SKILLS.value + MH.READ_BIT_MASK
602
+ buffer[1] = MH.CommandLength.CMD_LENGTH_GET_DEVICE_SKILLS.value - 2
603
+
604
+ #Wrap message with header and trailer in the case of USB communication
605
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
606
+ return Muse_Utils._WrapMessage(buffer)
607
+
608
+ return buffer
609
+
610
+ @staticmethod
611
+ def Cmd_GetMemoryStatus(channel = MH.CommunicationChannel.CHANNEL_BLE):
612
+ """
613
+ Builds command to retrieve current memory status.
614
+ .. code-block:: python
615
+
616
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
617
+ cmd = Muse_Utils.Cmd_GetMemoryStatus()
618
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
619
+
620
+ Args:
621
+ channel:
622
+ Communication channel over which the command will be sent.
623
+ """
624
+ #Definition of message buffer
625
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_MEM_CONTROL)
626
+
627
+ # Get available memory
628
+ buffer[0] = MH.Command.CMD_MEM_CONTROL.value + MH.READ_BIT_MASK
629
+
630
+ #Wrap message with header and trailer in the case of USB communication
631
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
632
+ return Muse_Utils._WrapMessage(buffer)
633
+
634
+ return buffer
635
+
636
+ @staticmethod
637
+ def Cmd_EraseMemory(channel = MH.CommunicationChannel.CHANNEL_BLE):
638
+ """
639
+ Builds command to erase device memory.
640
+ .. code-block:: python
641
+
642
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
643
+ cmd = Muse_Utils.Cmd_EraseMemory()
644
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
645
+
646
+ Args:
647
+ channel:
648
+ Communication channel over which the command will be sent.
649
+ """
650
+ #Definition of message buffer
651
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_SET_MEM_CONTROL)
652
+
653
+ #Erase memory
654
+ bulk_erase = 1 # Parameter to erase all memory
655
+ buffer[0] = MH.Command.CMD_MEM_CONTROL.value
656
+ buffer[1] = MH.CommandLength.CMD_LENGTH_SET_MEM_CONTROL.value - 2
657
+ buffer[2] = bulk_erase
658
+
659
+ #Wrap message with header and trailer in the case of USB communication
660
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
661
+ return Muse_Utils._WrapMessage(buffer)
662
+
663
+ return buffer
664
+
665
+ @staticmethod
666
+ def Cmd_MemoryFileInfo(fileId, channel = MH.CommunicationChannel.CHANNEL_BLE):
667
+ """
668
+ Builds command to retrieve file information.
669
+ .. code-block:: python
670
+
671
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
672
+ cmd = Muse_Utils.Cmd_MemoryFileInfo(fileId=0)
673
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
674
+
675
+ Args:
676
+ fileId:
677
+ File identifier.
678
+ channel:
679
+ Communication channel over which the command will be sent.
680
+ """
681
+ #Definition of message buffer
682
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_MEM_FILE_INFO)
683
+
684
+ #Retrieve file information given a specific file identifier
685
+ buffer[0] = MH.Command.CMD_MEM_FILE_INFO.value + MH.READ_BIT_MASK
686
+ buffer[1] = MH.CommandLength.CMD_LENGTH_GET_MEM_FILE_INFO.value - 2
687
+
688
+ #Set file id as a 2-bytes unsigned integer value
689
+ valueBytes = fileId.to_bytes(2, byteorder='little')
690
+ buffer[2] = valueBytes[0]
691
+ buffer[3] = valueBytes[1]
692
+
693
+ #Wrap message with header and trailer in the case of USB communication
694
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
695
+ return Muse_Utils._WrapMessage(buffer)
696
+
697
+ return buffer
698
+
699
+ @staticmethod
700
+ def Cmd_MemoryFileDownload(fileId, channel = MH.CommunicationChannel.CHANNEL_USB):
701
+ """
702
+ Builds command to activate a memory file offload procedure.
703
+ .. code-block:: python
704
+
705
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
706
+ cmd = Muse_Utils.Cmd_MemoryFileDownload(fileId=0)
707
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
708
+
709
+ Args:
710
+ fileId:
711
+ File identifier.
712
+ channel:
713
+ Communication channel over which the command will be sent.
714
+ """
715
+ #Definition of message buffer
716
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_MEM_FILE_DOWNLOAD)
717
+
718
+ #Start file offload procedure
719
+ buffer[0] = MH.Command.CMD_MEM_FILE_DOWNLOAD.value
720
+ buffer[1] = MH.CommandLength.CMD_LENGTH_GET_MEM_FILE_DOWNLOAD.value - 2
721
+
722
+ #Set file identifier
723
+ valueBytes = fileId.to_bytes(2, byteorder='little')
724
+ buffer[2] = valueBytes[0]
725
+ buffer[3] = valueBytes[1]
726
+
727
+ #Set file offload channel (USB vs BLE)
728
+ buffer[4] = channel.value
729
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
730
+ buffer[4] = 0x00; #set by default to USB channel (if 0x01 it manages the BLE transfer)
731
+
732
+ #Wrap message with header and trailer in the case of USB communication
733
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
734
+ return Muse_Utils._WrapMessage(buffer)
735
+
736
+ return buffer
737
+
738
+ @staticmethod
739
+ def Cmd_GetClockOffset(channel = MH.CommunicationChannel.CHANNEL_BLE):
740
+ """Builds command to retrieve current clock offset.
741
+
742
+ Args:
743
+ channel: Communication channel over which the command will be sent.
744
+ """
745
+ #Definition of message buffer
746
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_CLK_OFFSET)
747
+
748
+ #Get clock offset
749
+ buffer[0] = MH.Command.CMD_CLK_OFFSET.value + MH.READ_BIT_MASK
750
+
751
+ #Wrap message with header and trailer in the case of USB communication
752
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
753
+ return Muse_Utils._WrapMessage(buffer)
754
+
755
+ return buffer
756
+
757
+ @staticmethod
758
+ def Cmd_SetClockOffset(inOffset = 0, channel = MH.CommunicationChannel.CHANNEL_BLE):
759
+ """Builds command to trigger a clock offset estimation procedure.
760
+
761
+ Args:
762
+ inOffset: Allows to specify a custom clock offset to be set.
763
+ channel: Communication channel over which the command will be sent.
764
+ """
765
+ #Definition of message buffer
766
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_SET_CLK_OFFSET)
767
+
768
+ #Set clock offset
769
+ buffer[0] = MH.Command.CMD_CLK_OFFSET.value
770
+ buffer[1] = MH.CommandLength.CMD_LENGTH_SET_CLK_OFFSET.value - 2
771
+
772
+ valueBytes = inOffset.to_bytes(8, byteorder="little")
773
+ buffer[9] = valueBytes[7]
774
+ buffer[8] = valueBytes[6]
775
+ buffer[7] = valueBytes[5]
776
+ buffer[6] = valueBytes[4]
777
+ buffer[5] = valueBytes[3]
778
+ buffer[4] = valueBytes[2]
779
+ buffer[3] = valueBytes[1]
780
+ buffer[2] = valueBytes[0]
781
+
782
+ #Wrap message with header and trailer in the case of USB communication
783
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
784
+ return Muse_Utils._WrapMessage(buffer)
785
+
786
+ return buffer
787
+
788
+ @staticmethod
789
+ def Cmd_EnterTimeSync(channel = MH.CommunicationChannel.CHANNEL_BLE):
790
+ """Builds command to enter timesync routine.
791
+
792
+ Args:
793
+ channel: Communication channel over which the command will be sent.
794
+ """
795
+ #Definition of message buffer
796
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_ENTER_TIME_SYNC)
797
+
798
+ # Start timesync procedure
799
+ buffer[0] = MH.Command.CMD_TIME_SYNC.value
800
+
801
+ #Wrap message with header and trailer in the case of USB communication
802
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
803
+ return Muse_Utils._WrapMessage(buffer)
804
+
805
+ return buffer
806
+
807
+ @staticmethod
808
+ def Cmd_ExitTimeSync(channel = MH.CommunicationChannel.CHANNEL_BLE):
809
+ """Builds command to exit timesync routine.
810
+
811
+ Args:
812
+ channel: Communication channel over which the command will be sent.
813
+ """
814
+ #Definition of message buffer
815
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_EXIT_TIME_SYNC)
816
+
817
+ #Stop timesync procedure
818
+ buffer[0] = MH.Command.CMD_EXIT_TIME_SYNC.value
819
+
820
+ #Wrap message with header and trailer in the case of USB communication
821
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
822
+ return Muse_Utils._WrapMessage(buffer)
823
+
824
+ return buffer
825
+
826
+ @staticmethod
827
+ def Cmd_SetSensorsFullScale(gyrFS: MH.GyroscopeFS, axlFS: MH.AccelerometerFS, magFS: MH.MagnetometerFS, hdrFS : MH.AccelerometerHDRFS, channel = MH.CommunicationChannel.CHANNEL_BLE):
828
+ """
829
+ Builds command to set a custom sensors full scale configuration.
830
+ .. code-block:: python
831
+
832
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
833
+ gyro_FS = MH.GyroscopeFS.GYR_FS_2000dps
834
+ axl_FS = MH.AccelerometerFS.AXL_FS_32g
835
+ mag_FS = MH.MagnetometerFS.MAG_FS_08G
836
+ hdr_FS = MH.AccelerometerHDRFS.HDR_FS_100g
837
+ cmd = Muse_Utils.Cmd_SetSensorsFullScale(gyro_FS, axl_FS, mag_FS, hdr_FS)
838
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
839
+
840
+ Args:
841
+ gyrFS:
842
+ Gyroscope full scale code.
843
+ axlFS:
844
+ Accelerometer full scale code.
845
+ magFS:
846
+ Magnetometer full scale code.
847
+ hdrFS:
848
+ High Dynamic Range (HDR) Accelerometer code
849
+ channel:
850
+ Communication channel over which the command will be sent.
851
+ """
852
+ #Definition of message buffer
853
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_SET_SENSORS_FS)
854
+
855
+ #Set sensors full scale configuration
856
+ buffer[0] = MH.Command.CMD_SENSORS_FS.value
857
+ buffer[1] = MH.CommandLength.CMD_LENGTH_SET_SENSORS_FS.value - 2
858
+
859
+ #Build integer configuration code to be sent
860
+ buffer[2] = (axlFS.value | gyrFS.value | hdrFS.value | magFS.value)
861
+
862
+ #Wrap message with header and trailer in the case of USB communication
863
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
864
+ return Muse_Utils._WrapMessage(buffer)
865
+
866
+ return buffer
867
+
868
+ @staticmethod
869
+ def Cmd_GetSensorsFullScale(channel = MH.CommunicationChannel.CHANNEL_BLE):
870
+ """
871
+ Get the current stored sensors full scales.
872
+ .. code-block:: python
873
+
874
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
875
+ cmd = Muse_Utils.Cmd_GetSensorsFullScale()
876
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
877
+
878
+ Args:
879
+ channel:
880
+ Communication channel over which the command will be sent.
881
+ """
882
+ #Definition of message buffer
883
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_SENSORS_FS)
884
+
885
+ #Get current sensors full scale configuration
886
+ buffer[0] = MH.Command.CMD_SENSORS_FS.value + MH.READ_BIT_MASK
887
+
888
+ #Wrap message with header and trailer in the case of USB communication
889
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
890
+ return Muse_Utils._WrapMessage(buffer)
891
+
892
+ return buffer
893
+
894
+
895
+ #CMD_CALIB_MATRIX = 0x48
896
+ @staticmethod
897
+ def Cmd_SetCalibrationMatrix(rowId: bytes, colId: bytes, r1: float, r2: float, r3: float, channel = MH.CommunicationChannel.CHANNEL_BLE):
898
+
899
+ #Definition of message buffer
900
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_SET_CALIB_MATRIX)
901
+
902
+ #Set calibration matrix components
903
+ buffer[0] = MH.Command.CMD_CALIB_MATRIX.value
904
+ buffer[1] = MH.CommandLength.CMD_LENGTH_SET_CALIB_MATRIX.value - 2
905
+
906
+ #Payload
907
+ buffer[2] = rowId; #Row index in the range 0-2
908
+ buffer[3] = colId; #Column index in the range 0-3
909
+
910
+ # Values for a given row and col
911
+ valByteArray = struct.pack("<f", r1)
912
+ buffer[4:] = valByteArray[:]
913
+ valByteArray = struct.pack("<f", r2)
914
+ buffer[8:] = valByteArray[:]
915
+ valByteArray = struct.pack("<f", r3)
916
+ buffer[12:] = valByteArray[:]
917
+
918
+ #Wrap message with header and trailer in the case of USB communication
919
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
920
+ return Muse_Utils._WrapMessage(buffer)
921
+
922
+
923
+ return buffer
924
+
925
+ @staticmethod
926
+ def Cmd_GetCalibrationMatrix(calibrationType: bytes, colId: bytes, channel = MH.CommunicationChannel.CHANNEL_BLE):
927
+ """
928
+ Get a specific row of a calibration matrix.
929
+ .. code-block:: python
930
+
931
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
932
+ cmd = Muse_Utils.Cmd_GetCalibrationMatrix(calibrationType=0x01, colId=0)
933
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
934
+
935
+ Args:
936
+ calibrationType:
937
+ Define the MEMS calibration you want to retrieve (Accelerometer: 0x01, Gyroscope: 0x02, Magnetometer: 0x03).
938
+ colId:
939
+ Column index in the range 0-3.
940
+ channel:
941
+ Communication channel over which the command will be sent.
942
+ """
943
+ #Definition of message buffer
944
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_CALIB_MATRIX)
945
+
946
+ #Get current calibration matrix values
947
+ buffer[0] = MH.Command.CMD_CALIB_MATRIX.value + MH.READ_BIT_MASK
948
+ buffer[1] = MH.CommandLength.CMD_LENGTH_GET_CALIB_MATRIX.value - 2
949
+
950
+ #Set row/col payload indexes to be retrieved
951
+ buffer[2] = calibrationType
952
+ buffer[3] = colId
953
+
954
+ #Wrap message with header and trailer in the case of USB communication
955
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
956
+ return Muse_Utils._WrapMessage(buffer)
957
+
958
+ return buffer
959
+
960
+ @staticmethod
961
+ def Cmd_SetButtonLogConfiguration(mode: MH.DataMode, frequency : MH.DataFrequency, channel = MH.CommunicationChannel.CHANNEL_BLE):
962
+ """
963
+ Set the button log configuration.
964
+ .. code-block:: python
965
+
966
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
967
+ cmd = Muse_Utils.Cmd_SetButtonLogConfiguration(mode=MH.DataMode.DATA_MODE_IMU, frequency=MH.DataFrequency.DATA_FREQ_50Hz)
968
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
969
+
970
+ Args:
971
+ mode:
972
+ Identifies the set of data to be acquired.
973
+ frequency:
974
+ Identifies the data acquisition frequency.
975
+ channel:
976
+ Communication channel over which the command will be sent.
977
+ """
978
+ #Definition of message buffer
979
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_SET_BUTTON_LOG)
980
+
981
+ #Set log mode command
982
+ buffer[0] = MH.Command.CMD_BUTTON_LOG.value
983
+ buffer[1] = MH.CommandLength.CMD_LENGTH_SET_BUTTON_LOG.value - 2
984
+
985
+ #Set log mode code
986
+ tmp = struct.pack("I", mode.value)
987
+ buffer[2:5] = tmp[:3]
988
+
989
+ #Set log frequency
990
+ buffer[5] = frequency.value
991
+
992
+ #Wrap message with header and trailer in the case of USB communication
993
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
994
+ return Muse_Utils._WrapMessage(buffer)
995
+
996
+
997
+ return buffer
998
+
999
+ @staticmethod
1000
+ def Cmd_GetButtonLogConfiguration(channel = MH.CommunicationChannel.CHANNEL_BLE):
1001
+ """
1002
+ Get the current button log configuration.
1003
+ .. code-block:: python
1004
+
1005
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
1006
+ cmd = Muse_Utils.Cmd_GetButtonLogConfiguration()
1007
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1008
+
1009
+ Args:
1010
+ channel:
1011
+ Communication channel over which the command will be sent.
1012
+ """
1013
+ #Definition of message buffer
1014
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_BUTTON_LOG)
1015
+
1016
+ #Get current log mode
1017
+ buffer[0] = MH.Command.CMD_BUTTON_LOG.value + MH.READ_BIT_MASK
1018
+
1019
+ #Wrap message with header and trailer in the case of USB communication
1020
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
1021
+ return Muse_Utils._WrapMessage(buffer)
1022
+
1023
+ return buffer
1024
+
1025
+ @staticmethod
1026
+ def Cmd_GetUserConfiguration(channel = MH.CommunicationChannel.CHANNEL_BLE):
1027
+ """
1028
+ Get the current user configuration parameters.
1029
+ .. code-block:: python
1030
+
1031
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
1032
+ cmd = Muse_Utils.Cmd_GetUserConfiguration()
1033
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1034
+
1035
+ Args:
1036
+ channel:
1037
+ Communication channel over which the command will be sent.
1038
+ """
1039
+ #Definition of message buffer
1040
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_USER_CONFIG)
1041
+
1042
+ #Get current user configuration
1043
+ buffer[0] = MH.Command.CMD_USER_CONFIG.value + MH.READ_BIT_MASK
1044
+
1045
+ #Wrap message with header and trailer in the case of USB communication
1046
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
1047
+ return Muse_Utils._WrapMessage(buffer)
1048
+
1049
+ return buffer
1050
+
1051
+ @staticmethod
1052
+ def Cmd_SetUserConfiguration(standby: Optional[bool]=None, memory:Optional[bool]=None,
1053
+ streaming_channel:Optional[MH.StreamingChannel]=None, mpe9dof:Optional[bool]=None,
1054
+ slowfreq:Optional[bool]=None, channel=MH.CommunicationChannel.CHANNEL_BLE):
1055
+ """
1056
+ Set the User Configuration.
1057
+ .. code-block:: python
1058
+
1059
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
1060
+ # Enable Standby
1061
+ cmd = Muse_Utils.Cmd_SetUserConfiguration(standby = True)
1062
+ # Enable BLE channel for streaming
1063
+ cmd = Muse_Utils.Cmd_SetUserConfiguration(streaming_channel = MH.StreamingChannel.CHANNEL_BLE)
1064
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1065
+
1066
+ Args:
1067
+ standby (bool):
1068
+ It allows to enable (True) or disable (False) automatic standby. If enabled, the device will move to standby condition after 120 seconds of inactivity. The default value is ENABLED (value: 1). The system can also be forced into standby by using the button when the automatic standby is disabled. If the device is connected, the system is prevented from going into standby condition.
1069
+ memory (bool):
1070
+ It allows to enable (True) or disable (False) memory management using a circular buffer instead of a linear one. The default value is DISABLED (value: 0). If enabled, the memory is treated as a circular buffer, which means that when the memory runs out, the oldest data is deleted to make space for new. This means that a log can never be interrupted by an end-of-memory condition.
1071
+ streaming_channel (Muse_HW.StreamingChannel):
1072
+ It allows to select the streaming channel. It is mutually exlusive among BLE, USB, TCP and MQTT.
1073
+ mpe9dof (bool):
1074
+ It allows to enable (True) or disable (False) the use of the magnetometer in the MPE. The default value is DISABLED (value: 0). If enabled, it indicates that the MPE algorithm will take into account the magnetometer in its orientation estimation.
1075
+ slowfreq (bool):
1076
+ It allows to enable (True) or disable (False) the use of the slow frequency mode.
1077
+ channel (Muse_HW.CommunicationChannel):
1078
+ Communication channel over which the command will be sent.
1079
+
1080
+ """
1081
+ # Definition of message buffer
1082
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_SET_USER_CONFIG)
1083
+
1084
+ # Set a custom user configuration
1085
+ buffer[0] = MH.Command.CMD_USER_CONFIG.value
1086
+ buffer[1] = MH.CommandLength.CMD_LENGTH_SET_USER_CONFIG.value - 2
1087
+
1088
+ # Initialize bit-mask
1089
+ bitMask = 0
1090
+
1091
+ # Build configuration code to be sent using user input
1092
+ config_code = 0
1093
+ if not(standby is None):
1094
+ bitMask = bitMask + MH.UserConfigMask.USER_CFG_MASK_AUTO_STANDBY.value
1095
+ if (standby):
1096
+ config_code |= MH.UserConfigMask.USER_CFG_MASK_AUTO_STANDBY.value
1097
+ if not(memory is None):
1098
+ bitMask = bitMask + MH.UserConfigMask.USER_CFG_MASK_CIRCULAR_MEMORY.value
1099
+ if (memory):
1100
+ config_code |= MH.UserConfigMask.USER_CFG_MASK_CIRCULAR_MEMORY.value
1101
+
1102
+ # STEAMING configurations are mutually exclusive
1103
+ if not(streaming_channel is None):
1104
+
1105
+ bitMask = bitMask + MH.UserConfigMask.USER_CFG_MASK_STREAMING_CHANNEL.value
1106
+
1107
+ mqttcommands=False
1108
+ if (streaming_channel == MH.StreamingChannel.CHANNEL_BLE):
1109
+ config_code |= MH.UserConfigMask.USER_CFG_MASK_BLE_STREAM.value
1110
+ elif (streaming_channel == MH.StreamingChannel.CHANNEL_USB):
1111
+ config_code |= MH.UserConfigMask.USER_CFG_MASK_USB_STREAM.value
1112
+ elif (streaming_channel == MH.StreamingChannel.CHANNEL_TCP):
1113
+ config_code |= MH.UserConfigMask.USER_CFG_MASK_TCP_STREAM.value
1114
+ mqttcommands = True
1115
+ elif (streaming_channel == MH.StreamingChannel.CHANNEL_MQTT):
1116
+ config_code |= MH.UserConfigMask.USER_CFG_MASK_MQTT_STREAM.value
1117
+ mqttcommands = True
1118
+
1119
+ # Update mqtt commands flag
1120
+ bitMask = bitMask + MH.UserConfigMask.USER_CFG_MASK_MQTT_COMMANDS.value
1121
+ if (mqttcommands):
1122
+ config_code |= MH.UserConfigMask.USER_CFG_MASK_MQTT_COMMANDS.value
1123
+
1124
+
1125
+ if not(mpe9dof is None):
1126
+ bitMask = bitMask + MH.UserConfigMask.USER_CFG_MASK_9DOF_MPE.value
1127
+ if (mpe9dof):
1128
+ config_code |= MH.UserConfigMask.USER_CFG_MASK_9DOF_MPE.value
1129
+ if not(slowfreq is None):
1130
+ bitMask = bitMask + MH.UserConfigMask.USER_CFG_MASK_SLOW_FREQUENCY.value
1131
+ if (slowfreq):
1132
+ config_code |= MH.UserConfigMask.USER_CFG_MASK_SLOW_FREQUENCY.value
1133
+
1134
+ # Set bit-mask
1135
+ tmp = bitMask.to_bytes(2, byteorder='little')
1136
+ buffer[2:4] = tmp
1137
+
1138
+ # Set configuration code
1139
+ tmp = config_code.to_bytes(2, byteorder='little')
1140
+ buffer[4:6] = tmp
1141
+
1142
+ # Wrap message with header and trailer in the case of USB communication
1143
+ if channel == MH.CommunicationChannel.CHANNEL_USB:
1144
+ return Muse_Utils._WrapMessage(buffer)
1145
+
1146
+ return bytes(buffer)
1147
+
1148
+ # WiFi commands
1149
+
1150
+ @staticmethod
1151
+ def Cmd_SetWifiSSIDHead(ssid: str, channel = MH.CommunicationChannel.CHANNEL_BLE):
1152
+ """
1153
+ Set the SSID for WiFi connection (header part).
1154
+ .. code-block:: python
1155
+
1156
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
1157
+ SSID_string = "221e_wifi"
1158
+ cmd = Muse_Utils.Cmd_SetWifiSSIDHead(ssid = SSID_string)
1159
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1160
+
1161
+ Args:
1162
+ ssid (str):
1163
+ It is a 2 up to 32 bytes hex string representing the SSID to be set.
1164
+ channel:
1165
+ Communication channel over which the command will be sent.
1166
+ """
1167
+ # Definition of message buffer
1168
+ buffer = bytearray()
1169
+
1170
+ #Check bleName to be set consistency before proceeding
1171
+ if (ssid != "" and len(ssid) <= 18):
1172
+
1173
+ # Definition of message buffer
1174
+ buffer = bytearray(len(ssid))
1175
+
1176
+ # Set the SSID of the network – head part
1177
+ buffer[0] = MH.Command.CMD_WIFI_SSID_HEAD.value
1178
+ buffer[1] = len(ssid)
1179
+
1180
+ for i in range(len(ssid)):
1181
+ buffer[2 + i:] = bytearray(ssid[i],'utf-8')
1182
+
1183
+ # Wrap message with header and trailer in the case of USB communication
1184
+ if channel == MH.CommunicationChannel.CHANNEL_USB:
1185
+ return Muse_Utils._WrapMessage(buffer)
1186
+
1187
+ return buffer
1188
+
1189
+ @staticmethod
1190
+ def Cmd_SetWifiSSIDCont(ssid: str, channel = MH.CommunicationChannel.CHANNEL_BLE):
1191
+ """
1192
+ Set the SSID for WiFi connection (continuation part).
1193
+ .. code-block:: python
1194
+
1195
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
1196
+ SSID_string = "221e_wifi_with_long_SSID"
1197
+ # Send the first part of SSID to be set
1198
+ cmd = Muse_Utils.Cmd_SetWifiSSIDHead(ssid = SSID_string[:18])
1199
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1200
+ # Send the remaining part of SSID to be set
1201
+ cmd = Muse_Utils.Cmd_SetWifiSSIDCont(ssid = SSID_string[18:])
1202
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1203
+
1204
+ Args:
1205
+ ssid (str):
1206
+ It is a 2 up to 32 bytes hex string representing the SSID to be set.
1207
+ channel:
1208
+ Communication channel over which the command will be sent.
1209
+ """
1210
+ # Definition of message buffer
1211
+ buffer = bytearray()
1212
+
1213
+ #Check bleName to be set consistency before proceeding
1214
+ if (ssid != "" and len(ssid) <= 14):
1215
+
1216
+ # Definition of message buffer
1217
+ buffer = bytearray(len(ssid))
1218
+
1219
+ # Set the SSID of the network – head part
1220
+ buffer[0] = MH.Command.CMD_WIFI_SSID_CONT.value
1221
+ buffer[1] = len(ssid)
1222
+
1223
+ for i in range(len(ssid)):
1224
+ buffer[2 + i:] = bytearray(ssid[i],'utf-8')
1225
+
1226
+ # Wrap message with header and trailer in the case of USB communication
1227
+ if channel == MH.CommunicationChannel.CHANNEL_USB:
1228
+ return Muse_Utils._WrapMessage(buffer)
1229
+
1230
+ return buffer
1231
+
1232
+ @staticmethod
1233
+ def Cmd_SetWifiPassowrdHead(password: str, channel = MH.CommunicationChannel.CHANNEL_BLE):
1234
+ """
1235
+ Set the password for WiFi connection (header part).
1236
+ .. code-block:: python
1237
+
1238
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
1239
+ password_string = "221e_wifiPassword"
1240
+ cmd = Muse_Utils.Cmd_SetWifiPassowrdHead(password = password_string)
1241
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1242
+
1243
+ Args:
1244
+ password (str):
1245
+ It is a hex string representing the password to be set.
1246
+ channel:
1247
+ Communication channel over which the command will be sent.
1248
+ """
1249
+ # Definition of message buffer
1250
+ buffer = bytearray()
1251
+
1252
+ #Check bleName to be set consistency before proceeding
1253
+ if (password != "" and len(password) <= 18):
1254
+
1255
+ # Definition of message buffer
1256
+ buffer = bytearray(len(password))
1257
+
1258
+ # Set the Password of the network – head part
1259
+ buffer[0] = MH.Command.CMD_WIFI_PSW_HEAD.value
1260
+ buffer[1] = len(password)
1261
+
1262
+ for i in range(len(password)):
1263
+ buffer[2 + i:] = bytearray(password[i],'utf-8')
1264
+
1265
+ # Wrap message with header and trailer in the case of USB communication
1266
+ if channel == MH.CommunicationChannel.CHANNEL_USB:
1267
+ return Muse_Utils._WrapMessage(buffer)
1268
+
1269
+ return buffer
1270
+
1271
+ @staticmethod
1272
+ def Cmd_SetWifiPassowrdCont(password: str, channel = MH.CommunicationChannel.CHANNEL_BLE):
1273
+ """
1274
+ Set the password for WiFi connection (header part).
1275
+ .. code-block:: python
1276
+
1277
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
1278
+ password_string = "221e_very_long_wifi_password"
1279
+ # Send the first part of the password to be set
1280
+ cmd = Muse_Utils.Cmd_SetWifiPassowrdHead(password = password_string[:18])
1281
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1282
+ # Send continuation part of the password to be set
1283
+ cmd = Muse_Utils.Cmd_SetWifiPassowrdCont(password = password_string[18:])
1284
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1285
+
1286
+ Args:
1287
+ password (str):
1288
+ It is a hex string representing the password to be set.
1289
+ channel:
1290
+ Communication channel over which the command will be sent.
1291
+ """
1292
+ # Definition of message buffer
1293
+ buffer = bytearray()
1294
+
1295
+ #Check bleName to be set consistency before proceeding
1296
+ if (password != "" and len(password) <= 18):
1297
+
1298
+ # Definition of message buffer
1299
+ buffer = bytearray(len(password))
1300
+
1301
+ # Set the Password of the network – head part
1302
+ buffer[0] = MH.Command.CMD_WIFI_PSW_CONT.value
1303
+ buffer[1] = len(password)
1304
+
1305
+ for i in range(len(password)):
1306
+ buffer[2 + i:] = bytearray(password[i],'utf-8')
1307
+
1308
+ # Wrap message with header and trailer in the case of USB communication
1309
+ if channel == MH.CommunicationChannel.CHANNEL_USB:
1310
+ return Muse_Utils._WrapMessage(buffer)
1311
+
1312
+ return buffer
1313
+
1314
+ @staticmethod
1315
+ def Cmd_SetWifiStreamHostHead(hostname: str, channel = MH.CommunicationChannel.CHANNEL_BLE):
1316
+ """
1317
+ Set the hostname for WiFi connection (header part).
1318
+ .. code-block:: python
1319
+
1320
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
1321
+ host = "192.168.137.1"
1322
+ cmd = Muse_Utils.Cmd_SetWifiStreamHostHead(hostname = host)
1323
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1324
+
1325
+ Args:
1326
+ hostname (str):
1327
+ It is a hex string representing the hostname to be set.
1328
+ channel:
1329
+ Communication channel over which the command will be sent.
1330
+ """
1331
+ # Definition of message buffer
1332
+ buffer = bytearray()
1333
+
1334
+ #Check bleName to be set consistency before proceeding
1335
+ if (hostname != "" and len(hostname) <= 18):
1336
+
1337
+ # Definition of message buffer
1338
+ buffer = bytearray(len(hostname))
1339
+
1340
+ # Set the Host of the network – head part
1341
+ buffer[0] = MH.Command.CMD_WIFI_STREAM_HOST_HEAD.value
1342
+ buffer[1] = len(hostname)
1343
+
1344
+ for i in range(len(hostname)):
1345
+ buffer[2 + i:] = bytearray(hostname[i],'utf-8')
1346
+
1347
+ # Wrap message with header and trailer in the case of USB communication
1348
+ if channel == MH.CommunicationChannel.CHANNEL_USB:
1349
+ return Muse_Utils._WrapMessage(buffer)
1350
+
1351
+ return buffer
1352
+
1353
+ @staticmethod
1354
+ def Cmd_SetWifiStreamHostCont(hostname: str, channel = MH.CommunicationChannel.CHANNEL_BLE):
1355
+ """
1356
+ Set the hostname for WiFi connection (continuation part).
1357
+ .. code-block:: python
1358
+
1359
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
1360
+ host = "my_very_long_hostname.com"
1361
+ # header part
1362
+ cmd = Muse_Utils.Cmd_SetWifiStreamHostHead(hostname = host[:18])
1363
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1364
+ # continuation part
1365
+ cmd = Muse_Utils.Cmd_SetWifiStreamHostCont(hostname = host[18:])
1366
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1367
+
1368
+ Args:
1369
+ hostname (str):
1370
+ It is a hex string representing the continuation part of hostname to be set.
1371
+ channel:
1372
+ Communication channel over which the command will be sent.
1373
+ """
1374
+ # Definition of message buffer
1375
+ buffer = bytearray()
1376
+
1377
+ #Check bleName to be set consistency before proceeding
1378
+ if (hostname != "" and len(hostname) <= 18):
1379
+
1380
+ # Definition of message buffer
1381
+ buffer = bytearray(len(hostname))
1382
+
1383
+ # Set the Host of the network – head part
1384
+ buffer[0] = MH.Command.CMD_WIFI_STREAM_HOST_CONT.value
1385
+ buffer[1] = len(hostname)
1386
+
1387
+ for i in range(len(hostname)):
1388
+ buffer[2 + i:] = bytearray(hostname[i],'utf-8')
1389
+
1390
+ # Wrap message with header and trailer in the case of USB communication
1391
+ if channel == MH.CommunicationChannel.CHANNEL_USB:
1392
+ return Muse_Utils._WrapMessage(buffer)
1393
+
1394
+ return buffer
1395
+
1396
+ @staticmethod
1397
+ def Cmd_SetWifiStreamHostPort(port: int, channel = MH.CommunicationChannel.CHANNEL_BLE):
1398
+ """
1399
+ Set the hostname for WiFi connection (header part).
1400
+ .. code-block:: python
1401
+
1402
+ CMD_UUID = "d5913036-2d8a-41ee-85b9-4e361aa5c8a7"
1403
+ host_port = 1883
1404
+ cmd = Muse_Utils.Cmd_GetWifiStreamHostHead(port = host_port)
1405
+ await client.write_gatt_char(char_specifier=CMD_UUID, data=cmd, response=True)
1406
+
1407
+ Args:
1408
+ port (int):
1409
+ It is a 16-bit unsigned integer representing the host port to which the muse device will be connected to.
1410
+ channel:
1411
+ Communication channel over which the command will be sent.
1412
+ """
1413
+ # Definition of message buffer
1414
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_SET_WIFI_STREAM_HOST_PORT)
1415
+
1416
+ # Set the Host of the network – head part
1417
+ buffer[0] = MH.Command.CMD_WIFI_STREAM_HOST_PORT.value
1418
+ buffer[1] = MH.CommandLength.CMD_LENGTH_SET_WIFI_STREAM_HOST_PORT.value - 2
1419
+
1420
+ payload = struct.pack('<H', port)
1421
+
1422
+ buffer[2:] = payload[:]
1423
+
1424
+ # Wrap message with header and trailer in the case of USB communication
1425
+ if channel == MH.CommunicationChannel.CHANNEL_USB:
1426
+ return Muse_Utils._WrapMessage(buffer)
1427
+
1428
+ return buffer
1429
+
1430
+ @staticmethod
1431
+ def Cmd_GetWifiSSIDHead(channel = MH.CommunicationChannel.CHANNEL_BLE):
1432
+ """
1433
+ Get the SSID currently set for WiFi connection (header part).
1434
+ .. code-block:: python
1435
+
1436
+ SSID_head = Muse_Utils.Cmd_GetWifiSSIDHead(channel = MH.CommunicationChannel.CHANNEL_BLE)
1437
+ print(SSID_head)
1438
+
1439
+ Args:
1440
+ channel:
1441
+ Communication channel over which the command will be sent.
1442
+ """
1443
+ #Definition of message buffer
1444
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_WIFI_SSID_HEAD)
1445
+
1446
+ #Get device name (i.e., ble name)
1447
+ buffer[0] = MH.Command.CMD_WIFI_SSID_HEAD.value + MH.READ_BIT_MASK
1448
+
1449
+ #Wrap message with header and trailer in the case of USB communication
1450
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
1451
+ return Muse_Utils._WrapMessage(buffer)
1452
+
1453
+ return buffer
1454
+
1455
+ @staticmethod
1456
+ def Cmd_GetWifiSSIDCont(channel = MH.CommunicationChannel.CHANNEL_BLE):
1457
+ """
1458
+ Get the SSID currently set for WiFi connection (continuation part).
1459
+ .. code-block:: python
1460
+
1461
+ SSID_continuation = Muse_Utils.Cmd_GetWifiSSIDCont(channel = MH.CommunicationChannel.CHANNEL_BLE)
1462
+ print(SSID_continuation)
1463
+
1464
+ Args:
1465
+ channel:
1466
+ Communication channel over which the command will be sent.
1467
+ """
1468
+ #Definition of message buffer
1469
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_WIFI_SSID_CONT)
1470
+
1471
+ buffer[0] = MH.Command.CMD_WIFI_SSID_CONT.value + MH.READ_BIT_MASK
1472
+
1473
+ #Wrap message with header and trailer in the case of USB communication
1474
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
1475
+ return Muse_Utils._WrapMessage(buffer)
1476
+
1477
+ return buffer
1478
+
1479
+ @staticmethod
1480
+ def Cmd_GetWifiStreamHostHead(channel = MH.CommunicationChannel.CHANNEL_BLE):
1481
+ """
1482
+ Get the hostname currently set for WiFi connection (header part).
1483
+ .. code-block:: python
1484
+
1485
+ host_head = Muse_Utils.Cmd_GetWifiStreamHostHead(channel = MH.CommunicationChannel.CHANNEL_BLE)
1486
+ print(host_head)
1487
+
1488
+ Args:
1489
+ channel:
1490
+ Communication channel over which the command will be sent.
1491
+ """
1492
+ # Definition of message buffer
1493
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_WIFI_STREAM_HOST_HEAD)
1494
+
1495
+ # Set the header part of Host of the network
1496
+ buffer[0] = MH.Command.CMD_WIFI_STREAM_HOST_HEAD.value + MH.READ_BIT_MASK
1497
+ buffer[1] = MH.CommandLength.CMD_LENGTH_GET_WIFI_STREAM_HOST_HEAD.value - 2
1498
+
1499
+ # Wrap message with header and trailer in the case of USB communication
1500
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
1501
+ return Muse_Utils._WrapMessage(buffer)
1502
+
1503
+ return buffer
1504
+
1505
+ @staticmethod
1506
+ def Cmd_GetWifiStreamHostCont(channel = MH.CommunicationChannel.CHANNEL_BLE):
1507
+ """
1508
+ Get the hostname currently set for WiFi connection (continuation part).
1509
+ .. code-block:: python
1510
+
1511
+ host_continuation = Muse_Utils.Cmd_GetWifiStreamHostCont(channel = MH.CommunicationChannel.CHANNEL_BLE)
1512
+ print(host_continuation)
1513
+
1514
+ Args:
1515
+ channel:
1516
+ Communication channel over which the command will be sent.
1517
+ """
1518
+ # Definition of message buffer
1519
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_WIFI_STREAM_HOST_CONT)
1520
+
1521
+ # Set the continuation part of Host of the network
1522
+ buffer[0] = MH.Command.CMD_WIFI_STREAM_HOST_CONT.value + MH.READ_BIT_MASK
1523
+ buffer[1] = MH.CommandLength.CMD_LENGTH_GET_WIFI_STREAM_HOST_CONT.value - 2
1524
+
1525
+ # Wrap message with header and trailer in the case of USB communication
1526
+ if (channel == MH.CommunicationChannel.CHANNEL_USB):
1527
+ return Muse_Utils._WrapMessage(buffer)
1528
+
1529
+ return buffer
1530
+
1531
+ @staticmethod
1532
+ def Cmd_GetWifiStreamHostPort(channel = MH.CommunicationChannel.CHANNEL_BLE):
1533
+ """
1534
+ Get the host port set for WiFi connection.
1535
+ .. code-block:: python
1536
+
1537
+ port = Muse_Utils.Cmd_GetWifiStreamHostPort(channel = MH.CommunicationChannel.CHANNEL_BLE)
1538
+ print(port)
1539
+
1540
+ Args:
1541
+ channel:
1542
+ Communication channel over which the command will be sent.
1543
+ """
1544
+ # Definition of message buffer
1545
+ buffer = bytearray(MH.CommandLength.CMD_LENGTH_GET_WIFI_STREAM_HOST_PORT)
1546
+
1547
+ # Set the Port of the network
1548
+ buffer[0] = MH.Command.CMD_WIFI_STREAM_HOST_PORT.value + MH.READ_BIT_MASK
1549
+ buffer[1] = MH.CommandLength.CMD_LENGTH_GET_WIFI_STREAM_HOST_PORT.value - 2
1550
+
1551
+ # Wrap message with header and trailer in the case of USB communication
1552
+ if channel == MH.CommunicationChannel.CHANNEL_USB:
1553
+ return Muse_Utils._WrapMessage(buffer)
1554
+
1555
+ return buffer
1556
+
1557
+ # end ENCODING COMMAND IMPLEMENTATION
1558
+
1559
+ # DECODING FUNCTIONS
1560
+
1561
+ @staticmethod
1562
+ def ParseCommandCharacteristic(channel: MH.CommunicationChannel, buffer: bytearray):
1563
+ """
1564
+ Parse command characteristic to get a command response object. Manage also header and trailer removal in case of BLE or USB channel
1565
+
1566
+ Args:
1567
+ channel:
1568
+ Communication channel over which the command will be sent.
1569
+ buffer (bytearray):
1570
+ Byte array to be parsed.
1571
+
1572
+ Returns:
1573
+ response:
1574
+ CommandResponse type output
1575
+ """
1576
+ if (channel == MH.CommunicationChannel.CHANNEL_BLE):
1577
+ response = CommandResponse(buffer)
1578
+ else:
1579
+ response = CommandResponse(Muse_Utils._ExtractMessage(buffer))
1580
+
1581
+ return response
1582
+
1583
+ @staticmethod
1584
+ def Dec_SystemState(response: CommandResponse):
1585
+ """
1586
+ Decode system state.
1587
+
1588
+ Args:
1589
+ response:
1590
+ CommandResponse object to be decoded.
1591
+
1592
+ Returns:
1593
+ state:
1594
+ Output reference to the SystemState value.
1595
+ """
1596
+ state = MH.SystemState.SYS_NONE.value
1597
+
1598
+ #Decode system state given command response
1599
+ if ((response.tx & 0x7F) == MH.Command.CMD_STATE.value and
1600
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
1601
+
1602
+ state = response.payload[0]
1603
+
1604
+ return state
1605
+
1606
+ @staticmethod
1607
+ def Dec_ApplicationInfo(response: CommandResponse):
1608
+ """
1609
+ Decode firmware application information.
1610
+
1611
+ Args:
1612
+ response:
1613
+ CommandResponse object to be decoded.
1614
+
1615
+ Returns:
1616
+ (crc, length):
1617
+ - Output reference to the Circular Redundancy Check (CRC) as 32-bit unsigned integer.
1618
+ - Output reference to the length of the application firmware (i.e., number of bytes), as a 32-bit unsigned integer.
1619
+ """
1620
+ crc = 0
1621
+ length = 0
1622
+
1623
+ #Decode firmware application info given command response payload
1624
+ if ( response.tx == MH.Command.CMD_APP_INFO and
1625
+ response.ack== MH.AcknowledgeType.ACK_SUCCESS.value):
1626
+
1627
+ crc = int.from_bytes(response.payload[:4], byteorder='little', signed=False)
1628
+ length = int.from_bytes(response.payload[4: ], byteorder='little', signed=False)
1629
+
1630
+ return crc, length
1631
+
1632
+ @staticmethod
1633
+ def Dec_BatteryCharge(response: CommandResponse):
1634
+ """
1635
+ Decode battery charge level.
1636
+
1637
+ Args:
1638
+ response:
1639
+ CommandResponse object to be decoded.
1640
+
1641
+ Returns:
1642
+ charge:
1643
+ Output reference to battery charge value [%].
1644
+ """
1645
+ charge = -1
1646
+
1647
+ #Decode battery charge percentage value given command response payload
1648
+ if (response.tx == MH.Command.CMD_BATTERY_CHARGE and
1649
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS.value):
1650
+
1651
+ charge = response.payload[0]
1652
+
1653
+ return charge
1654
+
1655
+ @staticmethod
1656
+ def Dec_BatteryVoltage(response: CommandResponse):
1657
+ """
1658
+ Decode battery voltage level.
1659
+
1660
+ Args:
1661
+ response:
1662
+ CommandResponse object to be decoded.
1663
+
1664
+ Returns:
1665
+ voltage:
1666
+ Output reference to battery voltage value [mV].
1667
+ """
1668
+ #Decode battery voltage [mV] value given command response payload
1669
+ if (response.tx == MH.Command.CMD_BATTERY_VOLTAGE and
1670
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS.value):
1671
+
1672
+ voltage = struct.unpack('<H', response.payload)[0]
1673
+
1674
+ return voltage
1675
+
1676
+ @staticmethod
1677
+ def Dec_CheckUp(response: CommandResponse):
1678
+ """
1679
+ Decode check-up register code.
1680
+
1681
+ Args:
1682
+ response:
1683
+ CommandResponse object to be decoded.
1684
+
1685
+ Returns:
1686
+ checkup:
1687
+ Output reference to check-up register code.
1688
+ """
1689
+ #Decode checkup register value, as string, given the command response payload
1690
+ tmpOut = ""
1691
+
1692
+ #Decode current checkup register value given command response payload
1693
+ if (response.tx == MH.Command.CMD_CHECK_UP and
1694
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
1695
+
1696
+ for i in range(response.len-2):
1697
+ tmpOut += format(response.payload[i], '02X') + ' '
1698
+
1699
+ checkup = tmpOut
1700
+
1701
+ return checkup
1702
+
1703
+ @staticmethod
1704
+ def Dec_FirmwareVersion(response: CommandResponse):
1705
+ """
1706
+ Decode firmware version labels.
1707
+
1708
+ Args:
1709
+ response:
1710
+ CommandResponse object to be decoded.
1711
+
1712
+ Returns:
1713
+ (bootrev, apprev):
1714
+ - bootrev: Output reference to boot loader firmware version label.
1715
+ - apprev: Output reference to application firmware version label.
1716
+ """
1717
+ boot_rev = "x.x.x"
1718
+ app_rev = "x.x.x"
1719
+
1720
+ #Decode current firmware versions given command response payload
1721
+ if (response.tx == MH.Command.CMD_FW_VERSION and
1722
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
1723
+
1724
+ #Decode bootloader and application firmware versions
1725
+ words = str(response.payload, 'utf-8').replace('\0', ' ').split()
1726
+
1727
+ boot_rev = words[0]
1728
+ app_rev = str(response.payload[7]) + '.' + str(response.payload[8]) + '.' + str(response.payload[9]) # words[1]
1729
+
1730
+ return boot_rev, app_rev
1731
+
1732
+ @staticmethod
1733
+ def Dec_DateTime(response: CommandResponse):
1734
+ """
1735
+ Decode date/time.
1736
+
1737
+ Args:
1738
+ response:
1739
+ CommandResponse object to be decoded.
1740
+
1741
+ Returns:
1742
+ current_time:
1743
+ Output reference to the current date/time.
1744
+ """
1745
+ time = datetime.datetime(1970, 1, 1, 0, 0, 0)
1746
+
1747
+ #Decode current Date/Time given command response payload
1748
+ if (response.tx == MH.Command.CMD_TIME and
1749
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
1750
+
1751
+ epoch = datetime.datetime(1970, 1, 1) # UTC time
1752
+ time = epoch + datetime.timedelta(seconds=int.from_bytes(response.payload, byteorder='little'))
1753
+
1754
+ return time
1755
+
1756
+ @staticmethod
1757
+ def Dec_DeviceName(response: CommandResponse):
1758
+ """
1759
+ Decode device name (i.e., it is the name advertised by Bluetooth module).
1760
+
1761
+ Args:
1762
+ response:
1763
+ CommandResponse object to be decoded.
1764
+
1765
+ Returns:
1766
+ name:
1767
+ Output reference to device name.
1768
+ """
1769
+ name = "-"
1770
+
1771
+ #Decode current device name (i.e., ble name) given command response payload
1772
+ if (response.tx == MH.Command.CMD_BLE_NAME.value and
1773
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
1774
+
1775
+ name = str(response.payload, 'utf-8')
1776
+
1777
+ return name
1778
+
1779
+ @staticmethod
1780
+ def Dec_DeviceID(response: CommandResponse):
1781
+ """
1782
+ Decode device unique identifier.
1783
+
1784
+ Args:
1785
+ response:
1786
+ CommandResponse object to be decoded.
1787
+
1788
+ Returns:
1789
+ id:
1790
+ Output reference to device unique identifier.
1791
+ """
1792
+ id = ""
1793
+
1794
+ #Decode current device unique identifier given command response payload
1795
+ if (response.tx == MH.Command.CMD_DEVICE_ID and
1796
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
1797
+
1798
+ #Revert array before printing string in order to keep endianness in byte representation
1799
+ response.payload.reverse
1800
+ for i in range(response.len - 2):
1801
+ id += format(response.payload[i], '02X')
1802
+
1803
+ return id
1804
+
1805
+ @staticmethod
1806
+ def Dec_DeviceSkills(response: CommandResponse):
1807
+ """
1808
+ Decode hardware and software skills.
1809
+
1810
+ Args:
1811
+ response:
1812
+ CommandResponse object to be decoded.
1813
+
1814
+ Returns:
1815
+ hw_skills:
1816
+ Output reference to hardware skills.
1817
+ sw_skills:
1818
+ Output reference to software skills.
1819
+ """
1820
+ hw_skills = {}
1821
+ sw_skills = {}
1822
+
1823
+ #Get skills code to be parsed
1824
+ skillsCode = struct.unpack("<I", response.payload[:4])[0]
1825
+
1826
+ #Extract hardware skills given the overall skills code
1827
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_GYRO.value) > 0):
1828
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_GYRO.value: "GYR"})
1829
+
1830
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_AXL.value) > 0):
1831
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_AXL.value: "AXL"})
1832
+
1833
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_MAGN.value) > 0):
1834
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_MAGN.value: "MAG"})
1835
+
1836
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_HDR.value) > 0):
1837
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_HDR.value: "HDR"})
1838
+
1839
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_TEMP.value) > 0):
1840
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_TEMP.value: "TEMP"})
1841
+
1842
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_RH.value) > 0):
1843
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_RH.value: "RH"})
1844
+
1845
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_BAR.value) > 0):
1846
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_BAR.value: "BAR"})
1847
+
1848
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_LUM_VIS.value) > 0):
1849
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_LUM_VIS.value: "LUM/VIS"})
1850
+
1851
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_LUM_IR.value) > 0):
1852
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_LUM_IR.value: "LUM/IR"})
1853
+
1854
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_RANGE.value) > 0):
1855
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_RANGE.value: "RANGE"})
1856
+
1857
+ if ((skillsCode & MH.HardwareSkills.SKILLS_HW_MIC.value) > 0):
1858
+ hw_skills.update({MH.HardwareSkills.SKILLS_HW_MIC.value: "MIC"})
1859
+
1860
+
1861
+ #Get skills code to be parsed
1862
+ skillsCode = struct.unpack("<I", response.payload[4:])[0]
1863
+
1864
+ #Extract software skills given the overall skills code
1865
+ if ((skillsCode & MH.SoftwareSkills.SKILLS_SW_MPE.value) > 0):
1866
+ sw_skills.update({MH.SoftwareSkills.SKILLS_SW_MPE.value: "MPE"})
1867
+
1868
+ if ((skillsCode & MH.SoftwareSkills.SKILLS_SW_MAD.value) > 0):
1869
+ sw_skills.update({MH.SoftwareSkills.SKILLS_SW_MAD.value: "MAD"})
1870
+
1871
+ return hw_skills, sw_skills
1872
+
1873
+ @staticmethod
1874
+ def Dec_MemoryStatus(response: CommandResponse):
1875
+ """
1876
+ Decode memory status information.
1877
+
1878
+ Args:
1879
+ response:
1880
+ CommandResponse object to be decoded.
1881
+
1882
+ Returns:
1883
+ (available_memory, number_of_files):
1884
+ - Output reference to available memory.
1885
+ - Output reference to number of files currently saved in memory.
1886
+ """
1887
+ available_memory = 100
1888
+ number_of_files = 0
1889
+
1890
+ #Decode current memory status given command response payload
1891
+ if (response.tx == MH.Command.CMD_MEM_CONTROL and
1892
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
1893
+
1894
+ #Get available free memory (i.e., percentage value)
1895
+ available_memory = response.payload[0]
1896
+
1897
+ #Get number of files currently saved in memory
1898
+
1899
+ number_of_files = int.from_bytes(response.payload[1:3], byteorder='little', signed=False)
1900
+
1901
+ return available_memory, number_of_files
1902
+
1903
+ @staticmethod
1904
+ def Dec_FileInfo(response: CommandResponse):
1905
+ """
1906
+ Decode file information.
1907
+
1908
+ Args:
1909
+ response:
1910
+ CommandResponse object to be decoded.
1911
+
1912
+ Returns:
1913
+ file_info:
1914
+ Output reference to a FileInfo structure.
1915
+ """
1916
+ #Decode current file info given command response payload
1917
+ if (response.tx == MH.Command.CMD_MEM_FILE_INFO and
1918
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
1919
+
1920
+ #Decode file timestamp
1921
+ tmp = bytearray(8)
1922
+ tmp[:5] = response.payload[:5]
1923
+ ts = struct.unpack("<Q", tmp)[0]
1924
+ ts += MH.REFERENCE_EPOCH * 1000
1925
+
1926
+ #Decode sensors full scales
1927
+
1928
+ #Pad 1-bytes response with further 3-bytes before converting to UInt32 and extracting configuration codes
1929
+ tmp = bytearray(4)
1930
+ tmp[0] = response.payload[5]
1931
+ code = struct.unpack("<I", tmp)[0]
1932
+ #DecodeMEMSConfiguration(response.payload[5], out gyrConfig, out axlConfig, out magConfig, out hdrConfig);
1933
+ gyrConfig, axlConfig, magConfig, hdrConfig = Muse_Utils.DecodeMEMSConfiguration(code)
1934
+
1935
+ #Decode data acquisition mode
1936
+ tmp = bytearray(4)
1937
+ tmp[:3] = response.payload[6:9]
1938
+ dm = struct.unpack("<I", tmp)[0]
1939
+
1940
+ #Update file_info object
1941
+ file_info = FileInfo(ts, gyrConfig, axlConfig, magConfig, hdrConfig, dm, response.payload[9])
1942
+
1943
+ return file_info
1944
+
1945
+ @staticmethod
1946
+ def Dec_ClockOffset(response: CommandResponse):
1947
+ """
1948
+ Decode clock offset.
1949
+
1950
+ Args:
1951
+ response:
1952
+ CommandResponse object to be decoded.
1953
+
1954
+ Returns:
1955
+ clock_offset:
1956
+ Output reference to clock offset.
1957
+ """
1958
+ clock_offset = 0
1959
+
1960
+ #Decode current file info given command response payload
1961
+ if (response.tx == MH.Command.CMD_CLK_OFFSET and
1962
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
1963
+
1964
+ tmp = bytearray(8)
1965
+ tmp[:5] = response.payload[:5]
1966
+ clock_offset = struct.unpack("<Q", tmp)[0]
1967
+
1968
+ return clock_offset
1969
+
1970
+ @staticmethod
1971
+ def Dec_SensorsFullScales(response: CommandResponse):
1972
+ """
1973
+ Decode sensors full scale / sensitivity configuration.
1974
+
1975
+ Args:
1976
+ response:
1977
+ CommandResponse object to be decoded.
1978
+
1979
+ Returns:
1980
+ (gyrConfig, axlConfig, magConfig, hdrConfig):
1981
+ - gyrConfig: Output reference to Gyroscope configuration.
1982
+ - axlConfig: Output reference to Accelerometer configuration.
1983
+ - magConfig: Output reference to Magnetometer configuration.
1984
+ - hdrConfig: Output reference to High Dynamic Range (HDR) Accelerometer configuration.
1985
+ """
1986
+ gyrConfig = SensorConfig(0,0)
1987
+ axlConfig = SensorConfig(0,0)
1988
+ magConfig = SensorConfig(0,0)
1989
+ hdrConfig = SensorConfig(0,0)
1990
+
1991
+ #Decode current sensors full scales (i.e., gyr / axl / hdr / mag) given command response payload
1992
+ if (response.tx == MH.Command.CMD_SENSORS_FS and
1993
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
1994
+
1995
+
1996
+ #Pad 3-bytes response.payload array with a further byte before converting to UInt32 and extracting configuration codes
1997
+ tmp = bytearray(4)
1998
+ tmp = response.payload[:3]
1999
+ code = struct.unpack("<I", tmp + b"\x00")[0]
2000
+
2001
+ #Extract MEMS configurations
2002
+ #DecodeMEMSConfiguration(response.payload[0], out gyrConfig, out axlConfig, out magConfig, out hdrConfig);
2003
+ gyrConfig, axlConfig, magConfig, hdrConfig = Muse_Utils.DecodeMEMSConfiguration(code)
2004
+
2005
+
2006
+
2007
+ return gyrConfig, axlConfig, magConfig, hdrConfig
2008
+
2009
+ @staticmethod
2010
+ def Dec_CalibrationMatrixValues(col_val: bytearray):
2011
+ """
2012
+ Decode calibration matric values
2013
+
2014
+ Args:
2015
+ col_val (bytearray):
2016
+ bytearray containing column values to be decoded.
2017
+
2018
+ Returns:
2019
+ col_values:
2020
+ float list of decoded column values.
2021
+ """
2022
+ col_values = [0.0, 0.0, 0.0]
2023
+
2024
+ for i in range(3):
2025
+ start_index = i * 4
2026
+ tmp = col_val[start_index:start_index+4]
2027
+ col_values[i] = struct.unpack("<f", tmp)[0]
2028
+
2029
+ return col_values
2030
+
2031
+ @staticmethod
2032
+ def Dec_ButtonLogConfiguration(response: CommandResponse):
2033
+ """
2034
+ Decode button log configuration.
2035
+
2036
+ Args:
2037
+ response:
2038
+ CommandResponse object to be decoded.
2039
+
2040
+ Returns:
2041
+ (mode, frequency):
2042
+ - mode: Output reference to current acquisition mode configured.
2043
+ - frequency: Output reference to current acquisition frequency configured.
2044
+ """
2045
+ mode = ""
2046
+ frequency = ""
2047
+
2048
+ #Decode current sensors full scales (i.e., gyr / axl / hdr / mag) given command response payload
2049
+ if (response.tx == MH.Command.CMD_BUTTON_LOG and
2050
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
2051
+
2052
+ #Pad 3-bytes response.payload array with a further byte before converting to UInt32
2053
+ tmp = bytearray(4)
2054
+ tmp = response.payload[:3]
2055
+ code = struct.unpack("<I", tmp + b"\x00")[0]
2056
+
2057
+ #Build acquisition mode string description
2058
+ mode = Muse_Utils.DataModeToString(code)
2059
+
2060
+ #Set acquisition frequency string representation
2061
+ frequency = str(MH.DataFrequency(response.payload[3]).value)
2062
+
2063
+ return mode, frequency
2064
+
2065
+ @staticmethod
2066
+ def Dec_UserConfiguration(response: CommandResponse):
2067
+ """
2068
+ Decode user configuration packet.
2069
+
2070
+ Args:
2071
+ response:
2072
+ CommandResponse object to be decoded.
2073
+
2074
+ Returns:
2075
+ userConfig:
2076
+ Output reference to user configuration object (type UserConfig).
2077
+ """
2078
+ #Get code from which the user configuration must be extracted
2079
+ code = struct.unpack('<H', response.payload)[0]
2080
+
2081
+ #Set internal boolean flags
2082
+ standby = False
2083
+ memory = False
2084
+ ble = False
2085
+ usb = False
2086
+ tcp = False
2087
+ mqtt = False
2088
+ mpe9dof = False
2089
+ slowfreq = False
2090
+ mqttcommands = False
2091
+
2092
+ if ((code & MH.UserConfigMask.USER_CFG_MASK_AUTO_STANDBY.value) > 0):
2093
+ standby = True
2094
+ if ((code & MH.UserConfigMask.USER_CFG_MASK_CIRCULAR_MEMORY.value) > 0):
2095
+ memory = True
2096
+ if ((code & MH.UserConfigMask.USER_CFG_MASK_STREAMING_CHANNEL.value) == MH.UserConfigMask.USER_CFG_MASK_BLE_STREAM.value):
2097
+ ble = True
2098
+ if ((code & MH.UserConfigMask.USER_CFG_MASK_STREAMING_CHANNEL.value) == MH.UserConfigMask.USER_CFG_MASK_USB_STREAM.value):
2099
+ usb = True
2100
+ if ((code & MH.UserConfigMask.USER_CFG_MASK_STREAMING_CHANNEL.value) == MH.UserConfigMask.USER_CFG_MASK_TCP_STREAM.value):
2101
+ tcp = True
2102
+ if ((code & MH.UserConfigMask.USER_CFG_MASK_STREAMING_CHANNEL.value) == MH.UserConfigMask.USER_CFG_MASK_MQTT_STREAM.value):
2103
+ mqtt = True
2104
+ if ((code & MH.UserConfigMask.USER_CFG_MASK_9DOF_MPE.value) > 0):
2105
+ mpe9dof = True
2106
+ if ((code & MH.UserConfigMask.USER_CFG_MASK_SLOW_FREQUENCY.value) > 0):
2107
+ slowfreq = True
2108
+ if ((code & MH.UserConfigMask.USER_CFG_MASK_MQTT_COMMANDS.value) > 0):
2109
+ mqttcommands = True
2110
+
2111
+ return UserConfig(standby, memory, ble, usb, tcp, mqtt, mpe9dof, slowfreq, mqttcommands)
2112
+
2113
+ @staticmethod
2114
+ def GetPacketDimension(inMode: MH.DataMode) -> int:
2115
+ """
2116
+ Returns the data packet dimension corresponding to a given data acquisition mode.
2117
+
2118
+ Args:
2119
+ inMode:
2120
+ Data acquisition mode as DataMode type.
2121
+
2122
+ Returns:
2123
+ packet_dimension:
2124
+ A integer value representing the data packet dimension.
2125
+ """
2126
+ packet_dimension = 0
2127
+
2128
+ #Compute packet dimension given data acquisition mode
2129
+ if ((inMode.value & MH.DataMode.DATA_MODE_GYRO.value) > 0):
2130
+ packet_dimension += int(MH.DataSize.DATA_SIZE_GYRO.value)
2131
+ if ((inMode.value & MH.DataMode.DATA_MODE_AXL.value) > 0):
2132
+ packet_dimension += int(MH.DataSize.DATA_SIZE_AXL.value)
2133
+ if ((inMode.value & MH.DataMode.DATA_MODE_HDR.value) > 0):
2134
+ packet_dimension += int(MH.DataSize.DATA_SIZE_HDR.value)
2135
+ if ((inMode.value & MH.DataMode.DATA_MODE_MAGN.value) > 0):
2136
+ packet_dimension += int(MH.DataSize.DATA_SIZE_MAGN.value)
2137
+ if ((inMode.value & MH.DataMode.DATA_MODE_ORIENTATION.value) > 0):
2138
+ packet_dimension += int(MH.DataSize.DATA_SIZE_ORIENTATION.value)
2139
+ if ((inMode.value & MH.DataMode.DATA_MODE_TIMESTAMP.value) > 0):
2140
+ packet_dimension += int(MH.DataSize.DATA_SIZE_TIMESTAMP.value)
2141
+ if ((inMode.value & MH.DataMode.DATA_MODE_TEMP_HUM.value) > 0):
2142
+ packet_dimension += int(MH.DataSize.DATA_SIZE_TEMP_HUM.value)
2143
+ if ((inMode.value & MH.DataMode.DATA_MODE_TEMP_PRESS.value) > 0):
2144
+ packet_dimension += int(MH.DataSize.DATA_SIZE_TEMP_PRESS)
2145
+ if ((inMode.value & MH.DataMode.DATA_MODE_RANGE.value) > 0):
2146
+ packet_dimension += int(MH.DataSize.DATA_SIZE_RANGE.value)
2147
+ if ((inMode.value & MH.DataMode.DATA_MODE_SOUND.value) > 0):
2148
+ packet_dimension += int(MH.DataSize.DATA_SIZE_SOUND.value)
2149
+
2150
+ #Check packet dimension consistency
2151
+ #It MUST be a dividend of 120 (2040)
2152
+ if (packet_dimension == 6 or packet_dimension == 12 or packet_dimension == 24 or
2153
+ packet_dimension == 30 or packet_dimension == 60):
2154
+ return packet_dimension
2155
+
2156
+ #Return -1 in case of data dimension inconsistency
2157
+ return -1
2158
+
2159
+ @staticmethod
2160
+ def GetNumberOfPackets(inMode: MH.DataMode):
2161
+ """
2162
+ Returns the overall number of data packets contained into a 128-bytes data characteristic given a data acquisition mode.
2163
+
2164
+ Args:
2165
+ inMode:
2166
+ Data acquisition mode as DataMode type.
2167
+
2168
+ Returns:
2169
+ num_of_packets:
2170
+ A integer value representing the number of data packets.
2171
+ """
2172
+ # Compute packet dimension and check its consistency given data acquisition mode
2173
+ packet_dimension = Muse_Utils.GetPacketDimension(inMode)
2174
+
2175
+ num_of_packets = -1
2176
+ # Compute overall number of packets contained into a data characteristic update
2177
+ if (packet_dimension != -1):
2178
+ num_of_packets = (120 / packet_dimension)
2179
+
2180
+ # Return -1 in case of data dimension inconsistency
2181
+ return num_of_packets
2182
+
2183
+ @staticmethod
2184
+ def DecodePacket(buffer: bytearray, timestamp: int, mode: MH.DataMode, gyr_res: float, axl_res: float, mag_res: float, hdr_res: float):
2185
+ """
2186
+ Decode data packet.
2187
+
2188
+ Args:
2189
+ buffer (bytearray):
2190
+ Bytearray to be decoded.
2191
+ timestamp (int):
2192
+ Reference timestamp related to the last notification.
2193
+ mode (Muse_HW.DataMode):
2194
+ Data acquisition mode.
2195
+ gyr_res (float):
2196
+ Gyroscope sensitivity coefficient.
2197
+ axl_res (float):
2198
+ Accelerometer sensitivity coefficient.
2199
+ mag_res (float):
2200
+ Magnetometer sensitivity coefficient.
2201
+ hdr_res (float):
2202
+ High Dynamic Range (HDR) Accelerometer sensitivity coefficient.
2203
+
2204
+ Returns:
2205
+ current_data (Muse_Data):
2206
+ A Muse_Data object with decoded data.
2207
+ """
2208
+ #Check input buffer consistency
2209
+ if (buffer != None and len(buffer) > 0):
2210
+
2211
+ #Define muse data container
2212
+ packet_index_offset = 0
2213
+
2214
+ current_data = Muse_Data()
2215
+ #Set overall timestamp reference
2216
+ current_data.overall_timestamp = timestamp
2217
+ current_data.timestamp = timestamp
2218
+
2219
+ #Ther order of IF statements is related to the order with which the data coming within each packet in order to properly update the packet_index_offset
2220
+ if ((mode & MH.DataMode.DATA_MODE_GYRO.value) > 0):
2221
+
2222
+ #Decode Gyroscope reading
2223
+ current_packet = bytearray(int(MH.DataSize.DATA_SIZE_GYRO.value))
2224
+
2225
+ current_packet[:] = buffer[packet_index_offset:packet_index_offset+int(MH.DataSize.DATA_SIZE_GYRO.value)]
2226
+ packet_index_offset += int(MH.DataSize.DATA_SIZE_GYRO.value)
2227
+
2228
+ current_data.gyr = Muse_Utils.DataTypeGYR(current_packet, gyr_res)
2229
+
2230
+
2231
+ if ((mode & MH.DataMode.DATA_MODE_AXL.value) > 0):
2232
+
2233
+ #Decode Accelerometer reading
2234
+ current_packet = bytearray(int(MH.DataSize.DATA_SIZE_AXL.value))
2235
+
2236
+ current_packet[:] = buffer[packet_index_offset:packet_index_offset+int(MH.DataSize.DATA_SIZE_AXL.value)]
2237
+ packet_index_offset += int(MH.DataSize.DATA_SIZE_AXL.value)
2238
+
2239
+ current_data.axl = Muse_Utils.DataTypeAXL(current_packet, axl_res)
2240
+
2241
+
2242
+ if ((mode & MH.DataMode.DATA_MODE_MAGN.value) > 0):
2243
+
2244
+ # Decode Magnetometer reading
2245
+ current_packet = bytearray(int(MH.DataSize.DATA_SIZE_MAGN.value))
2246
+ current_packet[:] = buffer[packet_index_offset:packet_index_offset+int(MH.DataSize.DATA_SIZE_MAGN.value)]
2247
+ packet_index_offset += int(MH.DataSize.DATA_SIZE_MAGN.value)
2248
+
2249
+ current_data.mag = Muse_Utils.DataTypeMAGN(current_packet, mag_res)
2250
+
2251
+
2252
+ if ((mode & MH.DataMode.DATA_MODE_HDR.value) > 0):
2253
+
2254
+ #Decode Accelerometer HDR reading
2255
+ current_packet = bytearray(int(MH.DataSize.DATA_SIZE_HDR.value))
2256
+ current_packet[:] = buffer[packet_index_offset:packet_index_offset+int(MH.DataSize.DATA_SIZE_HDR.value)]
2257
+ packet_index_offset += int(MH.DataSize.DATA_SIZE_HDR.value)
2258
+
2259
+ current_data.hdr = Muse_Utils.DataTypeHDR(current_packet, hdr_res)
2260
+
2261
+
2262
+ if ((mode & MH.DataMode.DATA_MODE_ORIENTATION.value) > 0):
2263
+
2264
+ #Decode orientation quaternion
2265
+ current_packet = bytearray(int(MH.DataSize.DATA_SIZE_ORIENTATION.value))
2266
+ current_packet[:] = buffer[packet_index_offset:packet_index_offset+int(MH.DataSize.DATA_SIZE_ORIENTATION.value)]
2267
+ packet_index_offset += int(MH.DataSize.DATA_SIZE_ORIENTATION.value)
2268
+
2269
+ current_data.quat = Muse_Utils.DataTypeOrientation(current_packet)
2270
+ current_data.euler = Muse_Utils.GetAnglesFromQuaternion(current_data.quat)
2271
+
2272
+
2273
+ if ((mode & MH.DataMode.DATA_MODE_TIMESTAMP.value) > 0):
2274
+
2275
+ #Decode timestamp
2276
+ current_packet = bytearray(int(MH.DataSize.DATA_SIZE_TIMESTAMP.value))
2277
+ current_packet[:] = buffer[packet_index_offset:packet_index_offset+int(MH.DataSize.DATA_SIZE_TIMESTAMP.value)]
2278
+ packet_index_offset += int(MH.DataSize.DATA_SIZE_TIMESTAMP.value)
2279
+
2280
+ current_data.timestamp = Muse_Utils.DataTypeTimestamp(current_packet)
2281
+
2282
+
2283
+ if ((mode & MH.DataMode.DATA_MODE_TEMP_HUM.value) > 0):
2284
+
2285
+ #Decode temperature and humidity reading
2286
+ current_packet = bytearray(int(MH.DataSize.DATA_SIZE_TEMP_HUM.value))
2287
+ current_packet[:] = buffer[packet_index_offset:packet_index_offset+int(MH.DataSize.DATA_SIZE_TEMP_HUM.value)]
2288
+ packet_index_offset += int(MH.DataSize.DATA_SIZE_TEMP_HUM.value)
2289
+
2290
+ current_data.th = Muse_Utils.DataTypeTempHum(current_packet)
2291
+
2292
+
2293
+ if ((mode & MH.DataMode.DATA_MODE_TEMP_PRESS.value) > 0):
2294
+
2295
+ #Decode temperature and barometric pressure reading
2296
+ current_packet = bytearray(int(MH.DataSize.DATA_SIZE_TEMP_PRESS.value))
2297
+ current_packet[:] = buffer[packet_index_offset:packet_index_offset+int(MH.DataSize.DATA_SIZE_TEMP_PRESS.value)]
2298
+ packet_index_offset += int(MH.DataSize.DATA_SIZE_TEMP_PRESS.value)
2299
+
2300
+ current_data.tp = Muse_Utils.DataTypeTempPress(current_packet)
2301
+
2302
+
2303
+ if ((mode & MH.DataMode.DATA_MODE_RANGE.value) > 0):
2304
+
2305
+ #Decode distance / luminosity reading
2306
+ current_packet = bytearray(int(MH.DataSize.DATA_SIZE_RANGE.value))
2307
+ current_packet[:] = buffer[packet_index_offset:packet_index_offset+int(MH.DataSize.DATA_SIZE_RANGE.value)]
2308
+ packet_index_offset += int(MH.DataSize.DATA_SIZE_RANGE.value)
2309
+
2310
+ tmp = Muse_Utils.DataTypeRange(current_packet)
2311
+ current_data.light = Light(tmp[0],tmp[1],tmp[2],tmp[3])
2312
+
2313
+
2314
+ if ((mode & MH.DataMode.DATA_MODE_SOUND.value) > 0):
2315
+
2316
+ #Decode sound reading
2317
+ current_packet = bytearray(int(MH.DataSize.DATA_SIZE_SOUND.value))
2318
+ current_packet[:] = buffer[packet_index_offset:packet_index_offset+int(MH.DataSize.DATA_SIZE_SOUND.value)]
2319
+ packet_index_offset += int(MH.DataSize.DATA_SIZE_SOUND.value)
2320
+
2321
+ current_data.sound = Muse_Utils.DataTypeSound(current_packet)
2322
+
2323
+
2324
+ return current_data
2325
+
2326
+ return None
2327
+
2328
+ @staticmethod
2329
+ def Dec_WifiSSIDHead(response: CommandResponse):
2330
+ """
2331
+ Decode SSID Head(i.e., it is the SSID advertised by Bluetooth module).
2332
+
2333
+ Args:
2334
+ response:
2335
+ CommandResponse object to be decoded.
2336
+
2337
+ Returns:
2338
+ ssid:
2339
+ Output reference to SSID head part.
2340
+
2341
+ """
2342
+ ssid = ""
2343
+
2344
+ #Decode SSID given command response payload
2345
+ if (response.tx == MH.Command.CMD_WIFI_SSID_HEAD.value and
2346
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
2347
+
2348
+ ssid = str(response.payload, 'utf-8')
2349
+
2350
+ return ssid
2351
+
2352
+ @staticmethod
2353
+ def Dec_WifiSSIDCont(response: CommandResponse):
2354
+ """
2355
+ Decode device SSID Continuation part(i.e., it is the SSID advertised by Bluetooth module).
2356
+
2357
+ Args:
2358
+ response:
2359
+ CommandResponse object to be decoded.
2360
+
2361
+ Returns:
2362
+ ssid:
2363
+ Output reference to SSID continuation part.
2364
+ """
2365
+ ssid = ""
2366
+
2367
+ #Decode SSID given command response payload
2368
+ if (response.tx == MH.Command.CMD_WIFI_SSID_CONT.value and
2369
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS):
2370
+
2371
+ ssid = str(response.payload, 'utf-8')
2372
+
2373
+ return ssid
2374
+
2375
+ @staticmethod
2376
+ def Dec_WifiStreamHostHead(response: CommandResponse):
2377
+ """
2378
+ Decode Stream Host Head.
2379
+
2380
+ Args:
2381
+ response:
2382
+ CommandResponse object to be decoded.
2383
+
2384
+ Returns:
2385
+ host:
2386
+ Output reference to host head part.
2387
+ """
2388
+ #Decode battery voltage [mV] value given command response payload
2389
+ if (response.tx == MH.Command.CMD_WIFI_STREAM_HOST_HEAD and
2390
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS.value):
2391
+
2392
+ host = str(response.payload, 'utf-8')
2393
+
2394
+ return host
2395
+
2396
+ @staticmethod
2397
+ def Dec_WifiStreamHostCont(response: CommandResponse):
2398
+ """
2399
+ Decode Stream Host continuation part.
2400
+
2401
+ Args:
2402
+ response:
2403
+ CommandResponse object to be decoded.
2404
+
2405
+ Returns:
2406
+ host:
2407
+ Output reference to the remaining part of host.
2408
+ """
2409
+ #Decode battery voltage [mV] value given command response payload
2410
+ if (response.tx == MH.Command.CMD_WIFI_STREAM_HOST_CONT and
2411
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS.value):
2412
+
2413
+ host = str(response.payload, 'utf-8')
2414
+
2415
+ return host
2416
+
2417
+ @staticmethod
2418
+ def Dec_WifiStreamHostPort(response: CommandResponse):
2419
+ """
2420
+ Decode Stream Host port.
2421
+
2422
+ Args:
2423
+ response:
2424
+ CommandResponse object to be decoded.
2425
+
2426
+ Returns:
2427
+ port:
2428
+ Output reference to the WiFi port.
2429
+ """
2430
+ #Decode battery voltage [mV] value given command response payload
2431
+ if (response.tx == MH.Command.CMD_WIFI_STREAM_HOST_PORT and
2432
+ response.ack == MH.AcknowledgeType.ACK_SUCCESS.value):
2433
+
2434
+ port = struct.unpack('<H', response.payload)[0]
2435
+
2436
+ return port
2437
+
2438
+ @staticmethod
2439
+ def DataTypeGYR(current_payload: bytearray, gyr_res: float):
2440
+ """Decode Gyroscope reading.
2441
+
2442
+ Args:
2443
+ current_payload (bytearray):
2444
+ Bytes array to be decoded.
2445
+ gyr_res (float):
2446
+ Gyroscope sensitivity coefficient.
2447
+
2448
+ Returns:
2449
+ current_data: 3-elements array of float (i.e., x, y, z channels) containing Gyroscope data
2450
+ """
2451
+ #Define 3-elements array of float (i.e., x, y, z channels)
2452
+ current_data = [0.0]*3
2453
+
2454
+ #Iterate across Gyroscope channels
2455
+ for i in range(3):
2456
+
2457
+ #Extract channel raw value (i.e., 2-bytes each)
2458
+ raw_value = current_payload[2*i:2*(i+1)]
2459
+ # Convert to Int16 and apply sensitivity scaling
2460
+ current_data[i] = int.from_bytes(raw_value, byteorder='little', signed=True) * gyr_res
2461
+
2462
+
2463
+ #Return decoded Gyroscope reading
2464
+ return current_data
2465
+
2466
+ @staticmethod
2467
+ def DataTypeAXL(current_payload: bytearray, axl_res: float):
2468
+ """Decode Accelerometer reading.
2469
+
2470
+ Args:
2471
+ current_payload (bytearray):
2472
+ Bytes array to be decoded.
2473
+ axl_res (float):
2474
+ Accelerometer sensitivity coefficient.
2475
+
2476
+ Returns:
2477
+ current_data: 3-elements array of float (i.e., x, y, z channels) containing Accelerometer data
2478
+ """
2479
+ current_data = [0.0] * 3
2480
+
2481
+ #Accelerometer
2482
+ for i in range(3):
2483
+ #Extract channel raw value (i.e., 2-bytes each)
2484
+ raw_value = current_payload[2*i:2*(i+1)]
2485
+ # Convert to Int16 and apply sensitivity scaling
2486
+ current_data[i] = int.from_bytes(raw_value, byteorder='little', signed=True) * axl_res
2487
+
2488
+
2489
+ return current_data
2490
+
2491
+ @staticmethod
2492
+ def DataTypeMAGN(current_payload: bytearray, mag_res: float):
2493
+ """Decode Magnetometer reading.
2494
+
2495
+ Args:
2496
+ current_payload (bytearray):
2497
+ Bytes array to be decoded.
2498
+ mag_res (float):
2499
+ Magnetometer sensitivity coefficient.
2500
+
2501
+ Returns:
2502
+ current_data: 3-elements array of float (i.e., x, y, z channels) containing Magnetometer data
2503
+ """
2504
+ current_data = [0.0] * 3
2505
+
2506
+ #Magnetometer
2507
+ for i in range(3):
2508
+ #Extract channel raw value (i.e., 2-bytes each)
2509
+ raw_value = current_payload[2*i:2*(i+1)]
2510
+ # Convert to Int16 and apply sensitivity scaling
2511
+ current_data[i] = int.from_bytes(raw_value, byteorder='little', signed=True) * mag_res
2512
+
2513
+
2514
+ return current_data
2515
+
2516
+ @staticmethod
2517
+ def DataTypeHDR(current_payload: bytearray, hdr_res: float):
2518
+ """Decode High Dynamic Range (HDR) Accelerometer reading.
2519
+
2520
+ Args:
2521
+ current_payload (bytearray):
2522
+ Bytes array to be decoded.
2523
+ hdr_res (float):
2524
+ High Dynamic Range (HDR) Accelerometer sensitivity coefficient.
2525
+
2526
+ Returns:
2527
+ current_data: 3-elements array of float (i.e., x, y, z channels) containing High Dynamic Range (HDR) Accelerometer data
2528
+ """
2529
+ current_data = [0.0]*3
2530
+ tmp = bytearray(2)
2531
+
2532
+ #Accelerometer HDR
2533
+ for i in range(3):
2534
+ #Extract channel raw value (i.e., 2-bytes each)
2535
+ raw_value = current_payload[2*i:2*(i+1)]
2536
+ # Convert to Int16 and apply sensitivity scaling
2537
+ current_data[i] = int.from_bytes(raw_value, byteorder='little', signed=True) * hdr_res
2538
+
2539
+ return current_data
2540
+
2541
+ @staticmethod
2542
+ def DataTypeOrientation(current_payload: bytearray):
2543
+ """Decode orientation (unit) quaternion
2544
+
2545
+ Args:
2546
+ current_payload (bytearray):
2547
+ Bytes array to be decoded.
2548
+
2549
+ Returns:
2550
+ current_data: 4-elements array of float (i.e., qw, qi, qj, qk) containing Quaternion components
2551
+ """
2552
+ #Define 4-elements array of float (i.e., qw, qi, qj, qk channels)
2553
+ current_data = [0.0] * 4
2554
+
2555
+ #Orientation Quaternion (i.e., UNIT QUATERNION)
2556
+ tempFloat = [0.0] * 3
2557
+ for i in range(3):
2558
+
2559
+ #Extract channel raw value (i.e., 2-bytes each) and convert to Int16
2560
+ tempFloat[i] = int.from_bytes(current_payload[2*i:2*i+2], byteorder='little', signed=True)
2561
+
2562
+ #Keep into account the data resolution
2563
+ tempFloat[i] /= 32767
2564
+
2565
+ #Assign imaginary parts of quaternion
2566
+ current_data[i + 1] = tempFloat[i]
2567
+
2568
+
2569
+ #Compute real component of quaternion given immaginary parts
2570
+ current_data[0] = math.sqrt(1 - (current_data[1] * current_data[1] +
2571
+ current_data[2] * current_data[2] + current_data[3] * current_data[3]))
2572
+
2573
+ #Return decoded Unit Quaternion
2574
+ return current_data
2575
+
2576
+ @staticmethod
2577
+ def GetAnglesFromQuaternion(q):
2578
+ """Compute Euler Angles given a unit quaternion.
2579
+
2580
+ Args:
2581
+ q:
2582
+ Unit quaternion to be converted (4-elements array of float).
2583
+
2584
+ Returns:
2585
+ result: 3-elements array of float (i.e., roll, pitch and yaw) containing Euler Angles
2586
+ """
2587
+ result = [0.0] * 3
2588
+
2589
+ #Compute Euler Angles given a unit quaternion
2590
+ result[0] = float(math.atan2(2 * (q[0] * q[1] + q[2] * q[3]), 1 - 2 * (q[1] * q[1] + q[2] * q[2])) * 180 / math.pi)
2591
+ result[1] = float(math.asin(2 * q[0] * q[2] - 2 * q[3] * q[1]) * 180 / math.pi)
2592
+ result[2] = float(math.atan2(2 * (q[0] * q[3] + q[1] * q[2]), 1 - 2 * (q[2] * q[2] + q[3] * q[3])) * 180 / math.pi)
2593
+
2594
+ return result
2595
+
2596
+ @staticmethod
2597
+ def DataTypeTimestamp(current_payload: bytearray):
2598
+ """Decode timestamp.
2599
+
2600
+ Args:
2601
+ current_payload (bytearray):
2602
+ Bytes array to be decoded.
2603
+
2604
+ Returns:
2605
+ currentData: A unsigned long integer value representing the timestamp in epoch format.
2606
+ """
2607
+ #Set current data variable to 0
2608
+ currentData = 0
2609
+
2610
+ #Get raw byte array representation to be decoded
2611
+ tmp = bytearray(8)
2612
+
2613
+ # Copy the first 6 bytes of currentPayload into tmp
2614
+ tmp[:6] = current_payload[:6]
2615
+
2616
+ # Convert the first 6 bytes of tmp to a 64-bit unsigned integer
2617
+ tempTime = struct.unpack("<Q", tmp)[0] & 0x0000FFFFFFFFFFFF
2618
+
2619
+ # Add the reference epoch (in milliseconds) to tempTime
2620
+ tempTime += MH.REFERENCE_EPOCH * 1000
2621
+
2622
+ currentData = tempTime
2623
+
2624
+ return currentData
2625
+
2626
+ @staticmethod
2627
+ def DataTypeTempHum(current_payload: bytearray):
2628
+ """Decode temperature and humidity reading.
2629
+
2630
+ Args:
2631
+ current_payload (bytearray):
2632
+ Bytes array to be decoded.
2633
+
2634
+ Returns:
2635
+ current_data: An array of float (i.e., temperature, humidity)
2636
+ """
2637
+ #Define 2-elements array of float (i.e., temperature, humidity)
2638
+ current_data = [0.0] * 2
2639
+
2640
+ #Temperature
2641
+ #Define temporary internal variable used for data conversion
2642
+ tmp = bytearray(2)
2643
+ # Copy the first 2 bytes of currentPayload into tmp
2644
+ tmp[:2] = current_payload[0:2]
2645
+ # Convert to UInt16 and apply sensitivity scaling
2646
+ current_data[0] = int.from_bytes(tmp, byteorder='little', signed=False)
2647
+ current_data[0] *= 0.002670
2648
+ current_data[0] -= 45
2649
+
2650
+ #Humidity
2651
+ tmp[:2] = current_payload[2:4]
2652
+ # Convert to UInt16 and apply sensitivity scaling
2653
+ current_data[1] = int.from_bytes(tmp, byteorder='little', signed=False)
2654
+ current_data[1] *= 0.001907
2655
+ current_data[1] -= 6
2656
+
2657
+ #Return decoded data
2658
+ return current_data
2659
+
2660
+ @staticmethod
2661
+ def DataTypeTempPress(current_payload: bytearray):
2662
+ """Decode temperature and barometric pressure reading.
2663
+
2664
+ Args:
2665
+ current_payload (bytearray):
2666
+ Bytes array to be decoded.
2667
+
2668
+ Returns:
2669
+ current_data: An array of float (i.e., temperature, pressure)
2670
+ """
2671
+ #Define 2-elements array of float (i.e., temperature, pressure)
2672
+ current_data = [0.0] * 2
2673
+
2674
+ #Temperature
2675
+ #Define temporary internal variable used for data conversion
2676
+ tmp = bytearray(2)
2677
+ # Copy the first 2 bytes of currentPayload into tmp
2678
+ tmp[:2] = current_payload[3:5]
2679
+ # Convert to UInt16 and apply sensitivity scaling
2680
+ current_data[0] = int.from_bytes(tmp, byteorder='little', signed=False)
2681
+ current_data[0] /= 100
2682
+
2683
+ #Pressure
2684
+ tmp = bytearray(3)
2685
+ tmp[:3] = current_payload[0:3]
2686
+ # Convert to UInt16 and apply sensitivity scaling
2687
+ current_data[1] = int.from_bytes(tmp, byteorder='little', signed=False)
2688
+ current_data[1] /= 4096
2689
+
2690
+ #Return decoded data
2691
+ return current_data
2692
+
2693
+ @staticmethod
2694
+ def DataTypeRange(current_payload: bytearray):
2695
+ """Decode Light sensor data.
2696
+
2697
+ Args:
2698
+ current_payload (bytearray):
2699
+ Bytes array to be decoded.
2700
+
2701
+ Returns:
2702
+ current_data: A 4-element float array (i.e., range, vis, ir, lux)
2703
+ """
2704
+ current_data = [0] * 4
2705
+
2706
+ #Define temporary internal variable used for data conversion
2707
+ tmp = bytearray(2)
2708
+ # Copy the first 2 bytes of currentPayload into tmp
2709
+ tmp[:2] = current_payload[0:2]
2710
+ # Convert to UInt16
2711
+ range = int.from_bytes(tmp, byteorder='little', signed=False)
2712
+
2713
+ tmp[:2] = current_payload[2:4]
2714
+ # Convert to UInt16
2715
+ vis = int.from_bytes(tmp, byteorder='little', signed=False)
2716
+
2717
+ tmp[:2] = current_payload[4:6]
2718
+ # Convert to UInt16
2719
+ ir = int.from_bytes(tmp, byteorder='little', signed=False)
2720
+
2721
+ current_data[0] = range
2722
+ current_data[1] = vis
2723
+ current_data[2] = ir
2724
+
2725
+ lux = 0.0
2726
+ if vis > 0:
2727
+ if (ir / vis < 0.109):
2728
+ lux = 1.534 * vis - 3.759 * ir
2729
+ elif (ir / vis < 0.429):
2730
+ lux = 1.339 * vis - 1.972 * ir
2731
+ elif (ir / vis < 0.95 * 1.45):
2732
+ lux = 0.701 * vis - 0.483 * ir
2733
+ elif (ir / vis < 1.5 * 1.45):
2734
+ lux = 2.0 * 0.701 * vis - 1.18 * 0.483 * ir
2735
+ elif (ir / vis < 2.5 * 1.45):
2736
+ lux = 4.0 * 0.701 * vis - 1.33 * 0.483 * ir
2737
+ else:
2738
+ lux = 8.0 * 0.701 * vis
2739
+ else:
2740
+ # manage division by zero
2741
+ lux = 0.0
2742
+
2743
+ current_data[3] = round(lux)
2744
+
2745
+ return current_data
2746
+
2747
+ @staticmethod
2748
+ def DataTypeSound(current_payload: bytearray):
2749
+ """Decode Sound data. TO BE IMPLEMENTED
2750
+ """
2751
+ current_data = 0.0
2752
+
2753
+ return current_data
2754
+
2755
+ @staticmethod
2756
+ def DecodeMEMSConfiguration(code: int):
2757
+ """Decode sensors configuration in terms of full scale and sensitivity.
2758
+
2759
+ Args:
2760
+ code (int):
2761
+ 24-bit unsigned integer code.
2762
+
2763
+ Returns:
2764
+ (gyrConfig, axlConfig, magConfig, hdrConfig):
2765
+ - gyrConfig: Output reference to Gyroscope configuration.
2766
+ - axlConfig: Output reference to Accelerometer configuration.
2767
+ - magConfig: Output reference to Magnetometer configuration.
2768
+ - hdrConfig: Output reference to HDR Accelerometer configuration.
2769
+ """
2770
+ #Apply bitwise mask to get sensor full scale code (i.e., gyr, axl, hdr, mag LSB-order)
2771
+ gyrCode = (code & MH.MEMS_FullScaleMask.SENSORSFS_MASK_GYRO.value)
2772
+ axlCode = (code & MH.MEMS_FullScaleMask.SENSORSFS_MASK_AXL.value)
2773
+ hdrCode = (code & MH.MEMS_FullScaleMask.SENSORSFS_MASK_HDR.value)
2774
+ magCode = (code & MH.MEMS_FullScaleMask.SENSORSFS_MASK_MAGN.value)
2775
+
2776
+ #Gyroscope
2777
+ gyrConfig = MH.Gyroscope_CFG[gyrCode]
2778
+ #Accelerometer
2779
+ axlConfig = MH.Accelerometer_CFG[axlCode]
2780
+ #Magnetometer
2781
+ magConfig = MH.Magnetometer_CFG[magCode]
2782
+ # HDR Accelerometer
2783
+ hdrConfig = MH.AccelerometerHDR_CFG[hdrCode]
2784
+
2785
+ return gyrConfig, axlConfig, magConfig, hdrConfig
2786
+
2787
+ @staticmethod
2788
+ def DataModeToString(code: int) -> str:
2789
+ """Create a string representation of data acquisition mode.
2790
+
2791
+ Args:
2792
+ code (int):
2793
+ 32-bit unsigned integer code.
2794
+
2795
+ Returns:
2796
+ modeString:
2797
+ string representation of data acquisition mode.
2798
+ """
2799
+ modeString = ""
2800
+
2801
+ #Build acquisition mode string description
2802
+ if ((code & ctypes.c_uint32(MH.DataMode.DATA_MODE_GYRO.value).value) > 0):
2803
+ if (len(modeString)>0):
2804
+ modeString += " | "
2805
+ modeString += "GYR"
2806
+ if ((code & ctypes.c_uint32(MH.DataMode.DATA_MODE_AXL.value).value) > 0):
2807
+ if (len(modeString)>0):
2808
+ modeString += " | "
2809
+ modeString += "AXL"
2810
+ if ((code & ctypes.c_uint32(MH.DataMode.DATA_MODE_HDR.value).value) > 0):
2811
+ if (len(modeString)>0):
2812
+ modeString += " | "
2813
+ modeString += "HDR"
2814
+ if ((code & ctypes.c_uint32(MH.DataMode.DATA_MODE_MAGN.value).value) > 0):
2815
+ if (len(modeString)>0):
2816
+ modeString += " | "
2817
+ modeString += "MAG"
2818
+ if ((code & ctypes.c_uint32(MH.DataMode.DATA_MODE_ORIENTATION.value).value) > 0):
2819
+ if (len(modeString)>0):
2820
+ modeString += " | "
2821
+ modeString += "QUAT"
2822
+ if ((code & ctypes.c_uint32(MH.DataMode.DATA_MODE_TIMESTAMP.value).value) > 0):
2823
+ if (len(modeString)>0):
2824
+ modeString += " | "
2825
+ modeString += "TIME"
2826
+ if ((code & ctypes.c_uint32(MH.DataMode.DATA_MODE_TEMP_HUM.value).value) > 0):
2827
+ if (len(modeString)>0):
2828
+ modeString += " | "
2829
+ modeString += "TH"
2830
+ if ((code & ctypes.c_uint32(MH.DataMode.DATA_MODE_TEMP_PRESS.value).value) > 0):
2831
+ if (len(modeString)>0):
2832
+ modeString += " | "
2833
+ modeString += "TP"
2834
+ if ((code & ctypes.c_uint32(MH.DataMode.DATA_MODE_RANGE.value).value) > 0):
2835
+ if (len(modeString)>0):
2836
+ modeString += " | "
2837
+ modeString += "RANGE"
2838
+ if ((code & ctypes.c_uint32(MH.DataMode.DATA_MODE_SOUND.value).value) > 0):
2839
+ if (len(modeString)>0):
2840
+ modeString += " | "
2841
+ modeString += "MIC"
2842
+
2843
+ if (modeString != ""):
2844
+ modeString.join(", ")
2845
+
2846
+ return modeString
2847
+
2848
+ @staticmethod
2849
+ def DataFrequencyToString(frequency: MH.DataFrequency) -> str:
2850
+ """Create a string representation of data acquisition mode.
2851
+
2852
+ Args:
2853
+ frequency (Muse_HW.DataFrequency):
2854
+ DataFrequency value of sampling frequency
2855
+
2856
+ Returns:
2857
+ string representation of data acquisition frequency.
2858
+ """
2859
+ if frequency == MH.DataFrequency.DATA_FREQ_25Hz :
2860
+ return "25 Hz"
2861
+
2862
+ if frequency == MH.DataFrequency.DATA_FREQ_50Hz:
2863
+ return "50 Hz"
2864
+
2865
+ if frequency == MH.DataFrequency.DATA_FREQ_100Hz:
2866
+ return "100 Hz"
2867
+
2868
+ if frequency == MH.DataFrequency.DATA_FREQ_200Hz:
2869
+ return "200 Hz"
2870
+
2871
+ if frequency == MH.DataFrequency.DATA_FREQ_400Hz:
2872
+ return "400 Hz"
2873
+
2874
+ if frequency == MH.DataFrequency.DATA_FREQ_800Hz:
2875
+ return "800 Hz"
2876
+
2877
+ if frequency == MH.DataFrequency.DATA_FREQ_1600Hz:
2878
+ return "1600 Hz"
2879
+ else:
2880
+ return "NONE"
2881
+
2882
+ # end DECODING FUNCTIONS