slm-open-api-package 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. slm_open_api_package-0.1.0/PKG-INFO +21 -0
  2. slm_open_api_package-0.1.0/README.md +1 -0
  3. slm_open_api_package-0.1.0/pyproject.toml +33 -0
  4. slm_open_api_package-0.1.0/pyproject.toml.orig +32 -0
  5. slm_open_api_package-0.1.0/src/open_api_package/__init__.py +5 -0
  6. slm_open_api_package-0.1.0/src/open_api_package/example_multi.py +145 -0
  7. slm_open_api_package-0.1.0/src/open_api_package/flac_example.py +160 -0
  8. slm_open_api_package-0.1.0/src/open_api_package/helpers/FigureHandler.py +65 -0
  9. slm_open_api_package-0.1.0/src/open_api_package/helpers/Leq.py +87 -0
  10. slm_open_api_package-0.1.0/src/open_api_package/helpers/__init__.py +5 -0
  11. slm_open_api_package-0.1.0/src/open_api_package/helpers/buffer.py +27 -0
  12. slm_open_api_package-0.1.0/src/open_api_package/helpers/fft.py +38 -0
  13. slm_open_api_package-0.1.0/src/open_api_package/helpers/flac_stream_2_samples.py +64 -0
  14. slm_open_api_package-0.1.0/src/open_api_package/helpers/measurment_handler.py +20 -0
  15. slm_open_api_package-0.1.0/src/open_api_package/helpers/sequence_handler.py +44 -0
  16. slm_open_api_package-0.1.0/src/open_api_package/helpers/stream_handler.py +86 -0
  17. slm_open_api_package-0.1.0/src/open_api_package/helpers/stream_handlers.py +152 -0
  18. slm_open_api_package-0.1.0/src/open_api_package/helpers/tmpFileStateMachine.py +93 -0
  19. slm_open_api_package-0.1.0/src/open_api_package/helpers/websocket_handler.py +8 -0
  20. slm_open_api_package-0.1.0/src/open_api_package/helpers/webxi_helper_functions.py +67 -0
  21. slm_open_api_package-0.1.0/src/open_api_package/mp3_example.py +161 -0
  22. slm_open_api_package-0.1.0/src/open_api_package/webxi/README.md +25 -0
  23. slm_open_api_package-0.1.0/src/open_api_package/webxi/__init__.py +5 -0
  24. slm_open_api_package-0.1.0/src/open_api_package/webxi/webxi-header.ksy +32 -0
  25. slm_open_api_package-0.1.0/src/open_api_package/webxi/webxi-stream.ksy +342 -0
  26. slm_open_api_package-0.1.0/src/open_api_package/webxi/webxi_header.py +42 -0
  27. slm_open_api_package-0.1.0/src/open_api_package/webxi/webxi_stream.py +450 -0
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.3
2
+ Name: slm-open-api-package
3
+ Version: 0.1.0
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: python-dotenv
12
+ Requires-Dist: packaging
13
+ Requires-Dist: pyqtgraph
14
+ Requires-Dist: pyqt5
15
+ Requires-Dist: pyqt5-qt5<5.15.11
16
+ Requires-Dist: miniaudio
17
+ Requires-Dist: matplotlib>=3.11.1
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+
21
+ # open-api-package
@@ -0,0 +1 @@
1
+ # open-api-package
@@ -0,0 +1,33 @@
1
+ [project]
2
+ name = "slm-open-api-package"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "requests",
9
+ "numpy",
10
+ "websockets",
11
+ "kaitaistruct",
12
+ "python-dotenv",
13
+ "packaging",
14
+ "pyqtgraph",
15
+ "PyQt5",
16
+ "PyQt5-Qt5<5.15.11",
17
+ "miniaudio",
18
+ "matplotlib>=3.11.1",
19
+ ]
20
+
21
+ [[project.authors]]
22
+ name = "Sofus Vingaard"
23
+ email = "sofus.krebsvingaard@hbkworld.com"
24
+
25
+ [project.scripts]
26
+ open-api-package = "open_api_package:main"
27
+
28
+ [tool.uv.build-backend]
29
+ module-name = "open_api_package"
30
+
31
+ [build-system]
32
+ requires = ["uv_build>=0.12.5,<0.13.0"]
33
+ build-backend = "uv_build"
@@ -0,0 +1,32 @@
1
+ [project]
2
+ name = "slm-open-api-package"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Sofus Vingaard", email = "sofus.krebsvingaard@hbkworld.com" }
8
+ ]
9
+ requires-python = ">=3.12"
10
+ dependencies = [
11
+ "requests",
12
+ "numpy",
13
+ "websockets",
14
+ "kaitaistruct",
15
+ "python-dotenv",
16
+ "packaging",
17
+ "pyqtgraph",
18
+ "PyQt5",
19
+ "PyQt5-Qt5<5.15.11",
20
+ "miniaudio",
21
+ "matplotlib>=3.11.1",
22
+ ]
23
+
24
+ [project.scripts]
25
+ open-api-package = "open_api_package:main"
26
+
27
+ [tool.uv.build-backend]
28
+ module-name = "open_api_package"
29
+
30
+ [build-system]
31
+ requires = ["uv_build>=0.12.5,<0.13.0"]
32
+ build-backend = "uv_build"
@@ -0,0 +1,5 @@
1
+ """Top-level package for open_api_package (submodules are not auto-imported)."""
2
+
3
+
4
+ def main() -> None:
5
+ print("Hello from open-api-package!")
@@ -0,0 +1,145 @@
1
+ # This example will show how to stream multiple sequences at the same time using the same stream
2
+ # For this example enable the wanted sequences on the device
3
+ import asyncio
4
+ import requests
5
+ import threading
6
+ import sys, traceback
7
+ import numpy as np
8
+ import time
9
+ import matplotlib.pyplot as plt
10
+ from matplotlib.animation import FuncAnimation
11
+
12
+ # Modules to convert webxi data
13
+ from open_api_package.webxi import webxi_stream as webxiStream
14
+
15
+ from open_api_package.helpers import sequence_handler as seq # Get sequences, e.g. LAeq functions
16
+ from open_api_package.helpers import stream_handler as stream # SLM stream functions
17
+ from open_api_package.helpers import measurment_handler as meas # Start/pause/Stop measurments functions
18
+ from open_api_package.helpers.Leq import MovingLeq, SLM_Setup_LAeq # Class to hold moving Leq
19
+ from open_api_package.helpers import websocket_handler as webSocket # Async functions to control communication
20
+ from open_api_package.helpers import webxi_helper_functions as webxi_helper
21
+ from open_api_package.helpers import stream_handlers as sh
22
+
23
+ host, ip = webxi_helper.set_host_ip(__file__)
24
+
25
+ # This example will stream LAeq. Add more names to the list to plot several sequences at once.
26
+ # Incase of error make sure the sequences are enabled on the SLM.
27
+ sequenceNames = ["LAeq", "LCeq", "LAF"]
28
+
29
+
30
+ # class streamHandler:
31
+
32
+ # def __init__(self, startStream = False):
33
+ # self.streamInit()
34
+ # if startStream:
35
+ # self.startStream()
36
+
37
+ # def streamInit(self):
38
+ # self.IDs = []
39
+ # self.sequences = []
40
+ # self.sequenceFuncs = []
41
+
42
+ # for x in sequenceNames:
43
+ # ID, sequence = seq.get_sequence(host, seq.getSequenceID(host, x))
44
+ # self.IDs.append(ID)
45
+ # self.sequences.append(sequence)
46
+ # self.sequenceFuncs.append(MovingLeq(10, storedata=True, windowSize=100))
47
+
48
+ # self.streamName = "MultipleSequences"
49
+ # self.uri = stream.setup_stream(host, ip, self.IDs, self.streamName)
50
+ # # Start a measurement. This is needed to obtain data from the device
51
+ # meas.start_pause_measurement(host,True)
52
+
53
+ # def startStream(self):
54
+ # self.StreamRun = True
55
+ # asyncio.run(self.runStream())
56
+
57
+ # async def runStream(self):
58
+ # self.loop = asyncio.get_running_loop()
59
+ # self.fut = self.loop.create_future()
60
+ # # Create lambda function to use for the stream message. In this example is a function
61
+ # # call used
62
+ # self.msg_func = lambda msg : self.print_data(msg, self.IDs, self.sequences, self.sequenceFuncs, self.fut)
63
+ # # Initilize and run the websocket to retrive data
64
+ # task = self.loop.create_task(webSocket.next_async_websocket(self.uri, self.msg_func))
65
+ # await self.fut
66
+ # task.cancel()
67
+ # meas.stop_measurement(host)
68
+
69
+ # def stopStream(self):
70
+ # self.StreamRun = False
71
+ # if hasattr(self, "loop"):
72
+ # self.loop.call_soon_threadsafe(self._resolve)
73
+ # stream.delete_stream(host, self.streamName) # Cleaning up and deleting the stream used
74
+
75
+ # def _resolve(self):
76
+ # if not self.fut.done():
77
+ # self.fut.set_result(True)
78
+
79
+ # def print_data(self, message, IDs, sequences, sequenceFuncs, fut):
80
+ # package = webxiStream.WebxiStream.from_bytes(message)
81
+ # if package.header.message_type == webxiStream.WebxiStream.Header.EMessageType.e_sequence_data:
82
+ # for ID, sequence, Func in zip(IDs, sequences, sequenceFuncs):
83
+ # for data in package.content.sequence_blocks:
84
+ # if data.sequence_id == ID:
85
+ # value = stream.data_type_conv(sequence["DataType"], data.values, None)
86
+ # value = (np.array(value) if isinstance(value, list) else value) / 100
87
+ # move = Func.move(value)
88
+ # seqName = sequence["Name"]
89
+ # print(f"{seqName}: {value} and 10s avg: {move:.2f}")
90
+
91
+ # if not self.StreamRun:
92
+ # self._resolve()
93
+
94
+ class FigHandler:
95
+
96
+ def __init__(self, dataHandler):
97
+ self.fig, self.ax = plt.subplots(2,1,sharex=True, sharey=True)
98
+ axis = np.arange(-(len(dataHandler[0].getPlotData(True)) - 1),1,1)
99
+ self.dataHandler = dataHandler if isinstance(dataHandler, list) else [dataHandler]
100
+ self.ln1 = []
101
+ self.ln2 = []
102
+ for x, ii in zip(self.dataHandler, sequenceNames):
103
+ self.ln1.append((self.ax[0].plot(axis,x.getPlotData(True), label=ii))[0])
104
+ self.ln2.append((self.ax[1].plot(axis,x.getPlotData(False)))[0])
105
+ self.ax[1].set_xlim(left=np.min(axis), right=np.max(axis))
106
+ self.ax[1].set_ylim(bottom=30, top=100)
107
+ self.ax[0].set_ylabel("dB [SPL]")
108
+ self.ax[1].set_xlabel("Time [s]")
109
+ self.ax[1].set_ylabel("dB [SPL]")
110
+ self.ax[0].set_title('Moving avaraged')
111
+ self.ax[1].set_title('Instantaneous')
112
+ leg = self.ax[0].legend(loc='upper left')
113
+ self.ax[0].grid()
114
+ self.ax[1].grid()
115
+ self.fig.autofmt_xdate()
116
+ self.fig.tight_layout()
117
+ self.fig.canvas.mpl_connect('close_event', on_close)
118
+ self.fig.canvas.setWindowTitle('LAeq example')
119
+
120
+ def _update(self, i):
121
+ for idx, x in enumerate(self.dataHandler):
122
+ self.ln1[idx].set_ydata(x.getPlotData(True))
123
+ self.ln2[idx].set_ydata(x.getPlotData(False))
124
+
125
+ def startAnimation(self):
126
+ self.ani = FuncAnimation(self.fig, self._update, interval=1000)
127
+
128
+ def on_close(event):
129
+ streamer.stopStream()
130
+
131
+ if __name__ == "__main__":
132
+ # turns off all BB freq weights to prevent interference
133
+ webxi_helper.turn_off_bb_freq_weight(host)
134
+
135
+ # turns on the wanted BB freq weights for this example
136
+ webxi_helper.turn_on_bb_freq_weight(host, ["A", "C"])
137
+
138
+ # sets the sequences to true. You can add or remove sequences at the top of the file.
139
+ webxi_helper.turn_on_bb_leq(host, sequenceNames)
140
+
141
+ streamer = sh.WebXiStreamHandler(host=host, ip=ip, sequenceNames=sequenceNames, multi=True)
142
+ fig = FigHandler(streamer.sequenceFuncs)
143
+ fig.startAnimation()
144
+ threading.Thread(target=streamer.startStream, daemon=True).start()
145
+ plt.show()
@@ -0,0 +1,160 @@
1
+ import asyncio
2
+ import requests
3
+ import sys
4
+ import pyqtgraph as pg
5
+ import numpy as np
6
+ from open_api_package.helpers import webxi_helper_functions as webxi_helper
7
+
8
+ # Modules to convert webxi data
9
+ from open_api_package.webxi import webxi_stream as webxiStream
10
+ # Help functions located in the helpers folder
11
+ # Read these files to get examples on how to communicate with the SLM
12
+ from open_api_package.helpers import stream_handler as stream # SLM stream functions
13
+ # Start/pause/Stop measurments functions
14
+ from open_api_package.helpers import measurment_handler as meas
15
+ # Get sequences,
16
+ from open_api_package.helpers import sequence_handler as seq
17
+ # Stream handler
18
+ from open_api_package.helpers import stream_handlers as sh
19
+
20
+
21
+ # Async functions to control communication
22
+ from open_api_package.helpers import websocket_handler as webSocket
23
+ from timeit import default_timer as timer
24
+ # Buffer and decoder for the flac stream
25
+ from open_api_package.helpers.buffer import DataBuffer
26
+ from open_api_package.helpers.fft import dBfft
27
+ from open_api_package.helpers import flac_stream_2_samples as flac2samples
28
+ import threading
29
+
30
+ from open_api_package.helpers.FigureHandler import FigureHandler
31
+
32
+ # FLAC streaming is only available on 2255
33
+
34
+ host, ip = webxi_helper.set_host_ip(__file__)
35
+
36
+ sequenceID = 157
37
+
38
+
39
+ # class streamHandler:
40
+ # def __init__(self, startStream=False):
41
+ # self.i = 0
42
+ # self.max_input = 15.6263 / np.sqrt(2)
43
+ # self.streamInit()
44
+ # if startStream:
45
+ # self.startStream()
46
+
47
+ # def decode_flac_stream(self, message):
48
+ # start = timer()
49
+ # package = webxiStream.WebxiStream.from_bytes(message)
50
+ # if package.header.message_type == webxiStream.WebxiStream.Header.EMessageType.e_sequence_data:
51
+ # # Get the encoded flac block
52
+ # flac = package.content.sequence_blocks[0]
53
+ # # Decode the compressed samples and add it to the data bufffer
54
+ # DataBuffer.append(flac2samples.decode(flac, self.calibrationFactor))
55
+ # end = timer()
56
+ # total = (end - start)
57
+ # if 0.0625 < total:
58
+ # print(f"TotalTime: {total}")
59
+ # if not self.StreamRun:
60
+ # self._resolve()
61
+
62
+ # def get_calibration_factor(self):
63
+ # # Calculate calibration factor from the microphone sensitivity
64
+ # response = requests.get(f"{host}/WebXi/Applications/SLM/Outputs/Sensitivity")
65
+ # assert (response.status_code == 200)
66
+ # mic_sens = float(response.text) # V/Pa
67
+ # max_lvl = 20 * np.log10((self.max_input / mic_sens) / 20e-6) # dB SPL re 20 uPa
68
+ # self.calibrationFactor = (20e-6 * 10 ** (max_lvl / 20)) / (2 ** 23 - 1) * np.sqrt(2)
69
+
70
+ # def streamInit(self):
71
+ # # Enable audio recording analysis quality
72
+ # response = requests.put(f"{host}/WebXi/Applications/SLM/setup/AudioRecordingAnalysisQuality", json = 1)
73
+ # assert(response.status_code == 200)
74
+ # self.get_calibration_factor()
75
+ # self.ID, self.sequence = seq.get_sequence(host, sequenceID)
76
+ # # Get URI for stream
77
+ # self.streamName = "Flac stream"
78
+ # self.uri = stream.setup_stream(host, ip, self.ID, self.streamName)
79
+
80
+ # # Start a measurement. This is needed to obtain data from the device
81
+ # meas.start_pause_measurement(host, True)
82
+
83
+ # def startStream(self):
84
+ # self.StreamRun = True
85
+
86
+ # asyncio.run(self.runStream())
87
+
88
+ # async def runStream(self):
89
+ # self.loop = asyncio.get_running_loop()
90
+ # self.fut = self.loop.create_future()
91
+ # # Create lambda function to use for the stream message. In this example is a function
92
+ # # call used
93
+ # self.msg_func = lambda msg : self.decode_flac_stream(msg)
94
+ # # Initilize and run the websocket to retrive data
95
+
96
+ # task = self.loop.create_task(webSocket.next_async_websocket(self.uri, self.msg_func))
97
+ # await self.fut
98
+ # task.cancel()
99
+ # meas.stop_measurement(host)
100
+
101
+
102
+ # def stopStream(self):
103
+ # self.StreamRun = False
104
+ # # Resolve the future directly; waiting for the next message may never happen
105
+ # if hasattr(self, "loop"):
106
+ # self.loop.call_soon_threadsafe(self._resolve)
107
+ # stream.delete_stream(host, self.streamName) # Cleaning up and deleting the stream used
108
+
109
+ # def _resolve(self):
110
+ # if not self.fut.done():
111
+ # self.fut.set_result(True)
112
+
113
+
114
+ class figureHandler(FigureHandler):
115
+
116
+ def axisConfig(self):
117
+ self.plotTime.getAxis('left').setStyle(tickFont=pg.QtGui.QFont('Arial', 11))
118
+ self.plotTime.getAxis('bottom').setStyle(tickFont=pg.QtGui.QFont('Arial', 11))
119
+ self.plotTime.getAxis('left').setLabel('Pressure', units='Pa', **self.labelStyle)
120
+ self.plotTime.getAxis('bottom').setLabel('Time', units='s', **self.labelStyle)
121
+ self.plotFreq.getAxis('left').setStyle(tickFont=pg.QtGui.QFont('Arial', 14))
122
+ self.plotFreq.getAxis('bottom').setStyle(tickFont=pg.QtGui.QFont('Arial', 14))
123
+ self.plotFreq.getAxis('left').setLabel('dB SPL re 20 µPa', **self.labelStyle)
124
+ self.plotFreq.getAxis('bottom').setLabel('Frequency', units='Hz', **self.labelStyle)
125
+
126
+ def update(self):
127
+ signal = DataBuffer.getPart(self.chunkToShow)
128
+ x = np.linspace(np.min(self.axis), np.max(self.axis), len(signal))
129
+ freq, s_dbfs = dBfft(signal, 2**16, self.fftHamming, ref=20e-6) #Reference = 20µPa
130
+ # Average the fft for a smoother plot
131
+ avg = s_dbfs / 3 + self.old / 3 + self.oldold / 3
132
+ self.curveTime.setData(x, signal)
133
+ self.curveFreq.setData(freq, avg)
134
+ self.oldold = self.old
135
+ self.old = s_dbfs
136
+ if (self.i % 5 == 0):
137
+ # Autoscale and print min/max values every 0.5 seconds
138
+ min_Pa = np.round(min(signal), 2)
139
+ max_Pa = np.round(max(signal), 2)
140
+ fft_peak = np.round(max(avg), 2)
141
+ fft_min = np.round(min(avg), 2)
142
+ peak_freq = (freq[np.argmax(avg)])
143
+ if (min_Pa != max_Pa):
144
+ self.plotTime.setYRange(min_Pa * 1.2, max_Pa * 1.2)
145
+ if (np.isinf(fft_peak) == False):
146
+ self.plotFreq.setYRange(fft_min, fft_peak * 1.2)
147
+ print(f"Min: {min_Pa} Pa, Max: {max_Pa} Pa, Peak: {fft_peak} dB SPL, Peak freq: {peak_freq} Hz")
148
+ self.i += 1
149
+
150
+ def on_close():
151
+ streamer.stopStream()
152
+
153
+ if __name__ == "__main__":
154
+ # streamer = streamHandler()
155
+ streamer = sh.WebXiStreamHandler(host, ip, sequenceID = sequenceID, flac=True)
156
+ fig = figureHandler()
157
+ fig.app.aboutToQuit.connect(on_close)
158
+ threading.Thread(target=streamer.startStream, daemon=True).start()
159
+ fig.run()
160
+
@@ -0,0 +1,65 @@
1
+ import pyqtgraph as pg
2
+ import sys
3
+ import numpy as np
4
+ from PyQt5 import QtWidgets
5
+
6
+ from abc import ABC, abstractmethod
7
+
8
+ class FigureHandler(ABC):
9
+ def __init__(self):
10
+ self.i = 0
11
+ self.chunkToShow = 2 ** 15
12
+ self.fftSize = self.chunkToShow
13
+ self.fftHamming = np.hamming(self.fftSize)
14
+ # Used to store "old" spectrums for fft averaging
15
+ self.old = 0
16
+ self.oldold = 0
17
+
18
+ self.x = np.linspace(0.0, 10, self.chunkToShow)
19
+
20
+ ## Figures
21
+ self.axis = np.arange(self.chunkToShow)
22
+ self.axis = np.flip(self.axis * -1 / self.chunkToShow)
23
+ self.x = np.zeros(len(self.axis))
24
+ self.app = QtWidgets.QApplication(sys.argv)
25
+ self.win = pg.GraphicsLayoutWidget(title="Streaming")
26
+ self.win.setBackground('w')
27
+ self.win.resize(1000, 600)
28
+ self.plotTime = self.win.addPlot()
29
+ self.curveTime = self.plotTime.plot(self.x, self.axis)
30
+ self.curveTime.setPen(color='b', width=2, autoDownsample=True, clipToView=True)
31
+ self.plotTime.showGrid(x=True, y=True)
32
+ self.plotTime.setXRange(np.min(self.axis), np.max(self.axis))
33
+ self.plotTime.setYRange(-2, 2)
34
+ self.labelStyle = {'color': '#000', 'font-size': '13pt'}
35
+
36
+ # Subplot 2 (FFT)
37
+ # Calculate the frequency vector
38
+ self.win.nextRow()
39
+ self.plotFreq = self.win.addPlot()
40
+ freq = np.arange(self.fftSize//2 + 1) / (float(self.fftSize) / 32e3)
41
+ self.curveFreq = self.plotFreq.plot(freq, np.arange(len(freq)))
42
+ self.curveFreq.setPen(color='b', width=2, autoDownsample=True, clipToView=True)
43
+ self.plotFreq.setXRange(0, np.max(freq))
44
+ self.plotFreq.setYRange(-20, 130)
45
+ self.plotFreq.getAxis('bottom').enableAutoSIPrefix(enable=False)
46
+ self.plotFreq.setLogMode(x=True, y=False)
47
+ self.plotFreq.showGrid(x=True, y=True)
48
+ self.axisConfig()
49
+ self.win.show()
50
+
51
+ self.timer = pg.QtCore.QTimer()
52
+ self.timer.timeout.connect(self.update)
53
+ self.timer.start(int(0.1 * 1000))
54
+
55
+ def run(self):
56
+ QtWidgets.QApplication.instance().exec_()
57
+
58
+ @abstractmethod
59
+ def axisConfig(self):
60
+ pass
61
+
62
+ @abstractmethod
63
+ def update(self):
64
+ pass
65
+
@@ -0,0 +1,87 @@
1
+ import numpy as np
2
+ import requests
3
+
4
+
5
+ def SLM_Setup_LAeq(host):
6
+ # Enable logging mode
7
+ requests.put(host + "/webxi/Applications/SLM/Setup/ControlLoggingMode", json=1)
8
+ # Set the device in free runnign mode
9
+ requests.put(host + "/webxi/Applications/SLM/Setup/ControlMeasurementTimeControl", json=0)
10
+ # Enable A weight frequency broad band weighting, but first clear the other once to be sure
11
+ requests.put(host + "/webxi/Applications/SLM/Setup/BBFreqWeightB", json=False)
12
+ requests.put(host + "/webxi/Applications/SLM/Setup/BBFreqWeightC", json=False)
13
+ requests.put(host + "/webxi/Applications/SLM/Setup/BBFreqWeightZ", json=False)
14
+ requests.put(host + "/webxi/Applications/SLM/Setup/BBFreqWeightA", json=True)
15
+ # Enable LAeq mode on the device
16
+ requests.put(host + "/webxi/applications/slm/setup/BBLAeq", json=True)
17
+
18
+ class MovingLeq:
19
+ """Class to create a moving Leq object.\n
20
+ Stores a window of Leq logging intervals to be combined into a total Leq\n
21
+ Calling move(new_interval) on the object will overwrite the oldest value in the
22
+ window, recalculate the total Leq and return it."""
23
+
24
+ def __init__(self, window_length_sec, storedata=False, windowSize=None):
25
+ self.window_length = window_length_sec
26
+ windowSize = self.window_length * 10 + 1 if windowSize is None else windowSize
27
+
28
+ # Intialize an empty array, index to track oldest value, and window status
29
+ self.leq_window = np.zeros(window_length_sec)
30
+ self.leq_window.fill(np.nan)
31
+ self.storedata = storedata
32
+ if self.storedata:
33
+ self.rawData = LeqData(windowSize) # Used to store the raw data for plotting
34
+ self.leqData = LeqData(windowSize) # Used to store the Leq data for plotting
35
+ self.oldest_value_index = 0
36
+ self.leq_total = 0.0
37
+ self.window_full = False
38
+
39
+ def total_leq(self,window):
40
+ """ Function to combine several Leq periods into a total Leq, as per:\n
41
+ 10 * log10(10^(L1/10) + 10^(L2/10) + ... 10^(Ln/10) / n)"""
42
+ sound_Pa = np.sum(np.power(10,window/10)) / len(window)
43
+ return 10 * np.log10(sound_Pa)
44
+
45
+ def move(self, new_value):
46
+ """Overwrite the oldest value in the array with the new logging interval
47
+ and update the oldest value index to point to the next oldest value"""
48
+ self.leq_window[self.oldest_value_index] = new_value
49
+ self.oldest_value_index += 1
50
+
51
+ # Calculate the total Leq for the moving window and return it.
52
+ if self.window_full:
53
+ self.leq_total = self.total_leq(self.leq_window)
54
+ self.oldest_value_index = self.oldest_value_index % self.window_length
55
+ if self.storedata:
56
+ self.leqData.move(self.leq_total)
57
+ self.rawData.move(new_value)
58
+ return self.leq_total
59
+
60
+ # If the moving window is not full of measured data, simply calculate total Leq
61
+ # for the data that is in the window (and ignore the empty part of the array)
62
+ else:
63
+ self.leq_total = self.total_leq(self.leq_window[:self.oldest_value_index])
64
+ self.oldest_value_index = self.oldest_value_index % self.window_length
65
+ if self.oldest_value_index == self.window_length - 1:
66
+ self.window_full = True
67
+ if self.storedata:
68
+ self.leqData.move(self.leq_total)
69
+ self.rawData.move(new_value)
70
+ return self.leq_total
71
+
72
+ def getPlotData(self,Leq):
73
+ if Leq:
74
+ return self.leqData.getData()
75
+ else:
76
+ return self.rawData.getData()
77
+
78
+ class LeqData:
79
+ def __init__(self,window_length_sec):
80
+ self.leq_window = np.zeros(window_length_sec)
81
+ self.leq_window[:] = np.nan
82
+
83
+ def move(self, new_value):
84
+ self.leq_window = np.insert(self.leq_window[1:], self.leq_window.size-1,new_value)
85
+
86
+ def getData(self):
87
+ return self.leq_window
@@ -0,0 +1,5 @@
1
+ from .stream_handler import *
2
+ from .measurment_handler import *
3
+ from .sequence_handler import *
4
+ from .Leq import *
5
+ from .stream_handlers import *
@@ -0,0 +1,27 @@
1
+ import numpy as np
2
+
3
+ class buffer:
4
+ def __init__(self, size):
5
+ self.size = size
6
+ self.data = np.zeros(self.size)
7
+
8
+ def append(self, x):
9
+ """
10
+ Adds data in the front of the buffer. Discard the oldest data if buffer is full
11
+ """
12
+ self.data = np.append(self.data[-(self.size - len(x)) :: ] , x)
13
+
14
+ def get(self):
15
+ """
16
+ Returns the whole buffer
17
+ """
18
+ return self.data
19
+
20
+ def getPart(self, start = 2**16):
21
+ """
22
+ Returns X points of the newest data.
23
+ """
24
+ return self.data[-start :: ]
25
+
26
+ # Create databuffer to store the converted package data
27
+ DataBuffer = buffer(2**16)
@@ -0,0 +1,38 @@
1
+ import numpy as np
2
+
3
+ def dBfft(x, fs, win=None, ref=32768):
4
+ """
5
+ Calculate spectrum in dB scale
6
+ Args:
7
+ x: input signal
8
+ fs: sampling frequency
9
+ win: vector containing window samples (same length as x).
10
+ If not provided, then rectangular window is used by default.
11
+ ref: reference value used for dBFS scale. 32768 for int16 and 1 for float
12
+
13
+ Returns:
14
+ freq: frequency vector
15
+ s_db: spectrum in dB scale
16
+ """
17
+ N = len(x) # Length of input sequence
18
+
19
+ if win is None:
20
+ win = np.ones(1, N)
21
+ if len(x) != len(win):
22
+ raise ValueError('Signal and window must be of the same length')
23
+ x = x * win
24
+
25
+ # Calculate real FFT and frequency vector
26
+ sp = np.fft.rfft(x)
27
+ freq = np.arange((N / 2) + 1) / (float(N) / fs)
28
+
29
+ # Scale the magnitude of FFT by window and factor of 2,
30
+ # because we are using half of FFT spectrum.
31
+ s_mag = (np.abs(sp) * np.sqrt(2)) / np.sum(win)
32
+
33
+ for i in range(len(s_mag)):
34
+ if s_mag[i] == 0:
35
+ s_mag[i] = 1e-10
36
+ # Convert to dBFS
37
+ s_dbfs = 20 * np.log10(s_mag/ref)
38
+ return freq, s_dbfs