dynamics.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /*
  2. * wiiuse
  3. *
  4. * Written By:
  5. * Michael Laforest < para >
  6. * Email: < thepara (--AT--) g m a i l [--DOT--] com >
  7. *
  8. * Copyright 2006-2007
  9. *
  10. * This file is part of wiiuse.
  11. *
  12. * This program is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License as published by
  14. * the Free Software Foundation; either version 3 of the License, or
  15. * (at your option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License
  23. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  24. *
  25. * $Header$
  26. *
  27. */
  28. /**
  29. * @file
  30. * @brief Handles the dynamics of the wiimote.
  31. *
  32. * The file includes functions that handle the dynamics
  33. * of the wiimote. Such dynamics include orientation and
  34. * motion sensing.
  35. */
  36. #include "dynamics.h"
  37. #include <math.h> /* for atan2f, atanf, sqrt */
  38. #include <stdlib.h> /* for abs */
  39. /**
  40. * @brief Calculate the roll, pitch, yaw.
  41. *
  42. * @param ac An accelerometer (accel_t) structure.
  43. * @param accel [in] Pointer to a vec3b_t structure that holds the raw acceleration data.
  44. * @param orient [out] Pointer to a orient_t structure that will hold the orientation data.
  45. * @param rorient [out] Pointer to a orient_t structure that will hold the non-smoothed orientation data.
  46. * @param smooth If smoothing should be performed on the angles calculated. 1 to enable, 0 to disable.
  47. *
  48. * Given the raw acceleration data from the accelerometer struct, calculate
  49. * the orientation of the device and set it in the \a orient parameter.
  50. */
  51. void calculate_orientation(struct accel_t* ac, struct vec3b_t* accel, struct orient_t* orient, int smooth) {
  52. float xg, yg, zg;
  53. float x, y, z;
  54. /*
  55. * roll - use atan(z / x) [ ranges from -180 to 180 ]
  56. * pitch - use atan(z / y) [ ranges from -180 to 180 ]
  57. * yaw - impossible to tell without IR
  58. */
  59. /* yaw - set to 0, IR will take care of it if it's enabled */
  60. orient->yaw = 0.0f;
  61. /* find out how much it has to move to be 1g */
  62. xg = (float)ac->cal_g.x;
  63. yg = (float)ac->cal_g.y;
  64. zg = (float)ac->cal_g.z;
  65. /* find out how much it actually moved and normalize to +/- 1g */
  66. x = ((float)accel->x - (float)ac->cal_zero.x) / xg;
  67. y = ((float)accel->y - (float)ac->cal_zero.y) / yg;
  68. z = ((float)accel->z - (float)ac->cal_zero.z) / zg;
  69. /* make sure x,y,z are between -1 and 1 for the tan functions */
  70. if (x < -1.0f) {
  71. x = -1.0f;
  72. } else if (x > 1.0f) {
  73. x = 1.0f;
  74. }
  75. if (y < -1.0f) {
  76. y = -1.0f;
  77. } else if (y > 1.0f) {
  78. y = 1.0f;
  79. }
  80. if (z < -1.0f) {
  81. z = -1.0f;
  82. } else if (z > 1.0f) {
  83. z = 1.0f;
  84. }
  85. /* if it is over 1g then it is probably accelerating and not reliable */
  86. if (abs(accel->x - ac->cal_zero.x) <= ac->cal_g.x) {
  87. /* roll */
  88. x = RAD_TO_DEGREE(atan2f(x, z));
  89. orient->roll = x;
  90. orient->a_roll = x;
  91. }
  92. if (abs(accel->y - ac->cal_zero.y) <= ac->cal_g.y) {
  93. /* pitch */
  94. y = RAD_TO_DEGREE(atan2f(y, z));
  95. orient->pitch = y;
  96. orient->a_pitch = y;
  97. }
  98. /* smooth the angles if enabled */
  99. if (smooth) {
  100. apply_smoothing(ac, orient, SMOOTH_ROLL);
  101. apply_smoothing(ac, orient, SMOOTH_PITCH);
  102. }
  103. }
  104. /**
  105. * @brief Calculate the gravity forces on each axis.
  106. *
  107. * @param ac An accelerometer (accel_t) structure.
  108. * @param accel [in] Pointer to a vec3b_t structure that holds the raw acceleration data.
  109. * @param gforce [out] Pointer to a gforce_t structure that will hold the gravity force data.
  110. */
  111. void calculate_gforce(struct accel_t* ac, struct vec3b_t* accel, struct gforce_t* gforce) {
  112. float xg, yg, zg;
  113. /* find out how much it has to move to be 1g */
  114. xg = (float)ac->cal_g.x;
  115. yg = (float)ac->cal_g.y;
  116. zg = (float)ac->cal_g.z;
  117. /* find out how much it actually moved and normalize to +/- 1g */
  118. gforce->x = ((float)accel->x - (float)ac->cal_zero.x) / xg;
  119. gforce->y = ((float)accel->y - (float)ac->cal_zero.y) / yg;
  120. gforce->z = ((float)accel->z - (float)ac->cal_zero.z) / zg;
  121. }
  122. static float applyCalibration(float inval, float minval, float maxval, float centerval) {
  123. float ret;
  124. /* We don't use the exact ranges but the ranges + 1 in case we get bad calibration data - avoid div0 */
  125. if (inval == centerval) {
  126. ret = 0;
  127. } else if (inval < centerval) {
  128. ret = (inval - centerval) / (centerval - minval + 1);
  129. } else {
  130. ret = (inval - centerval) / (maxval - centerval + 1);
  131. }
  132. return ret;
  133. }
  134. /**
  135. * @brief Calculate the angle and magnitude of a joystick.
  136. *
  137. * @param js [out] Pointer to a joystick_t structure.
  138. * @param x The raw x-axis value.
  139. * @param y The raw y-axis value.
  140. */
  141. void calc_joystick_state(struct joystick_t* js, float x, float y) {
  142. float rx, ry, ang;
  143. /*
  144. * Since the joystick center may not be exactly:
  145. * (min + max) / 2
  146. * Then the range from the min to the center and the center to the max
  147. * may be different.
  148. * Because of this, depending on if the current x or y value is greater
  149. * or less than the associated axis center value, it needs to be interpolated
  150. * between the center and the minimum or maxmimum rather than between
  151. * the minimum and maximum.
  152. *
  153. * So we have something like this:
  154. * (x min) [-1] ---------*------ [0] (x center) [0] -------- [1] (x max)
  155. * Where the * is the current x value.
  156. * The range is therefore -1 to 1, 0 being the exact center rather than
  157. * the middle of min and max.
  158. */
  159. rx = applyCalibration(x, js->min.x, js->max.x, js->center.x);
  160. ry = applyCalibration(y, js->min.y, js->max.y, js->center.y);
  161. /* calculate the joystick angle and magnitude */
  162. ang = RAD_TO_DEGREE(atan2f(ry, rx));
  163. js->ang = ang + 180.0f;
  164. js->mag = sqrtf((rx * rx) + (ry * ry));
  165. }
  166. void apply_smoothing(struct accel_t* ac, struct orient_t* orient, int type) {
  167. switch (type) {
  168. case SMOOTH_ROLL: {
  169. /* it's possible last iteration was nan or inf, so set it to 0 if that happened */
  170. if (isnan(ac->st_roll) || isinf(ac->st_roll)) {
  171. ac->st_roll = 0.0f;
  172. }
  173. /*
  174. * If the sign changes (which will happen if going from -180 to 180)
  175. * or from (-1 to 1) then don't smooth, just use the new angle.
  176. */
  177. if (((ac->st_roll < 0) && (orient->roll > 0)) || ((ac->st_roll > 0) && (orient->roll < 0))) {
  178. ac->st_roll = orient->roll;
  179. } else {
  180. orient->roll = ac->st_roll + (ac->st_alpha * (orient->a_roll - ac->st_roll));
  181. ac->st_roll = orient->roll;
  182. }
  183. return;
  184. }
  185. case SMOOTH_PITCH: {
  186. if (isnan(ac->st_pitch) || isinf(ac->st_pitch)) {
  187. ac->st_pitch = 0.0f;
  188. }
  189. if (((ac->st_pitch < 0) && (orient->pitch > 0)) || ((ac->st_pitch > 0) && (orient->pitch < 0))) {
  190. ac->st_pitch = orient->pitch;
  191. } else {
  192. orient->pitch = ac->st_pitch + (ac->st_alpha * (orient->a_pitch - ac->st_pitch));
  193. ac->st_pitch = orient->pitch;
  194. }
  195. return;
  196. }
  197. }
  198. }