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 cross couplings and scaling factors using
33   * RANSAC algorithm.
34   * <p>
35   * To use this calibrator at least 7 measurements with known magnetic field norm at
36   * an unknown position and instant must be taken at 7 different unknown orientations
37   * when common z-axis is assumed, otherwise at least 10
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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator extends
59          RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator {
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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator() {
103         super();
104     }
105 
106     /**
107      * Constructor.
108      *
109      * @param listener listener to handle events raised by this calibrator.
110      */
111     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
112             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener 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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(final boolean commonAxisUsed) {
137         super(commonAxisUsed);
138     }
139 
140     /**
141      * Constructor.
142      *
143      * @param hardIron known hard-iron.
144      * @throws IllegalArgumentException if provided hard-iron array does
145      *                                  not have length 3.
146      */
147     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(final double[] hardIron) {
148         super(hardIron);
149     }
150 
151     /**
152      * Constructor.
153      *
154      * @param hardIron known hard-iron.
155      * @throws IllegalArgumentException if provided hard-iron matrix is not
156      *                                  3x1.
157      */
158     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(final Matrix hardIron) {
159         super(hardIron);
160     }
161 
162     /**
163      * Constructor.
164      *
165      * @param hardIron  known hard-iron.
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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
173             final Matrix hardIron, final Matrix initialMm) {
174         super(hardIron, 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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
188             final List<StandardDeviationBodyMagneticFluxDensity> measurements,
189             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener 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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
222             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
223             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener 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 hardIron     known hard-iron.
236      * @throws IllegalArgumentException if provided hard-iron array does
237      *                                  not have length 3.
238      */
239     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
240             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] hardIron) {
241         super(measurements, hardIron);
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 hardIron     known hard-iron.
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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
258             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] hardIron,
259             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
260         super(measurements, hardIron, 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 hardIron       known hard-iron.
274      * @throws IllegalArgumentException if provided hard-iron array does
275      *                                  not have length 3.
276      */
277     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
278             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
279             final double[] hardIron) {
280         super(measurements, commonAxisUsed, hardIron);
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 hardIron       known hard-iron.
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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
299             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
300             final double[] hardIron,
301             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
302         super(measurements, commonAxisUsed, hardIron, 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 hardIron     known hard-iron.
314      * @throws IllegalArgumentException if provided hard-iron matrix is not
315      *                                  3x1.
316      */
317     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
318             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron) {
319         super(measurements, hardIron);
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 hardIron     known 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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
336             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
337             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
338         super(measurements, hardIron, 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 hardIron       known hard-iron.
352      * @throws IllegalArgumentException if provided hard-iron matrix is not
353      *                                  3x1.
354      */
355     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
356             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
357             final Matrix hardIron) {
358         super(measurements, commonAxisUsed, hardIron);
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 hardIron       known hard-iron.
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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
377             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
378             final Matrix hardIron,
379             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
380         super(measurements, commonAxisUsed, hardIron, 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 hardIron     known hard-iron.
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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
399             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
400             final Matrix initialMm) {
401         super(measurements, hardIron, 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 hardIron     known hard-iron.
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 RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
421             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
422             final Matrix initialMm,
423             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
424         super(measurements, hardIron, initialMm, listener);
425     }
426 
427     /**
428      * Constructor.
429      *
430      * @param measurements   collection of body magnetic flux density
431      *                       measurements with standard deviation of
432      *                       magnetometer measurements taken at the same
433      *                       position with zero velocity and unknown different
434      *                       orientations.
435      * @param commonAxisUsed indicates whether z-axis is assumed to be common
436      *                       for the accelerometer, gyroscope and magnetometer.
437      * @param hardIron       known hard-iron.
438      * @param initialMm      initial soft-iron matrix containing scale factors
439      *                       and cross coupling errors.
440      * @throws IllegalArgumentException if provided hard-iron matrix is not
441      *                                  3x1 or if soft-iron matrix is not
442      *                                  3x3.
443      */
444     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
445             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
446             final Matrix hardIron, final Matrix initialMm) {
447         super(measurements, commonAxisUsed, hardIron, initialMm);
448     }
449 
450     /**
451      * Constructor.
452      *
453      * @param measurements   collection of body magnetic flux density
454      *                       measurements with standard deviation of
455      *                       magnetometer measurements taken at the same
456      *                       position with zero velocity and unknown different
457      *                       orientations.
458      * @param commonAxisUsed indicates whether z-axis is assumed to be common
459      *                       for the accelerometer, gyroscope and magnetometer.
460      * @param hardIron       known hard-iron.
461      * @param initialMm      initial soft-iron matrix containing scale factors
462      *                       and cross coupling errors.
463      * @param listener       listener to handle events raised by this calibrator.
464      * @throws IllegalArgumentException if provided hard-iron matrix is not
465      *                                  3x1 or if soft-iron matrix is not
466      *                                  3x3.
467      */
468     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
469             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
470             final Matrix hardIron, final Matrix initialMm,
471             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
472         super(measurements, commonAxisUsed, hardIron, initialMm, listener);
473     }
474 
475     /**
476      * Constructor.
477      *
478      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
479      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
480      */
481     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
482             final Double groundTruthMagneticFluxDensityNorm) {
483         super(groundTruthMagneticFluxDensityNorm);
484     }
485 
486     /**
487      * Constructor.
488      *
489      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
490      * @param listener                           listener to handle events raised by this calibrator.
491      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
492      */
493     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
494             final Double groundTruthMagneticFluxDensityNorm,
495             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
496         super(groundTruthMagneticFluxDensityNorm, listener);
497     }
498 
499     /**
500      * Constructor.
501      *
502      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
503      * @param measurements                       collection of body magnetic flux density
504      *                                           measurements with standard deviation of
505      *                                           magnetometer measurements taken at the same
506      *                                           position with zero velocity and unknown different
507      *                                           orientations.
508      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
509      */
510     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
511             final Double groundTruthMagneticFluxDensityNorm,
512             final List<StandardDeviationBodyMagneticFluxDensity> measurements) {
513         super(groundTruthMagneticFluxDensityNorm, measurements);
514     }
515 
516     /**
517      * Constructor.
518      *
519      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
520      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
521      *                                           for the accelerometer, gyroscope and magnetometer.
522      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
523      */
524     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
525             final Double groundTruthMagneticFluxDensityNorm, final boolean commonAxisUsed) {
526         super(groundTruthMagneticFluxDensityNorm, commonAxisUsed);
527     }
528 
529     /**
530      * Constructor.
531      *
532      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
533      * @param hardIron                           known hard-iron.
534      * @throws IllegalArgumentException if provided magnetic flux norm value is
535      *                                  negative, or if provided hard-iron array does
536      *                                  not have length 3.
537      */
538     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
539             final Double groundTruthMagneticFluxDensityNorm, final double[] hardIron) {
540         super(groundTruthMagneticFluxDensityNorm, hardIron);
541     }
542 
543     /**
544      * Constructor.
545      *
546      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
547      * @param hardIron                           known hard-iron.
548      * @throws IllegalArgumentException if provided magnetic flux norm value is
549      *                                  negative, or if provided hard-iron matrix is not
550      *                                  3x1.
551      */
552     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
553             final Double groundTruthMagneticFluxDensityNorm, final Matrix hardIron) {
554         super(groundTruthMagneticFluxDensityNorm, hardIron);
555     }
556 
557     /**
558      * Constructor.
559      *
560      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
561      * @param hardIron                           known hard-iron.
562      * @param initialMm                          initial soft-iron matrix containing scale factors
563      *                                           and cross coupling errors.
564      * @throws IllegalArgumentException if provided magnetic flux norm value is
565      *                                  negative, or if provided hard-iron matrix is not
566      *                                  3x1 or if soft-iron matrix is not
567      *                                  3x3.
568      */
569     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
570             final Double groundTruthMagneticFluxDensityNorm, final Matrix hardIron, final Matrix initialMm) {
571         super(groundTruthMagneticFluxDensityNorm, hardIron, initialMm);
572     }
573 
574     /**
575      * Constructor.
576      *
577      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
578      * @param measurements                       collection of body magnetic flux density
579      *                                           measurements with standard deviation of
580      *                                           magnetometer measurements taken at the same
581      *                                           position with zero velocity and unknown different
582      *                                           orientations.
583      * @param listener                           listener to handle events raised by this calibrator.
584      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
585      */
586     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
587             final Double groundTruthMagneticFluxDensityNorm,
588             final List<StandardDeviationBodyMagneticFluxDensity> measurements,
589             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
590         super(groundTruthMagneticFluxDensityNorm, measurements, listener);
591     }
592 
593     /**
594      * Constructor.
595      *
596      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
597      * @param measurements                       collection of body magnetic flux density
598      *                                           measurements with standard deviation of
599      *                                           magnetometer measurements taken at the same
600      *                                           position with zero velocity and unknown different
601      *                                           orientations.
602      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
603      *                                           for the accelerometer, gyroscope and magnetometer.
604      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
605      */
606     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
607             final Double groundTruthMagneticFluxDensityNorm,
608             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed) {
609         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed);
610     }
611 
612     /**
613      * Constructor.
614      *
615      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
616      * @param measurements                       collection of body magnetic flux density
617      *                                           measurements with standard deviation of
618      *                                           magnetometer measurements taken at the same
619      *                                           position with zero velocity and unknown different
620      *                                           orientations.
621      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
622      *                                           for the accelerometer, gyroscope and magnetometer.
623      * @param listener                           listener to handle events raised by this calibrator.
624      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
625      */
626     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
627             final Double groundTruthMagneticFluxDensityNorm,
628             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
629             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
630         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, listener);
631     }
632 
633     /**
634      * Constructor.
635      *
636      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
637      * @param measurements                       collection of body magnetic flux density
638      *                                           measurements with standard deviation of
639      *                                           magnetometer measurements taken at the same
640      *                                           position with zero velocity and unknown different
641      *                                           orientations.
642      * @param hardIron                           known hard-iron.
643      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
644      *                                  or if provided hard-iron array does not have length 3.
645      */
646     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
647             final Double groundTruthMagneticFluxDensityNorm,
648             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] hardIron) {
649         super(groundTruthMagneticFluxDensityNorm, measurements, hardIron);
650     }
651 
652     /**
653      * Constructor.
654      *
655      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
656      * @param measurements                       collection of body magnetic flux density
657      *                                           measurements with standard deviation of
658      *                                           magnetometer measurements taken at the same
659      *                                           position with zero velocity and unknown different
660      *                                           orientations.
661      * @param hardIron                           known hard-iron.
662      * @param listener                           listener to handle events raised by this calibrator.
663      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
664      *                                  or if provided hard-iron array does not have length 3.
665      */
666     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
667             final Double groundTruthMagneticFluxDensityNorm,
668             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] hardIron,
669             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
670         super(groundTruthMagneticFluxDensityNorm, measurements, hardIron, listener);
671     }
672 
673     /**
674      * Constructor.
675      *
676      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
677      * @param measurements                       collection of body magnetic flux density
678      *                                           measurements with standard deviation of
679      *                                           magnetometer measurements taken at the same
680      *                                           position with zero velocity and unknown different
681      *                                           orientations.
682      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
683      *                                           for the accelerometer, gyroscope and magnetometer.
684      * @param hardIron                           known hard-iron.
685      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
686      *                                  or if provided hard-iron array does not have length 3.
687      */
688     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
689             final Double groundTruthMagneticFluxDensityNorm,
690             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
691             final double[] hardIron) {
692         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, hardIron);
693     }
694 
695     /**
696      * Constructor.
697      *
698      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
699      * @param measurements                       collection of body magnetic flux density
700      *                                           measurements with standard deviation of
701      *                                           magnetometer measurements taken at the same
702      *                                           position with zero velocity and unknown different
703      *                                           orientations.
704      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
705      *                                           for the accelerometer, gyroscope and magnetometer.
706      * @param hardIron                           known hard-iron.
707      * @param listener                           listener to handle events raised by this calibrator.
708      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
709      *                                  or if provided hard-iron array does not have length 3.
710      */
711     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
712             final Double groundTruthMagneticFluxDensityNorm,
713             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
714             final double[] hardIron,
715             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
716         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, hardIron, listener);
717     }
718 
719     /**
720      * Constructor.
721      *
722      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
723      * @param measurements                       collection of body magnetic flux density
724      *                                           measurements with standard deviation of
725      *                                           magnetometer measurements taken at the same
726      *                                           position with zero velocity and unknown different
727      *                                           orientations.
728      * @param hardIron                           known hard-iron.
729      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
730      *                                  or if provided hard-iron matrix is not 3x1.
731      */
732     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
733             final Double groundTruthMagneticFluxDensityNorm,
734             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron) {
735         super(groundTruthMagneticFluxDensityNorm, measurements, hardIron);
736     }
737 
738     /**
739      * Constructor.
740      *
741      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
742      * @param measurements                       collection of body magnetic flux density
743      *                                           measurements with standard deviation of
744      *                                           magnetometer measurements taken at the same
745      *                                           position with zero velocity and unknown different
746      *                                           orientations.
747      * @param hardIron                           known hard-iron.
748      * @param listener                           listener to handle events raised by this calibrator.
749      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
750      *                                  or if provided hard-iron matrix is not 3x1.
751      */
752     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
753             final Double groundTruthMagneticFluxDensityNorm,
754             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
755             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
756         super(groundTruthMagneticFluxDensityNorm, measurements, hardIron, listener);
757     }
758 
759     /**
760      * Constructor.
761      *
762      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
763      * @param measurements                       collection of body magnetic flux density
764      *                                           measurements with standard deviation of
765      *                                           magnetometer measurements taken at the same
766      *                                           position with zero velocity and unknown different
767      *                                           orientations.
768      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
769      *                                           for the accelerometer, gyroscope and magnetometer.
770      * @param hardIron                           known hard-iron.
771      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
772      *                                  or if provided hard-iron matrix is not 3x1.
773      */
774     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
775             final Double groundTruthMagneticFluxDensityNorm,
776             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
777             final Matrix hardIron) {
778         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, hardIron);
779     }
780 
781     /**
782      * Constructor.
783      *
784      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
785      * @param measurements                       collection of body magnetic flux density
786      *                                           measurements with standard deviation of
787      *                                           magnetometer measurements taken at the same
788      *                                           position with zero velocity and unknown different
789      *                                           orientations.
790      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
791      *                                           for the accelerometer, gyroscope and magnetometer.
792      * @param hardIron                           known hard-iron.
793      * @param listener                           listener to handle events raised by this calibrator.
794      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
795      *                                  or if provided hard-iron matrix is not 3x1.
796      */
797     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
798             final Double groundTruthMagneticFluxDensityNorm,
799             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
800             final Matrix hardIron,
801             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
802         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, hardIron, listener);
803     }
804 
805     /**
806      * Constructor.
807      *
808      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
809      * @param measurements                       collection of body magnetic flux density
810      *                                           measurements with standard deviation of
811      *                                           magnetometer measurements taken at the same
812      *                                           position with zero velocity and unknown different
813      *                                           orientations.
814      * @param hardIron                           known hard-iron.
815      * @param initialMm                          initial soft-iron matrix containing scale factors
816      *                                           and cross coupling errors.
817      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
818      *                                  or if provided hard-iron matrix is not 3x1 or if
819      *                                  soft-iron matrix is not 3x3.
820      */
821     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
822             final Double groundTruthMagneticFluxDensityNorm,
823             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
824             final Matrix initialMm) {
825         super(groundTruthMagneticFluxDensityNorm, measurements, hardIron, initialMm);
826     }
827 
828     /**
829      * Constructor.
830      *
831      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
832      * @param measurements                       collection of body magnetic flux density
833      *                                           measurements with standard deviation of
834      *                                           magnetometer measurements taken at the same
835      *                                           position with zero velocity and unknown different
836      *                                           orientations.
837      * @param hardIron                           known hard-iron.
838      * @param initialMm                          initial soft-iron matrix containing scale factors
839      *                                           and cross coupling errors.
840      * @param listener                           listener to handle events raised by this calibrator.
841      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
842      *                                  or if provided hard-iron matrix is not 3x1 or if
843      *                                  soft-iron matrix is not 3x3.
844      */
845     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
846             final Double groundTruthMagneticFluxDensityNorm,
847             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
848             final Matrix initialMm,
849             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
850         super(groundTruthMagneticFluxDensityNorm, measurements, hardIron, initialMm, listener);
851     }
852 
853     /**
854      * Constructor.
855      *
856      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
857      * @param measurements                       collection of body magnetic flux density
858      *                                           measurements with standard deviation of
859      *                                           magnetometer measurements taken at the same
860      *                                           position with zero velocity and unknown different
861      *                                           orientations.
862      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
863      *                                           for the accelerometer, gyroscope and magnetometer.
864      * @param hardIron                           known hard-iron.
865      * @param initialMm                          initial soft-iron matrix containing scale factors
866      *                                           and cross coupling errors.
867      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
868      *                                  or if provided hard-iron matrix is not 3x1
869      *                                  or if soft-iron matrix is not 3x3.
870      */
871     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
872             final Double groundTruthMagneticFluxDensityNorm,
873             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
874             final Matrix hardIron, final Matrix initialMm) {
875         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, hardIron, initialMm);
876     }
877 
878     /**
879      * Constructor.
880      *
881      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
882      * @param measurements                       collection of body magnetic flux density
883      *                                           measurements with standard deviation of
884      *                                           magnetometer measurements taken at the same
885      *                                           position with zero velocity and unknown different
886      *                                           orientations.
887      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
888      *                                           for the accelerometer, gyroscope and magnetometer.
889      * @param hardIron                           known hard-iron.
890      * @param initialMm                          initial soft-iron matrix containing scale factors
891      *                                           and cross coupling errors.
892      * @param listener                           listener to handle events raised by this calibrator.
893      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
894      *                                  or if provided hard-iron matrix is not 3x1
895      *                                  or if soft-iron matrix is not 3x3.
896      */
897     public RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
898             final Double groundTruthMagneticFluxDensityNorm,
899             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
900             final Matrix hardIron, final Matrix initialMm,
901             final RobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
902         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, hardIron, initialMm, listener);
903     }
904 
905     /**
906      * Gets threshold to determine whether samples are inliers or not when testing possible solutions.
907      * The threshold refers to the amount of error on norm between measured specific forces and the
908      * ones generated with estimated calibration parameters provided for each sample.
909      *
910      * @return threshold to determine whether samples are inliers or not.
911      */
912     public double getThreshold() {
913         return threshold;
914     }
915 
916     /**
917      * Sets threshold to determine whether samples are inliers or not when testing possible solutions.
918      * The threshold refers to the amount of error on norm between measured specific forces and the
919      * ones generated with estimated calibration parameters provided for each sample.
920      *
921      * @param threshold threshold to determine whether samples are inliers or not.
922      * @throws IllegalArgumentException if provided value is equal or less than zero.
923      * @throws LockedException          if calibrator is currently running.
924      */
925     public void setThreshold(final double threshold) throws LockedException {
926         if (running) {
927             throw new LockedException();
928         }
929         if (threshold <= MIN_THRESHOLD) {
930             throw new IllegalArgumentException();
931         }
932         this.threshold = threshold;
933     }
934 
935     /**
936      * Indicates whether inliers must be computed and kept.
937      *
938      * @return true if inliers must be computed and kept, false if inliers
939      * only need to be computed but not kept.
940      */
941     public boolean isComputeAndKeepInliersEnabled() {
942         return computeAndKeepInliers;
943     }
944 
945     /**
946      * Specifies whether inliers must be computed and kept.
947      *
948      * @param computeAndKeepInliers true if inliers must be computed and kept,
949      *                              false if inliers only need to be computed but not kept.
950      * @throws LockedException if calibrator is currently running.
951      */
952     public void setComputeAndKeepInliersEnabled(final boolean computeAndKeepInliers) throws LockedException {
953         if (running) {
954             throw new LockedException();
955         }
956         this.computeAndKeepInliers = computeAndKeepInliers;
957     }
958 
959     /**
960      * Indicates whether residuals must be computed and kept.
961      *
962      * @return true if residuals must be computed and kept, false if residuals
963      * only need to be computed but not kept.
964      */
965     public boolean isComputeAndKeepResiduals() {
966         return computeAndKeepResiduals;
967     }
968 
969     /**
970      * Specifies whether residuals must be computed and kept.
971      *
972      * @param computeAndKeepResiduals true if residuals must be computed and kept,
973      *                                false if residuals only need to be computed but not kept.
974      * @throws LockedException if calibrator is currently running.
975      */
976     public void setComputeAndKeepResidualsEnabled(final boolean computeAndKeepResiduals) throws LockedException {
977         if (running) {
978             throw new LockedException();
979         }
980         this.computeAndKeepResiduals = computeAndKeepResiduals;
981     }
982 
983     /**
984      * Estimates magnetometer calibration parameters containing hard-iron
985      * bias and soft-iron scale factors and cross-coupling errors.
986      *
987      * @throws LockedException      if calibrator is currently running.
988      * @throws NotReadyException    if calibrator is not ready.
989      * @throws CalibrationException if estimation fails for numerical reasons.
990      */
991     @SuppressWarnings("DuplicatedCode")
992     @Override
993     public void calibrate() throws LockedException, NotReadyException, CalibrationException {
994         if (running) {
995             throw new LockedException();
996         }
997         if (!isReady()) {
998             throw new NotReadyException();
999         }
1000 
1001         final var innerEstimator = new RANSACRobustEstimator<>(new RANSACRobustEstimatorListener<PreliminaryResult>() {
1002             @Override
1003             public double getThreshold() {
1004                 return threshold;
1005             }
1006 
1007             @Override
1008             public int getTotalSamples() {
1009                 return measurements.size();
1010             }
1011 
1012             @Override
1013             public int getSubsetSize() {
1014                 return preliminarySubsetSize;
1015             }
1016 
1017             @Override
1018             public void estimatePreliminarSolutions(
1019                     final int[] samplesIndices, final List<PreliminaryResult> solutions) {
1020                 computePreliminarySolutions(samplesIndices, solutions);
1021             }
1022 
1023             @Override
1024             public double computeResidual(final PreliminaryResult currentEstimation, final int i) {
1025                 return computeError(measurements.get(i), currentEstimation);
1026             }
1027 
1028             @Override
1029             public boolean isReady() {
1030                 return RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator.super.isReady();
1031             }
1032 
1033             @Override
1034             public void onEstimateStart(final RobustEstimator<PreliminaryResult> estimator) {
1035                 // no action needed
1036             }
1037 
1038             @Override
1039             public void onEstimateEnd(final RobustEstimator<PreliminaryResult> estimator) {
1040                 // no action needed
1041             }
1042 
1043             @Override
1044             public void onEstimateNextIteration(
1045                     final RobustEstimator<PreliminaryResult> estimator, final int iteration) {
1046                 if (listener != null) {
1047                     listener.onCalibrateNextIteration(
1048                             RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator.this,
1049                             iteration);
1050                 }
1051             }
1052 
1053             @Override
1054             public void onEstimateProgressChange(
1055                     final RobustEstimator<PreliminaryResult> estimator, final float progress) {
1056                 if (listener != null) {
1057                     listener.onCalibrateProgressChange(
1058                             RANSACRobustKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator.this,
1059                             progress);
1060                 }
1061             }
1062         });
1063 
1064         try {
1065             running = true;
1066 
1067             if (listener != null) {
1068                 listener.onCalibrateStart(this);
1069             }
1070 
1071             inliersData = null;
1072 
1073             innerEstimator.setComputeAndKeepInliersEnabled(computeAndKeepInliers || refineResult);
1074             innerEstimator.setComputeAndKeepResidualsEnabled(computeAndKeepResiduals || refineResult);
1075             innerEstimator.setConfidence(confidence);
1076             innerEstimator.setMaxIterations(maxIterations);
1077             innerEstimator.setProgressDelta(progressDelta);
1078             final var preliminaryResult = innerEstimator.estimate();
1079             inliersData = innerEstimator.getInliersData();
1080 
1081             attemptRefine(preliminaryResult);
1082 
1083             if (listener != null) {
1084                 listener.onCalibrateEnd(this);
1085             }
1086 
1087         } catch (final com.irurueta.numerical.LockedException e) {
1088             throw new LockedException(e);
1089         } catch (final com.irurueta.numerical.NotReadyException e) {
1090             throw new NotReadyException(e);
1091         } catch (final RobustEstimatorException e) {
1092             throw new CalibrationException(e);
1093         } finally {
1094             running = false;
1095         }
1096     }
1097 
1098     /**
1099      * Returns method being used for robust estimation.
1100      *
1101      * @return method being used for robust estimation.
1102      */
1103     @Override
1104     public RobustEstimatorMethod getMethod() {
1105         return RobustEstimatorMethod.RANSAC;
1106     }
1107 
1108     /**
1109      * Indicates whether this calibrator requires quality scores for each
1110      * measurement or not.
1111      *
1112      * @return true if quality scores are required, false otherwise.
1113      */
1114     @Override
1115     public boolean isQualityScoresRequired() {
1116         return false;
1117     }
1118 }