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