GCC Code Coverage Report


Directory: ./
File: overlays/wled/lib8tion/trig8.h
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 21 22 95.5%
Functions: 2 2 100.0%
Branches: 3 6 50.0%

Line Branch Exec Source
1 #ifndef __INC_LIB8TION_TRIG_H
2 #define __INC_LIB8TION_TRIG_H
3
4 #include <stdint.h>
5 #include "../lib8tion/lib8static.h"
6
7 /// @file trig8.h
8 /// Fast, efficient 8-bit trigonometry functions specifically
9 /// designed for high-performance LED programming.
10
11 /// @ingroup lib8tion
12 /// @{
13
14 /// @defgroup Trig Fast Trigonometry Functions
15 /// Fast 8-bit and 16-bit approximations of sin(x) and cos(x).
16 ///
17 /// Don't use these approximations for calculating the
18 /// trajectory of a rocket to Mars, but they're great
19 /// for art projects and LED displays.
20 ///
21 /// On Arduino/AVR, the 16-bit approximation is more than
22 /// 10X faster than floating point sin(x) and cos(x), while
23 /// the 8-bit approximation is more than 20X faster.
24 /// @{
25
26 #if defined(USE_SIN_32)
27
28 #define sin16 fl::sin16lut
29 #define cos16 fl::cos16lut
30
31 #include "fl/sin32.h"
32
33 #elif defined(__AVR__)
34
35 /// Platform-independent alias of the fast sin implementation
36 #define sin16 sin16_avr
37
38 /// Fast 16-bit approximation of sin(x). This approximation never varies more
39 /// than 0.69% from the floating point value you'd get by doing
40 /// @code{.cpp}
41 /// float s = sin(x) * 32767.0;
42 /// @endcode
43 ///
44 /// @param theta input angle from 0-65535
45 /// @returns sin of theta, value between -32767 to 32767.
46 LIB8STATIC int16_t sin16_avr(uint16_t theta) {
47 static const uint8_t data[] = {
48 0, 0, 49, 0, 6393 % 256, 6393 / 256, 48, 0,
49 12539 % 256, 12539 / 256, 44, 0, 18204 % 256, 18204 / 256, 38, 0,
50 23170 % 256, 23170 / 256, 31, 0, 27245 % 256, 27245 / 256, 23, 0,
51 30273 % 256, 30273 / 256, 14, 0, 32137 % 256, 32137 / 256, 4 /*,0*/};
52
53 uint16_t offset = (theta & 0x3FFF);
54
55 // AVR doesn't have a multi-bit shift instruction,
56 // so if we say "offset >>= 3", gcc makes a tiny loop.
57 // Inserting empty volatile statements between each
58 // bit shift forces gcc to unroll the loop.
59 offset >>= 1; // 0..8191
60 asm volatile("");
61 offset >>= 1; // 0..4095
62 asm volatile("");
63 offset >>= 1; // 0..2047
64
65 if (theta & 0x4000)
66 offset = 2047 - offset;
67
68 uint8_t sectionX4;
69 sectionX4 = offset / 256;
70 sectionX4 *= 4;
71
72 uint8_t m;
73
74 union {
75 uint16_t b;
76 struct {
77 uint8_t blo;
78 uint8_t bhi;
79 };
80 } u;
81
82 // in effect u.b = blo + (256 * bhi);
83 u.blo = data[sectionX4];
84 u.bhi = data[sectionX4 + 1];
85 m = data[sectionX4 + 2];
86
87 uint8_t secoffset8 = (uint8_t)(offset) / 2;
88
89 uint16_t mx = m * secoffset8;
90
91 int16_t y = mx + u.b;
92 if (theta & 0x8000)
93 y = -y;
94
95 return y;
96 }
97
98 #else
99
100 /// Platform-independent alias of the fast sin implementation
101 #define sin16 sin16_C
102
103 /// Fast 16-bit approximation of sin(x). This approximation never varies more
104 /// than 0.69% from the floating point value you'd get by doing
105 /// @code{.cpp}
106 /// float s = sin(x) * 32767.0;
107 /// @endcode
108 ///
109 /// @param theta input angle from 0-65535
110 /// @returns sin of theta, value between -32767 to 32767.
111 LIB8STATIC int16_t sin16_C(uint16_t theta) {
112 static const uint16_t base[] = {0, 6393, 12539, 18204,
113 23170, 27245, 30273, 32137};
114 static const uint8_t slope[] = {49, 48, 44, 38, 31, 23, 14, 4};
115
116 uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047
117 if (theta & 0x4000)
118 offset = 2047 - offset;
119
120 uint8_t section = offset / 256; // 0..7
121 uint16_t b = base[section];
122 uint8_t m = slope[section];
123
124 uint8_t secoffset8 = (uint8_t)(offset) / 2;
125
126 uint16_t mx = m * secoffset8;
127 int16_t y = mx + b;
128
129 if (theta & 0x8000)
130 y = -y;
131
132 return y;
133 }
134
135 #endif
136
137 /// Fast 16-bit approximation of cos(x). This approximation never varies more
138 /// than 0.69% from the floating point value you'd get by doing
139 /// @code{.cpp}
140 /// float s = cos(x) * 32767.0;
141 /// @endcode
142 ///
143 /// @param theta input angle from 0-65535
144 /// @returns cos of theta, value between -32767 to 32767.
145 #ifndef USE_SIN_32
146 LIB8STATIC int16_t cos16(uint16_t theta) { return sin16(theta + 16384); }
147 #endif
148
149 ///////////////////////////////////////////////////////////////////////
150 // sin8() and cos8()
151 // Fast 8-bit approximations of sin(x) & cos(x).
152
153 /// Pre-calculated lookup table used in sin8() and cos8() functions
154 const uint8_t b_m16_interleave[] = {0, 49, 49, 41, 90, 27, 117, 10};
155
156 #if defined(__AVR__) && !defined(LIB8_ATTINY)
157 /// Platform-independent alias of the fast sin implementation
158 #define sin8 sin8_avr
159
160 /// Fast 8-bit approximation of sin(x). This approximation never varies more
161 /// than 2% from the floating point value you'd get by doing
162 /// @code{.cpp}
163 /// float s = (sin(x) * 128.0) + 128;
164 /// @endcode
165 ///
166 /// @param theta input angle from 0-255
167 /// @returns sin of theta, value between 0 and 255
168 LIB8STATIC uint8_t sin8_avr(uint8_t theta) {
169 uint8_t offset = theta;
170
171 asm volatile("sbrc %[theta],6 \n\t"
172 "com %[offset] \n\t"
173 : [theta] "+r"(theta), [offset] "+r"(offset));
174
175 offset &= 0x3F; // 0..63
176
177 uint8_t secoffset = offset & 0x0F; // 0..15
178 if (theta & 0x40)
179 ++secoffset;
180
181 uint8_t m16;
182 uint8_t b;
183
184 uint8_t section = offset >> 4; // 0..3
185 uint8_t s2 = section * 2;
186
187 const uint8_t *p = b_m16_interleave;
188 p += s2;
189 b = *p;
190 ++p;
191 m16 = *p;
192
193 uint8_t mx;
194 uint8_t xr1;
195 asm volatile("mul %[m16],%[secoffset] \n\t"
196 "mov %[mx],r0 \n\t"
197 "mov %[xr1],r1 \n\t"
198 "eor r1, r1 \n\t"
199 "swap %[mx] \n\t"
200 "andi %[mx],0x0F \n\t"
201 "swap %[xr1] \n\t"
202 "andi %[xr1], 0xF0 \n\t"
203 "or %[mx], %[xr1] \n\t"
204 : [mx] "=d"(mx), [xr1] "=d"(xr1)
205 : [m16] "d"(m16), [secoffset] "d"(secoffset));
206
207 int8_t y = mx + b;
208 if (theta & 0x80)
209 y = -y;
210
211 y += 128;
212
213 return y;
214 }
215
216 #else
217
218 /// Platform-independent alias of the fast sin implementation
219 #define sin8 sin8_C
220
221 /// Fast 8-bit approximation of sin(x). This approximation never varies more
222 /// than 2% from the floating point value you'd get by doing
223 /// @code{.cpp}
224 /// float s = (sin(x) * 128.0) + 128;
225 /// @endcode
226 ///
227 /// @param theta input angle from 0-255
228 /// @returns sin of theta, value between 0 and 255
229 14 LIB8STATIC uint8_t sin8_C(uint8_t theta) {
230 14 uint8_t offset = theta;
231
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (theta & 0x40) {
232 14 offset = (uint8_t)255 - offset;
233 }
234 14 offset &= 0x3F; // 0..63
235
236 14 uint8_t secoffset = offset & 0x0F; // 0..15
237
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (theta & 0x40)
238 14 ++secoffset;
239
240 14 uint8_t section = offset >> 4; // 0..3
241 14 uint8_t s2 = section * 2;
242 14 const uint8_t *p = b_m16_interleave;
243 14 p += s2;
244 14 uint8_t b = *p;
245 14 ++p;
246 14 uint8_t m16 = *p;
247
248 14 uint8_t mx = (m16 * secoffset) >> 4;
249
250 14 int8_t y = mx + b;
251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (theta & 0x80)
252 ✗ y = -y;
253
254 14 y += 128;
255
256 14 return y;
257 }
258
259 #endif
260
261 /// Fast 8-bit approximation of cos(x). This approximation never varies more
262 /// than 2% from the floating point value you'd get by doing
263 /// @code{.cpp}
264 /// float s = (cos(x) * 128.0) + 128;
265 /// @endcode
266 ///
267 /// @param theta input angle from 0-255
268 /// @returns cos of theta, value between 0 and 255
269 14 LIB8STATIC uint8_t cos8(uint8_t theta) { return sin8(theta + 64); }
270
271 /// @} Trig
272 /// @} lib8tion
273
274 #endif
275