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/kalman.h>
12 
13 static uint32_t zsl_fus_kalm_freq;
14 static uint32_t zsl_fus_kalm_initialised;
15 
zsl_fus_kalman(struct zsl_vec * g,struct zsl_vec * a,struct zsl_vec * m,zsl_real_t * var_g,zsl_real_t * var_a,zsl_real_t * var_m,zsl_real_t * incl,struct zsl_mtx * P,struct zsl_quat * q)16 static int zsl_fus_kalman(struct zsl_vec *g, struct zsl_vec *a,
17 			  struct zsl_vec *m, zsl_real_t *var_g, zsl_real_t *var_a,
18 			  zsl_real_t *var_m, zsl_real_t *incl, struct zsl_mtx *P,
19 			  struct zsl_quat *q)
20 {
21 	int rc = 0;
22 
23 #if CONFIG_ZSL_BOUNDS_CHECKS
24 	if (a == NULL || a->sz != 3 || m == NULL || m->sz != 3 || g == NULL ||
25 	    g->sz != 3) {
26 		rc = -EINVAL;
27 		goto err;
28 	}
29 #endif
30 
31 	/* PREDICTION STEP. */
32 
33 	/* Useful constant to reduce the code. */
34 	zsl_real_t if2 = 1.0 / (2.0 * zsl_fus_kalm_freq);
35 
36 	/* Calculate the matrix F and its transpose. */
37 	ZSL_MATRIX_DEF(F, 4, 4);
38 	ZSL_MATRIX_DEF(Ft, 4, 4);
39 
40 	zsl_real_t F_data[16] = {
41 		1.0, -if2 * g->data[0], -if2 * g->data[1], -if2 * g->data[2],
42 		if2 * g->data[0], 1.0, if2 * g->data[2], -if2 * g->data[1],
43 		if2 * g->data[1], -if2 * g->data[2], 1.0, if2 * g->data[0],
44 		if2 * g->data[2], if2 * g->data[1], -if2 * g->data[0], 1.0,
45 	};
46 
47 	zsl_mtx_from_arr(&F, F_data);
48 	zsl_mtx_trans(&F, &Ft);
49 
50 	/* Calculate the matrix W and its transpose. */
51 	ZSL_MATRIX_DEF(W, 4, 3);
52 	ZSL_MATRIX_DEF(Wt, 3, 4);
53 
54 	zsl_real_t W_data[12] = {
55 		-q->i, -q->j, -q->k,
56 		q->r, -q->k,  q->j,
57 		q->k,  q->r, -q->i,
58 		-q->j,  q->i,  q->r
59 	};
60 
61 	zsl_mtx_from_arr(&W, W_data);
62 	zsl_mtx_trans(&W, &Wt);
63 
64 	/* Calculate the matrix Q. */
65 	ZSL_MATRIX_DEF(Q, 4, 4);
66 	zsl_mtx_mult(&W, &Wt, &Q);
67 	zsl_mtx_scalar_mult_d(&Q, *var_g * if2 * if2);
68 
69 	/* Calculate the matrix P. */
70 	ZSL_MATRIX_DEF(FP, 4, 4);
71 	zsl_mtx_mult(&F, P, &FP);
72 	zsl_mtx_mult(&FP, &Ft, P);
73 	zsl_mtx_add_d(P, &Q);
74 
75 	/* Calculate an estimation of the orientation using only the data of the
76 	 * gyroscope and quaternion integration. */
77 	zsl_quat_from_ang_vel(g, q, 1.0 / zsl_fus_kalm_freq, q);
78 
79 	/* CORRECTION STEP. */
80 
81 	/* Normalize the magnetometer and accelerometer data. */
82 	zsl_vec_to_unit(a);
83 	zsl_vec_to_unit(m);
84 
85 	/* Define the measurement vector 'z'. */
86 	ZSL_MATRIX_DEF(z, 6, 1);
87 	z.data[0] = a->data[0];
88 	z.data[1] = a->data[1];
89 	z.data[2] = a->data[2];
90 	z.data[3] = m->data[0];
91 	z.data[4] = m->data[1];
92 	z.data[5] = m->data[2];
93 
94 	/* Turn the data of the magnetometer into a pure quaterion. */
95 	struct zsl_quat qm = {
96 		.r = 0.0,
97 		.i = m->data[0],
98 		.j = m->data[1],
99 		.k = m->data[2]
100 	};
101 
102 	/* Define the normalized gravity and magnetic field in the global NED
103 	 * frame. */
104 	struct zsl_quat gv = { .r = 0.0, .i = 0.0, .j = 0.0, .k = -1.0 };
105 	struct zsl_quat mg = { .r = 0.0, .i = 0.0, .j = 0.0, .k = 0.0 };
106 	if (incl == NULL) {
107 		struct zsl_quat h;
108 		zsl_quat_rot(q, &qm, &h);
109 		mg.i = ZSL_SQRT(h.i * h.i + h.j * h.j);
110 		mg.k = h.k;
111 	} else {
112 		mg.i = ZSL_COS(*incl * ZSL_PI / 180.0);
113 		mg.k = ZSL_SIN(*incl * ZSL_PI / 180.0);
114 	}
115 
116 	zsl_quat_to_unit_d(&mg);
117 
118 	/* Calculate the measurement model, h. To do this, use the predicted
119 	 * orientation 'q' to rotate the quaternions 'grav' and 'magn'. */
120 	ZSL_MATRIX_DEF(h, 6, 1);
121 	struct zsl_quat a2;
122 	struct zsl_quat m2;
123 
124 	zsl_quat_rot(q, &gv, &a2);
125 	zsl_quat_rot(q, &mg, &m2);
126 
127 	h.data[0] = 2.0 * a2.i;
128 	h.data[1] = 2.0 * a2.j;
129 	h.data[2] = 2.0 * a2.k;
130 	h.data[3] = 2.0 * m2.i;
131 	h.data[4] = 2.0 * m2.j;
132 	h.data[5] = 2.0 * m2.k;
133 
134 	/* Calculate H, which is the Jacobian matrix of h. Calculate its
135 	 * transpose too. */
136 	ZSL_MATRIX_DEF(H, 6, 4);
137 	ZSL_MATRIX_DEF(Ht, 4, 6);
138 
139 	zsl_real_t H_data[24] = {
140 		/* Fisrt 3 rows of the H matrix. */
141 		q->j, -q->k, q->r, -q->i,
142 		-q->i, -q->r, -q->k, -q->j,
143 		0.0, 2.0 * q->i, 2.0 * q->j, 0.0,
144 
145 		/* Fourth row of the H matrix. */
146 		-mg.k * q->j, mg.k * q->k, -2.0 * mg.i * q->j - mg.k * q->r,
147 		-2.0 * mg.i * q->k + mg.k * q->i,
148 
149 		/* Fifth row of the H matrix. */
150 		-mg.i * q->k + mg.k * q->i,  mg.i * q->j + mg.k * q->r,
151 		mg.i * q->i + mg.k * q->k, -mg.i * q->r + mg.k * q->j,
152 
153 		/* Sixth row of the H matrix. */
154 		mg.i * q->j, mg.i * q->k - 2.0 * mg.k * q->i,
155 		mg.i * q->r - 2.0 * mg.k * q->j, mg.i * q->i
156 	};
157 
158 	zsl_mtx_from_arr(&H, H_data);
159 	zsl_mtx_scalar_mult_d(&H, 2.0);
160 	zsl_mtx_trans(&H, &Ht);
161 
162 	/* Define the R matrix with the variance of the accelerometer
163 	 * and magnetometer. */
164 	ZSL_MATRIX_DEF(R, 6, 6);
165 	zsl_mtx_init(&R, zsl_mtx_entry_fn_identity);
166 	zsl_mtx_set(&R, 0, 0, *var_a);
167 	zsl_mtx_set(&R, 1, 1, *var_a);
168 	zsl_mtx_set(&R, 2, 2, *var_a);
169 	zsl_mtx_set(&R, 3, 3, *var_m);
170 	zsl_mtx_set(&R, 4, 4, *var_m);
171 	zsl_mtx_set(&R, 5, 5, *var_m);
172 
173 	/* Define the vector v, the matrix S and the matrix K. */
174 	ZSL_MATRIX_DEF(v, 6, 1);
175 	ZSL_MATRIX_DEF(S, 6, 6);
176 	ZSL_MATRIX_DEF(K, 4, 6);
177 
178 	/* Calculation of v. */
179 	zsl_mtx_sub(&z, &h, &v);
180 
181 	/* Calculation of S. */
182 	ZSL_MATRIX_DEF(HP, 6, 4);
183 	ZSL_MATRIX_DEF(HPHt, 6, 6);
184 	zsl_mtx_mult(&H, P, &HP);
185 	zsl_mtx_mult(&HP, &Ht, &HPHt);
186 	zsl_mtx_add(&HPHt, &R, &S);
187 
188 	/* Calculation of K. */
189 	ZSL_MATRIX_DEF(S_inv, 6, 6);
190 	ZSL_MATRIX_DEF(PHt, 4, 6);
191 	zsl_mtx_inv(&S, &S_inv);
192 	zsl_mtx_mult(P, &Ht, &PHt);
193 	zsl_mtx_mult(&PHt, &S_inv, &K);
194 
195 	/* Calculate the corrected matrix P. */
196 	ZSL_MATRIX_DEF(idx, 4, 4);
197 	ZSL_MATRIX_DEF(KH, 4, 4);
198 	zsl_mtx_init(&idx, zsl_mtx_entry_fn_identity);
199 	zsl_mtx_mult(&K, &H, &KH);
200 	zsl_mtx_sub_d(&idx, &KH);
201 	zsl_mtx_mult(&idx, P, P);
202 
203 	/* Calculate the corrected orientation quaternion q. */
204 	ZSL_MATRIX_DEF(Kv, 4, 1);
205 	zsl_mtx_mult(&K, &v, &Kv);
206 
207 	q->r += Kv.data[0];
208 	q->i += Kv.data[1];
209 	q->j += Kv.data[2];
210 	q->k += Kv.data[3];
211 
212 	/* Normalize the output quaternion. */
213 	zsl_quat_to_unit_d(q);
214 
215 err:
216 	return rc;
217 }
218 
zsl_fus_kalm_quat_init(struct zsl_vec * a,struct zsl_vec * m,struct zsl_quat * q)219 static int zsl_fus_kalm_quat_init(struct zsl_vec *a, struct zsl_vec *m,
220 				  struct zsl_quat *q)
221 {
222 	int rc = 0;
223 
224 #if CONFIG_ZSL_BOUNDS_CHECKS
225 	/* Make sure that the input vectors are tridimensional. */
226 	if (a == NULL || a->sz != 3 || m == NULL || m->sz != 3) {
227 		rc = -EINVAL;
228 		goto err;
229 	}
230 #endif
231 
232 	/* Define the rotation matrix and two helper vectors. */
233 	ZSL_MATRIX_DEF(mtx, 3, 3);
234 	ZSL_VECTOR_DEF(v, 3);
235 	ZSL_VECTOR_DEF(w, 3);
236 	ZSL_VECTOR_DEF(u, 3);
237 
238 	/* Set the first column of the rotation matrix as (a x m) x a (cross
239 	 * product), the second column as (a x m) and the third one as 'a'. */
240 	zsl_vec_cross(a, m, &w);
241 	zsl_vec_cross(&w, a, &v);
242 	zsl_vec_copy(&u, a);
243 
244 	zsl_vec_to_unit(&v);
245 	zsl_vec_to_unit(&w);
246 	zsl_vec_to_unit(&u);
247 
248 	zsl_mtx_set_col(&mtx, 0, v.data);
249 	zsl_mtx_set_col(&mtx, 1, w.data);
250 	zsl_mtx_set_col(&mtx, 2, u.data);
251 
252 	/* The initial quaternion is obtained converting this rotation matrix into
253 	 * a unit quaternion. */
254 	zsl_quat_from_rot_mtx(&mtx, q);
255 	zsl_quat_to_unit_d(q);
256 
257 	/* Inidicate that we have already called this function. */
258 	zsl_fus_kalm_initialised = 1;
259 
260 err:
261 	return rc;
262 }
263 
zsl_fus_kalm_P_init(struct zsl_mtx * P)264 static int zsl_fus_kalm_P_init(struct zsl_mtx *P)
265 {
266 	int rc = 0;
267 
268 #if CONFIG_ZSL_BOUNDS_CHECKS
269 	/* Make sure that the input matrix P has dimension 4x4. */
270 	if (P->sz_rows != 4 || P->sz_cols != 4) {
271 		rc = -EINVAL;
272 		goto err;
273 	}
274 #endif
275 
276 	zsl_mtx_init(P, zsl_mtx_entry_fn_identity);
277 
278 	/* Inidicate that we have already called this function. */
279 	zsl_fus_kalm_initialised = 1;
280 
281 err:
282 	return rc;
283 }
284 
zsl_fus_kalm_init(uint32_t freq,void * cfg)285 int zsl_fus_kalm_init(uint32_t freq, void *cfg)
286 {
287 	int rc = 0;
288 
289 	struct zsl_fus_kalm_cfg *mcfg = cfg;
290 
291 	(void)mcfg;
292 
293 #if CONFIG_ZSL_BOUNDS_CHECKS
294 	/* Make sure that the sample frequency is positive. */
295 	if (freq <= 0.0) {
296 		rc = -EINVAL;
297 		goto err;
298 	}
299 #endif
300 
301 	zsl_fus_kalm_freq = freq;
302 
303 err:
304 	return rc;
305 }
306 
zsl_fus_kalm_feed(struct zsl_vec * a,struct zsl_vec * m,struct zsl_vec * g,zsl_real_t * incl,struct zsl_quat * q,void * cfg)307 int zsl_fus_kalm_feed(struct zsl_vec *a, struct zsl_vec *m, struct zsl_vec *g,
308 		      zsl_real_t *incl, struct zsl_quat *q, void *cfg)
309 {
310 	struct zsl_fus_kalm_cfg *mcfg = cfg;
311 
312 	if (mcfg->var_g < 0.0 || mcfg->var_a < 0.0 || mcfg->var_m < 0.0) {
313 		return -EINVAL;
314 	}
315 
316 	/* This functions should only be called once. */
317 	if (!zsl_fus_kalm_initialised) {
318 		zsl_fus_kalm_quat_init(a, m, q);
319 		zsl_fus_kalm_P_init(&(mcfg->P));
320 	}
321 
322 	return zsl_fus_kalman(g, a, m, &(mcfg->var_g), &(mcfg->var_a),
323 			      &(mcfg->var_m), incl, &(mcfg->P), q);
324 }
325 
zsl_fus_kalm_error(int error)326 void zsl_fus_kalm_error(int error)
327 {
328 	/* ToDo: Log error in default handler. */
329 }
330