/* * dynamics.c * * This file is part of WiiC, written by: * Gabriele Randelli * Email: randelli@dis.uniroma1.it * * Copyright 2010 * * This file is based on Wiiuse, written By: * Michael Laforest < para > * Email: < thepara (--AT--) g m a i l [--DOT--] com > * * Copyright 2006-2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * $Header$ */ /** * @file * @brief Handles the dynamics of the wiimote. * * The file includes functions that handle the dynamics * of the wiimote. Such dynamics include orientation and * motion sensing. */ #include #include #include #include "definitions.h" #include "wiic_internal.h" #include "ir.h" #include "dynamics.h" /** * @brief Calculate the roll, pitch, yaw. * * @param ac An accelerometer (accel_t) structure. * @param accel [in] Pointer to a vec3b_t structure that holds the raw acceleration data. * @param orient [out] Pointer to a orient_t structure that will hold the orientation data. * @param rorient [out] Pointer to a orient_t structure that will hold the non-smoothed orientation data. * @param smooth If smoothing should be performed on the angles calculated. 1 to enable, 0 to disable. * * Given the raw acceleration data from the accelerometer struct, calculate * the orientation of the device and set it in the \a orient parameter. */ void calculate_orientation(struct vec3f_t* in, struct ang3f_t* out) { float x, y, z; /* * roll - use atan(z / x) [ ranges from -180 to 180 ] * pitch - use atan(z / y) [ ranges from -180 to 180 ] * yaw - impossible to tell without IR */ /* yaw - set to 0, IR will take care of it if it's enabled */ out->yaw = 0.0f; /* find out how much it actually moved and normalize to +/- 1g */ x = in->x; y = in->y; z = in->z; /* make sure x,y,z are between -1 and 1 for the tan functions */ if (x < -1.0f) x = -1.0f; else if (x > 1.0f) x = 1.0f; if (y < -1.0f) y = -1.0f; else if (y > 1.0f) y = 1.0f; if (z < -1.0f) z = -1.0f; else if (z > 1.0f) z = 1.0f; /* if it is over 1g then it is probably accelerating and the gravity vector cannot be identified */ if (abs(in->x) <= 1.0) { /* roll */ x = -RAD_TO_DEGREE(atan2f(x, z)); out->roll = x; } if (abs(in->y) <= 1.0) { /* pitch */ y = RAD_TO_DEGREE(atan2f(y, z)); out->pitch = y; } } /** * @brief Calculate the gravity forces on each axis. * * @param ac An accelerometer (accel_t) structure. * @param accel [in] Pointer to a vec3b_t structure that holds the raw acceleration data. * @param gforce [out] Pointer to a gforce_t structure that will hold the gravity force data. */ void calculate_gforce(struct accel_t* ac, struct vec3b_t* accel, struct gforce_t* gforce, int smooth) { float xg, yg, zg; /* find out how much it has to move to be 1g */ xg = (int)ac->cal_g.x; yg = (int)ac->cal_g.y; zg = (int)ac->cal_g.z; /* find out how much it actually moved and normalize to +/- 1g */ gforce->a_vec.x = ((int)accel->x - (int)ac->cal_zero.x) / xg; gforce->a_vec.y = ((int)accel->y - (int)ac->cal_zero.y) / yg; gforce->a_vec.z = ((int)accel->z - (int)ac->cal_zero.z) / zg; if(smooth) { apply_smoothing(gforce, ac->st_alpha); } else { gforce->vec.x = gforce->a_vec.x; gforce->vec.y = gforce->a_vec.y; gforce->vec.z = gforce->a_vec.z; } } /** * @brief Calculate the angle and magnitude of a joystick. * * @param js [out] Pointer to a joystick_t structure. * @param x The raw x-axis value. * @param y The raw y-axis value. */ void calc_joystick_state(struct joystick_t* js, float x, float y) { float rx, ry, ang; /* * Since the joystick center may not be exactly: * (min + max) / 2 * Then the range from the min to the center and the center to the max * may be different. * Because of this, depending on if the current x or y value is greater * or less than the assoicated axis center value, it needs to be interpolated * between the center and the minimum or maxmimum rather than between * the minimum and maximum. * * So we have something like this: * (x min) [-1] ---------*------ [0] (x center) [0] -------- [1] (x max) * Where the * is the current x value. * The range is therefore -1 to 1, 0 being the exact center rather than * the middle of min and max. */ if (x == js->center.x) rx = 0; else if (x >= js->center.x) rx = ((float)(x - js->center.x) / (float)(js->max.x - js->center.x)); else rx = ((float)(x - js->min.x) / (float)(js->center.x - js->min.x)) - 1.0f; if (y == js->center.y) ry = 0; else if (y >= js->center.y) ry = ((float)(y - js->center.y) / (float)(js->max.y - js->center.y)); else ry = ((float)(y - js->min.y) / (float)(js->center.y - js->min.y)) - 1.0f; /* calculate the joystick angle and magnitude */ ang = RAD_TO_DEGREE(atanf(ry / rx)); ang -= 90.0f; if (rx < 0.0f) ang -= 180.0f; js->ang = absf(ang); js->mag = (float) sqrt((rx * rx) + (ry * ry)); } /** * * @brief Apply a smooth factor to accelerometer angles. * * @param accel Last acceleration measured and normalized to +/-g. * @param gforce [out] gravity vector after smoothing */ void apply_smoothing(struct gforce_t* gforce, float alpha) { gforce->vec.x = alpha*gforce->vec.x + (1.0-alpha)*gforce->a_vec.x; gforce->vec.y = alpha*gforce->vec.y + (1.0-alpha)*gforce->a_vec.y; gforce->vec.z = alpha*gforce->vec.z + (1.0-alpha)*gforce->a_vec.z; }