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