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