First release of the Numpy compatible Python wrapper for the CMSIS-DSP.

pull/19/head
Christophe Favergeon 7 years ago
parent a8cf6e9bb1
commit bb426fa559

@ -0,0 +1,179 @@
# README
This Python wrapper for CMSIS-DSP is compatible with numpy.
It is a very experimental wrapper with lots of limitations as described in the corresponding section below.
But even with those limitations it can be very useful to test a CMSIS-DSP implemention of an algorithm with all the power oif numpy and scipy.
# How to build and install
The build is using a customized arm_math.h in folder cmsisdsp_pkg/src to be able to compile on windows.
As a consequence, if you build on an Arm computer, you won't get the optimizations of the CMSIS library. It is possible to get them by replacing the customized arm_math.h by the official one.
Following command will build in place.
> python setup.py build_ext --inplace
If you launch Python from same directory you'll be able to play with the test scripts.
If you want to install the cmsisdsp package, you need to build a binary distribution with
> python setup.py bdist
Then it is advised to install it into a virtualenv
For instance, with Python 3:
Create a folder for this virtual environment. For instance : cmsisdsp_tests
Go to this folder.
Type:
> python -m venv env
Activate the environment:
> env\Scripts\activate
Install some packages
> pip install numpy
> pip instal scipy
> pip instal matplotplib
Now, you can install the cmsisdsp package:
> pip install -e "Path To The Folder Containing setup.py"
# Usage
You can looks at testdsp.py and example.py for some examples.
The idea is to follow as close 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
Then you can use a CMSIS-DSP function:
> r = dsp.arm_add_f32(np.array([1.,2,3]),np.array([4.,5,7]))
The function call also be called more simply with
> r = dsp.arm_add_f32([1.,2,3],[4.,5,7])
But the result of a CMSIS-DSP function will always be a numpy array whatever the arguments were (numpy array or list).
When the CMSIS-DSP function is requiring an instance data structure, it is just a bit more complex:
First we create this instance.
> firf32 = dsp.arm_fir_instance_f32()
Although the initialization function on Python side can also be used to initialize some of the fields of the corresponding instance using named arguments it is not advised to do so. In CMSIS-DSP there are init function for this and they can do some additional processing.
So, we need to call the init function
> dsp.arm_fir_init_f32(firf32,3,[1.,2,3],[0,0,0,0,0,0,0])
The third arument in this function is the state. Since all arguments (except the instance one) 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.
print(firf32.numTaps())
filtered_x = signal.lfilter([3,2,1.], 1.0, [1,2,3,4,5,1,2,3,4,5])
print(filtered_x)
Then you can filter a signal.
> 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.
So if you want to filter more them 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,[1,2,3,4,5]))
If you want vot compare with scipy it is easy bu warning : coefficients for the filter are in opposite order:
> filtered_x = signal.lfilter([3,2,1.], 1.0, [1,2,3,4,5,1,2,3,4,5])
> print(filtered_x)
The principles are the same for all other APIs.
For Fourier transforms there are no init functions for the instance variables which must be initialized from a C struct. To make it simpler to use them from Python, the wrapper is introducing its own init functions.
So, for instance:
The signal we want to use for the FFT.
> nb = 16
> signal = np.cos(2 * np.pi * np.arange(nb) / nb)
The CMSIS-DSP cfft is requring complex signal 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 the complex signal must be converted into a real one. The function imToReal1D is defined in testdsp.py
> signalR = imToReal1D(signal)
The we create our FFT instance:
> cfftf32=dsp.arm_cfft_instance_f32()
We initialize the instance with the init function provided by the wrapper:
> status=dsp.arm_cfft_init_f32(cfftf32,nb)
> print(status)
We compute the FFT:
> resultR = dsp.arm_cfft_f32(cfftf32,signalR,0,1)
We converte back to a complex format to compare with scipy:
> resultI = realToIm1D(resultR)
> print(resultI)
For matrix the instances are masked by the Python API. We decided that for matrix only there was no use for making the CMSIS-DSP instance visibles since they contain the same information as the numpy array (samples, width 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.
## example.py
This example depends on a data file which can be downloaded here:
https://www.physionet.org/pn3/ecgiddb/Person_87/rec_2.dat
# LIMITATIONS
Due to the high number of functions in the CMSIS-DSP, the first version of the wrapper was generated automatically from a custom script.
Only a subset of the functions has been tested.
It is likely that some problems are present. The API is quite regular in CMSIS-DSP but there are a few exceptions and the generation script is probably not managing all of them.
So, API may crash due to unallocated variable or wrong data conversion.
The generated C code is a first version for bootstrapping the process. Now that this C file exists, the improvement will be done on it rather than on the generation script.

File diff suppressed because it is too large Load Diff

@ -0,0 +1,121 @@
/* ----------------------------------------------------------------------
* Project: CMSIS DSP Python Wrapper
* Title: arm_common_tables.h
* Description: Extern declaration for common tables
*
* $Date: 25. March 2019
* $Revision: V.1.5.1
*
* Target Processor: Cortex-M cores
* -------------------------------------------------------------------- */
/*
* Copyright (C) 2010-2019 ARM Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _ARM_COMMON_TABLES_H
#define _ARM_COMMON_TABLES_H
#include "arm_math.h"
extern const uint16_t armBitRevTable[1024];
extern const q15_t armRecipTableQ15[64];
extern const q31_t armRecipTableQ31[64];
extern const float32_t twiddleCoef_16[32];
extern const float32_t twiddleCoef_32[64];
extern const float32_t twiddleCoef_64[128];
extern const float32_t twiddleCoef_128[256];
extern const float32_t twiddleCoef_256[512];
extern const float32_t twiddleCoef_512[1024];
extern const float32_t twiddleCoef_1024[2048];
extern const float32_t twiddleCoef_2048[4096];
extern const float32_t twiddleCoef_4096[8192];
#define twiddleCoef twiddleCoef_4096
extern const q31_t twiddleCoef_16_q31[24];
extern const q31_t twiddleCoef_32_q31[48];
extern const q31_t twiddleCoef_64_q31[96];
extern const q31_t twiddleCoef_128_q31[192];
extern const q31_t twiddleCoef_256_q31[384];
extern const q31_t twiddleCoef_512_q31[768];
extern const q31_t twiddleCoef_1024_q31[1536];
extern const q31_t twiddleCoef_2048_q31[3072];
extern const q31_t twiddleCoef_4096_q31[6144];
extern const q15_t twiddleCoef_16_q15[24];
extern const q15_t twiddleCoef_32_q15[48];
extern const q15_t twiddleCoef_64_q15[96];
extern const q15_t twiddleCoef_128_q15[192];
extern const q15_t twiddleCoef_256_q15[384];
extern const q15_t twiddleCoef_512_q15[768];
extern const q15_t twiddleCoef_1024_q15[1536];
extern const q15_t twiddleCoef_2048_q15[3072];
extern const q15_t twiddleCoef_4096_q15[6144];
extern const float32_t twiddleCoef_rfft_32[32];
extern const float32_t twiddleCoef_rfft_64[64];
extern const float32_t twiddleCoef_rfft_128[128];
extern const float32_t twiddleCoef_rfft_256[256];
extern const float32_t twiddleCoef_rfft_512[512];
extern const float32_t twiddleCoef_rfft_1024[1024];
extern const float32_t twiddleCoef_rfft_2048[2048];
extern const float32_t twiddleCoef_rfft_4096[4096];
/* floating-point bit reversal tables */
#define ARMBITREVINDEXTABLE_16_TABLE_LENGTH ((uint16_t)20)
#define ARMBITREVINDEXTABLE_32_TABLE_LENGTH ((uint16_t)48)
#define ARMBITREVINDEXTABLE_64_TABLE_LENGTH ((uint16_t)56)
#define ARMBITREVINDEXTABLE_128_TABLE_LENGTH ((uint16_t)208)
#define ARMBITREVINDEXTABLE_256_TABLE_LENGTH ((uint16_t)440)
#define ARMBITREVINDEXTABLE_512_TABLE_LENGTH ((uint16_t)448)
#define ARMBITREVINDEXTABLE_1024_TABLE_LENGTH ((uint16_t)1800)
#define ARMBITREVINDEXTABLE_2048_TABLE_LENGTH ((uint16_t)3808)
#define ARMBITREVINDEXTABLE_4096_TABLE_LENGTH ((uint16_t)4032)
extern const uint16_t armBitRevIndexTable16[ARMBITREVINDEXTABLE_16_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable32[ARMBITREVINDEXTABLE_32_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable64[ARMBITREVINDEXTABLE_64_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable128[ARMBITREVINDEXTABLE_128_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable256[ARMBITREVINDEXTABLE_256_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable512[ARMBITREVINDEXTABLE_512_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable1024[ARMBITREVINDEXTABLE_1024_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable2048[ARMBITREVINDEXTABLE_2048_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable4096[ARMBITREVINDEXTABLE_4096_TABLE_LENGTH];
/* fixed-point bit reversal tables */
#define ARMBITREVINDEXTABLE_FIXED_16_TABLE_LENGTH ((uint16_t)12)
#define ARMBITREVINDEXTABLE_FIXED_32_TABLE_LENGTH ((uint16_t)24)
#define ARMBITREVINDEXTABLE_FIXED_64_TABLE_LENGTH ((uint16_t)56)
#define ARMBITREVINDEXTABLE_FIXED_128_TABLE_LENGTH ((uint16_t)112)
#define ARMBITREVINDEXTABLE_FIXED_256_TABLE_LENGTH ((uint16_t)240)
#define ARMBITREVINDEXTABLE_FIXED_512_TABLE_LENGTH ((uint16_t)480)
#define ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH ((uint16_t)992)
#define ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH ((uint16_t)1984)
#define ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH ((uint16_t)4032)
extern const uint16_t armBitRevIndexTable_fixed_16[ARMBITREVINDEXTABLE_FIXED_16_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_32[ARMBITREVINDEXTABLE_FIXED_32_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_64[ARMBITREVINDEXTABLE_FIXED_64_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_128[ARMBITREVINDEXTABLE_FIXED_128_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_256[ARMBITREVINDEXTABLE_FIXED_256_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_512[ARMBITREVINDEXTABLE_FIXED_512_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_1024[ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_2048[ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_4096[ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH];
/* Tables for Fast Math Sine and Cosine */
extern const float32_t sinTable_f32[FAST_MATH_TABLE_SIZE + 1];
extern const q31_t sinTable_q31[FAST_MATH_TABLE_SIZE + 1];
extern const q15_t sinTable_q15[FAST_MATH_TABLE_SIZE + 1];
#endif /* ARM_COMMON_TABLES_H */

@ -0,0 +1,379 @@
/* ----------------------------------------------------------------------
* Project: CMSIS DSP Python Wrapper
* Title: arm_const_structs.c
* Description: Constant structs that are initialized for user convenience.
* For example, some can be given as arguments to the arm_cfft_f32() or arm_rfft_f32() functions.
*
* $Date: 25. March 2019
* $Revision: V.1.5.1
*
* Target Processor: Cortex-M cores
* -------------------------------------------------------------------- */
/*
* Copyright (C) 2010-2019 ARM Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "arm_const_structs.h"
/* Floating-point structs */
const arm_cfft_instance_f32 arm_cfft_sR_f32_len16 = {
16, twiddleCoef_16, armBitRevIndexTable16, ARMBITREVINDEXTABLE_16_TABLE_LENGTH
};
const arm_cfft_instance_f32 arm_cfft_sR_f32_len32 = {
32, twiddleCoef_32, armBitRevIndexTable32, ARMBITREVINDEXTABLE_32_TABLE_LENGTH
};
const arm_cfft_instance_f32 arm_cfft_sR_f32_len64 = {
64, twiddleCoef_64, armBitRevIndexTable64, ARMBITREVINDEXTABLE_64_TABLE_LENGTH
};
const arm_cfft_instance_f32 arm_cfft_sR_f32_len128 = {
128, twiddleCoef_128, armBitRevIndexTable128, ARMBITREVINDEXTABLE_128_TABLE_LENGTH
};
const arm_cfft_instance_f32 arm_cfft_sR_f32_len256 = {
256, twiddleCoef_256, armBitRevIndexTable256, ARMBITREVINDEXTABLE_256_TABLE_LENGTH
};
const arm_cfft_instance_f32 arm_cfft_sR_f32_len512 = {
512, twiddleCoef_512, armBitRevIndexTable512, ARMBITREVINDEXTABLE_512_TABLE_LENGTH
};
const arm_cfft_instance_f32 arm_cfft_sR_f32_len1024 = {
1024, twiddleCoef_1024, armBitRevIndexTable1024, ARMBITREVINDEXTABLE_1024_TABLE_LENGTH
};
const arm_cfft_instance_f32 arm_cfft_sR_f32_len2048 = {
2048, twiddleCoef_2048, armBitRevIndexTable2048, ARMBITREVINDEXTABLE_2048_TABLE_LENGTH
};
const arm_cfft_instance_f32 arm_cfft_sR_f32_len4096 = {
4096, twiddleCoef_4096, armBitRevIndexTable4096, ARMBITREVINDEXTABLE_4096_TABLE_LENGTH
};
/* Fixed-point structs */
const arm_cfft_instance_q31 arm_cfft_sR_q31_len16 = {
16, twiddleCoef_16_q31, armBitRevIndexTable_fixed_16, ARMBITREVINDEXTABLE_FIXED_16_TABLE_LENGTH
};
const arm_cfft_instance_q31 arm_cfft_sR_q31_len32 = {
32, twiddleCoef_32_q31, armBitRevIndexTable_fixed_32, ARMBITREVINDEXTABLE_FIXED_32_TABLE_LENGTH
};
const arm_cfft_instance_q31 arm_cfft_sR_q31_len64 = {
64, twiddleCoef_64_q31, armBitRevIndexTable_fixed_64, ARMBITREVINDEXTABLE_FIXED_64_TABLE_LENGTH
};
const arm_cfft_instance_q31 arm_cfft_sR_q31_len128 = {
128, twiddleCoef_128_q31, armBitRevIndexTable_fixed_128, ARMBITREVINDEXTABLE_FIXED_128_TABLE_LENGTH
};
const arm_cfft_instance_q31 arm_cfft_sR_q31_len256 = {
256, twiddleCoef_256_q31, armBitRevIndexTable_fixed_256, ARMBITREVINDEXTABLE_FIXED_256_TABLE_LENGTH
};
const arm_cfft_instance_q31 arm_cfft_sR_q31_len512 = {
512, twiddleCoef_512_q31, armBitRevIndexTable_fixed_512, ARMBITREVINDEXTABLE_FIXED_512_TABLE_LENGTH
};
const arm_cfft_instance_q31 arm_cfft_sR_q31_len1024 = {
1024, twiddleCoef_1024_q31, armBitRevIndexTable_fixed_1024, ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH
};
const arm_cfft_instance_q31 arm_cfft_sR_q31_len2048 = {
2048, twiddleCoef_2048_q31, armBitRevIndexTable_fixed_2048, ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH
};
const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096 = {
4096, twiddleCoef_4096_q31, armBitRevIndexTable_fixed_4096, ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH
};
const arm_cfft_instance_q15 arm_cfft_sR_q15_len16 = {
16, twiddleCoef_16_q15, armBitRevIndexTable_fixed_16, ARMBITREVINDEXTABLE_FIXED_16_TABLE_LENGTH
};
const arm_cfft_instance_q15 arm_cfft_sR_q15_len32 = {
32, twiddleCoef_32_q15, armBitRevIndexTable_fixed_32, ARMBITREVINDEXTABLE_FIXED_32_TABLE_LENGTH
};
const arm_cfft_instance_q15 arm_cfft_sR_q15_len64 = {
64, twiddleCoef_64_q15, armBitRevIndexTable_fixed_64, ARMBITREVINDEXTABLE_FIXED_64_TABLE_LENGTH
};
const arm_cfft_instance_q15 arm_cfft_sR_q15_len128 = {
128, twiddleCoef_128_q15, armBitRevIndexTable_fixed_128, ARMBITREVINDEXTABLE_FIXED_128_TABLE_LENGTH
};
const arm_cfft_instance_q15 arm_cfft_sR_q15_len256 = {
256, twiddleCoef_256_q15, armBitRevIndexTable_fixed_256, ARMBITREVINDEXTABLE_FIXED_256_TABLE_LENGTH
};
const arm_cfft_instance_q15 arm_cfft_sR_q15_len512 = {
512, twiddleCoef_512_q15, armBitRevIndexTable_fixed_512, ARMBITREVINDEXTABLE_FIXED_512_TABLE_LENGTH
};
const arm_cfft_instance_q15 arm_cfft_sR_q15_len1024 = {
1024, twiddleCoef_1024_q15, armBitRevIndexTable_fixed_1024, ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH
};
const arm_cfft_instance_q15 arm_cfft_sR_q15_len2048 = {
2048, twiddleCoef_2048_q15, armBitRevIndexTable_fixed_2048, ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH
};
const arm_cfft_instance_q15 arm_cfft_sR_q15_len4096 = {
4096, twiddleCoef_4096_q15, armBitRevIndexTable_fixed_4096, ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH
};
/* Structure for real-value inputs */
/* Floating-point structs */
const arm_rfft_fast_instance_f32 arm_rfft_fast_sR_f32_len32 = {
{ 16, twiddleCoef_32, armBitRevIndexTable32, ARMBITREVINDEXTABLE_16_TABLE_LENGTH },
32U,
(float32_t *)twiddleCoef_rfft_32
};
const arm_rfft_fast_instance_f32 arm_rfft_fast_sR_f32_len64 = {
{ 32, twiddleCoef_32, armBitRevIndexTable32, ARMBITREVINDEXTABLE_32_TABLE_LENGTH },
64U,
(float32_t *)twiddleCoef_rfft_64
};
const arm_rfft_fast_instance_f32 arm_rfft_fast_sR_f32_len128 = {
{ 64, twiddleCoef_64, armBitRevIndexTable64, ARMBITREVINDEXTABLE_64_TABLE_LENGTH },
128U,
(float32_t *)twiddleCoef_rfft_128
};
const arm_rfft_fast_instance_f32 arm_rfft_fast_sR_f32_len256 = {
{ 128, twiddleCoef_128, armBitRevIndexTable128, ARMBITREVINDEXTABLE_128_TABLE_LENGTH },
256U,
(float32_t *)twiddleCoef_rfft_256
};
const arm_rfft_fast_instance_f32 arm_rfft_fast_sR_f32_len512 = {
{ 256, twiddleCoef_256, armBitRevIndexTable256, ARMBITREVINDEXTABLE_256_TABLE_LENGTH },
512U,
(float32_t *)twiddleCoef_rfft_512
};
const arm_rfft_fast_instance_f32 arm_rfft_fast_sR_f32_len1024 = {
{ 512, twiddleCoef_512, armBitRevIndexTable512, ARMBITREVINDEXTABLE_512_TABLE_LENGTH },
1024U,
(float32_t *)twiddleCoef_rfft_1024
};
const arm_rfft_fast_instance_f32 arm_rfft_fast_sR_f32_len2048 = {
{ 1024, twiddleCoef_1024, armBitRevIndexTable1024, ARMBITREVINDEXTABLE_1024_TABLE_LENGTH },
2048U,
(float32_t *)twiddleCoef_rfft_2048
};
const arm_rfft_fast_instance_f32 arm_rfft_fast_sR_f32_len4096 = {
{ 2048, twiddleCoef_2048, armBitRevIndexTable2048, ARMBITREVINDEXTABLE_2048_TABLE_LENGTH },
4096U,
(float32_t *)twiddleCoef_rfft_4096
};
/* Fixed-point structs */
/* q31_t */
extern const q31_t realCoefAQ31[8192];
extern const q31_t realCoefBQ31[8192];
const arm_rfft_instance_q31 arm_rfft_sR_q31_len32 = {
32U,
0,
1,
256U,
(q31_t*)realCoefAQ31,
(q31_t*)realCoefBQ31,
&arm_cfft_sR_q31_len16
};
const arm_rfft_instance_q31 arm_rfft_sR_q31_len64 = {
64U,
0,
1,
128U,
(q31_t*)realCoefAQ31,
(q31_t*)realCoefBQ31,
&arm_cfft_sR_q31_len32
};
const arm_rfft_instance_q31 arm_rfft_sR_q31_len128 = {
128U,
0,
1,
64U,
(q31_t*)realCoefAQ31,
(q31_t*)realCoefBQ31,
&arm_cfft_sR_q31_len64
};
const arm_rfft_instance_q31 arm_rfft_sR_q31_len256 = {
256U,
0,
1,
32U,
(q31_t*)realCoefAQ31,
(q31_t*)realCoefBQ31,
&arm_cfft_sR_q31_len128
};
const arm_rfft_instance_q31 arm_rfft_sR_q31_len512 = {
512U,
0,
1,
16U,
(q31_t*)realCoefAQ31,
(q31_t*)realCoefBQ31,
&arm_cfft_sR_q31_len256
};
const arm_rfft_instance_q31 arm_rfft_sR_q31_len1024 = {
1024U,
0,
1,
8U,
(q31_t*)realCoefAQ31,
(q31_t*)realCoefBQ31,
&arm_cfft_sR_q31_len512
};
const arm_rfft_instance_q31 arm_rfft_sR_q31_len2048 = {
2048U,
0,
1,
4U,
(q31_t*)realCoefAQ31,
(q31_t*)realCoefBQ31,
&arm_cfft_sR_q31_len1024
};
const arm_rfft_instance_q31 arm_rfft_sR_q31_len4096 = {
4096U,
0,
1,
2U,
(q31_t*)realCoefAQ31,
(q31_t*)realCoefBQ31,
&arm_cfft_sR_q31_len2048
};
const arm_rfft_instance_q31 arm_rfft_sR_q31_len8192 = {
8192U,
0,
1,
1U,
(q31_t*)realCoefAQ31,
(q31_t*)realCoefBQ31,
&arm_cfft_sR_q31_len4096
};
/* q15_t */
extern const q15_t realCoefAQ15[8192];
extern const q15_t realCoefBQ15[8192];
const arm_rfft_instance_q15 arm_rfft_sR_q15_len32 = {
32U,
0,
1,
256U,
(q15_t*)realCoefAQ15,
(q15_t*)realCoefBQ15,
&arm_cfft_sR_q15_len16
};
const arm_rfft_instance_q15 arm_rfft_sR_q15_len64 = {
64U,
0,
1,
128U,
(q15_t*)realCoefAQ15,
(q15_t*)realCoefBQ15,
&arm_cfft_sR_q15_len32
};
const arm_rfft_instance_q15 arm_rfft_sR_q15_len128 = {
128U,
0,
1,
64U,
(q15_t*)realCoefAQ15,
(q15_t*)realCoefBQ15,
&arm_cfft_sR_q15_len64
};
const arm_rfft_instance_q15 arm_rfft_sR_q15_len256 = {
256U,
0,
1,
32U,
(q15_t*)realCoefAQ15,
(q15_t*)realCoefBQ15,
&arm_cfft_sR_q15_len128
};
const arm_rfft_instance_q15 arm_rfft_sR_q15_len512 = {
512U,
0,
1,
16U,
(q15_t*)realCoefAQ15,
(q15_t*)realCoefBQ15,
&arm_cfft_sR_q15_len256
};
const arm_rfft_instance_q15 arm_rfft_sR_q15_len1024 = {
1024U,
0,
1,
8U,
(q15_t*)realCoefAQ15,
(q15_t*)realCoefBQ15,
&arm_cfft_sR_q15_len512
};
const arm_rfft_instance_q15 arm_rfft_sR_q15_len2048 = {
2048U,
0,
1,
4U,
(q15_t*)realCoefAQ15,
(q15_t*)realCoefBQ15,
&arm_cfft_sR_q15_len1024
};
const arm_rfft_instance_q15 arm_rfft_sR_q15_len4096 = {
4096U,
0,
1,
2U,
(q15_t*)realCoefAQ15,
(q15_t*)realCoefBQ15,
&arm_cfft_sR_q15_len2048
};
const arm_rfft_instance_q15 arm_rfft_sR_q15_len8192 = {
8192U,
0,
1,
1U,
(q15_t*)realCoefAQ15,
(q15_t*)realCoefBQ15,
&arm_cfft_sR_q15_len4096
};

@ -0,0 +1,66 @@
/* ----------------------------------------------------------------------
* Project: CMSIS DSP Python Wrapper
* Title: arm_const_structs.h
* Description: Constant structs that are initialized for user convenience.
* For example, some can be given as arguments to the arm_cfft_f32() function.
*
* $Date: 25. March 2019
* $Revision: V.1.5.1
*
* Target Processor: Cortex-M cores
* -------------------------------------------------------------------- */
/*
* Copyright (C) 2010-2019 ARM Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _ARM_CONST_STRUCTS_H
#define _ARM_CONST_STRUCTS_H
#include "arm_math.h"
#include "arm_common_tables.h"
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len16;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len32;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len64;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len128;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len256;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len512;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len1024;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len2048;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len4096;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len16;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len32;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len64;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len128;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len256;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len512;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len1024;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len2048;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len16;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len32;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len64;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len128;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len256;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len512;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len1024;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len2048;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len4096;
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,408 @@
/* ----------------------------------------------------------------------
* Project: CMSIS DSP Python Wrapper
* Title: cmsismodule.c
* Description: C code for the CMSIS-DSP Python wrapper
*
* $Date: 25. March 2019
* $Revision: V0.0.1
*
* Target Processor: Cortex-M cores
* -------------------------------------------------------------------- */
/*
* Copyright (C) 2010-2019 ARM Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define NPY_NO_DEPRECATED_API NPY_1_15_API_VERSION
#ifdef WIN
#pragma warning( disable : 4013 )
#pragma warning( disable : 4244 )
#endif
#include <Python.h>
#define MAX(A,B) (A) < (B) ? (B) : (A)
#define CAT1(A,B) A##B
#define CAT(A,B) CAT1(A,B)
#ifdef CMSISDSP
#include "arm_math.h"
#define MODNAME "cmsisdsp"
#define MODINITNAME cmsisdsp
#endif
#include <numpy/arrayobject.h>
#include <numpy/ndarraytypes.h>
#if PY_MAJOR_VERSION >= 3
#define IS_PY3K
#endif
struct module_state {
PyObject *error;
};
#if PY_MAJOR_VERSION >= 3
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
#else
#define GETSTATE(m) (&_state)
static struct module_state _state;
#endif
static PyObject *
error_out(PyObject *m) {
struct module_state *st = GETSTATE(m);
PyErr_SetString(st->error, "something bad happened");
return NULL;
}
#define MLTYPE(name,thenewfunc,deallocfunc,initfunc,methods)\
static PyTypeObject ml_##name##Type = { \
PyVarObject_HEAD_INIT(NULL, 0) \
.tp_name=MODNAME".##name", \
.tp_basicsize = sizeof(ml_##name##Object), \
.tp_itemsize = 0, \
.tp_dealloc = (destructor)deallocfunc, \
.tp_flags = Py_TPFLAGS_DEFAULT, \
.tp_doc = #name, \
.tp_init = (initproc)initfunc, \
.tp_new = (newfunc)thenewfunc, \
.tp_methods = methods \
};
#define MEMCPY(DST,SRC,NB,FORMAT) \
for(memCpyIndex = 0; memCpyIndex < (NB) ; memCpyIndex++)\
{ \
(DST)[memCpyIndex] = (FORMAT)(SRC)[memCpyIndex]; \
}
#define GETFIELD(NAME,FIELD,FORMAT) \
static PyObject * \
Method_##NAME##_##FIELD(ml_##NAME##Object *self, PyObject *ignored)\
{ \
return(Py_BuildValue(FORMAT,self->instance->FIELD)); \
}
#define GETFIELDARRAY(NAME,FIELD,FORMAT) \
static PyObject * \
Method_##NAME##_##FIELD(ml_##NAME##Object *self, PyObject *ignored)\
{ \
return(specific_##NAME##_##FIELD(self->instance)); \
}
#define INITARRAYFIELD(FIELD,FORMAT,SRCFORMAT,DSTFORMAT) \
if (FIELD) \
{ \
PyArray_Descr *desct=PyArray_DescrFromType(FORMAT); \
PyArrayObject *FIELD##c = (PyArrayObject *)PyArray_FromAny(FIELD,desct,\
1,0,NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ALIGNED | NPY_ARRAY_FORCECAST, \
NULL); \
if (FIELD##c) \
{ \
uint32_t memCpyIndex; \
SRCFORMAT *f=(SRCFORMAT*)PyArray_DATA(FIELD##c); \
uint32_t n = PyArray_SIZE(FIELD##c); \
self->instance->FIELD =PyMem_Malloc(sizeof(DSTFORMAT)*n); \
MEMCPY(self->instance->FIELD ,f,n,DSTFORMAT); \
Py_DECREF(FIELD##c); \
} \
}
#define GETCARRAY(PYVAR,CVAR,FORMAT,SRCFORMAT,DSTFORMAT) \
if (PYVAR) \
{ \
PyArray_Descr *desct=PyArray_DescrFromType(FORMAT); \
PyArrayObject *PYVAR##c = (PyArrayObject *)PyArray_FromAny(PYVAR,desct,\
1,0,NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ALIGNED | NPY_ARRAY_FORCECAST, \
NULL); \
if (PYVAR##c) \
{ \
uint32_t memCpyIndex; \
SRCFORMAT *f=(SRCFORMAT*)PyArray_DATA(PYVAR##c); \
uint32_t n = PyArray_SIZE(PYVAR##c); \
CVAR =PyMem_Malloc(sizeof(DSTFORMAT)*n); \
MEMCPY(CVAR ,f,n,DSTFORMAT); \
Py_DECREF(PYVAR##c); \
} \
}
#define GETARGUMENT(FIELD,FORMAT,SRCFORMAT,DSTFORMAT) \
uint32_t arraySize##FIELD=0; \
if (FIELD) \
{ \
PyArray_Descr *desct=PyArray_DescrFromType(FORMAT); \
PyArrayObject *FIELD##c = (PyArrayObject *)PyArray_FromAny(FIELD,desct, \
1,0,NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ALIGNED | NPY_ARRAY_FORCECAST, \
NULL); \
if (FIELD##c) \
{ \
uint32_t memCpyIndex; \
SRCFORMAT *f=(SRCFORMAT*)PyArray_DATA(FIELD##c); \
arraySize##FIELD = PyArray_SIZE(FIELD##c); \
FIELD##_converted =PyMem_Malloc(sizeof(DSTFORMAT)*arraySize##FIELD);\
MEMCPY(FIELD##_converted ,f,arraySize##FIELD,DSTFORMAT); \
Py_DECREF(FIELD##c); \
} \
}
#define FREEARGUMENT(FIELD) \
PyMem_Free(FIELD)
#ifdef IS_PY3K
#define ADDTYPE(name) \
if (PyType_Ready(&ml_##name##Type) < 0) \
return; \
\
Py_INCREF(&ml_##name##Type); \
PyModule_AddObject(module, #name, (PyObject *)&ml_##name##Type);
#else
#define ADDTYPE(name) \
if (PyType_Ready(&ml_##name##Type) < 0) \
return; \
\
Py_INCREF(&ml_##name##Type); \
PyModule_AddObject(module, #name, (PyObject *)&ml_##name##Type);
#endif
#define FLOATARRAY2(OBJ,NB1,NB2,DATA) \
npy_intp dims[2]; \
dims[0]=NB1; \
dims[1]=NB2; \
const int ND=2; \
PyObject *OBJ=PyArray_SimpleNewFromData(ND, dims, NPY_FLOAT, DATA);
#define FLOATARRAY1(OBJ,NB1,DATA) \
npy_intp dims[1]; \
dims[0]=NB1; \
const int ND=1; \
PyObject *OBJ=PyArray_SimpleNewFromData(ND, dims, NPY_FLOAT, DATA);
#define FLOAT64ARRAY1(OBJ,NB1,DATA) \
npy_intp dims[1]; \
dims[0]=NB1; \
const int ND=1; \
PyObject *OBJ=PyArray_SimpleNewFromData(ND, dims, NPY_DOUBLE, DATA);
#define UINT32ARRAY1(OBJ,NB1,DATA) \
npy_intp dims[1]; \
dims[0]=NB1; \
const int ND=1; \
PyObject *OBJ=PyArray_SimpleNewFromData(ND, dims, NPY_UINT32, DATA);
#define INT32ARRAY1(OBJ,NB1,DATA) \
npy_intp dims[1]; \
dims[0]=NB1; \
const int ND=1; \
PyObject *OBJ=PyArray_SimpleNewFromData(ND, dims, NPY_INT32, DATA);
#define INT16ARRAY1(OBJ,NB1,DATA) \
npy_intp dims[1]; \
dims[0]=NB1; \
const int ND=1; \
PyObject *OBJ=PyArray_SimpleNewFromData(ND, dims, NPY_INT16, DATA);
#define INT8ARRAY1(OBJ,NB1,DATA) \
npy_intp dims[1]; \
dims[0]=NB1; \
const int ND=1; \
PyObject *OBJ=PyArray_SimpleNewFromData(ND, dims, NPY_BYTE, DATA);
#define MATRIXFROMNUMPY(EXT,TYP,SRCTYPE,NUMPYTYPE) \
arm_matrix_instance_##EXT *EXT##MatrixFromNumpy(PyObject *o) \
{ \
arm_matrix_instance_##EXT *s; \
\
s=PyMem_Malloc(sizeof(arm_matrix_instance_##EXT)); \
s->pData=NULL; \
s->numRows=0; \
s->numCols=0; \
\
PyArray_Descr *desct=PyArray_DescrFromType(NUMPYTYPE); \
PyArrayObject *cdata = (PyArrayObject *)PyArray_FromAny(o,desct, \
1,0,NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ALIGNED | NPY_ARRAY_FORCECAST, \
NULL); \
if (cdata) \
{ \
uint32_t memCpyIndex; \
SRCTYPE *f=(SRCTYPE*)PyArray_DATA(cdata); \
s->numRows=PyArray_DIM(cdata,0); \
s->numCols=PyArray_DIM(cdata,1); \
uint32_t nb = PyArray_SIZE(cdata); \
s->pData = PyMem_Malloc(sizeof(TYP)*nb); \
MEMCPY(s->pData ,f,nb,TYP); \
Py_DECREF(cdata); \
} \
\
\
return(s); \
\
}
MATRIXFROMNUMPY(f32,float32_t,double,NPY_DOUBLE);
MATRIXFROMNUMPY(f64,float64_t,double,NPY_DOUBLE);
MATRIXFROMNUMPY(q31,q31_t,int32_t,NPY_INT32);
MATRIXFROMNUMPY(q15,q15_t,int16_t,NPY_INT16);
#define CREATEMATRIX(EXT,TYP) \
arm_matrix_instance_##EXT *create##EXT##Matrix(uint32_t r,uint32_t c)\
{ \
arm_matrix_instance_##EXT *s; \
\
s=PyMem_Malloc(sizeof(arm_matrix_instance_##EXT)); \
s->pData=PyMem_Malloc(sizeof(TYP)*r*c); \
s->numRows=r; \
s->numCols=c; \
return(s); \
}
CREATEMATRIX(f32,float32_t);
CREATEMATRIX(f64,float64_t);
CREATEMATRIX(q31,q31_t);
CREATEMATRIX(q15,q15_t);
#define NUMPYARRAYFROMMATRIX(EXT,NUMPYTYPE_FROMC) \
PyObject *NumpyArrayFrom##EXT##Matrix(arm_matrix_instance_##EXT *mat) \
{ \
npy_intp dims[2]; \
dims[0]=mat->numRows; \
dims[1]=mat->numCols; \
const int ND=2; \
PyObject *OBJ=PyArray_SimpleNewFromData(ND, dims, NUMPYTYPE_FROMC, mat->pData);\
return(OBJ); \
}
NUMPYARRAYFROMMATRIX(f32,NPY_FLOAT);
NUMPYARRAYFROMMATRIX(f64,NPY_DOUBLE);
NUMPYARRAYFROMMATRIX(q31,NPY_INT32);
NUMPYARRAYFROMMATRIX(q15,NPY_INT16);
#include "specific.h"
#include "cmsismodule.h"
#if 0
static PyObject *cmsisml_test(PyObject *obj, PyObject *args)
{
ml_arm_svm_linear_instance_f32Object *self=NULL;
PyObject *svm, *vector=NULL;
if (!PyArg_ParseTuple(args, "OO", &svm,&vector))
return NULL;
self=(ml_arm_svm_linear_instance_f32Object*)svm;
if (self)
{
if (self->instance)
{
int result;
float32_t *input=NULL;
GETCARRAY(vector,input,NPY_DOUBLE,double,float32_t);
arm_svm_linear_predict_f32(self->instance,input,&result);
/*
printf("Dual\n");
for(int i = 0 ; i < self->instance->nbOfSupportVectors ; i++)
{
printf("%f\n",self->instance->dualCoefficients[i]);
}
printf("Vectors\n");
int k=0;
for(int i = 0 ; i < self->instance->nbOfSupportVectors ; i++)
{
printf("Vector %d\n",i);
for(int j = 0 ; j < self->instance->vectorDimension ; j++)
{
printf("%f\n",self->instance->supportVectors[k]);
k++;
}
}
printf("Classes\n");
for(int i = 0 ; i < 2 ; i++)
{
printf("%d\n",self->instance->classes[i]);
}
printf("Intercept %f\n",self->instance->intercept);
*/
PyMem_Free(input);
return(Py_BuildValue("i",result));
}
}
return(Py_BuildValue("i",-1));
}
#endif
#ifdef IS_PY3K
static int cmsisml_traverse(PyObject *m, visitproc visit, void *arg) {
Py_VISIT(GETSTATE(m)->error);
return 0;
}
static int cmsisml_clear(PyObject *m) {
Py_CLEAR(GETSTATE(m)->error);
return 0;
}
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
MODNAME,
NULL,
sizeof(struct module_state),
CMSISMLMethods,
NULL,
cmsisml_traverse,
cmsisml_clear,
NULL
};
#define INITERROR return NULL
PyMODINIT_FUNC
CAT(PyInit_,MODINITNAME)(void)
#else
#define INITERROR return
void CAT(init,MODINITNAME)(void)
#endif
{
import_array();
#ifdef IS_PY3K
PyObject *module = PyModule_Create(&moduledef);
#else
PyObject *module = Py_InitModule(MODNAME, CMSISMLMethods);
#endif
if (module == NULL)
INITERROR;
struct module_state *st = GETSTATE(module);
st->error = PyErr_NewException(MODNAME".Error", NULL, NULL);
if (st->error == NULL) {
Py_DECREF(module);
INITERROR;
}
typeRegistration(module);
#ifdef IS_PY3K
return module;
#endif
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,262 @@
/* ----------------------------------------------------------------------
* Project: CMSIS DSP Python Wrapper
* Title: fftinit.c
* Description: FFT init functions for the Python wrapper
*
* $Date: 25. March 2019
* $Revision: V0.0.1
*
* Target Processor: Cortex-M cores
* -------------------------------------------------------------------- */
/*
* Copyright (C) 2010-2019 ARM Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "arm_math.h"
#include "arm_common_tables.h"
#include "arm_const_structs.h"
#define FFTINIT(SIZE) \
S->bitRevLength = arm_cfft_sR_f32_len##SIZE.bitRevLength; \
S->pBitRevTable = arm_cfft_sR_f32_len##SIZE.pBitRevTable; \
S->pTwiddle = arm_cfft_sR_f32_len##SIZE.pTwiddle;
#define FFTFXTINIT(EXT,SIZE) \
S->bitRevLength = arm_cfft_sR_##EXT##_len##SIZE.bitRevLength; \
S->pBitRevTable = arm_cfft_sR_##EXT##_len##SIZE.pBitRevTable; \
S->pTwiddle = arm_cfft_sR_##EXT##_len##SIZE.pTwiddle;
arm_status arm_cfft_init_f32(
arm_cfft_instance_f32 * S,
uint16_t fftLen)
{
/* Initialise the default arm status */
arm_status status = ARM_MATH_SUCCESS;
/* Initialise the FFT length */
S->fftLen = fftLen;
/* Initialise the Twiddle coefficient pointer */
S->pTwiddle = (float32_t *)twiddleCoef_4096;
/* Initializations of Instance structure depending on the FFT length */
switch (S->fftLen) {
/* Initializations of structure parameters for 4096 point FFT */
case 4096U:
/* Initialise the bit reversal table modifier */
FFTINIT(4096);
break;
/* Initializations of structure parameters for 2048 point FFT */
case 2048U:
/* Initialise the bit reversal table modifier */
FFTINIT(2048);
break;
/* Initializations of structure parameters for 1024 point FFT */
case 1024U:
/* Initialise the bit reversal table modifier */
FFTINIT(1024);
break;
/* Initializations of structure parameters for 512 point FFT */
case 512U:
/* Initialise the bit reversal table modifier */
FFTINIT(512);
break;
case 256U:
FFTINIT(256);
break;
case 128U:
FFTINIT(128);
break;
case 64U:
FFTINIT(64);
break;
case 32U:
FFTINIT(32);
break;
case 16U:
/* Initializations of structure parameters for 16 point FFT */
FFTINIT(16);
break;
default:
/* Reporting argument error if fftSize is not valid value */
status = ARM_MATH_ARGUMENT_ERROR;
break;
}
return (status);
}
arm_status arm_cfft_init_q31(
arm_cfft_instance_q31 * S,
uint16_t fftLen)
{
/* Initialise the default arm status */
arm_status status = ARM_MATH_SUCCESS;
/* Initialise the FFT length */
S->fftLen = fftLen;
/* Initialise the Twiddle coefficient pointer */
S->pTwiddle = (float32_t *)twiddleCoef_4096;
/* Initializations of Instance structure depending on the FFT length */
switch (S->fftLen) {
/* Initializations of structure parameters for 4096 point FFT */
case 4096U:
/* Initialise the bit reversal table modifier */
FFTFXTINIT(q31,4096);
break;
/* Initializations of structure parameters for 2048 point FFT */
case 2048U:
/* Initialise the bit reversal table modifier */
FFTFXTINIT(q31,2048);
break;
/* Initializations of structure parameters for 1024 point FFT */
case 1024U:
/* Initialise the bit reversal table modifier */
FFTFXTINIT(q31,1024);
break;
/* Initializations of structure parameters for 512 point FFT */
case 512U:
/* Initialise the bit reversal table modifier */
FFTFXTINIT(q31,512);
break;
case 256U:
FFTFXTINIT(q31,256);
break;
case 128U:
FFTFXTINIT(q31,128);
break;
case 64U:
FFTFXTINIT(q31,64);
break;
case 32U:
FFTFXTINIT(q31,32);
break;
case 16U:
/* Initializations of structure parameters for 16 point FFT */
FFTFXTINIT(q31,16);
break;
default:
/* Reporting argument error if fftSize is not valid value */
status = ARM_MATH_ARGUMENT_ERROR;
break;
}
return (status);
}
arm_status arm_cfft_init_q15(
arm_cfft_instance_q15 * S,
uint16_t fftLen)
{
/* Initialise the default arm status */
arm_status status = ARM_MATH_SUCCESS;
/* Initialise the FFT length */
S->fftLen = fftLen;
/* Initialise the Twiddle coefficient pointer */
S->pTwiddle = (float32_t *)twiddleCoef_4096;
/* Initializations of Instance structure depending on the FFT length */
switch (S->fftLen) {
/* Initializations of structure parameters for 4096 point FFT */
case 4096U:
/* Initialise the bit reversal table modifier */
FFTFXTINIT(q15,4096);
break;
/* Initializations of structure parameters for 2048 point FFT */
case 2048U:
/* Initialise the bit reversal table modifier */
FFTFXTINIT(q15,2048);
break;
/* Initializations of structure parameters for 1024 point FFT */
case 1024U:
/* Initialise the bit reversal table modifier */
FFTFXTINIT(q15,1024);
break;
/* Initializations of structure parameters for 512 point FFT */
case 512U:
/* Initialise the bit reversal table modifier */
FFTFXTINIT(q15,512);
break;
case 256U:
FFTFXTINIT(q15,256);
break;
case 128U:
FFTFXTINIT(q15,128);
break;
case 64U:
FFTFXTINIT(q15,64);
break;
case 32U:
FFTFXTINIT(q15,32);
break;
case 16U:
/* Initializations of structure parameters for 16 point FFT */
FFTFXTINIT(q15,16);
break;
default:
/* Reporting argument error if fftSize is not valid value */
status = ARM_MATH_ARGUMENT_ERROR;
break;
}
return (status);
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,32 @@
/* ----------------------------------------------------------------------
* Project: CMSIS DSP Python Wrapper
* Title: specific.h
* Description: For future use
*
* $Date: 25. March 2019
* $Revision: V0.0.1
*
* Target Processor: Cortex-M cores
* -------------------------------------------------------------------- */
/*
* Copyright (C) 2010-2019 ARM Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
For future
*/

@ -0,0 +1,12 @@
CMSISDSP = 1
ROOT=".."
config = CMSISDSP
if config == CMSISDSP:
extensionName = 'cmsisdsp'
setupName = 'CMSISDSP'
setupDescription = 'CMSIS-DSP Python API'
cflags="-DCMSISDSP"

@ -0,0 +1,79 @@
import cmsisdsp as dsp
import numpy as np
from scipy import signal
from pylab import figure, clf, plot, xlabel, ylabel, xlim, ylim, title, grid, axes, show,semilogx, semilogy
# Data file from https://www.physionet.org/pn3/ecgiddb/Person_87/rec_2.dat
def q31sat(x):
if x > 0x7FFFFFFF:
return(np.int32(0x7FFFFFFF))
elif x < -0x80000000:
return(np.int32(0x80000000))
else:
return(np.int32(x))
q31satV=np.vectorize(q31sat)
def toQ31(x):
return(q31satV(np.round(x * (1<<31))))
def Q31toF32(x):
return(1.0*x / 2**31)
filename = 'rec_2.dat'
f = open(filename,"r")
sig = np.fromfile(f, dtype=np.int16)
f.closed
sig = 1.0*sig / (1 << 12)
p0 = np.exp(1j*0.05) * 0.98
p1 = np.exp(1j*0.25) * 0.9
p2 = np.exp(1j*0.45) * 0.97
z0 = np.exp(1j*0.02)
z1 = np.exp(1j*0.65)
z2 = np.exp(1j*1.0)
g = 0.02
nb = 300
sos = signal.zpk2sos(
[z0,np.conj(z0),z1,np.conj(z1),z2,np.conj(z2)]
,[p0, np.conj(p0),p1, np.conj(p1),p2, np.conj(p2)]
,g)
res=signal.sosfilt(sos,sig)
figure()
plot(sig[1:nb])
figure()
plot(res[1:nb])
biquadQ31 = dsp.arm_biquad_casd_df1_inst_q31()
numStages=3
state=np.zeros(numStages*4)
# For use in CMSIS, denominator coefs must be negated
# and first a0 coef wihich is always 1 must be removed
coefs=np.reshape(np.hstack((sos[:,:3],-sos[:,4:])),15)
coefs = coefs / 4.0
coefsQ31 = toQ31(coefs)
postshift = 2
dsp.arm_biquad_cascade_df1_init_q31(biquadQ31,numStages,coefsQ31,state,postshift)
sigQ31=toQ31(sig)
nbSamples=sigQ31.shape[0]
# Here we demonstrate how we can process a long sequence of samples per block
# and thus check that the state of the biquad is well updated and preserved
# between the calls.
half = round(nbSamples / 2)
res2a=dsp.arm_biquad_cascade_df1_q31(biquadQ31,sigQ31[1:half])
res2b=dsp.arm_biquad_cascade_df1_q31(biquadQ31,sigQ31[half+1:nbSamples])
res2=Q31toF32(np.hstack((res2a,res2b)))
figure()
plot(res2[1:nb])
show()#

@ -0,0 +1,79 @@
from distutils.core import setup, Extension
import glob
import numpy
import config
import sys
import os
from config import ROOT
includes = [os.path.join("cmsisdsp_pkg","custom"),os.path.join("cmsisdsp_pkg","src")]
if sys.platform == 'win32':
cflags = ["-DWIN",config.cflags,"-DUNALIGNED_SUPPORT_DISABLE"]
# Custom because a customized arm_math.h is required to build on windows
# since the visual compiler and the win platform are
# not supported by default in arm_math.h
else:
cflags = ["-Wno-unused-variable","-Wno-implicit-function-declaration",config.cflags]
transform = glob.glob(os.path.join(ROOT,"Source","TransformFunctions","*.c"))
transform.remove(os.path.join(ROOT,"Source","TransformFunctions","arm_dct4_init_q15.c"))
transform.remove(os.path.join(ROOT,"Source","TransformFunctions","arm_rfft_init_q15.c"))
transform.remove(os.path.join(ROOT,"Source","TransformFunctions","TransformFunctions.c"))
support = glob.glob(os.path.join(ROOT,"Source","SupportFunctions","*.c"))
support.remove(os.path.join(ROOT,"Source","SupportFunctions","SupportFunctions.c"))
fastmath = glob.glob(os.path.join(ROOT,"Source","FastMathFunctions","*.c"))
fastmath.remove(os.path.join(ROOT,"Source","FastMathFunctions","FastMathFunctions.c"))
filtering = glob.glob(os.path.join(ROOT,"Source","FilteringFunctions","*.c"))
filtering.remove(os.path.join(ROOT,"Source","FilteringFunctions","FilteringFunctions.c"))
matrix = glob.glob(os.path.join(ROOT,"Source","MatrixFunctions","*.c"))
matrix.remove(os.path.join(ROOT,"Source","MatrixFunctions","MatrixFunctions.c"))
statistics = glob.glob(os.path.join(ROOT,"Source","StatisticsFunctions","*.c"))
statistics.remove(os.path.join(ROOT,"Source","StatisticsFunctions","StatisticsFunctions.c"))
complexf = glob.glob(os.path.join(ROOT,"Source","ComplexMathFunctions","*.c"))
complexf.remove(os.path.join(ROOT,"Source","ComplexMathFunctions","ComplexMathFunctions.c"))
basic = glob.glob(os.path.join(ROOT,"Source","BasicMathFunctions","*.c"))
basic.remove(os.path.join(ROOT,"Source","BasicMathFunctions","BasicMathFunctions.c"))
controller = glob.glob(os.path.join(ROOT,"Source","ControllerFunctions","*.c"))
controller.remove(os.path.join(ROOT,"Source","ControllerFunctions","ControllerFunctions.c"))
modulesrc = glob.glob(os.path.join("cmsisdsp_pkg","src","*.c"))
module1 = Extension(config.extensionName,
sources = (support
+ fastmath
+ filtering
+ matrix
+ statistics
+ complexf
+ basic
+ controller
+ transform
+ modulesrc
)
,
include_dirs = includes + [numpy.get_include()],
#extra_compile_args = ["-Wno-unused-variable","-Wno-implicit-function-declaration",config.cflags]
extra_compile_args = cflags
)
setup (name = config.setupName,
version = '0.0.1',
description = config.setupDescription,
ext_modules = [module1],
author = 'Copyright (C) 2010-2019 ARM Limited or its affiliates. All rights reserved.',
url="https://github.com/ARM-software/CMSIS_5",
classifiers=[
"Programming Language :: Python",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
])

@ -0,0 +1,363 @@
import cmsisdsp as dsp
import numpy as np
from scipy import signal
#import matplotlib.pyplot as plt
#from scipy.fftpack import dct
#r = dsp.arm_add_f32(np.array([1.,2,3]),np.array([4.,5,7]))
#print(r)
#r = dsp.arm_add_q31([1,2,3],[4,5,7])
#print(r)
#
#r = dsp.arm_add_q15([1,2,3],[4,5,7])
#print(r)
#
#r = dsp.arm_add_q7([-1,2,3],[4,127,7])
#print(r)
#
#r = dsp.arm_scale_f32([1.,2,3],2)
#print(r)
#
#r = dsp.arm_scale_q31([0x7FFF,0x3FFF,0x1FFF],1 << 20,2)
#print(r)
#
#r = dsp.arm_scale_q15([0x7FFF,0x3FFF,0x1FFF],1 << 10,2)
#print(r)
#
#r = dsp.arm_scale_q7([0x7F,0x3F,0x1F],1 << 5,2)
#print(r)
#
#
#r = dsp.arm_negate_f32([1.,2,3])
#print(r)
#
#r = dsp.arm_negate_q31([1,2,3])
#print(r)
#
#r = dsp.arm_negate_q15([1,2,3])
#print(r)
#
#r = dsp.arm_negate_q7(np.array([0x80,0x81,0x82]))
#print(r)
#r = dsp.arm_cmplx_conj_f32([1.,2,3,4])
#print(r)
#r = dsp.arm_cmplx_conj_q31([1,2,3,4])
#print(r)
#r = dsp.arm_cmplx_conj_q15([1,2,3,4])
#print(r)
#r = dsp.arm_cmplx_dot_prod_f32([1.,2,3,4],[1.,2,3,4])
#print(r)
#r = dsp.arm_cmplx_dot_prod_q31([0x1FFF,0x3FFF,0x1FFF,0x3FFF],[0x1FFF,0x3FFF,0x1FFF,0x3FFF])
#print(r)
#r = dsp.arm_cmplx_mult_real_f32([1.0,2,3,4],[5.,5.,5.,5.])
#print(r)
#pidf32 = dsp.arm_pid_instance_f32(Kp=1.0,Ki=1.2,Kd=0.4)
#print(pidf32.Kp())
#print(pidf32.Ki())
#print(pidf32.Kd())
#print(pidf32.A0())
#
#dsp.arm_pid_init_f32(pidf32,0)
#print(pidf32.A0())
#print(dsp.arm_cos_f32(3.14/4.))
#print(dsp.arm_sqrt_q31(0x7FFF))
firf32 = dsp.arm_fir_instance_f32()
dsp.arm_fir_init_f32(firf32,3,[1.,2,3],[0,0,0,0,0,0,0])
print(firf32.numTaps())
filtered_x = signal.lfilter([3,2,1.], 1.0, [1,2,3,4,5,1,2,3,4,5])
print(filtered_x)
print(dsp.arm_fir_f32(firf32,[1,2,3,4,5]))
print(dsp.arm_fir_f32(firf32,[1,2,3,4,5]))
def q31sat(x):
if x > 0x7FFFFFFF:
return(np.int32(0x7FFFFFFF))
elif x < -0x80000000:
return(np.int32(0x80000000))
else:
return(np.int32(x))
q31satV=np.vectorize(q31sat)
def toQ31(x):
return(q31satV(np.round(x * (1<<31))))
def q15sat(x):
if x > 0x7FFF:
return(np.int16(0x7FFF))
elif x < -0x8000:
return(np.int16(0x8000))
else:
return(np.int16(x))
q15satV=np.vectorize(q15sat)
def toQ15(x):
return(q15satV(np.round(x * (1<<15))))
def q7sat(x):
if x > 0x7F:
return(np.int8(0x7F))
elif x < -0x80:
return(np.int8(0x80))
else:
return(np.int8(x))
q7satV=np.vectorize(q7sat)
def toQ7(x):
return(q7satV(np.round(x * (1<<7))))
def Q31toF32(x):
return(1.0*x / 2**31)
def Q15toF32(x):
return(1.0*x / 2**15)
def Q7toF32(x):
return(1.0*x / 2**7)
#firq31 = dsp.arm_fir_instance_q31()
#x=np.array([1,2,3,4,5])/10.0
#taps=np.array([1,2,3])/10.0
#xQ31=toQ31(x)
#tapsQ31=toQ31(taps)
#dsp.arm_fir_init_q31(firq31,3,tapsQ31,[0,0,0,0,0,0,0])
#print(firq31.numTaps())
#resultQ31=dsp.arm_fir_q31(firq31,xQ31)
#result=Q31toF32(resultQ31)
#print(result)
#a=np.array([[1.,2,3,4],[5,6,7,8],[9,10,11,12]])
#b=np.array([[1.,2,3,4],[5.1,6,7,8],[9.1,10,11,12]])
#print(a+b)
#v=dsp.arm_mat_add_f32(a,b)
#print(v)
#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]])
#print(np.dot(a , b))
#v=dsp.arm_mat_mult_f32(a,b)
#print(v)
def imToReal2D(a):
ar=np.zeros(np.array(a.shape) * [1,2])
ar[::,0::2]=a.real
ar[::,1::2]=a.imag
return(ar)
def realToIm2D(ar):
return(ar[::,0::2] + 1j * ar[::,1::2])
#a=np.array([[1. + 2j,3 + 4j],[5 + 6j,7 + 8j],[9 + 10j,11 + 12j]])
#b=np.array([[1. + 2j, 3 + 5.1j ,6 + 7j],[9.1 + 10j,11 + 5j,8 +4j]])
#print(np.dot(a , b))
#
# Convert complex array to real array for use in CMSIS DSP
#ar = imToReal2D(a)
#br = imToReal2D(b)
#
#v=dsp.arm_mat_cmplx_mult_f32(ar,br)
#print(v)
#a=np.array([[1.,2,3,4],[5,6,7,8],[9,10,11,12]]) / 30.0
#b=np.array([[1.,2,3,4],[5.1,6,7,8],[9.1,10,11,12]]) / 30.0
#print(a+b)
#
#aQ31=toQ31(a)
#bQ31=toQ31(b)
#v=dsp.arm_mat_add_q31(aQ31,bQ31)
#rQ31=v[1]
#r=Q31toF32(rQ31)
#print(r)#
#a=np.array([[1.,2,3,4],[5,6,7,8],[9,10,11,12]])
#print(np.transpose(a))
#print(dsp.arm_mat_trans_f32(a))
#a = np.array([[1., 2.], [3., 4.]])
#print(np.linalg.inv(a))
#print(dsp.arm_mat_inverse_f32(a))
#a = np.array([[1., 2.], [3., 4.]])
#print(np.linalg.inv(a))
#print(dsp.arm_mat_inverse_f64(a))
#a=np.array([[1.,2,3,4],[5,6,7,8],[9,10,11,12]])
#print(2.5*a)
#print(dsp.arm_mat_scale_f32(a,2.5))
#a=np.array([1.,2,3,4,5,6,7,8,9,10,11,12])
#print(np.max(a))
#print(np.argmax(a))
#print(dsp.arm_max_f32(a))
#
#print(np.mean(a))
#print(dsp.arm_mean_f32(a))
#
#print(np.dot(a,a))
#print(dsp.arm_power_f32(a))
#
def imToReal1D(a):
ar=np.zeros(np.array(a.shape) * 2)
ar[0::2]=a.real
ar[1::2]=a.imag
return(ar)
def realToIm1D(ar):
return(ar[0::2] + 1j * ar[1::2])
#nb = 16
#signal = np.cos(2 * np.pi * np.arange(nb) / nb)
#result=np.fft.fft(signal)
#print(result)
#signalR = imToReal1D(signal)
#cfftf32=dsp.arm_cfft_instance_f32()
#status=dsp.arm_cfft_init_f32(cfftf32,nb)
#print(status)
#resultR = dsp.arm_cfft_f32(cfftf32,signalR,0,1)
#resultI = realToIm1D(resultR)
#print(resultI)
#signal = signal / 10.0
#result=np.fft.fft(signal)
#print(result)
#
#signalR = imToReal1D(signal)
#signalRQ31=toQ31(signalR)
#cfftq31=dsp.arm_cfft_instance_q31()
#status=dsp.arm_cfft_init_q31(cfftq31,nb)
#print(status)
#resultR = dsp.arm_cfft_q31(cfftq31,signalRQ31,0,1)
#resultI = realToIm1D(Q31toF32(resultR))*16
#print(resultI)
#signal = signal / 10.0
#result=np.fft.fft(signal)
#print(result)
##
#signalR = imToReal1D(signal)
#signalRQ15=toQ15(signalR)
#cfftq15=dsp.arm_cfft_instance_q15()
#status=dsp.arm_cfft_init_q15(cfftq15,nb)
#print(status)
#resultR = dsp.arm_cfft_q15(cfftq15,signalRQ15,0,1)
#resultR=Q15toF32(resultR)
#resultI = realToIm1D(resultR)*16
#print(resultI)
#nb = 128
#signal = np.cos(2 * np.pi * np.arange(nb) / nb)
#
#result=np.fft.fft(signal)
##print(result)
#cfftradix4f32=dsp.arm_cfft_radix4_instance_f32()
#rfftf32=dsp.arm_rfft_instance_f32()
#status=dsp.arm_rfft_init_f32(rfftf32,cfftradix4f32,nb,0,1)
#print(status)
#resultI = dsp.arm_rfft_f32(rfftf32,signal)
#print(result)
#nb = 128
#signal = np.cos(2 * np.pi * np.arange(nb) / nb)
#signalRQ31=toQ31(signal)
#
#result=np.fft.fft(signal)
##print(result)
#rfftq31=dsp.arm_rfft_instance_q31()
#status=dsp.arm_rfft_init_q31(rfftq31,nb,0,1)
#print(status)
#resultI = dsp.arm_rfft_q31(rfftq31,signalRQ31)
#resultI=Q31toF32(resultI)*(1 << 7)
##print(result)
#nb = 128
#signal = np.cos(2 * np.pi * np.arange(nb) / nb)
#signalRQ15=toQ15(signal)
#
#result=np.fft.fft(signal)
##print(result)
#rfftq15=dsp.arm_rfft_instance_q15()
#status=dsp.arm_rfft_init_q15(rfftq15,nb,0,1)
#print(status)
#resultI = dsp.arm_rfft_q15(rfftq15,signalRQ15)
#resultI=Q15toF32(resultI)*(1 << 7)
#print(result)
#nb = 128
#nb2=64
#signal = np.cos(2 * np.pi * np.arange(nb) / nb)
#result=dct(signal,4,norm='ortho')
##print(result)
#cfftradix4f32=dsp.arm_cfft_radix4_instance_f32()
#rfftf32=dsp.arm_rfft_instance_f32()
#dct4f32=dsp.arm_dct4_instance_f32()
#status=dsp.arm_dct4_init_f32(dct4f32,rfftf32,cfftradix4f32,nb,nb2,0.125)
#print(status)
#state=np.zeros(2*nb)
#resultI = dsp.arm_dct4_f32(dct4f32,state,signal)
##print(resultI)
#signal = signal / 10.0
#result=dct(signal,4,norm='ortho')
#signalQ31=toQ31(signal)
#cfftradix4q31=dsp.arm_cfft_radix4_instance_q31()
#rfftq31=dsp.arm_rfft_instance_q31()
#dct4q31=dsp.arm_dct4_instance_q31()
#status=dsp.arm_dct4_init_q31(dct4q31,rfftq31,cfftradix4q31,nb,nb2,0x10000000)
#print(status)
#state=np.zeros(2*nb)
#resultI = dsp.arm_dct4_q31(dct4q31,state,signalQ31)
#resultI=Q31toF32(resultI)*(1 << 7)
#nb = 128
#nb2=64
#signal = np.cos(2 * np.pi * np.arange(nb) / nb)
#signal = signal / 10.0
#result=dct(signal,4,norm='ortho')
#signalQ15=toQ15(signal)
#cfftradix4q15=dsp.arm_cfft_radix4_instance_q15()
#rfftq15=dsp.arm_rfft_instance_q15()
#dct4q15=dsp.arm_dct4_instance_q15()
#status=dsp.arm_dct4_init_q15(dct4q15,rfftq15,cfftradix4q15,nb,nb2,0x1000)
#print(status)
#state=np.zeros(2*nb)
#resultI = dsp.arm_dct4_q15(dct4q15,state,signalQ15)
#resultI=Q15toF32(resultI)*(1 << 7)
#
#
#from pylab import figure, clf, plot, xlabel, ylabel, xlim, ylim, title, grid, axes, show
#figure(1)
#plot(np.absolute(signal))
#t = np.arange(nb)
#freq = np.fft.fftfreq(t.shape[-1])
#resultmag=np.absolute(result)
#figure(2)
#plot(resultmag)
#figure(3)
#cmsigmag=np.absolute(resultI)
#plot(cmsigmag)
#show()##
#biquadf32 = dsp.arm_biquad_casd_df1_inst_f32()
#numStages=1
#state=np.zeros(numStages*4)
#coefs=[1.,2,3,4,5]
#dsp.arm_biquad_cascade_df1_init_f32(biquadf32,1,coefs,state)
#print(dsp.arm_biquad_cascade_df1_f32(biquadf32,[1,2,3,4,5]))#
Loading…
Cancel
Save