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