ISSDK  1.8
IoT Sensing Software Development Kit
approximations.c
Go to the documentation of this file.
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
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
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
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
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
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 }
#define TAN30DEG
#define PADE_C
#define TAN15DEG
Math approximations file.
float fatan_deg(float x)
float fasin_deg(float x)
#define PADE_B
float fatan_15deg(float x)
float fatan2_deg(float y, float x)
float facos_deg(float x)
#define PADE_A