OpenWareLaboratory
ShortArray.cpp
Go to the documentation of this file.
1 #include "ShortArray.h"
2 #include "basicmaths.h"
3 #include "message.h"
4 #include <string.h>
5 #include <limits.h>
6 
7 #ifndef ARM_CORTEX
8 static int16_t saturateTo16(int64_t value){
9  if(value > SHRT_MAX)
10  value = SHRT_MAX;
11  else if(value < SHRT_MIN)
12  value = SHRT_MIN;
13  return value;
14 }
15 #endif
16 
17 void ShortArray::getMin(int16_t* value, int* index){
19 #ifdef ARM_CORTEX
20  uint32_t idx;
21  arm_min_q15(data, size, value, &idx);
22  *index = (int)idx;
23 #else
24  *value=data[0];
25  *index=0;
26  for(size_t n=1; n<size; n++){
27  int16_t currentValue=data[n];
28  if(currentValue<*value){
29  *value=currentValue;
30  *index=n;
31  }
32  }
33 #endif
34 }
35 
37  int16_t value;
38  int index;
40  getMin(&value, &index);
41  return value;
42 }
43 
45  int16_t value;
46  int index;
48  getMin(&value, &index);
49  return index;
50 }
51 
52 void ShortArray::getMax(int16_t* value, int* index){
53  ASSERT(size>0, "Wrong size");
55 #ifdef ARM_CORTEX
56  uint32_t idx;
57  arm_max_q15(data, size, value, &idx);
58  *index = (int)idx;
59 #else
60  *value=data[0];
61  *index=0;
62  for(size_t n=1; n<size; n++){
63  int16_t currentValue=data[n];
64  if(currentValue>*value){
65  *value=currentValue;
66  *index=n;
67  }
68  }
69 #endif
70 }
71 
73  int16_t value;
74  int index;
76  getMax(&value, &index);
77  return value;
78 }
79 
81  int16_t value;
82  int index;
84  getMax(&value, &index);
85  return index;
86 }
87 
88 void ShortArray::rectify(ShortArray& destination){ //this is actually "copy data with rectifify"
90 #ifdef ARM_CORTEX
91  arm_abs_q15(data, destination.getData(), size);
92 #else
93  size_t minSize= min(size,destination.getSize()); //TODO: shall we take this out and allow it to segfault?
94  for(size_t n=0; n<minSize; n++){
95  destination[n] = fabs(data[n]);
96  }
97 #endif
98 }
99 
100 void ShortArray::rectify(){//in place
102  rectify(*this);
103 }
104 
105 void ShortArray::reverse(ShortArray& destination){ //this is actually "copy data with reverse"
106  if(destination==*this){ //make sure it is not called "in-place"
107  reverse();
108  return;
109  }
110  for(size_t n=0; n<size; n++){
111  destination[n]=data[size-n-1];
112  }
113 }
114 
115 void ShortArray::reverse(){//in place
116  for(size_t n=0; n<size/2; n++){
117  int16_t temp=data[n];
118  data[n]=data[size-n-1];
119  data[size-n-1]=temp;
120  }
121 }
122 
124  int16_t* data = getData();
125  for(size_t n=0; n<getSize(); n++)
126  destination[n] = (int16_t)(0.5 + 1.0f/data[n]);
127 }
128 
129 void ShortArray::reciprocal(){//in place
130  reciprocal(*this);
131 }
132 
134  int16_t result;
135 #ifdef ARM_CORTEX
137  arm_rms_q15 (data, size, &result);
138 #else
139  int64_t value = 0;
140  for(size_t n=0; n < size; ++n){
141  value += data[n] * data[n];
142  }
143  value = sqrtf(value / size);
144  result = saturateTo16(value);
145 #endif
146  return result;
147 }
148 
150  int16_t result;
152 #ifdef ARM_CORTEX
153  arm_mean_q15 (data, size, &result);
154 #else
155  int32_t value = 0;
156  for(size_t n=0; n < size; n++){
157  value += data[n];
158  }
159  value = value / size;
160  result = saturateTo16(value);
161 #endif
162  return result;
163 }
164 
166  int64_t result;
168 #ifdef ARM_CORTEX
169  arm_power_q15 (data, size, &result);
170 #else
171  result=0;
172  int16_t *pSrc = data;
173  for(size_t n=0; n < size; n++){
174  int32_t value = (int32_t)pSrc[n]*pSrc[n];
175  result += saturateTo16(value >> 15);
176  }
177 #endif
178  return result;
179 }
180 
182  int16_t result;
184 #ifdef ARM_CORTEX
185  arm_std_q15 (data, size, &result);
186 #else
187  result=sqrtf(getVariance());
188 #endif
189  return result;
190 }
191 
193  int16_t result;
195 #ifdef ARM_CORTEX
196  arm_var_q15(data, size, &result);
197 #else
198  int16_t sumOfSquares=getPower();
199  int16_t sum=0;
200  for(size_t n=0; n<size; n++){
201  sum+=data[n];
202  }
203  result=(sumOfSquares - sum*sum/size) / (size - 1);
204 #endif
205  return result;
206 }
207 
208 void ShortArray::clip(int16_t max){
209  for(size_t n=0; n<size; n++){
210  if(data[n]>max)
211  data[n]=max;
212  else if(data[n]<-max)
213  data[n]=-max;
214  }
215 }
216 
217 void ShortArray::clip(int16_t min, int16_t max){
218  for(size_t n=0; n<size; n++){
219  if(data[n]>max)
220  data[n]=max;
221  else if(data[n]<min)
222  data[n]=min;
223  }
224 }
225 
226 ShortArray ShortArray::subArray(int offset, size_t length){
227  ASSERT(size >= offset+length, "Array too small");
228  return ShortArray(data+offset, length);
229 }
230 
231 void ShortArray::setAll(int16_t value){
233 #ifdef ARM_CORTEX
234  arm_fill_q15(value, data, size);
235 #else
236  for(size_t n=0; n<size; n++){
237  data[n]=value;
238  }
239 #endif /* ARM_CORTEX */
240 }
241 
242 void ShortArray::add(ShortArray operand2, ShortArray destination){ //allows in-place
243  ASSERT(operand2.size >= size && destination.size<=size, "Arrays must be matching size");
245 #ifdef ARM_CORTEX
246  /* despite not explicitely documented in the CMSIS documentation,
247  this has been tested to behave properly even when pSrcA==pDst
248  void arm_add_q15 (int16_t32_t *pSrcA, int16_t32_t *pSrcB, int16_t32_t *pDst, uint32_t blockSize)
249  */
250  arm_add_q15(data, operand2.data, destination.data, size);
251 #else
252  for(size_t n=0; n<size; n++){
253  int32_t value = data[n] + operand2[n];
254  destination[n] = saturateTo16(value);
255  }
256 #endif /* ARM_CORTEX */
257 }
258 
259 void ShortArray::add(ShortArray operand2){ //in-place
261  add(operand2, *this);
262 }
263 
264 void ShortArray::add(int16_t scalar){
265 #ifdef ARM_CORTEX
266  int16_t doubleScalar[2] = {scalar, scalar};
267  q15_t * pSrcA = data;
268  q15_t * pSrcB = (int16_t*)&doubleScalar;
269  q15_t * pDst = data;
270  uint32_t blockSize = size;
271 
272  uint32_t blkCnt; /* loop counter */
273 /* Run the below code for Cortex-M4 and Cortex-M3 */
274  q31_t inA1, inA2, inB1, inB2;
275 
276  /*loop Unrolling */
277  blkCnt = blockSize >> 2u;
278 
279  /* First part of the processing with loop unrolling. Compute 4 outputs at a time.
280  ** a second loop below computes the remaining 1 to 3 samples. */
281  while(blkCnt > 0u)
282  {
283  /* C = A + B */
284  /* Add and then store the results in the destination buffer. */
285  inA1 = *__SIMD32(pSrcA)++;
286  inA2 = *__SIMD32(pSrcA)++;
287  inB1 = *__SIMD32(pSrcB); //HERE: do not increment pSrcB
288  inB2 = *__SIMD32(pSrcB); //HERE: do not increment pSrcB
289 
290  *__SIMD32(pDst)++ = __QADD16(inA1, inB1);
291  *__SIMD32(pDst)++ = __QADD16(inA2, inB2);
292 
293  /* Decrement the loop counter */
294  blkCnt--;
295  }
296 
297  /* If the blockSize is not a multiple of 4, compute any remaining output samples here.
298  ** No loop unrolling is used. */
299  blkCnt = blockSize % 0x4u;
300 
301  while(blkCnt > 0u)
302  {
303  /* C = A + B */
304  /* Add and then store the results in the destination buffer. */
305  *pDst++ = (q15_t) __QADD16(*pSrcA++, *pSrcB); //HERE: do not increment pSrcB
306 
307  /* Decrement the loop counter */
308  blkCnt--;
309  }
310 #else
311  for(size_t n=0; n < size; ++n){
312  int32_t value = data[n] + scalar;
313  data[n] = saturateTo16(value);
314  }
315 #endif
316 }
317 
318 void ShortArray::subtract(ShortArray operand2, ShortArray destination){ //allows in-place
319  ASSERT(operand2.size == size && destination.size >= size, "Arrays size mismatch");
320 #ifdef ARM_CORTEX
322  /* despite not explicitely documented in the CMSIS documentation,
323  this has been tested to behave properly even when pSrcA==pDst
324  void arm_sub_q15 (int16_t32_t *pSrcA, int16_t32_t *pSrcB, int16_t32_t *pDst, uint32_t blockSize)
325  */
326  arm_sub_q15(data, operand2.data, destination.data, size);
327 #else
328  for(size_t n=0; n < size; ++n){
329  int32_t value = data[n] - operand2[n];
330  destination[n] = saturateTo16(value);
331  }
332 #endif /* ARM_CORTEX */
333 }
334 
335 void ShortArray::subtract(ShortArray operand2){ //in-place
337  subtract(operand2, *this);
338 }
339 
340 void ShortArray::subtract(int16_t scalar)
341 {
342 #ifdef ARM_CORTEX
343  // this method is modelled on arm_sub_q15
344  // from <a href="http://www.keil.com/pack/doc/CMSIS/General/html/index.html">CMSIS library</a>
345  // just avoid incrementing the pSrcB pointer(see below)
346  int16_t *pSrcA = data;
347  int16_t scalarDouble[2] = {scalar, scalar};
348  int16_t *pSrcB = scalarDouble;
349  int16_t *pDst = data;
350  uint32_t blockSize = size;
351 
352  uint32_t blkCnt; /* loop counter */
353 /* Run the below code for Cortex-M4 and Cortex-M3 */
354  q31_t inA1, inA2;
355  q31_t inB1, inB2;
356 
357  /*loop Unrolling */
358  blkCnt = blockSize >> 2u;
359 
360  /* First part of the processing with loop unrolling. Compute 4 outputs at a time.
361  ** a second loop below computes the remaining 1 to 3 samples. */
362  while(blkCnt > 0u)
363  {
364  /* C = A - B */
365  /* Subtract and then store the results in the destination buffer two samples at a time. */
366  inA1 = *__SIMD32(pSrcA)++;
367  inA2 = *__SIMD32(pSrcA)++;
368  inB1 = *__SIMD32(pSrcB); //HERE: not incrementing pSrcB
369  inB2 = *__SIMD32(pSrcB); //HERE: not incrementing pSrcB
370 
371  *__SIMD32(pDst)++ = __QSUB16(inA1, inB1);
372  *__SIMD32(pDst)++ = __QSUB16(inA2, inB2);
373 
374  /* Decrement the loop counter */
375  blkCnt--;
376  }
377 
378  /* If the blockSize is not a multiple of 4, compute any remaining output samples here.
379  ** No loop unrolling is used. */
380  blkCnt = blockSize % 0x4u;
381 
382  while(blkCnt > 0u)
383  {
384  /* C = A - B */
385  /* Subtract and then store the result in the destination buffer. */
386  *pDst++ = (q15_t) __QSUB16(*pSrcA++, *pSrcB); //HERE: not incrementing pSrcB
387 
388  /* Decrement the loop counter */
389  blkCnt--;
390  }
391 #else
392  for(size_t n = 0; n < size; ++n){
393  int32_t value = data[n] - scalar;
394  data[n] = saturateTo16(value);
395  }
396 #endif
397 }
398 
399 void ShortArray::multiply(ShortArray operand2, ShortArray destination){ //allows in-place
400  ASSERT(operand2.size == size && destination.size==size, "Arrays must be same size");
402 #ifdef ARM_CORTEX
403  /* despite not explicitely documented in the CMSIS documentation,
404  this has been tested to behave properly even when pSrcA==pDst
405  void arm_mult_q15 (int16_t32_t *pSrcA, int16_t32_t *pSrcB, int16_t32_t *pDst, uint32_t blockSize)
406  */
407  arm_mult_q15(data, operand2.data, destination, size);
408 #else
409  for(size_t n=0; n<size; n++){
410  int32_t value = data[n] * operand2[n];
411  destination[n] = saturateTo16(value >> 15);
412  }
413 
414 #endif /* ARM_CORTEX */
415 }
416 
417 void ShortArray::multiply(ShortArray operand2){ //in-place
419  multiply(operand2, *this);
420 }
421 
422 void ShortArray::multiply(int16_t scalar){
423  // this method is modelled on arm_mult_q15
424  // from <a href="http://www.keil.com/pack/doc/CMSIS/General/html/index.html">CMSIS library</a>
425  // just avoid incrementing the pSrcB pointer(see below)
426 #ifdef ARM_CORTEX
427  arm_scale_q15(data, scalar, 0, data, size);
428 #else
429  for(size_t n = 0; n < size; ++n){
430  int32_t value = data[n] * scalar;
431  data[n] = saturateTo16(value >> 15);
432  }
433 #endif
434 }
435 
436 void ShortArray::negate(ShortArray& destination){//allows in-place
438 #ifdef ARM_CORTEX
439  arm_negate_q15(data, destination.getData(), size);
440 #else
441  for(size_t n=0; n<size; n++){
442  destination[n]=-data[n];
443  }
444 #endif /* ARM_CORTEX */
445 }
448  negate(*this);
449 }
450 
452  noise(-32768, 32767);
453 }
454 
455 void ShortArray::noise(int16_t min, int16_t max){
456  uint16_t amplitude = abs((int32_t)max-(int32_t)min);
457  int16_t offset = min;
458  for(size_t n=0; n<size; n++){
459  data[n]=(rand()/((float)RAND_MAX)) * amplitude + offset;
460  }
461 }
462 
463 
464 void ShortArray::convolve(ShortArray operand2, ShortArray destination){
465  ASSERT(destination.size >= size + operand2.size -1, "Destination array too small");
467 #ifdef ARM_CORTEX
468  arm_conv_q15(data, size, operand2.data, operand2.size, destination);
469 #else
470  size_t size2=operand2.getSize();
471  for (size_t n=0; n<size+size2-1; n++){
472  size_t n1=n;
473  destination[n] =0;
474  for(size_t k=0; k<size2; k++){
475  if(n1>=0 && n1<size)
476  destination[n]+=data[n1]*operand2[k];
477  n1--;
478  }
479  }
480 #endif /* ARM_CORTEX */
481 }
482 
483 void ShortArray::convolve(ShortArray operand2, ShortArray destination, int offset, size_t samples){
484  ASSERT(destination.size >= size + operand2.size -1, "Destination array too small"); //TODO: change this condition to the actual size being written(will be samples+ tail)
486 #ifdef ARM_CORTEX
487  //TODO: I suspect a bug in arm_conv_partial_q15
488  //it seems that destination[n] is left unchanged for n<offset
489  //and the result is actually stored from destination[offset] onwards
490  //that is, in the same position where they would be if a full convolution was performed.
491  //This requires (destination.size >= size + operand2.size -1). Ideally you would want destination to be smaller
492  arm_conv_partial_q15(data, size, operand2.data, operand2.size, destination.getData(), offset, samples);
493 #else
494  //this implementations reproduces the (buggy?) behaviour of arm_conv_partial (see comment above and inline comments below)
495  /*
496  This implementation is just a copy/paste/edit from the overloaded method
497  */
498  size_t size2=operand2.getSize();
499  for (size_t n=offset; n<offset+samples; n++){
500  size_t n1=n;
501  destination[n] =0; //this should be [n-offset]
502  for(size_t k=0; k<size2; k++){
503  if(n1>=0 && n1<size)
504  destination[n]+=data[n1]*operand2[k];//this should be destination[n-offset]
505  n1--;
506  }
507  }
508 #endif /* ARM_CORTEX */
509 }
510 
511 void ShortArray::correlate(ShortArray operand2, ShortArray destination){
512  destination.setAll(0);
514  correlateInitialized(operand2, destination);
515 }
517  ASSERT(destination.size >= size+operand2.size-1, "Destination array too small"); //TODO: change CMSIS docs, which state a different size
519 #ifdef ARM_CORTEX
520  arm_correlate_q15(data, size, operand2.data, operand2.size, destination);
521 #else
522  //correlation is the same as a convolution where one of the signals is flipped in time
523  //so we flip in time operand2
524  operand2.reverse();
525  //and convolve it with fa to obtain the correlation
526  convolve(operand2, destination);
527  //and we flip back operand2, so that the input is not modified
528  operand2.reverse();
529 #endif /* ARM_CORTEX */
530 }
531 
532 void ShortArray::shift(int shiftValue){
533 #ifdef ARM_CORTEX
534  arm_shift_q15(data, shiftValue, data, size);
535 #else
536  if(shiftValue >= 0){
537  for(size_t n=0; n<size; n++)
538  data[n] = data[n] << shiftValue;
539  }else{
540  shiftValue = -shiftValue;
541  for(size_t n=0; n<size; n++)
542  data[n] = data[n] >> shiftValue;
543  }
544 #endif
545 }
546 
547 void ShortArray::setFloatValue(uint32_t n, float value){
548  data[n] = value * -SHRT_MIN;
549 }
550 
551 float ShortArray::getFloatValue(uint32_t n){
552  return data[n] / (float)-SHRT_MIN;
553 }
554 
556  ASSERT(source.getSize() == size, "Size does not match");
557 #ifdef ARM_CORTEX
559  arm_float_to_q15((float*)source, data, size);
560 #else
561  for(size_t n = 0; n < size; ++n){
562  setFloatValue(n, source[n]);
563  }
564 #endif
565 }
566 
567 void ShortArray::toFloat(FloatArray destination){
568  ASSERT(destination.getSize() == size, "Size does not match");
569 #ifdef ARM_CORTEX
571  arm_q15_to_float(data, (float*)destination, size);
572 #else
573  for(size_t n = 0; n < size; ++n){
574  destination[n] = getFloatValue(n);
575  }
576 #endif
577 }
578 
580  ShortArray fa(new int16_t[size], size);
581  fa.clear();
582  return fa;
583 }
584 
586  delete[] array.data;
587 }
static int16_t saturateTo16(int64_t value)
Definition: ShortArray.cpp:8
#define abs(x)
Definition: basicmaths.h:44
#define min(a, b)
Definition: basicmaths.h:38
#define max(a, b)
Definition: basicmaths.h:41
This class contains useful methods for manipulating arrays of floats.
Definition: FloatArray.h:12
This class contains useful methods for manipulating arrays of int16_ts.
Definition: ShortArray.h:12
int16_t getMean()
Mean of the array.
Definition: ShortArray.cpp:149
void correlate(ShortArray operand2, ShortArray destination)
Correlation between arrays.
Definition: ShortArray.cpp:511
void rectify()
Absolute value of the array.
Definition: ShortArray.cpp:100
void setFloatValue(uint32_t n, float value)
Converts a float to int16 and stores it.
Definition: ShortArray.cpp:547
void setAll(int16_t value)
Set all the values in the array.
Definition: ShortArray.cpp:231
int16_t getStandardDeviation()
Standard deviation of the array.
Definition: ShortArray.cpp:181
void noise()
Random values Fills the array with random values in the range [-1, 1)
Definition: ShortArray.cpp:451
void fromFloat(FloatArray source)
Copies the content of a FloatArray into a ShortArray, converting the float elements to fixed-point 1....
Definition: ShortArray.cpp:555
void toFloat(FloatArray destination)
Copies the content of the array to a FloatArray, interpreting the content of the ShortArray as 1....
Definition: ShortArray.cpp:567
void reverse(ShortArray &destination)
Reverse the array Copies the elements of the array in reversed order into destination.
Definition: ShortArray.cpp:105
int getMinIndex()
Get the index of the minimum value in the array.
Definition: ShortArray.cpp:44
void shift(int shiftValue)
Bitshift the array values, saturating.
Definition: ShortArray.cpp:532
void clip(int16_t range)
Clips the elements in the array in the range [-**range**, range].
Definition: ShortArray.cpp:208
static void destroy(ShortArray array)
Destroys a ShortArray created with the create() method.
Definition: ShortArray.cpp:585
ShortArray subArray(int offset, size_t length)
A subset of the array.
Definition: ShortArray.cpp:226
void clear()
Clear the array.
Definition: ShortArray.h:22
void subtract(ShortArray operand2, ShortArray destination)
Element-wise difference between arrays.
Definition: ShortArray.cpp:318
void multiply(ShortArray operand2, ShortArray destination)
Element-wise multiplication between arrays.
Definition: ShortArray.cpp:399
int16_t getMaxValue()
Get the maximum value in the array.
Definition: ShortArray.cpp:72
float getFloatValue(uint32_t n)
Returns an element of the array converted to float.
Definition: ShortArray.cpp:551
void reciprocal()
Reciprocal of the array.
Definition: ShortArray.cpp:129
int16_t getMinValue()
Get the minimum value in the array.
Definition: ShortArray.cpp:36
int16_t getVariance()
Variance of the array.
Definition: ShortArray.cpp:192
int16_t getRms()
Root mean square value of the array.
Definition: ShortArray.cpp:133
static ShortArray create(int size)
Creates a new ShortArray.
Definition: ShortArray.cpp:579
void getMax(int16_t *value, int *index)
Get the maximum value in the array and its index.
Definition: ShortArray.cpp:52
int64_t getPower()
Power of the array.
Definition: ShortArray.cpp:165
int getMaxIndex()
Get the index of the maximum value in the array.
Definition: ShortArray.cpp:80
void negate()
Negate the array.
Definition: ShortArray.cpp:446
void correlateInitialized(ShortArray operand2, ShortArray destination)
Correlation between arrays.
Definition: ShortArray.cpp:516
void getMin(int16_t *value, int *index)
Get the minimum value in the array and its index.
Definition: ShortArray.cpp:17
void convolve(ShortArray operand2, ShortArray destination)
Convolution between arrays.
Definition: ShortArray.cpp:464
void add(ShortArray operand2, ShortArray destination)
Element-wise sum between arrays.
Definition: ShortArray.cpp:242
void reverse()
Reverse the array.
Definition: ShortArray.cpp:115
size_t getSize() const
Definition: SimpleArray.h:31
T * getData()
Get the data stored in the Array.
Definition: SimpleArray.h:27
#define ASSERT(cond, msg)
Definition: message.h:16
int16_t q15_t
Definition: qint.h:25
int32_t q31_t
Definition: qint.h:26