1 /*
2 * Copyright (c) 2021 Marti Riba Pons
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdio.h>
8 #include <math.h>
9 #include <errno.h>
10 #include <zsl/orientation/fusion/fusion.h>
11 #include <zsl/orientation/fusion/madgwick.h>
12
13 static uint32_t zsl_fus_madg_freq;
14
zsl_fus_madgwick_imu(struct zsl_vec * g,struct zsl_vec * a,zsl_real_t * beta,zsl_real_t * incl,struct zsl_quat * q)15 static int zsl_fus_madgwick_imu(struct zsl_vec *g, struct zsl_vec *a,
16 zsl_real_t *beta, zsl_real_t *incl, struct zsl_quat *q)
17 {
18 int rc = 0;
19
20 #if CONFIG_ZSL_BOUNDS_CHECKS
21 /* Make sure that the input vectors are tridimensional. */
22 if ((a != NULL && (a->sz != 3)) || g->sz != 3) {
23 rc = -EINVAL;
24 goto err;
25 }
26 /* Make sure that the input quaternion is not zero. */
27 if (ZSL_ABS(zsl_quat_magn(q)) < 1E-6) {
28 rc = -EINVAL;
29 goto err;
30 }
31 #endif
32
33 /* Convert the input quaternion to a unit quaternion. */
34 zsl_quat_to_unit_d(q);
35
36 /* Declare the gradient vector and set it to zero. It will only be
37 * modified if the acceleration vector is valid. */
38 ZSL_VECTOR_DEF(grad, 4);
39 zsl_vec_init(&grad);
40
41 /* Continue with the calculations only if the data from the accelerometer
42 * is valid (non zero). */
43 if ((a != NULL) && ZSL_ABS(zsl_vec_norm(a)) > 1E-6) {
44
45 /* Normalize the acceleration vector. */
46 zsl_vec_to_unit(a);
47
48 /* Define the normalized quaternion of acceleration on the earth's
49 * reference frame, which only has a vertical component. */
50 struct zsl_quat qa = {
51 .r = 0.0,
52 .i = 0.0,
53 .j = 0.0,
54 .k = 1.0
55 };
56
57 /* Calculate the function f by using the input quaternion to rotate the
58 * normalised acceleration vector in the earth's reference frame, and
59 * then substracting the acceleration vector. */
60 ZSL_MATRIX_DEF(f, 3, 1);
61 struct zsl_quat qaq;
62 zsl_quat_rot(q, &qa, &qaq);
63 f.data[0] = qaq.i - a->data[0];
64 f.data[1] = qaq.j - a->data[1];
65 f.data[2] = qaq.k - a->data[2];
66
67 /* Define and compute the transpose of the Jacobian matrix of the
68 * function f. */
69 ZSL_MATRIX_DEF(jt, 4, 3);
70 zsl_real_t jt_data[12] = {
71 -2.0 * q->j, 2.0 * q->i, 0.0,
72 2.0 * q->k, 2.0 * q->r, -4.0 * q->i,
73 -2.0 * q->r, 2.0 * q->k, -4.0 * q->j,
74 2.0 * q->i, 2.0 * q->j, 0.0
75 };
76
77 zsl_mtx_from_arr(&jt, jt_data);
78
79 /* Calculate the gradient of f by multiplying the transposed Jacobian
80 * matrix by the function itself. */
81 ZSL_MATRIX_DEF(jtf, 4, 1);
82 zsl_mtx_mult(&jt, &f, &jtf);
83
84 /* Normalize the gradient vector. */
85 zsl_vec_from_arr(&grad, jtf.data);
86 zsl_vec_to_unit(&grad);
87 }
88
89 /* Update the input quaternion with a modified quaternion integration. */
90 zsl_quat_from_ang_vel(g, q, 1.0 / zsl_fus_madg_freq, q);
91 q->r -= (1.0 / zsl_fus_madg_freq) * (*beta * grad.data[0]);
92 q->i -= (1.0 / zsl_fus_madg_freq) * (*beta * grad.data[1]);
93 q->j -= (1.0 / zsl_fus_madg_freq) * (*beta * grad.data[2]);
94 q->k -= (1.0 / zsl_fus_madg_freq) * (*beta * grad.data[3]);
95
96 /* Normalize the output quaternion. */
97 zsl_quat_to_unit_d(q);
98
99 err:
100 return rc;
101 }
102
103 /**
104 * @brief
105 *
106 * @param g
107 * @param a
108 * @param m
109 * @param beta
110 * @param q
111 *
112 * @return int
113 */
zsl_fus_madgwick(struct zsl_vec * g,struct zsl_vec * a,struct zsl_vec * m,zsl_real_t * beta,zsl_real_t * incl,struct zsl_quat * q)114 static int zsl_fus_madgwick(struct zsl_vec *g, struct zsl_vec *a,
115 struct zsl_vec *m, zsl_real_t *beta, zsl_real_t *incl,
116 struct zsl_quat *q)
117 {
118 int rc = 0;
119
120 #if CONFIG_ZSL_BOUNDS_CHECKS
121 /* Make sure that the input vectors are tridimensional. */
122 if ((a != NULL && (a->sz != 3)) || (m != NULL && (m->sz != 3)) ||
123 g->sz != 3) {
124 rc = -EINVAL;
125 goto err;
126 }
127 /* Make sure that the input quaternion is not zero. */
128 if (ZSL_ABS(zsl_quat_magn(q)) < 1E-6) {
129 rc = -EINVAL;
130 goto err;
131 }
132 #endif
133
134 /* Use IMU algorithm if the magnetometer measurement is invalid. */
135 if ((m == NULL) || (ZSL_ABS(zsl_vec_norm(m)) < 1E-6)) {
136 return zsl_fus_madgwick_imu(g, a, beta, incl, q);
137 }
138
139 /* Convert the input quaternion to a unit quaternion. */
140 zsl_quat_to_unit_d(q);
141
142 /* Declare the gradient vector and set it to zero. It will only be
143 * modified if the acceleration vector is valid. */
144 ZSL_VECTOR_DEF(grad, 4);
145 zsl_vec_init(&grad);
146
147 /* Continue with the calculations only if the data from the accelerometer
148 * is valid (non zero). */
149 if ((a != NULL) && ZSL_ABS(zsl_vec_norm(a)) > 1E-6) {
150
151 /* Normalize the acceleration vector. */
152 zsl_vec_to_unit(a);
153
154 /* Normalize the magnetic field vector. */
155 zsl_vec_to_unit(m);
156
157 /* Define the normalized vector of acceleration on the earth's
158 * reference frame, which only has a vertical component. */
159 struct zsl_quat qa = {
160 .r = 0.0,
161 .i = 0.0,
162 .j = 0.0,
163 .k = 1.0
164 };
165
166 /* Calculate the function f_g by using the input quaternion to rotate
167 * the normalised acceleration quaternion in the earth's reference
168 * frame 'qa', and then substracting the acceleration vector. */
169 ZSL_MATRIX_DEF(f_g, 3, 1);
170 struct zsl_quat qaq;
171 zsl_quat_rot(q, &qa, &qaq);
172 f_g.data[0] = qaq.i - a->data[0];
173 f_g.data[1] = qaq.j - a->data[1];
174 f_g.data[2] = qaq.k - a->data[2];
175
176 /* Define and compute the transpose of the Jacobian matrix of the
177 * function f_g. */
178 ZSL_MATRIX_DEF(jt_g, 4, 3);
179 zsl_real_t jt_g_data[12] = {
180 -2.0 * q->j, 2.0 * q->i, 0.0,
181 2.0 * q->k, 2.0 * q->r, -4.0 * q->i,
182 -2.0 * q->r, 2.0 * q->k, -4.0 * q->j,
183 2.0 * q->i, 2.0 * q->j, 0.0
184 };
185
186 zsl_mtx_from_arr(&jt_g, jt_g_data);
187
188 /* Calculate the gradient of f_g by multiplying the transposed Jacobian
189 * matrix by the function itself. */
190 ZSL_MATRIX_DEF(jtf_g, 4, 1);
191 zsl_mtx_mult(&jt_g, &f_g, &jtf_g);
192
193 /* Turn the data of the magnetometer into a pure quaterion. */
194 struct zsl_quat qm = {
195 .r = 0.0,
196 .i = m->data[0],
197 .j = m->data[1],
198 .k = m->data[2]
199 };
200
201 /* Define the normalized quaternion 'b' of magnetic field on the
202 * earth's reference frame, which only has a x (north) and z (vertical)
203 * components. If the declination angle is known, use it to calculate
204 * this vector. */
205 zsl_real_t bx;
206 zsl_real_t bz;
207
208 if (incl == NULL) {
209 struct zsl_quat h;
210 zsl_quat_rot(q, &qm, &h);
211 bx = ZSL_SQRT(h.i * h.i + h.j * h.j);
212 bz = h.k;
213 } else {
214 bx = ZSL_COS(*incl * ZSL_PI / 180.0);
215 bz = ZSL_SIN(*incl * ZSL_PI / 180.0);
216 }
217
218 struct zsl_quat b = { .r = 0.0, .i = bx, .j = 0.0, .k = bz };
219
220 /* Calculate the function f_b by using the input quaternion to rotate
221 * the normalised magnetic field quaternion in the earth's reference
222 * frame 'b', and then substracting the magnetometer data vector. */
223 ZSL_MATRIX_DEF(f_b, 3, 1);
224 struct zsl_quat qbq;
225 zsl_quat_rot(q, &b, &qbq);
226 f_b.data[0] = qbq.i - m->data[0];
227 f_b.data[1] = qbq.j - m->data[1];
228 f_b.data[2] = qbq.k - m->data[2];
229
230 /* Define and compute the transpose of the Jacobian matrix of the
231 * function f_b. */
232 ZSL_MATRIX_DEF(jt_b, 4, 3);
233 zsl_real_t jt_b_data[12] = {
234 /* 1x1..1x3 */
235 -2.0 * bz * q->j,
236 -2.0 * bx * q->k + 2.0 * bz * q->i,
237 2.0 * bx * q->j,
238 /* 2x1..2x3 */
239 2.0 * bz * q->k,
240 2.0 * bz * q->j + 2.0 * bz * q->r,
241 2.0 * bx * q->k - 4.0 * bz * q->i,
242 /* 3x1..3x3 */
243 -4.0 * bx * q->j - 2.0 * bz * q->r,
244 2.0 * bx * q->i - 2.0 * bz * q->k,
245 2.0 * bx * q->r - 2.0 * bz * q->j,
246 /* 4x1..4x3 */
247 -4.0 * bx * q->k + 2.0 * bz * q->i,
248 -2.0 * bx * q->r + 2.0 * bz * q->j,
249 2.0 * bx * q->i
250 };
251
252 zsl_mtx_from_arr(&jt_b, jt_b_data);
253
254 /* Calculate the gradient of f_b by multiplying the transposed Jacobian
255 * matrix by the function itself. */
256 ZSL_MATRIX_DEF(jtf_b, 4, 1);
257 zsl_mtx_mult(&jt_b, &f_b, &jtf_b);
258
259 /* Compute the total gradient vector by adding the gradient of f_g and
260 * total gradient of f_b. Then normalize the total gradient vector. */
261 grad.data[0] = jtf_g.data[0] + jtf_b.data[0];
262 grad.data[1] = jtf_g.data[1] + jtf_b.data[1];
263 grad.data[2] = jtf_g.data[2] + jtf_b.data[2];
264 grad.data[3] = jtf_g.data[3] + jtf_b.data[3];
265
266 zsl_vec_to_unit(&grad);
267 }
268
269 /* Update the input quaternion with a modified quaternion integration. */
270 zsl_quat_from_ang_vel(g, q, 1.0 / zsl_fus_madg_freq, q);
271 q->r -= (1.0 / zsl_fus_madg_freq) * (*beta * grad.data[0]);
272 q->i -= (1.0 / zsl_fus_madg_freq) * (*beta * grad.data[1]);
273 q->j -= (1.0 / zsl_fus_madg_freq) * (*beta * grad.data[2]);
274 q->k -= (1.0 / zsl_fus_madg_freq) * (*beta * grad.data[3]);
275
276 /* Normalize the output quaternion. */
277 zsl_quat_to_unit_d(q);
278
279 err:
280 return rc;
281 }
282
zsl_fus_madg_init(uint32_t freq,void * cfg)283 int zsl_fus_madg_init(uint32_t freq, void *cfg)
284 {
285 int rc = 0;
286
287 struct zsl_fus_madg_cfg *mcfg = cfg;
288
289 (void)mcfg;
290
291 #if CONFIG_ZSL_BOUNDS_CHECKS
292 /* Make sure that the sample frequency is positive. */
293 if (freq <= 0) {
294 rc = -EINVAL;
295 goto err;
296 }
297 #endif
298
299 zsl_fus_madg_freq = freq;
300
301 err:
302 return rc;
303 }
304
zsl_fus_madg_feed(struct zsl_vec * a,struct zsl_vec * m,struct zsl_vec * g,zsl_real_t * incl,struct zsl_quat * q,void * cfg)305 int zsl_fus_madg_feed(struct zsl_vec *a, struct zsl_vec *m, struct zsl_vec *g,
306 zsl_real_t *incl, struct zsl_quat *q, void *cfg)
307 {
308 struct zsl_fus_madg_cfg *mcfg = cfg;
309
310 if (mcfg->beta < 0.0) {
311 return -EINVAL;
312 }
313
314 return zsl_fus_madgwick(g, a, m, &(mcfg->beta), incl, q);
315 }
316
zsl_fus_madg_error(int error)317 void zsl_fus_madg_error(int error)
318 {
319 /* ToDo: Log error in default handler. */
320 }
321