View Javadoc
1   /*
2    * Copyright (C) 2022 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.navigation.LockedException;
20  import com.irurueta.navigation.NotReadyException;
21  import com.irurueta.navigation.inertial.calibration.CalibrationException;
22  import com.irurueta.navigation.inertial.calibration.StandardDeviationBodyMagneticFluxDensity;
23  import com.irurueta.numerical.robust.RANSACRobustEstimator;
24  import com.irurueta.numerical.robust.RANSACRobustEstimatorListener;
25  import com.irurueta.numerical.robust.RobustEstimator;
26  import com.irurueta.numerical.robust.RobustEstimatorException;
27  import com.irurueta.numerical.robust.RobustEstimatorMethod;
28  
29  import java.util.List;
30  
31  /**
32   * Robustly estimates magnetometer hard-iron biases, cross couplings and
33   * scaling factors using RANSAC algorithm.
34   * <p>
35   * To use this calibrator at least 10 measurements with known magnetic field norm at
36   * an unknown position and instant must be taken at 10 different unknown orientations
37   * when common z-axis is assumed, otherwise at least 13
38   * measurements are required.
39   * <p>
40   * Measured magnetic flux density is assumed to follow the model shown below:
41   * <pre>
42   *     mBmeas = bm + (I + Mm) * mBtrue + w
43   * </pre>
44   * Where:
45   * - mBmeas is the measured magnetic flux density. This is a 3x1 vector.
46   * - bm is magnetometer hard-iron bias. Ideally, on a perfect magnetometer,
47   * this should be a 3x1 zero vector.
48   * - I is the 3x3 identity matrix.
49   * - Mm is the 3x3 soft-iron matrix containing cross-couplings and scaling
50   * factors. Ideally, on a perfect magnetometer, this should be a 3x3 zero
51   * matrix.
52   * - mBtrue is ground-truth magnetic flux density. This is a 3x1 vector.
53   * - w is measurement noise. This is a 3x1 vector.
54   * Notice that this calibrator assumes that all measurements are taken in
55   * a short span of time, where Earth magnetic field can be assumed to be
56   * constant at provided location and instant.
57   */
58  public class RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator extends
59          RobustKnownMagneticFluxDensityNormMagnetometerCalibrator {
60  
61      /**
62       * Constant defining default threshold to determine whether samples are inliers or not.
63       */
64      public static final double DEFAULT_THRESHOLD = 1e-9;
65  
66      /**
67       * Minimum value that can be set as threshold.
68       * Threshold must be strictly greater than 0.0.
69       */
70      public static final double MIN_THRESHOLD = 0.0;
71  
72      /**
73       * Indicates that by default inliers will only be computed but not kept.
74       */
75      public static final boolean DEFAULT_COMPUTE_AND_KEEP_INLIERS = false;
76  
77      /**
78       * Indicates that by default residuals will only be computed but not kept.
79       */
80      public static final boolean DEFAULT_COMPUTE_AND_KEEP_RESIDUALS = false;
81  
82      /**
83       * Threshold to determine whether samples are inliers or not when testing possible solutions.
84       * The threshold refers to the amount of error on distance between estimated position and
85       * distances provided for each sample.
86       */
87      private double threshold = DEFAULT_THRESHOLD;
88  
89      /**
90       * Indicates whether inliers must be computed and kept.
91       */
92      private boolean computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
93  
94      /**
95       * Indicates whether residuals must be computed and kept.
96       */
97      private boolean computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
98  
99      /**
100      * Constructor.
101      */
102     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator() {
103         super();
104     }
105 
106     /**
107      * Constructor.
108      *
109      * @param listener listener to handle events raised by this calibrator.
110      */
111     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
112             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
113         super(listener);
114     }
115 
116     /**
117      * Constructor.
118      *
119      * @param measurements list of body magnetic flux density
120      *                     measurements with standard deviation of
121      *                     magnetometer measurements taken at the same
122      *                     position with zero velocity and unknown different
123      *                     orientations.
124      */
125     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
126             final List<StandardDeviationBodyMagneticFluxDensity> measurements) {
127         super(measurements);
128     }
129 
130     /**
131      * Constructor.
132      *
133      * @param commonAxisUsed indicates whether z-axis is assumed to be common
134      *                       for the accelerometer, gyroscope and magnetometer.
135      */
136     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(final boolean commonAxisUsed) {
137         super(commonAxisUsed);
138     }
139 
140     /**
141      * Constructor.
142      *
143      * @param initialHardIron initial hard-iron to find a solution.
144      * @throws IllegalArgumentException if provided hard-iron array does
145      *                                  not have length 3.
146      */
147     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(final double[] initialHardIron) {
148         super(initialHardIron);
149     }
150 
151     /**
152      * Constructor.
153      *
154      * @param initialHardIron initial hard-iron to find a solution.
155      * @throws IllegalArgumentException if provided hard-iron matrix is not
156      *                                  3x1.
157      */
158     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(final Matrix initialHardIron) {
159         super(initialHardIron);
160     }
161 
162     /**
163      * Constructor.
164      *
165      * @param initialHardIron initial hard-iron to find a solution.
166      * @param initialMm       initial soft-iron matrix containing scale factors
167      *                        and cross coupling errors.
168      * @throws IllegalArgumentException if provided hard-iron matrix is not
169      *                                  3x1 or if soft-iron matrix is not
170      *                                  3x3.
171      */
172     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
173             final Matrix initialHardIron, final Matrix initialMm) {
174         super(initialHardIron, initialMm);
175     }
176 
177     /**
178      * Constructor.
179      *
180      * @param measurements list of body magnetic flux density
181      *                     measurements with standard deviation of
182      *                     magnetometer measurements taken at the same
183      *                     position with zero velocity and unknown different
184      *                     orientations.
185      * @param listener     listener to handle events raised by this calibrator.
186      */
187     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
188             final List<StandardDeviationBodyMagneticFluxDensity> measurements,
189             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
190         super(measurements, listener);
191     }
192 
193     /**
194      * Constructor.
195      *
196      * @param measurements   collection of body magnetic flux density
197      *                       measurements with standard deviation of
198      *                       magnetometer measurements taken at the same
199      *                       position with zero velocity and unknown different
200      *                       orientations.
201      * @param commonAxisUsed indicates whether z-axis is assumed to be common
202      *                       for the accelerometer, gyroscope and magnetometer.
203      */
204     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
205             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed) {
206         super(measurements, commonAxisUsed);
207     }
208 
209     /**
210      * Constructor.
211      *
212      * @param measurements   collection of body magnetic flux density
213      *                       measurements with standard deviation of
214      *                       magnetometer measurements taken at the same
215      *                       position with zero velocity and unknown different
216      *                       orientations.
217      * @param commonAxisUsed indicates whether z-axis is assumed to be common
218      *                       for the accelerometer, gyroscope and magnetometer.
219      * @param listener       listener to handle events raised by this calibrator.
220      */
221     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
222             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
223             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
224         super(measurements, commonAxisUsed, listener);
225     }
226 
227     /**
228      * Constructor.
229      *
230      * @param measurements    collection of body magnetic flux density
231      *                        measurements with standard deviation of
232      *                        magnetometer measurements taken at the same
233      *                        position with zero velocity and unknown different
234      *                        orientations.
235      * @param initialHardIron initial hard-iron to find a solution.
236      * @throws IllegalArgumentException if provided hard-iron array does
237      *                                  not have length 3.
238      */
239     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
240             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] initialHardIron) {
241         super(measurements, initialHardIron);
242     }
243 
244     /**
245      * Constructor.
246      *
247      * @param measurements    collection of body magnetic flux density
248      *                        measurements with standard deviation of
249      *                        magnetometer measurements taken at the same
250      *                        position with zero velocity and unknown different
251      *                        orientations.
252      * @param initialHardIron initial hard-iron to find a solution.
253      * @param listener        listener to handle events raised by this calibrator.
254      * @throws IllegalArgumentException if provided hard-iron array does
255      *                                  not have length 3.
256      */
257     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
258             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] initialHardIron,
259             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
260         super(measurements, initialHardIron, listener);
261     }
262 
263     /**
264      * Constructor.
265      *
266      * @param measurements    collection of body magnetic flux density
267      *                        measurements with standard deviation of
268      *                        magnetometer measurements taken at the same
269      *                        position with zero velocity and unknown different
270      *                        orientations.
271      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
272      *                        for the accelerometer, gyroscope and magnetometer.
273      * @param initialHardIron initial hard-iron to find a solution.
274      * @throws IllegalArgumentException if provided hard-iron array does
275      *                                  not have length 3.
276      */
277     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
278             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
279             final double[] initialHardIron) {
280         super(measurements, commonAxisUsed, initialHardIron);
281     }
282 
283     /**
284      * Constructor.
285      *
286      * @param measurements    collection of body magnetic flux density
287      *                        measurements with standard deviation of
288      *                        magnetometer measurements taken at the same
289      *                        position with zero velocity and unknown different
290      *                        orientations.
291      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
292      *                        for the accelerometer, gyroscope and magnetometer.
293      * @param initialHardIron initial hard-iron to find a solution.
294      * @param listener        listener to handle events raised by this calibrator.
295      * @throws IllegalArgumentException if provided hard-iron array does
296      *                                  not have length 3.
297      */
298     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
299             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
300             final double[] initialHardIron,
301             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
302         super(measurements, commonAxisUsed, initialHardIron, listener);
303     }
304 
305     /**
306      * Constructor.
307      *
308      * @param measurements    collection of body magnetic flux density
309      *                        measurements with standard deviation of
310      *                        magnetometer measurements taken at the same
311      *                        position with zero velocity and unknown different
312      *                        orientations.
313      * @param initialHardIron initial hard-iron to find a solution.
314      * @throws IllegalArgumentException if provided hard-iron matrix is not
315      *                                  3x1.
316      */
317     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
318             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron) {
319         super(measurements, initialHardIron);
320     }
321 
322     /**
323      * Constructor.
324      *
325      * @param measurements    collection of body magnetic flux density
326      *                        measurements with standard deviation of
327      *                        magnetometer measurements taken at the same
328      *                        position with zero velocity and unknown different
329      *                        orientations.
330      * @param initialHardIron initial hard-iron to find a solution.
331      * @param listener        listener to handle events raised by this calibrator.
332      * @throws IllegalArgumentException if provided hard-iron matrix is not
333      *                                  3x1.
334      */
335     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
336             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
337             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
338         super(measurements, initialHardIron, listener);
339     }
340 
341     /**
342      * Constructor.
343      *
344      * @param measurements    collection of body magnetic flux density
345      *                        measurements with standard deviation of
346      *                        magnetometer measurements taken at the same
347      *                        position with zero velocity and unknown different
348      *                        orientations.
349      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
350      *                        for the accelerometer, gyroscope and magnetometer.
351      * @param initialHardIron initial hard-iron to find a solution.
352      * @throws IllegalArgumentException if provided hard-iron matrix is not
353      *                                  3x1.
354      */
355     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
356             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
357             final Matrix initialHardIron) {
358         super(measurements, commonAxisUsed, initialHardIron);
359     }
360 
361     /**
362      * Constructor.
363      *
364      * @param measurements    collection of body magnetic flux density
365      *                        measurements with standard deviation of
366      *                        magnetometer measurements taken at the same
367      *                        position with zero velocity and unknown different
368      *                        orientations.
369      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
370      *                        for the accelerometer, gyroscope and magnetometer.
371      * @param initialHardIron initial hard-iron to find a solution.
372      * @param listener        listener to handle events raised by this calibrator.
373      * @throws IllegalArgumentException if provided hard-iron matrix is not
374      *                                  3x1.
375      */
376     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
377             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
378             final Matrix initialHardIron,
379             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
380         super(measurements, commonAxisUsed, initialHardIron, listener);
381     }
382 
383     /**
384      * Constructor.
385      *
386      * @param measurements    collection of body magnetic flux density
387      *                        measurements with standard deviation of
388      *                        magnetometer measurements taken at the same
389      *                        position with zero velocity and unknown different
390      *                        orientations.
391      * @param initialHardIron initial hard-iron to find a solution.
392      * @param initialMm       initial soft-iron matrix containing scale factors
393      *                        and cross coupling errors.
394      * @throws IllegalArgumentException if provided hard-iron matrix is not
395      *                                  3x1 or if soft-iron matrix is not
396      *                                  3x3.
397      */
398     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
399             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
400             final Matrix initialMm) {
401         super(measurements, initialHardIron, initialMm);
402     }
403 
404     /**
405      * Constructor.
406      *
407      * @param measurements    collection of body magnetic flux density
408      *                        measurements with standard deviation of
409      *                        magnetometer measurements taken at the same
410      *                        position with zero velocity and unknown different
411      *                        orientations.
412      * @param initialHardIron initial hard-iron to find a solution.
413      * @param initialMm       initial soft-iron matrix containing scale factors
414      *                        and cross coupling errors.
415      * @param listener        listener to handle events raised by this calibrator.
416      * @throws IllegalArgumentException if provided hard-iron matrix is not
417      *                                  3x1 or if soft-iron matrix is not
418      *                                  3x3.
419      */
420     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
421             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
422             final Matrix initialMm, final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
423         super(measurements, initialHardIron, initialMm, listener);
424     }
425 
426     /**
427      * Constructor.
428      *
429      * @param measurements    collection of body magnetic flux density
430      *                        measurements with standard deviation of
431      *                        magnetometer measurements taken at the same
432      *                        position with zero velocity and unknown different
433      *                        orientations.
434      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
435      *                        for the accelerometer, gyroscope and magnetometer.
436      * @param initialHardIron initial hard-iron to find a solution.
437      * @param initialMm       initial soft-iron matrix containing scale factors
438      *                        and cross coupling errors.
439      * @throws IllegalArgumentException if provided hard-iron matrix is not
440      *                                  3x1 or if soft-iron matrix is not
441      *                                  3x3.
442      */
443     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
444             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
445             final Matrix initialHardIron, final Matrix initialMm) {
446         super(measurements, commonAxisUsed, initialHardIron, initialMm);
447     }
448 
449     /**
450      * Constructor.
451      *
452      * @param measurements    collection of body magnetic flux density
453      *                        measurements with standard deviation of
454      *                        magnetometer measurements taken at the same
455      *                        position with zero velocity and unknown different
456      *                        orientations.
457      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
458      *                        for the accelerometer, gyroscope and magnetometer.
459      * @param initialHardIron initial hard-iron to find a solution.
460      * @param initialMm       initial soft-iron matrix containing scale factors
461      *                        and cross coupling errors.
462      * @param listener        listener to handle events raised by this calibrator.
463      * @throws IllegalArgumentException if provided hard-iron matrix is not
464      *                                  3x1 or if soft-iron matrix is not
465      *                                  3x3.
466      */
467     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
468             final List<StandardDeviationBodyMagneticFluxDensity> measurements,
469             final boolean commonAxisUsed, final Matrix initialHardIron, final Matrix initialMm,
470             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
471         super(measurements, commonAxisUsed, initialHardIron, initialMm, listener);
472     }
473 
474     /**
475      * Constructor.
476      *
477      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
478      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
479      */
480     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
481             final Double groundTruthMagneticFluxDensityNorm) {
482         super(groundTruthMagneticFluxDensityNorm);
483     }
484 
485     /**
486      * Constructor.
487      *
488      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
489      * @param listener                           listener to handle events raised by this calibrator.
490      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
491      */
492     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
493             final Double groundTruthMagneticFluxDensityNorm,
494             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
495         super(groundTruthMagneticFluxDensityNorm, listener);
496     }
497 
498     /**
499      * Constructor.
500      *
501      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
502      * @param measurements                       collection of body magnetic flux density
503      *                                           measurements with standard deviation of
504      *                                           magnetometer measurements taken at the same
505      *                                           position with zero velocity and unknown different
506      *                                           orientations.
507      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
508      */
509     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
510             final Double groundTruthMagneticFluxDensityNorm,
511             final List<StandardDeviationBodyMagneticFluxDensity> measurements) {
512         super(groundTruthMagneticFluxDensityNorm, measurements);
513     }
514 
515     /**
516      * Constructor.
517      *
518      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
519      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
520      *                                           for the accelerometer, gyroscope and magnetometer.
521      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
522      */
523     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
524             final Double groundTruthMagneticFluxDensityNorm, final boolean commonAxisUsed) {
525         super(groundTruthMagneticFluxDensityNorm, commonAxisUsed);
526     }
527 
528     /**
529      * Constructor.
530      *
531      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
532      * @param initialHardIron                    initial hard-iron to find a solution.
533      * @throws IllegalArgumentException if provided magnetic flux norm value is
534      *                                  negative, or if provided hard-iron array does
535      *                                  not have length 3.
536      */
537     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
538             final Double groundTruthMagneticFluxDensityNorm, final double[] initialHardIron) {
539         super(groundTruthMagneticFluxDensityNorm, initialHardIron);
540     }
541 
542     /**
543      * Constructor.
544      *
545      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
546      * @param initialHardIron                    initial hard-iron to find a solution.
547      * @throws IllegalArgumentException if provided magnetic flux norm value is
548      *                                  negative, or if provided hard-iron matrix is not
549      *                                  3x1.
550      */
551     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
552             final Double groundTruthMagneticFluxDensityNorm, final Matrix initialHardIron) {
553         super(groundTruthMagneticFluxDensityNorm, initialHardIron);
554     }
555 
556     /**
557      * Constructor.
558      *
559      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
560      * @param initialHardIron                    initial hard-iron to find a solution.
561      * @param initialMm                          initial soft-iron matrix containing scale factors
562      *                                           and cross coupling errors.
563      * @throws IllegalArgumentException if provided magnetic flux norm value is
564      *                                  negative, or if provided hard-iron matrix is not
565      *                                  3x1 or if soft-iron matrix is not
566      *                                  3x3.
567      */
568     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
569             final Double groundTruthMagneticFluxDensityNorm, final Matrix initialHardIron, final Matrix initialMm) {
570         super(groundTruthMagneticFluxDensityNorm, initialHardIron, initialMm);
571     }
572 
573     /**
574      * Constructor.
575      *
576      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
577      * @param measurements                       collection of body magnetic flux density
578      *                                           measurements with standard deviation of
579      *                                           magnetometer measurements taken at the same
580      *                                           position with zero velocity and unknown different
581      *                                           orientations.
582      * @param listener                           listener to handle events raised by this calibrator.
583      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
584      */
585     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
586             final Double groundTruthMagneticFluxDensityNorm,
587             final List<StandardDeviationBodyMagneticFluxDensity> measurements,
588             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
589         super(groundTruthMagneticFluxDensityNorm, measurements, listener);
590     }
591 
592     /**
593      * Constructor.
594      *
595      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
596      * @param measurements                       collection of body magnetic flux density
597      *                                           measurements with standard deviation of
598      *                                           magnetometer measurements taken at the same
599      *                                           position with zero velocity and unknown different
600      *                                           orientations.
601      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
602      *                                           for the accelerometer, gyroscope and magnetometer.
603      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
604      */
605     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
606             final Double groundTruthMagneticFluxDensityNorm,
607             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed) {
608         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed);
609     }
610 
611     /**
612      * Constructor.
613      *
614      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
615      * @param measurements                       collection of body magnetic flux density
616      *                                           measurements with standard deviation of
617      *                                           magnetometer measurements taken at the same
618      *                                           position with zero velocity and unknown different
619      *                                           orientations.
620      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
621      *                                           for the accelerometer, gyroscope and magnetometer.
622      * @param listener                           listener to handle events raised by this calibrator.
623      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
624      */
625     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
626             final Double groundTruthMagneticFluxDensityNorm,
627             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
628             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
629         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, listener);
630     }
631 
632     /**
633      * Constructor.
634      *
635      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
636      * @param measurements                       collection of body magnetic flux density
637      *                                           measurements with standard deviation of
638      *                                           magnetometer measurements taken at the same
639      *                                           position with zero velocity and unknown different
640      *                                           orientations.
641      * @param initialHardIron                    initial hard-iron to find a solution.
642      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
643      *                                  or if provided hard-iron array does not have length 3.
644      */
645     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
646             final Double groundTruthMagneticFluxDensityNorm,
647             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] initialHardIron) {
648         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron);
649     }
650 
651     /**
652      * Constructor.
653      *
654      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
655      * @param measurements                       collection of body magnetic flux density
656      *                                           measurements with standard deviation of
657      *                                           magnetometer measurements taken at the same
658      *                                           position with zero velocity and unknown different
659      *                                           orientations.
660      * @param initialHardIron                    initial hard-iron to find a solution.
661      * @param listener                           listener to handle events raised by this calibrator.
662      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
663      *                                  or if provided hard-iron array does not have length 3.
664      */
665     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
666             final Double groundTruthMagneticFluxDensityNorm,
667             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] initialHardIron,
668             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
669         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron, listener);
670     }
671 
672     /**
673      * Constructor.
674      *
675      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
676      * @param measurements                       collection of body magnetic flux density
677      *                                           measurements with standard deviation of
678      *                                           magnetometer measurements taken at the same
679      *                                           position with zero velocity and unknown different
680      *                                           orientations.
681      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
682      *                                           for the accelerometer, gyroscope and magnetometer.
683      * @param initialHardIron                    initial hard-iron to find a solution.
684      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
685      *                                  or if provided hard-iron array does not have length 3.
686      */
687     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
688             final Double groundTruthMagneticFluxDensityNorm,
689             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
690             final double[] initialHardIron) {
691         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron);
692     }
693 
694     /**
695      * Constructor.
696      *
697      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
698      * @param measurements                       collection of body magnetic flux density
699      *                                           measurements with standard deviation of
700      *                                           magnetometer measurements taken at the same
701      *                                           position with zero velocity and unknown different
702      *                                           orientations.
703      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
704      *                                           for the accelerometer, gyroscope and magnetometer.
705      * @param initialHardIron                    initial hard-iron to find a solution.
706      * @param listener                           listener to handle events raised by this calibrator.
707      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
708      *                                  or if provided hard-iron array does not have length 3.
709      */
710     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
711             final Double groundTruthMagneticFluxDensityNorm,
712             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
713             final double[] initialHardIron,
714             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
715         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron, listener);
716     }
717 
718     /**
719      * Constructor.
720      *
721      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
722      * @param measurements                       collection of body magnetic flux density
723      *                                           measurements with standard deviation of
724      *                                           magnetometer measurements taken at the same
725      *                                           position with zero velocity and unknown different
726      *                                           orientations.
727      * @param initialHardIron                    initial hard-iron to find a solution.
728      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
729      *                                  or if provided hard-iron matrix is not 3x1.
730      */
731     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
732             final Double groundTruthMagneticFluxDensityNorm,
733             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron) {
734         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron);
735     }
736 
737     /**
738      * Constructor.
739      *
740      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
741      * @param measurements                       collection of body magnetic flux density
742      *                                           measurements with standard deviation of
743      *                                           magnetometer measurements taken at the same
744      *                                           position with zero velocity and unknown different
745      *                                           orientations.
746      * @param initialHardIron                    initial hard-iron to find a solution.
747      * @param listener                           listener to handle events raised by this calibrator.
748      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
749      *                                  or if provided hard-iron matrix is not 3x1.
750      */
751     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
752             final Double groundTruthMagneticFluxDensityNorm,
753             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
754             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
755         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron, listener);
756     }
757 
758     /**
759      * Constructor.
760      *
761      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
762      * @param measurements                       collection of body magnetic flux density
763      *                                           measurements with standard deviation of
764      *                                           magnetometer measurements taken at the same
765      *                                           position with zero velocity and unknown different
766      *                                           orientations.
767      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
768      *                                           for the accelerometer, gyroscope and magnetometer.
769      * @param initialHardIron                    initial hard-iron to find a solution.
770      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
771      *                                  or if provided hard-iron matrix is not 3x1.
772      */
773     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
774             final Double groundTruthMagneticFluxDensityNorm,
775             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
776             final Matrix initialHardIron) {
777         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron);
778     }
779 
780     /**
781      * Constructor.
782      *
783      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
784      * @param measurements                       collection of body magnetic flux density
785      *                                           measurements with standard deviation of
786      *                                           magnetometer measurements taken at the same
787      *                                           position with zero velocity and unknown different
788      *                                           orientations.
789      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
790      *                                           for the accelerometer, gyroscope and magnetometer.
791      * @param initialHardIron                    initial hard-iron to find a solution.
792      * @param listener                           listener to handle events raised by this calibrator.
793      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
794      *                                  or if provided hard-iron matrix is not 3x1.
795      */
796     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
797             final Double groundTruthMagneticFluxDensityNorm,
798             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
799             final Matrix initialHardIron,
800             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
801         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron, listener);
802     }
803 
804     /**
805      * Constructor.
806      *
807      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
808      * @param measurements                       collection of body magnetic flux density
809      *                                           measurements with standard deviation of
810      *                                           magnetometer measurements taken at the same
811      *                                           position with zero velocity and unknown different
812      *                                           orientations.
813      * @param initialHardIron                    initial hard-iron to find a solution.
814      * @param initialMm                          initial soft-iron matrix containing scale factors
815      *                                           and cross coupling errors.
816      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
817      *                                  or if provided hard-iron matrix is not 3x1 or if
818      *                                  soft-iron matrix is not 3x3.
819      */
820     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
821             final Double groundTruthMagneticFluxDensityNorm,
822             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
823             final Matrix initialMm) {
824         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron, initialMm);
825     }
826 
827     /**
828      * Constructor.
829      *
830      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
831      * @param measurements                       collection of body magnetic flux density
832      *                                           measurements with standard deviation of
833      *                                           magnetometer measurements taken at the same
834      *                                           position with zero velocity and unknown different
835      *                                           orientations.
836      * @param initialHardIron                    initial hard-iron to find a solution.
837      * @param initialMm                          initial soft-iron matrix containing scale factors
838      *                                           and cross coupling errors.
839      * @param listener                           listener to handle events raised by this calibrator.
840      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
841      *                                  or if provided hard-iron matrix is not 3x1 or if
842      *                                  soft-iron matrix is not 3x3.
843      */
844     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
845             final Double groundTruthMagneticFluxDensityNorm,
846             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
847             final Matrix initialMm, final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
848         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron, initialMm, listener);
849     }
850 
851     /**
852      * Constructor.
853      *
854      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
855      * @param measurements                       collection of body magnetic flux density
856      *                                           measurements with standard deviation of
857      *                                           magnetometer measurements taken at the same
858      *                                           position with zero velocity and unknown different
859      *                                           orientations.
860      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
861      *                                           for the accelerometer, gyroscope and magnetometer.
862      * @param initialHardIron                    initial hard-iron to find a solution.
863      * @param initialMm                          initial soft-iron matrix containing scale factors
864      *                                           and cross coupling errors.
865      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
866      *                                  or if provided hard-iron matrix is not 3x1
867      *                                  or if soft-iron matrix is not 3x3.
868      */
869     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
870             final Double groundTruthMagneticFluxDensityNorm,
871             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
872             final Matrix initialHardIron, final Matrix initialMm) {
873         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron, initialMm);
874     }
875 
876     /**
877      * Constructor.
878      *
879      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
880      * @param measurements                       collection of body magnetic flux density
881      *                                           measurements with standard deviation of
882      *                                           magnetometer measurements taken at the same
883      *                                           position with zero velocity and unknown different
884      *                                           orientations.
885      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
886      *                                           for the accelerometer, gyroscope and magnetometer.
887      * @param initialHardIron                    initial hard-iron to find a solution.
888      * @param initialMm                          initial soft-iron matrix containing scale factors
889      *                                           and cross coupling errors.
890      * @param listener                           listener to handle events raised by this calibrator.
891      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
892      *                                  or if provided hard-iron matrix is not 3x1
893      *                                  or if soft-iron matrix is not 3x3.
894      */
895     public RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
896             final Double groundTruthMagneticFluxDensityNorm,
897             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
898             final Matrix initialHardIron, final Matrix initialMm,
899             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
900         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron, initialMm, listener);
901     }
902 
903     /**
904      * Gets threshold to determine whether samples are inliers or not when testing possible solutions.
905      * The threshold refers to the amount of error on norm between measured specific forces and the
906      * ones generated with estimated calibration parameters provided for each sample.
907      *
908      * @return threshold to determine whether samples are inliers or not.
909      */
910     public double getThreshold() {
911         return threshold;
912     }
913 
914     /**
915      * Sets threshold to determine whether samples are inliers or not when testing possible solutions.
916      * The threshold refers to the amount of error on norm between measured specific forces and the
917      * ones generated with estimated calibration parameters provided for each sample.
918      *
919      * @param threshold threshold to determine whether samples are inliers or not.
920      * @throws IllegalArgumentException if provided value is equal or less than zero.
921      * @throws LockedException          if calibrator is currently running.
922      */
923     public void setThreshold(final double threshold) throws LockedException {
924         if (running) {
925             throw new LockedException();
926         }
927         if (threshold <= MIN_THRESHOLD) {
928             throw new IllegalArgumentException();
929         }
930         this.threshold = threshold;
931     }
932 
933     /**
934      * Indicates whether inliers must be computed and kept.
935      *
936      * @return true if inliers must be computed and kept, false if inliers
937      * only need to be computed but not kept.
938      */
939     public boolean isComputeAndKeepInliersEnabled() {
940         return computeAndKeepInliers;
941     }
942 
943     /**
944      * Specifies whether inliers must be computed and kept.
945      *
946      * @param computeAndKeepInliers true if inliers must be computed and kept,
947      *                              false if inliers only need to be computed but not kept.
948      * @throws LockedException if calibrator is currently running.
949      */
950     public void setComputeAndKeepInliersEnabled(final boolean computeAndKeepInliers) throws LockedException {
951         if (running) {
952             throw new LockedException();
953         }
954         this.computeAndKeepInliers = computeAndKeepInliers;
955     }
956 
957     /**
958      * Indicates whether residuals must be computed and kept.
959      *
960      * @return true if residuals must be computed and kept, false if residuals
961      * only need to be computed but not kept.
962      */
963     public boolean isComputeAndKeepResiduals() {
964         return computeAndKeepResiduals;
965     }
966 
967     /**
968      * Specifies whether residuals must be computed and kept.
969      *
970      * @param computeAndKeepResiduals true if residuals must be computed and kept,
971      *                                false if residuals only need to be computed but not kept.
972      * @throws LockedException if calibrator is currently running.
973      */
974     public void setComputeAndKeepResidualsEnabled(final boolean computeAndKeepResiduals) throws LockedException {
975         if (running) {
976             throw new LockedException();
977         }
978         this.computeAndKeepResiduals = computeAndKeepResiduals;
979     }
980 
981     /**
982      * Estimates magnetometer calibration parameters containing hard-iron
983      * bias and soft-iron scale factors and cross-coupling errors.
984      *
985      * @throws LockedException      if calibrator is currently running.
986      * @throws NotReadyException    if calibrator is not ready.
987      * @throws CalibrationException if estimation fails for numerical reasons.
988      */
989     @SuppressWarnings("DuplicatedCode")
990     @Override
991     public void calibrate() throws LockedException, NotReadyException, CalibrationException {
992         if (running) {
993             throw new LockedException();
994         }
995         if (!isReady()) {
996             throw new NotReadyException();
997         }
998 
999         final var innerEstimator = new RANSACRobustEstimator<>(new RANSACRobustEstimatorListener<PreliminaryResult>() {
1000             @Override
1001             public double getThreshold() {
1002                 return threshold;
1003             }
1004 
1005             @Override
1006             public int getTotalSamples() {
1007                 return measurements.size();
1008             }
1009 
1010             @Override
1011             public int getSubsetSize() {
1012                 return preliminarySubsetSize;
1013             }
1014 
1015             @Override
1016             public void estimatePreliminarSolutions(
1017                     final int[] samplesIndices, final List<PreliminaryResult> solutions) {
1018                 computePreliminarySolutions(samplesIndices, solutions);
1019             }
1020 
1021             @Override
1022             public double computeResidual(final PreliminaryResult currentEstimation, final int i) {
1023                 return computeError(measurements.get(i), currentEstimation);
1024             }
1025 
1026             @Override
1027             public boolean isReady() {
1028                 return RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator.super.isReady();
1029             }
1030 
1031             @Override
1032             public void onEstimateStart(final RobustEstimator<PreliminaryResult> estimator) {
1033                 // no action needed
1034             }
1035 
1036             @Override
1037             public void onEstimateEnd(final RobustEstimator<PreliminaryResult> estimator) {
1038                 // no action needed
1039             }
1040 
1041             @Override
1042             public void onEstimateNextIteration(
1043                     final RobustEstimator<PreliminaryResult> estimator, final int iteration) {
1044                 if (listener != null) {
1045                     listener.onCalibrateNextIteration(
1046                             RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator.this, iteration);
1047                 }
1048             }
1049 
1050             @Override
1051             public void onEstimateProgressChange(
1052                     final RobustEstimator<PreliminaryResult> estimator, final float progress) {
1053                 if (listener != null) {
1054                     listener.onCalibrateProgressChange(
1055                             RANSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator.this, progress);
1056                 }
1057             }
1058         });
1059 
1060         try {
1061             running = true;
1062 
1063             if (listener != null) {
1064                 listener.onCalibrateStart(this);
1065             }
1066 
1067             inliersData = null;
1068 
1069             innerEstimator.setComputeAndKeepInliersEnabled(computeAndKeepInliers || refineResult);
1070             innerEstimator.setComputeAndKeepResidualsEnabled(computeAndKeepResiduals || refineResult);
1071             innerEstimator.setConfidence(confidence);
1072             innerEstimator.setMaxIterations(maxIterations);
1073             innerEstimator.setProgressDelta(progressDelta);
1074             final var preliminaryResult = innerEstimator.estimate();
1075             inliersData = innerEstimator.getInliersData();
1076 
1077             attemptRefine(preliminaryResult);
1078 
1079             if (listener != null) {
1080                 listener.onCalibrateEnd(this);
1081             }
1082 
1083         } catch (final com.irurueta.numerical.LockedException e) {
1084             throw new LockedException(e);
1085         } catch (final com.irurueta.numerical.NotReadyException e) {
1086             throw new NotReadyException(e);
1087         } catch (final RobustEstimatorException e) {
1088             throw new CalibrationException(e);
1089         } finally {
1090             running = false;
1091         }
1092     }
1093 
1094     /**
1095      * Returns method being used for robust estimation.
1096      *
1097      * @return method being used for robust estimation.
1098      */
1099     @Override
1100     public RobustEstimatorMethod getMethod() {
1101         return RobustEstimatorMethod.RANSAC;
1102     }
1103 
1104     /**
1105      * Indicates whether this calibrator requires quality scores for each
1106      * measurement or not.
1107      *
1108      * @return true if quality scores are required, false otherwise.
1109      */
1110     @Override
1111     public boolean isQualityScoresRequired() {
1112         return false;
1113     }
1114 }