1 /*
2 * Copyright (C) 2020 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.navigation.inertial.calibration.gyroscope;
17
18 import com.irurueta.algebra.Matrix;
19 import com.irurueta.algebra.WrongSizeException;
20 import com.irurueta.navigation.LockedException;
21 import com.irurueta.navigation.NotReadyException;
22 import com.irurueta.navigation.inertial.BodyKinematics;
23 import com.irurueta.navigation.inertial.INSLooselyCoupledKalmanInitializerConfig;
24 import com.irurueta.navigation.inertial.INSTightlyCoupledKalmanInitializerConfig;
25 import com.irurueta.navigation.inertial.calibration.AngularSpeedTriad;
26 import com.irurueta.navigation.inertial.calibration.CalibrationException;
27 import com.irurueta.navigation.inertial.calibration.GyroscopeBiasUncertaintySource;
28 import com.irurueta.navigation.inertial.calibration.GyroscopeCalibrationSource;
29 import com.irurueta.navigation.inertial.calibration.StandardDeviationFrameBodyKinematics;
30 import com.irurueta.navigation.inertial.estimators.ECEFKinematicsEstimator;
31 import com.irurueta.numerical.robust.InliersData;
32 import com.irurueta.numerical.robust.RobustEstimatorMethod;
33 import com.irurueta.units.AngularSpeed;
34 import com.irurueta.units.AngularSpeedConverter;
35 import com.irurueta.units.AngularSpeedUnit;
36
37 import java.util.ArrayList;
38 import java.util.List;
39
40 /**
41 * This is an abstract class to robustly estimate gyroscope
42 * biases, cross couplings and scaling factors
43 * along with G-dependent cross biases introduced on the gyroscope by the
44 * specific forces sensed by the accelerometer.
45 * <p>
46 * To use this calibrator at least 7 measurements at different known frames must
47 * be provided. In other words, accelerometer and gyroscope (i.e. body kinematics)
48 * samples must be obtained at 7 different positions, orientations and velocities
49 * (although typically velocities are always zero).
50 * <p>
51 * Measured gyroscope angular rates is assumed to follow the model shown below:
52 * <pre>
53 * Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue + w
54 * </pre>
55 * Where:
56 * - Ωmeas is the measured gyroscope angular rates. This is a 3x1 vector.
57 * - bg is the gyroscope bias. Ideally, on a perfect gyroscope, this should be a
58 * 3x1 zero vector.
59 * - I is the 3x3 identity matrix.
60 * - Mg is the 3x3 matrix containing cross-couplings and scaling factors. Ideally, on
61 * a perfect gyroscope, this should be a 3x3 zero matrix.
62 * - Ωtrue is ground-truth gyroscope angular rates.
63 * - Gg is the G-dependent cross biases introduced by the specific forces sensed
64 * by the accelerometer. Ideally, on a perfect gyroscope, this should be a 3x3
65 * zero matrix.
66 * - ftrue is ground-truth specific force. This is a 3x1 vector.
67 * - w is measurement noise. This is a 3x1 vector.
68 */
69 public abstract class RobustKnownFrameGyroscopeCalibrator implements
70 GyroscopeNonLinearCalibrator, UnknownBiasNonLinearGyroscopeCalibrator, GyroscopeCalibrationSource,
71 GyroscopeBiasUncertaintySource, OrderedStandardDeviationFrameBodyKinematicsGyroscopeCalibrator,
72 QualityScoredGyroscopeCalibrator {
73
74 /**
75 * Indicates whether by default a common z-axis is assumed for both the accelerometer
76 * and gyroscope.
77 */
78 public static final boolean DEFAULT_USE_COMMON_Z_AXIS = false;
79
80 /**
81 * Required minimum number of measurements.
82 */
83 public static final int MINIMUM_MEASUREMENTS = 7;
84
85 /**
86 * Indicates that by default a linear calibrator is used for preliminary solution estimation.
87 * The result obtained on each preliminary solution might be later refined.
88 */
89 public static final boolean DEFAULT_USE_LINEAR_CALIBRATOR = true;
90
91 /**
92 * Indicates that by default preliminary solutions are refined.
93 */
94 public static final boolean DEFAULT_REFINE_PRELIMINARY_SOLUTIONS = false;
95
96 /**
97 * Default robust estimator method when none is provided.
98 */
99 public static final RobustEstimatorMethod DEFAULT_ROBUST_METHOD = RobustEstimatorMethod.LMEDS;
100
101 /**
102 * Indicates that result is refined by default using a non-linear calibrator
103 * (which uses a Levenberg-Marquardt fitter).
104 */
105 public static final boolean DEFAULT_REFINE_RESULT = true;
106
107 /**
108 * Indicates that covariance is kept by default after refining result.
109 */
110 public static final boolean DEFAULT_KEEP_COVARIANCE = true;
111
112 /**
113 * Default amount of progress variation before notifying a change in estimation progress.
114 * By default this is set to 5%.
115 */
116 public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
117
118 /**
119 * Minimum allowed value for progress delta.
120 */
121 public static final float MIN_PROGRESS_DELTA = 0.0f;
122
123 /**
124 * Maximum allowed value for progress delta.
125 */
126 public static final float MAX_PROGRESS_DELTA = 1.0f;
127
128 /**
129 * Constant defining default confidence of the estimated result, which is
130 * 99%. This means that with a probability of 99% estimation will be
131 * accurate because chosen sub-samples will be inliers.
132 */
133 public static final double DEFAULT_CONFIDENCE = 0.99;
134
135 /**
136 * Default maximum allowed number of iterations.
137 */
138 public static final int DEFAULT_MAX_ITERATIONS = 5000;
139
140 /**
141 * Minimum allowed confidence value.
142 */
143 public static final double MIN_CONFIDENCE = 0.0;
144
145 /**
146 * Maximum allowed confidence value.
147 */
148 public static final double MAX_CONFIDENCE = 1.0;
149
150 /**
151 * Minimum allowed number of iterations.
152 */
153 public static final int MIN_ITERATIONS = 1;
154
155 /**
156 * Contains a list of body kinematics measurements taken at different
157 * frames (positions, orientations and velocities) and containing the standard
158 * deviations of accelerometer and gyroscope measurements.
159 * If a single device IMU needs to be calibrated, typically all measurements are
160 * taken at the same position, with zero velocity and multiple orientations.
161 * However, if we just want to calibrate a given IMU model (e.g. obtain
162 * an average and less precise calibration for the IMU of a given phone model),
163 * we could take measurements collected throughout the planet at multiple positions
164 * while the phone remains static (e.g. while charging), hence each measurement
165 * position will change, velocity will remain zero and orientation will be
166 * typically constant at horizontal orientation while the phone remains on a
167 * flat surface.
168 */
169 protected List<StandardDeviationFrameBodyKinematics> measurements;
170
171 /**
172 * Listener to be notified of events such as when calibration starts, ends or its
173 * progress significantly changes.
174 */
175 protected RobustKnownFrameGyroscopeCalibratorListener listener;
176
177 /**
178 * Indicates whether estimator is running.
179 */
180 protected boolean running;
181
182 /**
183 * Amount of progress variation before notifying a progress change during calibration.
184 */
185 protected float progressDelta = DEFAULT_PROGRESS_DELTA;
186
187 /**
188 * Amount of confidence expressed as a value between 0.0 and 1.0 (which is equivalent
189 * to 100%). The amount of confidence indicates the probability that the estimated
190 * result is correct. Usually this value will be close to 1.0, but not exactly 1.0.
191 */
192 protected double confidence = DEFAULT_CONFIDENCE;
193
194 /**
195 * Maximum allowed number of iterations. When the maximum number of iterations is
196 * exceeded, result will not be available, however an approximate result will be
197 * available for retrieval.
198 */
199 protected int maxIterations = DEFAULT_MAX_ITERATIONS;
200
201 /**
202 * Data related to inliers found after calibration.
203 */
204 protected InliersData inliersData;
205
206 /**
207 * Indicates whether result must be refined using a non linear calibrator over
208 * found inliers.
209 * If true, inliers will be computed and kept in any implementation regardless of the
210 * settings.
211 */
212 protected boolean refineResult = DEFAULT_REFINE_RESULT;
213
214 /**
215 * Size of subsets to be checked during robust estimation.
216 */
217 protected int preliminarySubsetSize = MINIMUM_MEASUREMENTS;
218
219 /**
220 * This flag indicates whether z-axis is assumed to be common for accelerometer
221 * and gyroscope.
222 * When enabled, this eliminates 3 variables from Ma matrix.
223 */
224 private boolean commonAxisUsed = DEFAULT_USE_COMMON_Z_AXIS;
225
226 /**
227 * Initial x-coordinate of gyroscope bias to be used to find a solution.
228 * This is expressed in radians per second (rad/s).
229 */
230 private double initialBiasX;
231
232 /**
233 * Initial y-coordinate of gyroscope bias to be used to find a solution.
234 * This is expressed in radians per second (rad/s).
235 */
236 private double initialBiasY;
237
238 /**
239 * Initial z-coordinate of gyroscope bias to be used to find a solution.
240 * This is expressed in radians per second (rad/s).
241 */
242 private double initialBiasZ;
243
244 /**
245 * Initial x scaling factor.
246 */
247 private double initialSx;
248
249 /**
250 * Initial y scaling factor.
251 */
252 private double initialSy;
253
254 /**
255 * Initial z scaling factor.
256 */
257 private double initialSz;
258
259 /**
260 * Initial x-y cross coupling error.
261 */
262 private double initialMxy;
263
264 /**
265 * Initial x-z cross coupling error.
266 */
267 private double initialMxz;
268
269 /**
270 * Initial y-x cross coupling error.
271 */
272 private double initialMyx;
273
274 /**
275 * Initial y-z cross coupling error.
276 */
277 private double initialMyz;
278
279 /**
280 * Initial z-x cross coupling error.
281 */
282 private double initialMzx;
283
284 /**
285 * Initial z-y cross coupling error.
286 */
287 private double initialMzy;
288
289 /**
290 * Initial G-dependent cross biases introduced on the gyroscope by the
291 * specific forces sensed by the accelerometer.
292 */
293 private Matrix initialGg;
294
295 /**
296 * Indicates whether a linear calibrator is used or not for preliminary
297 * solutions.
298 */
299 private boolean useLinearCalibrator = DEFAULT_USE_LINEAR_CALIBRATOR;
300
301 /**
302 * Indicates whether preliminary solutions must be refined after an initial linear solution
303 * is found.
304 */
305 private boolean refinePreliminarySolutions = DEFAULT_REFINE_PRELIMINARY_SOLUTIONS;
306
307 /**
308 * Estimated gyroscope biases for each IMU axis expressed in radians per second
309 * (rad/s).
310 */
311 private double[] estimatedBiases;
312
313 /**
314 * Estimated gyroscope scale factors and cross coupling errors.
315 * This is the product of matrix Tg containing cross coupling errors and Kg
316 * containing scaling factors.
317 * So that:
318 * <pre>
319 * Mg = [sx mxy mxz] = Tg*Kg
320 * [myx sy myz]
321 * [mzx mzy sz ]
322 * </pre>
323 * Where:
324 * <pre>
325 * Kg = [sx 0 0 ]
326 * [0 sy 0 ]
327 * [0 0 sz]
328 * </pre>
329 * and
330 * <pre>
331 * Tg = [1 -alphaXy alphaXz ]
332 * [alphaYx 1 -alphaYz]
333 * [-alphaZx alphaZy 1 ]
334 * </pre>
335 * Hence:
336 * <pre>
337 * Mg = [sx mxy mxz] = Tg*Kg = [sx -sy * alphaXy sz * alphaXz ]
338 * [myx sy myz] [sx * alphaYx sy -sz * alphaYz]
339 * [mzx mzy sz ] [-sx * alphaZx sy * alphaZy sz ]
340 * </pre>
341 * This instance allows any 3x3 matrix however, typically alphaYx, alphaZx and alphaZy
342 * are considered to be zero if the gyroscope z-axis is assumed to be the same
343 * as the body z-axis. When this is assumed, myx = mzx = mzy = 0 and the Mg matrix
344 * becomes upper diagonal:
345 * <pre>
346 * Mg = [sx mxy mxz]
347 * [0 sy myz]
348 * [0 0 sz ]
349 * </pre>
350 * Values of this matrix are unit-less.
351 */
352 private Matrix estimatedMg;
353
354 /**
355 * Estimated G-dependent cross biases introduced on the gyroscope by the
356 * specific forces sensed by the accelerometer.
357 * This instance allows any 3x3 matrix.
358 */
359 private Matrix estimatedGg;
360
361 /**
362 * Indicates whether covariance must be kept after refining result.
363 * This setting is only taken into account if result is refined.
364 */
365 private boolean keepCovariance = DEFAULT_KEEP_COVARIANCE;
366
367 /**
368 * Estimated covariance of estimated position.
369 * This is only available when result has been refined and covariance is kept.
370 */
371 private Matrix estimatedCovariance;
372
373 /**
374 * Estimated chi square value.
375 */
376 private double estimatedChiSq;
377
378 /**
379 * Estimated degrees of freedom of chi square value. Degrees of freedom is equal to the number of sampled data
380 * minus the number of estimated parameters.
381 */
382 private int estimatedChiSqDegreesOfFreedom;
383
384 /**
385 * Estimated reduced chi square value. This is equal to estimated chi square value divided by its degrees of
386 * freedom. Ideally this value should be close to 1.0.
387 */
388 private double estimatedReducedChiSq;
389
390 /**
391 * Estimated mean square error respect to provided measurements.
392 */
393 private double estimatedMse;
394
395 /**
396 * Estimated probability of finding a smaller chi square value expressed as a value between 0.0 and 1.0. The smaller
397 * the found chi square value is, the better the fit of the estimated parameters to the actual parameter. Thus, the
398 * smaller the chance of finding a smaller chi square value, then the better the estimated fit is.
399 */
400 private double estimatedP;
401
402 /**
403 * Estimated measure of quality of estimated fit as a value between 0.0 and 1.0. The larger the quality value is,
404 * the better the fit that has been estimated.
405 */
406 private double estimatedQ;
407
408 /**
409 * A linear least squares calibrator.
410 */
411 private final KnownFrameGyroscopeLinearLeastSquaresCalibrator linearCalibrator =
412 new KnownFrameGyroscopeLinearLeastSquaresCalibrator();
413
414 /**
415 * A non-linear least squares calibrator.
416 */
417 private final KnownFrameGyroscopeNonLinearLeastSquaresCalibrator nonLinearCalibrator =
418 new KnownFrameGyroscopeNonLinearLeastSquaresCalibrator();
419
420 /**
421 * Constructor.
422 */
423 protected RobustKnownFrameGyroscopeCalibrator() {
424 try {
425 initialGg = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
426 } catch (final WrongSizeException ignore) {
427 // never happens
428 }
429 }
430
431 /**
432 * Constructor.
433 *
434 * @param listener listener to be notified of events such as when estimation
435 * starts, ends or its progress significantly changes.
436 */
437 protected RobustKnownFrameGyroscopeCalibrator(final RobustKnownFrameGyroscopeCalibratorListener listener) {
438 this();
439 this.listener = listener;
440 }
441
442 /**
443 * Constructor.
444 *
445 * @param measurements list of body kinematics measurements with standard
446 * deviations taken at different frames (positions, orientations
447 * and velocities).
448 */
449 protected RobustKnownFrameGyroscopeCalibrator(final List<StandardDeviationFrameBodyKinematics> measurements) {
450 this();
451 this.measurements = measurements;
452 }
453
454 /**
455 * Constructor.
456 *
457 * @param measurements list of body kinematics measurements with standard
458 * deviations taken at different frames (positions, orientations
459 * and velocities).
460 * @param listener listener to be notified of events such as when estimation
461 * starts, ends or its progress significantly changes.
462 */
463 protected RobustKnownFrameGyroscopeCalibrator(
464 final List<StandardDeviationFrameBodyKinematics> measurements,
465 final RobustKnownFrameGyroscopeCalibratorListener listener) {
466 this(measurements);
467 this.listener = listener;
468 }
469
470 /**
471 * Constructor.
472 *
473 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
474 * accelerometer and gyroscope.
475 */
476 protected RobustKnownFrameGyroscopeCalibrator(final boolean commonAxisUsed) {
477 this();
478 this.commonAxisUsed = commonAxisUsed;
479 }
480
481 /**
482 * Constructor.
483 *
484 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
485 * accelerometer and gyroscope.
486 * @param listener listener to handle events raised by this calibrator.
487 */
488 protected RobustKnownFrameGyroscopeCalibrator(
489 final boolean commonAxisUsed, final RobustKnownFrameGyroscopeCalibratorListener listener) {
490 this(commonAxisUsed);
491 this.listener = listener;
492 }
493
494 /**
495 * Constructor.
496 *
497 * @param measurements list of body kinematics measurements with standard
498 * deviations taken at different frames (positions, orientations
499 * and velocities).
500 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
501 * accelerometer and gyroscope.
502 */
503 protected RobustKnownFrameGyroscopeCalibrator(
504 final List<StandardDeviationFrameBodyKinematics> measurements, final boolean commonAxisUsed) {
505 this(measurements);
506 this.commonAxisUsed = commonAxisUsed;
507 }
508
509 /**
510 * Constructor.
511 *
512 * @param measurements list of body kinematics measurements with standard
513 * deviations taken at different frames (positions, orientations
514 * and velocities).
515 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
516 * accelerometer and gyroscope.
517 * @param listener listener to handle events raised by this calibrator.
518 */
519 protected RobustKnownFrameGyroscopeCalibrator(
520 final List<StandardDeviationFrameBodyKinematics> measurements, final boolean commonAxisUsed,
521 final RobustKnownFrameGyroscopeCalibratorListener listener) {
522 this(measurements, commonAxisUsed);
523 this.listener = listener;
524 }
525
526 /**
527 * Gets initial x-coordinate of gyroscope bias to be used to find a solutions.
528 * This is expressed in radians per second (rad/s) and only taken into
529 * account if non-linear preliminary solutions are used.
530 *
531 * @return initial x-coordinate of gyroscope bias.
532 */
533 @Override
534 public double getInitialBiasX() {
535 return initialBiasX;
536 }
537
538 /**
539 * Sets initial x-coordinate of gyroscope bias to be used to find a solution.
540 * This is expressed in radians per second (rad/s) and only taken into
541 * account if non-linear preliminary solutions are used.
542 *
543 * @param initialBiasX initial x-coordinate of gyroscope bias.
544 * @throws LockedException if calibrator is currently running.
545 */
546 @Override
547 public void setInitialBiasX(final double initialBiasX) throws LockedException {
548 if (running) {
549 throw new LockedException();
550 }
551 this.initialBiasX = initialBiasX;
552 }
553
554 /**
555 * Gets initial y-coordinate of gyroscope bias to be used to find a solution.
556 * This is expressed in radians per second (rad/s) and only taken into
557 * account if non-linear preliminary solutions are used.
558 *
559 * @return initial y-coordinate of gyroscope bias.
560 */
561 @Override
562 public double getInitialBiasY() {
563 return initialBiasY;
564 }
565
566 /**
567 * Sets initial y-coordinate of gyroscope bias to be used to find a solution.
568 * This is expressed in radians per second (rad/s) and only taken into
569 * account if non-linear preliminary solutions are used.
570 *
571 * @param initialBiasY initial y-coordinate of gyroscope bias.
572 * @throws LockedException if calibrator is currently running.
573 */
574 @Override
575 public void setInitialBiasY(final double initialBiasY) throws LockedException {
576 if (running) {
577 throw new LockedException();
578 }
579 this.initialBiasY = initialBiasY;
580 }
581
582 /**
583 * Gets initial z-coordinate of gyroscope bias to be used to find a solution.
584 * This is expressed in radians per second (rad/s) and only taken into
585 * account if non-linear preliminary solutions are used.
586 *
587 * @return initial z-coordinate of gyroscope bias.
588 */
589 @Override
590 public double getInitialBiasZ() {
591 return initialBiasZ;
592 }
593
594 /**
595 * Sets initial z-coordinate of gyroscope bias to be used to find a solution.
596 * This is expressed in radians per second (rad/s) and only taken into
597 * account if non-linear preliminary solutions are used.
598 *
599 * @param initialBiasZ initial z-coordinate of gyroscope bias.
600 * @throws LockedException if calibrator is currently running.
601 */
602 @Override
603 public void setInitialBiasZ(final double initialBiasZ) throws LockedException {
604 if (running) {
605 throw new LockedException();
606 }
607 this.initialBiasZ = initialBiasZ;
608 }
609
610 /**
611 * Gets initial x-coordinate of gyroscope bias to be used to find a solution.
612 *
613 * @return initial x-coordinate of gyroscope bias.
614 */
615 @Override
616 public AngularSpeed getInitialBiasAngularSpeedX() {
617 return new AngularSpeed(initialBiasX, AngularSpeedUnit.RADIANS_PER_SECOND);
618 }
619
620 /**
621 * Gets initial x-coordinate of gyroscope bias to be used to find a solution.
622 *
623 * @param result instance where result data will be stored.
624 */
625 @Override
626 public void getInitialBiasAngularSpeedX(final AngularSpeed result) {
627 result.setValue(initialBiasX);
628 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
629 }
630
631 /**
632 * Sets initial x-coordinate of gyroscope bias to be used to find a solution.
633 *
634 * @param initialBiasX initial x-coordinate of gyroscope bias.
635 * @throws LockedException if calibrator is currently running.
636 */
637 @Override
638 public void setInitialBiasX(final AngularSpeed initialBiasX) throws LockedException {
639 if (running) {
640 throw new LockedException();
641 }
642 this.initialBiasX = convertAngularSpeed(initialBiasX);
643 }
644
645 /**
646 * Gets initial y-coordinate of gyroscope bias to be used to find a solution.
647 *
648 * @return initial y-coordinate of gyroscope bias.
649 */
650 @Override
651 public AngularSpeed getInitialBiasAngularSpeedY() {
652 return new AngularSpeed(initialBiasY, AngularSpeedUnit.RADIANS_PER_SECOND);
653 }
654
655 /**
656 * Gets initial y-coordinate of gyroscope bias to be used to find a solution.
657 *
658 * @param result instance where result data will be stored.
659 */
660 @Override
661 public void getInitialBiasAngularSpeedY(final AngularSpeed result) {
662 result.setValue(initialBiasY);
663 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
664 }
665
666 /**
667 * Sets initial y-coordinate of gyroscope bias to be used to find a solution.
668 *
669 * @param initialBiasY initial y-coordinate of gyroscope bias.
670 * @throws LockedException if calibrator is currently running.
671 */
672 @Override
673 public void setInitialBiasY(final AngularSpeed initialBiasY) throws LockedException {
674 if (running) {
675 throw new LockedException();
676 }
677 this.initialBiasY = convertAngularSpeed(initialBiasY);
678 }
679
680 /**
681 * Gets initial z-coordinate of gyroscope bias to be used to find a solution.
682 *
683 * @return initial z-coordinate of gyroscope bias.
684 */
685 @Override
686 public AngularSpeed getInitialBiasAngularSpeedZ() {
687 return new AngularSpeed(initialBiasZ, AngularSpeedUnit.RADIANS_PER_SECOND);
688 }
689
690 /**
691 * Gets initial z-coordinate of gyroscope bias to be used to find a solution.
692 *
693 * @param result instance where result data will be stored.
694 */
695 @Override
696 public void getInitialBiasAngularSpeedZ(final AngularSpeed result) {
697 result.setValue(initialBiasZ);
698 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
699 }
700
701 /**
702 * Sets initial z-coordinate of gyroscope bias to be used to find a solution.
703 *
704 * @param initialBiasZ initial z-coordinate of gyroscope bias.
705 * @throws LockedException if calibrator is currently running.
706 */
707 @Override
708 public void setInitialBiasZ(final AngularSpeed initialBiasZ) throws LockedException {
709 if (running) {
710 throw new LockedException();
711 }
712 this.initialBiasZ = convertAngularSpeed(initialBiasZ);
713 }
714
715 /**
716 * Sets initial bias coordinates of gyroscope used to find a solution
717 * expressed in radians per second (rad/s).
718 *
719 * @param initialBiasX initial x-coordinate of gyroscope bias.
720 * @param initialBiasY initial y-coordinate of gyroscope bias.
721 * @param initialBiasZ initial z-coordinate of gyroscope bias.
722 * @throws LockedException if calibrator is currently running.
723 */
724 @Override
725 public void setInitialBias(final double initialBiasX, final double initialBiasY, final double initialBiasZ)
726 throws LockedException {
727 if (running) {
728 throw new LockedException();
729 }
730 this.initialBiasX = initialBiasX;
731 this.initialBiasY = initialBiasY;
732 this.initialBiasZ = initialBiasZ;
733 }
734
735 /**
736 * Sets initial bias coordinates of gyroscope used to find a solution.
737 *
738 * @param initialBiasX initial x-coordinate of gyroscope bias.
739 * @param initialBiasY initial y-coordinate of gyroscope bias.
740 * @param initialBiasZ initial z-coordinate of gyroscope bias.
741 * @throws LockedException if calibrator is currently running.
742 */
743 @Override
744 public void setInitialBias(
745 final AngularSpeed initialBiasX, final AngularSpeed initialBiasY, final AngularSpeed initialBiasZ)
746 throws LockedException {
747
748 if (running) {
749 throw new LockedException();
750 }
751 this.initialBiasX = convertAngularSpeed(initialBiasX);
752 this.initialBiasY = convertAngularSpeed(initialBiasY);
753 this.initialBiasZ = convertAngularSpeed(initialBiasZ);
754 }
755
756 /**
757 * Gets initial x scaling factor.
758 * This is only taken into account if non-linear preliminary solutions are used.
759 *
760 * @return initial x scaling factor.
761 */
762 @Override
763 public double getInitialSx() {
764 return initialSx;
765 }
766
767 /**
768 * Sets initial x scaling factor.
769 * This is only taken into account if non-linear preliminary solutions are used.
770 *
771 * @param initialSx initial x scaling factor.
772 * @throws LockedException if calibrator is currently running.
773 */
774 @Override
775 public void setInitialSx(final double initialSx) throws LockedException {
776 if (running) {
777 throw new LockedException();
778 }
779 this.initialSx = initialSx;
780 }
781
782 /**
783 * Gets initial y scaling factor.
784 * This is only taken into account if non-linear preliminary solutions are used.
785 *
786 * @return initial y scaling factor.
787 */
788 @Override
789 public double getInitialSy() {
790 return initialSy;
791 }
792
793 /**
794 * Sets initial y scaling factor.
795 * This is only taken into account if non-linear preliminary solutions are used.
796 *
797 * @param initialSy initial y scaling factor.
798 * @throws LockedException if calibrator is currently running.
799 */
800 @Override
801 public void setInitialSy(final double initialSy) throws LockedException {
802 if (running) {
803 throw new LockedException();
804 }
805 this.initialSy = initialSy;
806 }
807
808 /**
809 * Gets initial z scaling factor.
810 * This is only taken into account if non-linear preliminary solutions are used.
811 *
812 * @return initial z scaling factor.
813 */
814 @Override
815 public double getInitialSz() {
816 return initialSz;
817 }
818
819 /**
820 * Sets initial z scaling factor.
821 * This is only taken into account if non-linear preliminary solutions are used.
822 *
823 * @param initialSz initial z scaling factor.
824 * @throws LockedException if calibrator is currently running.
825 */
826 @Override
827 public void setInitialSz(final double initialSz) throws LockedException {
828 if (running) {
829 throw new LockedException();
830 }
831 this.initialSz = initialSz;
832 }
833
834 /**
835 * Gets initial x-y cross coupling error.
836 * This is only taken into account if non-linear preliminary solutions are used.
837 *
838 * @return initial x-y cross coupling error.
839 */
840 @Override
841 public double getInitialMxy() {
842 return initialMxy;
843 }
844
845 /**
846 * Sets initial x-y cross coupling error.
847 * This is only taken into account if non-linear preliminary solutions are used.
848 *
849 * @param initialMxy initial x-y cross coupling error.
850 * @throws LockedException if calibrator is currently running.
851 */
852 @Override
853 public void setInitialMxy(final double initialMxy) throws LockedException {
854 if (running) {
855 throw new LockedException();
856 }
857 this.initialMxy = initialMxy;
858 }
859
860 /**
861 * Gets initial x-z cross coupling error.
862 * This is only taken into account if non-linear preliminary solutions are used.
863 *
864 * @return initial x-z cross coupling error.
865 */
866 @Override
867 public double getInitialMxz() {
868 return initialMxz;
869 }
870
871 /**
872 * Sets initial x-z cross coupling error.
873 * This is only taken into account if non-linear preliminary solutions are used.
874 *
875 * @param initialMxz initial x-z cross coupling error.
876 * @throws LockedException if calibrator is currently running.
877 */
878 @Override
879 public void setInitialMxz(final double initialMxz) throws LockedException {
880 if (running) {
881 throw new LockedException();
882 }
883 this.initialMxz = initialMxz;
884 }
885
886 /**
887 * Gets initial y-x cross coupling error.
888 * This is only taken into account if non-linear preliminary solutions are used.
889 *
890 * @return initial y-x cross coupling error.
891 */
892 @Override
893 public double getInitialMyx() {
894 return initialMyx;
895 }
896
897 /**
898 * Sets initial y-x cross coupling error.
899 * This is only taken into account if non-linear preliminary solutions are used.
900 *
901 * @param initialMyx initial y-x cross coupling error.
902 * @throws LockedException if calibrator is currently running.
903 */
904 @Override
905 public void setInitialMyx(final double initialMyx) throws LockedException {
906 if (running) {
907 throw new LockedException();
908 }
909 this.initialMyx = initialMyx;
910 }
911
912 /**
913 * Gets initial y-z cross coupling error.
914 * This is only taken into account if non-linear preliminary solutions are used.
915 *
916 * @return initial y-z cross coupling error.
917 */
918 @Override
919 public double getInitialMyz() {
920 return initialMyz;
921 }
922
923 /**
924 * Sets initial y-z cross coupling error.
925 * This is only taken into account if non-linear preliminary solutions are used.
926 *
927 * @param initialMyz initial y-z cross coupling error.
928 * @throws LockedException if calibrator is currently running.
929 */
930 @Override
931 public void setInitialMyz(final double initialMyz) throws LockedException {
932 if (running) {
933 throw new LockedException();
934 }
935 this.initialMyz = initialMyz;
936 }
937
938 /**
939 * Gets initial z-x cross coupling error.
940 * This is only taken into account if non-linear preliminary solutions are used.
941 *
942 * @return initial z-x cross coupling error.
943 */
944 @Override
945 public double getInitialMzx() {
946 return initialMzx;
947 }
948
949 /**
950 * Sets initial z-x cross coupling error.
951 * This is only taken into account if non-linear preliminary solutions are used.
952 *
953 * @param initialMzx initial z-x cross coupling error.
954 * @throws LockedException if calibrator is currently running.
955 */
956 @Override
957 public void setInitialMzx(final double initialMzx) throws LockedException {
958 if (running) {
959 throw new LockedException();
960 }
961 this.initialMzx = initialMzx;
962 }
963
964 /**
965 * Gets initial z-y cross coupling error.
966 * This is only taken into account if non-linear preliminary solutions are used.
967 *
968 * @return initial z-y cross coupling error.
969 */
970 @Override
971 public double getInitialMzy() {
972 return initialMzy;
973 }
974
975 /**
976 * Sets initial z-y cross coupling error.
977 * This is only taken into account if non-linear preliminary solutions are used.
978 *
979 * @param initialMzy initial z-y cross coupling error.
980 * @throws LockedException if calibrator is currently running.
981 */
982 @Override
983 public void setInitialMzy(final double initialMzy) throws LockedException {
984 if (running) {
985 throw new LockedException();
986 }
987 this.initialMzy = initialMzy;
988 }
989
990 /**
991 * Sets initial scaling factors.
992 * This is only taken into account if non-linear preliminary solutions are used.
993 *
994 * @param initialSx initial x scaling factor.
995 * @param initialSy initial y scaling factor.
996 * @param initialSz initial z scaling factor.
997 * @throws LockedException if calibrator is currently running.
998 */
999 @Override
1000 public void setInitialScalingFactors(
1001 final double initialSx, final double initialSy, final double initialSz) throws LockedException {
1002 if (running) {
1003 throw new LockedException();
1004 }
1005 this.initialSx = initialSx;
1006 this.initialSy = initialSy;
1007 this.initialSz = initialSz;
1008 }
1009
1010 /**
1011 * Sets initial cross coupling errors.
1012 * This is only taken into account if non-linear preliminary solutions are used.
1013 *
1014 * @param initialMxy initial x-y cross coupling error.
1015 * @param initialMxz initial x-z cross coupling error.
1016 * @param initialMyx initial y-x cross coupling error.
1017 * @param initialMyz initial y-z cross coupling error.
1018 * @param initialMzx initial z-x cross coupling error.
1019 * @param initialMzy initial z-y cross coupling error.
1020 * @throws LockedException if calibrator is currently running.
1021 */
1022 @Override
1023 public void setInitialCrossCouplingErrors(
1024 final double initialMxy, final double initialMxz, final double initialMyx,
1025 final double initialMyz, final double initialMzx, final double initialMzy) throws LockedException {
1026 if (running) {
1027 throw new LockedException();
1028 }
1029 this.initialMxy = initialMxy;
1030 this.initialMxz = initialMxz;
1031 this.initialMyx = initialMyx;
1032 this.initialMyz = initialMyz;
1033 this.initialMzx = initialMzx;
1034 this.initialMzy = initialMzy;
1035 }
1036
1037 /**
1038 * Sets initial scaling factors and cross coupling errors.
1039 * This is only taken into account if non-linear preliminary solutions are used.
1040 *
1041 * @param initialSx initial x scaling factor.
1042 * @param initialSy initial y scaling factor.
1043 * @param initialSz initial z scaling factor.
1044 * @param initialMxy initial x-y cross coupling error.
1045 * @param initialMxz initial x-z cross coupling error.
1046 * @param initialMyx initial y-x cross coupling error.
1047 * @param initialMyz initial y-z cross coupling error.
1048 * @param initialMzx initial z-x cross coupling error.
1049 * @param initialMzy initial z-y cross coupling error.
1050 * @throws LockedException if calibrator is currently running.
1051 */
1052 @Override
1053 public void setInitialScalingFactorsAndCrossCouplingErrors(
1054 final double initialSx, final double initialSy, final double initialSz,
1055 final double initialMxy, final double initialMxz, final double initialMyx,
1056 final double initialMyz, final double initialMzx, final double initialMzy) throws LockedException {
1057 if (running) {
1058 throw new LockedException();
1059 }
1060 setInitialScalingFactors(initialSx, initialSy, initialSz);
1061 setInitialCrossCouplingErrors(initialMxy, initialMxz, initialMyx, initialMyz, initialMzx, initialMzy);
1062 }
1063
1064 /**
1065 * Gets initial bias to be used to find a solution as an array.
1066 * Array values are expressed in radians per second (rad/s).
1067 *
1068 * @return array containing coordinates of initial bias.
1069 */
1070 @Override
1071 public double[] getInitialBias() {
1072 final var result = new double[BodyKinematics.COMPONENTS];
1073 getInitialBias(result);
1074 return result;
1075 }
1076
1077 /**
1078 * Gets initial bias to be used to find a solution as an array.
1079 * Array values are expressed in radians per second (rad/s).
1080 *
1081 * @param result instance where result data will be copied to.
1082 * @throws IllegalArgumentException if provided array does not have length 3.
1083 */
1084 @Override
1085 public void getInitialBias(final double[] result) {
1086 if (result.length != BodyKinematics.COMPONENTS) {
1087 throw new IllegalArgumentException();
1088 }
1089 result[0] = initialBiasX;
1090 result[1] = initialBiasY;
1091 result[2] = initialBiasZ;
1092 }
1093
1094 /**
1095 * Sets initial bias to be used to find a solution as an array.
1096 * Array values are expressed in radians per second (rad/s).
1097 *
1098 * @param initialBias initial bias to find a solution.
1099 * @throws LockedException if calibrator is currently running.
1100 * @throws IllegalArgumentException if provided array does not have length 3.
1101 */
1102 @Override
1103 public void setInitialBias(final double[] initialBias) throws LockedException {
1104 if (running) {
1105 throw new LockedException();
1106 }
1107
1108 if (initialBias.length != BodyKinematics.COMPONENTS) {
1109 throw new IllegalArgumentException();
1110 }
1111 initialBiasX = initialBias[0];
1112 initialBiasY = initialBias[1];
1113 initialBiasZ = initialBias[2];
1114 }
1115
1116 /**
1117 * Gets initial bias to be used to find a solution as a column matrix.
1118 * Values are expressed in radians per second (rad/s).
1119 *
1120 * @return initial bias to be used to find a solution as a column matrix.
1121 */
1122 @Override
1123 public Matrix getInitialBiasAsMatrix() {
1124 Matrix result;
1125 try {
1126 result = new Matrix(BodyKinematics.COMPONENTS, 1);
1127 getInitialBiasAsMatrix(result);
1128 } catch (final WrongSizeException ignore) {
1129 // never happens
1130 result = null;
1131 }
1132 return result;
1133 }
1134
1135 /**
1136 * Gets initial bias to be used to find a solution as a column matrix.
1137 * Values are expressed in radians per second (rad/s).
1138 *
1139 * @param result instance where result data will be copied to.
1140 * @throws IllegalArgumentException if provided matrix is not 3x1.
1141 */
1142 @Override
1143 public void getInitialBiasAsMatrix(final Matrix result) {
1144 if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != 1) {
1145 throw new IllegalArgumentException();
1146 }
1147 result.setElementAtIndex(0, initialBiasX);
1148 result.setElementAtIndex(1, initialBiasY);
1149 result.setElementAtIndex(2, initialBiasZ);
1150 }
1151
1152 /**
1153 * Sets initial bias to be used to find a solution as a column matrix
1154 * with values expressed in radians per second (rad/s).
1155 *
1156 * @param initialBias initial bias to find a solution.
1157 * @throws LockedException if calibrator is currently running.
1158 * @throws IllegalArgumentException if provided matrix is not 3x1.
1159 */
1160 @Override
1161 public void setInitialBias(final Matrix initialBias) throws LockedException {
1162 if (running) {
1163 throw new LockedException();
1164 }
1165 if (initialBias.getRows() != BodyKinematics.COMPONENTS || initialBias.getColumns() != 1) {
1166 throw new IllegalArgumentException();
1167 }
1168
1169 initialBiasX = initialBias.getElementAtIndex(0);
1170 initialBiasY = initialBias.getElementAtIndex(1);
1171 initialBiasZ = initialBias.getElementAtIndex(2);
1172 }
1173
1174 /**
1175 * Gets initial bias coordinates of gyroscope used to find a solution.
1176 *
1177 * @return initial bias coordinates.
1178 */
1179 @Override
1180 public AngularSpeedTriad getInitialBiasAsTriad() {
1181 return new AngularSpeedTriad(AngularSpeedUnit.RADIANS_PER_SECOND, initialBiasX, initialBiasY, initialBiasZ);
1182 }
1183
1184 /**
1185 * Gets initial bias coordinates of gyroscope used to find a solution.
1186 *
1187 * @param result instance where result will be stored.
1188 */
1189 @Override
1190 public void getInitialBiasAsTriad(final AngularSpeedTriad result) {
1191 result.setValueCoordinatesAndUnit(initialBiasX, initialBiasY, initialBiasZ,
1192 AngularSpeedUnit.RADIANS_PER_SECOND);
1193 }
1194
1195 /**
1196 * Sets initial bias coordinates of gyroscope used to find a solution.
1197 *
1198 * @param initialBias initial bias coordinates to be set.
1199 * @throws LockedException if calibrator is currently running.
1200 */
1201 @Override
1202 public void setInitialBias(final AngularSpeedTriad initialBias) throws LockedException {
1203 if (running) {
1204 throw new LockedException();
1205 }
1206
1207 initialBiasX = convertAngularSpeed(initialBias.getValueX(), initialBias.getUnit());
1208 initialBiasY = convertAngularSpeed(initialBias.getValueY(), initialBias.getUnit());
1209 initialBiasZ = convertAngularSpeed(initialBias.getValueZ(), initialBias.getUnit());
1210 }
1211
1212 /**
1213 * Gets initial scale factors and cross coupling errors matrix.
1214 *
1215 * @return initial scale factors and cross coupling errors matrix.
1216 */
1217 @Override
1218 public Matrix getInitialMg() {
1219 Matrix result;
1220 try {
1221 result = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
1222 getInitialMg(result);
1223 } catch (final WrongSizeException ignore) {
1224 // never happens
1225 result = null;
1226 }
1227 return result;
1228 }
1229
1230 /**
1231 * Gets initial scale factors and cross coupling errors matrix.
1232 *
1233 * @param result instance where data will be stored.
1234 * @throws IllegalArgumentException if provided matrix is not 3x3.
1235 */
1236 @Override
1237 public void getInitialMg(final Matrix result) {
1238 if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != BodyKinematics.COMPONENTS) {
1239 throw new IllegalArgumentException();
1240 }
1241 result.setElementAtIndex(0, initialSx);
1242 result.setElementAtIndex(1, initialMyx);
1243 result.setElementAtIndex(2, initialMzx);
1244
1245 result.setElementAtIndex(3, initialMxy);
1246 result.setElementAtIndex(4, initialSy);
1247 result.setElementAtIndex(5, initialMzy);
1248
1249 result.setElementAtIndex(6, initialMxz);
1250 result.setElementAtIndex(7, initialMyz);
1251 result.setElementAtIndex(8, initialSz);
1252 }
1253
1254 /**
1255 * Sets initial scale factors and cross coupling errors matrix.
1256 *
1257 * @param initialMg initial scale factors and cross coupling errors matrix.
1258 * @throws IllegalArgumentException if provided matrix is not 3x3.
1259 * @throws LockedException if calibrator is currently running.
1260 */
1261 @Override
1262 public void setInitialMg(final Matrix initialMg) throws LockedException {
1263 if (running) {
1264 throw new LockedException();
1265 }
1266 if (initialMg.getRows() != BodyKinematics.COMPONENTS || initialMg.getColumns() != BodyKinematics.COMPONENTS) {
1267 throw new IllegalArgumentException();
1268 }
1269
1270 initialSx = initialMg.getElementAtIndex(0);
1271 initialMyx = initialMg.getElementAtIndex(1);
1272 initialMzx = initialMg.getElementAtIndex(2);
1273
1274 initialMxy = initialMg.getElementAtIndex(3);
1275 initialSy = initialMg.getElementAtIndex(4);
1276 initialMzy = initialMg.getElementAtIndex(5);
1277
1278 initialMxz = initialMg.getElementAtIndex(6);
1279 initialMyz = initialMg.getElementAtIndex(7);
1280 initialSz = initialMg.getElementAtIndex(8);
1281 }
1282
1283 /**
1284 * Gets initial G-dependent cross biases introduced on the gyroscope by the
1285 * specific forces sensed by the accelerometer.
1286 *
1287 * @return a 3x3 matrix containing initial g-dependent cross biases.
1288 */
1289 @Override
1290 public Matrix getInitialGg() {
1291 return new Matrix(initialGg);
1292 }
1293
1294 /**
1295 * Gets initial G-dependent cross biases introduced on the gyroscope by the
1296 * specific forces sensed by the accelerometer.
1297 *
1298 * @param result instance where data will be stored.
1299 * @throws IllegalArgumentException if provided matrix is not 3x3.
1300 */
1301 @Override
1302 public void getInitialGg(final Matrix result) {
1303 if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != BodyKinematics.COMPONENTS) {
1304 throw new IllegalArgumentException();
1305 }
1306
1307 result.copyFrom(initialGg);
1308 }
1309
1310 /**
1311 * Sets initial G-dependent cross biases introduced on the gyroscope by the
1312 * specific forces sensed by the accelerometer.
1313 *
1314 * @param initialGg g-dependent cross biases.
1315 * @throws LockedException if calibrator is currently running.
1316 * @throws IllegalArgumentException if provided matrix is not 3x3.
1317 */
1318 @Override
1319 public void setInitialGg(final Matrix initialGg) throws LockedException {
1320 if (running) {
1321 throw new LockedException();
1322 }
1323
1324 if (initialGg.getRows() != BodyKinematics.COMPONENTS || initialGg.getColumns() != BodyKinematics.COMPONENTS) {
1325 throw new IllegalArgumentException();
1326 }
1327
1328 initialGg.copyTo(this.initialGg);
1329 }
1330
1331 /**
1332 * Gets a list of body kinematics measurements taken at different
1333 * frames (positions, orientations and velocities) and containing the standard
1334 * deviations of accelerometer and gyroscope measurements.
1335 * If a single device IMU needs to be calibrated, typically all measurements are
1336 * taken at the same position, with zero velocity and multiple orientations.
1337 * However, if we just want to calibrate the a given IMU model (e.g. obtain
1338 * an average and less precise calibration for the IMU of a given phone model),
1339 * we could take measurements collected throughout the planet at multiple positions
1340 * while the phone remains static (e.g. while charging), hence each measurement
1341 * position will change, velocity will remain zero and orientation will be
1342 * typically constant at horizontal orientation while the phone remains on a
1343 * flat surface.
1344 *
1345 * @return a collection of body kinematics measurements taken at different
1346 * frames (positions, orientations and velocities).
1347 */
1348 @Override
1349 public List<StandardDeviationFrameBodyKinematics> getMeasurements() {
1350 return measurements;
1351 }
1352
1353 /**
1354 * Sets a list of body kinematics measurements taken at different
1355 * frames (positions, orientations and velocities) and containing the standard
1356 * deviations of accelerometer and gyroscope measurements.
1357 * If a single device IMU needs to be calibrated, typically all measurements are
1358 * taken at the same position, with zero velocity and multiple orientations.
1359 * However, if we just want to calibrate the a given IMU model (e.g. obtain
1360 * an average and less precise calibration for the IMU of a given phone model),
1361 * we could take measurements collected throughout the planet at multiple positions
1362 * while the phone remains static (e.g. while charging), hence each measurement
1363 * position will change, velocity will remain zero and orientation will be
1364 * typically constant at horizontal orientation while the phone remains on a
1365 * flat surface.
1366 *
1367 * @param measurements collection of body kinematics measurements taken at different
1368 * frames (positions, orientations and velocities).
1369 * @throws LockedException if calibrator is currently running.
1370 */
1371 @Override
1372 public void setMeasurements(final List<StandardDeviationFrameBodyKinematics> measurements) throws LockedException {
1373 if (running) {
1374 throw new LockedException();
1375 }
1376 this.measurements = measurements;
1377 }
1378
1379 /**
1380 * Indicates the type of measurement or sequence used by this calibrator.
1381 *
1382 * @return type of measurement or sequence used by this calibrator.
1383 */
1384 @Override
1385 public GyroscopeCalibratorMeasurementOrSequenceType getMeasurementOrSequenceType() {
1386 return GyroscopeCalibratorMeasurementOrSequenceType.STANDARD_DEVIATION_FRAME_BODY_KINEMATICS_MEASUREMENT;
1387 }
1388
1389 /**
1390 * Indicates whether this calibrator requires ordered measurements or sequences
1391 * in a list or not.
1392 *
1393 * @return true if measurements or sequences must be ordered, false otherwise.
1394 */
1395 @Override
1396 public boolean isOrderedMeasurementsOrSequencesRequired() {
1397 return true;
1398 }
1399
1400 /**
1401 * Indicates whether z-axis is assumed to be common for accelerometer and
1402 * gyroscope.
1403 * When enabled, this eliminates 3 variables from Ma matrix.
1404 *
1405 * @return true if z-axis is assumed to be common for accelerometer and gyroscope,
1406 * false otherwise.
1407 */
1408 @Override
1409 public boolean isCommonAxisUsed() {
1410 return commonAxisUsed;
1411 }
1412
1413 /**
1414 * Specifies whether z-axis is assumed to be common for accelerometer and
1415 * gyroscope.
1416 * When enabled, this eliminates 3 variables from Ma matrix.
1417 *
1418 * @param commonAxisUsed true if z-axis is assumed to be common for accelerometer
1419 * and gyroscope, false otherwise.
1420 * @throws LockedException if calibrator is currently running.
1421 */
1422 @Override
1423 public void setCommonAxisUsed(final boolean commonAxisUsed) throws LockedException {
1424 if (running) {
1425 throw new LockedException();
1426 }
1427
1428 this.commonAxisUsed = commonAxisUsed;
1429 }
1430
1431 /**
1432 * Gets listener to handle events raised by this estimator.
1433 *
1434 * @return listener to handle events raised by this estimator.
1435 */
1436 public RobustKnownFrameGyroscopeCalibratorListener getListener() {
1437 return listener;
1438 }
1439
1440 /**
1441 * Sets listener to handle events raised by this estimator.
1442 *
1443 * @param listener listener to handle events raised by this estimator.
1444 * @throws LockedException if calibrator is currently running.
1445 */
1446 public void setListener(final RobustKnownFrameGyroscopeCalibratorListener listener) throws LockedException {
1447 if (running) {
1448 throw new LockedException();
1449 }
1450
1451 this.listener = listener;
1452 }
1453
1454 /**
1455 * Gets minimum number of required measurements.
1456 *
1457 * @return minimum number of required measurements.
1458 */
1459 @Override
1460 public int getMinimumRequiredMeasurementsOrSequences() {
1461 return MINIMUM_MEASUREMENTS;
1462 }
1463
1464 /**
1465 * Indicates whether calibrator is ready to start.
1466 *
1467 * @return true if calibrator is ready, false otherwise.
1468 */
1469 @Override
1470 public boolean isReady() {
1471 return measurements != null && measurements.size() >= MINIMUM_MEASUREMENTS;
1472 }
1473
1474 /**
1475 * Indicates whether calibrator is currently running or not.
1476 *
1477 * @return true if calibrator is running, false otherwise.
1478 */
1479 @Override
1480 public boolean isRunning() {
1481 return running;
1482 }
1483
1484 /**
1485 * Indicates whether a linear calibrator is used or not for preliminary
1486 * solutions.
1487 *
1488 * @return indicates whether a linear calibrator is used or not for
1489 * preliminary solutions.
1490 */
1491 public boolean isLinearCalibratorUsed() {
1492 return useLinearCalibrator;
1493 }
1494
1495 /**
1496 * Specifies whether a linear calibrator is used or not for preliminary
1497 * solutions.
1498 *
1499 * @param linearCalibratorUsed indicates whether a linear calibrator is used
1500 * or not for preliminary solutions.
1501 * @throws LockedException if calibrator is currently running.
1502 */
1503 public void setLinearCalibratorUsed(final boolean linearCalibratorUsed) throws LockedException {
1504 if (running) {
1505 throw new LockedException();
1506 }
1507 useLinearCalibrator = linearCalibratorUsed;
1508 }
1509
1510 /**
1511 * Indicates whether preliminary solutions must be refined after an initial linear solution is found.
1512 * If no initial solution is found using a linear solver, a non linear solver will be
1513 * used regardless of this value using an average solution as the initial value to be
1514 * refined.
1515 *
1516 * @return true if preliminary solutions must be refined after an initial linear solution, false
1517 * otherwise.
1518 */
1519 public boolean isPreliminarySolutionRefined() {
1520 return refinePreliminarySolutions;
1521 }
1522
1523 /**
1524 * Specifies whether preliminary solutions must be refined after an initial linear solution is found.
1525 * If no initial solution is found using a linear solver, a non linear solver will be
1526 * used regardless of this value using an average solution as the initial value to be
1527 * refined.
1528 *
1529 * @param preliminarySolutionRefined true if preliminary solutions must be refined after an
1530 * initial linear solution, false otherwise.
1531 * @throws LockedException if calibrator is currently running.
1532 */
1533 public void setPreliminarySolutionRefined(final boolean preliminarySolutionRefined) throws LockedException {
1534 if (running) {
1535 throw new LockedException();
1536 }
1537
1538 refinePreliminarySolutions = preliminarySolutionRefined;
1539 }
1540
1541 /**
1542 * Returns amount of progress variation before notifying a progress change during
1543 * calibration.
1544 *
1545 * @return amount of progress variation before notifying a progress change during
1546 * calibration.
1547 */
1548 public float getProgressDelta() {
1549 return progressDelta;
1550 }
1551
1552 /**
1553 * Sets amount of progress variation before notifying a progress change during
1554 * calibration.
1555 *
1556 * @param progressDelta amount of progress variation before notifying a progress
1557 * change during calibration.
1558 * @throws IllegalArgumentException if progress delta is less than zero or greater than 1.
1559 * @throws LockedException if calibrator is currently running.
1560 */
1561 public void setProgressDelta(final float progressDelta) throws LockedException {
1562 if (running) {
1563 throw new LockedException();
1564 }
1565 if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
1566 throw new IllegalArgumentException();
1567 }
1568 this.progressDelta = progressDelta;
1569 }
1570
1571 /**
1572 * Returns amount of confidence expressed as a value between 0.0 and 1.0
1573 * (which is equivalent to 100%). The amount of confidence indicates the probability
1574 * that the estimated result is correct. Usually this value will be close to 1.0, but
1575 * not exactly 1.0.
1576 *
1577 * @return amount of confidence as a value between 0.0 and 1.0.
1578 */
1579 public double getConfidence() {
1580 return confidence;
1581 }
1582
1583 /**
1584 * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which is
1585 * equivalent to 100%). The amount of confidence indicates the probability that
1586 * the estimated result is correct. Usually this value will be close to 1.0, but
1587 * not exactly 1.0.
1588 *
1589 * @param confidence confidence to be set as a value between 0.0 and 1.0.
1590 * @throws IllegalArgumentException if provided value is not between 0.0 and 1.0.
1591 * @throws LockedException if calibrator is currently running.
1592 */
1593 public void setConfidence(final double confidence) throws LockedException {
1594 if (running) {
1595 throw new LockedException();
1596 }
1597 if (confidence < MIN_CONFIDENCE || confidence > MAX_CONFIDENCE) {
1598 throw new IllegalArgumentException();
1599 }
1600 this.confidence = confidence;
1601 }
1602
1603 /**
1604 * Returns maximum allowed number of iterations. If maximum allowed number of
1605 * iterations is achieved without converging to a result when calling calibrate(),
1606 * a RobustEstimatorException will be raised.
1607 *
1608 * @return maximum allowed number of iterations.
1609 */
1610 public int getMaxIterations() {
1611 return maxIterations;
1612 }
1613
1614 /**
1615 * Sets maximum allowed number of iterations. When the maximum number of iterations
1616 * is exceeded, result will not be available, however an approximate result will be
1617 * available for retrieval.
1618 *
1619 * @param maxIterations maximum allowed number of iterations to be set.
1620 * @throws IllegalArgumentException if provided value is less than 1.
1621 * @throws LockedException if calibrator is currently running.
1622 */
1623 public void setMaxIterations(final int maxIterations) throws LockedException {
1624 if (running) {
1625 throw new LockedException();
1626 }
1627 if (maxIterations < MIN_ITERATIONS) {
1628 throw new IllegalArgumentException();
1629 }
1630 this.maxIterations = maxIterations;
1631 }
1632
1633 /**
1634 * Gets data related to inliers found after estimation.
1635 *
1636 * @return data related to inliers found after estimation.
1637 */
1638 public InliersData getInliersData() {
1639 return inliersData;
1640 }
1641
1642 /**
1643 * Indicates whether result must be refined using a non-linear solver over found inliers.
1644 *
1645 * @return true to refine result, false to simply use result found by robust estimator
1646 * without further refining.
1647 */
1648 public boolean isResultRefined() {
1649 return refineResult;
1650 }
1651
1652 /**
1653 * Specifies whether result must be refined using a non-linear solver over found inliers.
1654 *
1655 * @param refineResult true to refine result, false to simply use result found by robust
1656 * estimator without further refining.
1657 * @throws LockedException if calibrator is currently running.
1658 */
1659 public void setResultRefined(final boolean refineResult) throws LockedException {
1660 if (running) {
1661 throw new LockedException();
1662 }
1663 this.refineResult = refineResult;
1664 }
1665
1666 /**
1667 * Indicates whether covariance must be kept after refining result.
1668 * This setting is only taken into account if result is refined.
1669 *
1670 * @return true if covariance must be kept after refining result, false otherwise.
1671 */
1672 public boolean isCovarianceKept() {
1673 return keepCovariance;
1674 }
1675
1676 /**
1677 * Specifies whether covariance must be kept after refining result.
1678 * This setting is only taken into account if result is refined.
1679 *
1680 * @param keepCovariance true if covariance must be kept after refining result,
1681 * false otherwise.
1682 * @throws LockedException if calibrator is currently running.
1683 */
1684 public void setCovarianceKept(final boolean keepCovariance) throws LockedException {
1685 if (running) {
1686 throw new LockedException();
1687 }
1688 this.keepCovariance = keepCovariance;
1689 }
1690
1691 /**
1692 * Returns quality scores corresponding to each measurement.
1693 * The larger the score value the better the quality of the sample.
1694 * This implementation always returns null.
1695 * Subclasses using quality scores must implement proper behavior.
1696 *
1697 * @return quality scores corresponding to each sample.
1698 */
1699 @Override
1700 public double[] getQualityScores() {
1701 return null;
1702 }
1703
1704 /**
1705 * Sets quality scores corresponding to each measurement.
1706 * The larger the score value the better the quality of the sample.
1707 * This implementation makes no action.
1708 * Subclasses using quality scores must implement proper behaviour.
1709 *
1710 * @param qualityScores quality scores corresponding to each pair of
1711 * matched points.
1712 * @throws IllegalArgumentException if provided quality scores length
1713 * is smaller than minimum required samples.
1714 * @throws LockedException if calibrator is currently running.
1715 */
1716 @Override
1717 public void setQualityScores(final double[] qualityScores) throws LockedException {
1718 }
1719
1720 /**
1721 * Gets array containing x,y,z components of estimated gyroscope biases
1722 * expressed in radians per second (rad/s).
1723 *
1724 * @return array containing x,y,z components of estimated gyroscope biases.
1725 */
1726 @Override
1727 public double[] getEstimatedBiases() {
1728 return estimatedBiases;
1729 }
1730
1731 /**
1732 * Gets array containing x,y,z components of estimated gyroscope biases
1733 * expressed in radians per second (rad/s).
1734 *
1735 * @param result instance where estimated gyroscope biases will be stored.
1736 * @return true if result instance was updated, false otherwise (when estimation
1737 * is not yet available).
1738 */
1739 @Override
1740 public boolean getEstimatedBiases(final double[] result) {
1741 if (estimatedBiases != null) {
1742 System.arraycopy(estimatedBiases, 0, result, 0, estimatedBiases.length);
1743 return true;
1744 } else {
1745 return false;
1746 }
1747 }
1748
1749 /**
1750 * Gets column matrix containing x,y,z components of estimated gyroscope biases
1751 * expressed in radians per second (rad/s).
1752 *
1753 * @return column matrix containing x,y,z components of estimated gyroscope
1754 * biases.
1755 */
1756 @Override
1757 public Matrix getEstimatedBiasesAsMatrix() {
1758 return estimatedBiases != null ? Matrix.newFromArray(estimatedBiases) : null;
1759 }
1760
1761 /**
1762 * Gets column matrix containing x,y,z components of estimated gyroscope biases
1763 * expressed in radians per second (rad/s).
1764 *
1765 * @param result instance where result data will be stored.
1766 * @return true if result was updated, false otherwise.
1767 * @throws WrongSizeException if provided result instance has invalid size.
1768 */
1769 @Override
1770 public boolean getEstimatedBiasesAsMatrix(final Matrix result) throws WrongSizeException {
1771 if (estimatedBiases != null) {
1772 result.fromArray(estimatedBiases);
1773 return true;
1774 } else {
1775 return false;
1776 }
1777 }
1778
1779 /**
1780 * Gets x coordinate of estimated gyroscope bias expressed in radians per
1781 * second (rad/s).
1782 *
1783 * @return x coordinate of estimated gyroscope bias or null if not available.
1784 */
1785 @Override
1786 public Double getEstimatedBiasX() {
1787 return estimatedBiases != null ? estimatedBiases[0] : null;
1788 }
1789
1790 /**
1791 * Gets y coordinate of estimated gyroscope bias expressed in radians per
1792 * second (rad/s).
1793 *
1794 * @return y coordinate of estimated gyroscope bias or null if not available.
1795 */
1796 @Override
1797 public Double getEstimatedBiasY() {
1798 return estimatedBiases != null ? estimatedBiases[1] : null;
1799 }
1800
1801 /**
1802 * Gets z coordinate of estimated gyroscope bias expressed in radians per
1803 * second (rad/s).
1804 *
1805 * @return z coordinate of estimated gyroscope bias or null if not available.
1806 */
1807 @Override
1808 public Double getEstimatedBiasZ() {
1809 return estimatedBiases != null ? estimatedBiases[2] : null;
1810 }
1811
1812 /**
1813 * Gets x coordinate of estimated gyroscope bias.
1814 *
1815 * @return x coordinate of estimated gyroscope bias or null if not available.
1816 */
1817 @Override
1818 public AngularSpeed getEstimatedBiasAngularSpeedX() {
1819 return estimatedBiases != null
1820 ? new AngularSpeed(estimatedBiases[0], AngularSpeedUnit.RADIANS_PER_SECOND) : null;
1821 }
1822
1823 /**
1824 * Gets x coordinate of estimated gyroscope bias.
1825 *
1826 * @param result instance where result will be stored.
1827 * @return true if result was updated, false if estimation is not available.
1828 */
1829 @Override
1830 public boolean getEstimatedBiasAngularSpeedX(final AngularSpeed result) {
1831 if (estimatedBiases != null) {
1832 result.setValue(estimatedBiases[0]);
1833 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
1834 return true;
1835 } else {
1836 return false;
1837 }
1838 }
1839
1840 /**
1841 * Gets y coordinate of estimated gyroscope bias.
1842 *
1843 * @return y coordinate of estimated gyroscope bias or null if not available.
1844 */
1845 @Override
1846 public AngularSpeed getEstimatedBiasAngularSpeedY() {
1847 return estimatedBiases != null
1848 ? new AngularSpeed(estimatedBiases[1], AngularSpeedUnit.RADIANS_PER_SECOND) : null;
1849 }
1850
1851 /**
1852 * Gets y coordinate of estimated gyroscope bias.
1853 *
1854 * @param result instance where result will be stored.
1855 * @return true if result was updated, false if estimation is not available.
1856 */
1857 @Override
1858 public boolean getEstimatedBiasAngularSpeedY(final AngularSpeed result) {
1859 if (estimatedBiases != null) {
1860 result.setValue(estimatedBiases[1]);
1861 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
1862 return true;
1863 } else {
1864 return false;
1865 }
1866 }
1867
1868 /**
1869 * Gets z coordinate of estimated gyroscope bias.
1870 *
1871 * @return z coordinate of estimated gyroscope bias or null if not available.
1872 */
1873 @Override
1874 public AngularSpeed getEstimatedBiasAngularSpeedZ() {
1875 return estimatedBiases != null
1876 ? new AngularSpeed(estimatedBiases[2], AngularSpeedUnit.RADIANS_PER_SECOND) : null;
1877 }
1878
1879 /**
1880 * Gets z coordinate of estimated gyroscope bias.
1881 *
1882 * @param result instance where result will be stored.
1883 * @return true if result was updated, false if estimation is not available.
1884 */
1885 @Override
1886 public boolean getEstimatedBiasAngularSpeedZ(final AngularSpeed result) {
1887 if (estimatedBiases != null) {
1888 result.setValue(estimatedBiases[2]);
1889 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
1890 return true;
1891 } else {
1892 return false;
1893 }
1894 }
1895
1896 /**
1897 * Gets estimated gyroscope bias.
1898 *
1899 * @return estimated gyroscope bias or null if not available.
1900 */
1901 @Override
1902 public AngularSpeedTriad getEstimatedBiasAsTriad() {
1903 return estimatedBiases != null
1904 ? new AngularSpeedTriad(AngularSpeedUnit.RADIANS_PER_SECOND,
1905 estimatedBiases[0], estimatedBiases[1], estimatedBiases[2])
1906 : null;
1907 }
1908
1909 /**
1910 * Gets estimated gyroscope bias.
1911 *
1912 * @param result instance where result will be stored.
1913 * @return true if estimated gyroscope bias is available and result was
1914 * modified, false otherwise.
1915 */
1916 @Override
1917 public boolean getEstimatedBiasAsTriad(final AngularSpeedTriad result) {
1918 if (estimatedBiases != null) {
1919 result.setValueCoordinatesAndUnit(
1920 estimatedBiases[0], estimatedBiases[1], estimatedBiases[2],
1921 AngularSpeedUnit.RADIANS_PER_SECOND);
1922 return true;
1923 } else {
1924 return false;
1925 }
1926 }
1927
1928 /**
1929 * Gets estimated gyroscope scale factors and cross coupling errors.
1930 * This is the product of matrix Tg containing cross coupling errors and Kg
1931 * containing scaling factors.
1932 * So that:
1933 * <pre>
1934 * Mg = [sx mxy mxz] = Tg*Kg
1935 * [myx sy myz]
1936 * [mzx mzy sz ]
1937 * </pre>
1938 * Where:
1939 * <pre>
1940 * Kg = [sx 0 0 ]
1941 * [0 sy 0 ]
1942 * [0 0 sz]
1943 * </pre>
1944 * and
1945 * <pre>
1946 * Tg = [1 -alphaXy alphaXz ]
1947 * [alphaYx 1 -alphaYz]
1948 * [-alphaZx alphaZy 1 ]
1949 * </pre>
1950 * Hence:
1951 * <pre>
1952 * Mg = [sx mxy mxz] = Tg*Kg = [sx -sy * alphaXy sz * alphaXz ]
1953 * [myx sy myz] [sx * alphaYx sy -sz * alphaYz]
1954 * [mzx mzy sz ] [-sx * alphaZx sy * alphaZy sz ]
1955 * </pre>
1956 * This instance allows any 3x3 matrix however, typically alphaYx, alphaZx and alphaZy
1957 * are considered to be zero if the gyroscope z-axis is assumed to be the same
1958 * as the body z-axis. When this is assumed, myx = mzx = mzy = 0 and the Mg matrix
1959 * becomes upper diagonal:
1960 * <pre>
1961 * Mg = [sx mxy mxz]
1962 * [0 sy myz]
1963 * [0 0 sz ]
1964 * </pre>
1965 * Values of this matrix are unit-less.
1966 *
1967 * @return estimated gyroscope scale factors and cross coupling errors, or null
1968 * if not available.
1969 */
1970 @Override
1971 public Matrix getEstimatedMg() {
1972 return estimatedMg;
1973 }
1974
1975 /**
1976 * Gets estimated x-axis scale factor.
1977 *
1978 * @return estimated x-axis scale factor or null if not available.
1979 */
1980 @Override
1981 public Double getEstimatedSx() {
1982 return estimatedMg != null ? estimatedMg.getElementAt(0, 0) : null;
1983 }
1984
1985 /**
1986 * Gets estimated y-axis scale factor.
1987 *
1988 * @return estimated y-axis scale factor or null if not available.
1989 */
1990 @Override
1991 public Double getEstimatedSy() {
1992 return estimatedMg != null ? estimatedMg.getElementAt(1, 1) : null;
1993 }
1994
1995 /**
1996 * Gets estimated z-axis scale factor.
1997 *
1998 * @return estimated z-axis scale factor or null if not available.
1999 */
2000 @Override
2001 public Double getEstimatedSz() {
2002 return estimatedMg != null ? estimatedMg.getElementAt(2, 2) : null;
2003 }
2004
2005 /**
2006 * Gets estimated x-y cross-coupling error.
2007 *
2008 * @return estimated x-y cross-coupling error or null if not available.
2009 */
2010 @Override
2011 public Double getEstimatedMxy() {
2012 return estimatedMg != null ? estimatedMg.getElementAt(0, 1) : null;
2013 }
2014
2015 /**
2016 * Gets estimated x-z cross-coupling error.
2017 *
2018 * @return estimated x-z cross-coupling error or null if not available.
2019 */
2020 @Override
2021 public Double getEstimatedMxz() {
2022 return estimatedMg != null ? estimatedMg.getElementAt(0, 2) : null;
2023 }
2024
2025 /**
2026 * Gets estimated y-x cross-coupling error.
2027 *
2028 * @return estimated y-x cross-coupling error or null if not available.
2029 */
2030 @Override
2031 public Double getEstimatedMyx() {
2032 return estimatedMg != null ? estimatedMg.getElementAt(1, 0) : null;
2033 }
2034
2035 /**
2036 * Gets estimated y-z cross-coupling error.
2037 *
2038 * @return estimated y-z cross-coupling error or null if not available.
2039 */
2040 @Override
2041 public Double getEstimatedMyz() {
2042 return estimatedMg != null ? estimatedMg.getElementAt(1, 2) : null;
2043 }
2044
2045 /**
2046 * Gets estimated z-x cross-coupling error.
2047 *
2048 * @return estimated z-x cross-coupling error or null if not available.
2049 */
2050 @Override
2051 public Double getEstimatedMzx() {
2052 return estimatedMg != null ? estimatedMg.getElementAt(2, 0) : null;
2053 }
2054
2055 /**
2056 * Gets estimated z-y cross-coupling error.
2057 *
2058 * @return estimated z-y cross-coupling error or null if not available.
2059 */
2060 @Override
2061 public Double getEstimatedMzy() {
2062 return estimatedMg != null ? estimatedMg.getElementAt(2, 1) : null;
2063 }
2064
2065 /**
2066 * Gets estimated G-dependent cross biases introduced on the gyroscope by the
2067 * specific forces sensed by the accelerometer.
2068 * This instance allows any 3x3 matrix.
2069 *
2070 * @return estimated G-dependent cross biases.
2071 */
2072 @Override
2073 public Matrix getEstimatedGg() {
2074 return estimatedGg;
2075 }
2076
2077 /**
2078 * Gets estimated mean square error respect to provided measurements.
2079 *
2080 * @return estimated mean square error respect to provided measurements.
2081 */
2082 @Override
2083 public double getEstimatedMse() {
2084 return estimatedMse;
2085 }
2086
2087 /**
2088 * Gets estimated chi square value.
2089 *
2090 * @return estimated chi square value.
2091 */
2092 @Override
2093 public double getEstimatedChiSq() {
2094 return estimatedChiSq;
2095 }
2096
2097 /**
2098 * Gets estimated chi square degrees of freedom. Degrees of freedom is equal to the number of sampled data minus the
2099 * number of estimated parameters.
2100 *
2101 * @return estimated degrees of freedom of chi square value
2102 */
2103 @Override
2104 public int getEstimatedChiSqDegreesOfFreedom() {
2105 return estimatedChiSqDegreesOfFreedom;
2106 }
2107
2108 /**
2109 * Gets estimated reduced chi square value. This is equal to estimated chi square value divided by its degrees of
2110 * freedom. Ideally this value should be close to 1.0, indicating that fit is optimal.
2111 * A value larger than 1.0 indicates that fit is not good or noise has been underestimated, and a value smaller than
2112 * 1.0 indicates that there is overfitting or noise has been overestimated.
2113 *
2114 * @return estimated reduced chi square value
2115 */
2116 @Override
2117 public double getEstimatedReducedChiSq() {
2118 return estimatedReducedChiSq;
2119 }
2120
2121 /**
2122 * Gets estimated probability of finding a smaller chi square value expressed as a value between 0.0 and 1.0. The
2123 * smaller the found chi square value is, the better the fit of the estimated parameters to the actual parameter.
2124 * Thus, the smaller the chance of finding a smaller chi square value, then the better the estimated fit is.
2125 *
2126 * @return estimated probability of finding a smaller chi square value.
2127 */
2128 @Override
2129 public double getEstimatedP() {
2130 return estimatedP;
2131 }
2132
2133 /**
2134 * Gets estimated measure of quality of estimated fit as a value between 0.0 and 1.0. The larger the quality value
2135 * is, the better the fit that has been estimated.
2136 *
2137 * @return estimated measure of quality of estimated fit.
2138 */
2139 @Override
2140 public double getEstimatedQ() {
2141 return estimatedQ;
2142 }
2143
2144 /**
2145 * Gets estimated covariance matrix for estimated calibration solution.
2146 * Diagonal elements of the matrix contains variance for the following
2147 * parameters (following indicated order): bgx, bgy, bgz, sx, sy, sz,
2148 * mxy, mxz, myx, myz, mzx, mzy, gg11, gg21, gg31, gg12, gg22, gg32,
2149 * gg13, gg23, gg33.
2150 * This is only available when result has been refined and covariance
2151 * is kept.
2152 *
2153 * @return estimated covariance matrix for estimated position.
2154 */
2155 @Override
2156 public Matrix getEstimatedCovariance() {
2157 return estimatedCovariance;
2158 }
2159
2160 /**
2161 * Gets variance of estimated x coordinate of gyroscope bias expressed in (rad^2/s^2).
2162 *
2163 * @return variance of estimated x coordinate of gyroscope bias or null if not available.
2164 */
2165 public Double getEstimatedBiasXVariance() {
2166 return estimatedCovariance != null ? estimatedCovariance.getElementAt(0, 0) : null;
2167 }
2168
2169 /**
2170 * Gets standard deviation of estimated x coordinate of gyroscope bias expressed in
2171 * radians per second (rad/s).
2172 *
2173 * @return standard deviation of estimated x coordinate of gyroscope bias or null if not
2174 * available.
2175 */
2176 public Double getEstimatedBiasXStandardDeviation() {
2177 final var variance = getEstimatedBiasXVariance();
2178 return variance != null ? Math.sqrt(variance) : null;
2179 }
2180
2181 /**
2182 * Gets standard deviation of estimated x coordinate of gyroscope bias.
2183 *
2184 * @return standard deviation of estimated x coordinate of gyroscope bias or null if not
2185 * available.
2186 */
2187 public AngularSpeed getEstimatedBiasXStandardDeviationAsAngularSpeed() {
2188 return estimatedCovariance != null
2189 ? new AngularSpeed(getEstimatedBiasXStandardDeviation(), AngularSpeedUnit.RADIANS_PER_SECOND) : null;
2190 }
2191
2192 /**
2193 * Gets standard deviation of estimated x coordinate of gyroscope bias.
2194 *
2195 * @param result instance where result will be stored.
2196 * @return true if standard deviation of estimated x coordinate of gyroscope bias is available,
2197 * false otherwise.
2198 */
2199 public boolean getEstimatedBiasXStandardDeviationAsAngularSpeed(final AngularSpeed result) {
2200 if (estimatedCovariance != null) {
2201 result.setValue(getEstimatedBiasXStandardDeviation());
2202 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2203 return true;
2204 } else {
2205 return false;
2206 }
2207 }
2208
2209 /**
2210 * Gets variance of estimated y coordinate of gyroscope bias expressed in (rad^2/s^2).
2211 *
2212 * @return variance of estimated y coordinate of gyroscope bias or null if not available.
2213 */
2214 public Double getEstimatedBiasYVariance() {
2215 return estimatedCovariance != null ? estimatedCovariance.getElementAt(1, 1) : null;
2216 }
2217
2218 /**
2219 * Gets standard deviation of estimated y coordinate of gyroscope bias expressed in
2220 * radians per second (rad/s).
2221 *
2222 * @return standard deviation of estimated y coordinate of gyroscope bias or null if not
2223 * available.
2224 */
2225 public Double getEstimatedBiasYStandardDeviation() {
2226 final var variance = getEstimatedBiasYVariance();
2227 return variance != null ? Math.sqrt(variance) : null;
2228 }
2229
2230 /**
2231 * Gets standard deviation of estimated y coordinate of gyroscope bias.
2232 *
2233 * @return standard deviation of estimated y coordinate of gyroscope bias or null if not
2234 * available.
2235 */
2236 public AngularSpeed getEstimatedBiasYStandardDeviationAsAngularSpeed() {
2237 return estimatedCovariance != null
2238 ? new AngularSpeed(getEstimatedBiasYStandardDeviation(), AngularSpeedUnit.RADIANS_PER_SECOND) : null;
2239 }
2240
2241 /**
2242 * Gets standard deviation of estimated y coordinate of gyroscope bias.
2243 *
2244 * @param result instance where result will be stored.
2245 * @return true if standard deviation of estimated y coordinate of gyroscope bias is available,
2246 * false otherwise.
2247 */
2248 public boolean getEstimatedBiasYStandardDeviationAsAngularSpeed(final AngularSpeed result) {
2249 if (estimatedCovariance != null) {
2250 result.setValue(getEstimatedBiasYStandardDeviation());
2251 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2252 return true;
2253 } else {
2254 return false;
2255 }
2256 }
2257
2258 /**
2259 * Gets variance of estimated z coordinate of gyroscope bias expressed in (rad^2/s^2).
2260 *
2261 * @return variance of estimated z coordinate of gyroscope bias or null if not available.
2262 */
2263 public Double getEstimatedBiasZVariance() {
2264 return estimatedCovariance != null ? estimatedCovariance.getElementAt(2, 2) : null;
2265 }
2266
2267 /**
2268 * Gets standard deviation of estimated z coordinate of gyroscope bias expressed in
2269 * radians per second (rad/s).
2270 *
2271 * @return standard deviation of estimated z coordinate of gyroscope bias or null if not
2272 * available.
2273 */
2274 public Double getEstimatedBiasZStandardDeviation() {
2275 final var variance = getEstimatedBiasZVariance();
2276 return variance != null ? Math.sqrt(variance) : null;
2277 }
2278
2279 /**
2280 * Gets standard deviation of estimated z coordinate of gyroscope bias.
2281 *
2282 * @return standard deviation of estimated z coordinate of gyroscope bias or null if not
2283 * available.
2284 */
2285 public AngularSpeed getEstimatedBiasZStandardDeviationAsAngularSpeed() {
2286 return estimatedCovariance != null
2287 ? new AngularSpeed(getEstimatedBiasZStandardDeviation(), AngularSpeedUnit.RADIANS_PER_SECOND) : null;
2288 }
2289
2290 /**
2291 * Gets standard deviation of estimated z coordinate of gyroscope bias.
2292 *
2293 * @param result instance where result will be stored.
2294 * @return true if standard deviation of estimated z coordinate of gyroscope bias is available,
2295 * false otherwise.
2296 */
2297 public boolean getEstimatedBiasZStandardDeviationAsAngularSpeed(final AngularSpeed result) {
2298 if (estimatedCovariance != null) {
2299 result.setValue(getEstimatedBiasZStandardDeviation());
2300 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2301 return true;
2302 } else {
2303 return false;
2304 }
2305 }
2306
2307 /**
2308 * Gets standard deviation of estimated gyroscope bias coordinates.
2309 *
2310 * @return standard deviation of estimated gyroscope bias coordinates.
2311 */
2312 public AngularSpeedTriad getEstimatedBiasStandardDeviation() {
2313 return estimatedCovariance != null
2314 ? new AngularSpeedTriad(AngularSpeedUnit.RADIANS_PER_SECOND,
2315 getEstimatedBiasXStandardDeviation(),
2316 getEstimatedBiasYStandardDeviation(),
2317 getEstimatedBiasZStandardDeviation())
2318 : null;
2319 }
2320
2321 /**
2322 * Gets standard deviation of estimated gyroscope bias coordinates.
2323 *
2324 * @param result instance where result will be stored.
2325 * @return true if standard deviation of gyroscope bias was available, false
2326 * otherwise.
2327 */
2328 public boolean getEstimatedBiasStandardDeviation(final AngularSpeedTriad result) {
2329 if (estimatedCovariance != null) {
2330 result.setValueCoordinatesAndUnit(
2331 getEstimatedBiasXStandardDeviation(),
2332 getEstimatedBiasYStandardDeviation(),
2333 getEstimatedBiasZStandardDeviation(),
2334 AngularSpeedUnit.RADIANS_PER_SECOND);
2335 return true;
2336 } else {
2337 return false;
2338 }
2339 }
2340
2341 /**
2342 * Gets average of estimated standard deviation of gyroscope bias coordinates expressed
2343 * in radians per second (rad/s).
2344 *
2345 * @return average of estimated standard deviation of gyroscope bias coordinates or null
2346 * if not available.
2347 */
2348 public Double getEstimatedBiasStandardDeviationAverage() {
2349 return estimatedCovariance != null
2350 ? (getEstimatedBiasXStandardDeviation() + getEstimatedBiasYStandardDeviation()
2351 + getEstimatedBiasZStandardDeviation()) / 3.0 : null;
2352 }
2353
2354 /**
2355 * Gets average of estimated standard deviation of gyroscope bias coordinates.
2356 *
2357 * @return average of estimated standard deviation of gyroscope bias coordinates or null.
2358 */
2359 public AngularSpeed getEstimatedBiasStandardDeviationAverageAsAngularSpeed() {
2360 return estimatedCovariance != null
2361 ? new AngularSpeed(getEstimatedBiasStandardDeviationAverage(), AngularSpeedUnit.RADIANS_PER_SECOND)
2362 : null;
2363 }
2364
2365 /**
2366 * Gets average of estimated standard deviation of gyroscope bias coordinates.
2367 *
2368 * @param result instance where result will be stored.
2369 * @return true if average of estimated standard deviation of gyroscope bias is available,
2370 * false otherwise.
2371 */
2372 public boolean getEstimatedBiasStandardDeviationAverageAsAngularSpeed(final AngularSpeed result) {
2373 if (estimatedCovariance != null) {
2374 result.setValue(getEstimatedBiasStandardDeviationAverage());
2375 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2376 return true;
2377 } else {
2378 return false;
2379 }
2380 }
2381
2382 /**
2383 * Gets norm of estimated standard deviation of gyroscope bias expressed in
2384 * radians per second (rad/s).
2385 * This can be used as the initial gyroscope bias uncertainty for
2386 * {@link INSLooselyCoupledKalmanInitializerConfig} or {@link INSTightlyCoupledKalmanInitializerConfig}.
2387 *
2388 * @return norm of estimated standard deviation of gyroscope bias or null
2389 * if not available.
2390 */
2391 @Override
2392 public Double getEstimatedBiasStandardDeviationNorm() {
2393 return estimatedCovariance != null
2394 ? Math.sqrt(getEstimatedBiasXVariance() + getEstimatedBiasYVariance() + getEstimatedBiasZVariance())
2395 : null;
2396 }
2397
2398 /**
2399 * Gets norm of estimated standard deviation of gyroscope bias.
2400 * This can be used as the initial gyroscope bias uncertainty for
2401 * {@link INSLooselyCoupledKalmanInitializerConfig} or {@link INSTightlyCoupledKalmanInitializerConfig}.
2402 *
2403 * @return norm of estimated standard deviation of gyroscope bias or null
2404 * if not available.
2405 */
2406 public AngularSpeed getEstimatedBiasStandardDeviationNormAsAngularSpeed() {
2407 return estimatedCovariance != null
2408 ? new AngularSpeed(getEstimatedBiasStandardDeviationNorm(), AngularSpeedUnit.RADIANS_PER_SECOND)
2409 : null;
2410 }
2411
2412 /**
2413 * Gets norm of estimated standard deviation of gyroscope bias coordinates.
2414 * This can be used as the initial gyroscope bias uncertainty for
2415 * {@link INSLooselyCoupledKalmanInitializerConfig} or {@link INSTightlyCoupledKalmanInitializerConfig}.
2416 *
2417 * @param result instance where result will be stored.
2418 * @return true if norm of estimated standard deviation of gyroscope bias is
2419 * available, false otherwise.
2420 */
2421 public boolean getEstimatedBiasStandardDeviationNormAsAngularSpeed(final AngularSpeed result) {
2422 if (estimatedCovariance != null) {
2423 result.setValue(getEstimatedBiasStandardDeviationNorm());
2424 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2425 return true;
2426 } else {
2427 return false;
2428 }
2429 }
2430
2431 /**
2432 * Gets size of subsets to be checked during robust estimation.
2433 * This has to be at least {@link #MINIMUM_MEASUREMENTS}.
2434 *
2435 * @return size of subsets to be checked during robust estimation.
2436 */
2437 public int getPreliminarySubsetSize() {
2438 return preliminarySubsetSize;
2439 }
2440
2441 /**
2442 * Sets size of subsets to be checked during robust estimation.
2443 * This has to be at least {@link #MINIMUM_MEASUREMENTS}.
2444 *
2445 * @param preliminarySubsetSize size of subsets to be checked during robust estimation.
2446 * @throws LockedException if calibrator is currently running.
2447 * @throws IllegalArgumentException if provided value is less than {@link #MINIMUM_MEASUREMENTS}.
2448 */
2449 public void setPreliminarySubsetSize(final int preliminarySubsetSize)
2450 throws LockedException {
2451 if (running) {
2452 throw new LockedException();
2453 }
2454 if (preliminarySubsetSize < MINIMUM_MEASUREMENTS) {
2455 throw new IllegalArgumentException();
2456 }
2457
2458 this.preliminarySubsetSize = preliminarySubsetSize;
2459 }
2460
2461 /**
2462 * Returns method being used for robust estimation.
2463 *
2464 * @return method being used for robust estimation.
2465 */
2466 public abstract RobustEstimatorMethod getMethod();
2467
2468 /**
2469 * Creates a robust gyroscope calibrator.
2470 *
2471 * @param method robust estimator method.
2472 * @return a robust gyroscope calibrator.
2473 */
2474 public static RobustKnownFrameGyroscopeCalibrator create(final RobustEstimatorMethod method) {
2475 return switch (method) {
2476 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator();
2477 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator();
2478 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator();
2479 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator();
2480 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator();
2481 };
2482 }
2483
2484 /**
2485 * Creates a robust gyroscope calibrator.
2486 *
2487 * @param listener listener to be notified of events such as when estimation
2488 * starts, ends or its progress significantly changes.
2489 * @param method robust estimator method.
2490 * @return a robust gyroscope calibrator.
2491 */
2492 public static RobustKnownFrameGyroscopeCalibrator create(
2493 final RobustKnownFrameGyroscopeCalibratorListener listener, final RobustEstimatorMethod method) {
2494 return switch (method) {
2495 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(listener);
2496 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(listener);
2497 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(listener);
2498 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(listener);
2499 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(listener);
2500 };
2501 }
2502
2503 /**
2504 * Creates a robust gyroscope calibrator.
2505 *
2506 * @param measurements list of body kinematics measurements with standard
2507 * deviations taken at different frames (positions, orientations
2508 * and velocities).
2509 * @param method robust estimator method.
2510 * @return a robust gyroscope calibrator.
2511 */
2512 public static RobustKnownFrameGyroscopeCalibrator create(
2513 final List<StandardDeviationFrameBodyKinematics> measurements, final RobustEstimatorMethod method) {
2514 return switch (method) {
2515 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(measurements);
2516 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(measurements);
2517 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(measurements);
2518 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(measurements);
2519 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(measurements);
2520 };
2521 }
2522
2523 /**
2524 * Creates a robust gyroscope calibrator.
2525 *
2526 * @param measurements list of body kinematics measurements with standard
2527 * deviations taken at different frames (positions, orientations
2528 * and velocities).
2529 * @param listener listener to handle events raised by this calibrator.
2530 * @param method robust estimator method.
2531 * @return a robust gyroscope calibrator.
2532 */
2533 public static RobustKnownFrameGyroscopeCalibrator create(
2534 final List<StandardDeviationFrameBodyKinematics> measurements,
2535 final RobustKnownFrameGyroscopeCalibratorListener listener, final RobustEstimatorMethod method) {
2536 return switch (method) {
2537 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(measurements, listener);
2538 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(measurements, listener);
2539 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(measurements, listener);
2540 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(measurements, listener);
2541 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(measurements, listener);
2542 };
2543 }
2544
2545 /**
2546 * Creates a robust gyroscope calibrator.
2547 *
2548 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2549 * accelerometer and gyroscope.
2550 * @param method robust estimator method.
2551 * @return a robust gyroscope calibrator.
2552 */
2553 public static RobustKnownFrameGyroscopeCalibrator create(
2554 final boolean commonAxisUsed, final RobustEstimatorMethod method) {
2555 return switch (method) {
2556 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(commonAxisUsed);
2557 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(commonAxisUsed);
2558 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(commonAxisUsed);
2559 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(commonAxisUsed);
2560 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(commonAxisUsed);
2561 };
2562 }
2563
2564 /**
2565 * Creates a robust gyroscope calibrator.
2566 *
2567 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2568 * accelerometer and gyroscope.
2569 * @param listener listener to handle events raised by this calibrator.
2570 * @param method robust estimator method.
2571 * @return a robust gyroscope calibrator.
2572 */
2573 public static RobustKnownFrameGyroscopeCalibrator create(
2574 final boolean commonAxisUsed, final RobustKnownFrameGyroscopeCalibratorListener listener,
2575 final RobustEstimatorMethod method) {
2576 return switch (method) {
2577 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(commonAxisUsed, listener);
2578 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(commonAxisUsed, listener);
2579 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(commonAxisUsed, listener);
2580 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(commonAxisUsed, listener);
2581 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(commonAxisUsed, listener);
2582 };
2583 }
2584
2585 /**
2586 * Creates a robust gyroscope calibrator.
2587 *
2588 * @param measurements list of body kinematics measurements with standard
2589 * deviations taken at different frames (positions, orientation
2590 * and velocities).
2591 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2592 * accelerometer and gyroscope.
2593 * @param method robust estimator method.
2594 * @return a robust gyroscope calibrator.
2595 */
2596 public static RobustKnownFrameGyroscopeCalibrator create(
2597 final List<StandardDeviationFrameBodyKinematics> measurements, final boolean commonAxisUsed,
2598 final RobustEstimatorMethod method) {
2599 return switch (method) {
2600 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed);
2601 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed);
2602 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed);
2603 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed);
2604 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed);
2605 };
2606 }
2607
2608 /**
2609 * Creates a robust gyroscope calibrator.
2610 *
2611 * @param measurements list of body kinematics measurements with standard
2612 * deviations taken at different frames (positions, orientations
2613 * and velocities).
2614 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2615 * accelerometer and gyroscope.
2616 * @param listener listener to handle events raised by this calibrator.
2617 * @param method robust estimator method.
2618 * @return a robust gyroscope calibrator.
2619 */
2620 public static RobustKnownFrameGyroscopeCalibrator create(
2621 final List<StandardDeviationFrameBodyKinematics> measurements, final boolean commonAxisUsed,
2622 final RobustKnownFrameGyroscopeCalibratorListener listener, final RobustEstimatorMethod method) {
2623 return switch (method) {
2624 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed, listener);
2625 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed, listener);
2626 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed, listener);
2627 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed, listener);
2628 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed, listener);
2629 };
2630 }
2631
2632 /**
2633 * Creates a robust gyroscope calibrator.
2634 *
2635 * @param qualityScores quality scores corresponding to each provided
2636 * measurement. The larger the score value the better
2637 * the quality of the sample.
2638 * @param method robust estimator method.
2639 * @return a robust gyroscope calibrator.
2640 */
2641 public static RobustKnownFrameGyroscopeCalibrator create(
2642 final double[] qualityScores, final RobustEstimatorMethod method) {
2643 return switch (method) {
2644 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator();
2645 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator();
2646 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator();
2647 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(qualityScores);
2648 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(qualityScores);
2649 };
2650 }
2651
2652 /**
2653 * Creates a robust gyroscope calibrator.
2654 *
2655 * @param qualityScores quality scores corresponding to each provided
2656 * measurement. The larger the score value the better
2657 * the quality of the sample.
2658 * @param listener listener to be notified of events such as when estimation
2659 * starts, ends or its progress significantly changes.
2660 * @param method robust estimator method.
2661 * @return a robust gyroscope calibrator.
2662 */
2663 public static RobustKnownFrameGyroscopeCalibrator create(
2664 final double[] qualityScores, final RobustKnownFrameGyroscopeCalibratorListener listener,
2665 final RobustEstimatorMethod method) {
2666 return switch (method) {
2667 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(listener);
2668 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(listener);
2669 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(listener);
2670 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(qualityScores, listener);
2671 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(qualityScores, listener);
2672 };
2673 }
2674
2675 /**
2676 * Creates a robust gyroscope calibrator.
2677 *
2678 * @param qualityScores quality scores corresponding to each provided
2679 * measurement. The larger the score value the better
2680 * the quality of the sample.
2681 * @param measurements list of body kinematics measurements with standard
2682 * deviations taken at different frames (positions, orientations
2683 * and velocities).
2684 * @param method robust estimator method.
2685 * @return a robust gyroscope calibrator.
2686 */
2687 public static RobustKnownFrameGyroscopeCalibrator create(
2688 final double[] qualityScores, final List<StandardDeviationFrameBodyKinematics> measurements,
2689 final RobustEstimatorMethod method) {
2690 return switch (method) {
2691 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(measurements);
2692 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(measurements);
2693 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(measurements);
2694 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(qualityScores, measurements);
2695 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(qualityScores, measurements);
2696 };
2697 }
2698
2699 /**
2700 * Creates a robust gyroscope calibrator.
2701 *
2702 * @param qualityScores quality scores corresponding to each provided
2703 * measurement. The larger the score value the better
2704 * the quality of the sample.
2705 * @param measurements list of body kinematics measurements with standard
2706 * deviations taken at different frames (positions, orientations
2707 * and velocities).
2708 * @param listener listener to handle events raised by this calibrator.
2709 * @param method robust estimator method.
2710 * @return a robust gyroscope calibrator.
2711 */
2712 public static RobustKnownFrameGyroscopeCalibrator create(
2713 final double[] qualityScores, final List<StandardDeviationFrameBodyKinematics> measurements,
2714 final RobustKnownFrameGyroscopeCalibratorListener listener, final RobustEstimatorMethod method) {
2715 return switch (method) {
2716 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(measurements, listener);
2717 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(measurements, listener);
2718 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(measurements, listener);
2719 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(qualityScores, measurements, listener);
2720 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(qualityScores, measurements, listener);
2721 };
2722 }
2723
2724 /**
2725 * Creates a robust gyroscope calibrator.
2726 *
2727 * @param qualityScores quality scores corresponding to each provided
2728 * measurement. The larger the score value the better
2729 * the quality of the sample.
2730 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2731 * accelerometer and gyroscope.
2732 * @param method robust estimator method.
2733 * @return a robust gyroscope calibrator.
2734 */
2735 public static RobustKnownFrameGyroscopeCalibrator create(
2736 final double[] qualityScores, final boolean commonAxisUsed, final RobustEstimatorMethod method) {
2737 return switch (method) {
2738 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(commonAxisUsed);
2739 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(commonAxisUsed);
2740 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(commonAxisUsed);
2741 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(qualityScores, commonAxisUsed);
2742 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(qualityScores, commonAxisUsed);
2743 };
2744 }
2745
2746 /**
2747 * Creates a robust gyroscope calibrator.
2748 *
2749 * @param qualityScores quality scores corresponding to each provided
2750 * measurement. The larger the score value the better
2751 * the quality of the sample.
2752 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2753 * accelerometer and gyroscope.
2754 * @param listener listener to handle events raised by this calibrator.
2755 * @param method robust estimator method.
2756 * @return a robust gyroscope calibrator.
2757 */
2758 public static RobustKnownFrameGyroscopeCalibrator create(
2759 final double[] qualityScores, final boolean commonAxisUsed,
2760 final RobustKnownFrameGyroscopeCalibratorListener listener, final RobustEstimatorMethod method) {
2761 return switch (method) {
2762 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(commonAxisUsed, listener);
2763 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(commonAxisUsed, listener);
2764 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(commonAxisUsed, listener);
2765 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(qualityScores, commonAxisUsed, listener);
2766 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(qualityScores, commonAxisUsed, listener);
2767 };
2768 }
2769
2770 /**
2771 * Creates a robust gyroscope calibrator.
2772 *
2773 * @param qualityScores quality scores corresponding to each provided
2774 * measurement. The larger the score value the better
2775 * the quality of the sample.
2776 * @param measurements list of body kinematics measurements with standard
2777 * deviations taken at different frames (positions, orientations
2778 * and velocities).
2779 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2780 * accelerometer and gyroscope.
2781 * @param method robust estimator method.
2782 * @return a robust gyroscope calibrator.
2783 */
2784 public static RobustKnownFrameGyroscopeCalibrator create(
2785 final double[] qualityScores, final List<StandardDeviationFrameBodyKinematics> measurements,
2786 final boolean commonAxisUsed, final RobustEstimatorMethod method) {
2787 return switch (method) {
2788 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed);
2789 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed);
2790 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed);
2791 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(qualityScores, measurements, commonAxisUsed);
2792 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(qualityScores, measurements, commonAxisUsed);
2793 };
2794 }
2795
2796 /**
2797 * Creates a robust gyroscope calibrator.
2798 *
2799 * @param qualityScores quality scores corresponding to each provided
2800 * measurement. The larger the score value the better the
2801 * quality of the sample.
2802 * @param measurements list of body kinematics measurements with standard
2803 * deviations taken at different frames (positions, orientations
2804 * and velocities).
2805 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2806 * accelerometer and gyroscope.
2807 * @param listener listener to handle events raised by this calibrator.
2808 * @param method robust estimator method.
2809 * @return a robust gyroscope calibrator.
2810 */
2811 public static RobustKnownFrameGyroscopeCalibrator create(
2812 final double[] qualityScores, final List<StandardDeviationFrameBodyKinematics> measurements,
2813 final boolean commonAxisUsed, final RobustKnownFrameGyroscopeCalibratorListener listener,
2814 final RobustEstimatorMethod method) {
2815 return switch (method) {
2816 case RANSAC -> new RANSACRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed, listener);
2817 case LMEDS -> new LMedSRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed, listener);
2818 case MSAC -> new MSACRobustKnownFrameGyroscopeCalibrator(measurements, commonAxisUsed, listener);
2819 case PROSAC -> new PROSACRobustKnownFrameGyroscopeCalibrator(qualityScores, measurements, commonAxisUsed,
2820 listener);
2821 default -> new PROMedSRobustKnownFrameGyroscopeCalibrator(qualityScores, measurements, commonAxisUsed,
2822 listener);
2823 };
2824 }
2825
2826 /**
2827 * Creates a robust gyroscope calibrator using default robust method.
2828 *
2829 * @return a robust gyroscope calibrator.
2830 */
2831 public static RobustKnownFrameGyroscopeCalibrator create() {
2832 return create(DEFAULT_ROBUST_METHOD);
2833 }
2834
2835 /**
2836 * Creates a robust gyroscope calibrator using default robust method.
2837 *
2838 * @param listener listener to be notified of events such as when estimation
2839 * starts, ends or its progress significantly changes.
2840 * @return a robust gyroscope calibrator.
2841 */
2842 public static RobustKnownFrameGyroscopeCalibrator create(
2843 final RobustKnownFrameGyroscopeCalibratorListener listener) {
2844 return create(listener, DEFAULT_ROBUST_METHOD);
2845 }
2846
2847 /**
2848 * Creates a robust gyroscope calibrator using default robust method.
2849 *
2850 * @param measurements list of body kinematics measurements with standard
2851 * deviations taken at different frames (positions, orientations
2852 * and velocities).
2853 * @return a robust gyroscope calibrator.
2854 */
2855 public static RobustKnownFrameGyroscopeCalibrator create(
2856 final List<StandardDeviationFrameBodyKinematics> measurements) {
2857 return create(measurements, DEFAULT_ROBUST_METHOD);
2858 }
2859
2860 /**
2861 * Creates a robust gyroscope calibrator using default robust method.
2862 *
2863 * @param measurements list of body kinematics measurements with standard
2864 * deviations taken at different frames (positions, orientations
2865 * and velocities).
2866 * @param listener listener to handle events raised by this calibrator.
2867 * @return a robust gyroscope calibrator.
2868 */
2869 public static RobustKnownFrameGyroscopeCalibrator create(
2870 final List<StandardDeviationFrameBodyKinematics> measurements,
2871 final RobustKnownFrameGyroscopeCalibratorListener listener) {
2872 return create(measurements, listener, DEFAULT_ROBUST_METHOD);
2873 }
2874
2875 /**
2876 * Creates a robust gyroscope calibrator using default robust method.
2877 *
2878 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2879 * accelerometer and gyroscope.
2880 * @return a robust accelerometer calibrator.
2881 */
2882 public static RobustKnownFrameGyroscopeCalibrator create(final boolean commonAxisUsed) {
2883 return create(commonAxisUsed, DEFAULT_ROBUST_METHOD);
2884 }
2885
2886 /**
2887 * Creates a robust gyroscope calibrator using default robust method.
2888 *
2889 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2890 * accelerometer and gyroscope.
2891 * @param listener listener to handle events raised by this calibrator.
2892 * @return a robust accelerometer calibrator.
2893 */
2894 public static RobustKnownFrameGyroscopeCalibrator create(
2895 final boolean commonAxisUsed, final RobustKnownFrameGyroscopeCalibratorListener listener) {
2896 return create(commonAxisUsed, listener, DEFAULT_ROBUST_METHOD);
2897 }
2898
2899 /**
2900 * Creates a robust gyroscope calibrator using default robust method.
2901 *
2902 * @param measurements list of body kinematics measurements with standard
2903 * deviations taken at different (positions, orientations and
2904 * velocities).
2905 * @param commonAxisUsed indicates whether z-axis is assigned to be common for
2906 * accelerometer and gyroscope.
2907 * @return a robust gyroscope calibrator.
2908 */
2909 public static RobustKnownFrameGyroscopeCalibrator create(
2910 final List<StandardDeviationFrameBodyKinematics> measurements, final boolean commonAxisUsed) {
2911 return create(measurements, commonAxisUsed, DEFAULT_ROBUST_METHOD);
2912 }
2913
2914 /**
2915 * Creates a robust gyroscope calibrator using default robust method.
2916 *
2917 * @param measurements list of body kinematics measurements with standard
2918 * deviations taken at different frames (positions, orientations
2919 * and velocities).
2920 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2921 * accelerometer and gyroscope.
2922 * @param listener listener to handle events raised by this calibrator.
2923 * @return a robust gyroscope calibrator.
2924 */
2925 public static RobustKnownFrameGyroscopeCalibrator create(
2926 final List<StandardDeviationFrameBodyKinematics> measurements, final boolean commonAxisUsed,
2927 final RobustKnownFrameGyroscopeCalibratorListener listener) {
2928 return create(measurements, commonAxisUsed, listener, DEFAULT_ROBUST_METHOD);
2929 }
2930
2931 /**
2932 * Creates a robust gyroscope calibrator using default robust method.
2933 *
2934 * @param qualityScores quality scores corresponding to each provided
2935 * measurement. The larger the score value the better
2936 * the quality of the sample.
2937 * @return a robust gyroscope calibrator.
2938 */
2939 public static RobustKnownFrameGyroscopeCalibrator create(final double[] qualityScores) {
2940 return create(qualityScores, DEFAULT_ROBUST_METHOD);
2941 }
2942
2943 /**
2944 * Creates a robust gyroscope calibrator using default robust method.
2945 *
2946 * @param qualityScores quality scores corresponding to each provided
2947 * measurement. The larger the score value the better
2948 * the quality of the sample.
2949 * @param listener listener to be notified of events such as when estimation
2950 * starts, ends or its progress significantly changes.
2951 * @return a robust gyroscope calibrator.
2952 */
2953 public static RobustKnownFrameGyroscopeCalibrator create(
2954 final double[] qualityScores, final RobustKnownFrameGyroscopeCalibratorListener listener) {
2955 return create(qualityScores, listener, DEFAULT_ROBUST_METHOD);
2956 }
2957
2958 /**
2959 * Creates a robust gyroscope calibrator using default robust method
2960 *
2961 * @param qualityScores quality scores corresponding to each provided
2962 * measurement. The larger the score value the better
2963 * the quality of the sample.
2964 * @param measurements list of body kinematics measurements with standard
2965 * deviations taken at different frames (positions, orientations
2966 * and velocities).
2967 * @return a robust gyroscope calibrator.
2968 */
2969 public static RobustKnownFrameGyroscopeCalibrator create(
2970 final double[] qualityScores, final List<StandardDeviationFrameBodyKinematics> measurements) {
2971 return create(qualityScores, measurements, DEFAULT_ROBUST_METHOD);
2972 }
2973
2974 /**
2975 * Creates a robust gyroscope calibrator using default robust method.
2976 *
2977 * @param qualityScores quality scores corresponding to each provided
2978 * measurement. The larger the score value the better
2979 * the quality of the sample.
2980 * @param measurements list of body kinematics measurements with standard
2981 * deviations taken at different frames (positions, orientations
2982 * and velocities).
2983 * @param listener listener to handle events raised by this calibrator.
2984 * @return a robust gyroscope calibrator.
2985 */
2986 public static RobustKnownFrameGyroscopeCalibrator create(
2987 final double[] qualityScores, final List<StandardDeviationFrameBodyKinematics> measurements,
2988 final RobustKnownFrameGyroscopeCalibratorListener listener) {
2989 return create(qualityScores, measurements, listener, DEFAULT_ROBUST_METHOD);
2990 }
2991
2992 /**
2993 * Creates a robust gyroscope calibrator using default robust method.
2994 *
2995 * @param qualityScores quality scores corresponding to each provided
2996 * measurement. The larger the score value the better
2997 * the quality of the sample.
2998 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
2999 * accelerometer and gyroscope.
3000 * @return a robust gyroscope calibrator.
3001 */
3002 public static RobustKnownFrameGyroscopeCalibrator create(
3003 final double[] qualityScores, final boolean commonAxisUsed) {
3004 return create(qualityScores, commonAxisUsed, DEFAULT_ROBUST_METHOD);
3005 }
3006
3007 /**
3008 * Creates a robust gyroscope calibrator using default robust method.
3009 *
3010 * @param qualityScores quality scores corresponding to each provided
3011 * measurement. The larger the score value the better
3012 * the quality of the sample.
3013 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
3014 * accelerometer and gyroscope.
3015 * @param listener listener to handle events raised by this calibrator.
3016 * @return a robust gyroscope calibrator.
3017 */
3018 public static RobustKnownFrameGyroscopeCalibrator create(
3019 final double[] qualityScores, final boolean commonAxisUsed,
3020 final RobustKnownFrameGyroscopeCalibratorListener listener) {
3021 return create(qualityScores, commonAxisUsed, listener, DEFAULT_ROBUST_METHOD);
3022 }
3023
3024 /**
3025 * Creates a robust gyroscope calibrator using default robust method.
3026 *
3027 * @param qualityScores quality scores corresponding to each provided
3028 * measurement. The larger the score value the better
3029 * the quality of the sample.
3030 * @param measurements list of body kinematics measurements with standard
3031 * deviations taken at different frames (positions, orientations
3032 * and velocities).
3033 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
3034 * accelerometer and gyroscope.
3035 * @return a robust gyroscope calibrator.
3036 */
3037 public static RobustKnownFrameGyroscopeCalibrator create(
3038 final double[] qualityScores, final List<StandardDeviationFrameBodyKinematics> measurements,
3039 final boolean commonAxisUsed) {
3040 return create(qualityScores, measurements, commonAxisUsed, DEFAULT_ROBUST_METHOD);
3041 }
3042
3043 /**
3044 * Creates a robust gyroscope calibrator using default robust method.
3045 *
3046 * @param qualityScores quality scores corresponding to each provided
3047 * measurement. The larger the score value the better
3048 * the quality of the sample.
3049 * @param measurements list of body kinematics measurements with standard
3050 * deviations taken at different frames (positions, orientations
3051 * and velocities).
3052 * @param commonAxisUsed indicates whether z-axis is assumed to be common for
3053 * accelerometer and gyroscope.
3054 * @param listener listener to handle events raised by this calibrator.
3055 * @return a robust gyroscope calibrator.
3056 */
3057 public static RobustKnownFrameGyroscopeCalibrator create(
3058 final double[] qualityScores, final List<StandardDeviationFrameBodyKinematics> measurements,
3059 final boolean commonAxisUsed, final RobustKnownFrameGyroscopeCalibratorListener listener) {
3060 return create(qualityScores, measurements, commonAxisUsed, listener, DEFAULT_ROBUST_METHOD);
3061 }
3062
3063
3064 /**
3065 * Computes error of a preliminary result respect a given measurement.
3066 *
3067 * @param measurement a measurement.
3068 * @param preliminaryResult a preliminary result.
3069 * @return computed error.
3070 */
3071 protected double computeError(
3072 final StandardDeviationFrameBodyKinematics measurement, final PreliminaryResult preliminaryResult) {
3073 // We know that measured angular rate is:
3074 // Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue
3075
3076 // Hence:
3077 // [Ωmeasx] = [bx] + ( [1 0 0] + [sx mxy mxz]) [Ωtruex] + [g11 g12 g13][ftruex]
3078 // [Ωmeasy] [by] [0 1 0] [myx sy myz] [Ωtruey] [g21 g22 g23][ftruey]
3079 // [Ωmeasz] [bz] [0 0 1] [mzx mzy sz ] [Ωtruez] [g31 g32 g33][ftruez]
3080
3081 final var measuredKinematics = measurement.getKinematics();
3082 final var ecefFrame = measurement.getFrame();
3083 final var previousEcefFrame = measurement.getPreviousFrame();
3084 final var timeInterval = measurement.getTimeInterval();
3085
3086 final var expectedKinematics = ECEFKinematicsEstimator.estimateKinematicsAndReturnNew(timeInterval, ecefFrame,
3087 previousEcefFrame);
3088
3089 final var angularRateMeasX1 = measuredKinematics.getAngularRateX();
3090 final var angularRateMeasY1 = measuredKinematics.getAngularRateY();
3091 final var angularRateMeasZ1 = measuredKinematics.getAngularRateZ();
3092
3093 final var angularRateTrueX = expectedKinematics.getAngularRateX();
3094 final var angularRateTrueY = expectedKinematics.getAngularRateY();
3095 final var angularRateTrueZ = expectedKinematics.getAngularRateZ();
3096
3097 final var fTrueX = expectedKinematics.getFx();
3098 final var fTrueY = expectedKinematics.getFy();
3099 final var fTrueZ = expectedKinematics.getFz();
3100
3101 final var b = preliminaryResult.estimatedBiases;
3102 final var bx = b[0];
3103 final var by = b[1];
3104 final var bz = b[2];
3105
3106 final var mg = preliminaryResult.estimatedMg;
3107
3108 final var gg = preliminaryResult.estimatedGg;
3109
3110 try {
3111 final var m1 = Matrix.identity(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
3112 m1.add(mg);
3113
3114 final var angularRateTrue = new Matrix(BodyKinematics.COMPONENTS, 1);
3115 angularRateTrue.setElementAtIndex(0, angularRateTrueX);
3116 angularRateTrue.setElementAtIndex(1, angularRateTrueY);
3117 angularRateTrue.setElementAtIndex(2, angularRateTrueZ);
3118
3119 m1.multiply(angularRateTrue);
3120
3121 final var fTrue = new Matrix(BodyKinematics.COMPONENTS, 1);
3122 fTrue.setElementAtIndex(0, fTrueX);
3123 fTrue.setElementAtIndex(1, fTrueY);
3124 fTrue.setElementAtIndex(2, fTrueZ);
3125 final var m2 = gg.multiplyAndReturnNew(fTrue);
3126
3127 m1.add(m2);
3128
3129 final var angularRateMeasX2 = bx + m1.getElementAtIndex(0);
3130 final var angularRateMeasY2 = by + m1.getElementAtIndex(1);
3131 final var angularRateMeasZ2 = bz + m1.getElementAtIndex(2);
3132
3133 final var diffX = angularRateMeasX2 - angularRateMeasX1;
3134 final var diffY = angularRateMeasY2 - angularRateMeasY1;
3135 final var diffZ = angularRateMeasZ2 - angularRateMeasZ1;
3136
3137 return Math.sqrt(diffX * diffX + diffY * diffY + diffZ * diffZ);
3138
3139 } catch (final WrongSizeException e) {
3140 return Double.MAX_VALUE;
3141 }
3142 }
3143
3144 /**
3145 * Computes a preliminary solution for a subset of samples picked by a robust estimator.
3146 *
3147 * @param samplesIndices indices of samples picked by the robust estimator.
3148 * @param solutions list where estimated preliminary solution will be stored.
3149 */
3150 protected void computePreliminarySolutions(final int[] samplesIndices, final List<PreliminaryResult> solutions) {
3151
3152 final var meas = new ArrayList<StandardDeviationFrameBodyKinematics>();
3153
3154 for (final var samplesIndex : samplesIndices) {
3155 meas.add(this.measurements.get(samplesIndex));
3156 }
3157
3158 try {
3159 final var result = new PreliminaryResult();
3160 result.estimatedBiases = getInitialBias();
3161 result.estimatedMg = getInitialMg();
3162 result.estimatedGg = getInitialGg();
3163
3164 if (useLinearCalibrator) {
3165 linearCalibrator.setCommonAxisUsed(commonAxisUsed);
3166 linearCalibrator.setMeasurements(meas);
3167 linearCalibrator.calibrate();
3168
3169 linearCalibrator.getEstimatedBiases(result.estimatedBiases);
3170 result.estimatedMg = linearCalibrator.getEstimatedMg();
3171 result.estimatedGg = linearCalibrator.getEstimatedGg();
3172 }
3173
3174 if (refinePreliminarySolutions) {
3175 nonLinearCalibrator.setInitialBias(result.estimatedBiases);
3176 nonLinearCalibrator.setInitialMg(result.estimatedMg);
3177 nonLinearCalibrator.setInitialGg(result.estimatedGg);
3178 nonLinearCalibrator.setCommonAxisUsed(commonAxisUsed);
3179 nonLinearCalibrator.setMeasurements(meas);
3180 nonLinearCalibrator.calibrate();
3181
3182 nonLinearCalibrator.getEstimatedBiases(result.estimatedBiases);
3183 result.estimatedMg = nonLinearCalibrator.getEstimatedMg();
3184 result.estimatedGg = nonLinearCalibrator.getEstimatedGg();
3185
3186 if (keepCovariance) {
3187 result.covariance = nonLinearCalibrator.getEstimatedCovariance();
3188 } else {
3189 result.covariance = null;
3190 }
3191
3192 result.estimatedMse = nonLinearCalibrator.getEstimatedMse();
3193 result.estimatedChiSq = nonLinearCalibrator.getEstimatedChiSq();
3194 result.estimatedChiSqDegreesOfFreedom = nonLinearCalibrator.getEstimatedChiSqDegreesOfFreedom();
3195 result.estimatedReducedChiSq = nonLinearCalibrator.getEstimatedReducedChiSq();
3196 result.estimatedP = nonLinearCalibrator.getEstimatedP();
3197 result.estimatedQ = nonLinearCalibrator.getEstimatedQ();
3198 }
3199
3200 solutions.add(result);
3201 } catch (final LockedException | CalibrationException | NotReadyException e) {
3202 solutions.clear();
3203 }
3204 }
3205
3206 /**
3207 * Attempts to refine calibration parameters if refinement is requested.
3208 * This method returns a refined solution or provided input if refinement is not
3209 * requested or has failed.
3210 * If refinement is enabled and it is requested to keep covariance, this method
3211 * will also keep covariance of refined position.
3212 *
3213 * @param preliminaryResult a preliminary result.
3214 */
3215 protected void attemptRefine(final PreliminaryResult preliminaryResult) {
3216 if (refineResult && inliersData != null) {
3217 final var inliers = inliersData.getInliers();
3218 final var nSamples = measurements.size();
3219
3220 final var inlierMeasurements = new ArrayList<StandardDeviationFrameBodyKinematics>();
3221 for (var i = 0; i < nSamples; i++) {
3222 if (inliers.get(i)) {
3223 // sample is inlier
3224 inlierMeasurements.add(measurements.get(i));
3225 }
3226 }
3227
3228 try {
3229 nonLinearCalibrator.setInitialBias(preliminaryResult.estimatedBiases);
3230 nonLinearCalibrator.setInitialMg(preliminaryResult.estimatedMg);
3231 nonLinearCalibrator.setInitialGg(preliminaryResult.estimatedGg);
3232 nonLinearCalibrator.setCommonAxisUsed(commonAxisUsed);
3233 nonLinearCalibrator.setMeasurements(inlierMeasurements);
3234 nonLinearCalibrator.calibrate();
3235
3236 estimatedBiases = nonLinearCalibrator.getEstimatedBiases();
3237 estimatedMg = nonLinearCalibrator.getEstimatedMg();
3238 estimatedGg = nonLinearCalibrator.getEstimatedGg();
3239
3240 if (keepCovariance) {
3241 estimatedCovariance = nonLinearCalibrator.getEstimatedCovariance();
3242 } else {
3243 estimatedCovariance = null;
3244 }
3245
3246 estimatedMse = nonLinearCalibrator.getEstimatedMse();
3247 estimatedChiSq = nonLinearCalibrator.getEstimatedChiSq();
3248 estimatedChiSqDegreesOfFreedom = nonLinearCalibrator.getEstimatedChiSqDegreesOfFreedom();
3249 estimatedReducedChiSq = nonLinearCalibrator.getEstimatedReducedChiSq();
3250 estimatedP = nonLinearCalibrator.getEstimatedP();
3251 estimatedQ = nonLinearCalibrator.getEstimatedQ();
3252
3253 } catch (final LockedException | CalibrationException | NotReadyException e) {
3254 estimatedCovariance = preliminaryResult.covariance;
3255 estimatedBiases = preliminaryResult.estimatedBiases;
3256 estimatedMg = preliminaryResult.estimatedMg;
3257 estimatedGg = preliminaryResult.estimatedGg;
3258 estimatedMse = preliminaryResult.estimatedMse;
3259 estimatedChiSq = preliminaryResult.estimatedChiSq;
3260 estimatedChiSqDegreesOfFreedom = preliminaryResult.estimatedChiSqDegreesOfFreedom;
3261 estimatedReducedChiSq = preliminaryResult.estimatedReducedChiSq;
3262 estimatedP = preliminaryResult.estimatedP;
3263 estimatedQ = preliminaryResult.estimatedQ;
3264 }
3265 } else {
3266 estimatedCovariance = preliminaryResult.covariance;
3267 estimatedBiases = preliminaryResult.estimatedBiases;
3268 estimatedMg = preliminaryResult.estimatedMg;
3269 estimatedGg = preliminaryResult.estimatedGg;
3270 estimatedMse = preliminaryResult.estimatedMse;
3271 estimatedChiSq = preliminaryResult.estimatedChiSq;
3272 estimatedChiSqDegreesOfFreedom = preliminaryResult.estimatedChiSqDegreesOfFreedom;
3273 estimatedReducedChiSq = preliminaryResult.estimatedReducedChiSq;
3274 estimatedP = preliminaryResult.estimatedP;
3275 estimatedQ = preliminaryResult.estimatedQ;
3276 }
3277 }
3278
3279 /**
3280 * Converts angular speed value and unit to radians per second.
3281 *
3282 * @param value angular speed value.
3283 * @param unit unit of angular speed value.
3284 * @return converted value.
3285 */
3286 private static double convertAngularSpeed(final double value, final AngularSpeedUnit unit) {
3287 return AngularSpeedConverter.convert(value, unit, AngularSpeedUnit.RADIANS_PER_SECOND);
3288 }
3289
3290 /**
3291 * Converts angular speed instance to radians per second.
3292 *
3293 * @param angularSpeed angular speed instance to be converted.
3294 * @return converted value.
3295 */
3296 private static double convertAngularSpeed(final AngularSpeed angularSpeed) {
3297 return convertAngularSpeed(angularSpeed.getValue().doubleValue(), angularSpeed.getUnit());
3298 }
3299
3300 /**
3301 * Internal class containing estimated preliminary result.
3302 */
3303 protected static class PreliminaryResult {
3304 /**
3305 * Estimated gyroscope biases for each IMU axis expressed in radians per second
3306 * (rad/s).
3307 */
3308 private double[] estimatedBiases;
3309
3310 /**
3311 * Estimated gyroscope scale factors and cross coupling errors.
3312 * This is the product of matrix Tg containing cross coupling errors and Kg
3313 * containing scaling factors.
3314 * So that:
3315 * <pre>
3316 * Mg = [sx mxy mxz] = Tg*Kg
3317 * [myx sy myz]
3318 * [mzx mzy sz ]
3319 * </pre>
3320 * Where:
3321 * <pre>
3322 * Kg = [sx 0 0 ]
3323 * [0 sy 0 ]
3324 * [0 0 sz]
3325 * </pre>
3326 * and
3327 * <pre>
3328 * Tg = [1 -alphaXy alphaXz ]
3329 * [alphaYx 1 -alphaYz]
3330 * [-alphaZx alphaZy 1 ]
3331 * </pre>
3332 * Hence:
3333 * <pre>
3334 * Mg = [sx mxy mxz] = Tg*Kg = [sx -sy * alphaXy sz * alphaXz ]
3335 * [myx sy myz] [sx * alphaYx sy -sz * alphaYz]
3336 * [mzx mzy sz ] [-sx * alphaZx sy * alphaZy sz ]
3337 * </pre>
3338 * This instance allows any 3x3 matrix however, typically alphaYx, alphaZx and alphaZy
3339 * are considered to be zero if the gyroscope z-axis is assumed to be the same
3340 * as the body z-axis. When this is assumed, myx = mzx = mzy = 0 and the Mg matrix
3341 * becomes upper diagonal:
3342 * <pre>
3343 * Mg = [sx mxy mxz]
3344 * [0 sy myz]
3345 * [0 0 sz ]
3346 * </pre>
3347 * Values of this matrix are unit-less.
3348 */
3349 private Matrix estimatedMg;
3350
3351 /**
3352 * Estimated G-dependent cross biases introduced on the gyroscope by the
3353 * specific forces sensed by the accelerometer.
3354 * This instance allows any 3x3 matrix.
3355 */
3356 private Matrix estimatedGg;
3357
3358 /**
3359 * Covariance matrix for estimated result.
3360 */
3361 private Matrix covariance;
3362
3363 /**
3364 * Estimated Mean Square Error.
3365 */
3366 private double estimatedMse;
3367
3368 /**
3369 * Estimated chi square value.
3370 */
3371 private double estimatedChiSq;
3372
3373 /**
3374 * Estimated degrees of freedom of chi square value. Degrees of freedom is equal to the number of sampled data
3375 * minus the number of estimated parameters.
3376 */
3377 private int estimatedChiSqDegreesOfFreedom;
3378
3379 /**
3380 * Estimated reduced chi square value. This is equal to estimated chi square value divided by its degrees of
3381 * freedom. Ideally this value should be close to 1.0.
3382 */
3383 private double estimatedReducedChiSq;
3384
3385 /**
3386 * Estimated probability of finding a smaller chi square value expressed as a value between 0.0 and 1.0. The smaller
3387 * the found chi square value is, the better the fit of the estimated parameters to the actual parameter. Thus, the
3388 * smaller the chance of finding a smaller chi square value, then the better the estimated fit is.
3389 */
3390 private double estimatedP;
3391
3392 /**
3393 * Estimated measure of quality of estimated fit as a value between 0.0 and 1.0. The larger the quality value is,
3394 * the better the fit that has been estimated.
3395 */
3396 private double estimatedQ;
3397 }
3398 }