Search code examples
cembeddedmicrocontrollercortex-m

Shape modification of sine signal in C using only integer math


I want to generate a sine signal on an ARM Cortex M0 microcontroller that is customizable concerning its shape by a shape parameter n. Have a look at the plot below as an example. Here I modelled the different curves with the function

$y(x) = (1/2*(1 + sin(x)))^n$

enter image description here

For a higher n, the maxima are "shorter" and the minima are "wider". For a lower n, the maxima are "wider" and the minima are "shorter".

In order to reduce memory consumption and increase performance, I only use integer math to generate the sine signal.

Here's the code for the sine calculation:

Input range [0R 2πR] mapped to [0 to 1024].

Output range [-1.0 1.0] mapped to [-16,384 16,384].

int_fast16_t sin_bam(int angle_bam) {
  angle_bam %= 1024;
  if (angle_bam < 0)
    angle_bam += 1024;
  uint_fast16_t sub_angle = angle_bam % (1024u / 2);
  int_fast16_t sn = unsigned_mult16x16_into_32(sub_angle, (1024/2 - sub_angle)) / 4;
  if (angle_bam & 512) {
    sn = -sn;
  }
  return sn;
}

Does anyone have an idea how to realize such a shape modification only with integer math?


Solution

  • Does anyone have an idea how to realize such a shape modification only with integer math?
    n could also have a range of 1 to 10 for example

    $y(x) = (1/2*(1 + sin(x)))^n$

    OP has a sine approximation function sin_bam() and then uses that to form 1/2*(1 + sin(x)) which is the hacovercosin(θ). All that is left is a scale power function.

    #include <inttypes.h>
    #define SCALE 16384
    
    int pow_scale(int x, unsigned n) {
      unsigned y = SCALE;
      while (n>0) {
        if (n%2) {
          y = unsigned_mult16x16_into_32(x,y)/SCALE; // 16-bit x 16-bit --> 32-bit product.
        }
        n /= 2;
        x = unsigned_mult16x16_into_32(x,x)/SCALE;
      }
      return x;
    }
    

    // x is the angle in 1024 BAM per period
    // output is scaled to range [0....SCALE]
    int shaped(int x, unsigned n) {
      return pow_scale(SCALE/2 + sin_bam(x)/2), n);
    }