Skip to content

Commit 67538b0

Browse files
committed
move spectrogram to utils module
1 parent 9dc9b77 commit 67538b0

File tree

13 files changed

+244
-238
lines changed

13 files changed

+244
-238
lines changed

code/scipy/scipy.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,4 @@ mp_obj_module_t ulab_scipy_module = {
4848
.base = { &mp_type_module },
4949
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_globals,
5050
};
51-
#endif
51+
#endif /* ULAB_HAS_SCIPY */

code/scipy/signal/signal.c

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,39 +19,6 @@
1919
#include "../../ulab.h"
2020
#include "../../ndarray.h"
2121
#include "../../numpy/carray/carray_tools.h"
22-
#include "../../numpy/fft/fft_tools.h"
23-
24-
#if ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM
25-
//| import ulab.numpy
26-
//|
27-
//| def spectrogram(r: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
28-
//| """
29-
//| :param ulab.numpy.ndarray r: A 1-dimension array of values whose size is a power of 2
30-
//|
31-
//| Computes the spectrum of the input signal. This is the absolute value of the (complex-valued) fft of the signal.
32-
//| This function is similar to scipy's ``scipy.signal.spectrogram``."""
33-
//| ...
34-
//|
35-
36-
mp_obj_t signal_spectrogram(size_t n_args, const mp_obj_t *args) {
37-
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
38-
return fft_fft_ifft_spectrogram(args[0], FFT_SPECTROGRAM);
39-
#else
40-
if(n_args == 2) {
41-
return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_SPECTROGRAM);
42-
} else {
43-
return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_SPECTROGRAM);
44-
}
45-
#endif
46-
}
47-
48-
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
49-
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(signal_spectrogram_obj, 1, 1, signal_spectrogram);
50-
#else
51-
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(signal_spectrogram_obj, 1, 2, signal_spectrogram);
52-
#endif
53-
54-
#endif /* ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM */
5522

5623
#if ULAB_SCIPY_SIGNAL_HAS_SOSFILT & ULAB_MAX_DIMS > 1
5724
static void signal_sosfilt_array(mp_float_t *x, const mp_float_t *coeffs, mp_float_t *zf, const size_t len) {
@@ -155,9 +122,6 @@ MP_DEFINE_CONST_FUN_OBJ_KW(signal_sosfilt_obj, 2, signal_sosfilt);
155122

156123
static const mp_rom_map_elem_t ulab_scipy_signal_globals_table[] = {
157124
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_signal) },
158-
#if ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM
159-
{ MP_OBJ_NEW_QSTR(MP_QSTR_spectrogram), (mp_obj_t)&signal_spectrogram_obj },
160-
#endif
161125
#if ULAB_SCIPY_SIGNAL_HAS_SOSFILT & ULAB_MAX_DIMS > 1
162126
{ MP_OBJ_NEW_QSTR(MP_QSTR_sosfilt), (mp_obj_t)&signal_sosfilt_obj },
163127
#endif

code/scipy/signal/signal.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
extern mp_obj_module_t ulab_scipy_signal_module;
2020

21-
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(signal_spectrogram_obj);
2221
MP_DECLARE_CONST_FUN_OBJ_KW(signal_sosfilt_obj);
2322

2423
#endif /* _SCIPY_SIGNAL_ */

code/ulab.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
#include "user/user.h"
3434
#include "utils/utils.h"
3535

36-
#define ULAB_VERSION 4.4.0
36+
#define ULAB_VERSION 4.4.1
3737
#define xstr(s) str(s)
3838
#define str(s) #s
3939

code/ulab.h

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -663,10 +663,6 @@
663663
#define ULAB_SCIPY_HAS_SIGNAL_MODULE (1)
664664
#endif
665665

666-
#ifndef ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM
667-
#define ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM (1)
668-
#endif
669-
670666
#ifndef ULAB_SCIPY_SIGNAL_HAS_SOSFILT
671667
#define ULAB_SCIPY_SIGNAL_HAS_SOSFILT (1)
672668
#endif
@@ -711,12 +707,7 @@
711707
#define ULAB_SCIPY_SPECIAL_HAS_GAMMALN (1)
712708
#endif
713709

714-
// user-defined module; source of the module and
715-
// its sub-modules should be placed in code/user/
716-
#ifndef ULAB_HAS_USER_MODULE
717-
#define ULAB_HAS_USER_MODULE (0)
718-
#endif
719-
710+
// functions of the utils module
720711
#ifndef ULAB_HAS_UTILS_MODULE
721712
#define ULAB_HAS_UTILS_MODULE (1)
722713
#endif
@@ -737,4 +728,14 @@
737728
#define ULAB_UTILS_HAS_FROM_UINT32_BUFFER (1)
738729
#endif
739730

