Skip to content

Commit 8a2dfb2

Browse files
committed
iir: Start of testing code
1 parent a2e949a commit 8a2dfb2

File tree

3 files changed

+205
-12
lines changed

3 files changed

+205
-12
lines changed

benchmarks/iir/SimpleFilters.ipynb

Lines changed: 99 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
"%autoreload 2\n",
1212
"\n",
1313
"import sys\n",
14-
"sys.path.insert(0, 'benchmarks/iir/')"
14+
"#sys.path.insert(0, '.benchmarks/iir/')"
1515
]
1616
},
1717
{
1818
"cell_type": "code",
19-
"execution_count": 42,
19+
"execution_count": 2,
2020
"id": "10e5b8fc-6972-4502-a397-300a800df9ee",
2121
"metadata": {},
2222
"outputs": [],
@@ -27,12 +27,14 @@
2727
"from matplotlib import pyplot as plt\n",
2828
"\n",
2929
"import numpy\n",
30-
"import pandas"
30+
"import pandas\n",
31+
"import librosa\n",
32+
"import seaborn"
3133
]
3234
},
3335
{
3436
"cell_type": "code",
35-
"execution_count": 43,
37+
"execution_count": 30,
3638
"id": "fe122a40-66ed-4add-b59a-3254a35ece9a",
3739
"metadata": {},
3840
"outputs": [
@@ -54,7 +56,7 @@
5456
"name": "stderr",
5557
"output_type": "stream",
5658
"text": [
57-
"/tmp/ipykernel_12537/4127207659.py:11: RuntimeWarning: divide by zero encountered in log10\n",
59+
"/tmp/ipykernel_27591/4127207659.py:11: RuntimeWarning: divide by zero encountered in log10\n",
5860
" db = 20 * numpy.log10(abs(h))\n"
5961
]
6062
},
@@ -118,17 +120,17 @@
118120
},
119121
{
120122
"cell_type": "code",
121-
"execution_count": 53,
123+
"execution_count": 5,
122124
"id": "0dff57e5-69cf-4ad7-80f0-d4b721a77910",
123125
"metadata": {},
124126
"outputs": [
125127
{
126128
"data": {
127129
"text/plain": [
128-
"array('i', [29321, 0, -58643, 29321, -58280, 26239, 2210, 0, 4420, 2210, -37452, 13526])"
130+
"array('i', [26233, 0, -52466, 26233, -51149, 21015])"
129131
]
130132
},
131-
"execution_count": 53,
133+
"execution_count": 5,
132134
"metadata": {},
133135
"output_type": "execute_result"
134136
}
@@ -166,7 +168,7 @@
166168
},
167169
{
168170
"cell_type": "code",
169-
"execution_count": 48,
171+
"execution_count": 6,
170172
"id": "7857c33a-6f7f-405b-9d01-16e9d86043f3",
171173
"metadata": {},
172174
"outputs": [
@@ -184,7 +186,7 @@
184186
"name": "stderr",
185187
"output_type": "stream",
186188
"text": [
187-
"/tmp/ipykernel_12537/4127207659.py:11: RuntimeWarning: divide by zero encountered in log10\n",
189+
"/tmp/ipykernel_27591/4127207659.py:11: RuntimeWarning: divide by zero encountered in log10\n",
188190
" db = 20 * numpy.log10(abs(h))\n"
189191
]
190192
},
@@ -222,13 +224,98 @@
222224
},
223225
{
224226
"cell_type": "code",
225-
"execution_count": null,
227+
"execution_count": 13,
226228
"id": "4cb895db-d746-4af0-8e12-e7805865586d",
227229
"metadata": {},
228230
"outputs": [],
231+
"source": []
232+
},
233+
{
234+
"cell_type": "code",
235+
"execution_count": 43,
236+
"id": "51a151c4-cf39-430d-bd49-c0f8fa0baab3",
237+
"metadata": {},
238+
"outputs": [
239+
{
240+
"name": "stdout",
241+
"output_type": "stream",
242+
"text": [
243+
"run: micropython /home/jon/projects/emlearn-micropython/benchmarks/iir/iir_run.py /tmp/tmp3kr9y4bm/input.npy /tmp/tmp3kr9y4bm/output.npy /tmp/tmp3kr9y4bm/filter.npy\n",
244+
"b''\n"
245+
]
246+
},
247+
{
248+
"ename": "AssertionError",
249+
"evalue": "/tmp/tmp3kr9y4bm/output.npy",
250+
"output_type": "error",
251+
"traceback": [
252+
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
253+
"\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)",
254+
"Cell \u001b[0;32mIn[43], line 32\u001b[0m\n\u001b[1;32m 30\u001b[0m noise \u001b[38;5;241m=\u001b[39m numpy\u001b[38;5;241m.\u001b[39mrandom\u001b[38;5;241m.\u001b[39mrandom(\u001b[38;5;241m1000\u001b[39m)\n\u001b[1;32m 31\u001b[0m out_scipy \u001b[38;5;241m=\u001b[39m scipy\u001b[38;5;241m.\u001b[39msignal\u001b[38;5;241m.\u001b[39msosfilt(coeff, noise)\n\u001b[0;32m---> 32\u001b[0m out_emlearn \u001b[38;5;241m=\u001b[39m \u001b[43msosfilt_emlearn\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnumpy\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcoeff\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnoise\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 34\u001b[0m fig, ax \u001b[38;5;241m=\u001b[39m plt\u001b[38;5;241m.\u001b[39msubplots(\u001b[38;5;241m1\u001b[39m, figsize\u001b[38;5;241m=\u001b[39m(\u001b[38;5;241m10\u001b[39m, \u001b[38;5;241m5\u001b[39m))\n\u001b[1;32m 35\u001b[0m plot_spectrum_difference(ax, noise, out_scipy, fs\u001b[38;5;241m=\u001b[39mfs, label\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mscipy\u001b[39m\u001b[38;5;124m'\u001b[39m)\n",
255+
"Cell \u001b[0;32mIn[43], line 24\u001b[0m, in \u001b[0;36msosfilt_emlearn\u001b[0;34m(coeff, input)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 20\u001b[0m \u001b[38;5;124;03mCalls MicroPython in a subprocess, using eml_iir modules from emlearn-micropython\u001b[39;00m\n\u001b[1;32m 21\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 22\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01miir_run_subprocess\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m run_iir\n\u001b[0;32m---> 24\u001b[0m out \u001b[38;5;241m=\u001b[39m \u001b[43mrun_iir\u001b[49m\u001b[43m(\u001b[49m\u001b[43marray\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marray\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43marray\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marray\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcoeff\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mflatten\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 26\u001b[0m out \u001b[38;5;241m=\u001b[39m numpy\u001b[38;5;241m.\u001b[39marray(out)\n\u001b[1;32m 28\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m out\n",
256+
"File \u001b[0;32m~/projects/emlearn-micropython/benchmarks/iir/iir_run_subprocess.py:48\u001b[0m, in \u001b[0;36mrun_iir\u001b[0;34m(data, coefficients, micropython_bin)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[38;5;28mprint\u001b[39m(out)\n\u001b[1;32m 47\u001b[0m \u001b[38;5;66;03m# load the output\u001b[39;00m\n\u001b[0;32m---> 48\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mexists(output_path), output_path\n\u001b[1;32m 49\u001b[0m output_shape, output \u001b[38;5;241m=\u001b[39m npyfile\u001b[38;5;241m.\u001b[39mload(output_path)\n\u001b[1;32m 51\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m output\n",
257+
"\u001b[0;31mAssertionError\u001b[0m: /tmp/tmp3kr9y4bm/output.npy"
258+
]
259+
}
260+
],
229261
"source": [
230-
"# TODO: run tests with eml_iir and eml_iir_q15, using scipy sosfilt as reference"
262+
"\n",
263+
"def spectrum_welch(signal, sr, n_fft, window='hann'):\n",
264+
" freqs, power = scipy.signal.welch(signal, fs=sr, nperseg=n_fft, window=window, scaling=\"spectrum\", average='mean')\n",
265+
" db = librosa.power_to_db(power, ref=0.0, top_db=240)\n",
266+
" return pandas.Series(db, index=freqs)\n",
267+
"\n",
268+
"\n",
269+
"def plot_spectrum_difference(ax, input, output, fs, label=None, alpha=0.5, n_fft = 512):\n",
270+
" \n",
271+
" S_in = spectrum_welch(input, sr=fs, n_fft=n_fft)\n",
272+
" S_out = spectrum_welch(output, sr=fs, n_fft=n_fft)\n",
273+
" \n",
274+
" FR = S_out - S_in\n",
275+
" FR += numpy.random.random(1)\n",
276+
" ax.plot(FR.index, FR.values, label=label, alpha=alpha)\n",
277+
" ax.set_xscale('log')\n",
278+
" ax.set_xlim(1, fs)\n",
279+
"\n",
280+
"def sosfilt_emlearn(coeff : numpy.array, input : numpy.array):\n",
281+
" \"\"\"\n",
282+
" Calls MicroPython in a subprocess, using eml_iir modules from emlearn-micropython\n",
283+
" \"\"\"\n",
284+
" from iir_run_subprocess import run_iir\n",
285+
" \n",
286+
" out = run_iir(array.array('f', input), array.array('f', coeff.flatten()))\n",
287+
"\n",
288+
" out = numpy.array(out)\n",
289+
" \n",
290+
" return out\n",
291+
"\n",
292+
"noise = numpy.random.random(1000)\n",
293+
"out_scipy = scipy.signal.sosfilt(coeff, noise)\n",
294+
"out_emlearn = sosfilt_emlearn(numpy.array(coeff), noise)\n",
295+
"\n",
296+
"fig, ax = plt.subplots(1, figsize=(10, 5))\n",
297+
"plot_spectrum_difference(ax, noise, out_scipy, fs=fs, label='scipy')\n",
298+
"plot_spectrum_difference(ax, noise, out_emlearn, fs=fs, label='emlearn')\n",
299+
"ax.legend()\n",
300+
"\n",
301+
"#numpy.save('noise.npy', noise)\n"
231302
]
303+
},
304+
{
305+
"cell_type": "code",
306+
"execution_count": null,
307+
"id": "e1597230-78a9-45e2-a1af-e8bdc0e5388e",
308+
"metadata": {},
309+
"outputs": [],
310+
"source": []
311+
},
312+
{
313+
"cell_type": "code",
314+
"execution_count": null,
315+
"id": "7546d0f2-29fa-420c-8b0a-13faf8919fc7",
316+
"metadata": {},
317+
"outputs": [],
318+
"source": []
232319
}
233320
],
234321
"metadata": {

benchmarks/iir/iir_run.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
2+
import sys
3+
import npyfile
4+
5+
def iir_process_file(inp, out, filters, chunksize):
6+
7+
coefficients_shape, coefficients = npyfile.load(filters)
8+
print('c', coefficients_shape, coefficients)
9+
10+
# NOTE: filter shape and type checked by the C modules
11+
12+
# Processing is done by streaming, to keep RAM usage low
13+
with npyfile.Reader(inp) as reader:
14+
15+
# Check inputs and setup filters
16+
print('r', reader.shape, reader.typecode, reader.itemsize)
17+
18+
if len(reader.shape) != 1:
19+
raise ValueError("Input must be 1d")
20+
if reader.typecode == 'f':
21+
import emliir
22+
filter = emliir.new(coefficients)
23+
elif reader.typecode == 'h':
24+
import eml_iir_q15
25+
filter = eml_iir_q15.new(coefficients)
26+
else:
27+
raise ValueError("Input must either be float32/f or int16/h")
28+
29+
30+
with npyfile.Writer(inp, reader.shape, reader.typecode) as writer:
31+
32+
# Apply filters
33+
for chunk in reader.read_data_chunks(chunksize):
34+
print(len(chunk))
35+
36+
filter.run(chunk) # operates in-place
37+
38+
writer.write_values(chunk)
39+
40+
41+
def main():
42+
43+
args = sys.argv
44+
if len(args) < 3:
45+
print('Usage: micropython iir_run.py INPUT.npy OUTPUT.npy FILTERS.npy')
46+
47+
_, inp, out, filters = args
48+
49+
chunksize = 100
50+
51+
iir_process_file(inp, out, filters, chunksize=chunksize)
52+
53+
if __name__ == '__main__':
54+
main()
55+

benchmarks/iir/iir_run_subprocess.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
2+
import os
3+
import array
4+
import tempfile
5+
import subprocess
6+
7+
import npyfile
8+
9+
10+
here = os.path.dirname(__file__)
11+
12+
13+
def run_iir(data : array.array,
14+
coefficients : array.array,
15+
micropython_bin='micropython',
16+
):
17+
18+
script_path = os.path.join(here, 'iir_run.py')
19+
20+
with tempfile.TemporaryDirectory() as temp_dir:
21+
22+
filter_path = os.path.join(temp_dir, 'filter.npy')
23+
input_path = os.path.join(temp_dir, 'input.npy')
24+
output_path = os.path.join(temp_dir, 'output.npy')
25+
26+
# write input data to files
27+
npyfile.save(filter_path, coefficients)
28+
npyfile.save(input_path, data)
29+
30+
assert not os.path.exists(output_path), output_path
31+
32+
# run the processing function
33+
args = [
34+
micropython_bin,
35+
script_path,
36+
input_path,
37+
output_path,
38+
filter_path,
39+
]
40+
cmd = ' '.join(args)
41+
42+
print('run: ', cmd)
43+
44+
out = subprocess.check_output(args)
45+
print(out)
46+
47+
# load the output
48+
assert os.path.exists(output_path), output_path
49+
output_shape, output = npyfile.load(output_path)
50+
51+
return output

0 commit comments

Comments
 (0)