dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
attitude.c
Go to the documentation of this file.
1 
24 /*
25  * This program is free software; you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation; either version 3 of the License, or
28  * (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful, but
31  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
32  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
33  * for more details.
34  *
35  * You should have received a copy of the GNU General Public License along
36  * with this program; if not, see <http://www.gnu.org/licenses/>
37  */
38 
49 #include "openpilot.h"
50 #include "pios.h"
51 #include "pios_thread.h"
52 #include "pios_queue.h"
53 #include "misc_math.h"
54 #include "physical_constants.h"
55 #include "coordinate_conversions.h"
56 #include "WorldMagModel.h"
57 #include "insgps.h"
58 
59 // UAVOs
60 #include "accels.h"
61 #include "attitudeactual.h"
62 #include "attitudesettings.h"
63 #include "baroaltitude.h"
64 #include "flightstatus.h"
65 #include "gpsposition.h"
66 #include "gpstime.h"
67 #include "gpsvelocity.h"
68 #include "gyros.h"
69 #include "gyrosbias.h"
70 #include "homelocation.h"
71 #include "sensorsettings.h"
72 #include "inssettings.h"
73 #include "insstate.h"
74 #include "magnetometer.h"
75 #include "nedaccel.h"
76 #include "nedposition.h"
77 #include "positionactual.h"
78 #include "stateestimation.h"
79 #include "systemalarms.h"
80 #include "velocityactual.h"
81 
82 // Private constants
83 #define STACK_SIZE_BYTES 2504
84 #define TASK_PRIORITY PIOS_THREAD_PRIO_HIGH
85 #define FAILSAFE_TIMEOUT_MS 10
86 
87 // Private types
88 
89 // Track the initialization state of the complementary filter
95 };
96 
99  uint32_t arming_count;
100 
102  float accel_alpha;
104  float accels_filtered[3];
106  float grot_filtered[3];
109 
116 
118  uint32_t reset_timeval;
119 
122 };
123 
125 struct cfvert {
126  float velocity_z;
127  float position_z;
133  float baro_zero;
134 };
135 
136 // Private variables
137 static struct pios_thread *attitudeTaskHandle;
138 
139 static struct pios_queue *gyroQueue;
140 static struct pios_queue *accelQueue;
141 static struct pios_queue *magQueue;
142 static struct pios_queue *baroQueue;
143 static struct pios_queue *gpsQueue;
144 static struct pios_queue *gpsVelQueue;
145 
146 static AttitudeSettingsData attitudeSettings;
147 static HomeLocationData homeLocation;
148 static INSSettingsData insSettings;
149 static StateEstimationData stateEstimation;
150 static bool gyroBiasSettingsUpdated = false;
151 static volatile bool settings_flag = true;
152 static volatile bool homeloc_flag = true;
153 static volatile bool sensors_flag = true;
155 
156 static const float zeros[3] = {0.0f, 0.0f, 0.0f};
157 
159 static struct cfvert cfvert;
160 
161 static float dT_expected = 0.001f; // assume 1KHz if we don't know.
162 
163 // Private functions
164 static void AttitudeTask(void *parameters);
165 
167 static int32_t setNavigationRaw();
168 
170 static int32_t setNavigationNone();
171 
173 static int32_t updateAttitudeComplementary(float dT, bool first_run, bool secondary, bool raw_gps, bool vel_compass);
175 static int32_t setAttitudeComplementary();
176 
177 static float calc_ned_accel(float *q, float *accels);
178 static void cfvert_reset(struct cfvert *cf, float baro, float time_constant);
179 static void cfvert_predict_pos(struct cfvert *cf, float z_accel, float dt);
180 static void cfvert_update_baro(struct cfvert *cf, float baro, float dt);
181 
183 static int32_t updateAttitudeINSGPS(bool first_run, bool outdoor_mode);
185 static int32_t setAttitudeINSGPS();
187 static int32_t setNavigationINSGPS();
188 static void updateNedAccel();
189 
191 static void apply_accel_filter(const float * raw, float * filtered);
192 static int32_t getNED(GPSPositionData * gpsPosition, float * NED);
193 
195 static void accumulate_gyro_compute();
196 
198 static void accumulate_gyro_zero();
199 
201 static void accumulate_gyro(GyrosData *gyrosData);
202 
204 static void set_state_estimation_error(SystemAlarmsStateEstimationOptions error_code);
205 
207 static void check_home_location();
208 
210 static float T[3];
211 
225 int32_t AttitudeInitialize(void)
226 {
227 
228  if (AttitudeActualInitialize() == -1 \
229  || AttitudeSettingsInitialize() == -1 \
230  || SensorSettingsInitialize() == -1 \
231  || INSSettingsInitialize() == -1 \
232  || INSStateInitialize() == -1 \
233  || NedAccelInitialize() == -1 \
234  || NEDPositionInitialize() == -1 \
235  || PositionActualInitialize() == -1 \
236  || StateEstimationInitialize() == -1 \
237  || VelocityActualInitialize() == -1) {
238 
239  return -1;
240  };
241 
242  // Initialize this here while we aren't setting the homelocation in GPS
243  if (HomeLocationInitialize() == -1) {
244  return -1;
245  }
246 
247  INSSettingsConnectCallbackCtx(UAVObjCbSetFlag, &settings_flag);
248  AttitudeSettingsConnectCallbackCtx(UAVObjCbSetFlag, &settings_flag);
249  StateEstimationConnectCallbackCtx(UAVObjCbSetFlag, &settings_flag);
250  HomeLocationConnectCallbackCtx(UAVObjCbSetFlag, &homeloc_flag);
251  SensorSettingsConnectCallbackCtx(UAVObjCbSetFlag, &sensors_flag);
252 
253  return 0;
254 }
255 
260 int32_t AttitudeStart(void)
261 {
262  // Create the queues for the sensors
263  gyroQueue = PIOS_Queue_Create(1, sizeof(UAVObjEvent));
264  accelQueue = PIOS_Queue_Create(1, sizeof(UAVObjEvent));
265  magQueue = PIOS_Queue_Create(2, sizeof(UAVObjEvent));
266  baroQueue = PIOS_Queue_Create(1, sizeof(UAVObjEvent));
267  gpsQueue = PIOS_Queue_Create(1, sizeof(UAVObjEvent));
268  gpsVelQueue = PIOS_Queue_Create(1, sizeof(UAVObjEvent));
269 
270  // Initialize quaternion
271  AttitudeActualData attitude;
272  AttitudeActualGet(&attitude);
273  attitude.q1 = 1;
274  attitude.q2 = 0;
275  attitude.q3 = 0;
276  attitude.q4 = 0;
277  AttitudeActualSet(&attitude);
278 
279  // Cannot trust the values to init right above if BL runs
280  GyrosBiasData gyrosBias;
281  GyrosBiasGet(&gyrosBias);
282  gyrosBias.x = 0;
283  gyrosBias.y = 0;
284  gyrosBias.z = 0;
285  GyrosBiasSet(&gyrosBias);
286 
287  GyrosConnectQueue(gyroQueue);
288  AccelsConnectQueue(accelQueue);
289  if (MagnetometerHandle())
290  MagnetometerConnectQueue(magQueue);
291  if (BaroAltitudeHandle())
292  BaroAltitudeConnectQueue(baroQueue);
293  if (GPSPositionHandle())
294  GPSPositionConnectQueue(gpsQueue);
295  if (GPSVelocityHandle())
296  GPSVelocityConnectQueue(gpsVelQueue);
297 
298  // Watchdog must be registered before starting task
300 
301  // Start main task
303  TaskMonitorAdd(TASKINFO_RUNNING_ATTITUDE, attitudeTaskHandle);
304 
305  return 0;
306 }
307 
309 
310 
313 static void AttitudeTask(void *parameters)
314 {
315  bool first_run = true;
316  uint32_t last_algorithm;
317  bool last_complementary;
318  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_UNDEFINED);
319 
320  // Wait for all the sensors be to read
321  PIOS_Thread_Sleep(100);
322 
323  // Invalidate previous algorithm to trigger a first run
324  last_algorithm = 0xfffffff;
325  last_complementary = false;
326 
327  uint16_t samp_rate = PIOS_SENSORS_GetSampleRate(PIOS_SENSOR_GYRO);
328 
329  if (samp_rate) {
330  dT_expected = 1.0f / samp_rate;
331  }
332 
333  // Main task loop
334  while (1) {
335  int32_t ret_val = -1;
336 
337  if (settings_flag) {
338  settings_flag = false;
339 
340  INSSettingsGet(&insSettings);
341  // In case INS currently running
342  INSSetMagVar(insSettings.MagVar);
343  INSSetAccelVar(insSettings.AccelVar);
344  INSSetGyroVar(insSettings.GyroVar);
345  INSSetBaroVar(insSettings.BaroVar);
346 
347  AttitudeSettingsGet(&attitudeSettings);
348 
349  // Calculate accel filter alpha, in the same way as for gyro data in stabilization module.
350  if(attitudeSettings.AccelTau < 0.0001f) {
351  complementary_filter_state.accel_alpha = 0; // not trusting this to resolve to 0
353  } else {
356  }
357 
358  StateEstimationGet(&stateEstimation);
359  }
360 
361  if (sensors_flag) {
362  sensors_flag = false;
363 
364  SensorSettingsData sensorSettings;
365  SensorSettingsGet(&sensorSettings);
366 
367  /* When the calibration is updated, update the GyroBias object */
368  GyrosBiasData gyrosBias;
369  gyrosBias.x = 0;
370  gyrosBias.y = 0;
371  gyrosBias.z = 0;
372  GyrosBiasSet(&gyrosBias);
373 
375  }
376 
377  if (homeloc_flag) {
378  uint8_t armed;
379  FlightStatusArmedGet(&armed);
380 
381  // Do not update the home location while armed as this can blow up the
382  // filter. This will need to be overhauled to handle long distance
383  // flights
384  if (armed == FLIGHTSTATUS_ARMED_DISARMED) {
385  homeloc_flag = false;
386 
387  HomeLocationGet(&homeLocation);
388  // Compute matrix to convert deltaLLA to NED
389  float lat, alt;
390  lat = homeLocation.Latitude / 10.0e6f * DEG2RAD;
391  alt = homeLocation.Altitude;
392 
393  T[0] = alt+6.378137E6f;
394  T[1] = cosf(lat)*(alt+6.378137E6f);
395  T[2] = -1.0f;
396 
397  home_location_updated = true;
398  }
399  }
400 
401  // When changing the attitude filter reinitialize
402  if (last_algorithm != stateEstimation.AttitudeFilter) {
403  last_algorithm = stateEstimation.AttitudeFilter;
404  first_run = true;
405  }
406 
407  // Determine if we can set the home location. This is done here to share the stack
408  // space with the INS which is the largest stack on the code.
410 
411  // There are two options to select:
412  // Attitude filter - what sets the attitude
413  // Navigation filter - what sets the position and velocity
414  // If the INS is used for either then it should run
415  bool ins = (stateEstimation.AttitudeFilter == STATEESTIMATION_ATTITUDEFILTER_INSOUTDOOR) ||
416  (stateEstimation.AttitudeFilter == STATEESTIMATION_ATTITUDEFILTER_INSINDOOR) ||
417  (stateEstimation.NavigationFilter == STATEESTIMATION_NAVIGATIONFILTER_INS);
418 
419  // INS outdoor mode when used for navigation OR explicit outdoor attitude
420  bool outdoor = (stateEstimation.AttitudeFilter == STATEESTIMATION_ATTITUDEFILTER_INSOUTDOOR) ||
421  (stateEstimation.NavigationFilter == STATEESTIMATION_NAVIGATIONFILTER_INS);
422 
423  // Complementary filter only needed when used for attitude
424  bool complementary = (stateEstimation.AttitudeFilter == STATEESTIMATION_ATTITUDEFILTER_COMPLEMENTARY) ||
425  (stateEstimation.AttitudeFilter == STATEESTIMATION_ATTITUDEFILTER_COMPLEMENTARYVELCOMPASS);
426 
427  // Update one or both filters
428  if (ins) {
429  ret_val = updateAttitudeINSGPS(first_run, outdoor);
430  if (complementary)
432  first_run || complementary != last_complementary,
433  true, // the secondary filter
434  false, // no raw gps is used
435  stateEstimation.AttitudeFilter == STATEESTIMATION_ATTITUDEFILTER_COMPLEMENTARYVELCOMPASS);
436 
437  } else {
439  first_run,
440  false,
441  stateEstimation.NavigationFilter == STATEESTIMATION_NAVIGATIONFILTER_RAW,
442  stateEstimation.AttitudeFilter == STATEESTIMATION_ATTITUDEFILTER_COMPLEMENTARYVELCOMPASS);
443  }
444 
445  last_complementary = complementary;
446 
447  // Get the requested data
448  // This function blocks on data queue
449  switch (stateEstimation.AttitudeFilter ) {
450  case STATEESTIMATION_ATTITUDEFILTER_COMPLEMENTARY:
451  case STATEESTIMATION_ATTITUDEFILTER_COMPLEMENTARYVELCOMPASS:
453  break;
454  case STATEESTIMATION_ATTITUDEFILTER_INSOUTDOOR:
455  case STATEESTIMATION_ATTITUDEFILTER_INSINDOOR:
457  break;
458  }
459 
460  // Use the selected source for position and velocity
461  switch (stateEstimation.NavigationFilter) {
462  case STATEESTIMATION_NAVIGATIONFILTER_INS:
463  // TODO: When running in dual mode and the INS is not initialized set
464  // an error here
466  break;
467  case STATEESTIMATION_NAVIGATIONFILTER_RAW:
469  break;
470  case STATEESTIMATION_NAVIGATIONFILTER_NONE:
471  default:
473  break;
474  }
475 
476  updateNedAccel();
477 
478  if(ret_val == 0)
479  first_run = false;
480 
482  }
483 }
484 
486 static float cf_q[4];
487 
493 static int32_t updateAttitudeComplementary(float dT, bool first_run, bool secondary, bool raw_gps, bool vel_compass)
494 {
495  UAVObjEvent ev;
496  GyrosData gyrosData;
497  AccelsData accelsData;
498 
499  // If this is the primary estimation filter, wait until the accel and
500  // gyro objects are updated. If it timeouts then go to failsafe.
501  if (!secondary) {
502  bool gyroTimeout = PIOS_Queue_Receive(gyroQueue, &ev, FAILSAFE_TIMEOUT_MS) != true;
503  bool accelTimeout = PIOS_Queue_Receive(accelQueue, &ev, 1) != true;
504 
505  // When one of these is updated so should the other.
506  if (gyroTimeout || accelTimeout) {
507  // Do not set attitude timeout warnings in simulation mode
508  if (!AttitudeActualReadOnly()) {
509  if (gyroTimeout)
510  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_GYROQUEUENOTUPDATING);
511  else if (accelTimeout)
512  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_ACCELEROMETERQUEUENOTUPDATING);
513 
514  return -1;
515  }
516  }
517  }
518 
519  AccelsGet(&accelsData);
520 
521  // When this algorithm is first run force it to a known condition
522  if(first_run) {
523  MagnetometerData magData;
524  magData.x = 100;
525  magData.y = 0;
526  magData.z = 0;
527 
528  // Wait for a mag reading if a magnetometer was registered
529  if (!vel_compass && PIOS_SENSORS_IsRegistered(PIOS_SENSOR_MAG)) {
530  if (!secondary && PIOS_Queue_Receive(magQueue, &ev, 20) != true) {
531  return -1;
532  }
533  MagnetometerGet(&magData);
534  }
535 
536  /* Initialization is tricky. We're using two reference vectors, one which
537  * is the Earth's gravitational field and the other the Earth's magnetic field
538  * While we know that gravity is always almost perfectly straight down, and
539  * thus has trivially large x and y components, the magnetic field has
540  * components in all directions. Toward the equator the Z component is small,
541  * but toward the poles it grows very quickly. For instance, near Boston, USA,
542  * the magnetic field strength is stronger in the Z direction than the X and
543  * Y combined.
544  *
545  * The upshot is that while we can initialize roll and pitch from the gravity
546  * field alone, we cannot get yaw without taking into account the vehicle's
547  * roll and pitch.
548  */
549  float RPY_D[3];
550 
551  float theta = atan2f(accelsData.x, -accelsData.z);
552  RPY_D[1] = theta * RAD2DEG;
553  RPY_D[0] = atan2f(-accelsData.y, -accelsData.z / cosf(theta)) * RAD2DEG;
554 
555  // See above note about why we rotate the magnetic field before continuing.
556  float RPY_R[3] = {RPY_D[0] * DEG2RAD, RPY_D[1] * DEG2RAD, 0};
557  float Rbe[3][3];
558  float mag_body_frame[3] = {magData.x, magData.y, magData.z};
559  float mag_earth_frame[3];
560  Euler2R(RPY_R, Rbe);
561  rot_mult(Rbe, mag_body_frame, mag_earth_frame, true);
562 
563  // Now that we have the magnetic field in the Earth frame, extract yaw.
564  RPY_D[2] = atan2f(-mag_earth_frame[1], mag_earth_frame[0]) * RAD2DEG;
565 
566  // Convert Euler angles into quaternion
567  RPY2Quaternion(RPY_D, cf_q);
568 
571 
573 
574  float baro;
575  BaroAltitudeAltitudeGet(&baro);
576  cfvert_reset(&cfvert, baro, attitudeSettings.VertPositionTau);
577 
578  return 0;
579  }
580 
581  FlightStatusData flightStatus;
582  FlightStatusGet(&flightStatus);
583 
584  float accKp = attitudeSettings.AccKp;
585  float accKi = attitudeSettings.AccKi;
586  float mgKp = attitudeSettings.MgKp;
587  float mgKi = attitudeSettings.MgKi;
588 
589  uint32_t ms_since_reset = PIOS_DELAY_DiffuS(complementary_filter_state.reset_timeval) / 1000;
591  // Wait one second before starting to initialize
593  (ms_since_reset > 1000) ?
594  CF_INITIALIZING :
595  CF_POWERON;
597  (ms_since_reset < 7000) &&
598  (ms_since_reset > 1000)) {
599 
600  // For first 7 seconds use accels to get gyro bias
601  accKp = MIN(accKp, 20 + 20 * (PIOS_Thread_Systime() < 4000));
602  accKi = 15;
603  mgKp = 1;
604 
605  } else if ((attitudeSettings.ZeroDuringArming == ATTITUDESETTINGS_ZERODURINGARMING_TRUE) &&
606  (flightStatus.Armed == FLIGHTSTATUS_ARMED_ARMING)) {
607  // Use a rapidly decrease accelKp to force the attitude to snap back
608  // to level and then converge more smoothly
610  accKp = 50.0f;
611  } else {
612  accKp = MIN(accKp, 50 - complementary_filter_state.arming_count);
613  }
614 
616 
617  // Set the other parameters to drive faster convergence
618  accKi = 1;
619  mgKp = 1;
620 
621  // Don't apply LPF to the accels during arming
623 
624  // Indicate arming so that after arming it reloads
625  // the normal settings
630  }
631 
632  // Reset the filter for barometric data
633  float baro;
634  BaroAltitudeAltitudeGet(&baro);
635  cfvert_reset(&cfvert, baro, attitudeSettings.VertPositionTau);
636 
639 
642 
643  // If arming that means we were accumulating gyro
644  // samples. Compute new bias.
649  }
650 
651  // Indicate normal mode to prevent rerunning this code
653 
654  // Reset the filter for barometric data
655  float baro;
656  BaroAltitudeAltitudeGet(&baro);
657  cfvert_reset(&cfvert, baro, attitudeSettings.VertPositionTau);
658  }
659 
660  GyrosGet(&gyrosData);
661  accumulate_gyro(&gyrosData);
662 
663  float grot[3];
664  float accel_err[3];
665  float *grot_filtered = complementary_filter_state.grot_filtered;
666  float *accels_filtered = complementary_filter_state.accels_filtered;
667 
668  // Apply smoothing to accel values, to reduce vibration noise before main calculations.
669  apply_accel_filter(&accelsData.x,accels_filtered);
670 
671  // Rotate gravity to body frame and cross with accels
672  grot[0] = -(2 * (cf_q[1] * cf_q[3] - cf_q[0] * cf_q[2]));
673  grot[1] = -(2 * (cf_q[2] * cf_q[3] + cf_q[0] * cf_q[1]));
674  grot[2] = -(cf_q[0]*cf_q[0] - cf_q[1]*cf_q[1] - cf_q[2]*cf_q[2] + cf_q[3]*cf_q[3]);
675 
676  // Apply same filtering to the rotated attitude to match delays
677  apply_accel_filter(grot,grot_filtered);
678 
679  // Compute the error between the predicted direction of gravity and smoothed acceleration
680  CrossProduct((const float *) accels_filtered, (const float *) grot_filtered, accel_err);
681 
682  float grot_mag;
684  grot_mag = sqrtf(grot_filtered[0]*grot_filtered[0] + grot_filtered[1]*grot_filtered[1] + grot_filtered[2]*grot_filtered[2]);
685  else
686  grot_mag = 1.0f;
687 
688  // Account for accel magnitude
689  float accel_mag;
690  accel_mag = accels_filtered[0]*accels_filtered[0] + accels_filtered[1]*accels_filtered[1] + accels_filtered[2]*accels_filtered[2];
691  accel_mag = sqrtf(accel_mag);
692  if (grot_mag > 1.0e-3f && accel_mag > 1.0e-3f) {
693  accel_err[0] /= (accel_mag * grot_mag);
694  accel_err[1] /= (accel_mag * grot_mag);
695  accel_err[2] /= (accel_mag * grot_mag);
696  } else {
697  accel_err[0] = 0;
698  accel_err[1] = 0;
699  accel_err[2] = 0;
700  }
701 
702  float mag_err[3];
703  if ((!vel_compass) && (secondary || PIOS_Queue_Receive(magQueue, &ev, 0) == true)) {
704  MagnetometerData mag;
705  MagnetometerGet(&mag);
706 
707  // Only use the magnetometer data if it is good, i.e. not NAN. A NAN would
708  // normally only arise due to a bad magnetometer calibration.
709  if (!(IS_NOT_FINITE(mag.x) || IS_NOT_FINITE(mag.y) || IS_NOT_FINITE(mag.z))) {
710  float bmag = 1.0f;
711  float brot[3];
712  float Rbe[3][3];
713 
714  // Get rotation to bring earth magnetic field into body frame
715  Quaternion2R(cf_q, Rbe);
716 
717  if (homeLocation.Set == HOMELOCATION_SET_TRUE) {
718  rot_mult(Rbe, homeLocation.Be, brot, false);
719  bmag = sqrtf(brot[0] * brot[0] + brot[1] * brot[1] + brot[2] * brot[2]);
720  brot[0] /= bmag;
721  brot[1] /= bmag;
722  brot[2] /= bmag;
723  } else {
724  const float Be[3] = {1.0f, 0.0f, 0.0f};
725  rot_mult(Rbe, Be, brot, false);
726  }
727 
728  float mag_len = sqrtf(mag.x * mag.x + mag.y * mag.y + mag.z * mag.z);
729  mag.x /= mag_len;
730  mag.y /= mag_len;
731  mag.z /= mag_len;
732 
733  // Only compute if neither vector is null
734  if (bmag < 1 || mag_len < 1)
735  mag_err[0] = mag_err[1] = mag_err[2] = 0;
736  else
737  CrossProduct((const float *) &mag.x, (const float *) brot, mag_err);
738 
739  if (mag_err[2] != mag_err[2])
740  mag_err[2] = 0;
741  } else {
742  mag_err[2] = 0;
743  }
744  } else if (vel_compass) {
745  GPSPositionData GpsPosition;
746  GPSPositionGet(&GpsPosition);
747 
748  if ((GpsPosition.Status == GPSPOSITION_STATUS_FIX3D) &&
749  (GpsPosition.Groundspeed > 3.0f)) {
750  float rpy[3];
751 
752  Quaternion2RPY(cf_q, rpy);
753 
754  /* The 0.008 is chosen to put things in a somewhat similar scale
755  * to the cross product. A 45 degree heading error will become
756  * a 7 deg/sec correction, so it neither takes forever to
757  * correct nor does a second of bad heading data screw us up
758  * too badly.
759  */
760  mag_err[2] = circular_modulus_deg(GpsPosition.Heading - rpy[2])
761  * 0.008f;
762  } else {
763  mag_err[2] = 0;
764  }
765  } else {
766  mag_err[2] = 0;
767  }
768 
769  // Accumulate integral of error. Scale here so that units are (deg/s) but Ki has units of s
770  GyrosBiasData gyrosBias;
771  GyrosBiasGet(&gyrosBias);
772  gyrosBias.x -= accel_err[0] * accKi * dT;
773  gyrosBias.y -= accel_err[1] * accKi * dT;
774  gyrosBias.z -= mag_err[2] * mgKi * dT;
775  GyrosBiasSet(&gyrosBias);
776 
777  // Correct rates based on error, integral component dealt with in updateSensors
778  gyrosData.x += accel_err[0] * accKp;
779  gyrosData.y += accel_err[1] * accKp;
780  gyrosData.z += accel_err[2] * accKp + mag_err[2] * mgKp;
781 
782  // Work out time derivative from INSAlgo writeup
783  // Also accounts for the fact that gyros are in deg/s
784  float qdot[4];
785  qdot[0] = (-cf_q[1] * gyrosData.x - cf_q[2] * gyrosData.y - cf_q[3] * gyrosData.z) * dT * DEG2RAD / 2;
786  qdot[1] = (cf_q[0] * gyrosData.x - cf_q[3] * gyrosData.y + cf_q[2] * gyrosData.z) * dT * DEG2RAD / 2;
787  qdot[2] = (cf_q[3] * gyrosData.x + cf_q[0] * gyrosData.y - cf_q[1] * gyrosData.z) * dT * DEG2RAD / 2;
788  qdot[3] = (-cf_q[2] * gyrosData.x + cf_q[1] * gyrosData.y + cf_q[0] * gyrosData.z) * dT * DEG2RAD / 2;
789 
790  // Take a time step
791  cf_q[0] = cf_q[0] + qdot[0];
792  cf_q[1] = cf_q[1] + qdot[1];
793  cf_q[2] = cf_q[2] + qdot[2];
794  cf_q[3] = cf_q[3] + qdot[3];
795 
796  if(cf_q[0] < 0) {
797  cf_q[0] = -cf_q[0];
798  cf_q[1] = -cf_q[1];
799  cf_q[2] = -cf_q[2];
800  cf_q[3] = -cf_q[3];
801  }
802 
803  // Renomalize
804  float qmag;
805  qmag = sqrtf(cf_q[0]*cf_q[0] + cf_q[1]*cf_q[1] + cf_q[2]*cf_q[2] + cf_q[3]*cf_q[3]);
806  cf_q[0] = cf_q[0] / qmag;
807  cf_q[1] = cf_q[1] / qmag;
808  cf_q[2] = cf_q[2] / qmag;
809  cf_q[3] = cf_q[3] / qmag;
810 
811  // If quaternion has become inappropriately short or has become Nan reinit.
812  // THIS SHOULD NEVER ACTUALLY HAPPEN
813  if((fabsf(qmag) < 1.0e-3f) || IS_NOT_FINITE(qmag)) {
814  cf_q[0] = 1;
815  cf_q[1] = 0;
816  cf_q[2] = 0;
817  cf_q[3] = 0;
818  }
819 
820  if (!secondary) {
821  static bool got_baro_pt = false;
822 
823  // Calculate the NED acceleration and get the z-component
824  float z_accel = calc_ned_accel(cf_q, &accelsData.x);
825 
826  // When this is the only filter compute th vertical state from baro data
827  // Reset the filter for barometric data
828  if (PIOS_Queue_Receive(baroQueue, &ev, 0) == true) {
829  float baro;
830  BaroAltitudeAltitudeGet(&baro);
831 
832  cfvert_predict_pos(&cfvert, z_accel, dT);
833  cfvert_update_baro(&cfvert, baro, dT);
834 
835  got_baro_pt = true;
836  } else if (!got_baro_pt) {
837  /* If we've never heard from the baro hold alt at 0 */
838  cfvert.position_z = 0;
839  cfvert.velocity_z = 0;
840  } else {
841  cfvert_predict_pos(&cfvert, z_accel, dT);
842  }
843  }
844 
845  if (!secondary && !raw_gps) {
846  // When in raw GPS mode, it will set the error to none if
847  // reception is good
848  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_NONE);
849  }
850 
851  return 0;
852 }
853 
859 static float calc_ned_accel(float *q, float *accels)
860 {
861  float accel_ned[3];
862  float Rbe[3][3];
863 
864  // rotate the accels into the NED frame and remove
865  // the influence of gravity
866  Quaternion2R(q, Rbe);
867  rot_mult(Rbe, accels, accel_ned, true);
868  accel_ned[2] += GRAVITY;
869 
870  NedAccelData nedAccel;
871  nedAccel.North = accel_ned[0];
872  nedAccel.East = accel_ned[1];
873  nedAccel.Down = accel_ned[2];
874  NedAccelSet(&nedAccel);
875 
876  return accel_ned[2];
877 }
878 
880 static void cfvert_reset(struct cfvert *cf, float baro, float time_constant)
881 {
882  cf->velocity_z = 0;
883  cf->position_z = 0;
884  cf->time_constant_z = time_constant;
885  cf->accel_correction_z = 0;
886  cf->position_base_z = 0;
887  cf->position_error_z = 0;
888  cf->position_correction_z = 0;
889  cf->baro_zero = baro;
890 }
891 
893 static void cfvert_predict_pos(struct cfvert *cf, float z_accel, float dt)
894 {
895  float k1_z = 3 / cf->time_constant_z;
896  float k2_z = 3 / powf(cf->time_constant_z, 2);
897  float k3_z = 1 / powf(cf->time_constant_z, 3);
898 
899  cf->accel_correction_z += cf->position_error_z * k3_z * dt;
900  cf->velocity_z += cf->position_error_z * k2_z * dt;
901  cf->position_correction_z += cf->position_error_z * k1_z * dt;
902 
903  float velocity_increase;
904  velocity_increase = (z_accel + cf->accel_correction_z) * dt;
905  cf->position_base_z += (cf->velocity_z + velocity_increase * 0.5f) * dt;
907  cf->velocity_z += velocity_increase;
908 }
909 
911 static void cfvert_update_baro(struct cfvert *cf, float baro, float dt)
912 {
913  float down = -(baro - cf->baro_zero);
914 
915  // TODO: get from a queue of previous position updates (150 ms latency)
916  float hist_position_base_d = cf->position_base_z;
917 
918  cf->position_error_z = down - (hist_position_base_d + cf->position_correction_z);
919 }
920 
922 static int32_t setNavigationRaw()
923 {
924  UAVObjEvent ev;
925 
926  if (homeLocation.Set == HOMELOCATION_SET_FALSE) {
927  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_NOHOME);
928  PIOS_Queue_Receive(gpsQueue, &ev, 0);
929  } else if (PIOS_Queue_Receive(gpsQueue, &ev, 0) == true) {
930  float NED[3];
931  // Transform the GPS position into NED coordinates
932  GPSPositionData gpsPosition;
933  GPSPositionGet(&gpsPosition);
934 
935  if (gpsPosition.Satellites < 6)
936  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_TOOFEWSATELLITES);
937  else if (gpsPosition.PDOP > 4.0f)
938  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_PDOPTOOHIGH);
939  else
940  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_NONE);
941 
942  getNED(&gpsPosition, NED);
943 
944  NEDPositionData nedPosition;
945  nedPosition.North = NED[0];
946  nedPosition.East = NED[1];
947  nedPosition.Down = NED[2];
948  NEDPositionSet(&nedPosition);
949 
950  PositionActualData positionActual;
951  positionActual.North = NED[0];
952  positionActual.East = NED[1];
953  positionActual.Down = cfvert.position_z;
954  PositionActualSet(&positionActual);
955  } else {
956  PositionActualDownSet(&cfvert.position_z);
957  }
958 
959  if (PIOS_Queue_Receive(gpsVelQueue, &ev, 0) == true) {
960  // Transform the GPS position into NED coordinates
961  GPSVelocityData gpsVelocity;
962  GPSVelocityGet(&gpsVelocity);
963 
964  VelocityActualData velocityActual;
965  velocityActual.North = gpsVelocity.North;
966  velocityActual.East = gpsVelocity.East;
967  velocityActual.Down = cfvert.velocity_z;
968  VelocityActualSet(&velocityActual);
969  } else {
970  VelocityActualDownSet(&cfvert.velocity_z);
971  }
972 
973  return 0;
974 }
975 
977 static int32_t setNavigationNone()
978 {
979  PositionActualDownSet(&cfvert.position_z);
980  VelocityActualDownSet(&cfvert.velocity_z);
981 
982  return 0;
983 }
984 
989 static int32_t setAttitudeComplementary()
990 {
991  AttitudeActualData attitude;
992  quat_copy(cf_q, &attitude.q1);
993  Quaternion2RPY(&attitude.q1,&attitude.Roll);
994  AttitudeActualSet(&attitude);
995 
996  return 0;
997 }
998 
1004 {
1007 
1008  // Accumulate integral of error. Scale here so that units are (deg/s) but Ki has units of s
1009  GyrosBiasData gyrosBias;
1013  GyrosBiasSet(&gyrosBias);
1014 
1016 
1018  }
1019 }
1020 
1025 {
1030 }
1031 
1037 static void accumulate_gyro(GyrosData *gyrosData)
1038 {
1040  return;
1041 
1043 
1044  // bias_correct_gyro
1045  if (true) {
1046  // Apply bias correction to the gyros from the state estimator
1047  GyrosBiasData gyrosBias;
1048  GyrosBiasGet(&gyrosBias);
1049 
1050  complementary_filter_state.accumulated_gyro[0] += gyrosData->x + gyrosBias.x;
1051  complementary_filter_state.accumulated_gyro[1] += gyrosData->y + gyrosBias.y;
1052  complementary_filter_state.accumulated_gyro[2] += gyrosData->z + gyrosBias.z;
1053  } else {
1054  complementary_filter_state.accumulated_gyro[0] += gyrosData->x;
1055  complementary_filter_state.accumulated_gyro[1] += gyrosData->y;
1056  complementary_filter_state.accumulated_gyro[2] += gyrosData->z;
1057  }
1058 }
1059 
1066 static int32_t updateAttitudeINSGPS(bool first_run, bool outdoor_mode)
1067 {
1068  UAVObjEvent ev;
1069  GyrosData gyrosData;
1070  AccelsData accelsData;
1071  MagnetometerData magData;
1072  GPSVelocityData gpsVelData;
1073  GyrosBiasData gyrosBias;
1074 
1075  // These should be static as their values are checked multiple times per update
1076  static BaroAltitudeData baroData;
1077  static GPSPositionData gpsData;
1078 
1079  static bool mag_updated = false;
1080  static bool baro_updated;
1081  static bool gps_updated;
1082  static bool gps_vel_updated;
1083 
1084  static float baro_offset = 0;
1085 
1086  static uint32_t ins_last_time = 0;
1087  static uint32_t ins_init_time = 0;
1088 
1089  static enum {INS_INIT, INS_WARMUP, INS_RUNNING} ins_state;
1090 
1091  float NED[3] = {0.0f, 0.0f, 0.0f};
1092  float vel[3] = {0.0f, 0.0f, 0.0f};
1093 
1094  // Perform the update
1095  uint16_t sensors = 0;
1096  float dT;
1097 
1098  // When the home location is adjusted the filter should be
1099  // reinitialized to correctly offset the baro and make sure it
1100  // does not blow up. This flag should only be set when not armed.
1101  if (first_run || home_location_updated) {
1102  ins_state = INS_INIT;
1103 
1104  mag_updated = false;
1105  baro_updated = false;
1106  gps_updated = false;
1107  gps_vel_updated = false;
1108 
1109  home_location_updated = false;
1110 
1111  ins_last_time = PIOS_DELAY_GetRaw();
1112 
1113  return 0;
1114  }
1115 
1116  mag_updated = mag_updated || PIOS_Queue_Receive(magQueue, &ev, 0);
1117  baro_updated = baro_updated || PIOS_Queue_Receive(baroQueue, &ev, 0);
1118  gps_updated = gps_updated || (PIOS_Queue_Receive(gpsQueue, &ev, 0) && outdoor_mode);
1119  gps_vel_updated = gps_vel_updated || (PIOS_Queue_Receive(gpsVelQueue, &ev, 0) && outdoor_mode);
1120 
1121  // Wait until the gyro and accel object is updated, if a timeout then go to failsafe
1122  if (PIOS_Queue_Receive(gyroQueue, &ev, FAILSAFE_TIMEOUT_MS) != true ||
1123  PIOS_Queue_Receive(accelQueue, &ev, 1) != true)
1124  {
1125  return -1;
1126  }
1127 
1128  // Get most recent data
1129  GyrosGet(&gyrosData);
1130  AccelsGet(&accelsData);
1131  GyrosBiasGet(&gyrosBias);
1132 
1133  // Need to get these values before initializing
1134  if(baro_updated)
1135  BaroAltitudeGet(&baroData);
1136 
1137  if (mag_updated)
1138  MagnetometerGet(&magData);
1139 
1140  if (gps_updated)
1141  GPSPositionGet(&gpsData);
1142 
1143  if (gps_vel_updated)
1144  GPSVelocityGet(&gpsVelData);
1145 
1146  // Discard mag if it has NAN (normally from bad calibration)
1147  mag_updated &= !(IS_NOT_FINITE(magData.x) || IS_NOT_FINITE(magData.y) || IS_NOT_FINITE(magData.z));
1148 
1149  // Indoor mode will fall back to reasonable Be and that is ok. For outdoor make sure home
1150  // Be is set and a good value
1151  mag_updated &= !outdoor_mode || (homeLocation.Be[0] != 0 || homeLocation.Be[1] != 0 || homeLocation.Be[2]);
1152 
1153  // A more stringent requirement for GPS to initialize the filter
1154  bool gps_init_usable = gps_updated && (gpsData.Satellites >= 7) && (gpsData.PDOP <= 3.5f) && (homeLocation.Set == HOMELOCATION_SET_TRUE);
1155 
1156  // Set user-friendly alarms appropriately based on state
1157  if (ins_state == INS_INIT) {
1158  if (!gps_init_usable && outdoor_mode)
1159  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_NOGPS);
1160  else if (!mag_updated)
1161  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_NOMAGNETOMETER);
1162  else if (!baro_updated)
1163  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_NOBAROMETER);
1164  else
1165  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_UNDEFINED);
1166 
1167  } else if (outdoor_mode && (gpsData.Satellites < 6 || gpsData.PDOP > 4.0f)) {
1168  if (gpsData.Satellites < 6)
1169  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_TOOFEWSATELLITES);
1170  else if (gpsData.PDOP > 4.0f)
1171  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_PDOPTOOHIGH);
1172  else if (homeLocation.Set == HOMELOCATION_SET_FALSE)
1173  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_NOHOME);
1174  else
1175  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_UNDEFINED);
1176  } else {
1177  set_state_estimation_error(SYSTEMALARMS_STATEESTIMATION_NONE);
1178  }
1179 
1180  if (ins_state == INS_INIT &&
1181  mag_updated && baro_updated &&
1182  (gps_init_usable || !outdoor_mode)) {
1183 
1184  INSGPSInit();
1185  INSSetMagVar(insSettings.MagVar);
1186  INSSetAccelVar(insSettings.AccelVar);
1187  INSSetGyroVar(insSettings.GyroVar);
1188  INSSetBaroVar(insSettings.BaroVar);
1189  /* This is more optimistic than in the actual flight loop, where
1190  * ublox accuracy data is added. But that seems OK */
1191  INSSetPosVelVar(insSettings.GpsVar[INSSETTINGS_GPSVAR_POS], insSettings.GpsVar[INSSETTINGS_GPSVAR_VEL], insSettings.GpsVar[INSSETTINGS_GPSVAR_VERTPOS]);
1192 
1193  // Initialize the gyro bias from the settings
1194  float gyro_bias[3] = {gyrosBias.x * DEG2RAD, gyrosBias.y * DEG2RAD, gyrosBias.z * DEG2RAD};
1195  INSSetGyroBias(gyro_bias);
1197 
1198  BaroAltitudeGet(&baroData);
1199 
1200  float RPY[3], q[4];
1201  RPY[0] = atan2f(-accelsData.y, -accelsData.z) * RAD2DEG;
1202  RPY[1] = atan2f(accelsData.x, -accelsData.z) * RAD2DEG;
1203  RPY[2] = atan2f(-magData.y, magData.x) * RAD2DEG;
1204  RPY2Quaternion(RPY,q);
1205 
1206  // Don't initialize until all sensors are read
1207  if (!outdoor_mode) {
1208  float pos[3] = {0.0f, 0.0f, 0.0f};
1209 
1210  // Initialize barometric offset to current altitude
1211  baro_offset = -baroData.Altitude;
1212  pos[2] = -(baroData.Altitude + baro_offset);
1213 
1214  if (homeLocation.Set == HOMELOCATION_SET_TRUE &&
1215  (homeLocation.Be[0] != 0 || homeLocation.Be[1] != 0 || homeLocation.Be[2]))
1216  // Use the configured mag, if one is available
1218  else {
1219  // Reasonable default is safe for indoor
1220  float Be[3] = {100,0,500};
1221  INSSetMagNorth(Be);
1222  }
1223 
1224  INSSetState(pos, zeros, q, zeros, zeros);
1225  } else {
1226  float NED[3];
1227 
1229 
1230  // Initialize the gyro bias from the settings
1231  float gyro_bias[3] = {gyrosBias.x * DEG2RAD, gyrosBias.y * DEG2RAD, gyrosBias.z * DEG2RAD};
1232  INSSetGyroBias(gyro_bias);
1233 
1234  // Initialize to current location
1235  getNED(&gpsData, NED);
1236 
1237  // Initialize barometric offset to current GPS NED coordinate
1238  baro_offset = -baroData.Altitude;
1239 
1240  INSSetState(NED, zeros, q, zeros, zeros);
1241  }
1242 
1243  // Once all sensors have been updated and initialized then enter warmup
1244  // state to make sure filter converges
1245  ins_state = INS_WARMUP;
1246 
1247  ins_last_time = PIOS_DELAY_GetRaw();
1248  ins_init_time = ins_last_time;
1249 
1250  return 0;
1251  } else if (ins_state == INS_INIT)
1252  return 0;
1253 
1254  // Keep in warmup for first 10 seconds. This zeros biases.
1255  if (ins_state == INS_WARMUP && PIOS_DELAY_DiffuS(ins_init_time) > 10e6f)
1256  ins_state = INS_RUNNING;
1257 
1258  // Let the filter know when we are armed
1259  uint8_t armed;
1260  FlightStatusArmedGet(&armed);
1261  INSSetArmed (armed == FLIGHTSTATUS_ARMED_ARMED);
1262 
1263 
1264  // Have a minimum requirement for gps usage a little more liberal than during initialization
1265  gps_updated &= (gpsData.Satellites >= 6) && (gpsData.PDOP <= 4.0f) && (homeLocation.Set == HOMELOCATION_SET_TRUE);
1266 
1267  dT = PIOS_DELAY_DiffuS(ins_last_time) / 1.0e6f;
1268  ins_last_time = PIOS_DELAY_GetRaw();
1269 
1270  // This should only happen at start up or at mode switches
1271  if(dT > 0.01f)
1272  dT = 0.01f;
1273  else if(dT <= 0.0005f)
1274  dT = 0.0005f;
1275 
1276  // When the sensor settings are updated, reset the biases. Also
1277  // while warming up, lock these at zero.
1278  if (gyroBiasSettingsUpdated || ins_state == INS_WARMUP) {
1279  gyroBiasSettingsUpdated = false;
1282  }
1283 
1284  // Because the sensor module remove the bias we need to add it
1285  // back in here so that the INS algorithm can track it correctly
1286  // this effectively means the INS is observing the "raw" data.
1287  float gyros[3];
1288  if (attitudeSettings.BiasCorrectGyro == ATTITUDESETTINGS_BIASCORRECTGYRO_TRUE) {
1289  gyros[0] = (gyrosData.x + gyrosBias.x) * DEG2RAD;
1290  gyros[1] = (gyrosData.y + gyrosBias.y) * DEG2RAD;
1291  gyros[2] = (gyrosData.z + gyrosBias.z) * DEG2RAD;
1292  } else {
1293  gyros[0] = gyrosData.x * DEG2RAD;
1294  gyros[1] = gyrosData.y * DEG2RAD;
1295  gyros[2] = gyrosData.z * DEG2RAD;
1296  }
1297 
1298  // Advance the state estimate
1299  INSStatePrediction(gyros, &accelsData.x, dT);
1300 
1301  // Advance the covariance estimate
1303 
1304  if(mag_updated) {
1305  sensors |= MAG_SENSORS;
1306  mag_updated = false;
1307  }
1308 
1309  if(baro_updated) {
1310  sensors |= BARO_SENSOR;
1311  baro_updated = false;
1312  }
1313 
1314  // GPS Position update
1315  if (gps_updated) { // only sets during outdoor mode
1316  sensors |= HORIZ_POS_SENSORS;
1317 
1318  // Transform the GPS position into NED coordinates
1319  getNED(&gpsData, NED);
1320 
1321  // Store this for inspecting offline
1322  NEDPositionData nedPos;
1323  nedPos.North = NED[0];
1324  nedPos.East = NED[1];
1325  nedPos.Down = NED[2];
1326  NEDPositionSet(&nedPos);
1327 
1328  gps_updated = false;
1329  }
1330 
1331  // GPS Velocity update
1332  if (gps_vel_updated) { // only sets during outdoor mode
1333  sensors |= HORIZ_VEL_SENSORS | VERT_VEL_SENSORS;
1334 
1335  vel[0] = gpsVelData.North;
1336  vel[1] = gpsVelData.East;
1337  vel[2] = gpsVelData.Down;
1338 
1339  gps_vel_updated = false;
1340  }
1341 
1342  // If either vel or pos is updated, update the variances.
1343  if (gps_updated || gps_vel_updated) {
1344  // Typical good pos 'accuracy' values are 2.5-3.5. Early
1345  // flight is near 4.0.
1346  // Typical bad pos 'accuracy' values are 10+
1347  // The values here are fudged. Basically, when GPS is
1348  // "good" we'll have a lower variance than the nominal
1349  // setting. But from there it will increase really quickly.
1350  // sqrt(.5) / 3.5 =~ 0.181f
1351  float pos_var = insSettings.GpsVar[INSSETTINGS_GPSVAR_POS] *
1352  (0.6f + powf(gpsData.Accuracy * 0.180f, 2));
1353 
1354  // A good value of speed accuracy in flight is 0.35-0.5.
1355  // sqrt(.5) / .5 =~ 1.414
1356  float speed_var = insSettings.GpsVar[INSSETTINGS_GPSVAR_VEL] *
1357  (0.5f + powf(gpsVelData.Accuracy * 1.414f, 2));
1358 
1359  float v_pos_var = insSettings.GpsVar[INSSETTINGS_GPSVAR_VERTPOS] +
1360  (0.7f + powf(gpsData.Accuracy * 0.167f, 3.0));
1361 
1362  // We trust the vertical much less as accuracy gets worse.
1363  // cuberoot(.3)/4.0 =~ .167
1364 
1365  INSSetPosVelVar(pos_var, speed_var, v_pos_var);
1366  }
1367 
1368  // Update fake position at 10 hz
1369  static uint32_t indoor_pos_time;
1370  if (!outdoor_mode && PIOS_DELAY_DiffuS(indoor_pos_time) > 100000) {
1371  sensors |= HORIZ_VEL_SENSORS | HORIZ_POS_SENSORS;
1372 
1373  indoor_pos_time = PIOS_DELAY_GetRaw();
1374  vel[0] = vel[1] = vel[2] = 0;
1375  NED[0] = NED[1] = 0;
1376  NED[2] = -(baroData.Altitude + baro_offset);
1377  }
1378 
1379  /*
1380  * TODO: Need to add a general sanity check for all the inputs to make sure their kosher
1381  * although probably should occur within INS itself
1382  */
1383  if (sensors)
1384  INSCorrection(&magData.x, NED, vel, ( baroData.Altitude + baro_offset ), sensors);
1385 
1386  // Export the state and variance for monitoring the EKF
1387  INSStateData state;
1388  INSGetVariance(state.Var);
1389  INSGetState(&state.State[0], &state.State[3], &state.State[6], &state.State[10], &state.State[13]);
1390  INSStateSet(&state); // this sets the UAVO
1391 
1392  if (insSettings.ComputeGyroBias == INSSETTINGS_COMPUTEGYROBIAS_FALSE)
1394 
1395  float accel_bias_corrected[3] = {accelsData.x - state.State[13], accelsData.y - state.State[14], accelsData.z - state.State[15]};
1396  calc_ned_accel(&state.State[6], accel_bias_corrected);
1397 
1398  return 0;
1399 }
1400 
1402 static int32_t setAttitudeINSGPS()
1403 {
1404  float gyro_bias[3];
1405  AttitudeActualData attitude;
1406 
1407  INSGetState(NULL, NULL, &attitude.q1, gyro_bias, NULL);
1408  Quaternion2RPY(&attitude.q1,&attitude.Roll);
1409  AttitudeActualSet(&attitude);
1410 
1411  if (insSettings.ComputeGyroBias == INSSETTINGS_COMPUTEGYROBIAS_TRUE &&
1413  // Copy the gyro bias into the UAVO except when it was updated
1414  // from the settings during the calculation, then consume it
1415  // next cycle
1416  GyrosBiasData gyrosBias;
1417  gyrosBias.x = gyro_bias[0] * RAD2DEG;
1418  gyrosBias.y = gyro_bias[1] * RAD2DEG;
1419  gyrosBias.z = gyro_bias[2] * RAD2DEG;
1420  GyrosBiasSet(&gyrosBias);
1421  }
1422 
1423  return 0;
1424 }
1425 
1427 static int32_t setNavigationINSGPS()
1428 {
1429  PositionActualData positionActual;
1430  VelocityActualData velocityActual;
1431 
1432  INSGetState(&positionActual.North, &velocityActual.North, NULL, NULL, NULL);
1433 
1434  PositionActualSet(&positionActual);
1435  VelocityActualSet(&velocityActual);
1436 
1437  return 0;
1438 }
1439 
1440 static void apply_accel_filter(const float * raw, float * filtered)
1441 {
1442  const float alpha = complementary_filter_state.accel_alpha;
1444  filtered[0] = filtered[0] * alpha + raw[0] * (1 - alpha);
1445  filtered[1] = filtered[1] * alpha + raw[1] * (1 - alpha);
1446  filtered[2] = filtered[2] * alpha + raw[2] * (1 - alpha);
1447  } else {
1448  filtered[0] = raw[0];
1449  filtered[1] = raw[1];
1450  filtered[2] = raw[2];
1451  }
1452 }
1453 
1463 static int32_t getNED(GPSPositionData * gpsPosition, float * NED)
1464 {
1465  float dL[3] = {(gpsPosition->Latitude - homeLocation.Latitude) / 10.0e6f * DEG2RAD,
1466  (gpsPosition->Longitude - homeLocation.Longitude) / 10.0e6f * DEG2RAD,
1467  (gpsPosition->Altitude - homeLocation.Altitude)};
1468 
1469  NED[0] = T[0] * dL[0];
1470  NED[1] = T[1] * dL[1];
1471  NED[2] = T[2] * dL[2];
1472 
1473  return 0;
1474 }
1475 
1479 static void updateNedAccel()
1480 {
1481  float accel[3];
1482  float q[4];
1483  float Rbe[3][3];
1484  float accel_ned[3];
1485  const float TAU = 0.95f;
1486 
1487  // Collect downsampled attitude data
1488  AccelsData accels;
1489  AccelsGet(&accels);
1490  accel[0] = accels.x;
1491  accel[1] = accels.y;
1492  accel[2] = accels.z;
1493 
1494  //rotate avg accels into earth frame and store it
1495  AttitudeActualData attitudeActual;
1496  AttitudeActualGet(&attitudeActual);
1497  q[0]=attitudeActual.q1;
1498  q[1]=attitudeActual.q2;
1499  q[2]=attitudeActual.q3;
1500  q[3]=attitudeActual.q4;
1501  Quaternion2R(q, Rbe);
1502  for (uint8_t i = 0; i < 3; i++) {
1503  accel_ned[i] = 0;
1504  for (uint8_t j = 0; j < 3; j++)
1505  accel_ned[i] += Rbe[j][i] * accel[j];
1506  }
1507  accel_ned[2] += GRAVITY;
1508 
1509  NedAccelData accelData;
1510 
1511  /* XXX this is redundant with another set when in INS modes... */
1512  NedAccelGet(&accelData);
1513  accelData.North = accelData.North * TAU + accel_ned[0] * (1 - TAU);
1514  accelData.East = accelData.East * TAU + accel_ned[1] * (1 - TAU);
1515  accelData.Down = accelData.Down * TAU + accel_ned[2] * (1 - TAU);
1516  NedAccelSet(&accelData);
1517 }
1518 
1522 static void check_home_location()
1523 {
1524  // Do not attempt this calculation while armed
1525  uint8_t armed;
1526  FlightStatusArmedGet(&armed);
1527  if (armed != FLIGHTSTATUS_ARMED_DISARMED)
1528  return;
1529 
1530  // Do not calculate if already set
1531  if (homeLocation.Set == HOMELOCATION_SET_TRUE)
1532  return;
1533 
1534  GPSPositionData gps;
1535  GPSPositionGet(&gps);
1536  GPSTimeData gpsTime;
1537  GPSTimeGet(&gpsTime);
1538 
1539  // Check for valid data for the calculation
1540  if (gps.PDOP < 3.5f &&
1541  gps.Satellites >= 7 &&
1542  (gps.Status == GPSPOSITION_STATUS_FIX3D ||
1543  gps.Status == GPSPOSITION_STATUS_DIFF3D) &&
1544  gpsTime.Year >= 2000)
1545  {
1546  // Store LLA
1547  homeLocation.Latitude = gps.Latitude;
1548  homeLocation.Longitude = gps.Longitude;
1549  homeLocation.Altitude = gps.Altitude; // Altitude referenced to mean sea level geoid (likely EGM 1996, but no guarantees)
1550 
1551  // Compute home ECEF coordinates and the rotation matrix into NED
1552  float LLA[3] = { homeLocation.Latitude / 10e6f, homeLocation.Longitude / 10e6f, homeLocation.Altitude };
1553 
1554  // Compute magnetic flux direction at home location
1555  if (WMM_GetMagVector(LLA[0], LLA[1], LLA[2], gpsTime.Month, gpsTime.Day, gpsTime.Year, &homeLocation.Be[0]) >= 0)
1556  { // calculations appeared to go OK
1557 
1558  // Compute local acceleration due to gravity. Vehicles that span a very large
1559  // range of altitude (say, weather balloons) may need to update this during the
1560  // flight.
1561  homeLocation.Set = HOMELOCATION_SET_TRUE;
1562  HomeLocationSet(&homeLocation);
1563  }
1564  }
1565 }
1566 
1571 static void set_state_estimation_error(SystemAlarmsStateEstimationOptions error_code)
1572 {
1573  // Get the severity of the alarm given the error code
1574  SystemAlarmsAlarmOptions severity;
1575  switch (error_code) {
1576  case SYSTEMALARMS_STATEESTIMATION_NONE:
1577  severity = SYSTEMALARMS_ALARM_OK;
1578  break;
1579  case SYSTEMALARMS_STATEESTIMATION_ACCELEROMETERQUEUENOTUPDATING:
1580  case SYSTEMALARMS_STATEESTIMATION_GYROQUEUENOTUPDATING:
1581  severity = SYSTEMALARMS_ALARM_WARNING;
1582  break;
1583  case SYSTEMALARMS_STATEESTIMATION_NOGPS:
1584  case SYSTEMALARMS_STATEESTIMATION_NOMAGNETOMETER:
1585  case SYSTEMALARMS_STATEESTIMATION_NOBAROMETER:
1586  case SYSTEMALARMS_STATEESTIMATION_NOHOME:
1587  case SYSTEMALARMS_STATEESTIMATION_TOOFEWSATELLITES:
1588  case SYSTEMALARMS_STATEESTIMATION_PDOPTOOHIGH:
1589  severity = SYSTEMALARMS_ALARM_ERROR;
1590  break;
1591  case SYSTEMALARMS_STATEESTIMATION_UNDEFINED:
1592  default:
1593  severity = SYSTEMALARMS_ALARM_CRITICAL;
1594  error_code = SYSTEMALARMS_STATEESTIMATION_UNDEFINED;
1595  break;
1596  }
1597 
1598  // Make sure not to set the error code if it didn't change
1599  SystemAlarmsStateEstimationOptions current_error_code;
1600  SystemAlarmsStateEstimationGet((uint8_t *) &current_error_code);
1601  if (current_error_code != error_code) {
1602  SystemAlarmsStateEstimationSet((uint8_t *) &error_code);
1603  }
1604 
1605  // AlarmSet checks only updates on toggle
1606  AlarmsSet(SYSTEMALARMS_ALARM_ATTITUDE, (uint8_t) severity);
1607 }
1608 
#define VERT_VEL_SENSORS
Definition: insgps.h:43
uint32_t arming_count
Track how many cycles the system has been arming to accelerate convergence.
Definition: attitude.c:99
void rot_mult(float R[3][3], const float vec[3], float vec_out[3], bool transpose)
Rotate a vector by a rotation matrix.
void INSGetState(float *pos, float *vel, float *attitude, float *gyro_bias, float *accel_bias)
Get the current state estimate.
uint32_t PIOS_Thread_Systime(void)
Definition: pios_thread.c:212
static AccelsData accelsData
Definition: sensors.c:101
uint32_t PIOS_DELAY_DiffuS(uint32_t raw)
Subtract raw time from now and convert to us.
Definition: pios_delay.c:159
struct _msp_pid_item mag
Definition: msp_messages.h:104
#define PIOS_WDG_ATTITUDE
Definition: pios_wdg.h:37
struct pios_queue * PIOS_Queue_Create(size_t queue_length, size_t item_size)
Definition: pios_queue.c:47
Main PiOS header to include all the compiled in PiOS options.
void Quaternion2R(float q[4], float Rbe[3][3])
float velocity_z
Definition: attitude.c:126
static float calc_ned_accel(float *q, float *accels)
Definition: attitude.c:859
static volatile bool homeloc_flag
Definition: attitude.c:152
void RPY2Quaternion(const float rpy[3], float q[4])
Include file of the INSGPS exposed functionality.
void quat_copy(const float q[4], float qnew[4])
Duplicate a quaternion.
void INSSetAccelBias(const float gyro_bias[3])
static struct GPSGlobals * gps
Definition: gps_airspeed.c:56
static void check_home_location()
Determine if it is safe to set the home location then do it.
Definition: attitude.c:1522
float position_correction_z
Definition: attitude.c:132
static struct pios_queue * gyroQueue
Definition: attitude.c:139
bool PIOS_WDG_RegisterFlag(uint16_t flag_requested)
Register a module against the watchdog.
Definition: pios_wdg.c:86
float grot_filtered[3]
Low pass filtered gravity vector.
Definition: attitude.c:106
void INSGetVariance(float *p)
volatile int j
Definition: loadabletest.c:12
#define HORIZ_VEL_SENSORS
Definition: insgps.h:42
int32_t AttitudeStart(void)
Definition: attitude.c:260
bool PIOS_WDG_UpdateFlag(uint16_t flag)
Function called by modules to indicate they are still running.
Definition: pios_wdg.c:102
int32_t AttitudeInitialize(void)
Definition: attitude.c:225
static StateEstimationData stateEstimation
Definition: attitude.c:149
void INSSetMagVar(const float scaled_mag_var[3])
uint32_t lat
Definition: msp_messages.h:97
void UAVObjCbSetFlag(const UAVObjEvent *objEv, void *ctx, void *obj, int len)
int32_t AlarmsSet(SystemAlarmsAlarmElem alarm, SystemAlarmsAlarmOptions severity)
Definition: alarms.c:97
float accel_correction_z
Definition: attitude.c:129
static struct pios_thread * attitudeTaskHandle
Definition: attitude.c:137
void INSSetPosVelVar(float PosVar, float VelVar, float VertPosVar)
bool PIOS_Queue_Receive(struct pios_queue *queuep, void *itemp, uint32_t timeout_ms)
Definition: pios_queue.c:213
enum complementary_filter_status initialization
Tracks the initialization state of the complementary filter.
Definition: attitude.c:121
static INSSettingsData insSettings
Definition: attitude.c:148
complementary_filter_status
Definition: attitude.c:90
void INSSetGyroBias(const float gyro_bias[3])
uint32_t PIOS_SENSORS_GetSampleRate(enum pios_sensor_type type)
Get the sample rate of a sensor (Hz)
Definition: pios_sensors.c:181
static void updateNedAccel()
Definition: attitude.c:1479
static void accumulate_gyro_zero()
Zero the gyro accumulators.
Definition: attitude.c:1024
static float T[3]
Scales used in NED transform (local tangent plane approx).
Definition: attitude.c:210
static int32_t setNavigationRaw()
Set the navigation information to the raw estimates.
Definition: attitude.c:922
#define TASK_PRIORITY
Definition: attitude.c:84
static int32_t setAttitudeINSGPS()
Set the attitude to the current INSGPS estimate.
Definition: attitude.c:1402
bool accumulating_gyro
Indicate if currently acquiring gyro samples.
Definition: attitude.c:115
static volatile bool settings_flag
Definition: attitude.c:151
static void AttitudeTask(void *parameters)
Definition: attitude.c:313
static void accumulate_gyro_compute()
Compute the mean gyro accumulated and assign the bias.
Definition: attitude.c:1003
#define HORIZ_POS_SENSORS
Definition: insgps.h:40
static int32_t updateAttitudeComplementary(float dT, bool first_run, bool secondary, bool raw_gps, bool vel_compass)
Update the complementary filter attitude estimate.
Definition: attitude.c:493
#define MODULE_HIPRI_INITCALL(ifn, sfn)
Definition: pios_initcall.h:66
void INSStatePrediction(const float gyro_data[3], const float accel_data[3], float dT)
Compute an update of the state estimate.
static bool gyroBiasSettingsUpdated
Definition: attitude.c:150
static int32_t getNED(GPSPositionData *gpsPosition, float *NED)
Convert the GPS LLA position into NED coordinates.
Definition: attitude.c:1463
float circular_modulus_deg(float err)
Circular modulus.
Definition: misc_math.c:62
struct _msp_pid_item alt
Definition: msp_messages.h:99
bool PIOS_SENSORS_IsRegistered(enum pios_sensor_type type)
Checks if a sensor type is registered with the PIOS_SENSORS interface.
Definition: pios_sensors.c:88
static AttitudeSettingsData attitudeSettings
Definition: attitude.c:146
void INSSetMagNorth(const float B[3])
static float cf_q[4]
The complementary filter attitude estimate.
Definition: attitude.c:486
struct _msp_pid_item pos
Definition: msp_messages.h:100
float time_constant_z
Definition: attitude.c:128
Source file for the World Magnetic Model.
void INSSetBaroVar(float baro_var)
static struct PIOS_Sensor sensors[PIOS_SENSOR_NUM]
float position_base_z
Definition: attitude.c:130
static struct pios_queue * magQueue
Definition: attitude.c:141
struct pios_thread * PIOS_Thread_Create(void(*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
Definition: pios_thread.c:89
uint32_t reset_timeval
Store when the function is initialized to time arming and convergence.
Definition: attitude.c:118
uint32_t accumulated_gyro_samples
How many gyro samples were acquired.
Definition: attitude.c:113
bool accel_filter_enabled
If the accelerometer LPF is enabled.
Definition: attitude.c:108
static void cfvert_update_baro(struct cfvert *cf, float baro, float dt)
Update the baro feedback.
Definition: attitude.c:911
static struct pios_queue * gpsVelQueue
Definition: attitude.c:144
int32_t TaskMonitorAdd(TaskInfoRunningElem task, struct pios_thread *threadp)
Definition: taskmonitor.c:67
uint8_t i
Definition: msp_messages.h:97
static HomeLocationData homeLocation
Definition: attitude.c:147
void INSSetState(const float pos[3], const float vel[3], const float q[4], const float gyro_bias[3], const float accel_bias[3])
float baro_zero
Definition: attitude.c:133
static int32_t setNavigationNone()
Provide no navigation updates (indoor flying or without gps)
Definition: attitude.c:977
static void cfvert_predict_pos(struct cfvert *cf, float z_accel, float dt)
Predict the position in the future.
Definition: attitude.c:893
void INSGPSInit()
Reset the internal state variables and variances.
Definition: insgps13state.c:74
static void accumulate_gyro(GyrosData *gyrosData)
Store a gyro sample.
Definition: attitude.c:1037
float accel_alpha
Coefficient for the accelerometer LPF.
Definition: attitude.c:102
#define MAG_SENSORS
Definition: insgps.h:44
float Be[3]
Definition: insgps14state.c:65
void INSCorrection(const float mag_data[3], const float Pos[3], const float Vel[3], float BaroAlt, uint16_t SensorsUsed)
Correct the state and covariance estimate based on the sensors that were updated. ...
void INSSetArmed(bool armed)
Set the current flight state.
void Euler2R(float rpy[3], float Rbe[3][3])
void PIOS_Thread_Sleep(uint32_t time_ms)
Definition: pios_thread.c:229
Header for Coordinate conversions library in coordinate_conversions.c.
static int32_t updateAttitudeINSGPS(bool first_run, bool outdoor_mode)
Update the INSGPS attitude estimate.
Definition: attitude.c:1066
void CrossProduct(const float v1[3], const float v2[3], float result[3])
float position_error_z
Definition: attitude.c:131
void INSCovariancePrediction(float dT)
Compute an update of the state covariance.
tuple f
Definition: px_mkfw.py:81
#define BARO_SENSOR
Definition: insgps.h:45
static struct pios_queue * accelQueue
Definition: attitude.c:140
#define MIN(a, b)
Definition: misc_math.h:41
static volatile bool sensors_flag
Definition: attitude.c:153
Includes PiOS and core architecture components.
static void cfvert_reset(struct cfvert *cf, float baro, float time_constant)
Resets the vertical baro complementary filter and zeros the altitude.
Definition: attitude.c:880
void INSSetGyroVar(const float gyro_var[3])
static const float zeros[3]
Definition: attitude.c:156
static void set_state_estimation_error(SystemAlarmsStateEstimationOptions error_code)
Set alarm and alarm code.
Definition: attitude.c:1571
static struct pios_queue * baroQueue
Definition: attitude.c:142
void INSSetAccelVar(const float accel_var[3])
static int32_t setAttitudeComplementary()
Set the AttitudeActual to the complementary filter estimate.
Definition: attitude.c:989
float accels_filtered[3]
Store the low pass filtered accelerometer.
Definition: attitude.c:104
static void apply_accel_filter(const float *raw, float *filtered)
A low pass filter on the accels which helps with vibration resistance.
Definition: attitude.c:1440
static float dT_expected
Definition: attitude.c:161
static int32_t setNavigationINSGPS()
Set the navigation to the current INSGPS estimate.
Definition: attitude.c:1427
#define STACK_SIZE_BYTES
Definition: attitude.c:83
void Quaternion2RPY(const float q[4], float rpy[3])
int WMM_GetMagVector(float Lat, float Lon, float AltEllipsoid, uint16_t Month, uint16_t Day, uint16_t Year, float B[3])
Definition: WorldMagModel.c:98
Struture that tracks the data for the vertical complementary filter.
Definition: attitude.c:125
static struct pios_queue * gpsQueue
Definition: attitude.c:143
#define FAILSAFE_TIMEOUT_MS
Definition: attitude.c:85
static bool home_location_updated
Definition: attitude.c:154
float accumulated_gyro[3]
The accumulator of gyros during arming.
Definition: attitude.c:111
enum arena_state state
static bool IS_NOT_FINITE(float x)
Definition: misc_math.h:81
struct _msp_pid_item vel
Definition: msp_messages.h:105
uint32_t PIOS_DELAY_GetRaw()
Get the raw delay timer, useful for timing.
Definition: pios_delay.c:153
float position_z
Definition: attitude.c:127