slm_open_api_package 0.2.10__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.
Files changed (37) hide show
  1. slm_api/CPB_example.py +74 -0
  2. slm_api/__init__.py +5 -0
  3. slm_api/enums/fast_log_intervals_enum.py +16 -0
  4. slm_api/enums/sequence_id_enum.py +352 -0
  5. slm_api/example_multi.py +78 -0
  6. slm_api/fast_log_example.py +172 -0
  7. slm_api/flac_example.py +164 -0
  8. slm_api/helpers/FigureHandler.py +65 -0
  9. slm_api/helpers/Leq.py +87 -0
  10. slm_api/helpers/__init__.py +5 -0
  11. slm_api/helpers/buffer.py +27 -0
  12. slm_api/helpers/data_handler.py +94 -0
  13. slm_api/helpers/fft.py +38 -0
  14. slm_api/helpers/flac_stream_2_samples.py +64 -0
  15. slm_api/helpers/https_requests.py +79 -0
  16. slm_api/helpers/measurment_handler.py +20 -0
  17. slm_api/helpers/sequence_handler.py +43 -0
  18. slm_api/helpers/stream_handler.py +105 -0
  19. slm_api/helpers/stream_handlers.py +434 -0
  20. slm_api/helpers/tmpFileStateMachine.py +93 -0
  21. slm_api/helpers/websocket_handler.py +12 -0
  22. slm_api/helpers/webxi_helper_functions.py +79 -0
  23. slm_api/helpers/webxi_root.pem +24 -0
  24. slm_api/mp3_example.py +165 -0
  25. slm_api/multi_device_flac.py +105 -0
  26. slm_api/multi_instrument_example.py +74 -0
  27. slm_api/screen_record_example.py +64 -0
  28. slm_api/webxi/README.md +25 -0
  29. slm_api/webxi/__init__.py +5 -0
  30. slm_api/webxi/webxi-header.ksy +32 -0
  31. slm_api/webxi/webxi-stream.ksy +342 -0
  32. slm_api/webxi/webxi_header.py +42 -0
  33. slm_api/webxi/webxi_stream.py +450 -0
  34. slm_open_api_package-0.2.10.dist-info/METADATA +206 -0
  35. slm_open_api_package-0.2.10.dist-info/RECORD +37 -0
  36. slm_open_api_package-0.2.10.dist-info/WHEEL +4 -0
  37. slm_open_api_package-0.2.10.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,450 @@
