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.MSACRobustEstimator;
24  import com.irurueta.numerical.robust.MSACRobustEstimatorListener;
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 MSAC 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 MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator extends
59          RobustKnownMagneticFluxDensityNormMagnetometerCalibrator {
60      /**
61       * Constant defining default threshold to determine whether samples are
62       * 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       * Threshold to determine whether samples are inliers or not when
74       * testing possible estimation solutions.
75       */
76      private double threshold = DEFAULT_THRESHOLD;
77  
78      /**
79       * Constructor.
80       */
81      public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator() {
82          super();
83      }
84  
85      /**
86       * Constructor.
87       *
88       * @param listener listener to handle events raised by this calibrator.
89       */
90      public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
91              final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
92          super(listener);
93      }
94  
95      /**
96       * Constructor.
97       *
98       * @param measurements list of body magnetic flux density
99       *                     measurements with standard deviation of
100      *                     magnetometer measurements taken at the same
101      *                     position with zero velocity and unknown different
102      *                     orientations.
103      */
104     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
105             final List<StandardDeviationBodyMagneticFluxDensity> measurements) {
106         super(measurements);
107     }
108 
109     /**
110      * Constructor.
111      *
112      * @param commonAxisUsed indicates whether z-axis is assumed to be common
113      *                       for the accelerometer, gyroscope and magnetometer.
114      */
115     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(final boolean commonAxisUsed) {
116         super(commonAxisUsed);
117     }
118 
119     /**
120      * Constructor.
121      *
122      * @param initialHardIron initial hard-iron to find a solution.
123      * @throws IllegalArgumentException if provided hard-iron array does
124      *                                  not have length 3.
125      */
126     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(final double[] initialHardIron) {
127         super(initialHardIron);
128     }
129 
130     /**
131      * Constructor.
132      *
133      * @param initialHardIron initial hard-iron to find a solution.
134      * @throws IllegalArgumentException if provided hard-iron matrix is not
135      *                                  3x1.
136      */
137     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(final Matrix initialHardIron) {
138         super(initialHardIron);
139     }
140 
141     /**
142      * Constructor.
143      *
144      * @param initialHardIron initial hard-iron to find a solution.
145      * @param initialMm       initial soft-iron matrix containing scale factors
146      *                        and cross coupling errors.
147      * @throws IllegalArgumentException if provided hard-iron matrix is not
148      *                                  3x1 or if soft-iron matrix is not
149      *                                  3x3.
150      */
151     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
152             final Matrix initialHardIron, final Matrix initialMm) {
153         super(initialHardIron, initialMm);
154     }
155 
156     /**
157      * Constructor.
158      *
159      * @param measurements list of body magnetic flux density
160      *                     measurements with standard deviation of
161      *                     magnetometer measurements taken at the same
162      *                     position with zero velocity and unknown different
163      *                     orientations.
164      * @param listener     listener to handle events raised by this calibrator.
165      */
166     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
167             final List<StandardDeviationBodyMagneticFluxDensity> measurements,
168             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
169         super(measurements, listener);
170     }
171 
172     /**
173      * Constructor.
174      *
175      * @param measurements   collection of body magnetic flux density
176      *                       measurements with standard deviation of
177      *                       magnetometer measurements taken at the same
178      *                       position with zero velocity and unknown different
179      *                       orientations.
180      * @param commonAxisUsed indicates whether z-axis is assumed to be common
181      *                       for the accelerometer, gyroscope and magnetometer.
182      */
183     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
184             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed) {
185         super(measurements, commonAxisUsed);
186     }
187 
188     /**
189      * Constructor.
190      *
191      * @param measurements   collection of body magnetic flux density
192      *                       measurements with standard deviation of
193      *                       magnetometer measurements taken at the same
194      *                       position with zero velocity and unknown different
195      *                       orientations.
196      * @param commonAxisUsed indicates whether z-axis is assumed to be common
197      *                       for the accelerometer, gyroscope and magnetometer.
198      * @param listener       listener to handle events raised by this calibrator.
199      */
200     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
201             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
202             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
203         super(measurements, commonAxisUsed, listener);
204     }
205 
206     /**
207      * Constructor.
208      *
209      * @param measurements    collection of body magnetic flux density
210      *                        measurements with standard deviation of
211      *                        magnetometer measurements taken at the same
212      *                        position with zero velocity and unknown different
213      *                        orientations.
214      * @param initialHardIron initial hard-iron to find a solution.
215      * @throws IllegalArgumentException if provided hard-iron array does
216      *                                  not have length 3.
217      */
218     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
219             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] initialHardIron) {
220         super(measurements, initialHardIron);
221     }
222 
223     /**
224      * Constructor.
225      *
226      * @param measurements    collection of body magnetic flux density
227      *                        measurements with standard deviation of
228      *                        magnetometer measurements taken at the same
229      *                        position with zero velocity and unknown different
230      *                        orientations.
231      * @param initialHardIron initial hard-iron to find a solution.
232      * @param listener        listener to handle events raised by this calibrator.
233      * @throws IllegalArgumentException if provided hard-iron array does
234      *                                  not have length 3.
235      */
236     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
237             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] initialHardIron,
238             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
239         super(measurements, initialHardIron, listener);
240     }
241 
242     /**
243      * Constructor.
244      *
245      * @param measurements    collection of body magnetic flux density
246      *                        measurements with standard deviation of
247      *                        magnetometer measurements taken at the same
248      *                        position with zero velocity and unknown different
249      *                        orientations.
250      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
251      *                        for the accelerometer, gyroscope and magnetometer.
252      * @param initialHardIron initial hard-iron to find a solution.
253      * @throws IllegalArgumentException if provided hard-iron array does
254      *                                  not have length 3.
255      */
256     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
257             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
258             final double[] initialHardIron) {
259         super(measurements, commonAxisUsed, initialHardIron);
260     }
261 
262     /**
263      * Constructor.
264      *
265      * @param measurements    collection of body magnetic flux density
266      *                        measurements with standard deviation of
267      *                        magnetometer measurements taken at the same
268      *                        position with zero velocity and unknown different
269      *                        orientations.
270      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
271      *                        for the accelerometer, gyroscope and magnetometer.
272      * @param initialHardIron initial hard-iron to find a solution.
273      * @param listener        listener to handle events raised by this calibrator.
274      * @throws IllegalArgumentException if provided hard-iron array does
275      *                                  not have length 3.
276      */
277     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
278             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
279             final double[] initialHardIron,
280             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
281         super(measurements, commonAxisUsed, initialHardIron, listener);
282     }
283 
284     /**
285      * Constructor.
286      *
287      * @param measurements    collection of body magnetic flux density
288      *                        measurements with standard deviation of
289      *                        magnetometer measurements taken at the same
290      *                        position with zero velocity and unknown different
291      *                        orientations.
292      * @param initialHardIron initial hard-iron to find a solution.
293      * @throws IllegalArgumentException if provided hard-iron matrix is not
294      *                                  3x1.
295      */
296     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
297             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron) {
298         super(measurements, initialHardIron);
299     }
300 
301     /**
302      * Constructor.
303      *
304      * @param measurements    collection of body magnetic flux density
305      *                        measurements with standard deviation of
306      *                        magnetometer measurements taken at the same
307      *                        position with zero velocity and unknown different
308      *                        orientations.
309      * @param initialHardIron initial hard-iron to find a solution.
310      * @param listener        listener to handle events raised by this calibrator.
311      * @throws IllegalArgumentException if provided hard-iron matrix is not
312      *                                  3x1.
313      */
314     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
315             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
316             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
317         super(measurements, initialHardIron, listener);
318     }
319 
320     /**
321      * Constructor.
322      *
323      * @param measurements    collection of body magnetic flux density
324      *                        measurements with standard deviation of
325      *                        magnetometer measurements taken at the same
326      *                        position with zero velocity and unknown different
327      *                        orientations.
328      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
329      *                        for the accelerometer, gyroscope and magnetometer.
330      * @param initialHardIron initial hard-iron to find a solution.
331      * @throws IllegalArgumentException if provided hard-iron matrix is not
332      *                                  3x1.
333      */
334     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
335             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
336             final Matrix initialHardIron) {
337         super(measurements, commonAxisUsed, initialHardIron);
338     }
339 
340     /**
341      * Constructor.
342      *
343      * @param measurements    collection of body magnetic flux density
344      *                        measurements with standard deviation of
345      *                        magnetometer measurements taken at the same
346      *                        position with zero velocity and unknown different
347      *                        orientations.
348      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
349      *                        for the accelerometer, gyroscope and magnetometer.
350      * @param initialHardIron initial hard-iron to find a solution.
351      * @param listener        listener to handle events raised by this calibrator.
352      * @throws IllegalArgumentException if provided hard-iron matrix is not
353      *                                  3x1.
354      */
355     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
356             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
357             final Matrix initialHardIron,
358             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
359         super(measurements, commonAxisUsed, initialHardIron, listener);
360     }
361 
362     /**
363      * Constructor.
364      *
365      * @param measurements    collection of body magnetic flux density
366      *                        measurements with standard deviation of
367      *                        magnetometer measurements taken at the same
368      *                        position with zero velocity and unknown different
369      *                        orientations.
370      * @param initialHardIron initial hard-iron to find a solution.
371      * @param initialMm       initial soft-iron matrix containing scale factors
372      *                        and cross coupling errors.
373      * @throws IllegalArgumentException if provided hard-iron matrix is not
374      *                                  3x1 or if soft-iron matrix is not
375      *                                  3x3.
376      */
377     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
378             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
379             final Matrix initialMm) {
380         super(measurements, initialHardIron, initialMm);
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      * @param listener        listener to handle events raised by this calibrator.
395      * @throws IllegalArgumentException if provided hard-iron matrix is not
396      *                                  3x1 or if soft-iron matrix is not
397      *                                  3x3.
398      */
399     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
400             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
401             final Matrix initialMm, final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
402         super(measurements, initialHardIron, initialMm, listener);
403     }
404 
405     /**
406      * Constructor.
407      *
408      * @param measurements    collection of body magnetic flux density
409      *                        measurements with standard deviation of
410      *                        magnetometer measurements taken at the same
411      *                        position with zero velocity and unknown different
412      *                        orientations.
413      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
414      *                        for the accelerometer, gyroscope and magnetometer.
415      * @param initialHardIron initial hard-iron to find a solution.
416      * @param initialMm       initial soft-iron matrix containing scale factors
417      *                        and cross coupling errors.
418      * @throws IllegalArgumentException if provided hard-iron matrix is not
419      *                                  3x1 or if soft-iron matrix is not
420      *                                  3x3.
421      */
422     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
423             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
424             final Matrix initialHardIron, final Matrix initialMm) {
425         super(measurements, commonAxisUsed, initialHardIron, initialMm);
426     }
427 
428     /**
429      * Constructor.
430      *
431      * @param measurements    collection of body magnetic flux density
432      *                        measurements with standard deviation of
433      *                        magnetometer measurements taken at the same
434      *                        position with zero velocity and unknown different
435      *                        orientations.
436      * @param commonAxisUsed  indicates whether z-axis is assumed to be common
437      *                        for the accelerometer, gyroscope and magnetometer.
438      * @param initialHardIron initial hard-iron to find a solution.
439      * @param initialMm       initial soft-iron matrix containing scale factors
440      *                        and cross coupling errors.
441      * @param listener        listener to handle events raised by this calibrator.
442      * @throws IllegalArgumentException if provided hard-iron matrix is not
443      *                                  3x1 or if soft-iron matrix is not
444      *                                  3x3.
445      */
446     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
447             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
448             final Matrix initialHardIron, final Matrix initialMm,
449             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
450         super(measurements, commonAxisUsed, initialHardIron, initialMm, listener);
451     }
452 
453     /**
454      * Constructor.
455      *
456      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
457      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
458      */
459     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
460             final Double groundTruthMagneticFluxDensityNorm) {
461         super(groundTruthMagneticFluxDensityNorm);
462     }
463 
464     /**
465      * Constructor.
466      *
467      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
468      * @param listener                           listener to handle events raised by this calibrator.
469      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
470      */
471     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
472             final Double groundTruthMagneticFluxDensityNorm,
473             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
474         super(groundTruthMagneticFluxDensityNorm, listener);
475     }
476 
477     /**
478      * Constructor.
479      *
480      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
481      * @param measurements                       collection of body magnetic flux density
482      *                                           measurements with standard deviation of
483      *                                           magnetometer measurements taken at the same
484      *                                           position with zero velocity and unknown different
485      *                                           orientations.
486      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
487      */
488     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
489             final Double groundTruthMagneticFluxDensityNorm,
490             final List<StandardDeviationBodyMagneticFluxDensity> measurements) {
491         super(groundTruthMagneticFluxDensityNorm, measurements);
492     }
493 
494     /**
495      * Constructor.
496      *
497      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
498      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
499      *                                           for the accelerometer, gyroscope and magnetometer.
500      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
501      */
502     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
503             final Double groundTruthMagneticFluxDensityNorm, final boolean commonAxisUsed) {
504         super(groundTruthMagneticFluxDensityNorm, commonAxisUsed);
505     }
506 
507     /**
508      * Constructor.
509      *
510      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
511      * @param initialHardIron                    initial hard-iron to find a solution.
512      * @throws IllegalArgumentException if provided magnetic flux norm value is
513      *                                  negative, or if provided hard-iron array does
514      *                                  not have length 3.
515      */
516     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
517             final Double groundTruthMagneticFluxDensityNorm, final double[] initialHardIron) {
518         super(groundTruthMagneticFluxDensityNorm, initialHardIron);
519     }
520 
521     /**
522      * Constructor.
523      *
524      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
525      * @param initialHardIron                    initial hard-iron to find a solution.
526      * @throws IllegalArgumentException if provided magnetic flux norm value is
527      *                                  negative, or if provided hard-iron matrix is not
528      *                                  3x1.
529      */
530     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
531             final Double groundTruthMagneticFluxDensityNorm, final Matrix initialHardIron) {
532         super(groundTruthMagneticFluxDensityNorm, initialHardIron);
533     }
534 
535     /**
536      * Constructor.
537      *
538      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
539      * @param initialHardIron                    initial hard-iron to find a solution.
540      * @param initialMm                          initial soft-iron matrix containing scale factors
541      *                                           and cross coupling errors.
542      * @throws IllegalArgumentException if provided magnetic flux norm value is
543      *                                  negative, or if provided hard-iron matrix is not
544      *                                  3x1 or if soft-iron matrix is not
545      *                                  3x3.
546      */
547     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
548             final Double groundTruthMagneticFluxDensityNorm, final Matrix initialHardIron, final Matrix initialMm) {
549         super(groundTruthMagneticFluxDensityNorm, initialHardIron, initialMm);
550     }
551 
552     /**
553      * Constructor.
554      *
555      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
556      * @param measurements                       collection of body magnetic flux density
557      *                                           measurements with standard deviation of
558      *                                           magnetometer measurements taken at the same
559      *                                           position with zero velocity and unknown different
560      *                                           orientations.
561      * @param listener                           listener to handle events raised by this calibrator.
562      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
563      */
564     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
565             final Double groundTruthMagneticFluxDensityNorm,
566             final List<StandardDeviationBodyMagneticFluxDensity> measurements,
567             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
568         super(groundTruthMagneticFluxDensityNorm, measurements, listener);
569     }
570 
571     /**
572      * Constructor.
573      *
574      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
575      * @param measurements                       collection of body magnetic flux density
576      *                                           measurements with standard deviation of
577      *                                           magnetometer measurements taken at the same
578      *                                           position with zero velocity and unknown different
579      *                                           orientations.
580      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
581      *                                           for the accelerometer, gyroscope and magnetometer.
582      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
583      */
584     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
585             final Double groundTruthMagneticFluxDensityNorm,
586             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed) {
587         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed);
588     }
589 
590     /**
591      * Constructor.
592      *
593      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
594      * @param measurements                       collection of body magnetic flux density
595      *                                           measurements with standard deviation of
596      *                                           magnetometer measurements taken at the same
597      *                                           position with zero velocity and unknown different
598      *                                           orientations.
599      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
600      *                                           for the accelerometer, gyroscope and magnetometer.
601      * @param listener                           listener to handle events raised by this calibrator.
602      * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
603      */
604     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
605             final Double groundTruthMagneticFluxDensityNorm,
606             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
607             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
608         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, listener);
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 initialHardIron                    initial hard-iron to find a solution.
621      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
622      *                                  or if provided hard-iron array does not have length 3.
623      */
624     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
625             final Double groundTruthMagneticFluxDensityNorm,
626             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] initialHardIron) {
627         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron);
628     }
629 
630     /**
631      * Constructor.
632      *
633      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
634      * @param measurements                       collection of body magnetic flux density
635      *                                           measurements with standard deviation of
636      *                                           magnetometer measurements taken at the same
637      *                                           position with zero velocity and unknown different
638      *                                           orientations.
639      * @param initialHardIron                    initial hard-iron to find a solution.
640      * @param listener                           listener to handle events raised by this calibrator.
641      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
642      *                                  or if provided hard-iron array does not have length 3.
643      */
644     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
645             final Double groundTruthMagneticFluxDensityNorm,
646             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] initialHardIron,
647             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
648         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron, listener);
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 commonAxisUsed                     indicates whether z-axis is assumed to be common
661      *                                           for the accelerometer, gyroscope and magnetometer.
662      * @param initialHardIron                    initial hard-iron to find a solution.
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 MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
667             final Double groundTruthMagneticFluxDensityNorm,
668             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
669             final double[] initialHardIron) {
670         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron);
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 initialHardIron                    initial hard-iron to find a solution.
685      * @param listener                           listener to handle events raised by this calibrator.
686      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
687      *                                  or if provided hard-iron array does not have length 3.
688      */
689     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
690             final Double groundTruthMagneticFluxDensityNorm,
691             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
692             final double[] initialHardIron,
693             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
694         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron, listener);
695     }
696 
697     /**
698      * Constructor.
699      *
700      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
701      * @param measurements                       collection of body magnetic flux density
702      *                                           measurements with standard deviation of
703      *                                           magnetometer measurements taken at the same
704      *                                           position with zero velocity and unknown different
705      *                                           orientations.
706      * @param initialHardIron                    initial hard-iron to find a solution.
707      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
708      *                                  or if provided hard-iron matrix is not 3x1.
709      */
710     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
711             final Double groundTruthMagneticFluxDensityNorm,
712             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron) {
713         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron);
714     }
715 
716     /**
717      * Constructor.
718      *
719      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
720      * @param measurements                       collection of body magnetic flux density
721      *                                           measurements with standard deviation of
722      *                                           magnetometer measurements taken at the same
723      *                                           position with zero velocity and unknown different
724      *                                           orientations.
725      * @param initialHardIron                    initial hard-iron to find a solution.
726      * @param listener                           listener to handle events raised by this calibrator.
727      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
728      *                                  or if provided hard-iron matrix is not 3x1.
729      */
730     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
731             final Double groundTruthMagneticFluxDensityNorm,
732             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
733             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
734         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron, listener);
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 commonAxisUsed                     indicates whether z-axis is assumed to be common
747      *                                           for the accelerometer, gyroscope and magnetometer.
748      * @param initialHardIron                    initial hard-iron to find a solution.
749      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
750      *                                  or if provided hard-iron matrix is not 3x1.
751      */
752     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
753             final Double groundTruthMagneticFluxDensityNorm,
754             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
755             final Matrix initialHardIron) {
756         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron);
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 initialHardIron                    initial hard-iron to find a solution.
771      * @param listener                           listener to handle events raised by this calibrator.
772      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
773      *                                  or if provided hard-iron matrix is not 3x1.
774      */
775     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
776             final Double groundTruthMagneticFluxDensityNorm,
777             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
778             final Matrix initialHardIron,
779             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
780         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron, listener);
781     }
782 
783     /**
784      * Constructor.
785      *
786      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
787      * @param measurements                       collection of body magnetic flux density
788      *                                           measurements with standard deviation of
789      *                                           magnetometer measurements taken at the same
790      *                                           position with zero velocity and unknown different
791      *                                           orientations.
792      * @param initialHardIron                    initial hard-iron to find a solution.
793      * @param initialMm                          initial soft-iron matrix containing scale factors
794      *                                           and cross coupling errors.
795      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
796      *                                  or if provided hard-iron matrix is not 3x1 or if
797      *                                  soft-iron matrix is not 3x3.
798      */
799     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
800             final Double groundTruthMagneticFluxDensityNorm,
801             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
802             final Matrix initialMm) {
803         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron, initialMm);
804     }
805 
806     /**
807      * Constructor.
808      *
809      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
810      * @param measurements                       collection of body magnetic flux density
811      *                                           measurements with standard deviation of
812      *                                           magnetometer measurements taken at the same
813      *                                           position with zero velocity and unknown different
814      *                                           orientations.
815      * @param initialHardIron                    initial hard-iron to find a solution.
816      * @param initialMm                          initial soft-iron matrix containing scale factors
817      *                                           and cross coupling errors.
818      * @param listener                           listener to handle events raised by this calibrator.
819      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
820      *                                  or if provided hard-iron matrix is not 3x1 or if
821      *                                  soft-iron matrix is not 3x3.
822      */
823     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
824             final Double groundTruthMagneticFluxDensityNorm,
825             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix initialHardIron,
826             final Matrix initialMm, final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
827         super(groundTruthMagneticFluxDensityNorm, measurements, initialHardIron, initialMm, listener);
828     }
829 
830     /**
831      * Constructor.
832      *
833      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
834      * @param measurements                       collection of body magnetic flux density
835      *                                           measurements with standard deviation of
836      *                                           magnetometer measurements taken at the same
837      *                                           position with zero velocity and unknown different
838      *                                           orientations.
839      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
840      *                                           for the accelerometer, gyroscope and magnetometer.
841      * @param initialHardIron                    initial hard-iron to find a solution.
842      * @param initialMm                          initial soft-iron matrix containing scale factors
843      *                                           and cross coupling errors.
844      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
845      *                                  or if provided hard-iron matrix is not 3x1
846      *                                  or if soft-iron matrix is not 3x3.
847      */
848     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
849             final Double groundTruthMagneticFluxDensityNorm,
850             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
851             final Matrix initialHardIron, final Matrix initialMm) {
852         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron, initialMm);
853     }
854 
855     /**
856      * Constructor.
857      *
858      * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
859      * @param measurements                       collection of body magnetic flux density
860      *                                           measurements with standard deviation of
861      *                                           magnetometer measurements taken at the same
862      *                                           position with zero velocity and unknown different
863      *                                           orientations.
864      * @param commonAxisUsed                     indicates whether z-axis is assumed to be common
865      *                                           for the accelerometer, gyroscope and magnetometer.
866      * @param initialHardIron                    initial hard-iron to find a solution.
867      * @param initialMm                          initial soft-iron matrix containing scale factors
868      *                                           and cross coupling errors.
869      * @param listener                           listener to handle events raised by this calibrator.
870      * @throws IllegalArgumentException if provided magnetic flux norm value is negative,
871      *                                  or if provided hard-iron matrix is not 3x1
872      *                                  or if soft-iron matrix is not 3x3.
873      */
874     public MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator(
875             final Double groundTruthMagneticFluxDensityNorm,
876             final List<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
877             final Matrix initialHardIron, final Matrix initialMm,
878             final RobustKnownMagneticFluxDensityNormMagnetometerCalibratorListener listener) {
879         super(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, initialHardIron, initialMm, listener);
880     }
881 
882     /**
883      * Returns threshold to determine whether samples are inliers or not.
884      *
885      * @return threshold to determine whether samples are inliers or not.
886      */
887     public double getThreshold() {
888         return threshold;
889     }
890 
891     /**
892      * Sets threshold to determine whether samples are inliers or not.
893      *
894      * @param threshold threshold to be set.
895      * @throws IllegalArgumentException if provided value is equal or less than
896      *                                  zero.
897      * @throws LockedException          if calibrator is currently running.
898      */
899     public void setThreshold(final double threshold) throws LockedException {
900         if (running) {
901             throw new LockedException();
902         }
903         if (threshold <= MIN_THRESHOLD) {
904             throw new IllegalArgumentException();
905         }
906         this.threshold = threshold;
907     }
908 
909     /**
910      * Estimates magnetometer calibration parameters containing hard-iron
911      * bias and soft-iron scale factors and cross-coupling errors.
912      *
913      * @throws LockedException      if calibrator is currently running.
914      * @throws NotReadyException    if calibrator is not ready.
915      * @throws CalibrationException if estimation fails for numerical reasons.
916      */
917     @SuppressWarnings("DuplicatedCode")
918     @Override
919     public void calibrate() throws LockedException, NotReadyException, CalibrationException {
920         if (running) {
921             throw new LockedException();
922         }
923         if (!isReady()) {
924             throw new NotReadyException();
925         }
926 
927         final var innerEstimator = new MSACRobustEstimator<>(new MSACRobustEstimatorListener<PreliminaryResult>() {
928             @Override
929             public double getThreshold() {
930                 return threshold;
931             }
932 
933             @Override
934             public int getTotalSamples() {
935                 return measurements.size();
936             }
937 
938             @Override
939             public int getSubsetSize() {
940                 return preliminarySubsetSize;
941             }
942 
943             @Override
944             public void estimatePreliminarSolutions(
945                     final int[] samplesIndices, final List<PreliminaryResult> solutions) {
946                 computePreliminarySolutions(samplesIndices, solutions);
947             }
948 
949             @Override
950             public double computeResidual(final PreliminaryResult currentEstimation, final int i) {
951                 return computeError(measurements.get(i), currentEstimation);
952             }
953 
954             @Override
955             public boolean isReady() {
956                 return MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator.super.isReady();
957             }
958 
959             @Override
960             public void onEstimateStart(final RobustEstimator<PreliminaryResult> estimator) {
961                 // no action needed
962             }
963 
964             @Override
965             public void onEstimateEnd(final RobustEstimator<PreliminaryResult> estimator) {
966                 // no action needed
967             }
968 
969             @Override
970             public void onEstimateNextIteration(
971                     final RobustEstimator<PreliminaryResult> estimator, final int iteration) {
972                 if (listener != null) {
973                     listener.onCalibrateNextIteration(
974                             MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator.this, iteration);
975                 }
976             }
977 
978             @Override
979             public void onEstimateProgressChange(
980                     final RobustEstimator<PreliminaryResult> estimator, final float progress) {
981                 if (listener != null) {
982                     listener.onCalibrateProgressChange(
983                             MSACRobustKnownMagneticFluxDensityNormMagnetometerCalibrator.this, progress);
984                 }
985             }
986         });
987 
988         try {
989             running = true;
990 
991             if (listener != null) {
992                 listener.onCalibrateStart(this);
993             }
994 
995             inliersData = null;
996 
997             innerEstimator.setConfidence(confidence);
998             innerEstimator.setMaxIterations(maxIterations);
999             innerEstimator.setProgressDelta(progressDelta);
1000             final var preliminaryResult = innerEstimator.estimate();
1001             inliersData = innerEstimator.getInliersData();
1002 
1003             attemptRefine(preliminaryResult);
1004 
1005             if (listener != null) {
1006                 listener.onCalibrateEnd(this);
1007             }
1008 
1009         } catch (final com.irurueta.numerical.LockedException e) {
1010             throw new LockedException(e);
1011         } catch (final com.irurueta.numerical.NotReadyException e) {
1012             throw new NotReadyException(e);
1013         } catch (final RobustEstimatorException e) {
1014             throw new CalibrationException(e);
1015         } finally {
1016             running = false;
1017         }
1018     }
1019 
1020     /**
1021      * Returns method being used for robust estimation.
1022      *
1023      * @return method being used for robust estimation.
1024      */
1025     @Override
1026     public RobustEstimatorMethod getMethod() {
1027         return RobustEstimatorMethod.MSAC;
1028     }
1029 
1030     /**
1031      * Indicates whether this calibrator requires quality scores for each
1032      * measurement or not.
1033      *
1034      * @return true if quality scores are required, false otherwise.
1035      */
1036     @Override
1037     public boolean isQualityScoresRequired() {
1038         return false;
1039     }
1040 }