Nextion: Calculate inverse tan (arctan) without trig functions or floating point

143 views Asked by At

I am programming an interface on this Nextion display. Specifically, I am trying to program a digital dial that rotates around a centre point.

These screens are great, but they don’t have any trig functions, they don’t have floating point and can’t rotate images. Just sharing these specs for background on my constraints, don’t worry about the screen.

I need a way to convert X,Y coordinates into a 0-359 output.

Does anyone know any hacks or tricks using integer math to approximate an angle?

All I have right now is gradients adjusted for each quartile, but it’s not linear because I’m just doing rise/run.

2

There are 2 answers

0
ezra_vdj On BEST ANSWER

Thankyou @jhole for pointing me in the direction of the arctan approximation formula. I ended up modifying it a bit to fit what I needed for my Nextion screen, including inputs for X & Y coordinates, outputting in degrees, and not making the variables too large to overflow (opting for a x10 multiplier to allow 1 digit for rounding).

I was able to get it up and running with this formula that outputs degrees (screenshot from Desmos where 'a' = 380 and 'b' = 400):

enter image description here

'a' is Y coordinate and 'b' is the X coordinate. I've included in the picture the benchmark trig function and you can see it's pretty close. I wanted the result to be 10x multiplied to allow for rounding since everything below the decimal point is lost on the Nextion.

I really wanted to remove those top two brackets to reduce everything into one big fraction that has one divide, but I found that the variables get way too large, so I found this to be a great sweet spot of speed and accuracy.

Crucial things to note with the implementation of this formula is that it only works where RISE <= RUN, and 'a' and 'b' are both positive. To get a measurement in the top and bottom four 45 degree segments I had to flip around the RISE/RUN variables. From there I was able to add/subtract from the 0,180,270,360 values depending on what quadrant you are touching.

0
jhole On

There is a paper Efficient approximations for the arctangent function describing a fast atan function:

double FastArcTan(double x)
{
    return M_PI_4*x - x*(fabs(x) - 1)*(0.2447 + 0.0663*fabs(x));
}

this uses double data type of course.

Rewritten in fixed point arithmetic with scale factor 10000 (so "(int) 10000" means "1.0" actually) you can write a function int IntArcTan(int x_scaled), here as complete little test program:

#include <stdio.h>
#include <math.h>

// -10000 < x_scaled < 10000; integer arithmetic i.e. (int)10000 means (float)1.0000
int IntArcTan(int x_scaled)
{
   int abs_x_scaled = x_scaled >= 0 ? x_scaled : -x_scaled;
   int a = 7854/* PI/4 */ * x_scaled / 10000;
   int b = x_scaled*(abs_x_scaled - 10000) / 10000;
   int c = b * 2447 / 10000;
   int d = b * 663 / 10000 * abs_x_scaled / 10000;

   return a - c - d;
}

int main()
{
   for (double x = -1.; x < 1.; x+=.01)
   {
      double   atan_std           = atan(x);
      int      atan_int_scaled    = IntArcTan((int)(x * 10000));
      double   atan_int           = (double)atan_int_scaled / 10000;
      printf("x = %10.3f, atan_std(x) = %10.3f, atan_int(x) = %10.3f, error = %10.3f%%\n", x, atan_std, atan_int, atan_int/atan_std*100-100);
   }
   
}

You just have to remember to scale your fixed point arithmetic variables (1.0 -> 10000) and that final output of atan is in radian so to get degrees atan_int_scaled * 45 / 7854