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