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 }