1
+ # This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
2
+
3
+ from packaging.version import Version as parse_version
4
+ import kaitaistruct
5
+ from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
6
+ from enum import Enum
7
+
8
+
9
+ if parse_version(kaitaistruct.__version__) < parse_version('0.9'):
10
+ raise Exception("Incompatible Kaitai Struct Python API: 0.9 or later is required, but you have %s" % (kaitaistruct.__version__))
11
+
12
+ class WebxiStream(KaitaiStruct):
13
+ def __init__(self, _io, _parent=None, _root=None):
14
+ self._io = _io
15
+ self._parent = _parent
16
+ self._root = _root if _root else self
17
+ self._read()
18
+
19
+ def _read(self):
20
+ self.header = self._root.Header(self._io, self, self._root)
21
+ _on = self.header.message_type
22
+ if _on == self._root.Header.EMessageType.e_state:
23
+ self._raw_content = self._io.read_bytes(self.header.content_length)
24
+ _io__raw_content = KaitaiStream(BytesIO(self._raw_content))
25
+ self.content = self._root.State(_io__raw_content, self, self._root)
26
+ elif _on == self._root.Header.EMessageType.e_sequence_data:
27
+ self._raw_content = self._io.read_bytes(self.header.content_length)
28
+ _io__raw_content = KaitaiStream(BytesIO(self._raw_content))
29
+ self.content = self._root.SequenceData(_io__raw_content, self, self._root)
30
+ elif _on == self._root.Header.EMessageType.e_debug:
31
+ self._raw_content = self._io.read_bytes(self.header.content_length)
32
+ _io__raw_content = KaitaiStream(BytesIO(self._raw_content))
33
+ self.content = self._root.Debug(_io__raw_content, self, self._root)
34
+ elif _on == self._root.Header.EMessageType.e_sync:
35
+ self._raw_content = self._io.read_bytes(self.header.content_length)
36
+ _io__raw_content = KaitaiStream(BytesIO(self._raw_content))
37
+ self.content = self._root.Sync(_io__raw_content, self, self._root)
38
+ elif _on == self._root.Header.EMessageType.e_aux_sequence_data:
39
+ self._raw_content = self._io.read_bytes(self.header.content_length)
40
+ _io__raw_content = KaitaiStream(BytesIO(self._raw_content))
41
+ self.content = self._root.AuxSequenceData(_io__raw_content, self, self._root)
42
+ elif _on == self._root.Header.EMessageType.e_package:
43
+ self._raw_content = self._io.read_bytes(self.header.content_length)
44
+ _io__raw_content = KaitaiStream(BytesIO(self._raw_content))
45
+ self.content = self._root.Package(_io__raw_content, self, self._root)
46
+ elif _on == self._root.Header.EMessageType.e_data_quality:
47
+ self._raw_content = self._io.read_bytes(self.header.content_length)
48
+ _io__raw_content = KaitaiStream(BytesIO(self._raw_content))
49
+ self.content = self._root.DataQuality(_io__raw_content, self, self._root)
50
+ elif _on == self._root.Header.EMessageType.e_node:
51
+ self._raw_content = self._io.read_bytes(self.header.content_length)
52
+ _io__raw_content = KaitaiStream(BytesIO(self._raw_content))
53
+ self.content = self._root.Node(_io__raw_content, self, self._root)
54
+ elif _on == self._root.Header.EMessageType.e_status:
55
+ self._raw_content = self._io.read_bytes(self.header.content_length)
56
+ _io__raw_content = KaitaiStream(BytesIO(self._raw_content))
57
+ self.content = self._root.Status(_io__raw_content, self, self._root)
58
+ elif _on == self._root.Header.EMessageType.e_trigger:
59
+ self._raw_content = self._io.read_bytes(self.header.content_length)
60
+ _io__raw_content = KaitaiStream(BytesIO(self._raw_content))
61
+ self.content = self._root.Trigger(_io__raw_content, self, self._root)
62
+ else:
63
+ self.content = self._io.read_bytes(self.header.content_length)
64
+
65
+ class NodeChange(KaitaiStruct):
66
+ def __init__(self, _io, _parent=None, _root=None):
67
+ self._io = _io
68
+ self._parent = _parent
69
+ self._root = _root if _root else self
70
+ self._read()
71
+
72
+ def _read(self):
73
+ self.flags = self._io.read_u2le()
74
+ self.reserved = self._io.read_u2le()
75
+ self.path = self._root.String(self._io, self, self._root)
76
+ self.json = self._root.String(self._io, self, self._root)
77
+
78
+
79
+ class Package(KaitaiStruct):
80
+ def __init__(self, _io, _parent=None, _root=None):
81
+ self._io = _io
82
+ self._parent = _parent
83
+ self._root = _root if _root else self
84
+ self._read()
85
+
86
+ def _read(self):
87
+ self.number_of_packages = self._io.read_u2le()
88
+ self.reserved = self._io.read_u2le()
89
+ self.packages = [None] * (self.number_of_packages)
90
+ for i in range(self.number_of_packages):
91
+ self.packages[i] = self._root.PackageBlock(self._io, self, self._root)
92
+
93
+
94
+
95
+ class AuxValue(KaitaiStruct):
96
+ def __init__(self, _io, _parent=None, _root=None):
97
+ self._io = _io
98
+ self._parent = _parent
99
+ self._root = _root if _root else self
100
+ self._read()
101
+
102
+ def _read(self):
103
+ self.relative_time = self._io.read_u4le()
104
+ self.values = self._root.Value(self._io, self, self._root)
105
+
106
+
107
+ class FlacSequenceDataBlock(KaitaiStruct):
108
+ def __init__(self, _io, _parent=None, _root=None):
109
+ self._io = _io
110
+ self._parent = _parent
111
+ self._root = _root if _root else self
112
+ self._read()
113
+
114
+ def _read(self):
115
+ self.number_of_sequences = self._io.read_u2le()
116
+ self.sequence_ids = [None] * (self.number_of_sequences)
117
+ for i in range(self.number_of_sequences):
118
+ self.sequence_ids[i] = self._io.read_u2le()
119
+
120
+ self.frame_length = self._io.read_u4le()
121
+ self.frame = [None] * (self.frame_length)
122
+ for i in range(self.frame_length):
123
+ self.frame[i] = self._io.read_u1()
124
+
125
+
126
+
127
+ class AuxSequenceDataBlock(KaitaiStruct):
128
+ def __init__(self, _io, _parent=None, _root=None):
129
+ self._io = _io
130
+ self._parent = _parent
131
+ self._root = _root if _root else self
132
+ self._read()
133
+
134
+ def _read(self):
135
+ self.sequence_id = self._io.read_u2le()
136
+ self.number_of_values = self._io.read_u2le()
137
+ self.values = [None] * (self.number_of_values)
138
+ for i in range(self.number_of_values):
139
+ self.values[i] = self._root.AuxValue(self._io, self, self._root)
140
+
141
+
142
+
143
+ class State(KaitaiStruct):
144
+
145
+ class EState(Enum):
146
+ initial = 0
147
+ error = 1
148
+ deactivated = 2
149
+ activated = 3
150
+ running = 4
151
+ ramping_down = 5
152
+ pause = 6
153
+ stopping = 7
154
+ result_measured = 8
155
+ result_loaded = 9
156
+ def __init__(self, _io, _parent=None, _root=None):
157
+ self._io = _io
158
+ self._parent = _parent
159
+ self._root = _root if _root else self
160
+ self._read()
161
+
162
+ def _read(self):
163
+ self.state = KaitaiStream.resolve_enum(self._root.State.EState, self._io.read_u2le())
164
+ self.reserved = self._io.read_u2le()
165
+ self.application_name = self._root.String(self._io, self, self._root)
166
+
167
+
168
+ class DataQuality(KaitaiStruct):
169
+ def __init__(self, _io, _parent=None, _root=None):
170
+ self._io = _io
171
+ self._parent = _parent
172
+ self._root = _root if _root else self
173
+ self._read()
174
+
175
+ def _read(self):
176
+ self.number_of_qualities = self._io.read_u2le()
177
+ self.reserved = self._io.read_u2le()
178
+ self.qualities = [None] * (self.number_of_qualities)
179
+ for i in range(self.number_of_qualities):
180
+ self.qualities[i] = self._root.QualityBlock(self._io, self, self._root)
181
+
182
+
183
+
184
+ class AuxSequenceData(KaitaiStruct):
185
+ def __init__(self, _io, _parent=None, _root=None):
186
+ self._io = _io
187
+ self._parent = _parent
188
+ self._root = _root if _root else self
189
+ self._read()
190
+
191
+ def _read(self):
192
+ self.number_of_sequences = self._io.read_u2le()
193
+ self.reserved = self._io.read_u2le()
194
+ self.aux_sequence_data_blocks = [None] * (self.number_of_sequences)
195
+ for i in range(self.number_of_sequences):
196
+ self.aux_sequence_data_blocks[i] = self._root.AuxSequenceDataBlock(self._io, self, self._root)
197
+
198
+
199
+
200
+ class SequenceData(KaitaiStruct):
201
+
202
+ class EMessageFormat(Enum):
203
+ raw = 0
204
+ mp3 = 1
205
+ flac = 2
206
+ def __init__(self, _io, _parent=None, _root=None):
207
+ self._io = _io
208
+ self._parent = _parent
209
+ self._root = _root if _root else self
210
+ self._read()
211
+
212
+ def _read(self):
213
+ self.number_of_blocks = self._io.read_u2le()
214
+ self.message_format = KaitaiStream.resolve_enum(self._root.SequenceData.EMessageFormat, self._io.read_u1())
215
+ self.reserved = self._io.read_u1()
216
+ self.sequence_blocks = [None] * (self.number_of_blocks)
217
+ for i in range(self.number_of_blocks):
218
+ _on = self.message_format
219
+ if _on == self._root.SequenceData.EMessageFormat.raw:
220
+ self.sequence_blocks[i] = self._root.RawSequenceDataBlock(self._io, self, self._root)
221
+ elif _on == self._root.SequenceData.EMessageFormat.mp3:
222
+ self.sequence_blocks[i] = self._root.Mp3SequenceDataBlock(self._io, self, self._root)
223
+ elif _on == self._root.SequenceData.EMessageFormat.flac:
224
+ self.sequence_blocks[i] = self._root.FlacSequenceDataBlock(self._io, self, self._root)
225
+
226
+
227
+
228
+ class String(KaitaiStruct):
229
+ def __init__(self, _io, _parent=None, _root=None):
230
+ self._io = _io
231
+ self._parent = _parent
232
+ self._root = _root if _root else self
233
+ self._read()
234
+
235
+ def _read(self):
236
+ self.count = self._io.read_u4le()
237
+ self.content = (self._io.read_bytes(self.count)).decode(u"UTF-8")
238
+
239
+
240
+ class PackageBlock(KaitaiStruct):
241
+ def __init__(self, _io, _parent=None, _root=None):
242
+ self._io = _io
243
+ self._parent = _parent
244
+ self._root = _root if _root else self
245
+ self._read()
246
+
247
+ def _read(self):
248
+ self.package_id = self._io.read_u2le()
249
+ self.data_length = self._io.read_u2le()
250
+ self.data = [None] * (self.data_length)
251
+ for i in range(self.data_length):
252
+ self.data[i] = self._io.read_u1()
253
+
254
+
255
+
256
+ class Debug(KaitaiStruct):
257
+ def __init__(self, _io, _parent=None, _root=None):
258
+ self._io = _io
259
+ self._parent = _parent
260
+ self._root = _root if _root else self
261
+ self._read()
262
+
263
+ def _read(self):
264
+ self.message = self._root.String(self._io, self, self._root)
265
+
266
+
267
+ class TimeFamily(KaitaiStruct):
268
+ def __init__(self, _io, _parent=None, _root=None):
269
+ self._io = _io
270
+ self._parent = _parent
271
+ self._root = _root if _root else self
272
+ self._read()
273
+
274
+ def _read(self):
275
+ self.k = self._io.read_u1()
276
+ self.l = self._io.read_u1()
277
+ self.m = self._io.read_u1()
278
+ self.n = self._io.read_u1()
279
+
280
+
281
+ class Status(KaitaiStruct):
282
+
283
+ class EChannelType(Enum):
284
+ todo = 0
285
+
286
+ class EStatusType(Enum):
287
+ todo1 = 0
288
+ def __init__(self, _io, _parent=None, _root=None):
289
+ self._io = _io
290
+ self._parent = _parent
291
+ self._root = _root if _root else self
292
+ self._read()
293
+
294
+ def _read(self):
295
+ self.channel_type = KaitaiStream.resolve_enum(self._root.Status.EChannelType, self._io.read_u2le())
296
+ self.channel_id = self._io.read_u2le()
297
+ self.status_type = KaitaiStream.resolve_enum(self._root.Status.EStatusType, self._io.read_u2le())
298
+ self.reserved = self._io.read_u2le()
299
+ self.value1 = self._io.read_s4le()
300
+ self.value2 = self._io.read_s4le()
301
+ self.str = self._root.String(self._io, self, self._root)
302
+
303
+
304
+ class QualityBlock(KaitaiStruct):
305
+ def __init__(self, _io, _parent=None, _root=None):
306
+ self._io = _io
307
+ self._parent = _parent
308
+ self._root = _root if _root else self
309
+ self._read()
310
+
311
+ def _read(self):
312
+ self.quality_id = self._io.read_u2le()
313
+ self.validity = self._io.read_u2le()
314
+ self.reserved = self._io.read_u4le()
315
+
316
+
317
+ class Sync(KaitaiStruct):
318
+ def __init__(self, _io, _parent=None, _root=None):
319
+ self._io = _io
320
+ self._parent = _parent
321
+ self._root = _root if _root else self
322
+ self._read()
323
+
324
+ def _read(self):
325
+ self.sync_id = self._io.read_u4le()
326
+
327
+
328
+ class Header(KaitaiStruct):
329
+
330
+ class EMessageType(Enum):
331
+ e_sequence_data = 1
332
+ e_data_quality = 2
333
+ e_state = 3
334
+ e_status = 4
335
+ e_trigger = 5
336
+ e_node = 6
337
+ e_sync = 7
338
+ e_debug = 9
339
+ e_package = 10
340
+ e_aux_sequence_data = 11
341
+ def __init__(self, _io, _parent=None, _root=None):
342
+ self._io = _io
343
+ self._parent = _parent
344
+ self._root = _root if _root else self
345
+ self._read()
346
+
347
+ def _read(self):
348
+ self.magic = self._io.read_bytes(2)
349
+ if not self.magic == b"\x42\x4B":
350
+ raise kaitaistruct.ValidationNotEqualError(b"\x42\x4B", self.magic, self._io, u"/types/header/seq/0")
351
+ self.header_length = self._io.read_u2le()
352
+ self.message_type = KaitaiStream.resolve_enum(self._root.Header.EMessageType, self._io.read_u2le())
353
+ self.content_version = self._io.read_u2le()
354
+ self.reserved2 = self._io.read_u4le()
355
+ self.time = self._io.read_u8le()
356
+ self.content_length = self._io.read_u4le()
357
+
358
+
359
+ class Node(KaitaiStruct):
360
+ def __init__(self, _io, _parent=None, _root=None):
361
+ self._io = _io
362
+ self._parent = _parent
363
+ self._root = _root if _root else self
364
+ self._read()
365
+
366
+ def _read(self):
367
+ self.n_changes = self._io.read_u2le()
368
+ self.reserved = self._io.read_u2le()
369
+ self.changes = [None] * (self.n_changes)
370
+ for i in range(self.n_changes):
371
+ self.changes[i] = self._root.NodeChange(self._io, self, self._root)
372
+
373
+
374
+
375
+ class Value(KaitaiStruct):
376
+ def __init__(self, _io, _parent=None, _root=None):
377
+ self._io = _io
378
+ self._parent = _parent
379
+ self._root = _root if _root else self
380
+ self._read()
381
+
382
+ def _read(self):
383
+ self.value1 = self._io.read_u1()
384
+ self.value2 = self._io.read_u1()
385
+ self.value3 = self._io.read_s1()
386
+ self.value4 = self._io.read_s1()
387
+
388
+ @property
389
+ def calc_value(self):
390
+ if hasattr(self, '_m_calc_value'):
391
+ return self._m_calc_value if hasattr(self, '_m_calc_value') else None
392
+
393
+ self._m_calc_value = ((self.value1 + (self.value2 << 8)) + (self.value3 << 16))
394
+ return self._m_calc_value if hasattr(self, '_m_calc_value') else None
395
+
396
+
397
+ class Mp3SequenceDataBlock(KaitaiStruct):
398
+ def __init__(self, _io, _parent=None, _root=None):
399
+ self._io = _io
400
+ self._parent = _parent
401
+ self._root = _root if _root else self
402
+ self._read()
403
+
404
+ def _read(self):
405
+ self.number_of_sequences = self._io.read_u2le()
406
+ self.sequence_ids = [None] * (self.number_of_sequences)
407
+ for i in range(self.number_of_sequences):
408
+ self.sequence_ids[i] = self._io.read_u2le()
409
+
410
+ self.frame_length = self._io.read_u4le()
411
+ self.frame = [None] * (self.frame_length)
412
+ for i in range(self.frame_length):
413
+ self.frame[i] = self._io.read_u1()
414
+
415
+
416
+
417
+ class RawSequenceDataBlock(KaitaiStruct):
418
+ def __init__(self, _io, _parent=None, _root=None):
419
+ self._io = _io
420
+ self._parent = _parent
421
+ self._root = _root if _root else self
422
+ self._read()
423
+
424
+ def _read(self):
425
+ self.sequence_id = self._io.read_u2le()
426
+ self.value_length = self._io.read_u4le()
427
+ self.values = [None] * (self.value_length)
428
+ for i in range(self.value_length):
429
+ self.values[i] = self._io.read_u1()
430
+
431
+
432
+
433
+ class Trigger(KaitaiStruct):
434
+
435
+ class ETriggerType(Enum):
436
+ unkown = 0
437
+ level = 1
438
+ start = 2
439
+ def __init__(self, _io, _parent=None, _root=None):
440
+ self._io = _io
441
+ self._parent = _parent
442
+ self._root = _root if _root else self
443
+ self._read()
444
+
445
+ def _read(self):
446
+ self.sequence_id = self._io.read_u2le()
447
+ self.trigger_type = KaitaiStream.resolve_enum(self._root.Trigger.ETriggerType, self._io.read_u2le())
448
+
449
+
450
+
@@ -0,0 +1,206 @@
1
+ Metadata-Version: 2.3
2
+ Name: slm_open_api_package
3
+ Version: 0.2.10
4
+ Summary: Add your description here
5
+ Author: Sofus Vingaard
6
+ Author-email: Sofus Vingaard <sofus.krebsvingaard@hbkworld.com>
7
+ Requires-Dist: requests
8
+ Requires-Dist: numpy
9
+ Requires-Dist: websockets
10
+ Requires-Dist: kaitaistruct
11
+ Requires-Dist: packaging
12
+ Requires-Dist: pyqtgraph
13
+ Requires-Dist: pyqt5
14
+ Requires-Dist: pyqt5-qt5<5.15.11
15
+ Requires-Dist: miniaudio
16
+ Requires-Dist: matplotlib>=3.11.1
17
+ Requires-Dist: cryptography>=50.0.1
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+
21
+ # SLM Open API Package
22
+
23
+ Python helpers for streaming measurements and audio from an HBK sound level meter (SLM) through WebXi.
24
+
25
+ This PyPI package is made for the [Open Interface for Sound Level Meter](https://github.com/hbkworld/Open-Interface-for-Sound-Level-Meter) repository. Refer to that repository for more detailed usage information and additional examples.
26
+
27
+
28
+
29
+ ## Install
30
+
31
+ ```powershell
32
+ pip install slm_open_api_package
33
+ ```
34
+
35
+ Or add it to an `uv` project:
36
+
37
+ ```powershell
38
+ uv add slm_open_api_package
39
+ ```
40
+
41
+ The package requires Python 3.12 or newer and a network connection to a compatible SLM.
42
+
43
+ ## Connect To A Device
44
+
45
+ Give every stream one `host`, either an IP address or host name:
46
+
47
+ ```python
48
+ host = "192.168.1.183"
49
+ ```
50
+
51
+ The bundled examples use `set_host()` to read a host from a local `slm_ip` file. If the file is missing, it prompts once and creates it:
52
+
53
+ ```python
54
+ from slm_api.helpers import webxi_helper_functions as webxi_helper
55
+
56
+ host = webxi_helper.set_host(__file__)
57
+ ```
58
+
59
+ ## Basic Leq Stream
60
+
61
+ `WebXiStreamHandler` is the main API. `mode="leq"` is the default and streams one sequence selected by `sequenceID`.
62
+
63
+ ```python
64
+ from slm_api.enums.sequence_id_enum import SequenceIdEnums
65
+ from slm_api.helpers.stream_handlers import WebXiStreamHandler
66
+
67
+ streamer = WebXiStreamHandler(
68
+ host="192.168.1.183",
69
+ sequenceID=SequenceIdEnums.LAeq.value,
70
+ mode="leq",
71
+ saving="csv",
72
+ saving_path="saved_data/laeq",
73
+ )
74
+
75
+ try:
76
+ streamer.startStream()
77
+ except KeyboardInterrupt:
78
+ streamer.stopStream()
79
+ ```
80
+
81
+ `stopStream()` stops the measurement, removes the device stream, and writes buffered saved data. Supported saving formats are `"csv"`, `"json"`, and `"pickle"`; their extensions are added automatically.
82
+
83
+ ## Multiple Sequences
84
+
85
+ Based on [example_multi.py](src/slm_api/example_multi.py), use `mode="multi"` for several enabled broadband sequences in a single stream:
86
+
87
+ ```python
88
+ from slm_api.helpers.data_handler import DataHandler
89
+ from slm_api.helpers.stream_handlers import WebXiStreamHandler
90
+
91
+
92
+ class PrintHandler(DataHandler):
93
+ def handle(self, *, host, name, value, **data):
94
+ print(f"{host} {name}: {value}")
95
+
96
+
97
+ streamer = WebXiStreamHandler(
98
+ host="192.168.1.183",
99
+ sequenceNames=["LAeq", "LCeq", "LAF"],
100
+ mode="multi",
101
+ saving="json",
102
+ saving_path="saved_data/broadband",
103
+ )
104
+ streamer.setDataHandler(PrintHandler())
105
+
106
+ try:
107
+ streamer.startStream()
108
+ except KeyboardInterrupt:
109
+ streamer.stopStream()
110
+ ```
111
+
112
+ Enable the requested sequences and frequency weightings on the SLM before starting the stream. Each `DataHandler.handle()` call receives the source `host`, `name`, and `value`. The `saving=` handler continues saving when a custom handler is attached.
113
+
114
+ ## Fast Logging
115
+
116
+ Based on [fast_log_example.py](src/slm_api/fast_log_example.py), use `mode="fast_logging"` for fast broadband samples:
117
+
118
+ ```python
119
+ from slm_api.enums.fast_log_intervals_enum import FastLogInterval
120
+ from slm_api.enums.sequence_id_enum import SequenceIdEnums
121
+ from slm_api.helpers.stream_handlers import WebXiStreamHandler
122
+
123
+ streamer = WebXiStreamHandler(
124
+ host="192.168.1.183",
125
+ sequenceID=SequenceIdEnums.FastLAF.value,
126
+ mode="fast_logging",
127
+ fast_logging_interval=FastLogInterval.interval_1000ms.value,
128
+ saving="csv",
129
+ saving_path="saved_data/fast_laf",
130
+ )
131
+
132
+ try:
133
+ streamer.startStream()
134
+ except KeyboardInterrupt:
135
+ streamer.stopStream()
136
+ ```
137
+
138
+ ## Audio
139
+
140
+ Use the MP3 or FLAC examples for decoded audio samples:
141
+
142
+ ```python
143
+ from slm_api.enums.sequence_id_enum import SequenceIdEnums
144
+ from slm_api.helpers.stream_handlers import WebXiStreamHandler
145
+
146
+ streamer = WebXiStreamHandler(
147
+ host="192.168.1.183",
148
+ sequenceID=SequenceIdEnums.MP3Signal.value,
149
+ mode="mp3",
150
+ )
151
+ ```
152
+
153
+ The matching `flac` form uses `SequenceIdEnums.FLACSignal.value` and `mode="flac"`. Audio setup is enabled by the handler. The SLM can take a few seconds to initialize audio recording when its quality setting changes.
154
+
155
+ ## Synchronized Multi-Device Stream
156
+
157
+ Based on [multi_device_flac.py](src/slm_api/multi_device_flac.py), pass `multi_device=True` and start/stop the group through the synchronized helpers:
158
+
159
+ ```python
160
+ from slm_api.enums.sequence_id_enum import SequenceIdEnums
161
+ from slm_api.helpers.stream_handlers import WebXiStreamHandler, start_all_synced, stop_all
162
+
163
+ hosts = ["192.168.1.183", "192.168.1.216"]
164
+ streams = [
165
+ WebXiStreamHandler(
166
+ host=host,
167
+ sequenceID=SequenceIdEnums.MP3Signal.value,
168
+ mode="mp3",
169
+ streamName=f"Audio_{host}",
170
+ multi_device=True,
171
+ saving="csv",
172
+ saving_path=f"saved_data/{host}",
173
+ )
174
+ for host in hosts
175
+ ]
176
+
177
+ start_all_synced(streams)
178
+
179
+ try:
180
+ while True:
181
+ time.sleep(1)
182
+ except KeyboardInterrupt:
183
+ stop_all(streams)
184
+ ```
185
+
186
+ `stop_all()` synchronizes the device measurement stops, flushes saving handlers, and removes each device stream.
187
+
188
+ ## Modes
189
+
190
+ | Mode | Use |
191
+ | --- | --- |
192
+ | `"leq"` | One broadband sequence; this is the default. |
193
+ | `"multi"` | Several named broadband sequences. |
194
+ | `"cpb"` | Constant-percentage-band data. |
195
+ | `"fast_logging"` | Fast broadband samples. |
196
+ | `"flac"` / `"mp3"` | Decoded audio samples. |
197
+ | `"screen_record"` | Latest SLM screen image in `streamer.image_bytes`. |
198
+
199
+ Legacy flags such as `mp3=True` remain supported, but `mode=` is preferred. Only select one mode per stream.
200
+ ```
201
+
202
+ ## Troubleshooting
203
+
204
+ - **No values arrive:** Confirm the requested sequences are enabled on the SLM.
205
+ - **Audio returns no samples after first enable:** Allow the SLM a few seconds to initialize audio recording before retrying.
206
+ - **Saved file is missing:** Call `stopStream()` or `stop_all()`; buffered files are written during shutdown.
@@ -0,0 +1,37 @@
1
+ slm_api/CPB_example.py,sha256=dFioae4gS2NWm5Wod87dCglbmO26n7WBvh0mklK_H8Q,2652
2
+ slm_api/__init__.py,sha256=Gl2zixNPabXv08KWFtImHPCBA3YhqkqHfk3V4QF2E84,136
3
+ slm_api/enums/fast_log_intervals_enum.py,sha256=o8JVtxBOiNYhGe9wRtrtpiQZMEm2jjfFQu9ehZWTSn8,398
4
+ slm_api/enums/sequence_id_enum.py,sha256=zFFiS0RzDQPSF4d6mg0iYfaRyyDmKe9YHnKGo7l03_k,6305
5
+ slm_api/example_multi.py,sha256=Tlf7opQBF1wvmaYR3WhKRA_IPkWhSiY3PtP_khTyJZw,2962
6
+ slm_api/fast_log_example.py,sha256=p2mwo8S6NLUazBEA2NDhIUS5Yd0d9CYm_jmyGIDe0pQ,6544
7
+ slm_api/flac_example.py,sha256=eJwxVNzS1D8xAVWibW9-Szu9D9KCVfNYBj6-d4TfCX0,6900
8
+ slm_api/helpers/FigureHandler.py,sha256=WCK7YxSDd1p3-1M2xhmiEh2Qu1teN0-NYIiikcLneZw,2251
9
+ slm_api/helpers/Leq.py,sha256=2ZDTP9pkIZHcZeEDqQtU-nL1dHgvYsbVxR6Sonnw5ck,4021
10
+ slm_api/helpers/__init__.py,sha256=hB7KKDrb-Xuzvjc7WeZRPEyCTlRfizOz3VBJ0qTj1r4,147
11
+ slm_api/helpers/buffer.py,sha256=k1lCKkCKT8mhHCzs28LTU_JaRRo1X7eo9jg5RduYPCk,670
12
+ slm_api/helpers/data_handler.py,sha256=mmKh4dCirCR32yJE12PntzBeNQpHijvV-oPhMrpHlgg,2984
13
+ slm_api/helpers/fft.py,sha256=p31aiPqnvsFhXwncB9Ixuz7DQEJBtNnm_obQzL8sYNI,1146
14
+ slm_api/helpers/flac_stream_2_samples.py,sha256=--WXFeQLnMZ4EMmYMxV9uogn98N98RYRRbQsZwQjA-s,1886
15
+ slm_api/helpers/https_requests.py,sha256=fL7U-_c4r6Ho1LTBkbkVoUy5pvwiNGTBW6WBOJjqxr4,2701
16
+ slm_api/helpers/measurment_handler.py,sha256=BzoRnd2REWPwG7KKrK9zyWdyADLNWoDRih3almUv7vg,920
17
+ slm_api/helpers/sequence_handler.py,sha256=a6jIgSZwXJaaR5QI1PSX-S7Lsr77pBNF-Q-3hGfVw1Y,1596
18
+ slm_api/helpers/stream_handler.py,sha256=dilwA5Y81G1R2yEleFLCveQJqor4e3QYJ-nNyBzQvMg,4283
19
+ slm_api/helpers/stream_handlers.py,sha256=N1Q7g8ihhrjtGT5sNfKXo9p48kiZpVh_oxSMtvY-NU0,20853
20
+ slm_api/helpers/tmpFileStateMachine.py,sha256=mDBL7PjPfEDoo8Tj-7ds4sx4W6-lavsYGLOHi6n5F-E,3431
21
+ slm_api/helpers/websocket_handler.py,sha256=QQtoMtSE3lojIQ2WpNb6dl5u1iKVGTbynu_oqaX4uVE,497
22
+ slm_api/helpers/webxi_helper_functions.py,sha256=Kc8YECW4wL63JW2DDw1iUY4k_DfYa9ih_TnaQYQ5_K4,3651
23
+ slm_api/helpers/webxi_root.pem,sha256=Vo-uzcN0ZomwWJWp1xaLzSA8mDqlxUAp_c1P31820Jc,1456
24
+ slm_api/mp3_example.py,sha256=Hh8m9emaIOVStgCqG-Vj3KA8mWqn39bfE-IeW3buMXg,6810
25
+ slm_api/multi_device_flac.py,sha256=eiZ0NYhuMbAHfVhf--qhyRU33v8YwIyc5nn_CPGiF0Q,3767
26
+ slm_api/multi_instrument_example.py,sha256=Rg21SSrQ5Ly6ze66cT21jpH-8JOXvdlkBZrxXwOrWLI,1997
27
+ slm_api/screen_record_example.py,sha256=hgeWpEYI2YcsHt8IvB3FOzC19S_5h_sq8jCvSo4BeeA,2183
28
+ slm_api/webxi/README.md,sha256=tlACB4cUWebZTrzmzzGlRwwW5hCpO4tSv6tOasx7uxc,1345
29
+ slm_api/webxi/__init__.py,sha256=iT4laRwuAPXHLZNrYBvwrBHEoIwNyQ0fG6CbQYcVCEY,118
30
+ slm_api/webxi/webxi-header.ksy,sha256=vapyptJbqS8n9c7FiQFbWRv4GuO50KG5_D1a2gJEji8,500
31
+ slm_api/webxi/webxi-stream.ksy,sha256=tve0OzI0ANE-HfPxQLgDHX3-jNwJ3wfwEJKTC38Ne2U,6557
32
+ slm_api/webxi/webxi_header.py,sha256=xHuL3h6OXol2iK6yjXouw9QFvtTQbDn0JpZK-zsadtg,1469
33
+ slm_api/webxi/webxi_stream.py,sha256=CBo6-AqtWvk60WKyFYCkOAI_pDQBd5Kf-0zuPi73iWQ,17310
34
+ slm_open_api_package-0.2.10.dist-info/WHEEL,sha256=9IUYslYuG_asT4WlWvfk_K76nveuLvSTKDoLeL0cV-Q,80
35
+ slm_open_api_package-0.2.10.dist-info/entry_points.txt,sha256=jJT0x7ixsqHY2_HAjA_WRaBt-4vgmpYS4bzCIIcnIXg,51
36
+ slm_open_api_package-0.2.10.dist-info/METADATA,sha256=D94TMF6PpcvbiOz5-LSEWI7jxVe5QJGgbKrK0lre630,6073
37
+ slm_open_api_package-0.2.10.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.13.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any