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 hard-iron biases, cross couplings and
37 * scaling factors using LMedS 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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator extends
63 RobustKnownPositionAndInstantMagnetometerCalibrator {
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator() {
111 super();
112 }
113
114 /**
115 * Constructor.
116 *
117 * @param listener listener to handle events raised by this calibrator.
118 */
119 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
120 final RobustKnownPositionAndInstantMagnetometerCalibratorListener 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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(final WorldMagneticModel magneticModel) {
155 super(magneticModel);
156 }
157
158 /**
159 * Constructor.
160 *
161 * @param initialHardIron initial hard-iron to find a solution.
162 * @throws IllegalArgumentException if provided hard-iron array does
163 * not have length 3.
164 */
165 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(final double[] initialHardIron) {
166 super(initialHardIron);
167 }
168
169 /**
170 * Constructor.
171 *
172 * @param initialHardIron initial hard-iron to find a solution.
173 * @throws IllegalArgumentException if provided hard-iron matrix is not
174 * 3x1.
175 */
176 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(final Matrix initialHardIron) {
177 super(initialHardIron);
178 }
179
180 /**
181 * Constructor.
182 *
183 * @param initialHardIron initial hard-iron to find a solution.
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
191 final Matrix initialHardIron, final Matrix initialMm) {
192 super(initialHardIron, initialMm);
193 }
194
195 /**
196 * Constructor.
197 *
198 * @param position position where body magnetic flux density measurements
199 * have been taken.
200 */
201 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
234 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
235 final RobustKnownPositionAndInstantMagnetometerCalibratorListener 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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
273 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
274 final boolean commonAxisUsed, final RobustKnownPositionAndInstantMagnetometerCalibratorListener 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 initialHardIron initial hard-iron to find a solution.
289 * @throws IllegalArgumentException if provided hard-iron array does
290 * not have length 3.
291 */
292 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
293 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
294 final double[] initialHardIron) {
295 super(position, measurements, initialHardIron);
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 initialHardIron initial hard-iron to find a solution.
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
314 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
315 final double[] initialHardIron,
316 final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
317 super(position, measurements, initialHardIron, 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 initialHardIron initial hard-iron to find a solution.
333 * @throws IllegalArgumentException if provided hard-iron array does
334 * not have length 3.
335 */
336 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
337 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
338 final boolean commonAxisUsed, final double[] initialHardIron) {
339 super(position, measurements, commonAxisUsed, initialHardIron);
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 initialHardIron initial hard-iron to find a solution.
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
360 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
361 final boolean commonAxisUsed, final double[] initialHardIron,
362 final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
363 super(position, measurements, commonAxisUsed, initialHardIron, 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 initialHardIron initial hard-iron to find a solution.
377 * @throws IllegalArgumentException if provided hard-iron matrix is not
378 * 3x1.
379 */
380 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
381 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
382 final Matrix initialHardIron) {
383 super(position, measurements, initialHardIron);
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 initialHardIron initial hard-iron to find a solution.
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
402 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
403 final Matrix initialHardIron, final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
404 super(position, measurements, initialHardIron, 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 initialHardIron initial hard-iron to find a solution.
420 * @throws IllegalArgumentException if provided hard-iron matrix is not
421 * 3x1.
422 */
423 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
424 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
425 final boolean commonAxisUsed, final Matrix initialHardIron) {
426 super(position, measurements, commonAxisUsed, initialHardIron);
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 initialHardIron initial hard-iron to find a solution.
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
447 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
448 final boolean commonAxisUsed, final Matrix initialHardIron,
449 final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
450 super(position, measurements, commonAxisUsed, initialHardIron, 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 initialHardIron initial hard-iron to find a solution.
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
471 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
472 final Matrix initialHardIron, final Matrix initialMm) {
473 super(position, measurements, initialHardIron, 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 initialHardIron initial hard-iron to find a solution.
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
495 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
496 final Matrix initialHardIron, final Matrix initialMm,
497 final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
498 super(position, measurements, initialHardIron, 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 initialHardIron initial hard-iron to find a solution.
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
521 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
522 final boolean commonAxisUsed, final Matrix initialHardIron, final Matrix initialMm) {
523 super(position, measurements, commonAxisUsed, initialHardIron, 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 initialHardIron initial hard-iron to find a solution.
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
547 final NEDPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
548 final boolean commonAxisUsed, final Matrix initialHardIron, final Matrix initialMm,
549 final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
550 super(position, measurements, commonAxisUsed, initialHardIron, 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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
592 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
593 final RobustKnownPositionAndInstantMagnetometerCalibratorListener 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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
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 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
631 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
632 final boolean commonAxisUsed, final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
633 super(position, measurements, commonAxisUsed, listener);
634 }
635
636 /**
637 * Constructor.
638 *
639 * @param position position where body magnetic flux density measurements
640 * have been taken.
641 * @param measurements collection of body magnetic flux density
642 * measurements with standard deviation of
643 * magnetometer measurements taken at the same
644 * position with zero velocity and unknown different
645 * orientations.
646 * @param initialHardIron initial hard-iron to find a solution.
647 * @throws IllegalArgumentException if provided hard-iron array does
648 * not have length 3.
649 */
650 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
651 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
652 final double[] initialHardIron) {
653 super(position, measurements, initialHardIron);
654 }
655
656 /**
657 * Constructor.
658 *
659 * @param position position where body magnetic flux density measurements
660 * have been taken.
661 * @param measurements collection of body magnetic flux density
662 * measurements with standard deviation of
663 * magnetometer measurements taken at the same
664 * position with zero velocity and unknown different
665 * orientations.
666 * @param initialHardIron initial hard-iron to find a solution.
667 * @param listener listener to handle events raised by this calibrator.
668 * @throws IllegalArgumentException if provided hard-iron array does
669 * not have length 3.
670 */
671 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
672 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
673 final double[] initialHardIron,
674 final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
675 super(position, measurements, initialHardIron, listener);
676 }
677
678 /**
679 * Constructor.
680 *
681 * @param position position where body magnetic flux density measurements
682 * have been taken.
683 * @param measurements collection of body magnetic flux density
684 * measurements with standard deviation of
685 * magnetometer measurements taken at the same
686 * position with zero velocity and unknown different
687 * orientations.
688 * @param commonAxisUsed indicates whether z-axis is assumed to be common
689 * for the accelerometer, gyroscope and magnetometer.
690 * @param initialHardIron initial hard-iron to find a solution.
691 * @throws IllegalArgumentException if provided hard-iron array does
692 * not have length 3.
693 */
694 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
695 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
696 final boolean commonAxisUsed, final double[] initialHardIron) {
697 super(position, measurements, commonAxisUsed, initialHardIron);
698 }
699
700 /**
701 * Constructor.
702 *
703 * @param position position where body magnetic flux density measurements
704 * have been taken.
705 * @param measurements collection of body magnetic flux density
706 * measurements with standard deviation of
707 * magnetometer measurements taken at the same
708 * position with zero velocity and unknown different
709 * orientations.
710 * @param commonAxisUsed indicates whether z-axis is assumed to be common
711 * for the accelerometer, gyroscope and magnetometer.
712 * @param initialHardIron initial hard-iron to find a solution.
713 * @param listener listener to handle events raised by this calibrator.
714 * @throws IllegalArgumentException if provided hard-iron array does
715 * not have length 3.
716 */
717 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
718 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
719 final boolean commonAxisUsed, final double[] initialHardIron,
720 final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
721 super(position, measurements, commonAxisUsed, initialHardIron, listener);
722 }
723
724 /**
725 * Constructor.
726 *
727 * @param position position where body magnetic flux density measurements
728 * have been taken.
729 * @param measurements collection of body magnetic flux density
730 * measurements with standard deviation of
731 * magnetometer measurements taken at the same
732 * position with zero velocity and unknown different
733 * orientations.
734 * @param initialHardIron initial hard-iron to find a solution.
735 * @throws IllegalArgumentException if provided hard-iron matrix is not
736 * 3x1.
737 */
738 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
739 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
740 final Matrix initialHardIron) {
741 super(position, measurements, initialHardIron);
742 }
743
744 /**
745 * Constructor.
746 *
747 * @param position position where body magnetic flux density measurements
748 * have been taken.
749 * @param measurements collection of body magnetic flux density
750 * measurements with standard deviation of
751 * magnetometer measurements taken at the same
752 * position with zero velocity and unknown different
753 * orientations.
754 * @param initialHardIron initial hard-iron to find a solution.
755 * @param listener listener to handle events raised by this calibrator.
756 * @throws IllegalArgumentException if provided hard-iron matrix is not
757 * 3x1.
758 */
759 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
760 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
761 final Matrix initialHardIron, final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
762 super(position, measurements, initialHardIron, listener);
763 }
764
765 /**
766 * Constructor.
767 *
768 * @param position position where body magnetic flux density measurements
769 * have been taken.
770 * @param measurements collection of body magnetic flux density
771 * measurements with standard deviation of
772 * magnetometer measurements taken at the same
773 * position with zero velocity and unknown different
774 * orientations.
775 * @param commonAxisUsed indicates whether z-axis is assumed to be common
776 * for the accelerometer, gyroscope and magnetometer.
777 * @param initialHardIron initial hard-iron to find a solution.
778 * @throws IllegalArgumentException if provided hard-iron matrix is not
779 * 3x1.
780 */
781 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
782 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
783 final boolean commonAxisUsed, final Matrix initialHardIron) {
784 super(position, measurements, commonAxisUsed, initialHardIron);
785 }
786
787 /**
788 * Constructor.
789 *
790 * @param position position where body magnetic flux density measurements
791 * have been taken.
792 * @param measurements collection of body magnetic flux density
793 * measurements with standard deviation of
794 * magnetometer measurements taken at the same
795 * position with zero velocity and unknown different
796 * orientations.
797 * @param commonAxisUsed indicates whether z-axis is assumed to be common
798 * for the accelerometer, gyroscope and magnetometer.
799 * @param initialHardIron initial hard-iron to find a solution.
800 * @param listener listener to handle events raised by this calibrator.
801 * @throws IllegalArgumentException if provided hard-iron matrix is not
802 * 3x1.
803 */
804 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
805 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
806 final boolean commonAxisUsed, final Matrix initialHardIron,
807 final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
808 super(position, measurements, commonAxisUsed, initialHardIron, listener);
809 }
810
811 /**
812 * Constructor.
813 *
814 * @param position position where body magnetic flux density measurements
815 * have been taken.
816 * @param measurements collection of body magnetic flux density
817 * measurements with standard deviation of
818 * magnetometer measurements taken at the same
819 * position with zero velocity and unknown different
820 * orientations.
821 * @param initialHardIron initial hard-iron to find a solution.
822 * @param initialMm initial soft-iron matrix containing scale factors
823 * and cross coupling errors.
824 * @throws IllegalArgumentException if provided hard-iron matrix is not
825 * 3x1 or if soft-iron matrix is not
826 * 3x3.
827 */
828 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
829 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
830 final Matrix initialHardIron, final Matrix initialMm) {
831 super(position, measurements, initialHardIron, initialMm);
832 }
833
834 /**
835 * Constructor.
836 *
837 * @param position position where body magnetic flux density measurements
838 * have been taken.
839 * @param measurements collection of body magnetic flux density
840 * measurements with standard deviation of
841 * magnetometer measurements taken at the same
842 * position with zero velocity and unknown different
843 * orientations.
844 * @param initialHardIron initial hard-iron to find a solution.
845 * @param initialMm initial soft-iron matrix containing scale factors
846 * and cross coupling errors.
847 * @param listener listener to handle events raised by this calibrator.
848 * @throws IllegalArgumentException if provided hard-iron matrix is not
849 * 3x1 or if soft-iron matrix is not
850 * 3x3.
851 */
852 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
853 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
854 final Matrix initialHardIron, final Matrix initialMm,
855 final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
856 super(position, measurements, initialHardIron, initialMm, listener);
857 }
858
859 /**
860 * Constructor.
861 *
862 * @param position position where body magnetic flux density measurements
863 * have been taken.
864 * @param measurements collection of body magnetic flux density
865 * measurements with standard deviation of
866 * magnetometer measurements taken at the same
867 * position with zero velocity and unknown different
868 * orientations.
869 * @param commonAxisUsed indicates whether z-axis is assumed to be common
870 * for the accelerometer, gyroscope and magnetometer.
871 * @param initialHardIron initial hard-iron to find a solution.
872 * @param initialMm initial soft-iron matrix containing scale factors
873 * and cross coupling errors.
874 * @throws IllegalArgumentException if provided hard-iron matrix is not
875 * 3x1 or if soft-iron matrix is not
876 * 3x3.
877 */
878 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
879 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
880 final boolean commonAxisUsed, final Matrix initialHardIron, final Matrix initialMm) {
881 super(position, measurements, commonAxisUsed, initialHardIron, initialMm);
882 }
883
884 /**
885 * Constructor.
886 *
887 * @param position position where body magnetic flux density measurements
888 * have been taken.
889 * @param measurements collection of body magnetic flux density
890 * measurements with standard deviation of
891 * magnetometer measurements taken at the same
892 * position with zero velocity and unknown different
893 * orientations.
894 * @param commonAxisUsed indicates whether z-axis is assumed to be common
895 * for the accelerometer, gyroscope and magnetometer.
896 * @param initialHardIron initial hard-iron to find a solution.
897 * @param initialMm initial soft-iron matrix containing scale factors
898 * and cross coupling errors.
899 * @param listener listener to handle events raised by this calibrator.
900 * @throws IllegalArgumentException if provided hard-iron matrix is not
901 * 3x1 or if soft-iron matrix is not
902 * 3x3.
903 */
904 public LMedSRobustKnownPositionAndInstantMagnetometerCalibrator(
905 final ECEFPosition position, final List<StandardDeviationBodyMagneticFluxDensity> measurements,
906 final boolean commonAxisUsed, final Matrix initialHardIron, final Matrix initialMm,
907 final RobustKnownPositionAndInstantMagnetometerCalibratorListener listener) {
908 super(position, measurements, commonAxisUsed, initialHardIron, initialMm, listener);
909 }
910
911 /**
912 * Returns threshold to be used to keep the algorithm iterating in case that
913 * best estimated threshold using median of residuals is not small enough.
914 * Once a solution is found that generates a threshold below this value, the
915 * algorithm will stop.
916 * The stop threshold can be used to prevent the LMedS algorithm to iterate
917 * too many times in cases where samples have a very similar accuracy.
918 * For instance, in cases where proportion of outliers is very small (close
919 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
920 * iterate for a long time trying to find the best solution when indeed
921 * there is no need to do that if a reasonable threshold has already been
922 * reached.
923 * Because of this behaviour the stop threshold can be set to a value much
924 * lower than the one typically used in RANSAC, and yet the algorithm could
925 * still produce even smaller thresholds in estimated results.
926 *
927 * @return stop threshold to stop the algorithm prematurely when a certain
928 * accuracy has been reached.
929 */
930 public double getStopThreshold() {
931 return stopThreshold;
932 }
933
934 /**
935 * Sets threshold to be used to keep the algorithm iterating in case that
936 * best estimated threshold using median of residuals is not small enough.
937 * Once a solution is found that generates a threshold below this value,
938 * the algorithm will stop.
939 * The stop threshold can be used to prevent the LMedS algorithm to iterate
940 * too many times in cases where samples have a very similar accuracy.
941 * For instance, in cases where proportion of outliers is very small (close
942 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
943 * iterate for a long time trying to find the best solution when indeed
944 * there is no need to do that if a reasonable threshold has already been
945 * reached.
946 * Because of this behaviour the stop threshold can be set to a value much
947 * lower than the one typically used in RANSAC, and yet the algorithm could
948 * still produce even smaller thresholds in estimated results.
949 *
950 * @param stopThreshold stop threshold to stop the algorithm prematurely
951 * when a certain accuracy has been reached.
952 * @throws IllegalArgumentException if provided value is zero or negative.
953 * @throws LockedException if calibrator is currently running.
954 */
955 public void setStopThreshold(final double stopThreshold) throws LockedException {
956 if (running) {
957 throw new LockedException();
958 }
959 if (stopThreshold <= MIN_STOP_THRESHOLD) {
960 throw new IllegalArgumentException();
961 }
962
963 this.stopThreshold = stopThreshold;
964 }
965
966 /**
967 * Estimates magnetometer calibration parameters containing hard-iron
968 * bias and soft-iron scale factors and cross-coupling errors.
969 *
970 * @throws LockedException if calibrator is currently running.
971 * @throws NotReadyException if calibrator is not ready.
972 * @throws CalibrationException if estimation fails for numerical reasons.
973 */
974 @SuppressWarnings("DuplicatedCode")
975 @Override
976 public void calibrate() throws LockedException, NotReadyException, CalibrationException {
977 if (running) {
978 throw new LockedException();
979 }
980 if (!isReady()) {
981 throw new NotReadyException();
982 }
983
984 final var innerEstimator = new LMedSRobustEstimator<>(new LMedSRobustEstimatorListener<PreliminaryResult>() {
985 @Override
986 public int getTotalSamples() {
987 return measurements.size();
988 }
989
990 @Override
991 public int getSubsetSize() {
992 return preliminarySubsetSize;
993 }
994
995 @Override
996 public void estimatePreliminarSolutions(
997 final int[] samplesIndices, final List<PreliminaryResult> solutions) {
998 computePreliminarySolutions(samplesIndices, solutions);
999 }
1000
1001 @Override
1002 public double computeResidual(final PreliminaryResult currentEstimation, final int i) {
1003 return computeError(measurements.get(i), currentEstimation);
1004 }
1005
1006 @Override
1007 public boolean isReady() {
1008 return LMedSRobustKnownPositionAndInstantMagnetometerCalibrator.this.isReady();
1009 }
1010
1011 @Override
1012 public void onEstimateStart(final RobustEstimator<PreliminaryResult> estimator) {
1013 // no action needed
1014 }
1015
1016 @Override
1017 public void onEstimateEnd(final RobustEstimator<PreliminaryResult> estimator) {
1018 // no action needed
1019 }
1020
1021 @Override
1022 public void onEstimateNextIteration(
1023 final RobustEstimator<PreliminaryResult> estimator, final int iteration) {
1024 if (listener != null) {
1025 listener.onCalibrateNextIteration(
1026 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator.this, iteration);
1027 }
1028 }
1029
1030 @Override
1031 public void onEstimateProgressChange(
1032 final RobustEstimator<PreliminaryResult> estimator, final float progress) {
1033 if (listener != null) {
1034 listener.onCalibrateProgressChange(
1035 LMedSRobustKnownPositionAndInstantMagnetometerCalibrator.this, progress);
1036 }
1037 }
1038 });
1039
1040 try {
1041 running = true;
1042
1043 if (listener != null) {
1044 listener.onCalibrateStart(this);
1045 }
1046
1047 inliersData = null;
1048
1049 initialize();
1050
1051 innerEstimator.setConfidence(confidence);
1052 innerEstimator.setMaxIterations(maxIterations);
1053 innerEstimator.setProgressDelta(progressDelta);
1054 innerEstimator.setStopThreshold(stopThreshold);
1055 final var preliminaryResult = innerEstimator.estimate();
1056 inliersData = innerEstimator.getInliersData();
1057
1058 attemptRefine(preliminaryResult);
1059
1060 if (listener != null) {
1061 listener.onCalibrateEnd(this);
1062 }
1063
1064 } catch (final com.irurueta.numerical.LockedException e) {
1065 throw new LockedException(e);
1066 } catch (final com.irurueta.numerical.NotReadyException e) {
1067 throw new NotReadyException(e);
1068 } catch (final RobustEstimatorException | IOException e) {
1069 throw new CalibrationException(e);
1070 } finally {
1071 running = false;
1072 }
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 }