Paparazzi UAS  v5.10_stable-5-g83a0da5-dirty
Paparazzi is a free software Unmanned Aircraft System.
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
ahrs_int_cmpl_quat.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008-2013 The Paparazzi Team
3  *
4  * This file is part of paparazzi.
5  *
6  * paparazzi is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * paparazzi is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with paparazzi; see the file COPYING. If not, write to
18  * the Free Software Foundation, 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
31 #include "generated/airframe.h"
32 
35 
36 #if USE_GPS
37 #include "subsystems/gps.h"
38 #endif
39 #include "math/pprz_trig_int.h"
40 #include "math/pprz_algebra_int.h"
41 
42 #ifdef AHRS_PROPAGATE_LOW_PASS_RATES
43 PRINT_CONFIG_MSG("LOW PASS FILTER ON GYRO RATES")
44 #endif
45 
46 #if USE_MAGNETOMETER && AHRS_USE_GPS_HEADING
47 #warning "Using both magnetometer and GPS course to update heading. Probably better to configure USE_MAGNETOMETER=0 if you want to use GPS course."
48 #endif
49 
50 #if !USE_MAGNETOMETER && !AHRS_USE_GPS_HEADING
51 #warning "Please use either USE_MAGNETOMETER or AHRS_USE_GPS_HEADING."
52 #endif
53 
54 #if AHRS_USE_GPS_HEADING && !USE_GPS
55 #error "AHRS_USE_GPS_HEADING needs USE_GPS to be TRUE"
56 #endif
57 
58 /*
59  * default gains for correcting attitude and bias from accel/mag
60  */
61 #ifndef AHRS_ACCEL_OMEGA
62 #define AHRS_ACCEL_OMEGA 0.063
63 #endif
64 #ifndef AHRS_ACCEL_ZETA
65 #define AHRS_ACCEL_ZETA 0.9
66 #endif
67 PRINT_CONFIG_VAR(AHRS_ACCEL_OMEGA)
68 PRINT_CONFIG_VAR(AHRS_ACCEL_ZETA)
69 
70 
71 #ifndef AHRS_MAG_OMEGA
72 #define AHRS_MAG_OMEGA 0.04
73 #endif
74 #ifndef AHRS_MAG_ZETA
75 #define AHRS_MAG_ZETA 0.9
76 #endif
77 #if USE_MAGNETOMETER
78 PRINT_CONFIG_VAR(AHRS_MAG_OMEGA)
79 PRINT_CONFIG_VAR(AHRS_MAG_ZETA)
80 #endif
81 
83 #ifndef AHRS_GRAVITY_HEURISTIC_FACTOR
84 #define AHRS_GRAVITY_HEURISTIC_FACTOR 30
85 #endif
86 
88 #ifndef AHRS_BIAS_UPDATE_HEADING_THRESHOLD
89 #define AHRS_BIAS_UPDATE_HEADING_THRESHOLD 5.0
90 #endif
91 
95 #ifndef AHRS_HEADING_UPDATE_GPS_MIN_SPEED
96 #define AHRS_HEADING_UPDATE_GPS_MIN_SPEED 5.0
97 #endif
98 
100 #ifdef AHRS_PROPAGATE_LOW_PASS_RATES
101 #ifndef AHRS_PROPAGATE_LOW_PASS_RATES_MUL
102 #define AHRS_PROPAGATE_LOW_PASS_RATES_MUL 2
103 #endif
104 
105 #ifndef AHRS_PROPAGATE_LOW_PASS_RATES_DIV
106 #define AHRS_PROPAGATE_LOW_PASS_RATES_DIV 3
107 #endif
108 #endif
109 
111 
112 static inline void UNUSED ahrs_icq_update_mag_full(struct Int32Vect3 *mag, float dt);
113 static inline void ahrs_icq_update_mag_2d(struct Int32Vect3 *mag, float dt);
114 
115 void ahrs_icq_init(void)
116 {
117 
119  ahrs_icq.is_aligned = false;
120 
122  ahrs_icq.heading_aligned = false;
123 
124  /* init ltp_to_imu quaternion as zero/identity rotation */
126 
128 
132 
133  /* set default filter cut-off frequency and damping */
140 
141  /* set default gravity heuristic */
143 
144 #if AHRS_GRAVITY_UPDATE_COORDINATED_TURN
145  ahrs_icq.correct_gravity = true;
146 #else
147  ahrs_icq.correct_gravity = false;
148 #endif
149 
151  MAG_BFP_OF_REAL(AHRS_H_Y), MAG_BFP_OF_REAL(AHRS_H_Z));
152 
153  ahrs_icq.accel_cnt = 0;
154  ahrs_icq.mag_cnt = 0;
155 }
156 
157 
158 bool ahrs_icq_align(struct Int32Rates *lp_gyro, struct Int32Vect3 *lp_accel,
159  struct Int32Vect3 *lp_mag)
160 {
161 
162 #if USE_MAGNETOMETER
163  /* Compute an initial orientation from accel and mag directly as quaternion */
165  lp_accel, lp_mag);
166  ahrs_icq.heading_aligned = true;
167 #else
168  /* Compute an initial orientation from accel and just set heading to zero */
170  ahrs_icq.heading_aligned = false;
171  // supress unused arg warning
172  lp_mag = lp_mag;
173 #endif
174 
175  /* Use low passed gyro value as initial bias */
176  RATES_COPY(ahrs_icq.gyro_bias, *lp_gyro);
177  RATES_COPY(ahrs_icq.high_rez_bias, *lp_gyro);
179 
181  ahrs_icq.is_aligned = true;
182 
183  return true;
184 }
185 
186 
187 void ahrs_icq_propagate(struct Int32Rates *gyro, float dt)
188 {
189  int32_t freq = (int32_t)(1. / dt);
190 
191  /* unbias gyro */
192  struct Int32Rates omega;
193  RATES_DIFF(omega, *gyro, ahrs_icq.gyro_bias);
194 
195  /* low pass rate */
196 #ifdef AHRS_PROPAGATE_LOW_PASS_RATES
197  RATES_SMUL(ahrs_icq.imu_rate, ahrs_icq.imu_rate, AHRS_PROPAGATE_LOW_PASS_RATES_MUL);
198  RATES_ADD(ahrs_icq.imu_rate, omega);
199  RATES_SDIV(ahrs_icq.imu_rate, ahrs_icq.imu_rate, AHRS_PROPAGATE_LOW_PASS_RATES_DIV);
200 #else
201  RATES_COPY(ahrs_icq.imu_rate, omega);
202 #endif
203 
204  /* add correction */
206  /* and zeros it */
208 
209  /* integrate quaternion */
211  &omega, freq);
213 
214  // increase accel and mag propagation counters
216  ahrs_icq.mag_cnt++;
217 }
218 
219 
221 {
222  /* Complementary filter proportionnal gain (without frequency correction)
223  * Kp = 2 * omega * zeta
224  *
225  * accel_inv_kp = 1 / (Kp * FRAC_conversion / cross_product_gain)
226  * accel_inv_kp = 4096 * 9.81 / Kp
227  */
228  ahrs_icq.accel_inv_kp = 4096 * 9.81 /
230 
231  /* Complementary filter integral gain
232  * Ki = omega^2
233  *
234  * accel_inv_ki = 2^5 / (Ki * FRAC_conversion / cross_product_gain)
235  * accel_inv_ki = 2^5 / 2^16 * 9.81 / Ki
236  * accel_inv_ki = 9.81 / 2048 / Ki
237  */
239 }
240 
241 void ahrs_icq_update_accel(struct Int32Vect3 *accel, float dt)
242 {
243  // check if we had at least one propagation since last update
244  if (ahrs_icq.accel_cnt == 0) {
245  return;
246  }
247 
248  // c2 = ltp z-axis in imu-frame
249  struct Int32RMat ltp_to_imu_rmat;
250  int32_rmat_of_quat(&ltp_to_imu_rmat, &ahrs_icq.ltp_to_imu_quat);
251  struct Int32Vect3 c2 = { RMAT_ELMT(ltp_to_imu_rmat, 0, 2),
252  RMAT_ELMT(ltp_to_imu_rmat, 1, 2),
253  RMAT_ELMT(ltp_to_imu_rmat, 2, 2)
254  };
255  struct Int32Vect3 residual;
256 
257  struct Int32Vect3 pseudo_gravity_measurement;
258 
260  /*
261  * centrifugal acceleration in body frame
262  * a_c_body = omega x (omega x r)
263  * (omega x r) = tangential velocity in body frame
264  * a_c_body = omega x vel_tangential_body
265  * assumption: tangential velocity only along body x-axis
266  */
267 
268  // FIXME: check overflows !
269 #define COMPUTATION_FRAC 16
270 #define ACC_FROM_CROSS_FRAC INT32_RATE_FRAC + INT32_SPEED_FRAC - INT32_ACCEL_FRAC - COMPUTATION_FRAC
271 
272  const struct Int32Vect3 vel_tangential_body =
274  struct Int32RMat *body_to_imu_rmat = orientationGetRMat_i(&ahrs_icq.body_to_imu);
275  struct Int32Rates body_rate;
276  int32_rmat_transp_ratemult(&body_rate, body_to_imu_rmat, &ahrs_icq.imu_rate);
277  struct Int32Vect3 acc_c_body;
278  VECT3_RATES_CROSS_VECT3(acc_c_body, body_rate, vel_tangential_body);
279  INT32_VECT3_RSHIFT(acc_c_body, acc_c_body, ACC_FROM_CROSS_FRAC);
280 
281  /* convert centrifucal acceleration from body to imu frame */
282  struct Int32Vect3 acc_c_imu;
283  int32_rmat_vmult(&acc_c_imu, body_to_imu_rmat, &acc_c_body);
284 
285  /* and subtract it from imu measurement to get a corrected measurement
286  * of the gravity vector */
287  VECT3_DIFF(pseudo_gravity_measurement, *accel, acc_c_imu);
288  } else {
289  VECT3_COPY(pseudo_gravity_measurement, *accel);
290  }
291 
292  /* compute the residual of the pseudo gravity vector in imu frame */
293  VECT3_CROSS_PRODUCT(residual, pseudo_gravity_measurement, c2);
294 
295 
296  /* FIR filtered pseudo_gravity_measurement */
297 #define FIR_FILTER_SIZE 8
298  static struct Int32Vect3 filtered_gravity_measurement = {0, 0, 0};
299  VECT3_SMUL(filtered_gravity_measurement, filtered_gravity_measurement, FIR_FILTER_SIZE - 1);
300  VECT3_ADD(filtered_gravity_measurement, pseudo_gravity_measurement);
301  VECT3_SDIV(filtered_gravity_measurement, filtered_gravity_measurement, FIR_FILTER_SIZE);
302 
303 
305  /* heuristic on acceleration (gravity estimate) norm */
306  /* Factor how strongly to change the weight.
307  * e.g. for gravity_heuristic_factor 30:
308  * <0.66G = 0, 1G = 1.0, >1.33G = 0
309  */
310 
311  struct FloatVect3 g_meas_f;
312  ACCELS_FLOAT_OF_BFP(g_meas_f, filtered_gravity_measurement);
313  const float g_meas_norm = FLOAT_VECT3_NORM(g_meas_f) / 9.81;
314  ahrs_icq.weight = 1.0 - ahrs_icq.gravity_heuristic_factor * fabs(1.0 - g_meas_norm) / 10;
315  Bound(ahrs_icq.weight, 0.15, 1.0);
316  } else {
317  ahrs_icq.weight = 1.0;
318  }
319 
320  /* Complementary filter proportional gain.
321  * Kp = 2 * zeta * omega
322  * final Kp with frequency correction = Kp * ahrs_icq.accel_cnt
323  * with ahrs_icq.accel_cnt beeing the number of propagations since last update
324  *
325  * residual FRAC : ACCEL_FRAC + TRIG_FRAC = 10 + 14 = 24
326  * rate_correction FRAC: RATE_FRAC = 12
327  * FRAC_conversion: 2^12 / 2^24 = 1 / 4096
328  * cross_product_gain : 9.81 m/s2
329  *
330  * accel_inv_kp = 1 / (Kp * FRAC_conversion / cross_product_gain)
331  * accel_inv_kp = 4096 * 9.81 / Kp
332  *
333  * inv_rate_scale = 1 / (weight * Kp * FRAC_conversion / cross_product_gain)
334  * inv_rate_scale = 1 / Kp / weight
335  * inv_rate_scale = accel_inv_kp / accel_cnt / weight
336  */
338  / ahrs_icq.weight);
339  Bound(inv_rate_scale, 8192, 4194304);
340 
341  ahrs_icq.rate_correction.p -= residual.x / inv_rate_scale;
342  ahrs_icq.rate_correction.q -= residual.y / inv_rate_scale;
343  ahrs_icq.rate_correction.r -= residual.z / inv_rate_scale;
344 
345  // reset accel propagation counter
346  ahrs_icq.accel_cnt = 0;
347 
348  /* Complementary filter integral gain
349  * Correct the gyro bias.
350  * Ki = omega^2 * dt
351  *
352  * residual FRAC = ACCEL_FRAC + TRIG_FRAC = 10 + 14 = 24
353  * high_rez_bias = RATE_FRAC+28 = 40
354  * FRAC_conversion: 2^40 / 2^24 = 2^16
355  * cross_product_gain : 9.81 m/s2
356  *
357  * accel_inv_ki = 2^5 / (Ki * FRAC_conversion / cross_product_gain)
358  * accel_inv_ki = 2^5 / 2^16 * 9.81 * Ki = 9.81 / 2^11 * Ki
359  *
360  * inv_bias_gain = 2^5 / (weight^2 * Ki * FRAC_conversion / cross_product_gain)
361  * inv_bias_gain = accel_inv_ki / weight^2
362  */
363 
364  int32_t inv_bias_gain = (int32_t)(ahrs_icq.accel_inv_ki /
365  (dt * ahrs_icq.weight * ahrs_icq.weight));
366  Bound(inv_bias_gain, 8, 65536)
367  ahrs_icq.high_rez_bias.p += (residual.x / inv_bias_gain) << 5;
368  ahrs_icq.high_rez_bias.q += (residual.y / inv_bias_gain) << 5;
369  ahrs_icq.high_rez_bias.r += (residual.z / inv_bias_gain) << 5;
370 
372 
373 }
374 
375 
376 void ahrs_icq_update_mag(struct Int32Vect3 *mag __attribute__((unused)), float dt __attribute__((unused)))
377 {
378 #if USE_MAGNETOMETER
379  // check if we had at least one propagation since last update
380  if (ahrs_icq.mag_cnt == 0) {
381  return;
382  }
383 #if AHRS_MAG_UPDATE_ALL_AXES
385 #else
387 #endif
388  // reset mag propagation counter
389  ahrs_icq.mag_cnt = 0;
390 #endif
391 }
392 
394 {
395  /* Complementary filter proportionnal gain = 2*omega*zeta */
397  /* Complementary filter integral gain = omega^2 */
399 }
400 
401 
402 static inline void ahrs_icq_update_mag_full(struct Int32Vect3 *mag, float dt)
403 {
404 
405  struct Int32RMat ltp_to_imu_rmat;
406  int32_rmat_of_quat(&ltp_to_imu_rmat, &ahrs_icq.ltp_to_imu_quat);
407 
408  struct Int32Vect3 expected_imu;
409  int32_rmat_vmult(&expected_imu, &ltp_to_imu_rmat, &ahrs_icq.mag_h);
410 
411  struct Int32Vect3 residual;
412  VECT3_CROSS_PRODUCT(residual, *mag, expected_imu);
413 
414  /* Complementary filter proportionnal gain.
415  * Kp = 2 * mag_zeta * mag_omega
416  * final Kp with frequency correction = Kp * ahrs_icq.mag_cnt
417  * with ahrs_icq.mag_cnt beeing the number of propagations since last update
418  *
419  * residual FRAC: 2 * MAG_FRAC = 22
420  * rate_correction FRAC: RATE_FRAC = 12
421  * FRAC conversion: 2^12 / 2^22 = 1/1024
422  *
423  * inv_rate_gain = 1 / Kp / FRAC_conversion
424  * inv_rate_gain = 1024 / Kp
425  */
426 
427  const int32_t inv_rate_gain = (int32_t)(1024.0 / (ahrs_icq.mag_kp * ahrs_icq.mag_cnt));
428 
429  ahrs_icq.rate_correction.p += residual.x / inv_rate_gain;
430  ahrs_icq.rate_correction.q += residual.y / inv_rate_gain;
431  ahrs_icq.rate_correction.r += residual.z / inv_rate_gain;
432 
433  /* Complementary filter integral gain
434  * Correct the gyro bias.
435  * Ki = omega^2 * dt
436  *
437  * residual FRAC: 2* MAG_FRAC = 22
438  * high_rez_bias FRAC: RATE_FRAC+28 = 40
439  * FRAC conversion: 2^40 / 2^22 = 2^18
440  *
441  * bias_gain = Ki * FRAC_conversion = Ki * 2^18
442  */
443  const int32_t bias_gain = (int32_t)(ahrs_icq.mag_ki * dt * (1 << 18));
444 
445  ahrs_icq.high_rez_bias.p -= residual.x * bias_gain;
446  ahrs_icq.high_rez_bias.q -= residual.y * bias_gain;
447  ahrs_icq.high_rez_bias.r -= residual.z * bias_gain;
448 
449 
451 
452 }
453 
454 
455 static inline void ahrs_icq_update_mag_2d(struct Int32Vect3 *mag, float dt)
456 {
457 
458  struct Int32Vect2 expected_ltp = {ahrs_icq.mag_h.x, ahrs_icq.mag_h.y};
459  /* normalize expected ltp in 2D (x,y) */
460  int32_vect2_normalize(&expected_ltp, INT32_MAG_FRAC);
461 
462  struct Int32RMat ltp_to_imu_rmat;
463  int32_rmat_of_quat(&ltp_to_imu_rmat, &ahrs_icq.ltp_to_imu_quat);
464 
465  struct Int32Vect3 measured_ltp;
466  int32_rmat_transp_vmult(&measured_ltp, &ltp_to_imu_rmat, mag);
467 
468  /* normalize measured ltp in 2D (x,y) */
469  struct Int32Vect2 measured_ltp_2d = {measured_ltp.x, measured_ltp.y};
470  int32_vect2_normalize(&measured_ltp_2d, INT32_MAG_FRAC);
471 
472  /* residual_ltp FRAC: 2 * MAG_FRAC - 5 = 17 */
473  struct Int32Vect3 residual_ltp = {
474  0,
475  0,
476  (measured_ltp_2d.x * expected_ltp.y - measured_ltp_2d.y * expected_ltp.x) / (1 << 5)
477  };
478 
479 
480  struct Int32Vect3 residual_imu;
481  int32_rmat_vmult(&residual_imu, &ltp_to_imu_rmat, &residual_ltp);
482 
483  /* Complementary filter proportionnal gain.
484  * Kp = 2 * mag_zeta * mag_omega
485  * final Kp with frequency correction = Kp * ahrs_icq.mag_cnt
486  * with ahrs_icq.mag_cnt beeing the number of propagations since last update
487  *
488  * residual_imu FRAC = residual_ltp FRAC = 17
489  * rate_correction FRAC: RATE_FRAC = 12
490  * FRAC conversion: 2^12 / 2^17 = 1/32
491  *
492  * inv_rate_gain = 1 / Kp / FRAC_conversion
493  * inv_rate_gain = 32 / Kp
494  */
495  int32_t inv_rate_gain = (int32_t)(32.0 / (ahrs_icq.mag_kp * ahrs_icq.mag_cnt));
496 
497  ahrs_icq.rate_correction.p += (residual_imu.x / inv_rate_gain);
498  ahrs_icq.rate_correction.q += (residual_imu.y / inv_rate_gain);
499  ahrs_icq.rate_correction.r += (residual_imu.z / inv_rate_gain);
500 
501  /* Complementary filter integral gain
502  * Correct the gyro bias.
503  * Ki = omega^2 * dt
504  *
505  * residual_imu FRAC = residual_ltp FRAC = 17
506  * high_rez_bias FRAC: RATE_FRAC+28 = 40
507  * FRAC conversion: 2^40 / 2^17 = 2^23
508  *
509  * bias_gain = Ki * FRAC_conversion = Ki * 2^23
510  */
511  int32_t bias_gain = (int32_t)(ahrs_icq.mag_ki * dt * (1 << 23));
512 
513  ahrs_icq.high_rez_bias.p -= (residual_imu.x * bias_gain);
514  ahrs_icq.high_rez_bias.q -= (residual_imu.y * bias_gain);
515  ahrs_icq.high_rez_bias.r -= (residual_imu.z * bias_gain);
516 
518 
519 }
520 
521 void ahrs_icq_update_gps(struct GpsState *gps_s __attribute__((unused)))
522 {
523 #if AHRS_GRAVITY_UPDATE_COORDINATED_TURN && USE_GPS
524  if (gps_s->fix >= GPS_FIX_3D) {
525  ahrs_icq.ltp_vel_norm = SPEED_BFP_OF_REAL(gps_s->speed_3d / 100.);
527  } else {
529  }
530 #endif
531 
532 #if AHRS_USE_GPS_HEADING && USE_GPS
533  // got a 3d fix, ground speed > AHRS_HEADING_UPDATE_GPS_MIN_SPEED (default 5.0 m/s)
534  // and course accuracy is better than 10deg
535  static const uint16_t gps_min_speed = AHRS_HEADING_UPDATE_GPS_MIN_SPEED * 100;
536  static const uint32_t max_cacc = RadOfDeg(10 * 1e7);
537  if (gps_s->fix >= GPS_FIX_3D &&
538  gps_s->gspeed >= gps_min_speed &&
539  gps_s->cacc <= max_cacc) {
540 
541  // gps_s->course is in rad * 1e7, we need it in rad * 2^INT32_ANGLE_FRAC
542  int32_t course = gps_s->course * ((1 << INT32_ANGLE_FRAC) / 1e7);
543 
544  /* the assumption here is that there is no side-slip, so heading=course */
545 
547  ahrs_icq_update_heading(course);
548  } else {
549  /* hard reset the heading if this is the first measurement */
550  ahrs_icq_realign_heading(course);
551  }
552  }
553 #endif
554 }
555 
556 
558 {
559 
560  INT32_ANGLE_NORMALIZE(heading);
561 
562  // row 0 of ltp_to_body_rmat = body x-axis in ltp frame
563  // we only consider x and y
564  struct Int32Quat *body_to_imu_quat = orientationGetQuat_i(&ahrs_icq.body_to_imu);
565  struct Int32Quat ltp_to_body_quat;
566  int32_quat_comp_inv(&ltp_to_body_quat, &ahrs_icq.ltp_to_imu_quat, body_to_imu_quat);
567  struct Int32RMat ltp_to_body_rmat;
568  int32_rmat_of_quat(&ltp_to_body_rmat, &ltp_to_body_quat);
569  struct Int32Vect2 expected_ltp = {
570  RMAT_ELMT(ltp_to_body_rmat, 0, 0),
571  RMAT_ELMT(ltp_to_body_rmat, 0, 1)
572  };
573 
574  int32_t heading_x, heading_y;
575  PPRZ_ITRIG_COS(heading_x, heading); // measured course in x-direction
576  PPRZ_ITRIG_SIN(heading_y, heading); // measured course in y-direction
577 
578  // expected_heading cross measured_heading ??
579  struct Int32Vect3 residual_ltp = {
580  0,
581  0,
582  (expected_ltp.x * heading_y - expected_ltp.y * heading_x) / (1 << INT32_ANGLE_FRAC)
583  };
584 
585  struct Int32Vect3 residual_imu;
586  struct Int32RMat ltp_to_imu_rmat;
587  int32_rmat_of_quat(&ltp_to_imu_rmat, &ahrs_icq.ltp_to_imu_quat);
588  int32_rmat_vmult(&residual_imu, &ltp_to_imu_rmat, &residual_ltp);
589 
590  // residual FRAC = TRIG_FRAC + TRIG_FRAC = 14 + 14 = 28
591  // rate_correction FRAC = RATE_FRAC = 12
592  // 2^12 / 2^28 * 4.0 = 1/2^14
593  // (1<<INT32_ANGLE_FRAC)/2^14 = 1/4
594  ahrs_icq.rate_correction.p += residual_imu.x / 4;
595  ahrs_icq.rate_correction.q += residual_imu.y / 4;
596  ahrs_icq.rate_correction.r += residual_imu.z / 4;
597 
598 
599  /* crude attempt to only update bias if deviation is small
600  * e.g. needed when you only have gps providing heading
601  * and the inital heading is totally different from
602  * the gps course information you get once you have a gps fix.
603  * Otherwise the bias will be falsely "corrected".
604  */
605  int32_t sin_max_angle_deviation;
606  PPRZ_ITRIG_SIN(sin_max_angle_deviation, TRIG_BFP_OF_REAL(RadOfDeg(AHRS_BIAS_UPDATE_HEADING_THRESHOLD)));
607  if (ABS(residual_ltp.z) < sin_max_angle_deviation) {
608  // residual_ltp FRAC = 2 * TRIG_FRAC = 28
609  // high_rez_bias = RATE_FRAC+28 = 40
610  // 2^40 / 2^28 * 2.5e-4 = 1
611  ahrs_icq.high_rez_bias.p -= residual_imu.x * (1 << INT32_ANGLE_FRAC);
612  ahrs_icq.high_rez_bias.q -= residual_imu.y * (1 << INT32_ANGLE_FRAC);
613  ahrs_icq.high_rez_bias.r -= residual_imu.z * (1 << INT32_ANGLE_FRAC);
614 
616  }
617 }
618 
620 {
621  struct Int32Quat *body_to_imu_quat = orientationGetQuat_i(&ahrs_icq.body_to_imu);
622  struct Int32Quat ltp_to_body_quat;
623  int32_quat_comp_inv(&ltp_to_body_quat, &ahrs_icq.ltp_to_imu_quat, body_to_imu_quat);
624 
625  /* quaternion representing only the heading rotation from ltp to body */
626  struct Int32Quat q_h_new;
627  q_h_new.qx = 0;
628  q_h_new.qy = 0;
629  PPRZ_ITRIG_SIN(q_h_new.qz, heading / 2);
630  PPRZ_ITRIG_COS(q_h_new.qi, heading / 2);
631 
632  /* quaternion representing current heading only */
633  struct Int32Quat q_h;
634  QUAT_COPY(q_h, ltp_to_body_quat);
635  q_h.qx = 0;
636  q_h.qy = 0;
637  int32_quat_normalize(&q_h);
638 
639  /* quaternion representing rotation from current to new heading */
640  struct Int32Quat q_c;
641  int32_quat_inv_comp_norm_shortest(&q_c, &q_h, &q_h_new);
642 
643  /* correct current heading in body frame */
644  struct Int32Quat q;
645  int32_quat_comp_norm_shortest(&q, &q_c, &ltp_to_body_quat);
646  QUAT_COPY(ltp_to_body_quat, q);
647 
648  /* compute ltp to imu rotations */
649  int32_quat_comp(&ahrs_icq.ltp_to_imu_quat, &ltp_to_body_quat, body_to_imu_quat);
650 
651  ahrs_icq.heading_aligned = true;
652 }
653 
655 {
657 }
658 
660 {
662 
663  if (!ahrs_icq.is_aligned) {
664  /* Set ltp_to_imu so that body is zero */
666  }
667 }
Quaternion complementary filter (fixed-point).
#define VECT3_CROSS_PRODUCT(_vo, _v1, _v2)
Definition: pprz_algebra.h:243
#define AHRS_ACCEL_OMEGA
#define RATES_SDIV(_ro, _ri, _s)
Definition: pprz_algebra.h:385
unsigned short uint16_t
Definition: types.h:16
static void ahrs_int_get_quat_from_accel_mag(struct Int32Quat *q, struct Int32Vect3 *accel, struct Int32Vect3 *mag)
struct AhrsIntCmplQuat ahrs_icq
Default Rate filter Low pass.
void ahrs_icq_update_mag(struct Int32Vect3 *mag, float dt)
angular rates
static void orientationSetQuat_f(struct OrientationReps *orientation, struct FloatQuat *quat)
Set vehicle body attitude from quaternion (float).
#define VECT3_ADD(_a, _b)
Definition: pprz_algebra.h:146
float accel_omega
filter cut-off frequency for correcting the attitude from accels.
static void int32_quat_normalize(struct Int32Quat *q)
normalize a quaternion inplace
static struct Int32RMat * orientationGetRMat_i(struct OrientationReps *orientation)
Get vehicle body attitude rotation matrix (int).
struct OrientationReps body_to_imu
struct Int32Vect3 mag_h
#define INT32_MAG_FRAC
static void int32_vect2_normalize(struct Int32Vect2 *v, uint8_t frac)
normalize 2D vector inplace
#define INT_RATES_LSHIFT(_o, _i, _r)
#define AHRS_ACCEL_ZETA
void int32_quat_comp(struct Int32Quat *a2c, struct Int32Quat *a2b, struct Int32Quat *b2c)
Composition (multiplication) of two quaternions.
#define RATES_ADD(_a, _b)
Definition: pprz_algebra.h:343
#define ACC_FROM_CROSS_FRAC
#define INT_RATES_RSHIFT(_o, _i, _r)
struct Int32Rates rate_correction
uint16_t accel_cnt
number of propagations since last accel update
#define INT32_ANGLE_FRAC
void ahrs_icq_set_body_to_imu(struct OrientationReps *body_to_imu)
#define INT_RATES_ZERO(_e)
#define VECT3_COPY(_a, _b)
Definition: pprz_algebra.h:139
#define AHRS_GRAVITY_HEURISTIC_FACTOR
by default use the gravity heuristic to reduce gain
#define VECT3_DIFF(_c, _a, _b)
Definition: pprz_algebra.h:181
void ahrs_icq_update_gps(struct GpsState *gps_s)
#define VECT3_ASSIGN(_a, _x, _y, _z)
Definition: pprz_algebra.h:124
void int32_quat_integrate_fi(struct Int32Quat *q, struct Int64Quat *hr, struct Int32Rates *omega, int freq)
in place quaternion first order integration with constant rotational velocity.
Paparazzi fixed point trig functions.
#define INT32_VECT3_RSHIFT(_o, _i, _r)
float dt
#define GPS_FIX_3D
3D GPS fix
Definition: gps.h:39
#define RATES_DIFF(_c, _a, _b)
Definition: pprz_algebra.h:371
static void UNUSED ahrs_icq_update_mag_full(struct Int32Vect3 *mag, float dt)
uint8_t gravity_heuristic_factor
sets how strongly the gravity heuristic reduces accel correction.
#define FLOAT_VECT3_NORM(_v)
void ahrs_icq_update_accel(struct Int32Vect3 *accel, float dt)
uint16_t mag_cnt
number of propagations since last mag update
void ahrs_icq_propagate(struct Int32Rates *gyro, float dt)
void ahrs_icq_set_body_to_imu_quat(struct FloatQuat *q_b2i)
#define COMPUTATION_FRAC
Roation quaternion.
#define QUAT_COPY(_qo, _qi)
Definition: pprz_algebra.h:532
int32_t r
in rad/s with INT32_RATE_FRAC
#define PPRZ_ITRIG_SIN(_s, _a)
#define AHRS_BIAS_UPDATE_HEADING_THRESHOLD
don't update gyro bias if heading deviation is above this threshold in degrees
static float heading
Definition: ahrs_infrared.c:45
void int32_rmat_vmult(struct Int32Vect3 *vb, struct Int32RMat *m_a2b, struct Int32Vect3 *va)
rotate 3D vector by rotation matrix.
struct Int32Rates gyro_bias
#define TRIG_BFP_OF_REAL(_tf)
float mag_zeta
filter damping for correcting the gyro bias from magnetometer.
struct Int32Quat ltp_to_imu_quat
#define MAG_BFP_OF_REAL(_af)
void int32_quat_inv_comp_norm_shortest(struct Int32Quat *b2c, struct Int32Quat *a2b, struct Int32Quat *a2c)
Composition (multiplication) of two quaternions with normalization.
data structure for GPS information
Definition: gps.h:81
void ahrs_icq_update_heading(int32_t heading)
Update yaw based on a heading measurement.
void int32_quat_comp_norm_shortest(struct Int32Quat *a2c, struct Int32Quat *a2b, struct Int32Quat *b2c)
Composition (multiplication) of two quaternions with normalization.
Device independent GPS code (interface)
float accel_zeta
filter damping for correcting the gyro-bias from accels.
#define INT32_ANGLE_NORMALIZE(_a)
unsigned long uint32_t
Definition: types.h:18
PRINT_CONFIG_MSG("USE_INS_NAV_INIT defaulting to TRUE")
#define VECT3_SDIV(_vo, _vi, _s)
Definition: pprz_algebra.h:195
static void ahrs_icq_update_mag_2d(struct Int32Vect3 *mag, float dt)
static void int32_quat_identity(struct Int32Quat *q)
initialises a quaternion to identity
void ahrs_icq_init(void)
Ahrs implementation specifc values.
void int32_quat_comp_inv(struct Int32Quat *a2b, struct Int32Quat *a2c, struct Int32Quat *b2c)
Composition (multiplication) of two quaternions.
float mag_omega
filter cut-off frequency for correcting the attitude (heading) from magnetometer. ...
void ahrs_icq_set_accel_gains(void)
update pre-computed inv_kp and inv_ki gains from acc_omega and acc_zeta
static void ahrs_int_get_quat_from_accel(struct Int32Quat *q, struct Int32Vect3 *accel)
#define RATES_SMUL(_ro, _ri, _s)
Definition: pprz_algebra.h:378
#define ACCELS_FLOAT_OF_BFP(_ef, _ei)
Definition: pprz_algebra.h:728
signed long int32_t
Definition: types.h:19
Utility functions for fixed point AHRS implementations.
struct Int64Rates high_rez_bias
#define AHRS_MAG_OMEGA
void ahrs_icq_realign_heading(int32_t heading)
Hard reset yaw to a heading.
#define VECT3_SMUL(_vo, _vi, _s)
Definition: pprz_algebra.h:188
bool correct_gravity
enable gravity vector correction by removing centrifugal acceleration
static struct Int32Quat * orientationGetQuat_i(struct OrientationReps *orientation)
Get vehicle body attitude quaternion (int).
#define RMAT_ELMT(_rm, _row, _col)
Definition: pprz_algebra.h:593
struct Int64Quat high_rez_quat
static int16_t course[3]
Definition: airspeed_uADC.c:57
struct Int32Rates imu_rate
void int32_rmat_transp_vmult(struct Int32Vect3 *vb, struct Int32RMat *m_b2a, struct Int32Vect3 *va)
rotate 3D vector by transposed rotation matrix.
bool ahrs_icq_align(struct Int32Rates *lp_gyro, struct Int32Vect3 *lp_accel, struct Int32Vect3 *lp_mag)
int32_t p
in rad/s with INT32_RATE_FRAC
rotation matrix
static struct OrientationReps body_to_imu
Definition: ins_alt_float.c:93
#define SPEED_BFP_OF_REAL(_af)
#define RATES_COPY(_a, _b)
Definition: pprz_algebra.h:336
void int32_rmat_transp_ratemult(struct Int32Rates *rb, struct Int32RMat *m_b2a, struct Int32Rates *ra)
rotate anglular rates by transposed rotation matrix.
#define AHRS_HEADING_UPDATE_GPS_MIN_SPEED
Minimum speed in m/s for heading update via GPS.
int32_t q
in rad/s with INT32_RATE_FRAC
#define AHRS_MAG_ZETA
Rotation quaternion.
#define FIR_FILTER_SIZE
void ahrs_icq_set_mag_gains(void)
update pre-computed kp and ki gains from mag_omega and mag_zeta
void int32_rmat_of_quat(struct Int32RMat *rm, struct Int32Quat *q)
Convert unit quaternion to rotation matrix.
static struct FloatQuat * orientationGetQuat_f(struct OrientationReps *orientation)
Get vehicle body attitude quaternion (float).
#define PPRZ_ITRIG_COS(_c, _a)
enum AhrsICQStatus status
status of the AHRS, AHRS_ICQ_UNINIT or AHRS_ICQ_RUNNING
Paparazzi fixed point algebra.
#define VECT3_RATES_CROSS_VECT3(_vo, _r1, _v2)
Definition: pprz_algebra.h:253