-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathfix16_trig.c
190 lines (164 loc) · 5.14 KB
/
fix16_trig.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include <limits.h>
#include "fix16.h"
#if defined(FIXMATH_SIN_LUT)
#include "fix16_trig_sin_lut.h"
#elif !defined(FIXMATH_NO_CACHE)
static fix16_t _fix16_sin_cache_index[4096] = { 0 };
static fix16_t _fix16_sin_cache_value[4096] = { 0 };
#endif
#ifndef FIXMATH_NO_CACHE
static fix16_t _fix16_atan_cache_index[2][4096] = { { 0 }, { 0 } };
static fix16_t _fix16_atan_cache_value[4096] = { 0 };
#endif
fix16_t fix16_sin_parabola(fix16_t inAngle)
{
fix16_t abs_inAngle, abs_retval, retval;
fix16_t mask;
/* Absolute function */
mask = (inAngle >> (sizeof(fix16_t)*CHAR_BIT-1));
abs_inAngle = (inAngle + mask) ^ mask;
/* On 0->PI, sin looks like x² that is :
- centered on PI/2,
- equals 1 on PI/2,
- equals 0 on 0 and PI
that means : 4/PI * x - 4/PI² * x²
Use abs(x) to handle (-PI) -> 0 zone.
*/
retval = fix16_mul(FOUR_DIV_PI, inAngle) + fix16_mul( fix16_mul(_FOUR_DIV_PI2, inAngle), abs_inAngle );
/* At this point, retval equals sin(inAngle) on important points ( -PI, -PI/2, 0, PI/2, PI),
but is not very precise between these points
*/
#ifndef FIXMATH_FAST_SIN
/* Absolute value of retval */
mask = (retval >> (sizeof(fix16_t)*CHAR_BIT-1));
abs_retval = (retval + mask) ^ mask;
/* So improve its precision by adding some x^4 component to retval */
retval += fix16_mul(X4_CORRECTION_COMPONENT, fix16_mul(retval, abs_retval) - retval );
#endif
return retval;
}
fix16_t fix16_sin(fix16_t inAngle)
{
fix16_t tempAngle = inAngle % (fix16_pi << 1);
#ifdef FIXMATH_SIN_LUT
if(tempAngle < 0)
tempAngle += (fix16_pi << 1);
fix16_t tempOut;
if(tempAngle >= fix16_pi) {
tempAngle -= fix16_pi;
if(tempAngle >= (fix16_pi >> 1))
tempAngle = fix16_pi - tempAngle;
tempOut = -(tempAngle >= _fix16_sin_lut_count ? fix16_one : _fix16_sin_lut[tempAngle]);
} else {
if(tempAngle >= (fix16_pi >> 1))
tempAngle = fix16_pi - tempAngle;
tempOut = (tempAngle >= _fix16_sin_lut_count ? fix16_one : _fix16_sin_lut[tempAngle]);
}
#else
if(tempAngle > fix16_pi)
tempAngle -= (fix16_pi << 1);
else if(tempAngle < -fix16_pi)
tempAngle += (fix16_pi << 1);
#ifndef FIXMATH_NO_CACHE
fix16_t tempIndex = ((inAngle >> 5) & 0x00000FFF);
if(_fix16_sin_cache_index[tempIndex] == inAngle)
return _fix16_sin_cache_value[tempIndex];
#endif
fix16_t tempAngleSq = fix16_mul(tempAngle, tempAngle);
#ifndef FIXMATH_FAST_SIN // Most accurate version, accurate to ~2.1%
fix16_t tempOut = tempAngle;
tempAngle = fix16_mul(tempAngle, tempAngleSq);
tempOut -= (tempAngle / 6);
tempAngle = fix16_mul(tempAngle, tempAngleSq);
tempOut += (tempAngle / 120);
tempAngle = fix16_mul(tempAngle, tempAngleSq);
tempOut -= (tempAngle / 5040);
tempAngle = fix16_mul(tempAngle, tempAngleSq);
tempOut += (tempAngle / 362880);
tempAngle = fix16_mul(tempAngle, tempAngleSq);
tempOut -= (tempAngle / 39916800);
#else // Fast implementation, runs at 159% the speed of above 'accurate' version with an slightly lower accuracy of ~2.3%
fix16_t tempOut;
tempOut = fix16_mul(-13, tempAngleSq) + 546;
tempOut = fix16_mul(tempOut, tempAngleSq) - 10923;
tempOut = fix16_mul(tempOut, tempAngleSq) + 65536;
tempOut = fix16_mul(tempOut, tempAngle);
#endif
#ifndef FIXMATH_NO_CACHE
_fix16_sin_cache_index[tempIndex] = inAngle;
_fix16_sin_cache_value[tempIndex] = tempOut;
#endif
#endif
return tempOut;
}
fix16_t fix16_cos(fix16_t inAngle)
{
return fix16_sin(inAngle + (fix16_pi >> 1));
}
fix16_t fix16_tan(fix16_t inAngle)
{
return fix16_sdiv(fix16_sin(inAngle), fix16_cos(inAngle));
}
fix16_t fix16_asin(fix16_t x)
{
if((x > fix16_one)
|| (x < -fix16_one))
return 0;
else if(x == fix16_one)
return fix16_pi_div_2;
else if(x == 0)
return 0;
fix16_t out;
out = (fix16_one - fix16_mul(x, x));
out = fix16_div(x, fix16_sqrt(out));
out = fix16_atan(out);
return out;
}
fix16_t fix16_acos(fix16_t x)
{
if(x == fix16_one)
return 0;
else if(x == 0)
return fix16_pi_div_2;
return ((fix16_pi >> 1) - fix16_asin(x));
}
fix16_t fix16_atan2(fix16_t inY , fix16_t inX)
{
fix16_t abs_inY, mask, angle, r, r_3;
#ifndef FIXMATH_NO_CACHE
uintptr_t hash = (inX ^ inY);
hash ^= hash >> 20;
hash &= 0x0FFF;
if((_fix16_atan_cache_index[0][hash] == inX) && (_fix16_atan_cache_index[1][hash] == inY))
return _fix16_atan_cache_value[hash];
#endif
/* Absolute inY */
mask = (inY >> (sizeof(fix16_t)*CHAR_BIT-1));
abs_inY = (inY + mask) ^ mask;
if (inX >= 0)
{
r = fix16_div( (inX - abs_inY), (inX + abs_inY));
r_3 = fix16_mul(fix16_mul(r, r),r);
angle = fix16_mul(0x00003240 , r_3) - fix16_mul(0x0000FB50,r) + PI_DIV_4;
} else {
r = fix16_div( (inX + abs_inY), (abs_inY - inX));
r_3 = fix16_mul(fix16_mul(r, r),r);
angle = fix16_mul(0x00003240 , r_3)
- fix16_mul(0x0000FB50,r)
+ THREE_PI_DIV_4;
}
if (inY < 0)
{
angle = -angle;
}
#ifndef FIXMATH_NO_CACHE
_fix16_atan_cache_index[0][hash] = inX;
_fix16_atan_cache_index[1][hash] = inY;
_fix16_atan_cache_value[hash] = angle;
#endif
return angle;
}
fix16_t fix16_atan(fix16_t x)
{
return fix16_atan2(x, fix16_one);
}