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