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/aqua.h>
12
13 static uint32_t zsl_fus_aqua_freq;
14 static uint32_t zsl_fus_aqua_initialised;
15
zsl_fus_aqua(struct zsl_vec * a,struct zsl_vec * m,struct zsl_vec * g,zsl_real_t * e_a,zsl_real_t * e_m,zsl_real_t * alpha,zsl_real_t * beta,struct zsl_quat * q)16 static int zsl_fus_aqua(struct zsl_vec *a, struct zsl_vec *m,
17 struct zsl_vec *g, zsl_real_t *e_a, zsl_real_t *e_m,
18 zsl_real_t *alpha, zsl_real_t *beta, struct zsl_quat *q)
19 {
20 int rc = 0;
21
22 #if CONFIG_ZSL_BOUNDS_CHECKS
23 /* Make sure that the input vectors are tridimensional. */
24 if ((a != NULL && (a->sz != 3)) || (m != NULL && (m->sz != 3)) ||
25 g->sz != 3) {
26 rc = -EINVAL;
27 goto err;
28 }
29 /* Make sure that the input quaternion is not zero. */
30 if (ZSL_ABS(zsl_quat_magn(q)) < 1E-6) {
31 rc = -EINVAL;
32 goto err;
33 }
34 #endif
35
36 /* Convert the input quaternion to a unit quaternion. */
37 zsl_quat_to_unit_d(q);
38
39 /* Calculate an estimation of the orientation using only the data of the
40 * gyroscope and quaternion integration. */
41 zsl_vec_scalar_mult(g, -1.0);
42 zsl_quat_from_ang_vel(g, q, 1.0 / zsl_fus_aqua_freq, q);
43
44 /* Continue with the calculations only if the data from the accelerometer
45 * is valid (non zero). */
46 if ((a != NULL) && ZSL_ABS(zsl_vec_norm(a)) > 1E-6) {
47
48 /* Normalize the acceleration vector. */
49 zsl_vec_to_unit(a);
50
51 /* Turn the data of the accelerometer into a pure quaterion. */
52 struct zsl_quat qa = {
53 .r = 0.0,
54 .i = a->data[0],
55 .j = a->data[1],
56 .k = a->data[2]
57 };
58
59 /* Invert q. */
60 struct zsl_quat q_inv;
61 zsl_quat_inv(q, &q_inv);
62
63 /* Rotate the accelerometer data quaternion (qa) using the inverse of
64 * previous estimation of the orientation (q_inv). */
65 struct zsl_quat qg;
66 zsl_quat_rot(&q_inv, &qa, &qg);
67
68 /* Compute the delta q_acc quaternion. */
69 struct zsl_quat Dq_acc = {
70 .r = ZSL_SQRT((qg.k + 1.0) / 2.0),
71 .i = -qg.j / ZSL_SQRT(2.0 * (qg.k + 1.0)),
72 .j = qg.i / ZSL_SQRT(2.0 * (qg.k + 1.0)),
73 .k = 0.0
74 };
75
76 /* Scale down the delta q_acc by using spherical linear interpolation
77 * or simply linear interpolation to minimize the effects of high
78 * frequency noise in the accelerometer. */
79 struct zsl_quat q_acc;
80 struct zsl_quat qi;
81 zsl_quat_init(&qi, ZSL_QUAT_TYPE_IDENTITY);
82 if (Dq_acc.r > *e_a) {
83 zsl_quat_lerp(&qi, &Dq_acc, *alpha, &q_acc);
84 } else {
85 zsl_quat_slerp(&qi, &Dq_acc, *alpha, &q_acc);
86 }
87
88 /* New orientation estimation with accelerometer correction. */
89 zsl_quat_mult(q, &q_acc, q);
90
91 /* Continue with the calculations only if the data from the
92 * magnetometer is valid (non zero). */
93 if ((m != NULL) && ZSL_ABS(zsl_vec_norm(m)) > 1E-6) {
94
95 /* Normalize the magnetic field vector. */
96 zsl_vec_to_unit(m);
97
98 /* Turn the data of the magnetometer into a pure quaterion. */
99 struct zsl_quat qm = {
100 .r = 0.0,
101 .i = m->data[0],
102 .j = m->data[1],
103 .k = m->data[2]
104 };
105
106 /* Invert q again. */
107 zsl_quat_inv(q, &q_inv);
108
109 /* Rotate the magnetometer data quaternion (qm) using the inverse
110 * of the previous estimation of the orientation (q_inv). */
111 struct zsl_quat ql;
112 zsl_quat_rot(&q_inv, &qm, &ql);
113
114 /* Compute the delta q_mag quaternion. */
115 zsl_real_t y = ZSL_SQRT(ql.i * ql.i + ql.j * ql.j);
116 struct zsl_quat Dq_mag = {
117 .r = ZSL_SQRT((y * y + ql.i * y) / (2.0 * y * y)),
118 .i = 0.0,
119 .j = 0.0,
120 .k = ql.j / ZSL_SQRT(2.0 * (y * y + ql.i * y))
121 };
122
123 /* Scale down the delta q_mag quaternion by using spherical linear
124 * interpolation or simply linear interpolation to minimize the
125 * effects of high frequency noise in the magnetometer. */
126 struct zsl_quat q_mag;
127 if (Dq_mag.r > *e_m) {
128 zsl_quat_lerp(&qi, &Dq_mag, *beta, &q_mag);
129 } else {
130 zsl_quat_slerp(&qi, &Dq_mag, *beta, &q_mag);
131 }
132
133 /* New orientation estimation with magnetometer correction. */
134 zsl_quat_mult(q, &q_mag, q);
135 }
136 }
137
138 /* Normalize the output quaternion. */
139 zsl_quat_to_unit_d(q);
140
141 err:
142 return rc;
143 }
144
zsl_fus_aqua_alpha_init(struct zsl_vec * a,zsl_real_t * alpha)145 static int zsl_fus_aqua_alpha_init(struct zsl_vec *a, zsl_real_t *alpha)
146 {
147 int rc = 0;
148
149 #if CONFIG_ZSL_BOUNDS_CHECKS
150 /* Make sure that the input vector is tridimensional. */
151 if (a->sz != 3) {
152 rc = -EINVAL;
153 goto err;
154 }
155 #endif
156
157 /* Calculate the value of alpha, which depends on the magnitude error
158 * (m_e) and the value of alpha in static conditions. */
159 zsl_real_t n = zsl_vec_norm(a);
160 zsl_real_t gr = 9.81; /* Earth's gravity. */
161 zsl_real_t m_e = ZSL_ABS(n - gr) / gr; /* Magnitude error. */
162 if (m_e >= 0.2) {
163 *alpha = 0.0;
164 } else if (m_e > 0.1 && m_e < 0.2) {
165 *alpha *= (0.2 - m_e) / 0.1;
166 }
167
168 /* Inidicate that we have already called this function. */
169 zsl_fus_aqua_initialised++;
170
171 err:
172 return rc;
173 }
174
zsl_fus_aqua_init(uint32_t freq,void * cfg)175 int zsl_fus_aqua_init(uint32_t freq, void *cfg)
176 {
177 int rc = 0;
178
179 struct zsl_fus_aqua_cfg *mcfg = cfg;
180
181 (void)mcfg;
182
183 #if CONFIG_ZSL_BOUNDS_CHECKS
184 /* Make sure that the sample frequency is positive. */
185 if (freq <= 0.0) {
186 rc = -EINVAL;
187 goto err;
188 }
189 #endif
190
191 zsl_fus_aqua_freq = freq;
192
193 err:
194 return rc;
195 }
196
zsl_fus_aqua_feed(struct zsl_vec * a,struct zsl_vec * m,struct zsl_vec * g,zsl_real_t * incl,struct zsl_quat * q,void * cfg)197 int zsl_fus_aqua_feed(struct zsl_vec *a, struct zsl_vec *m,
198 struct zsl_vec *g, zsl_real_t *incl, struct zsl_quat *q,
199 void *cfg)
200 {
201 struct zsl_fus_aqua_cfg *mcfg = cfg;
202
203 if (mcfg->alpha < 0.0 || mcfg->alpha > 1.0 || mcfg->beta < 0.0 ||
204 mcfg->beta > 1.0) {
205 return -EINVAL;
206 }
207
208 /* This functions should only be called once. */
209 if (!zsl_fus_aqua_initialised) {
210 zsl_fus_aqua_alpha_init(a, &(mcfg->alpha));
211 }
212
213 return zsl_fus_aqua(a, m, g, &(mcfg->e_a), &(mcfg->e_m), &(mcfg->alpha),
214 &(mcfg->beta), q);
215 }
216
zsl_fus_aqua_error(int error)217 void zsl_fus_aqua_error(int error)
218 {
219 /* ToDo: Log error in default handler. */
220 }
221