225 lines
6.4 KiB
ReStructuredText
Executable File
225 lines
6.4 KiB
ReStructuredText
Executable File
API
|
|
===
|
|
|
|
.. highlight:: python
|
|
|
|
The idea is to follow as closely as possible the CMSIS-DSP API to ease the migration to the final implementation on a board.
|
|
|
|
First you need to import the module::
|
|
|
|
import cmsisdsp as dsp
|
|
|
|
If you use numpy::
|
|
|
|
import numpy as np
|
|
|
|
If you use scipy signal processing functions::
|
|
|
|
from scipy import signal
|
|
|
|
|
|
Standard APIs
|
|
*************
|
|
|
|
.. code-block:: C
|
|
|
|
void arm_add_f32(
|
|
const float32_t * pSrcA,
|
|
const float32_t * pSrcB,
|
|
float32_t * pDst,
|
|
uint32_t blockSize);
|
|
|
|
.. py:function:: dsp.arm_add_f32(pSrcA,pSrcB)
|
|
|
|
Return a list of random ingredients as strings.
|
|
|
|
:param pSrcA: array.
|
|
:type pSrcA: NumPy array
|
|
:param pSrcB: array.
|
|
:type pSrcB: NumPy array
|
|
:return: array.
|
|
:rtype: NumPy array
|
|
|
|
Example::
|
|
|
|
import cmsisdsp as dsp
|
|
|
|
r = dsp.arm_add_f32([1.,2,3],[4.,5,7])
|
|
|
|
You can use a CMSIS-DSP function with numpy arrays: ::
|
|
|
|
r = dsp.arm_add_f32(np.array([1.,2,3]),np.array([4.,5,7]))
|
|
|
|
The result of a CMSIS-DSP function will always be a numpy array whatever the arguments were (numpy array or list).
|
|
|
|
Functions with instance arguments
|
|
*********************************
|
|
|
|
When the CMSIS-DSP function is requiring an instance data structure, it is just a bit more complex to use it:
|
|
|
|
First you need to create this instance::
|
|
|
|
import cmsisdsp as dsp
|
|
|
|
firf32 = dsp.arm_fir_instance_f32()
|
|
|
|
.. code-block:: C
|
|
|
|
void arm_fir_init_f32(
|
|
arm_fir_instance_f32 * S,
|
|
uint16_t numTaps,
|
|
const float32_t * pCoeffs,
|
|
float32_t * pState,
|
|
uint32_t blockSize);
|
|
|
|
|
|
.. py:function:: dsp.arm_fir_init_f32(S,numTaps,pCoeffs,pState)
|
|
|
|
Return a list of random ingredients as strings.
|
|
|
|
:param S: f32 instance.
|
|
:type S: int
|
|
:param pCoeffs: array.
|
|
:type pCoeffs: NumPy array
|
|
:param pState: array.
|
|
:type pState: NumPy array
|
|
:return: array.
|
|
:rtype: NumPy array
|
|
|
|
Example of use::
|
|
|
|
dsp.arm_fir_init_f32(firf32,3,[1.,2,3],[0,0,0,0,0,0,0])
|
|
|
|
The third argument in this function is the state. Since all arguments (except the instance ones) are read-only in this Python API, this state will never be changed ! It is just used to communicate the length of the state array which must be allocated by the init function. This argument is required because it is present in the CMSIS-DSP API and in the final C implementation you'll need to allocate a state array with the right dimension.
|
|
|
|
Since the goal is to be as close as possible to the C API, the API is forcing the use of this argument.
|
|
|
|
The only change compared to the C API is that the size variables (like blockSize for filter) are computed automatically from the other arguments. This choice was made to make it a bit easier the use of numpy array with the API.
|
|
|
|
Now, you can check that the instance was initialized correctly::
|
|
|
|
.. code-block:: python
|
|
|
|
print(firf32.numTaps())
|
|
|
|
The filter can then be called:
|
|
|
|
.. code-block:: C
|
|
|
|
void arm_fir_f32(
|
|
const arm_fir_instance_f32 * S,
|
|
const float32_t * pSrc,
|
|
float32_t * pDst,
|
|
uint32_t blockSize);
|
|
|
|
.. py:function:: dsp.arm_fir_f32(S,pSrc)
|
|
|
|
Return a list of random ingredients as strings.
|
|
|
|
:param S: f32 instance.
|
|
:type S: int
|
|
:param pSrc: array of input samples.
|
|
:type pSrc: NumPy array
|
|
:return: array.
|
|
:rtype: NumPy array
|
|
|
|
Then, you can filter with CMSIS-DSP::
|
|
|
|
print(dsp.arm_fir_f32(firf32,[1,2,3,4,5]))
|
|
|
|
The size of this signal should be blockSize. blockSize was inferred from the size of the state array : numTaps + blockSize - 1 according to CMSIS-DSP. So here the signal must have 5 samples.
|
|
|
|
If you want to filter more than 5 samples, then you can just call the function again. The state variable inside firf32 will ensure that it works like in the CMSIS-DSP C code::
|
|
|
|
print(dsp.arm_fir_f32(firf32,[6,7,8,9,10]))
|
|
|
|
If you want to compare with scipy it is easy but warning : coefficients for the filter are in opposite order in scipy ::
|
|
|
|
filtered_x = signal.lfilter([3,2,1.], 1.0, [1,2,3,4,5,6,7,8,9,10])
|
|
print(filtered_x)
|
|
|
|
FFT
|
|
***
|
|
|
|
The CMSIS-DSP cfft is requiring complex signals with a specific layout in memory.
|
|
|
|
To remain as close as possible to the C API, we are not using complex numbers in the wrapper. So a complex signal must be converted into a real one. A function like the bewlo one can be used::
|
|
|
|
def imToReal1D(a):
|
|
ar=np.zeros(np.array(a.shape) * 2)
|
|
ar[0::2]=a.real
|
|
ar[1::2]=a.imag
|
|
return(ar)
|
|
|
|
In the same way, the return array from the CMSIS-DSP FFT will not be containing complex Python scalars. It must be converted back with a function like::
|
|
|
|
def realToIm1D(ar):
|
|
return(ar[0::2] + 1j * ar[1::2])
|
|
|
|
Then, the utilisation of the API si very similar to what was done for the FIR example:
|
|
|
|
Then, you create the FFT instance with::
|
|
|
|
cfftf32=dsp.arm_cfft_instance_f32()
|
|
|
|
You initialize the instance with the init function ::
|
|
|
|
status=dsp.arm_cfft_init_f32(cfftf32, nb)
|
|
print(status)
|
|
|
|
You convert the complex signal to the format expected by the wrapper::
|
|
|
|
signalR = imToReal1D(signal)
|
|
|
|
You compute the FFT of the signal with::
|
|
|
|
resultR = dsp.arm_cfft_f32(cfftf32,signalR,0,1)
|
|
|
|
You convert back to a complex format to compare with scipy::
|
|
|
|
resultI = realToIm1D(resultR)
|
|
print(resultI)
|
|
|
|
Matrix
|
|
******
|
|
|
|
For matrix, the instance variables are masked by the Python API. We decided that for matrix only there was no use for having the CMSIS-DSP instance visibles since they contain the same information as the numpy array (samples and dimension).
|
|
|
|
So to use a CMSIS-DSP matrix function, it is very simple::
|
|
|
|
a=np.array([[1.,2,3,4],[5,6,7,8],[9,10,11,12]])
|
|
b=np.array([[1.,2,3],[5.1,6,7],[9.1,10,11],[5,8,4]])
|
|
|
|
Numpy result as reference::
|
|
|
|
print(np.dot(a , b))
|
|
|
|
CMSIS-DSP result::
|
|
|
|
v=dsp.arm_mat_mult_f32(a,b)
|
|
print(v)
|
|
|
|
In a real C code, a pointer to a data structure for the result v would have to be passed as argument of the function.
|
|
|
|
The C API is:
|
|
|
|
.. code-block:: C
|
|
|
|
arm_status arm_mat_mult_f32(
|
|
const arm_matrix_instance_f32 * pSrcA,
|
|
const arm_matrix_instance_f32 * pSrcB,
|
|
arm_matrix_instance_f32 * pDst);
|
|
|
|
The Python API is:
|
|
|
|
|
|
.. py:function:: dsp.arm_mat_mult_f32(pSrcA,pSrcB)
|
|
|
|
Return the matrix product pSrcA * pSrcB
|
|
|
|
:param pSrcA: array of input samples.
|
|
:type pSrcA: NumPy array
|
|
:param pSrcB: array of input samples.
|
|
:type pSrcB: NumPy array
|
|
:return: the matrix product.
|
|
:rtype: NumPy array |