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