dRonin  adbada4
dRonin GCS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
calibration.cpp
Go to the documentation of this file.
1 
14 /*
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful, but
21  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23  * for more details.
24  *
25  * You should have received a copy of the GNU General Public License along
26  * with this program; if not, see <http://www.gnu.org/licenses/>
27  *
28  * Additional note on redistribution: The copyright and license notices above
29  * must be maintained in each individual source file that is a derivative work
30  * of this source file; otherwise redistribution is prohibited.
31  */
32 
33 #include "calibration.h"
34 
35 #include "physical_constants.h"
36 
37 #include <coreplugin/icore.h>
38 
40 #include <QMainWindow>
41 #include <QMessageBox>
42 #include <QDebug>
43 #include <QThread>
44 #include <QTimer>
45 #include <QEventLoop>
46 #include <QApplication>
47 
48 #include "accels.h"
49 #include "attitudesettings.h"
50 #include "gyros.h"
51 #include "homelocation.h"
52 #include "magnetometer.h"
53 #include "sensorsettings.h"
54 #include "subtrimsettings.h"
55 #include "flighttelemetrystats.h"
56 
57 #include <Eigen/Core>
58 #include <Eigen/Cholesky>
59 #include <Eigen/SVD>
60 #include <Eigen/QR>
61 #include <cstdlib>
62 
63 #define META_OPERATIONS_TIMEOUT 5000
64 
66 
67 #define sign(x) ((x < 0) ? -1 : 1)
68 
70  : calibrateMags(false)
71  , accelLength(GRAVITY)
72  , xCurve(NULL)
73  , yCurve(NULL)
74  , zCurve(NULL)
75 {
76 }
77 
79 
85 void Calibration::initialize(bool calibrateAccels, bool calibrateMags)
86 {
87  this->calibrateAccels = calibrateAccels;
88  this->calibrateMags = calibrateMags;
89 }
90 
97 {
98  if (con) {
99  switch (sensor) {
100  case ACCEL: {
101  Accels *accels = Accels::GetInstance(getObjectManager());
102  Q_ASSERT(accels);
103 
104  assignUpdateRate(accels, SENSOR_UPDATE_PERIOD);
105  connect(accels, &UAVObject::objectUpdated, this, &Calibration::dataUpdated);
106  } break;
107 
108  case MAG: {
109  Magnetometer *mag = Magnetometer::GetInstance(getObjectManager());
110  Q_ASSERT(mag);
111 
112  assignUpdateRate(mag, SENSOR_UPDATE_PERIOD);
113  connect(mag, &UAVObject::objectUpdated, this, &Calibration::dataUpdated);
114  } break;
115 
116  case GYRO: {
117  Gyros *gyros = Gyros::GetInstance(getObjectManager());
118  Q_ASSERT(gyros);
119 
120  assignUpdateRate(gyros, SENSOR_UPDATE_PERIOD);
121  connect(gyros, &UAVObject::objectUpdated, this, &Calibration::dataUpdated);
122  } break;
123  }
124  } else {
125  switch (sensor) {
126  case ACCEL: {
127  Accels *accels = Accels::GetInstance(getObjectManager());
128  Q_ASSERT(accels);
129 
130  disconnect(accels, &UAVObject::objectUpdated, this, &Calibration::dataUpdated);
131  } break;
132 
133  case MAG: {
134  Magnetometer *mag = Magnetometer::GetInstance(getObjectManager());
135  Q_ASSERT(mag);
136 
137  disconnect(mag, &UAVObject::objectUpdated, this, &Calibration::dataUpdated);
138  } break;
139 
140  case GYRO: {
141  Gyros *gyros = Gyros::GetInstance(getObjectManager());
142  Q_ASSERT(gyros);
143 
144  disconnect(gyros, &UAVObject::objectUpdated, this, &Calibration::dataUpdated);
145  } break;
146  }
147  }
148 }
149 
156 void Calibration::assignUpdateRate(UAVObject *obj, quint32 updatePeriod)
157 {
158  UAVDataObject *dobj = dynamic_cast<UAVDataObject *>(obj);
159  Q_ASSERT(dobj);
160  UAVObject::Metadata mdata = obj->getMetadata();
162  mdata.flightTelemetryUpdatePeriod = static_cast<quint16>(updatePeriod);
163  QEventLoop loop;
164  QTimer::singleShot(META_OPERATIONS_TIMEOUT, &loop, &QEventLoop::quit);
165  connect(dobj->getMetaObject(),
166  QOverload<UAVObject *, bool>::of(&UAVDataObject::transactionCompleted), &loop,
167  &QEventLoop::quit);
168  // Show the UI is blocking
169  emit calibrationBusy(true);
170  obj->setMetadata(mdata);
171  obj->updated();
172  loop.exec();
173  emit calibrationBusy(false);
174 }
175 
182 void Calibration::dataUpdated(UAVObject *obj)
183 {
184 
185  if (!timer.isActive()) {
186  // ignore updates that come in after the timer has expired
187  return;
188  }
189 
190  switch (calibration_state) {
191  case IDLE:
192  case SIX_POINT_WAIT1:
193  case SIX_POINT_WAIT2:
194  case SIX_POINT_WAIT3:
195  case SIX_POINT_WAIT4:
196  case SIX_POINT_WAIT5:
197  case SIX_POINT_WAIT6:
198  // Do nothing
199  return;
200  case YAW_ORIENTATION:
201  // Store data while computing the yaw orientation
202  // and if completed go back to the idle state
204  // Disconnect sensors and reset metadata
205  connectSensor(ACCEL, false);
206  setMetadata(originalMetaData);
207 
208  calibration_state = IDLE;
209 
210  emit showYawOrientationMessage(tr("Orientation computed"));
211  emit toggleControls(true);
213  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
214  }
215  break;
216  case LEVELING:
217  // Store data while computing the level attitude
218  // and if completed go back to the idle state
219  if (storeLevelingMeasurement(obj)) {
220  // Disconnect sensors and reset metadata
221  connectSensor(GYRO, false);
222  connectSensor(ACCEL, false);
223  setMetadata(originalMetaData);
224 
225  calibration_state = IDLE;
226 
227  emit showLevelingMessage(tr("Level computed"));
228  emit toggleControls(true);
229  emit levelingProgressChanged(0);
230  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
231  }
232  break;
233  case SIX_POINT_COLLECT1:
234  // These state collect each position for six point calibration and
235  // when enough data is acquired advance to the next step
236  if (storeSixPointMeasurement(obj, 1)) {
237  // If all the data is collected advance to the next position
238  calibration_state = SIX_POINT_WAIT2;
239  emit showSixPointMessage(tr("Rotate left side down and press Save Position..."));
240  emit toggleSavePosition(true);
241  emit updatePlane(2);
242  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
243  }
244  break;
245  case SIX_POINT_COLLECT2:
246  if (storeSixPointMeasurement(obj, 2)) {
247  // If all the data is collected advance to the next position
248  calibration_state = SIX_POINT_WAIT3;
249  emit showSixPointMessage(tr("Rotate upside down and press Save Position..."));
250  emit toggleSavePosition(true);
251  emit updatePlane(3);
252  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
253  }
254  break;
255  case SIX_POINT_COLLECT3:
256  if (storeSixPointMeasurement(obj, 3)) {
257  // If all the data is collected advance to the next position
258  calibration_state = SIX_POINT_WAIT4;
259  emit showSixPointMessage(tr("Point right side down and press Save Position..."));
260  emit toggleSavePosition(true);
261  emit updatePlane(4);
262  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
263  }
264  break;
265  case SIX_POINT_COLLECT4:
266  if (storeSixPointMeasurement(obj, 4)) {
267  // If all the data is collected advance to the next position
268  calibration_state = SIX_POINT_WAIT5;
269  emit showSixPointMessage(tr("Point nose up and press Save Position..."));
270  emit toggleSavePosition(true);
271  emit updatePlane(5);
272  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
273  }
274  break;
275  case SIX_POINT_COLLECT5:
276  if (storeSixPointMeasurement(obj, 5)) {
277  // If all the data is collected advance to the next position
278  calibration_state = SIX_POINT_WAIT6;
279  emit showSixPointMessage(tr("Point nose down and press Save Position..."));
280  emit toggleSavePosition(true);
281  emit updatePlane(6);
282  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
283  }
284  break;
285  case SIX_POINT_COLLECT6:
286  // Store data points in the final position and if enough data
287  // has been computed attempt to calculate the scale and bias
288  // for the accel and optionally the mag.
289  if (storeSixPointMeasurement(obj, 6)) {
290  // Disconnect signals and set to IDLE before resetting
291  // the meta data to prevent coming here multiple times
292 
293  calibration_state = IDLE;
294  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
295 
296  // All data collected. Disconnect and reset all UAVOs, and compute value
297  connectSensor(GYRO, false);
298  if (calibrateAccels)
299  connectSensor(ACCEL, false);
300  if (calibrateMags)
301  connectSensor(MAG, false);
302  setMetadata(originalMetaData);
303 
304  emit toggleControls(true);
305  emit updatePlane(0);
306  emit sixPointProgressChanged(0);
307 
308  // Do calculation
309  int ret = computeScaleBias();
310  if (ret == CALIBRATION_SUCCESS) {
311  // Load calibration results
312  SensorSettings *sensorSettings = SensorSettings::GetInstance(getObjectManager());
313  SensorSettings::DataFields sensorSettingsData = sensorSettings->getData();
314 
315  // Generate result messages
316  QString accelCalibrationResults = "";
317  QString magCalibrationResults = "";
318  if (calibrateAccels == true) {
319  accelCalibrationResults =
320  QString(tr("Accelerometer bias, in [m/s^2]: x=%1, y=%2, z=%3\n"))
321  .arg(sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_X], -9)
322  .arg(sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Y], -9)
323  .arg(sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Z], -9)
324  + QString(tr("Accelerometer scale, in [-]: x=%1, y=%2, z=%3\n"))
325  .arg(sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_X], -9)
326  .arg(sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Y], -9)
327  .arg(sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Z], -9);
328  }
329  if (calibrateMags == true) {
330  magCalibrationResults =
331  QString(tr("Magnetometer bias, in [mG]: x=%1, y=%2, z=%3\n"))
332  .arg(sensorSettingsData.MagBias[SensorSettings::MAGBIAS_X], -9)
333  .arg(sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Y], -9)
334  .arg(sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Z], -9)
335  + QString(tr("Magnetometer scale, in [-]: x=%4, y=%5, z=%6"))
336  .arg(sensorSettingsData.MagScale[SensorSettings::MAGSCALE_X], -9)
337  .arg(sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Y], -9)
338  .arg(sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Z], -9);
339  }
340 
341  // Emit signal containing calibration success message
342  emit showSixPointMessage(QString(tr("Calibration succeeded")) + QString("\n")
343  + accelCalibrationResults + QString("\n")
344  + magCalibrationResults);
345  } else {
346  // Return sensor calibration values to their original settings
348 
349  if (ret == ACCELEROMETER_FAILED) {
350  emit showSixPointMessage(
351  tr("Acceleromter calibration failed. Original values have been written "
352  "back to device. Perhaps you moved too much during the calculation? "
353  "Please repeat calibration."));
354  } else if (ret == MAGNETOMETER_FAILED) {
355  emit showSixPointMessage(
356  tr("Magnetometer calibration failed. Original values have been written "
357  "back to device. Perhaps you performed the calibration near iron? "
358  "Please repeat calibration."));
359  }
360  }
361  }
362  break;
363  case GYRO_TEMP_CAL:
364  if (storeTempCalMeasurement(obj)) {
365  // Disconnect and reset data and metadata
366  connectSensor(GYRO, false);
368  setMetadata(originalMetaData);
369 
370  calibration_state = IDLE;
371  emit toggleControls(true);
372 
373  int ret = computeTempCal();
374  if (ret == CALIBRATION_SUCCESS) {
375  emit showTempCalMessage(tr("Temperature compensation calibration succeeded"));
376  } else {
377  emit showTempCalMessage(tr("Temperature compensation calibration succeeded"));
378  }
379  }
380  }
381 }
382 
387 void Calibration::timeout()
388 {
389  // Reset metadata update rates
390  setMetadata(originalMetaData);
391 
392  switch (calibration_state) {
393  case IDLE:
394  // Do nothing
395  return;
396  case YAW_ORIENTATION:
397  // Disconnect appropriate sensors
398  connectSensor(ACCEL, false);
399  calibration_state = IDLE;
400  emit showYawOrientationMessage(tr("Orientation timed out ..."));
402  break;
403  case LEVELING:
404  // Disconnect appropriate sensors
405  connectSensor(GYRO, false);
406  connectSensor(ACCEL, false);
407  calibration_state = IDLE;
408  emit showLevelingMessage(tr("Leveling timed out ..."));
409  emit levelingProgressChanged(0);
410  break;
411  case SIX_POINT_WAIT1:
412  case SIX_POINT_WAIT2:
413  case SIX_POINT_WAIT3:
414  case SIX_POINT_WAIT4:
415  case SIX_POINT_WAIT5:
416  case SIX_POINT_WAIT6:
417  // Do nothing, shouldn't happen
418  return;
419  case SIX_POINT_COLLECT1:
420  case SIX_POINT_COLLECT2:
421  case SIX_POINT_COLLECT3:
422  case SIX_POINT_COLLECT4:
423  case SIX_POINT_COLLECT5:
424  case SIX_POINT_COLLECT6:
425  connectSensor(GYRO, false);
426  if (calibrateAccels)
427  connectSensor(ACCEL, false);
428  if (calibrateMags)
429  connectSensor(MAG, false);
430  calibration_state = IDLE;
431  emit showSixPointMessage(tr("Six point data collection timed out"));
432  emit sixPointProgressChanged(0);
433  break;
434  case GYRO_TEMP_CAL:
435  connectSensor(GYRO, false);
436  calibration_state = IDLE;
437  emit showTempCalMessage(tr("Temperature calibration timed out"));
438  emit tempCalProgressChanged(0);
439  break;
440  }
441 
442  emit updatePlane(0);
443  emit toggleControls(true);
444 
445  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
446 
447  (void)QMessageBox::information(dynamic_cast<QWidget *>(Core::ICore::instance()->mainWindow()),
448  tr("Calibration failed"),
449  tr("Calibration timed out before receiving required updates."));
450 }
451 
456 {
457  accel_accum_x.clear();
458  accel_accum_y.clear();
459  accel_accum_z.clear();
460 
461  calibration_state = YAW_ORIENTATION;
462 
463  // Update sensor rates
464  originalMetaData = getObjectUtilManager()->readAllNonSettingsMetadata();
465  connectSensor(GYRO, false);
466  connectSensor(MAG, false);
467  connectSensor(ACCEL, true);
468 
469  emit toggleControls(false);
471  tr("Pitch vehicle forward approximately 30 degrees. Ensure it absolutely does not roll"));
472 
473  // Set up timeout timer
474  timer.setSingleShot(true);
475  timer.start(8000 + (NUM_SENSOR_UPDATES_YAW_ORIENTATION * SENSOR_UPDATE_PERIOD));
476  connect(&timer, &QTimer::timeout, this, &Calibration::timeout);
477 }
478 
481 {
482  zeroVertical = true;
483  doStartLeveling();
484 }
485 
488 {
489  zeroVertical = false;
490  doStartLeveling();
491 }
492 
496 void Calibration::doStartLeveling()
497 {
498  accel_accum_x.clear();
499  accel_accum_y.clear();
500  accel_accum_z.clear();
501  gyro_accum_x.clear();
502  gyro_accum_y.clear();
503  gyro_accum_z.clear();
504  gyro_accum_temp.clear();
505 
506  // Disable gyro bias correction to see raw data
507  AttitudeSettings *attitudeSettings = AttitudeSettings::GetInstance(getObjectManager());
508  Q_ASSERT(attitudeSettings);
509  AttitudeSettings::DataFields attitudeSettingsData = attitudeSettings->getData();
510  attitudeSettingsData.BiasCorrectGyro = AttitudeSettings::BIASCORRECTGYRO_FALSE;
511  attitudeSettings->setData(attitudeSettingsData);
512  attitudeSettings->updated();
513 
514  calibration_state = LEVELING;
515 
516  // Connect to the sensor updates and set higher rates
517  originalMetaData = getObjectUtilManager()->readAllNonSettingsMetadata();
518  connectSensor(MAG, false);
519  connectSensor(ACCEL, true);
520  connectSensor(GYRO, true);
521 
522  emit toggleControls(false);
523  emit showLevelingMessage(tr("Leave vehicle flat"));
524 
525  // Set up timeout timer
526  timer.setSingleShot(true);
527  timer.start(8000 + (NUM_SENSOR_UPDATES_LEVELING * SENSOR_UPDATE_PERIOD));
528  connect(&timer, &QTimer::timeout, this, &Calibration::timeout);
529 }
530 
538 {
539  // Save initial sensor settings
540  SensorSettings *sensorSettings = SensorSettings::GetInstance(getObjectManager());
541  Q_ASSERT(sensorSettings);
542  SensorSettings::DataFields sensorSettingsData = sensorSettings->getData();
543 
544  // Compute the board rotation matrix, so we can undo the rotation
545  AttitudeSettings *attitudeSettings = AttitudeSettings::GetInstance(getObjectManager());
546  Q_ASSERT(attitudeSettings);
547  AttitudeSettings::DataFields attitudeSettingsData = attitudeSettings->getData();
548  double rpy[3] = {
549  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_ROLL] * DEG2RAD / 100.0,
550  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_PITCH] * DEG2RAD / 100.0,
551  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_YAW] * DEG2RAD / 100.0
552  };
553  Euler2R(rpy, boardRotationMatrix);
554 
555  // If calibrating the accelerometer, remove any scaling
556  if (calibrateAccels) {
557  initialAccelsScale[0] = sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_X];
558  initialAccelsScale[1] = sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Y];
559  initialAccelsScale[2] = sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Z];
560  initialAccelsBias[0] = sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_X];
561  initialAccelsBias[1] = sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Y];
562  initialAccelsBias[2] = sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Z];
563 
564  // Reset the scale and bias to get a correct result
565  sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_X] = 1.0;
566  sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Y] = 1.0;
567  sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Z] = 1.0;
568  sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_X] = 0.0;
569  sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Y] = 0.0;
570  sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Z] = 0.0;
571  sensorSettingsData.ZAccelOffset = 0.0;
572  }
573 
574  // If calibrating the magnetometer, remove any scaling
575  if (calibrateMags) {
576  initialMagsScale[0] = sensorSettingsData.MagScale[SensorSettings::MAGSCALE_X];
577  initialMagsScale[1] = sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Y];
578  initialMagsScale[2] = sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Z];
579  initialMagsBias[0] = sensorSettingsData.MagBias[SensorSettings::MAGBIAS_X];
580  initialMagsBias[1] = sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Y];
581  initialMagsBias[2] = sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Z];
582 
583  // Reset the scale to get a correct result
584  sensorSettingsData.MagScale[SensorSettings::MAGSCALE_X] = 1;
585  sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Y] = 1;
586  sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Z] = 1;
587  sensorSettingsData.MagBias[SensorSettings::MAGBIAS_X] = 0;
588  sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Y] = 0;
589  sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Z] = 0;
590  }
591 
592  sensorSettings->setData(sensorSettingsData);
593 
594  // Clear the accumulators
595  accel_accum_x.clear();
596  accel_accum_y.clear();
597  accel_accum_z.clear();
598  mag_accum_x.clear();
599  mag_accum_y.clear();
600  mag_accum_z.clear();
601 
602  // TODO: Document why the thread needs to wait 100ms.
603  QThread::usleep(100000);
604 
605  connectSensor(ACCEL, false);
606  connectSensor(GYRO, false);
607  connectSensor(MAG, false);
608 
609  // Connect sensors and set higher update rate
610  if (calibrateAccels)
611  connectSensor(ACCEL, true);
612  if (calibrateMags)
613  connectSensor(MAG, true);
614 
615  // Set new metadata
616 
617  // Show UI parts and update the calibration state
618  emit showSixPointMessage(tr("Place horizontally and click save position..."));
619  emit updatePlane(1);
620  emit toggleControls(false);
621  emit toggleSavePosition(true);
622  calibration_state = SIX_POINT_WAIT1;
623 }
624 
630 {
631  // Return sensor calibration values to their original settings
633 
634  // Disconnect sensors and reset UAVO update rates
635  if (calibrateAccels)
636  connectSensor(ACCEL, false);
637  if (calibrateMags)
638  connectSensor(MAG, false);
639 
640  setMetadata(originalMetaData);
641 
642  calibration_state = IDLE;
643  emit toggleControls(true);
644  emit toggleSavePosition(false);
645  emit updatePlane(0);
646  emit sixPointProgressChanged(0);
647  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
648 
649  emit showSixPointMessage(tr("Calibration canceled."));
650 }
651 
656 {
657  switch (calibration_state) {
658  case SIX_POINT_WAIT1:
659  emit showSixPointMessage(tr("Hold..."));
660  emit toggleControls(false);
661  calibration_state = SIX_POINT_COLLECT1;
662  break;
663  case SIX_POINT_WAIT2:
664  emit showSixPointMessage(tr("Hold..."));
665  emit toggleControls(false);
666  calibration_state = SIX_POINT_COLLECT2;
667  break;
668  case SIX_POINT_WAIT3:
669  emit showSixPointMessage(tr("Hold..."));
670  emit toggleControls(false);
671  calibration_state = SIX_POINT_COLLECT3;
672  break;
673  case SIX_POINT_WAIT4:
674  emit showSixPointMessage(tr("Hold..."));
675  emit toggleControls(false);
676  calibration_state = SIX_POINT_COLLECT4;
677  break;
678  case SIX_POINT_WAIT5:
679  emit showSixPointMessage(tr("Hold..."));
680  emit toggleControls(false);
681  calibration_state = SIX_POINT_COLLECT5;
682  break;
683  case SIX_POINT_WAIT6:
684  emit showSixPointMessage(tr("Hold..."));
685  emit toggleControls(false);
686  calibration_state = SIX_POINT_COLLECT6;
687  break;
688  default:
689  return;
690  }
691 
692  emit toggleSavePosition(false);
693 
694  // Set up timeout timer
695  timer.setSingleShot(true);
696  timer.start(8000 + (NUM_SENSOR_UPDATES_SIX_POINT * SENSOR_UPDATE_PERIOD));
697  connect(&timer, &QTimer::timeout, this, &Calibration::timeout);
698 }
699 
704 {
705  gyro_accum_x.clear();
706  gyro_accum_y.clear();
707  gyro_accum_z.clear();
708  gyro_accum_temp.clear();
709 
710  // Disable gyro sensor bias correction to see raw data
711  AttitudeSettings *attitudeSettings = AttitudeSettings::GetInstance(getObjectManager());
712  Q_ASSERT(attitudeSettings);
713  AttitudeSettings::DataFields attitudeSettingsData = attitudeSettings->getData();
714  attitudeSettingsData.BiasCorrectGyro = AttitudeSettings::BIASCORRECTGYRO_FALSE;
715 
716  attitudeSettings->setData(attitudeSettingsData);
717  attitudeSettings->updated();
718 
719  // compute board rotation matrix
720  double rpy[3] = {
721  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_ROLL] * DEG2RAD / 100.0,
722  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_PITCH] * DEG2RAD / 100.0,
723  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_YAW] * DEG2RAD / 100.0
724  };
725  Euler2R(rpy, boardRotationMatrix);
726 
727  calibration_state = GYRO_TEMP_CAL;
728 
729  // Set up the data rates
730  originalMetaData = getObjectUtilManager()->readAllNonSettingsMetadata();
731  connectSensor(GYRO, true);
732 
733  emit toggleControls(false);
734  emit showTempCalMessage(tr("Leave board flat and very still while it changes temperature"));
735  emit tempCalProgressChanged(0);
736 
737  // Set up timeout timer
738  timer.setSingleShot(true);
739  timer.start(1800000);
740  connect(&timer, &QTimer::timeout, this, &Calibration::timeout);
741 }
742 
747 {
748  if (calibration_state == GYRO_TEMP_CAL) {
749  qDebug() << "Accepting";
750  // Disconnect sensor and reset UAVO update rates
751  connectSensor(GYRO, false);
752  setMetadata(originalMetaData);
753 
754  calibration_state = IDLE;
755  emit showTempCalMessage(tr("Temperature calibration accepted"));
756  emit tempCalProgressChanged(0);
757  emit toggleControls(true);
758 
759  timer.stop();
760  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
761 
762  computeTempCal();
763  }
764 }
765 
770 {
771  if (calibration_state == GYRO_TEMP_CAL) {
772  qDebug() << "Canceling";
773  // Disconnect sensor and reset UAVO update rates
774  connectSensor(GYRO, false);
775  setMetadata(originalMetaData);
776 
777  // Reenable gyro bias correction
778  AttitudeSettings *attitudeSettings = AttitudeSettings::GetInstance(getObjectManager());
779  Q_ASSERT(attitudeSettings);
780  AttitudeSettings::DataFields attitudeSettingsData = attitudeSettings->getData();
781  attitudeSettings->setData(attitudeSettingsData);
782  attitudeSettings->updated();
783  QThread::usleep(100000); // Sleep 100ms to make sure the new settings values are sent
784 
785  // Reset all sensor values
787 
788  calibration_state = IDLE;
789  emit showTempCalMessage(tr("Temperature calibration timed out"));
790  emit tempCalProgressChanged(0);
791  emit toggleControls(true);
792 
793  timer.stop();
794  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
795  }
796 }
797 
804 {
805  min_temperature_range = r;
806 }
807 
814 {
815  Accels *accels = Accels::GetInstance(getObjectManager());
816 
817  // Accumulate samples until we have _at least_ NUM_SENSOR_UPDATES_YAW_ORIENTATION samples
818  if (obj->getObjID() == Accels::OBJID) {
819  Accels::DataFields accelsData = accels->getData();
820  accel_accum_x.append(accelsData.x);
821  accel_accum_y.append(accelsData.y);
822  accel_accum_z.append(accelsData.z);
823  }
824 
825  // update the progress indicator
826  emit yawOrientationProgressChanged((100 * accel_accum_x.size())
827  / NUM_SENSOR_UPDATES_YAW_ORIENTATION);
828 
829  // If we have enough samples, then stop sampling and compute the biases
830  if (accel_accum_x.size() >= NUM_SENSOR_UPDATES_YAW_ORIENTATION) {
831  timer.stop();
832  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
833 
834  // Get the existing attitude settings
835  AttitudeSettings *attitudeSettings = AttitudeSettings::GetInstance(getObjectManager());
836  AttitudeSettings::DataFields attitudeSettingsData = attitudeSettings->getData();
837 
838  // Use sensor data without rotation, as it has already been rotated on-board.
839  double a_body[3] = { listMean(accel_accum_x), listMean(accel_accum_y),
840  listMean(accel_accum_z) };
841 
842  // Temporary variable
843  double psi;
844 
845  // Solve "a_sensor = Rot(phi, theta, psi) *[0;0;-g]" for the roll (phi) and pitch (theta)
846  // values.
847  // Recall that phi is in [-pi, pi] and theta is in [-pi/2, pi/2]
848  psi = atan2(a_body[1], -a_body[0]);
849 
850  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_YAW] +=
851  psi * RAD2DEG * 100.0; // Scale by 100 because units are [100*deg]
852 
853  // Wrap to [-pi, pi]
854  while (attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_YAW]
855  > 180 * 100) // Scale by 100 because units are [100*deg]
856  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_YAW] -= 360 * 100;
857  while (attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_YAW] < -180 * 100)
858  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_YAW] += 360 * 100;
859 
860  attitudeSettings->setData(attitudeSettingsData);
861  attitudeSettings->updated();
862 
863  // Inform the system that the calibration process has completed
864  emit calibrationCompleted();
865 
866  return true;
867  }
868  return false;
869 }
870 
877 {
878  Accels *accels = Accels::GetInstance(getObjectManager());
879  Gyros *gyros = Gyros::GetInstance(getObjectManager());
880 
881  // Accumulate samples until we have _at least_ NUM_SENSOR_UPDATES_LEVELING samples
882  if (obj->getObjID() == Accels::OBJID) {
883  Accels::DataFields accelsData = accels->getData();
884  accel_accum_x.append(accelsData.x);
885  accel_accum_y.append(accelsData.y);
886  accel_accum_z.append(accelsData.z);
887  } else if (obj->getObjID() == Gyros::OBJID) {
888  Gyros::DataFields gyrosData = gyros->getData();
889  gyro_accum_x.append(gyrosData.x);
890  gyro_accum_y.append(gyrosData.y);
891  gyro_accum_z.append(gyrosData.z);
892  gyro_accum_temp.append(gyrosData.temperature);
893  }
894 
895  // update the progress indicator
896  emit levelingProgressChanged((100 * qMin(accel_accum_x.size(), gyro_accum_x.size()))
897  / NUM_SENSOR_UPDATES_LEVELING);
898 
899  // If we have enough samples, then stop sampling and compute the biases
900  if (accel_accum_x.size() >= NUM_SENSOR_UPDATES_LEVELING
901  && gyro_accum_x.size() >= NUM_SENSOR_UPDATES_LEVELING) {
902  timer.stop();
903  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
904 
905  double x_gyro_bias = listMean(gyro_accum_x);
906  double y_gyro_bias = listMean(gyro_accum_y);
907  double z_gyro_bias = listMean(gyro_accum_z);
908  double temp = listMean(gyro_accum_temp);
909 
910  // Get the existing attitude settings
911  AttitudeSettings::DataFields attitudeSettingsData =
912  AttitudeSettings::GetInstance(getObjectManager())->getData();
913  SensorSettings::DataFields sensorSettingsData =
914  SensorSettings::GetInstance(getObjectManager())->getData();
915 
916  // Inverse rotation of sensor data, from body frame into sensor frame
917  double a_body[3] = { listMean(accel_accum_x), listMean(accel_accum_y),
918  listMean(accel_accum_z) };
919  double a_sensor[3];
920  double Rsb[3][3]; // The initial body-frame to sensor-frame rotation
921  double rpy[3] = { attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_ROLL]
922  * DEG2RAD / 100.0,
923  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_PITCH]
924  * DEG2RAD / 100.0,
925  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_YAW]
926  * DEG2RAD / 100.0 };
927  Euler2R(rpy, Rsb);
928  rotate_vector(Rsb, a_body, a_sensor, false);
929 
930  // Temporary variables
931  double psi, theta, phi;
932  Q_UNUSED(psi);
933  // Keep existing yaw rotation
934  psi = attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_YAW] * DEG2RAD
935  / 100.0;
936 
937  // Solve "a_sensor = Rot(phi, theta, psi) *[0;0;-g]" for the roll (phi) and pitch (theta)
938  // values.
939  // Recall that phi is in [-pi, pi] and theta is in [-pi/2, pi/2]
940  phi = atan2(-a_sensor[1], -a_sensor[2]);
941  theta = atan(-a_sensor[0] / (sin(phi) * a_sensor[1] + cos(phi) * a_sensor[2]));
942 
943  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_ROLL] =
944  static_cast<qint16>(phi * RAD2DEG * 100.0);
945  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_PITCH] =
946  static_cast<qint16>(theta * RAD2DEG * 100.0);
947 
948  if (zeroVertical) {
949  // If requested, calculate the offset in the z accelerometer that
950  // would make it reflect gravity
951 
952  // Rotate the accel measurements to the new body frame
953  double rpy[3] = {
954  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_ROLL] * DEG2RAD
955  / 100.0,
956  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_PITCH] * DEG2RAD
957  / 100.0,
958  attitudeSettingsData.BoardRotation[AttitudeSettings::BOARDROTATION_YAW] * DEG2RAD
959  / 100.0
960  };
961  double a_body_new[3];
962  Euler2R(rpy, Rsb);
963  rotate_vector(Rsb, a_sensor, a_body_new, false);
964 
965  // Compute the new offset to make it average accelLength(GRAVITY)
966  sensorSettingsData.ZAccelOffset += -(a_body_new[2] + accelLength);
967  }
968 
969  // Rotate the gyro bias from the body frame into the sensor frame
970  double gyro_sensor[3];
971  double gyro_body[3] = { x_gyro_bias, y_gyro_bias, z_gyro_bias };
972  rotate_vector(Rsb, gyro_body, gyro_sensor, false);
973 
974  // Store these new biases, accounting for any temperature coefficients
975  sensorSettingsData.XGyroTempCoeff[0] =
976  static_cast<float>(gyro_sensor[0] - temp * sensorSettingsData.XGyroTempCoeff[1]
977  - pow(temp, 2) * sensorSettingsData.XGyroTempCoeff[2]
978  - pow(temp, 3) * sensorSettingsData.XGyroTempCoeff[3]);
979  sensorSettingsData.YGyroTempCoeff[0] =
980  static_cast<float>(gyro_sensor[1] - temp * sensorSettingsData.YGyroTempCoeff[1]
981  - pow(temp, 2) * sensorSettingsData.YGyroTempCoeff[2]
982  - pow(temp, 3) * sensorSettingsData.YGyroTempCoeff[3]);
983  sensorSettingsData.ZGyroTempCoeff[0] =
984  static_cast<float>(gyro_sensor[2] - temp * sensorSettingsData.ZGyroTempCoeff[1]
985  - pow(temp, 2) * sensorSettingsData.ZGyroTempCoeff[2]
986  - pow(temp, 3) * sensorSettingsData.ZGyroTempCoeff[3]);
987  SensorSettings::GetInstance(getObjectManager())->setData(sensorSettingsData);
988 
989  // We offset the gyro bias by current bias to help precision
990  // Disable gyro bias correction to see raw data
991  AttitudeSettings *attitudeSettings = AttitudeSettings::GetInstance(getObjectManager());
992  Q_ASSERT(attitudeSettings);
993  attitudeSettingsData.BiasCorrectGyro = AttitudeSettings::BIASCORRECTGYRO_TRUE;
994  attitudeSettings->setData(attitudeSettingsData);
995  attitudeSettings->updated();
996 
997  // After recomputing the level for a frame, zero the trim settings
998  SubTrimSettings *trimSettings = SubTrimSettings::GetInstance(getObjectManager());
999  Q_ASSERT(trimSettings);
1000  SubTrimSettings::DataFields trim = trimSettings->getData();
1001  trim.Pitch = 0;
1002  trim.Roll = 0;
1003  trimSettings->setData(trim);
1004 
1005  // Inform the system that the calibration process has completed
1006  emit calibrationCompleted();
1007 
1008  return true;
1009  }
1010  return false;
1011 }
1012 
1019 {
1020  // Position is specified 1-6, but used as an index
1021  Q_ASSERT(position >= 1 && position <= 6);
1022  position--;
1023 
1024  if (calibrateAccels && obj->getObjID() == Accels::OBJID) {
1025  Accels *accels = Accels::GetInstance(getObjectManager());
1026  Q_ASSERT(accels);
1027  Accels::DataFields accelsData = accels->getData();
1028 
1029  accel_accum_x.append(accelsData.x);
1030  accel_accum_y.append(accelsData.y);
1031  accel_accum_z.append(accelsData.z);
1032  }
1033 
1034  if (calibrateMags && obj->getObjID() == Magnetometer::OBJID) {
1035  Magnetometer *mag = Magnetometer::GetInstance(getObjectManager());
1036  Q_ASSERT(mag);
1037  Magnetometer::DataFields magData = mag->getData();
1038 
1039  mag_accum_x.append(magData.x);
1040  mag_accum_y.append(magData.y);
1041  mag_accum_z.append(magData.z);
1042  }
1043 
1044  // Update progress bar
1045  int progress_percentage;
1046  if (calibrateAccels && !calibrateMags)
1047  progress_percentage = (100 * accel_accum_x.size()) / NUM_SENSOR_UPDATES_SIX_POINT;
1048  else if (!calibrateAccels && calibrateMags)
1049  progress_percentage = (100 * mag_accum_x.size()) / NUM_SENSOR_UPDATES_SIX_POINT;
1050  else
1051  progress_percentage = (100 * std::min(mag_accum_x.size(), accel_accum_x.size()))
1052  / NUM_SENSOR_UPDATES_SIX_POINT;
1053  emit sixPointProgressChanged(progress_percentage);
1054 
1055  // If enough data is collected, average it for this position
1056  if ((!calibrateAccels || accel_accum_x.size() >= NUM_SENSOR_UPDATES_SIX_POINT)
1057  && (!calibrateMags || mag_accum_x.size() >= NUM_SENSOR_UPDATES_SIX_POINT)) {
1058 
1059  // Store the average accelerometer value in that position
1060  if (calibrateAccels) {
1061  // undo the board rotation that has been applied to the sensor values
1062  double accel_body[3] = { listMean(accel_accum_x), listMean(accel_accum_y),
1063  listMean(accel_accum_z) };
1064  double accel_sensor[3];
1065  rotate_vector(boardRotationMatrix, accel_body, accel_sensor, false);
1066 
1067  accel_data_x[position] = accel_sensor[0];
1068  accel_data_y[position] = accel_sensor[1];
1069  accel_data_z[position] = accel_sensor[2];
1070  accel_accum_x.clear();
1071  accel_accum_y.clear();
1072  accel_accum_z.clear();
1073  }
1074 
1075  // Store the average magnetometer value in that position
1076  if (calibrateMags) {
1077  // undo the board rotation that has been applied to the sensor values
1078  double mag_body[3] = { listMean(mag_accum_x), listMean(mag_accum_y),
1079  listMean(mag_accum_z) };
1080  double mag_sensor[3];
1081  rotate_vector(boardRotationMatrix, mag_body, mag_sensor, false);
1082 
1083  mag_data_x[position] = mag_sensor[0];
1084  mag_data_y[position] = mag_sensor[1];
1085  mag_data_z[position] = mag_sensor[2];
1086  mag_accum_x.clear();
1087  mag_accum_y.clear();
1088  mag_accum_z.clear();
1089  }
1090 
1091  // Indicate all data collected for this position
1092  return true;
1093  }
1094  return false;
1095 }
1096 
1104 {
1105  xCurve = x;
1106  yCurve = y;
1107  zCurve = z;
1108 }
1109 
1115 {
1116  if (obj->getObjID() == Gyros::OBJID) {
1117  Gyros *gyros = Gyros::GetInstance(getObjectManager());
1118  Q_ASSERT(gyros);
1119  Gyros::DataFields gyrosData = gyros->getData();
1120  double gyros_body[3] = { gyrosData.x, gyrosData.y, gyrosData.z };
1121  double gyros_sensor[3];
1122  rotate_vector(boardRotationMatrix, gyros_body, gyros_sensor, false);
1123  gyro_accum_x.append(gyros_sensor[0]);
1124  gyro_accum_y.append(gyros_sensor[1]);
1125  gyro_accum_z.append(gyros_sensor[2]);
1126  gyro_accum_temp.append(gyrosData.temperature);
1127  }
1128 
1129  auto m = std::minmax_element(gyro_accum_temp.begin(), gyro_accum_temp.end());
1130  double range = *m.second - *m.first; // max - min
1131  emit tempCalProgressChanged(static_cast<int>((100 * range) / min_temperature_range));
1132 
1133  if ((gyro_accum_temp.size() % 10) == 0) {
1135  }
1136 
1137  // If enough data is collected, average it for this position
1138  if (range >= min_temperature_range) {
1139  return true;
1140  }
1141 
1142  return false;
1143 }
1144 
1149 {
1150  int n_samples = gyro_accum_temp.size();
1151 
1152  // Construct the matrix of temperature.
1153  Eigen::Matrix<double, Eigen::Dynamic, 4> X(n_samples, 4);
1154 
1155  // And the matrix of gyro samples.
1156  Eigen::Matrix<double, Eigen::Dynamic, 3> Y(n_samples, 3);
1157 
1158  for (int i = 0; i < n_samples; ++i) {
1159  X(i, 0) = 1;
1160  X(i, 1) = gyro_accum_temp[i];
1161  X(i, 2) = pow(gyro_accum_temp[i], 2);
1162  X(i, 3) = pow(gyro_accum_temp[i], 3);
1163  Y(i, 0) = gyro_accum_x[i];
1164  Y(i, 1) = gyro_accum_y[i];
1165  Y(i, 2) = gyro_accum_z[i];
1166  }
1167 
1168  // Solve Y = X * B
1169  // Use the cholesky-based Penrose pseudoinverse method.
1170  Eigen::Matrix<double, 4, 3> result = (X.transpose() * X).ldlt().solve(X.transpose() * Y);
1171 
1172  QList<double> xCoeffs, yCoeffs, zCoeffs;
1173  xCoeffs.clear();
1174  xCoeffs.append(result(0, 0));
1175  xCoeffs.append(result(1, 0));
1176  xCoeffs.append(result(2, 0));
1177  xCoeffs.append(result(3, 0));
1178  yCoeffs.clear();
1179  yCoeffs.append(result(0, 1));
1180  yCoeffs.append(result(1, 1));
1181  yCoeffs.append(result(2, 1));
1182  yCoeffs.append(result(3, 1));
1183  zCoeffs.clear();
1184  zCoeffs.append(result(0, 2));
1185  zCoeffs.append(result(1, 2));
1186  zCoeffs.append(result(2, 2));
1187  zCoeffs.append(result(3, 2));
1188 
1189  if (xCurve != NULL)
1190  xCurve->plotData(gyro_accum_temp, gyro_accum_x, xCoeffs);
1191  if (yCurve != NULL)
1192  yCurve->plotData(gyro_accum_temp, gyro_accum_y, yCoeffs);
1193  if (zCurve != NULL)
1194  zCurve->plotData(gyro_accum_temp, gyro_accum_z, zCoeffs);
1195 }
1196 
1203 {
1204  timer.stop();
1205  disconnect(&timer, &QTimer::timeout, this, &Calibration::timeout);
1206 
1207  // Reenable gyro bias correction
1208  AttitudeSettings *attitudeSettings = AttitudeSettings::GetInstance(getObjectManager());
1209  Q_ASSERT(attitudeSettings);
1210  AttitudeSettings::DataFields attitudeSettingsData = attitudeSettings->getData();
1211  attitudeSettingsData.BiasCorrectGyro = AttitudeSettings::BIASCORRECTGYRO_TRUE;
1212  attitudeSettings->setData(attitudeSettingsData);
1213  attitudeSettings->updated();
1214 
1215  int n_samples = gyro_accum_temp.size();
1216 
1217  // Construct the matrix of temperature.
1218  Eigen::Matrix<double, Eigen::Dynamic, 4> X(n_samples, 4);
1219 
1220  // And the matrix of gyro samples.
1221  Eigen::Matrix<double, Eigen::Dynamic, 3> Y(n_samples, 3);
1222 
1223  for (int i = 0; i < n_samples; ++i) {
1224  X(i, 0) = 1;
1225  X(i, 1) = gyro_accum_temp[i];
1226  X(i, 2) = pow(gyro_accum_temp[i], 2);
1227  X(i, 3) = pow(gyro_accum_temp[i], 3);
1228  Y(i, 0) = gyro_accum_x[i];
1229  Y(i, 1) = gyro_accum_y[i];
1230  Y(i, 2) = gyro_accum_z[i];
1231  }
1232 
1233  // Solve Y = X * B
1234  // Use the cholesky-based Penrose pseudoinverse method.
1235  Eigen::Matrix<double, 4, 3> result = (X.transpose() * X).ldlt().solve(X.transpose() * Y);
1236 
1237  std::stringstream str;
1238  str << result.format(Eigen::IOFormat(4, 0, ", ", "\n", "[", "]"));
1239  qDebug().noquote() << "Solution:\n" << QString::fromStdString(str.str());
1240 
1241  // Store the results
1242  SensorSettings *sensorSettings = SensorSettings::GetInstance(getObjectManager());
1243  Q_ASSERT(sensorSettings);
1244  SensorSettings::DataFields sensorSettingsData = sensorSettings->getData();
1245  sensorSettingsData.XGyroTempCoeff[0] = static_cast<float>(result(0, 0));
1246  sensorSettingsData.XGyroTempCoeff[1] = static_cast<float>(result(1, 0));
1247  sensorSettingsData.XGyroTempCoeff[2] = static_cast<float>(result(2, 0));
1248  sensorSettingsData.XGyroTempCoeff[3] = static_cast<float>(result(3, 0));
1249  sensorSettingsData.YGyroTempCoeff[0] = static_cast<float>(result(0, 1));
1250  sensorSettingsData.YGyroTempCoeff[1] = static_cast<float>(result(1, 1));
1251  sensorSettingsData.YGyroTempCoeff[2] = static_cast<float>(result(2, 1));
1252  sensorSettingsData.YGyroTempCoeff[3] = static_cast<float>(result(3, 1));
1253  sensorSettingsData.ZGyroTempCoeff[0] = static_cast<float>(result(0, 2));
1254  sensorSettingsData.ZGyroTempCoeff[1] = static_cast<float>(result(1, 2));
1255  sensorSettingsData.ZGyroTempCoeff[2] = static_cast<float>(result(2, 2));
1256  sensorSettingsData.ZGyroTempCoeff[3] = static_cast<float>(result(3, 2));
1257  sensorSettings->setData(sensorSettingsData);
1258 
1259  QList<double> xCoeffs, yCoeffs, zCoeffs;
1260  xCoeffs.clear();
1261  xCoeffs.append(result(0, 0));
1262  xCoeffs.append(result(1, 0));
1263  xCoeffs.append(result(2, 0));
1264  xCoeffs.append(result(3, 0));
1265  yCoeffs.clear();
1266  yCoeffs.append(result(0, 1));
1267  yCoeffs.append(result(1, 1));
1268  yCoeffs.append(result(2, 1));
1269  yCoeffs.append(result(3, 1));
1270  zCoeffs.clear();
1271  zCoeffs.append(result(0, 2));
1272  zCoeffs.append(result(1, 2));
1273  zCoeffs.append(result(2, 2));
1274  zCoeffs.append(result(3, 2));
1275 
1276  if (xCurve != NULL)
1277  xCurve->plotData(gyro_accum_temp, gyro_accum_x, xCoeffs);
1278  if (yCurve != NULL)
1279  yCurve->plotData(gyro_accum_temp, gyro_accum_y, yCoeffs);
1280  if (zCurve != NULL)
1281  zCurve->plotData(gyro_accum_temp, gyro_accum_z, zCoeffs);
1282 
1283  emit tempCalProgressChanged(0);
1284 
1285  // Inform the system that the calibration process has completed
1286  emit calibrationCompleted();
1287 
1288  return CALIBRATION_SUCCESS;
1289 }
1290 
1295 UAVObjectManager *Calibration::getObjectManager()
1296 {
1297  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
1298  UAVObjectManager *objMngr = pm->getObject<UAVObjectManager>();
1299  Q_ASSERT(objMngr);
1300  return objMngr;
1301 }
1302 
1308 UAVObjectUtilManager *Calibration::getObjectUtilManager()
1309 {
1310  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
1311  UAVObjectUtilManager *utilMngr = pm->getObject<UAVObjectUtilManager>();
1312  Q_ASSERT(utilMngr);
1313  return utilMngr;
1314 }
1315 
1321 {
1322  SensorSettings *sensorSettings = SensorSettings::GetInstance(getObjectManager());
1323  Q_ASSERT(sensorSettings);
1324  SensorSettings::DataFields sensorSettingsData = sensorSettings->getData();
1325 
1326  bool good_calibration = true;
1327 
1328  // Assign calibration data
1329  if (calibrateAccels) {
1330  good_calibration = true;
1331 
1332  qDebug() << "Accel measurements";
1333  for (int i = 0; i < 6; i++)
1334  qDebug() << accel_data_x[i] << ", " << accel_data_y[i] << ", " << accel_data_z[i]
1335  << ";";
1336 
1337  // Solve for accelerometer calibration
1338  double S[3], b[3];
1339  SixPointInConstFieldCal(accelLength, accel_data_x, accel_data_y, accel_data_z, S, b);
1340 
1341  sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_X] += (-sign(S[0]) * b[0]);
1342  sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Y] += (-sign(S[1]) * b[1]);
1343  sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Z] += (-sign(S[2]) * b[2]);
1344 
1345  sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_X] *= fabs(S[0]);
1346  sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Y] *= fabs(S[1]);
1347  sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Z] *= fabs(S[2]);
1348 
1349  // Check the accel calibration is good (checks for NaN's)
1350  good_calibration &=
1351  !std::isnan(sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_X]);
1352  good_calibration &=
1353  !std::isnan(sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Y]);
1354  good_calibration &=
1355  !std::isnan(sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Z]);
1356  good_calibration &= !std::isnan(sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_X]);
1357  good_calibration &= !std::isnan(sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Y]);
1358  good_calibration &= !std::isnan(sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Z]);
1359 
1360  // This can happen if, for instance, HomeLocation.g_e == 0
1361  if ((S[0] + S[1] + S[2]) < 0.0001) {
1362  good_calibration = false;
1363  }
1364 
1365  if (good_calibration) {
1366  qDebug() << "Accel bias: " << sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_X]
1367  << " " << sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Y] << " "
1368  << sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Z];
1369  } else {
1370  return ACCELEROMETER_FAILED;
1371  }
1372  }
1373 
1374  if (calibrateMags) {
1375  good_calibration = true;
1376 
1377  qDebug() << "Mag measurements";
1378  for (int i = 0; i < 6; i++)
1379  qDebug() << mag_data_x[i] << ", " << mag_data_y[i] << ", " << mag_data_z[i] << ";";
1380 
1381  // Work out the average vector length as nominal since mag scale normally close to 1
1382  // and we don't use the magnitude anyway. Avoids requiring home location.
1383  double m_x = 0, m_y = 0, m_z = 0, len = 0;
1384  for (int i = 0; i < 6; i++) {
1385  m_x += mag_data_x[i];
1386  m_y += mag_data_x[i];
1387  m_z += mag_data_x[i];
1388  }
1389  m_x /= 6;
1390  m_y /= 6;
1391  m_z /= 6;
1392  for (int i = 0; i < 6; i++) {
1393  len += sqrt(pow(mag_data_x[i] - m_x, 2) + pow(mag_data_y[i] - m_y, 2)
1394  + pow(mag_data_z[i] - m_z, 2));
1395  }
1396  len /= 6;
1397 
1398  // Solve for magnetometer calibration
1399  double S[3], b[3];
1400  SixPointInConstFieldCal(len, mag_data_x, mag_data_y, mag_data_z, S, b);
1401 
1402  // Assign calibration data
1403  sensorSettingsData.MagBias[SensorSettings::MAGBIAS_X] += (-sign(S[0]) * b[0]);
1404  sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Y] += (-sign(S[1]) * b[1]);
1405  sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Z] += (-sign(S[2]) * b[2]);
1406  sensorSettingsData.MagScale[SensorSettings::MAGSCALE_X] *= fabs(S[0]);
1407  sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Y] *= fabs(S[1]);
1408  sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Z] *= fabs(S[2]);
1409 
1410  // Check the mag calibration is good (checks for NaN's)
1411  good_calibration &= !std::isnan(sensorSettingsData.MagBias[SensorSettings::MAGBIAS_X]);
1412  good_calibration &= !std::isnan(sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Y]);
1413  good_calibration &= !std::isnan(sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Z]);
1414  good_calibration &= !std::isnan(sensorSettingsData.MagScale[SensorSettings::MAGSCALE_X]);
1415  good_calibration &= !std::isnan(sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Y]);
1416  good_calibration &= !std::isnan(sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Z]);
1417 
1418  // This can happen if, for instance, HomeLocation.g_e == 0
1419  if ((S[0] + S[1] + S[2]) < 0.0001) {
1420  good_calibration = false;
1421  }
1422 
1423  if (good_calibration) {
1424  qDebug() << "Mag bias: " << sensorSettingsData.MagBias[SensorSettings::MAGBIAS_X] << " "
1425  << sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Y] << " "
1426  << sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Z];
1427  } else {
1428  return MAGNETOMETER_FAILED;
1429  }
1430  }
1431 
1432  // If we've made it this far, it's because good_calibration == true
1433  sensorSettings->setData(sensorSettingsData);
1434 
1435  // Inform the system that the calibration process has completed
1436  emit calibrationCompleted();
1437 
1438  return CALIBRATION_SUCCESS;
1439 }
1440 
1446 {
1447  // Write the original accelerometer values back to the device
1448  SensorSettings *sensorSettings = SensorSettings::GetInstance(getObjectManager());
1449  Q_ASSERT(sensorSettings);
1450  SensorSettings::DataFields sensorSettingsData = sensorSettings->getData();
1451 
1452  if (calibrateAccels) {
1453  sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_X] =
1454  static_cast<float>(initialAccelsScale[0]);
1455  sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Y] =
1456  static_cast<float>(initialAccelsScale[1]);
1457  sensorSettingsData.AccelScale[SensorSettings::ACCELSCALE_Z] =
1458  static_cast<float>(initialAccelsScale[2]);
1459  sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_X] =
1460  static_cast<float>(initialAccelsBias[0]);
1461  sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Y] =
1462  static_cast<float>(initialAccelsBias[1]);
1463  sensorSettingsData.AccelBias[SensorSettings::ACCELBIAS_Z] =
1464  static_cast<float>(initialAccelsBias[2]);
1465  }
1466 
1467  if (calibrateMags) {
1468  // Write the original magnetometer values back to the device
1469  sensorSettingsData.MagScale[SensorSettings::MAGSCALE_X] =
1470  static_cast<float>(initialMagsScale[0]);
1471  sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Y] =
1472  static_cast<float>(initialMagsScale[1]);
1473  sensorSettingsData.MagScale[SensorSettings::MAGSCALE_Z] =
1474  static_cast<float>(initialMagsScale[2]);
1475  sensorSettingsData.MagBias[SensorSettings::MAGBIAS_X] =
1476  static_cast<float>(initialMagsBias[0]);
1477  sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Y] =
1478  static_cast<float>(initialMagsBias[1]);
1479  sensorSettingsData.MagBias[SensorSettings::MAGBIAS_Z] =
1480  static_cast<float>(initialMagsBias[2]);
1481  }
1482 
1483  sensorSettings->setData(sensorSettingsData);
1484  sensorSettings->updated();
1485 }
1486 
1493 void Calibration::Euler2R(double rpy[3], double Rbe[3][3])
1494 {
1495  double sF = sin(rpy[0]), cF = cos(rpy[0]);
1496  double sT = sin(rpy[1]), cT = cos(rpy[1]);
1497  double sP = sin(rpy[2]), cP = cos(rpy[2]);
1498 
1499  Rbe[0][0] = cT * cP;
1500  Rbe[0][1] = cT * sP;
1501  Rbe[0][2] = -sT;
1502  Rbe[1][0] = sF * sT * cP - cF * sP;
1503  Rbe[1][1] = sF * sT * sP + cF * cP;
1504  Rbe[1][2] = cT * sF;
1505  Rbe[2][0] = cF * sT * cP + sF * sP;
1506  Rbe[2][1] = cF * sT * sP - sF * cP;
1507  Rbe[2][2] = cT * cF;
1508 }
1509 
1517 void Calibration::rotate_vector(double R[3][3], const double vec[3], double vec_out[3],
1518  bool transpose = true)
1519 {
1520  if (!transpose) {
1521  vec_out[0] = R[0][0] * vec[0] + R[0][1] * vec[1] + R[0][2] * vec[2];
1522  vec_out[1] = R[1][0] * vec[0] + R[1][1] * vec[1] + R[1][2] * vec[2];
1523  vec_out[2] = R[2][0] * vec[0] + R[2][1] * vec[1] + R[2][2] * vec[2];
1524  } else {
1525  vec_out[0] = R[0][0] * vec[0] + R[1][0] * vec[1] + R[2][0] * vec[2];
1526  vec_out[1] = R[0][1] * vec[0] + R[1][1] * vec[1] + R[2][1] * vec[2];
1527  vec_out[2] = R[0][2] * vec[0] + R[1][2] * vec[1] + R[2][2] * vec[2];
1528  }
1529 }
1530 
1551 int Calibration::SixPointInConstFieldCal(double ConstMag, double x[6], double y[6], double z[6],
1552  double S[3], double b[3])
1553 {
1554  Eigen::Matrix<double, 5, 5> A;
1555  Eigen::Matrix<double, 5, 1> f;
1556  double xp, yp, zp, Sx;
1557 
1558  // Fill in matrix A -
1559  // write six difference-in-magnitude equations of the form
1560  // Sx^2(x2^2-x1^2) + 2*Sx*bx*(x2-x1) + Sy^2(y2^2-y1^2) + 2*Sy*by*(y2-y1) + Sz^2(z2^2-z1^2) +
1561  // 2*Sz*bz*(z2-z1) = 0
1562  // or in other words
1563  // 2*Sx*bx*(x2-x1)/Sx^2 + Sy^2(y2^2-y1^2)/Sx^2 + 2*Sy*by*(y2-y1)/Sx^2 + Sz^2(z2^2-z1^2)/Sx^2
1564  // + 2*Sz*bz*(z2-z1)/Sx^2 = (x1^2-x2^2)
1565  for (int i = 0; i < 5; i++) {
1566  A(i, 0) = 2.0 * (x[i + 1] - x[i]);
1567  A(i, 1) = y[i + 1] * y[i + 1] - y[i] * y[i];
1568  A(i, 2) = 2.0 * (y[i + 1] - y[i]);
1569  A(i, 3) = z[i + 1] * z[i + 1] - z[i] * z[i];
1570  A(i, 4) = 2.0 * (z[i + 1] - z[i]);
1571  f[i] = x[i] * x[i] - x[i + 1] * x[i + 1];
1572  }
1573 
1574  // solve for c0=bx/Sx, c1=Sy^2/Sx^2; c2=Sy*by/Sx^2, c3=Sz^2/Sx^2, c4=Sz*bz/Sx^2
1575  Eigen::Matrix<double, 5, 1> c = A.fullPivHouseholderQr().solve(f);
1576 
1577  // use one magnitude equation and c's to find Sx - doesn't matter which - all give the same
1578  // answer
1579  xp = x[0];
1580  yp = y[0];
1581  zp = z[0];
1582  Sx = sqrt(ConstMag * ConstMag
1583  / (xp * xp + 2 * c[0] * xp + c[0] * c[0] + c[1] * yp * yp + 2 * c[2] * yp
1584  + c[2] * c[2] / c[1] + c[3] * zp * zp + 2 * c[4] * zp + c[4] * c[4] / c[3]));
1585 
1586  S[0] = Sx;
1587  b[0] = Sx * c[0];
1588  S[1] = sqrt(c[1] * Sx * Sx);
1589  b[1] = c[2] * Sx * Sx / S[1];
1590  S[2] = sqrt(c[3] * Sx * Sx);
1591  b[2] = c[4] * Sx * Sx / S[2];
1592 
1593  return 1;
1594 }
1595 
1596 void Calibration::setMetadata(QMap<QString, UAVObject::Metadata> metaList)
1597 {
1598  QEventLoop loop;
1599  QTimer::singleShot(META_OPERATIONS_TIMEOUT, &loop, &QEventLoop::quit);
1600  connect(getObjectUtilManager(), &UAVObjectUtilManager::completedMetadataWrite, &loop,
1601  &QEventLoop::quit);
1602  // Show the UI is blocking
1603  emit calibrationBusy(true);
1604  // Set new metadata
1605  getObjectUtilManager()->setAllNonSettingsMetadata(metaList);
1606  loop.exec();
1607  emit calibrationBusy(false);
1608 }
void updatePlane(int position)
Change the UAV visualization.
virtual void setMetadata(const Metadata &mdata)=0
bool storeTempCalMeasurement(UAVObject *obj)
Store a sample for temperature compensation.
void doSaveSixPointPosition()
Indicates UAV is in a position to collect data during 6pt calibration.
void calibrationCompleted()
Indicate that a calibration process has successfully completed and the results saved to UAVO...
void completedMetadataWrite(bool)
DEG2RAD
Definition: OPPlots.m:107
void doStartSixPoint()
Start the six point calibration routine.
static double listMean(QList< double > list)
Compute the mean value of a list.
Definition: calibration.h:253
Core plugin system that manages the plugins, their life cycle and their registered objects...
Definition: pluginmanager.h:53
int computeScaleBias()
Computes the scale and bias for the accelerometer and mag.
void setTempCalRange(int r)
Set temperature calibration range.
bool storeLevelingMeasurement(UAVObject *obj)
Store leveling sample and compute level if finished.
for i
Definition: OPPlots.m:140
void showLevelingMessage(QString message)
Show an instruction to the user for leveling.
void plotData(QList< double > temp, QList< double > gyro, QList< double > coefficients)
Show calibration data for one of the channels.
void tempCalProgressChanged(int)
Indicate what the progress is for temperature calibration.
void objectUpdated(UAVObject *obj)
Signal sent whenever any field of the object is updated.
QMap< QString, UAVObject::Metadata > readAllNonSettingsMetadata()
UAVObjectUtilManager::readAllNonSettingsMetadata Convenience function for calling readMetadata...
static void SetFlightTelemetryUpdateMode(Metadata &meta, UpdateMode val)
Definition: uavobject.cpp:468
void connectSensor(sensor_type sensor, bool connect)
Connect and speed up or disconnect a sensor.
Definition: calibration.cpp:96
virtual Metadata getMetadata()=0
int SixPointInConstFieldCal(double ConstMag, double x[6], double y[6], double z[6], double S[3], double b[3])
void levelingProgressChanged(int)
Indicate what the progress is for leveling.
UAVMetaObject * getMetaObject()
bool setAllNonSettingsMetadata(QMap< QString, UAVObject::Metadata >)
UAVObjectUtilManager::setAllNonSettingsMetadata Convenience function for calling setMetadata.
void initialize(bool calibrateAccels, bool calibrateMags)
Calibration::initialize Configure whether to calibrate the magnetometer and/or accelerometer during 6...
Definition: calibration.cpp:85
void showYawOrientationMessage(QString message)
Show an instruction to the user for yaw orientation.
void doStartOrientation()
Start collecting data while vehicle is in pure pitch.
void doStartNoBiasLeveling()
Start collecting data while vehicle is level.
void showTempCalMessage(QString message)
Show an instruction or message from temperature calibration.
void transactionCompleted(UAVObject *obj, bool success)
transactionCompleted. Triggered by a call to emitTransactionCompleted - done in telemetry.cpp whenever a transaction finishes.
static ICore * instance()
Definition: coreimpl.cpp:46
void sixPointProgressChanged(int)
Indicate what the progress is for six point collection.
void updated()
Definition: uavobject.cpp:179
void rotate_vector(double R[3][3], const double vec[3], double vec_out[3], bool transpose)
Rotate a vector by the rotation matrix, optionally trasposing.
calibrationSuccessMessages
Definition: calibration.cpp:65
z
Definition: OPPlots.m:102
void doCancelSixPoint()
Cancels the six point calibration routine.
void showSixPointMessage(QString message)
Show an instruction to the user for six point calibration.
void Euler2R(double rpy[3], double Rbe[3][3])
Compute a rotation matrix from a set of euler angles.
quint32 getObjID()
Definition: uavobject.cpp:107
void doCancelTempCalPoint()
Cancels the temperature calibration routine.
void yawOrientationProgressChanged(int)
Indicate what the progress is for yaw orientation.
bool storeSixPointMeasurement(UAVObject *obj, int position)
Store a measurement at this position and indicate if it is the last one.
RAD2DEG
Definition: OPPlots.m:106
Gui-less support class for calibration.
void doStartBiasAndLeveling()
Start collecting data while vehicle is level.
void updateTempCompCalibrationDisplay()
Update the graphs with the temperature compensation.
void(NAME)
void toggleSavePosition(bool enable)
Indicate whether to enable or disable controls.
void calibrationBusy(bool busy)
Indicate whether the calibration is busy.
void doAcceptTempCal()
Accept gyro temp calibration data.
x
Definition: OPPlots.m:100
void toggleControls(bool enable)
Indicate whether to enable or disable controls.
int computeTempCal()
Compute temperature compensation factors.
void configureTempCurves(TempCompCurve *x, TempCompCurve *y, TempCompCurve *z)
Set up the curves.
bool storeYawOrientationMeasurement(UAVObject *obj)
Store yaw orientation sample and compute orientation if finished.
void resetSensorCalibrationToOriginalValues()
Reset sensor settings to pre-calibration values.
void doStartTempCal()
Start collecting gyro temp calibration data.
y
Definition: OPPlots.m:101