1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2017 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 
10 /*! \file approximations.c
11     \brief Math approximations file
12 
13     Significant efficiencies were found by creating a set of trig functions
14     which trade off precision for improved power/CPU performance.  Full details
15     are included in Application Note AN5015: Trigonometry Approximations
16 */
17 
18 #include "math.h"
19 #include "stdlib.h"
20 #include "stdint.h"
21 
22 #include "approximations.h"
23 
24 // function returns an approximation to angle(deg)=asin(x) for x in the range -1 <= x <= 1
25 // and returns -90 <= angle <= 90 deg
26 
27 // maximum error is 10.29E-6 deg
fasin_deg(float x)28 float fasin_deg(float x)
29 {
30     // for robustness, check for invalid argument
31     if (x >= 1.0F) return 90.0F;
32     if (x <= -1.0F) return -90.0F;
33 
34     // call the atan which will return an angle in the correct range -90 to 90 deg
35     // this line cannot fail from division by zero or negative square root since |x| < 1
36     return (fatan_deg(x / sqrtf(1.0F - x * x)));
37 }
38 
39 // function returns an approximation to angle(deg)=acos(x) for x in the range -1 <= x <= 1
40 // and returns 0 <= angle <= 180 deg
41 
42 // maximum error is 14.67E-6 deg
facos_deg(float x)43 float facos_deg(float x)
44 {
45     // for robustness, check for invalid arguments
46     if (x >= 1.0F) return 0.0F;
47     if (x <= -1.0F) return 180.0F;
48 
49     // call the atan which will return an angle in the incorrect range -90 to 90 deg
50     // these lines cannot fail from division by zero or negative square root
51     if (x == 0.0F) return 90.0F;
52     if (x > 0.0F) return fatan_deg((sqrtf(1.0F - x * x) / x));
53     return 180.0F + fatan_deg((sqrtf(1.0F - x * x) / x));
54 }
55 
56 // function returns angle in range -90 to 90 deg
57 
58 // maximum error is 9.84E-6 deg
fatan_deg(float x)59 float fatan_deg(float x)
60 {
61     float   fangledeg;                  // compute computed (deg)
62     int8_t    ixisnegative;             // argument x is negative
63     int8_t    ixexceeds1;               // argument x is greater than 1.0
64     int8_t    ixmapped;                 // argument in range tan(15 deg) to tan(45 deg)=1.0
65 #define TAN15DEG    0.26794919243F      // tan(15 deg) = 2 - sqrt(3)
66 #define TAN30DEG    0.57735026919F      // tan(30 deg) = 1/sqrt(3)
67     // reset all flags
68     ixisnegative = ixexceeds1 = ixmapped = 0;
69 
70     // test for negative argument to allow use of tan(-x)=-tan(x)
71     if (x < 0.0F)
72     {
73         x = -x;
74         ixisnegative = 1;
75     }
76 
77     // test for argument above 1 to allow use of atan(x)=pi/2-atan(1/x)
78     if (x > 1.0F)
79     {
80         x = 1.0F / x;
81         ixexceeds1 = 1;
82     }
83 
84     // at this point, x is in the range 0 to 1 inclusive
85     // map argument onto range -tan(15 deg) to tan(15 deg)
86     // using tan(angle-30deg) = (tan(angle)-tan(30deg)) / (1 + tan(angle)tan(30deg))
87     // tan(15deg) maps to tan(-15 deg) = -tan(15 deg)
88     // 1. maps to (sqrt(3) - 1) / (sqrt(3) + 1) = 2 - sqrt(3) = tan(15 deg)
89     if (x > TAN15DEG)
90     {
91         x = (x - TAN30DEG) / (1.0F + TAN30DEG * x);
92         ixmapped = 1;
93     }
94 
95     // call the atan estimator to obtain -15 deg <= angle <= 15 deg
96     fangledeg = fatan_15deg(x);
97 
98     // undo the distortions applied earlier to obtain -90 deg <= angle <= 90 deg
99     if (ixmapped) fangledeg += 30.0F;
100     if (ixexceeds1) fangledeg = 90.0F - fangledeg;
101     if (ixisnegative) fangledeg = -fangledeg;
102 
103     return (fangledeg);
104 }
105 
106 // function returns approximate atan2 angle in range -180 to 180 deg
107 
108 // maximum error is 14.58E-6 deg
fatan2_deg(float y,float x)109 float fatan2_deg(float y, float x)
110 {
111     // check for zero x to avoid division by zero
112     if (x == 0.0F)
113     {
114         // return 90 deg for positive y
115         if (y > 0.0F) return 90.0F;
116 
117         // return -90 deg for negative y
118         if (y < 0.0F) return -90.0F;
119 
120         // otherwise y= 0.0 and return 0 deg (invalid arguments)
121         return 0.0F;
122     }
123 
124     // from here onwards, x is guaranteed to be non-zero
125     // compute atan2 for quadrant 1 (0 to 90 deg) and quadrant 4 (-90 to 0 deg)
126     if (x > 0.0F) return (fatan_deg(y / x));
127 
128     // compute atan2 for quadrant 2 (90 to 180 deg)
129     if ((x < 0.0F) && (y > 0.0F)) return (180.0F + fatan_deg(y / x));
130 
131     // compute atan2 for quadrant 3 (-180 to -90 deg)
132     return (-180.0F + fatan_deg(y / x));
133 }
134 
135 // approximation to inverse tan function (deg) for x in range
136 // -tan(15 deg) to tan(15 deg) giving an output -15 deg <= angle <= 15 deg
137 
138 // using modified Pade[3/2] approximation
fatan_15deg(float x)139 float fatan_15deg(float x)
140 {
141     float   x2;                 // x^2
142 #define PADE_A  96.644395816F   // theoretical Pade[3/2] value is 5/3*180/PI=95.49296
143 #define PADE_B  25.086941612F   // theoretical Pade[3/2] value is 4/9*180/PI=25.46479
144 #define PADE_C  1.6867633134F   // theoretical Pade[3/2] value is 5/3=1.66667
145     // compute the approximation to the inverse tangent
146     // the function is anti-symmetric as required for positive and negative arguments
147     x2 = x * x;
148     return (x * (PADE_A + x2 * PADE_B) / (PADE_C + x2));
149 }
150