View Javadoc
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 }