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