731+
#ifndef ULAB_UTILS_HAS_SPECTROGRAM
732+
#define ULAB_UTILS_HAS_SPECTROGRAM (1)
733+
#endif
734+
735+
// user-defined module; source of the module and
736+
// its sub-modules should be placed in code/user/
737+
#ifndef ULAB_HAS_USER_MODULE
738+
#define ULAB_HAS_USER_MODULE (0)
739+
#endif
740+
740741
#endif

code/utils/utils.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include "py/misc.h"
1717
#include "utils.h"
1818

19+
#include "../numpy/fft/fft_tools.h"
20+
1921
#if ULAB_HAS_UTILS_MODULE
2022

2123
enum UTILS_BUFFER_TYPE {
@@ -187,8 +189,41 @@ static mp_obj_t utils_from_uint32_buffer(size_t n_args, const mp_obj_t *pos_args
187189
MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_uint32_buffer_obj, 1, utils_from_uint32_buffer);
188190
#endif
189191

192+
#endif /* ULAB_UTILS_HAS_FROM_INT16_BUFFER | ULAB_UTILS_HAS_FROM_UINT16_BUFFER | ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER */
193+
194+
#if ULAB_UTILS_HAS_SPECTROGRAM
195+
//| import ulab.numpy
196+
//|
197+
//| def spectrogram(r: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
198+
//| """
199+
//| :param ulab.numpy.ndarray r: A 1-dimension array of values whose size is a power of 2
200+
//|
201+
//| Computes the spectrum of the input signal. This is the absolute value of the (complex-valued) fft of the signal.
202+
//| This function is similar to scipy's ``scipy.signal.welch`` https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.welch.html."""
203+
//| ...
204+
//|
205+
206+
mp_obj_t utils_spectrogram(size_t n_args, const mp_obj_t *args) {
207+
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
208+
return fft_fft_ifft_spectrogram(args[0], FFT_SPECTROGRAM);
209+
#else
210+
if(n_args == 2) {
211+
return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_SPECTROGRAM);
212+
} else {
213+
return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_SPECTROGRAM);
214+
}
215+
#endif
216+
}
217+
218+
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
219+
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(utils_spectrogram_obj, 1, 1, utils_spectrogram);
220+
#else
221+
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(utils_spectrogram_obj, 1, 2, utils_spectrogram);
190222
#endif
191223

224+
#endif /* ULAB_UTILS_HAS_SPECTROGRAM */
225+
226+
192227
static const mp_rom_map_elem_t ulab_utils_globals_table[] = {
193228
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_utils) },
194229
#if ULAB_UTILS_HAS_FROM_INT16_BUFFER
@@ -203,6 +238,9 @@ static const mp_rom_map_elem_t ulab_utils_globals_table[] = {
203238
#if ULAB_UTILS_HAS_FROM_UINT32_BUFFER
204239
{ MP_OBJ_NEW_QSTR(MP_QSTR_from_uint32_buffer), (mp_obj_t)&utils_from_uint32_buffer_obj },
205240
#endif
241+
#if ULAB_UTILS_HAS_SPECTROGRAM
242+
{ MP_OBJ_NEW_QSTR(MP_QSTR_spectrogram), (mp_obj_t)&utils_spectrogram_obj },
243+
#endif
206244
};
207245

208246
static MP_DEFINE_CONST_DICT(mp_module_ulab_utils_globals, ulab_utils_globals_table);
@@ -212,4 +250,4 @@ mp_obj_module_t ulab_utils_module = {
212250
.globals = (mp_obj_dict_t*)&mp_module_ulab_utils_globals,
213251
};
214252

215-
#endif
253+
#endif /* ULAB_HAS_UTILS_MODULE */

docs/manual/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
author = 'Zoltán Vörös'
2828

2929
# The full version, including alpha/beta/rc tags
30-
release = '4.4.0'
30+
release = '4.4.1'
3131

3232

3333
# -- General configuration ---------------------------------------------------

docs/manual/source/scipy-signal.rst

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ Functions in the ``signal`` module can be called by prepending them by
66
``scipy.signal.``. The module defines the following two functions:
77

88
1. `scipy.signal.sosfilt <#sosfilt>`__
9-
2. `scipy.signal.spectrogram <#spectrogram>`__
109

