You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

284 lines
7.5 KiB

  1. #ifndef __INC_LIB8TION_TRIG_H
  2. #define __INC_LIB8TION_TRIG_H
  3. ///@ingroup lib8tion
  4. ///@defgroup Trig Fast trig functions
  5. /// Fast 8 and 16-bit approximations of sin(x) and cos(x).
  6. /// Don't use these approximations for calculating the
  7. /// trajectory of a rocket to Mars, but they're great
  8. /// for art projects and LED displays.
  9. ///
  10. /// On Arduino/AVR, the 16-bit approximation is more than
  11. /// 10X faster than floating point sin(x) and cos(x), while
  12. /// the 8-bit approximation is more than 20X faster.
  13. ///@{
  14. #if defined(__AVR__)
  15. #define sin16 sin16_avr
  16. #else
  17. #define sin16 sin16_C
  18. #endif
  19. /// Fast 16-bit approximation of sin(x). This approximation never varies more than
  20. /// 0.69% from the floating point value you'd get by doing
  21. ///
  22. /// float s = sin(x) * 32767.0;
  23. ///
  24. /// @param theta input angle from 0-65535
  25. /// @returns sin of theta, value between -32767 to 32767.
  26. LIB8STATIC int16_t sin16_avr( uint16_t theta )
  27. {
  28. static const uint8_t data[] =
  29. { 0, 0, 49, 0, 6393%256, 6393/256, 48, 0,
  30. 12539%256, 12539/256, 44, 0, 18204%256, 18204/256, 38, 0,
  31. 23170%256, 23170/256, 31, 0, 27245%256, 27245/256, 23, 0,
  32. 30273%256, 30273/256, 14, 0, 32137%256, 32137/256, 4 /*,0*/ };
  33. uint16_t offset = (theta & 0x3FFF);
  34. // AVR doesn't have a multi-bit shift instruction,
  35. // so if we say "offset >>= 3", gcc makes a tiny loop.
  36. // Inserting empty volatile statements between each
  37. // bit shift forces gcc to unroll the loop.
  38. offset >>= 1; // 0..8191
  39. asm volatile("");
  40. offset >>= 1; // 0..4095
  41. asm volatile("");
  42. offset >>= 1; // 0..2047
  43. if( theta & 0x4000 ) offset = 2047 - offset;
  44. uint8_t sectionX4;
  45. sectionX4 = offset / 256;
  46. sectionX4 *= 4;
  47. uint8_t m;
  48. union {
  49. uint16_t b;
  50. struct {
  51. uint8_t blo;
  52. uint8_t bhi;
  53. };
  54. } u;
  55. //in effect u.b = blo + (256 * bhi);
  56. u.blo = data[ sectionX4 ];
  57. u.bhi = data[ sectionX4 + 1];
  58. m = data[ sectionX4 + 2];
  59. uint8_t secoffset8 = (uint8_t)(offset) / 2;
  60. uint16_t mx = m * secoffset8;
  61. int16_t y = mx + u.b;
  62. if( theta & 0x8000 ) y = -y;
  63. return y;
  64. }
  65. /// Fast 16-bit approximation of sin(x). This approximation never varies more than
  66. /// 0.69% from the floating point value you'd get by doing
  67. ///
  68. /// float s = sin(x) * 32767.0;
  69. ///
  70. /// @param theta input angle from 0-65535
  71. /// @returns sin of theta, value between -32767 to 32767.
  72. LIB8STATIC int16_t sin16_C( uint16_t theta )
  73. {
  74. static const uint16_t base[] =
  75. { 0, 6393, 12539, 18204, 23170, 27245, 30273, 32137 };
  76. static const uint8_t slope[] =
  77. { 49, 48, 44, 38, 31, 23, 14, 4 };
  78. uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047
  79. if( theta & 0x4000 ) offset = 2047 - offset;
  80. uint8_t section = offset / 256; // 0..7
  81. uint16_t b = base[section];
  82. uint8_t m = slope[section];
  83. uint8_t secoffset8 = (uint8_t)(offset) / 2;
  84. uint16_t mx = m * secoffset8;
  85. int16_t y = mx + b;
  86. if( theta & 0x8000 ) y = -y;
  87. return y;
  88. }
  89. /// Fast 16-bit approximation of cos(x). This approximation never varies more than
  90. /// 0.69% from the floating point value you'd get by doing
  91. ///
  92. /// float s = cos(x) * 32767.0;
  93. ///
  94. /// @param theta input angle from 0-65535
  95. /// @returns sin of theta, value between -32767 to 32767.
  96. LIB8STATIC int16_t cos16( uint16_t theta)
  97. {
  98. return sin16( theta + 16384);
  99. }
  100. ///////////////////////////////////////////////////////////////////////
  101. // sin8 & cos8
  102. // Fast 8-bit approximations of sin(x) & cos(x).
  103. // Input angle is an unsigned int from 0-255.
  104. // Output is an unsigned int from 0 to 255.
  105. //
  106. // This approximation can vary to to 2%
  107. // from the floating point value you'd get by doing
  108. // float s = (sin( x ) * 128.0) + 128;
  109. //
  110. // Don't use this approximation for calculating the
  111. // "real" trigonometric calculations, but it's great
  112. // for art projects and LED displays.
  113. //
  114. // On Arduino/AVR, this approximation is more than
  115. // 20X faster than floating point sin(x) and cos(x)
  116. #if defined(__AVR__) && !defined(LIB8_ATTINY)
  117. #define sin8 sin8_avr
  118. #else
  119. #define sin8 sin8_C
  120. #endif
  121. static const uint8_t b_m16_interleave[8] = { 0, 49, 49, 41, 90, 27, 117, 10 };
  122. /// Fast 8-bit approximation of sin(x). This approximation never varies more than
  123. /// 2% from the floating point value you'd get by doing
  124. ///
  125. /// float s = (sin(x) * 128.0) + 128;
  126. ///
  127. /// @param theta input angle from 0-255
  128. /// @returns sin of theta, value between 0 and 255
  129. LIB8STATIC uint8_t sin8_avr( uint8_t theta)
  130. {
  131. uint8_t offset = theta;
  132. asm volatile(
  133. "sbrc %[theta],6 \n\t"
  134. "com %[offset] \n\t"
  135. : [theta] "+r" (theta), [offset] "+r" (offset)
  136. );
  137. offset &= 0x3F; // 0..63
  138. uint8_t secoffset = offset & 0x0F; // 0..15
  139. if( theta & 0x40) secoffset++;
  140. uint8_t m16; uint8_t b;
  141. uint8_t section = offset >> 4; // 0..3
  142. uint8_t s2 = section * 2;
  143. const uint8_t* p = b_m16_interleave;
  144. p += s2;
  145. b = *p;
  146. p++;
  147. m16 = *p;
  148. uint8_t mx;
  149. uint8_t xr1;
  150. asm volatile(
  151. "mul %[m16],%[secoffset] \n\t"
  152. "mov %[mx],r0 \n\t"
  153. "mov %[xr1],r1 \n\t"
  154. "eor r1, r1 \n\t"
  155. "swap %[mx] \n\t"
  156. "andi %[mx],0x0F \n\t"
  157. "swap %[xr1] \n\t"
  158. "andi %[xr1], 0xF0 \n\t"
  159. "or %[mx], %[xr1] \n\t"
  160. : [mx] "=d" (mx), [xr1] "=d" (xr1)
  161. : [m16] "d" (m16), [secoffset] "d" (secoffset)
  162. );
  163. int8_t y = mx + b;
  164. if( theta & 0x80 ) y = -y;
  165. y += 128;
  166. return y;
  167. }
  168. /// Fast 8-bit approximation of sin(x). This approximation never varies more than
  169. /// 2% from the floating point value you'd get by doing
  170. ///
  171. /// float s = (sin(x) * 128.0) + 128;
  172. ///
  173. /// @param theta input angle from 0-255
  174. /// @returns sin of theta, value between 0 and 255
  175. LIB8STATIC uint8_t sin8_C( uint8_t theta)
  176. {
  177. uint8_t offset = theta;
  178. if( theta & 0x40 ) {
  179. offset = (uint8_t)255 - offset;
  180. }
  181. offset &= 0x3F; // 0..63
  182. uint8_t secoffset = offset & 0x0F; // 0..15
  183. if( theta & 0x40) secoffset++;
  184. uint8_t section = offset >> 4; // 0..3
  185. uint8_t s2 = section * 2;
  186. const uint8_t* p = b_m16_interleave;
  187. p += s2;
  188. uint8_t b = *p;
  189. p++;
  190. uint8_t m16 = *p;
  191. uint8_t mx = (m16 * secoffset) >> 4;
  192. int8_t y = mx + b;
  193. if( theta & 0x80 ) y = -y;
  194. y += 128;
  195. return y;
  196. }
  197. /// Fast 8-bit approximation of cos(x). This approximation never varies more than
  198. /// 2% from the floating point value you'd get by doing
  199. ///
  200. /// float s = (cos(x) * 128.0) + 128;
  201. ///
  202. /// @param theta input angle from 0-255
  203. /// @returns sin of theta, value between 0 and 255
  204. LIB8STATIC uint8_t cos8( uint8_t theta)
  205. {
  206. return sin8( theta + 64);
  207. }
  208. /// Fast 16-bit approximation of atan2(x).
  209. /// @returns atan2, value between 0 and 255
  210. LIB8STATIC uint8_t atan2_8(int16_t dy, int16_t dx)
  211. {
  212. if (dy == 0)
  213. {
  214. if (dx >= 0)
  215. return 0;
  216. else
  217. return 128;
  218. }
  219. int16_t abs_y = dy > 0 ? dy : -dy;
  220. int8_t a;
  221. if (dx >= 0)
  222. a = 32 - (32 * (dx - abs_y) / (dx + abs_y));
  223. else
  224. a = 96 - (32 * (dx + abs_y) / (abs_y - dx));
  225. if (dy < 0)
  226. return -a; // negate if in quad III or IV
  227. return a;
  228. }
  229. ///@}
  230. #endif