X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=src%2Fcore%2Fao_quaternion.h;h=044f1607d8b4a8aabba54841aaae7da47ec6434a;hp=f4b8aaa49a40f99ea572521722c523e7055096f7;hb=bb9fdef607728cc326a82aa632e59724f272e53b;hpb=08143a922fe27bc50a19924f46538f9476ab5fd1 diff --git a/src/core/ao_quaternion.h b/src/core/ao_quaternion.h index f4b8aaa4..044f1607 100644 --- a/src/core/ao_quaternion.h +++ b/src/core/ao_quaternion.h @@ -26,11 +26,44 @@ struct ao_quaternion { }; static inline void ao_quaternion_multiply(struct ao_quaternion *r, - struct ao_quaternion *a, - struct ao_quaternion *b) + const struct ao_quaternion *a, + const struct ao_quaternion *b) { struct ao_quaternion t; #define T(_a,_b) (((a)->_a) * ((b)->_b)) + +/* + * Quaternions + * + * ii = jj = kk = ijk = -1; + * + * kji = 1; + * + * ij = k; ji = -k; + * kj = -i; jk = i; + * ik = -j; ki = j; + * + * Multiplication p * q: + * + * (pr + ipx + jpy + kpz) (qr + iqx + jqy + kqz) = + * + * ( pr * qr + pr * iqx + pr * jqy + pr * kqz) + + * (ipx * qr + ipx * iqx + ipx * jqy + ipx * kqz) + + * (jpy * qr + jpy * iqx + jpy * jqy + jpy * kqz) + + * (kpz * qr + kpz * iqx + kpz * jqy + kpz * kqz) = + * + * + * (pr * qr) + i(pr * qx) + j(pr * qy) + k(pr * qz) + + * i(px * qr) - (px * qx) + k(px * qy) - j(px * qz) + + * j(py * qr) - k(py * qx) - (py * qy) + i(py * qz) + + * k(pz * qr) + j(pz * qx) - i(pz * qy) - (pz * qz) = + * + * 1 * ( (pr * qr) - (px * qx) - (py * qy) - (pz * qz) ) + + * i * ( (pr * qx) + (px * qr) + (py * qz) - (pz * qy) ) + + * j * ( (pr * qy) - (px * qz) + (py * qr) + (pz * qx) ) + + * k * ( (pr * qz) + (px * qy) - (py * qx) + (pz * qr); + */ + t.r = T(r,r) - T(x,x) - T(y,y) - T(z,z); t.x = T(r,x) + T(x,r) + T(y,z) - T(z,y); t.y = T(r,y) - T(x,z) + T(y,r) + T(z,x); @@ -40,7 +73,7 @@ static inline void ao_quaternion_multiply(struct ao_quaternion *r, } static inline void ao_quaternion_conjugate(struct ao_quaternion *r, - struct ao_quaternion *a) + const struct ao_quaternion *a) { r->r = a->r; r->x = -a->x; @@ -48,7 +81,7 @@ static inline void ao_quaternion_conjugate(struct ao_quaternion *r, r->z = -a->z; } -static inline float ao_quaternion_normal(struct ao_quaternion *a) +static inline float ao_quaternion_normal(const struct ao_quaternion *a) { #define S(_a) (((a)->_a) * ((a)->_a)) return S(r) + S(x) + S(y) + S(z); @@ -56,7 +89,7 @@ static inline float ao_quaternion_normal(struct ao_quaternion *a) } static inline void ao_quaternion_scale(struct ao_quaternion *r, - struct ao_quaternion *a, + const struct ao_quaternion *a, float b) { r->r = a->r * b; @@ -66,7 +99,7 @@ static inline void ao_quaternion_scale(struct ao_quaternion *r, } static inline void ao_quaternion_normalize(struct ao_quaternion *r, - struct ao_quaternion *a) + const struct ao_quaternion *a) { float n = ao_quaternion_normal(a); @@ -76,18 +109,78 @@ static inline void ao_quaternion_normalize(struct ao_quaternion *r, *r = *a; } +static inline float ao_quaternion_dot(const struct ao_quaternion *a, + const struct ao_quaternion *b) +{ +#define T(_a) (((a)->_a) * ((b)->_a)) + return T(r) + T(x) + T(y) + T(z); +#undef T +} + + static inline void ao_quaternion_rotate(struct ao_quaternion *r, - struct ao_quaternion *a, - struct ao_quaternion *b) + const struct ao_quaternion *a, + const struct ao_quaternion *b) { struct ao_quaternion c; struct ao_quaternion t; - ao_quaternion_conjugate(&c, b); ao_quaternion_multiply(&t, b, a); + ao_quaternion_conjugate(&c, b); ao_quaternion_multiply(r, &t, &c); } +/* + * Compute a rotation quaternion between two vectors + * + * cos(θ) + u * sin(θ) + * + * where θ is the angle between the two vectors and u + * is a unit vector axis of rotation + */ + +static inline void ao_quaternion_vectors_to_rotation(struct ao_quaternion *r, + const struct ao_quaternion *a, + const struct ao_quaternion *b) +{ + /* + * The cross product will point orthogonally to the two + * vectors, forming our rotation axis. The length will be + * sin(θ), so these values are already multiplied by that. + */ + + float x = a->y * b->z - a->z * b->y; + float y = a->z * b->x - a->x * b->z; + float z = a->x * b->y - a->y * b->x; + + float s_2 = x*x + y*y + z*z; + float s = sqrtf(s_2); + + /* cos(θ) = a · b / (|a| |b|). + * + * a and b are both unit vectors, so the divisor is one + */ + float c = a->x*b->x + a->y*b->y + a->z*b->z; + + float c_half = sqrtf ((1 + c) / 2); + float s_half = sqrtf ((1 - c) / 2); + + /* + * Divide out the sine factor from the + * cross product, then multiply in the + * half sine factor needed for the quaternion + */ + float s_scale = s_half / s; + + r->x = x * s_scale; + r->y = y * s_scale; + r->z = z * s_scale; + + r->r = c_half; + + ao_quaternion_normalize(r, r); +} + static inline void ao_quaternion_init_vector(struct ao_quaternion *r, float x, float y, float z) { @@ -113,4 +206,44 @@ static inline void ao_quaternion_init_zero_rotation(struct ao_quaternion *r) r->x = r->y = r->z = 0; } +/* + * The sincosf from newlib just calls sinf and cosf. This is a bit + * faster, if slightly less precise + */ + +static inline void +ao_sincosf(float a, float *s, float *c) { + float _s = sinf(a); + *s = _s; + *c = sqrtf(1 - _s*_s); +} + +/* + * Initialize a quaternion from 1/2 euler rotation angles (in radians). + * + * Yes, it would be nicer if there were a faster way, but because we + * sample the gyros at only 100Hz, we end up getting angles too large + * to take advantage of sin(x) ≃ x. + * + * We might be able to use just a couple of elements of the sin taylor + * series though, instead of the whole sin function? + */ + +static inline void ao_quaternion_init_half_euler(struct ao_quaternion *r, + float x, float y, float z) +{ + float s_x, c_x; + float s_y, c_y; + float s_z, c_z; + + ao_sincosf(x, &s_x, &c_x); + ao_sincosf(y, &s_y, &c_y); + ao_sincosf(z, &s_z, &c_z); + + r->r = c_x * c_y * c_z + s_x * s_y * s_z; + r->x = s_x * c_y * c_z - c_x * s_y * s_z; + r->y = c_x * s_y * c_z + s_x * c_y * s_z; + r->z = c_x * c_y * s_z - s_x * s_y * c_z; +} + #endif /* _AO_QUATERNION_H_ */