1110
sosfilt
1211
-------
@@ -69,69 +68,3 @@ initial values are assumed to be 0.
6968
7069
7170
72-
73-
spectrogram
74-
-----------
75-
76-
In addition to the Fourier transform and its inverse, ``ulab`` also
77-
sports a function called ``spectrogram``, which returns the absolute
78-
value of the Fourier transform. This could be used to find the dominant
79-
spectral component in a time series. The arguments are treated in the
80-
same way as in ``fft``, and ``ifft``. This means that, if the firmware
81-
was compiled with complex support, the input can also be a complex
82-
array.
83-
84-
.. code::
85-
86-
# code to be run in micropython
87-
88-
from ulab import numpy as np
89-
from ulab import scipy as spy
90-
91-
x = np.linspace(0, 10, num=1024)
92-
y = np.sin(x)
93-
94-
a = spy.signal.spectrogram(y)
95-
96-
print('original vector:\t', y)
97-
print('\nspectrum:\t', a)
98-
99-
.. parsed-literal::
100-
101-
original vector: array([0.0, 0.009775015390171337, 0.01954909674625918, ..., -0.5275140569487312, -0.5357931822978732, -0.5440211108893639], dtype=float64)
102-
103-
spectrum: array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
104-
105-
106-
107-
108-
As such, ``spectrogram`` is really just a shorthand for
109-
``np.sqrt(a*a + b*b)``:
110-
111-
.. code::
112-
113-
# code to be run in micropython
114-
115-
from ulab import numpy as np
116-
from ulab import scipy as spy
117-
118-
x = np.linspace(0, 10, num=1024)
119-
y = np.sin(x)
120-
121-
a, b = np.fft.fft(y)
122-
123-
print('\nspectrum calculated the hard way:\t', np.sqrt(a*a + b*b))
124-
125-
a = spy.signal.spectrogram(y)
126-
127-
print('\nspectrum calculated the lazy way:\t', a)
128-
129-
.. parsed-literal::
130-
131-
132-
spectrum calculated the hard way: array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
133-
134-
spectrum calculated the lazy way: array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
135-
136-
137-

docs/manual/source/ulab-utils.rst

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,76 @@ These two functions are identical to ``utils.from_int32_buffer``, and
137137
``utils.from_uint32_buffer``, with the exception that they convert
138138
16-bit integers to floating point ``ndarray``\ s.
139139

140+
spectrogram
141+
-----------
142+
143+
In addition to the Fourier transform and its inverse, ``ulab`` also
144+
sports a function called ``spectrogram``, which returns the absolute
145+
value of the Fourier transform, also known as the power spectrum. This
146+
could be used to find the dominant spectral component in a time series.
147+
The arguments are treated in the same way as in ``fft``, and ``ifft``.
148+
This means that, if the firmware was compiled with complex support, the
149+
input can also be a complex array.
150+
151+
.. code::
152+
153+
# code to be run in micropython
154+
155+
from ulab import numpy as np
156+
from ulab import utils as utils
157+
158+
x = np.linspace(0, 10, num=1024)
159+
y = np.sin(x)
160+
161+
a = utils.spectrogram(y)
162+
163+
print('original vector:\n', y)
164+
print('\nspectrum:\n', a)
165+
166+
.. parsed-literal::
167+
168+
original vector:
169+
array([0.0, 0.009775015390171337, 0.01954909674625918, ..., -0.5275140569487312, -0.5357931822978732, -0.5440211108893697], dtype=float64)
170+
171+
spectrum:
172+
array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
173+
174+
175+
176+
177+
As such, ``spectrogram`` is really just a shorthand for
178+
``np.sqrt(a*a + b*b)``, however, it saves significant amounts of RAM:
179+
the expression ``a*a + b*b`` has to allocate memory for ``a*a``,
180+
``b*b``, and finally, their sum. In contrast, ``spectrogram`` calculates
181+
the spectrum internally, and stores it in the memory segment that was
182+
reserved for the real part of the Fourier transform.
183+
140184
.. code::
185+
186+
# code to be run in micropython
187+
188+
from ulab import numpy as np
189+
from ulab import utils as utils
190+
191+
x = np.linspace(0, 10, num=1024)
192+
y = np.sin(x)
193+
194+
a, b = np.fft.fft(y)
195+
196+
print('\nspectrum calculated the hard way:\n', np.sqrt(a*a + b*b))
197+
198+
a = utils.spectrogram(y)
199+
200+
print('\nspectrum calculated the lazy way:\n', a)
201+
202+
.. parsed-literal::
141203
142-
# code to be run in CPython
143204
205+
spectrum calculated the hard way:
206+
array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
207+
208+
spectrum calculated the lazy way:
209+
array([187.8635087634579, 315.3112063607119, 347.8814873399374, ..., 84.45888934298905, 347.8814873399374, 315.3112063607118], dtype=float64)
210+
211+
212+

0 commit comments

Comments
 (0)