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 }