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/complementary.h>
12 
13 static uint32_t zsl_fus_comp_freq = 0;
14 
zsl_fus_comp(struct zsl_vec * a,struct zsl_vec * m,struct zsl_vec * g,zsl_real_t * alpha,struct zsl_quat * q)15 static int zsl_fus_comp(struct zsl_vec *a, struct zsl_vec *m,
16 			struct zsl_vec *g, zsl_real_t *alpha, 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. Alpha must be
22 	 * between zero and one (included). */
23 	if ((a != NULL && (a->sz != 3)) || (m != NULL && (m->sz != 3)) ||
24 	    g->sz != 3 || *alpha < 0.0 || *alpha > 1.0) {
25 		rc = -EINVAL;
26 		goto err;
27 	}
28 	/* Make sure that the input quaternion is not zero. */
29 	if (ZSL_ABS(zsl_quat_magn(q)) < 1E-6) {
30 		rc = -EINVAL;
31 		goto err;
32 	}
33 #endif
34 
35 	/* Normalize the input quaternion. */
36 	zsl_quat_to_unit_d(q);
37 
38 	/* Estimate the orientation (q_w) using the angular velocity data from the
39 	 * gyroscope. */
40 	struct zsl_quat q_w;
41 	zsl_quat_from_ang_vel(g, q, 1.0 / zsl_fus_comp_freq, &q_w);
42 
43 	/* Continue with the calculations only if the data from the accelerometer
44 	 * and magnetometer is valid (non zero). */
45 	if ((m != NULL) && (ZSL_ABS(zsl_vec_norm(m)) > 1E-6) &&
46 	    (a != NULL) && (ZSL_ABS(zsl_vec_norm(a)) > 1E-6)) {
47 		/* Estimate the orientation (q_am) using the acceleration and magnetic
48 		 * field data to calculate the attitude. Then convert this orientation
49 		 * to a unit quaternion. */
50 		struct zsl_attitude att;
51 		struct zsl_euler e;
52 		struct zsl_quat q_am;
53 		zsl_att_from_accelmag(a, m, &att);
54 		zsl_att_to_euler(&att, &e);
55 		zsl_quat_from_euler(&e, &q_am);
56 
57 		/* The final estimation is a linear interpolation the previous
58 		 * estimations (q_w and q_am). Alpha is arbitrary and depends on the
59 		 * reliability of the sensors. For more accurate gyroscope data, alpha
60 		 * should be closer to zero. For more accurate accelerometer and
61 		 * magnetometer data, alpha should be closer to 1. */
62 		zsl_quat_lerp(&q_w, &q_am, *alpha, q);
63 	} else {
64 		/* If the data from the accelerometer and magnetometer is invalid, then
65 		 * set the output quaternion as just q_w. */
66 		q->r = q_w.r;
67 		q->i = q_w.i;
68 		q->j = q_w.j;
69 		q->k = q_w.k;
70 	}
71 
72 	/* Normalize the output quaternion. */
73 	zsl_quat_to_unit_d(q);
74 
75 err:
76 	return rc;
77 }
78 
zsl_fus_comp_init(uint32_t freq,void * cfg)79 int zsl_fus_comp_init(uint32_t freq, void *cfg)
80 {
81 	int rc = 0;
82 
83 	struct zsl_fus_comp_cfg *mcfg = cfg;
84 
85 	(void)mcfg;
86 
87 #if CONFIG_ZSL_BOUNDS_CHECKS
88 	/* Make sure that the sample frequency is positive. */
89 	if (freq <= 0.0) {
90 		rc = -EINVAL;
91 		goto err;
92 	}
93 #endif
94 
95 	zsl_fus_comp_freq = freq;
96 
97 err:
98 	return rc;
99 }
100 
zsl_fus_comp_feed(struct zsl_vec * a,struct zsl_vec * m,struct zsl_vec * g,zsl_real_t * incl,struct zsl_quat * q,void * cfg)101 int zsl_fus_comp_feed(struct zsl_vec *a, struct zsl_vec *m,
102 		      struct zsl_vec *g, zsl_real_t *incl, struct zsl_quat *q, void *cfg)
103 {
104 	struct zsl_fus_comp_cfg *mcfg = cfg;
105 
106 	if (mcfg->alpha < 0.0 || mcfg->alpha > 1.0) {
107 		return -EINVAL;
108 	}
109 
110 	return zsl_fus_comp(a, m, g, &(mcfg->alpha), q);
111 }
112 
zsl_fus_comp_error(int error)113 void zsl_fus_comp_error(int error)
114 {
115 	/* ToDo: Log error in default handler. */
116 }
117