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