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.AlgebraException;
19 import com.irurueta.algebra.Matrix;
20 import com.irurueta.algebra.Utils;
21 import com.irurueta.algebra.WrongSizeException;
22 import com.irurueta.navigation.LockedException;
23 import com.irurueta.navigation.NotReadyException;
24 import com.irurueta.navigation.inertial.BodyKinematics;
25 import com.irurueta.navigation.inertial.BodyMagneticFluxDensity;
26 import com.irurueta.navigation.inertial.calibration.CalibrationException;
27 import com.irurueta.navigation.inertial.calibration.MagneticFluxDensityTriad;
28 import com.irurueta.navigation.inertial.calibration.StandardDeviationBodyMagneticFluxDensity;
29 import com.irurueta.numerical.EvaluationException;
30 import com.irurueta.numerical.GradientEstimator;
31 import com.irurueta.numerical.fitting.FittingException;
32 import com.irurueta.numerical.fitting.LevenbergMarquardtMultiDimensionFitter;
33 import com.irurueta.numerical.fitting.LevenbergMarquardtMultiDimensionFunctionEvaluator;
34 import com.irurueta.statistics.MaxIterationsExceededException;
35 import com.irurueta.units.MagneticFluxDensity;
36 import com.irurueta.units.MagneticFluxDensityConverter;
37 import com.irurueta.units.MagneticFluxDensityUnit;
38
39 import java.util.Collection;
40
41 /**
42 * Abstract class to estimate magnetometer cross couplings and scaling factors.
43 * This calibrator uses Levenberg-Marquardt to find a minimum least squared
44 * error solution.
45 * <p>
46 * To use this calibrator at least 7 measurements taken at a single unknown
47 * and unknown orientations when common z-axis is assumed, otherwise at least 10
48 * measurements are required.
49 * <p>
50 * Measured magnetic flux density is assumed to follow the model shown below:
51 * <pre>
52 * mBmeas = bm + (I + Mm) * mBtrue + w
53 * </pre>
54 * Where:
55 * - mBmeas is the measured magnetic flux density. This is a 3x1 vector.
56 * - bm is magnetometer hard-iron bias. Ideally, on a perfect magnetometer,
57 * this should be a 3x1 zero vector.
58 * - I is the 3x3 identity matrix.
59 * - Mm is the 3x3 soft-iron matrix containing cross-couplings and scaling
60 * factors. Ideally, on a perfect magnetometer, this should be a 3x3 zero
61 * matrix.
62 * - mBtrue is ground-truth magnetic flux density. This is a 3x1 vector.
63 * - w is measurement noise. This is a 3x1 vector.
64 * Notice that this calibrator assumes that all measurements are taken in
65 * a short span of time where Earth magnetic field can be assumed to be
66 * constant at provided location and instant.
67 *
68 * @param <C> Calibrator type.
69 * @param <L> Listener type.
70 */
71 public class BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator<
72 C extends BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator<?, ?>,
73 L extends BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibratorListener<C>>
74 implements MagnetometerNonLinearCalibrator, KnownHardIronMagnetometerCalibrator,
75 UnorderedStandardDeviationBodyMagneticFluxDensityMagnetometerCalibrator {
76
77 /**
78 * Indicates whether by default a common z-axis is assumed for the accelerometer,
79 * gyroscope and magnetometer.
80 */
81 public static final boolean DEFAULT_USE_COMMON_Z_AXIS = false;
82
83 /**
84 * Number of unknowns when common z-axis is assumed for the accelerometer,
85 * gyroscope and magnetometer.
86 */
87 private static final int COMMON_Z_AXIS_UNKNOWNS = 6;
88
89 /**
90 * Number of unknowns for the general case.
91 */
92 private static final int GENERAL_UNKNOWNS = 9;
93
94 /**
95 * Required minimum number of measurements when common z-axis is assumed.
96 */
97 public static final int MINIMUM_MEASUREMENTS_COMMON_Z_AXIS = COMMON_Z_AXIS_UNKNOWNS + 1;
98
99 /**
100 * Required minimum number of measurements for the general case.
101 */
102 public static final int MINIMUM_MEASUREMENTS_GENERAL = GENERAL_UNKNOWNS + 1;
103
104 /**
105 * Ground truth magnetic flux density norm to be expected at location where measurements have been made,
106 * expressed in Teslas (T).
107 */
108 protected Double groundTruthMagneticFluxDensityNorm;
109
110 /**
111 * Levenberg-Marquardt fitter to find a non-linear solution.
112 */
113 private final LevenbergMarquardtMultiDimensionFitter fitter = new LevenbergMarquardtMultiDimensionFitter();
114
115 /**
116 * Known x-coordinate of hard-iron bias to be used to find a solution.
117 * This is expressed in Teslas (T).
118 */
119 private double hardIronX;
120
121 /**
122 * Known y-coordinate of hard-iron bias to be used to find a solution.
123 * This is expressed in Teslas (T).
124 */
125 private double hardIronY;
126
127 /**
128 * Known z-coordinate of hard-iron bias to be used to find a solution.
129 * This is expressed in Teslas (T).
130 */
131 private double hardIronZ;
132
133 /**
134 * Initial x scaling factor.
135 */
136 private double initialSx;
137
138 /**
139 * Initial y scaling factor.
140 */
141 private double initialSy;
142
143 /**
144 * Initial z scaling factor.
145 */
146 private double initialSz;
147
148 /**
149 * Initial x-y cross coupling error.
150 */
151 private double initialMxy;
152
153 /**
154 * Initial x-z cross coupling error.
155 */
156 private double initialMxz;
157
158 /**
159 * Initial y-x cross coupling error.
160 */
161 private double initialMyx;
162
163 /**
164 * Initial y-z cross coupling error.
165 */
166 private double initialMyz;
167
168 /**
169 * Initial z-x cross coupling error.
170 */
171 private double initialMzx;
172
173 /**
174 * Initial z-y cross coupling error.
175 */
176 private double initialMzy;
177
178 /**
179 * Contains a collection of body magnetic flux density measurements taken
180 * at a given position with different unknown orientations and containing the
181 * standard deviation of magnetometer measurements.
182 */
183 private Collection<StandardDeviationBodyMagneticFluxDensity> measurements;
184
185 /**
186 * This flag indicates whether z-axis is assumed to be common for accelerometer,
187 * gyroscope and magnetometer.
188 * When enabled, this eliminates 3 variables from Mm matrix.
189 */
190 private boolean commonAxisUsed = DEFAULT_USE_COMMON_Z_AXIS;
191
192 /**
193 * Listener to handle events raised by this calibrator.
194 */
195 private L listener;
196
197 /**
198 * Estimated magnetometer soft-iron matrix containing scale factors
199 * and cross coupling errors.
200 * This is the product of matrix Tm containing cross coupling errors and Km
201 * containing scaling factors.
202 * So tat:
203 * <pre>
204 * Mm = [sx mxy mxz] = Tm*Km
205 * [myx sy myz]
206 * [mzx mzy sz ]
207 * </pre>
208 * Where:
209 * <pre>
210 * Km = [sx 0 0 ]
211 * [0 sy 0 ]
212 * [0 0 sz]
213 * </pre>
214 * and
215 * <pre>
216 * Tm = [1 -alphaXy alphaXz ]
217 * [alphaYx 1 -alphaYz]
218 * [-alphaZx alphaZy 1 ]
219 * </pre>
220 * Hence:
221 * <pre>
222 * Mm = [sx mxy mxz] = Tm*Km = [sx -sy * alphaXy sz * alphaXz ]
223 * [myx sy myz] [sx * alphaYx sy -sz * alphaYz]
224 * [mzx mzy sz ] [-sx * alphaZx sy * alphaZy sz ]
225 * </pre>
226 * This instance allows any 3x3 matrix however, typically alphaYx, alphaZx and alphaZy
227 * are considered to be zero if the accelerometer z-axis is assumed to be the same
228 * as the body z-axis. When this is assumed, myx = mzx = mzy = 0 and the Mm matrix
229 * becomes upper diagonal:
230 * <pre>
231 * Mm = [sx mxy mxz]
232 * [0 sy myz]
233 * [0 0 sz ]
234 * </pre>
235 * Values of this matrix are unit-less.
236 */
237 private Matrix estimatedMm;
238
239 /**
240 * Estimated covariance matrix for estimated parameters.
241 */
242 private Matrix estimatedCovariance;
243
244 /**
245 * Estimated chi square value.
246 */
247 private double estimatedChiSq;
248
249 /**
250 * Estimated degrees of freedom of chi square value. Degrees of freedom is equal to the number of sampled data
251 * minus the number of estimated parameters.
252 */
253 private int estimatedChiSqDegreesOfFreedom;
254
255 /**
256 * Estimated reduced chi square value. This is equal to estimated chi square value divided by its degrees of
257 * freedom. Ideally this value should be close to 1.0.
258 */
259 private double estimatedReducedChiSq;
260
261 /**
262 * Estimated mean square error respect to provided measurements.
263 */
264 private double estimatedMse;
265
266 /**
267 * Estimated probability of finding a smaller chi square value expressed as a value between 0.0 and 1.0. The smaller
268 * the found chi square value is, the better the fit of the estimated parameters to the actual parameter. Thus, the
269 * smaller the chance of finding a smaller chi square value, then the better the estimated fit is.
270 */
271 private double estimatedP;
272
273 /**
274 * Estimated measure of quality of estimated fit as a value between 0.0 and 1.0. The larger the quality value is,
275 * the better the fit that has been estimated.
276 */
277 private double estimatedQ;
278
279 /**
280 * Indicates whether calibrator is running.
281 */
282 private boolean running;
283
284 /**
285 * Internally holds x-coordinate of measured magnetic flux density
286 * during calibration.
287 */
288 private double bmeasX;
289
290 /**
291 * Internally holds y-coordinate of measured magnetic flux density
292 * during calibration.
293 */
294 private double bmeasY;
295
296 /**
297 * Internally holds z-coordinate of measured magnetic flux density
298 * during calibration.
299 */
300 private double bmeasZ;
301
302 /**
303 * Internally holds measured magnetic flux density during calibration
304 * expressed as a column matrix.
305 */
306 private Matrix bmeas;
307
308 /**
309 * Internally holds cross-coupling errors during calibration.
310 */
311 private Matrix m;
312
313 /**
314 * Internally holds inverse of cross-coupling errors during calibration.
315 */
316 private Matrix invM;
317
318 /**
319 * Internally holds biases during calibration.
320 */
321 private Matrix b;
322
323 /**
324 * Internally holds computed true magnetic flux density during
325 * calibration.
326 */
327 private Matrix btrue;
328
329 /**
330 * Internally hold biases during calibration in external format.
331 */
332 private Matrix bm;
333
334 /**
335 * Constructor.
336 */
337 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator() {
338 }
339
340 /**
341 * Constructor.
342 *
343 * @param listener listener to handle events raised by this calibrator.
344 */
345 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(final L listener) {
346 this.listener = listener;
347 }
348
349 /**
350 * Constructor.
351 *
352 * @param measurements collection of body magnetic flux density
353 * measurements with standard deviation of
354 * magnetometer measurements taken at the same
355 * position with zero velocity and unknown different
356 * orientations.
357 */
358 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
359 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements) {
360 this.measurements = measurements;
361 }
362
363 /**
364 * Constructor.
365 *
366 * @param measurements collection of body magnetic flux density
367 * measurements with standard deviation of
368 * magnetometer measurements taken at the same
369 * position with zero velocity and unknown different
370 * orientations.
371 * @param listener listener to handle events raised by this calibrator.
372 */
373 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
374 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final L listener) {
375 this(measurements);
376 this.listener = listener;
377 }
378
379 /**
380 * Constructor.
381 *
382 * @param commonAxisUsed indicates whether z-axis is assumed to be common
383 * for the accelerometer, gyroscope and magnetometer.
384 */
385 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(final boolean commonAxisUsed) {
386 this.commonAxisUsed = commonAxisUsed;
387 }
388
389 /**
390 * Constructor.
391 *
392 * @param measurements collection of body magnetic flux density
393 * measurements with standard deviation of
394 * magnetometer measurements taken at the same
395 * position with zero velocity and unknown different
396 * orientations.
397 * @param commonAxisUsed indicates whether z-axis is assumed to be common
398 * for the accelerometer, gyroscope and magnetometer.
399 */
400 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
401 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed) {
402 this.measurements = measurements;
403 this.commonAxisUsed = commonAxisUsed;
404 }
405
406 /**
407 * Constructor.
408 *
409 * @param measurements collection of body magnetic flux density
410 * measurements with standard deviation of
411 * magnetometer measurements taken at the same
412 * position with zero velocity and unknown different
413 * orientations.
414 * @param commonAxisUsed indicates whether z-axis is assumed to be common
415 * for the accelerometer, gyroscope and magnetometer.
416 * @param listener listener to handle events raised by this calibrator.
417 */
418 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
419 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements,
420 final boolean commonAxisUsed, final L listener) {
421 this(measurements, commonAxisUsed);
422 this.listener = listener;
423 }
424
425 /**
426 * Constructor.
427 *
428 * @param hardIron known hard-iron.
429 * @throws IllegalArgumentException if provided hard-iron array does
430 * not have length 3.
431 */
432 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(final double[] hardIron) {
433 try {
434 setHardIron(hardIron);
435 } catch (final LockedException ignore) {
436 // never happens
437 }
438 }
439
440 /**
441 * Constructor.
442 *
443 * @param measurements collection of body magnetic flux density
444 * measurements with standard deviation of
445 * magnetometer measurements taken at the same
446 * position with zero velocity and unknown different
447 * orientations.
448 * @param hardIron known hard-iron.
449 * @throws IllegalArgumentException if provided hard-iron array does
450 * not have length 3.
451 */
452 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
453 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] hardIron) {
454 this(hardIron);
455 this.measurements = measurements;
456 }
457
458 /**
459 * Constructor.
460 *
461 * @param measurements collection of body magnetic flux density
462 * measurements with standard deviation of
463 * magnetometer measurements taken at the same
464 * position with zero velocity and unknown different
465 * orientations.
466 * @param hardIron known hard-iron.
467 * @param listener listener to handle events raised by this calibrator.
468 * @throws IllegalArgumentException if provided hard-iron array does
469 * not have length 3.
470 */
471 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
472 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] hardIron,
473 final L listener) {
474 this(measurements, hardIron);
475 this.listener = listener;
476 }
477
478 /**
479 * Constructor.
480 *
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 commonAxisUsed indicates whether z-axis is assumed to be common
487 * for the accelerometer, gyroscope and magnetometer.
488 * @param hardIron known hard-iron.
489 * @throws IllegalArgumentException if provided hard-iron array does
490 * not have length 3.
491 */
492 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
493 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
494 final double[] hardIron) {
495 this(measurements, hardIron);
496 this.commonAxisUsed = commonAxisUsed;
497 }
498
499 /**
500 * Constructor.
501 *
502 * @param measurements collection of body magnetic flux density
503 * measurements with standard deviation of
504 * magnetometer measurements taken at the same
505 * position with zero velocity and unknown different
506 * orientations.
507 * @param commonAxisUsed indicates whether z-axis is assumed to be common
508 * for the accelerometer, gyroscope and magnetometer.
509 * @param hardIron known hard-iron.
510 * @param listener listener to handle events raised by this calibrator.
511 * @throws IllegalArgumentException if provided hard-iron array does
512 * not have length 3.
513 */
514 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
515 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
516 final double[] hardIron, final L listener) {
517 this(measurements, commonAxisUsed, hardIron);
518 this.listener = listener;
519 }
520
521 /**
522 * Constructor.
523 *
524 * @param hardIron known hard-iron.
525 * @throws IllegalArgumentException if provided hard-iron matrix is not
526 * 3x1.
527 */
528 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(final Matrix hardIron) {
529 try {
530 setHardIron(hardIron);
531 } catch (final LockedException ignore) {
532 // never happens
533 }
534 }
535
536 /**
537 * Constructor.
538 *
539 * @param measurements collection of body magnetic flux density
540 * measurements with standard deviation of
541 * magnetometer measurements taken at the same
542 * position with zero velocity and unknown different
543 * orientations.
544 * @param hardIron known hard-iron.
545 * @throws IllegalArgumentException if provided hard-iron matrix is not
546 * 3x1.
547 */
548 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
549 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron) {
550 this(hardIron);
551 this.measurements = measurements;
552 }
553
554 /**
555 * Constructor.
556 *
557 * @param measurements collection of body magnetic flux density
558 * measurements with standard deviation of
559 * magnetometer measurements taken at the same
560 * position with zero velocity and unknown different
561 * orientations.
562 * @param hardIron known hard-iron.
563 * @param listener listener to handle events raised by this calibrator.
564 * @throws IllegalArgumentException if provided hard-iron matrix is not
565 * 3x1.
566 */
567 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
568 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
569 final L listener) {
570 this(measurements, hardIron);
571 this.listener = listener;
572 }
573
574 /**
575 * Constructor.
576 *
577 * @param measurements collection of body magnetic flux density
578 * measurements with standard deviation of
579 * magnetometer measurements taken at the same
580 * position with zero velocity and unknown different
581 * orientations.
582 * @param commonAxisUsed indicates whether z-axis is assumed to be common
583 * for the accelerometer, gyroscope and magnetometer.
584 * @param hardIron known hard-iron.
585 * @throws IllegalArgumentException if provided hard-iron matrix is not
586 * 3x1.
587 */
588 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
589 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
590 final Matrix hardIron) {
591 this(measurements, hardIron);
592 this.commonAxisUsed = commonAxisUsed;
593 }
594
595 /**
596 * Constructor.
597 *
598 * @param measurements collection of body magnetic flux density
599 * measurements with standard deviation of
600 * magnetometer measurements taken at the same
601 * position with zero velocity and unknown different
602 * orientations.
603 * @param commonAxisUsed indicates whether z-axis is assumed to be common
604 * for the accelerometer, gyroscope and magnetometer.
605 * @param hardIron known hard-iron.
606 * @param listener listener to handle events raised by this calibrator.
607 * @throws IllegalArgumentException if provided hard-iron matrix is not
608 * 3x1.
609 */
610 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
611 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
612 final Matrix hardIron, final L listener) {
613 this(measurements, commonAxisUsed, hardIron);
614 this.listener = listener;
615 }
616
617 /**
618 * Constructor.
619 *
620 * @param hardIron known hard-iron.
621 * @param initialMm initial soft-iron matrix containing scale factors
622 * and cross coupling errors.
623 * @throws IllegalArgumentException if provided hard-iron matrix is not
624 * 3x1 or if soft-iron matrix is not
625 * 3x3.
626 */
627 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
628 final Matrix hardIron, final Matrix initialMm) {
629 this(hardIron);
630 try {
631 setInitialMm(initialMm);
632 } catch (final LockedException ignore) {
633 // never happens
634 }
635 }
636
637 /**
638 * Constructor.
639 *
640 * @param measurements collection of body magnetic flux density
641 * measurements with standard deviation of
642 * magnetometer measurements taken at the same
643 * position with zero velocity and unknown different
644 * orientations.
645 * @param hardIron known hard-iron.
646 * @param initialMm initial soft-iron matrix containing scale factors
647 * and cross coupling errors.
648 * @throws IllegalArgumentException if provided hard-iron matrix is not
649 * 3x1 or if soft-iron matrix is not
650 * 3x3.
651 */
652 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
653 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
654 final Matrix initialMm) {
655 this(hardIron, initialMm);
656 this.measurements = measurements;
657 }
658
659 /**
660 * Constructor.
661 *
662 * @param measurements collection of body magnetic flux density
663 * measurements with standard deviation of
664 * magnetometer measurements taken at the same
665 * position with zero velocity and unknown different
666 * orientations.
667 * @param hardIron known hard-iron.
668 * @param initialMm initial soft-iron matrix containing scale factors
669 * and cross coupling errors.
670 * @param listener listener to handle events raised by this calibrator.
671 * @throws IllegalArgumentException if provided hard-iron matrix is not
672 * 3x1 or if soft-iron matrix is not
673 * 3x3.
674 */
675 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
676 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
677 final Matrix initialMm, final L listener) {
678 this(measurements, hardIron, initialMm);
679 this.listener = listener;
680 }
681
682 /**
683 * Constructor.
684 *
685 * @param measurements collection of body magnetic flux density
686 * measurements with standard deviation of
687 * magnetometer measurements taken at the same
688 * position with zero velocity and unknown different
689 * orientations.
690 * @param commonAxisUsed indicates whether z-axis is assumed to be common
691 * for the accelerometer, gyroscope and magnetometer.
692 * @param hardIron known hard-iron.
693 * @param initialMm initial soft-iron matrix containing scale factors
694 * and cross coupling errors.
695 * @throws IllegalArgumentException if provided hard-iron matrix is not
696 * 3x1 or if soft-iron matrix is not
697 * 3x3.
698 */
699 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
700 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
701 final Matrix hardIron, final Matrix initialMm) {
702 this(measurements, hardIron, initialMm);
703 this.commonAxisUsed = commonAxisUsed;
704 }
705
706 /**
707 * Constructor.
708 *
709 * @param measurements collection of body magnetic flux density
710 * measurements with standard deviation of
711 * magnetometer measurements taken at the same
712 * position with zero velocity and unknown different
713 * orientations.
714 * @param commonAxisUsed indicates whether z-axis is assumed to be common
715 * for the accelerometer, gyroscope and magnetometer.
716 * @param hardIron known hard-iron.
717 * @param initialMm initial soft-iron matrix containing scale factors
718 * and cross coupling errors.
719 * @param listener listener to handle events raised by this calibrator.
720 * @throws IllegalArgumentException if provided hard-iron matrix is not
721 * 3x1 or if soft-iron matrix is not
722 * 3x3.
723 */
724 public BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
725 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
726 final Matrix hardIron, final Matrix initialMm, final L listener) {
727 this(measurements, commonAxisUsed, hardIron, initialMm);
728 this.listener = listener;
729 }
730
731 /**
732 * Constructor.
733 *
734 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
735 * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
736 */
737 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
738 final Double groundTruthMagneticFluxDensityNorm) {
739 internalSetGroundTruthMagneticFluxDensityNorm(groundTruthMagneticFluxDensityNorm);
740 }
741
742 /**
743 * Constructor.
744 *
745 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
746 * @param listener listener to handle events raised by this calibrator.
747 * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
748 */
749 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
750 final Double groundTruthMagneticFluxDensityNorm, final L listener) {
751 this(groundTruthMagneticFluxDensityNorm);
752 this.listener = listener;
753 }
754
755 /**
756 * Constructor.
757 *
758 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
759 * @param measurements collection of body magnetic flux density
760 * measurements with standard deviation of
761 * magnetometer measurements taken at the same
762 * position with zero velocity and unknown different
763 * orientations.
764 * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
765 */
766 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
767 final Double groundTruthMagneticFluxDensityNorm,
768 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements) {
769 this(groundTruthMagneticFluxDensityNorm);
770 this.measurements = measurements;
771 }
772
773 /**
774 * Constructor.
775 *
776 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
777 * @param measurements collection of body magnetic flux density
778 * measurements with standard deviation of
779 * magnetometer measurements taken at the same
780 * position with zero velocity and unknown different
781 * orientations.
782 * @param listener listener to handle events raised by this calibrator.
783 * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
784 */
785 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
786 final Double groundTruthMagneticFluxDensityNorm,
787 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final L listener) {
788 this(groundTruthMagneticFluxDensityNorm, measurements);
789 this.listener = listener;
790 }
791
792 /**
793 * Constructor.
794 *
795 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
796 * @param measurements collection of body magnetic flux density
797 * measurements with standard deviation of
798 * magnetometer measurements taken at the same
799 * position with zero velocity and unknown different
800 * orientations.
801 * @param commonAxisUsed indicates whether z-axis is assumed to be common
802 * for the accelerometer, gyroscope and magnetometer.
803 * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
804 */
805 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
806 final Double groundTruthMagneticFluxDensityNorm,
807 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed) {
808 this(groundTruthMagneticFluxDensityNorm, measurements);
809 this.commonAxisUsed = commonAxisUsed;
810 }
811
812 /**
813 * Constructor.
814 *
815 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
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 commonAxisUsed indicates whether z-axis is assumed to be common
822 * for the accelerometer, gyroscope and magnetometer.
823 * @param listener listener to handle events raised by this calibrator.
824 * @throws IllegalArgumentException if provided magnetic flux norm value is negative.
825 */
826 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
827 final Double groundTruthMagneticFluxDensityNorm,
828 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
829 final L listener) {
830 this(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed);
831 this.listener = listener;
832 }
833
834 /**
835 * Constructor.
836 *
837 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
838 * @param measurements collection of body magnetic flux density
839 * measurements with standard deviation of
840 * magnetometer measurements taken at the same
841 * position with zero velocity and unknown different
842 * orientations.
843 * @param hardIron known hard-iron.
844 * @throws IllegalArgumentException if provided magnetic flux norm value is
845 * negative, or if provided hard-iron array does
846 * not have length 3.
847 */
848 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
849 final Double groundTruthMagneticFluxDensityNorm,
850 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] hardIron) {
851 this(hardIron);
852 internalSetGroundTruthMagneticFluxDensityNorm(groundTruthMagneticFluxDensityNorm);
853 this.measurements = measurements;
854 }
855
856 /**
857 * Constructor.
858 *
859 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
860 * @param measurements collection of body magnetic flux density
861 * measurements with standard deviation of
862 * magnetometer measurements taken at the same
863 * position with zero velocity and unknown different
864 * orientations.
865 * @param hardIron known hard-iron.
866 * @param listener listener to handle events raised by this calibrator.
867 * @throws IllegalArgumentException if provided magnetic flux norm value is
868 * negative, or if provided hard-iron array does
869 * not have length 3.
870 */
871 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
872 final Double groundTruthMagneticFluxDensityNorm,
873 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final double[] hardIron,
874 final L listener) {
875 this(groundTruthMagneticFluxDensityNorm, measurements, hardIron);
876 this.listener = listener;
877 }
878
879 /**
880 * Constructor.
881 *
882 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
883 * @param measurements collection of body magnetic flux density
884 * measurements with standard deviation of
885 * magnetometer measurements taken at the same
886 * position with zero velocity and unknown different
887 * orientations.
888 * @param commonAxisUsed indicates whether z-axis is assumed to be common
889 * for the accelerometer, gyroscope and magnetometer.
890 * @param hardIron known hard-iron.
891 * @throws IllegalArgumentException if provided magnetic flux norm value is
892 * negative, or if provided hard-iron array does
893 * not have length 3.
894 */
895 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
896 final Double groundTruthMagneticFluxDensityNorm,
897 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
898 final double[] hardIron) {
899 this(groundTruthMagneticFluxDensityNorm, measurements, hardIron);
900 this.commonAxisUsed = commonAxisUsed;
901 }
902
903 /**
904 * Constructor.
905 *
906 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
907 * @param measurements collection of body magnetic flux density
908 * measurements with standard deviation of
909 * magnetometer measurements taken at the same
910 * position with zero velocity and unknown different
911 * orientations.
912 * @param commonAxisUsed indicates whether z-axis is assumed to be common
913 * for the accelerometer, gyroscope and magnetometer.
914 * @param hardIron known hard-iron.
915 * @param listener listener to handle events raised by this calibrator.
916 * @throws IllegalArgumentException if provided magnetic flux norm value is
917 * negative, or if provided hard-iron array does
918 * not have length 3.
919 */
920 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
921 final Double groundTruthMagneticFluxDensityNorm,
922 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
923 final double[] hardIron, final L listener) {
924 this(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, hardIron);
925 this.listener = listener;
926 }
927
928 /**
929 * Constructor.
930 *
931 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
932 * @param measurements collection of body magnetic flux density
933 * measurements with standard deviation of
934 * magnetometer measurements taken at the same
935 * position with zero velocity and unknown different
936 * orientations.
937 * @param hardIron known hard-iron.
938 * @throws IllegalArgumentException if provided magnetic flux norm value is
939 * negative, or if provided hard-iron matrix is not
940 * 3x1.
941 */
942 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
943 final Double groundTruthMagneticFluxDensityNorm,
944 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron) {
945 this(hardIron);
946 internalSetGroundTruthMagneticFluxDensityNorm(groundTruthMagneticFluxDensityNorm);
947 this.measurements = measurements;
948 }
949
950 /**
951 * Constructor.
952 *
953 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
954 * @param measurements collection of body magnetic flux density
955 * measurements with standard deviation of
956 * magnetometer measurements taken at the same
957 * position with zero velocity and unknown different
958 * orientations.
959 * @param hardIron known hard-iron.
960 * @param listener listener to handle events raised by this calibrator.
961 * @throws IllegalArgumentException if provided magnetic flux norm value is
962 * negative, or if provided hard-iron matrix is not
963 * 3x1.
964 */
965 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
966 final Double groundTruthMagneticFluxDensityNorm,
967 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
968 final L listener) {
969 this(groundTruthMagneticFluxDensityNorm, measurements, hardIron);
970 this.listener = listener;
971 }
972
973 /**
974 * Constructor.
975 *
976 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
977 * @param measurements collection of body magnetic flux density
978 * measurements with standard deviation of
979 * magnetometer measurements taken at the same
980 * position with zero velocity and unknown different
981 * orientations.
982 * @param commonAxisUsed indicates whether z-axis is assumed to be common
983 * for the accelerometer, gyroscope and magnetometer.
984 * @param hardIron known hard-iron.
985 * @throws IllegalArgumentException if provided magnetic flux norm value is
986 * negative, or if provided hard-iron matrix is not
987 * 3x1.
988 */
989 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
990 final Double groundTruthMagneticFluxDensityNorm,
991 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
992 final Matrix hardIron) {
993 this(groundTruthMagneticFluxDensityNorm, measurements, hardIron);
994 this.commonAxisUsed = commonAxisUsed;
995 }
996
997 /**
998 * Constructor.
999 *
1000 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
1001 * @param measurements collection of body magnetic flux density
1002 * measurements with standard deviation of
1003 * magnetometer measurements taken at the same
1004 * position with zero velocity and unknown different
1005 * orientations.
1006 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1007 * for the accelerometer, gyroscope and magnetometer.
1008 * @param hardIron known hard-iron.
1009 * @param listener listener to handle events raised by this calibrator.
1010 * @throws IllegalArgumentException if provided magnetic flux norm value is
1011 * negative, or if provided hard-iron matrix is not
1012 * 3x1.
1013 */
1014 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
1015 final Double groundTruthMagneticFluxDensityNorm,
1016 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
1017 final Matrix hardIron, final L listener) {
1018 this(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, hardIron);
1019 this.listener = listener;
1020 }
1021
1022 /**
1023 * Constructor.
1024 *
1025 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
1026 * @param measurements collection of body magnetic flux density
1027 * measurements with standard deviation of
1028 * magnetometer measurements taken at the same
1029 * position with zero velocity and unknown different
1030 * orientations.
1031 * @param hardIron known hard-iron.
1032 * @param initialMm initial soft-iron matrix containing scale factors
1033 * and cross coupling errors.
1034 * @throws IllegalArgumentException if provided magnetic flux norm value is
1035 * negative, or if provided hard-iron matrix is not
1036 * 3x1 or if soft-iron matrix is not
1037 * 3x3.
1038 */
1039 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
1040 final Double groundTruthMagneticFluxDensityNorm,
1041 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
1042 final Matrix initialMm) {
1043 this(hardIron, initialMm);
1044 internalSetGroundTruthMagneticFluxDensityNorm(groundTruthMagneticFluxDensityNorm);
1045 this.measurements = measurements;
1046 }
1047
1048 /**
1049 * Constructor.
1050 *
1051 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
1052 * @param measurements collection of body magnetic flux density
1053 * measurements with standard deviation of
1054 * magnetometer measurements taken at the same
1055 * position with zero velocity and unknown different
1056 * orientations.
1057 * @param hardIron known hard-iron.
1058 * @param initialMm initial soft-iron matrix containing scale factors
1059 * and cross coupling errors.
1060 * @param listener listener to handle events raised by this calibrator.
1061 * @throws IllegalArgumentException if provided magnetic flux norm value is
1062 * negative, or if provided hard-iron matrix is not
1063 * 3x1 or if soft-iron matrix is not
1064 * 3x3.
1065 */
1066 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
1067 final Double groundTruthMagneticFluxDensityNorm,
1068 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final Matrix hardIron,
1069 final Matrix initialMm, final L listener) {
1070 this(groundTruthMagneticFluxDensityNorm, measurements, hardIron, initialMm);
1071 this.listener = listener;
1072 }
1073
1074 /**
1075 * Constructor.
1076 *
1077 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
1078 * @param measurements collection of body magnetic flux density
1079 * measurements with standard deviation of
1080 * magnetometer measurements taken at the same
1081 * position with zero velocity and unknown different
1082 * orientations.
1083 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1084 * for the accelerometer, gyroscope and magnetometer.
1085 * @param hardIron known hard-iron.
1086 * @param initialMm initial soft-iron matrix containing scale factors
1087 * and cross coupling errors.
1088 * @throws IllegalArgumentException if provided magnetic flux norm value is
1089 * negative, or if provided hard-iron matrix is not
1090 * 3x1 or if soft-iron matrix is not
1091 * 3x3.
1092 */
1093 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
1094 final Double groundTruthMagneticFluxDensityNorm,
1095 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
1096 final Matrix hardIron, final Matrix initialMm) {
1097 this(groundTruthMagneticFluxDensityNorm, measurements, hardIron, initialMm);
1098 this.commonAxisUsed = commonAxisUsed;
1099 }
1100
1101 /**
1102 * Constructor.
1103 *
1104 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm expressed in Teslas (T).
1105 * @param measurements collection of body magnetic flux density
1106 * measurements with standard deviation of
1107 * magnetometer measurements taken at the same
1108 * position with zero velocity and unknown different
1109 * orientations.
1110 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1111 * for the accelerometer, gyroscope and magnetometer.
1112 * @param hardIron known hard-iron.
1113 * @param initialMm initial soft-iron matrix containing scale factors
1114 * and cross coupling errors.
1115 * @param listener listener to handle events raised by this calibrator.
1116 * @throws IllegalArgumentException if provided magnetic flux norm value is
1117 * negative, or if provided hard-iron matrix is not
1118 * 3x1 or if soft-iron matrix is not
1119 * 3x3.
1120 */
1121 protected BaseKnownHardIronMagneticFluxDensityNormMagnetometerCalibrator(
1122 final Double groundTruthMagneticFluxDensityNorm,
1123 final Collection<StandardDeviationBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
1124 final Matrix hardIron, final Matrix initialMm, final L listener) {
1125 this(groundTruthMagneticFluxDensityNorm, measurements, commonAxisUsed, hardIron, initialMm);
1126 this.listener = listener;
1127 }
1128
1129 /**
1130 * Gets ground truth magnetic flux density norm to be expected at location where measurements have been made,
1131 * expressed in Teslas (T).
1132 *
1133 * @return ground truth magnetic flux density or null.
1134 */
1135 public Double getGroundTruthMagneticFluxDensityNorm() {
1136 return groundTruthMagneticFluxDensityNorm;
1137 }
1138
1139 /**
1140 * Gets ground truth magnetic flux density norm to be expected at location where measurements have been made.
1141 *
1142 * @return ground truth magnetic flux density or null.
1143 */
1144 public MagneticFluxDensity getGroundTruthMagneticFluxDensityNormAsMagneticFluxDensity() {
1145 return groundTruthMagneticFluxDensityNorm != null
1146 ? new MagneticFluxDensity(groundTruthMagneticFluxDensityNorm, MagneticFluxDensityUnit.TESLA) : null;
1147 }
1148
1149 /**
1150 * Gets ground truth magnetic flux density norm to be expected at location where measurements have been made.
1151 *
1152 * @param result instance where result will be stored.
1153 * @return true if ground truth magnetic flux density norm has been defined, false if it is not available yet.
1154 */
1155 public boolean getGroundTruthMagneticFluxDensityNormAsMagneticFluxDensity(final MagneticFluxDensity result) {
1156 if (groundTruthMagneticFluxDensityNorm != null) {
1157 result.setValue(groundTruthMagneticFluxDensityNorm);
1158 result.setUnit(MagneticFluxDensityUnit.TESLA);
1159 return true;
1160 } else {
1161 return false;
1162 }
1163 }
1164
1165 /**
1166 * Gets known x-coordinate of magnetometer hard-iron bias.
1167 * This is expressed in Teslas (T).
1168 *
1169 * @return known x-coordinate of magnetometer hard-iron bias.
1170 */
1171 @Override
1172 public double getHardIronX() {
1173 return hardIronX;
1174 }
1175
1176 /**
1177 * Sets known x-coordinate of magnetometer hard-iron bias.
1178 * This is expressed in Teslas (T).
1179 *
1180 * @param hardIronX known x-coordinate of magnetometer
1181 * hard-iron bias.
1182 * @throws LockedException if calibrator is currently running.
1183 */
1184 @Override
1185 public void setHardIronX(final double hardIronX) throws LockedException {
1186 if (running) {
1187 throw new LockedException();
1188 }
1189 this.hardIronX = hardIronX;
1190 }
1191
1192 /**
1193 * Gets known y-coordinate of magnetometer hard-iron bias.
1194 * This is expressed in Teslas (T).
1195 *
1196 * @return known y-coordinate of magnetometer hard-iron bias.
1197 */
1198 @Override
1199 public double getHardIronY() {
1200 return hardIronY;
1201 }
1202
1203 /**
1204 * Sets known y-coordinate of magnetometer hard-iron bias.
1205 * This is expressed in Teslas (T).
1206 *
1207 * @param hardIronY known y-coordinate of magnetometer
1208 * hard-iron bias.
1209 * @throws LockedException if calibrator is currently running.
1210 */
1211 @Override
1212 public void setHardIronY(final double hardIronY) throws LockedException {
1213 if (running) {
1214 throw new LockedException();
1215 }
1216 this.hardIronY = hardIronY;
1217 }
1218
1219 /**
1220 * Gets known z-coordinate of magnetometer hard-iron bias.
1221 * This is expressed in Teslas (T).
1222 *
1223 * @return known z-coordinate of magnetometer hard-iron bias.
1224 */
1225 @Override
1226 public double getHardIronZ() {
1227 return hardIronZ;
1228 }
1229
1230 /**
1231 * Sets known z-coordinate of magnetometer hard-iron bias.
1232 * This is expressed in meters Teslas (T).
1233 *
1234 * @param hardIronZ known z-coordinate of magnetometer
1235 * hard-iron bias.
1236 * @throws LockedException if calibrator is currently running.
1237 */
1238 @Override
1239 public void setHardIronZ(final double hardIronZ) throws LockedException {
1240 if (running) {
1241 throw new LockedException();
1242 }
1243 this.hardIronZ = hardIronZ;
1244 }
1245
1246 /**
1247 * Gets known x coordinate of magnetometer hard-iron.
1248 *
1249 * @return x coordinate of magnetometer hard-iron.
1250 */
1251 @Override
1252 public MagneticFluxDensity getHardIronXAsMagneticFluxDensity() {
1253 return new MagneticFluxDensity(hardIronX, MagneticFluxDensityUnit.TESLA);
1254 }
1255
1256 /**
1257 * Gets known x coordinate of magnetometer hard-iron.
1258 *
1259 * @param result instance where result will be stored.
1260 */
1261 @Override
1262 public void getHardIronXAsMagneticFluxDensity(final MagneticFluxDensity result) {
1263 result.setValue(hardIronX);
1264 result.setUnit(MagneticFluxDensityUnit.TESLA);
1265 }
1266
1267 /**
1268 * Sets known x-coordinate of magnetometer hard-iron.
1269 *
1270 * @param hardIronX known x-coordinate of magnetometer hard-iron.
1271 * @throws LockedException if calibrator is currently running.
1272 */
1273 @Override
1274 public void setHardIronX(final MagneticFluxDensity hardIronX) throws LockedException {
1275 if (running) {
1276 throw new LockedException();
1277 }
1278 this.hardIronX = convertMagneticFluxDensity(hardIronX);
1279 }
1280
1281 /**
1282 * Gets known y coordinate of magnetometer hard-iron.
1283 *
1284 * @return y coordinate of magnetometer hard-iron.
1285 */
1286 @Override
1287 public MagneticFluxDensity getHardIronYAsMagneticFluxDensity() {
1288 return new MagneticFluxDensity(hardIronY, MagneticFluxDensityUnit.TESLA);
1289 }
1290
1291 /**
1292 * Gets known y coordinate of magnetometer hard-iron.
1293 *
1294 * @param result instance where result will be stored.
1295 */
1296 @Override
1297 public void getHardIronYAsMagneticFluxDensity(final MagneticFluxDensity result) {
1298 result.setValue(hardIronY);
1299 result.setUnit(MagneticFluxDensityUnit.TESLA);
1300 }
1301
1302 /**
1303 * Sets known y-coordinate of magnetometer hard-iron.
1304 *
1305 * @param hardIronY known y-coordinate of magnetometer hard-iron.
1306 * @throws LockedException if calibrator is currently running.
1307 */
1308 @Override
1309 public void setHardIronY(final MagneticFluxDensity hardIronY) throws LockedException {
1310 if (running) {
1311 throw new LockedException();
1312 }
1313 this.hardIronY = convertMagneticFluxDensity(hardIronY);
1314 }
1315
1316 /**
1317 * Gets known z coordinate of magnetometer hard-iron.
1318 *
1319 * @return z coordinate of magnetometer hard-iron.
1320 */
1321 @Override
1322 public MagneticFluxDensity getHardIronZAsMagneticFluxDensity() {
1323 return new MagneticFluxDensity(hardIronZ, MagneticFluxDensityUnit.TESLA);
1324 }
1325
1326 /**
1327 * Gets known z coordinate of magnetometer hard-iron.
1328 *
1329 * @param result instance where result will be stored.
1330 */
1331 @Override
1332 public void getHardIronZAsMagneticFluxDensity(final MagneticFluxDensity result) {
1333 result.setValue(hardIronZ);
1334 result.setUnit(MagneticFluxDensityUnit.TESLA);
1335 }
1336
1337 /**
1338 * Sets known z-coordinate of magnetometer hard-iron.
1339 *
1340 * @param hardIronZ known z-coordinate of magnetometer hard-iron.
1341 * @throws LockedException if calibrator is currently running.
1342 */
1343 @Override
1344 public void setHardIronZ(final MagneticFluxDensity hardIronZ) throws LockedException {
1345 if (running) {
1346 throw new LockedException();
1347 }
1348 this.hardIronZ = convertMagneticFluxDensity(hardIronZ);
1349 }
1350
1351 /**
1352 * Sets known hard-iron bias coordinates of magnetometer expressed in
1353 * Teslas (T).
1354 *
1355 * @param hardIronX x-coordinate of magnetometer
1356 * known hard-iron bias.
1357 * @param hardIronY y-coordinate of magnetometer
1358 * known hard-iron bias.
1359 * @param hardIronZ z-coordinate of magnetometer
1360 * known hard-iron bias.
1361 * @throws LockedException if calibrator is currently running.
1362 */
1363 @Override
1364 public void setHardIronCoordinates(
1365 final double hardIronX, final double hardIronY, final double hardIronZ) throws LockedException {
1366 if (running) {
1367 throw new LockedException();
1368 }
1369 this.hardIronX = hardIronX;
1370 this.hardIronY = hardIronY;
1371 this.hardIronZ = hardIronZ;
1372 }
1373
1374 /**
1375 * Sets known hard-iron coordinates.
1376 *
1377 * @param hardIronX x-coordinate of magnetometer hard-iron.
1378 * @param hardIronY y-coordinate of magnetometer hard-iron.
1379 * @param hardIronZ z-coordinate of magnetometer hard-iron.
1380 * @throws LockedException if calibrator is currently running.
1381 */
1382 @Override
1383 public void setHardIronCoordinates(
1384 final MagneticFluxDensity hardIronX, final MagneticFluxDensity hardIronY,
1385 final MagneticFluxDensity hardIronZ) throws LockedException {
1386 if (running) {
1387 throw new LockedException();
1388 }
1389 this.hardIronX = convertMagneticFluxDensity(hardIronX);
1390 this.hardIronY = convertMagneticFluxDensity(hardIronY);
1391 this.hardIronZ = convertMagneticFluxDensity(hardIronZ);
1392 }
1393
1394 /**
1395 * Gets known hard-iron.
1396 *
1397 * @return known hard-iron.
1398 */
1399 @Override
1400 public MagneticFluxDensityTriad getHardIronAsTriad() {
1401 return new MagneticFluxDensityTriad(MagneticFluxDensityUnit.TESLA, hardIronX, hardIronY, hardIronZ);
1402 }
1403
1404 /**
1405 * Gets known hard-iron.
1406 *
1407 * @param result instance where result will be stored.
1408 */
1409 @Override
1410 public void getHardIronAsTriad(final MagneticFluxDensityTriad result) {
1411 result.setValueCoordinatesAndUnit(hardIronX, hardIronY, hardIronZ, MagneticFluxDensityUnit.TESLA);
1412 }
1413
1414 /**
1415 * Sets known hard-iron.
1416 *
1417 * @param hardIron hard-iron to be set.
1418 * @throws LockedException if calibrator is currently running.
1419 */
1420 @Override
1421 public void setHardIron(final MagneticFluxDensityTriad hardIron) throws LockedException {
1422 if (running) {
1423 throw new LockedException();
1424 }
1425
1426 hardIronX = convertMagneticFluxDensity(hardIron.getValueX(), hardIron.getUnit());
1427 hardIronY = convertMagneticFluxDensity(hardIron.getValueY(), hardIron.getUnit());
1428 hardIronZ = convertMagneticFluxDensity(hardIron.getValueZ(), hardIron.getUnit());
1429 }
1430
1431 /**
1432 * Gets initial x scaling factor.
1433 *
1434 * @return initial x scaling factor.
1435 */
1436 @Override
1437 public double getInitialSx() {
1438 return initialSx;
1439 }
1440
1441 /**
1442 * Sets initial x scaling factor.
1443 *
1444 * @param initialSx initial x scaling factor.
1445 * @throws LockedException if calibrator is currently running.
1446 */
1447 @Override
1448 public void setInitialSx(final double initialSx) throws LockedException {
1449 if (running) {
1450 throw new LockedException();
1451 }
1452 this.initialSx = initialSx;
1453 }
1454
1455 /**
1456 * Gets initial y scaling factor.
1457 *
1458 * @return initial y scaling factor.
1459 */
1460 @Override
1461 public double getInitialSy() {
1462 return initialSy;
1463 }
1464
1465 /**
1466 * Sets initial y scaling factor.
1467 *
1468 * @param initialSy initial y scaling factor.
1469 * @throws LockedException if calibrator is currently running.
1470 */
1471 @Override
1472 public void setInitialSy(final double initialSy) throws LockedException {
1473 if (running) {
1474 throw new LockedException();
1475 }
1476 this.initialSy = initialSy;
1477 }
1478
1479 /**
1480 * Gets initial z scaling factor.
1481 *
1482 * @return initial z scaling factor.
1483 */
1484 @Override
1485 public double getInitialSz() {
1486 return initialSz;
1487 }
1488
1489 /**
1490 * Sets initial z scaling factor.
1491 *
1492 * @param initialSz initial z scaling factor.
1493 * @throws LockedException if calibrator is currently running.
1494 */
1495 @Override
1496 public void setInitialSz(final double initialSz) throws LockedException {
1497 if (running) {
1498 throw new LockedException();
1499 }
1500 this.initialSz = initialSz;
1501 }
1502
1503 /**
1504 * Gets initial x-y cross coupling error.
1505 *
1506 * @return initial x-y cross coupling error.
1507 */
1508 @Override
1509 public double getInitialMxy() {
1510 return initialMxy;
1511 }
1512
1513 /**
1514 * Sets initial x-y cross coupling error.
1515 *
1516 * @param initialMxy initial x-y cross coupling error.
1517 * @throws LockedException if calibrator is currently running.
1518 */
1519 @Override
1520 public void setInitialMxy(final double initialMxy) throws LockedException {
1521 if (running) {
1522 throw new LockedException();
1523 }
1524 this.initialMxy = initialMxy;
1525 }
1526
1527 /**
1528 * Gets initial x-z cross coupling error.
1529 *
1530 * @return initial x-z cross coupling error.
1531 */
1532 @Override
1533 public double getInitialMxz() {
1534 return initialMxz;
1535 }
1536
1537 /**
1538 * Sets initial x-z cross coupling error.
1539 *
1540 * @param initialMxz initial x-z cross coupling error.
1541 * @throws LockedException if calibrator is currently running.
1542 */
1543 @Override
1544 public void setInitialMxz(final double initialMxz) throws LockedException {
1545 if (running) {
1546 throw new LockedException();
1547 }
1548 this.initialMxz = initialMxz;
1549 }
1550
1551 /**
1552 * Gets initial y-x cross coupling error.
1553 *
1554 * @return initial y-x cross coupling error.
1555 */
1556 @Override
1557 public double getInitialMyx() {
1558 return initialMyx;
1559 }
1560
1561 /**
1562 * Sets initial y-x cross coupling error.
1563 *
1564 * @param initialMyx initial y-x cross coupling error.
1565 * @throws LockedException if calibrator is currently running.
1566 */
1567 @Override
1568 public void setInitialMyx(final double initialMyx) throws LockedException {
1569 if (running) {
1570 throw new LockedException();
1571 }
1572 this.initialMyx = initialMyx;
1573 }
1574
1575 /**
1576 * Gets initial y-z cross coupling error.
1577 *
1578 * @return initial y-z cross coupling error.
1579 */
1580 @Override
1581 public double getInitialMyz() {
1582 return initialMyz;
1583 }
1584
1585 /**
1586 * Sets initial y-z cross coupling error.
1587 *
1588 * @param initialMyz initial y-z cross coupling error.
1589 * @throws LockedException if calibrator is currently running.
1590 */
1591 @Override
1592 public void setInitialMyz(final double initialMyz) throws LockedException {
1593 if (running) {
1594 throw new LockedException();
1595 }
1596 this.initialMyz = initialMyz;
1597 }
1598
1599 /**
1600 * Gets initial z-x cross coupling error.
1601 *
1602 * @return initial z-x cross coupling error.
1603 */
1604 @Override
1605 public double getInitialMzx() {
1606 return initialMzx;
1607 }
1608
1609 /**
1610 * Sets initial z-x cross coupling error.
1611 *
1612 * @param initialMzx initial z-x cross coupling error.
1613 * @throws LockedException if calibrator is currently running.
1614 */
1615 @Override
1616 public void setInitialMzx(final double initialMzx) throws LockedException {
1617 if (running) {
1618 throw new LockedException();
1619 }
1620 this.initialMzx = initialMzx;
1621 }
1622
1623 /**
1624 * Gets initial z-y cross coupling error.
1625 *
1626 * @return initial z-y cross coupling error.
1627 */
1628 @Override
1629 public double getInitialMzy() {
1630 return initialMzy;
1631 }
1632
1633 /**
1634 * Sets initial z-y cross coupling error.
1635 *
1636 * @param initialMzy initial z-y cross coupling error.
1637 * @throws LockedException if calibrator is currently running.
1638 */
1639 @Override
1640 public void setInitialMzy(final double initialMzy) throws LockedException {
1641 if (running) {
1642 throw new LockedException();
1643 }
1644 this.initialMzy = initialMzy;
1645 }
1646
1647 /**
1648 * Sets initial scaling factors.
1649 *
1650 * @param initialSx initial x scaling factor.
1651 * @param initialSy initial y scaling factor.
1652 * @param initialSz initial z scaling factor.
1653 * @throws LockedException if calibrator is currently running.
1654 */
1655 @Override
1656 public void setInitialScalingFactors(
1657 final double initialSx, final double initialSy, final double initialSz) throws LockedException {
1658 if (running) {
1659 throw new LockedException();
1660 }
1661 this.initialSx = initialSx;
1662 this.initialSy = initialSy;
1663 this.initialSz = initialSz;
1664 }
1665
1666 /**
1667 * Sets initial cross coupling errors.
1668 *
1669 * @param initialMxy initial x-y cross coupling error.
1670 * @param initialMxz initial x-z cross coupling error.
1671 * @param initialMyx initial y-x cross coupling error.
1672 * @param initialMyz initial y-z cross coupling error.
1673 * @param initialMzx initial z-x cross coupling error.
1674 * @param initialMzy initial z-y cross coupling error.
1675 * @throws LockedException if calibrator is currently running.
1676 */
1677 @Override
1678 public void setInitialCrossCouplingErrors(
1679 final double initialMxy, final double initialMxz, final double initialMyx,
1680 final double initialMyz, final double initialMzx, final double initialMzy) throws LockedException {
1681 if (running) {
1682 throw new LockedException();
1683 }
1684 this.initialMxy = initialMxy;
1685 this.initialMxz = initialMxz;
1686 this.initialMyx = initialMyx;
1687 this.initialMyz = initialMyz;
1688 this.initialMzx = initialMzx;
1689 this.initialMzy = initialMzy;
1690 }
1691
1692 /**
1693 * Sets initial scaling factors and cross coupling errors.
1694 *
1695 * @param initialSx initial x scaling factor.
1696 * @param initialSy initial y scaling factor.
1697 * @param initialSz initial z scaling factor.
1698 * @param initialMxy initial x-y cross coupling error.
1699 * @param initialMxz initial x-z cross coupling error.
1700 * @param initialMyx initial y-x cross coupling error.
1701 * @param initialMyz initial y-z cross coupling error.
1702 * @param initialMzx initial z-x cross coupling error.
1703 * @param initialMzy initial z-y cross coupling error.
1704 * @throws LockedException if calibrator is currently running.
1705 */
1706 @Override
1707 public void setInitialScalingFactorsAndCrossCouplingErrors(
1708 final double initialSx, final double initialSy, final double initialSz,
1709 final double initialMxy, final double initialMxz, final double initialMyx,
1710 final double initialMyz, final double initialMzx, final double initialMzy) throws LockedException {
1711 if (running) {
1712 throw new LockedException();
1713 }
1714 setInitialScalingFactors(initialSx, initialSy, initialSz);
1715 setInitialCrossCouplingErrors(initialMxy, initialMxz, initialMyx, initialMyz, initialMzx, initialMzy);
1716 }
1717
1718 /**
1719 * Gets known hard-iron bias as an array.
1720 * Array values are expressed in Teslas (T).
1721 *
1722 * @return array containing coordinates of known bias.
1723 */
1724 @Override
1725 public double[] getHardIron() {
1726 final var result = new double[BodyMagneticFluxDensity.COMPONENTS];
1727 getHardIron(result);
1728 return result;
1729 }
1730
1731 /**
1732 * Gets known hard-iron bias as an array.
1733 * Array values are expressed in Teslas (T).
1734 *
1735 * @param result instance where result data will be copied to.
1736 * @throws IllegalArgumentException if provided array does not have
1737 * length 3.
1738 */
1739 @Override
1740 public void getHardIron(final double[] result) {
1741 if (result.length != BodyMagneticFluxDensity.COMPONENTS) {
1742 throw new IllegalArgumentException();
1743 }
1744 result[0] = hardIronX;
1745 result[1] = hardIronY;
1746 result[2] = hardIronZ;
1747 }
1748
1749 /**
1750 * Sets known hard-iron bias as an array.
1751 * Array values are expressed in Teslas (T).
1752 *
1753 * @param hardIron known hard-iron.
1754 * @throws LockedException if calibrator is currently running.
1755 * @throws IllegalArgumentException if provided array does not have length 3.
1756 */
1757 @Override
1758 public void setHardIron(final double[] hardIron) throws LockedException {
1759 if (running) {
1760 throw new LockedException();
1761 }
1762
1763 if (hardIron.length != BodyMagneticFluxDensity.COMPONENTS) {
1764 throw new IllegalArgumentException();
1765 }
1766 hardIronX = hardIron[0];
1767 hardIronY = hardIron[1];
1768 hardIronZ = hardIron[2];
1769 }
1770
1771 /**
1772 * Gets known hard-iron bias as a column matrix.
1773 *
1774 * @return known hard-iron bias as a column matrix.
1775 */
1776 @Override
1777 public Matrix getHardIronMatrix() {
1778 Matrix result;
1779 try {
1780 result = new Matrix(BodyMagneticFluxDensity.COMPONENTS, 1);
1781 getHardIronMatrix(result);
1782 } catch (final WrongSizeException ignore) {
1783 // never happens
1784 result = null;
1785 }
1786 return result;
1787 }
1788
1789 /**
1790 * Gets known hard-iron bias as a column matrix.
1791 *
1792 * @param result instance where result data will be copied to.
1793 * @throws IllegalArgumentException if provided matrix is not 3x1.
1794 */
1795 @Override
1796 public void getHardIronMatrix(final Matrix result) {
1797 if (result.getRows() != BodyMagneticFluxDensity.COMPONENTS || result.getColumns() != 1) {
1798 throw new IllegalArgumentException();
1799 }
1800 result.setElementAtIndex(0, hardIronX);
1801 result.setElementAtIndex(1, hardIronY);
1802 result.setElementAtIndex(2, hardIronZ);
1803 }
1804
1805 /**
1806 * Sets known hard-iron bias as a column matrix.
1807 *
1808 * @param hardIron known hard-iron bias.
1809 * @throws LockedException if calibrator is currently running.
1810 * @throws IllegalArgumentException if provided matrix is not 3x1.
1811 */
1812 @Override
1813 public void setHardIron(final Matrix hardIron) throws LockedException {
1814 if (running) {
1815 throw new LockedException();
1816 }
1817 if (hardIron.getRows() != BodyMagneticFluxDensity.COMPONENTS || hardIron.getColumns() != 1) {
1818 throw new IllegalArgumentException();
1819 }
1820
1821 hardIronX = hardIron.getElementAtIndex(0);
1822 hardIronY = hardIron.getElementAtIndex(1);
1823 hardIronZ = hardIron.getElementAtIndex(2);
1824 }
1825
1826 /**
1827 * Gets initial scale factors and cross coupling errors matrix.
1828 *
1829 * @return initial scale factors and cross coupling errors matrix.
1830 */
1831 @Override
1832 public Matrix getInitialMm() {
1833 Matrix result;
1834 try {
1835 result = new Matrix(BodyMagneticFluxDensity.COMPONENTS, BodyMagneticFluxDensity.COMPONENTS);
1836 getInitialMm(result);
1837 } catch (final WrongSizeException ignore) {
1838 // never happens
1839 result = null;
1840 }
1841 return result;
1842 }
1843
1844 /**
1845 * Gets initial scale factors and cross coupling errors matrix.
1846 *
1847 * @param result instance where data will be stored.
1848 * @throws IllegalArgumentException if provided matrix is not 3x3.
1849 */
1850 @Override
1851 public void getInitialMm(final Matrix result) {
1852 if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != BodyKinematics.COMPONENTS) {
1853 throw new IllegalArgumentException();
1854 }
1855 result.setElementAtIndex(0, initialSx);
1856 result.setElementAtIndex(1, initialMyx);
1857 result.setElementAtIndex(2, initialMzx);
1858
1859 result.setElementAtIndex(3, initialMxy);
1860 result.setElementAtIndex(4, initialSy);
1861 result.setElementAtIndex(5, initialMzy);
1862
1863 result.setElementAtIndex(6, initialMxz);
1864 result.setElementAtIndex(7, initialMyz);
1865 result.setElementAtIndex(8, initialSz);
1866 }
1867
1868 /**
1869 * Sets initial scale factors and cross coupling errors matrix.
1870 *
1871 * @param initialMm initial scale factors and cross coupling errors matrix.
1872 * @throws IllegalArgumentException if provided matrix is not 3x3.
1873 * @throws LockedException if calibrator is currently running.
1874 */
1875 @Override
1876 public void setInitialMm(final Matrix initialMm) throws LockedException {
1877 if (running) {
1878 throw new LockedException();
1879 }
1880 if (initialMm.getRows() != BodyKinematics.COMPONENTS || initialMm.getColumns() != BodyKinematics.COMPONENTS) {
1881 throw new IllegalArgumentException();
1882 }
1883
1884 initialSx = initialMm.getElementAtIndex(0);
1885 initialMyx = initialMm.getElementAtIndex(1);
1886 initialMzx = initialMm.getElementAtIndex(2);
1887
1888 initialMxy = initialMm.getElementAtIndex(3);
1889 initialSy = initialMm.getElementAtIndex(4);
1890 initialMzy = initialMm.getElementAtIndex(5);
1891
1892 initialMxz = initialMm.getElementAtIndex(6);
1893 initialMyz = initialMm.getElementAtIndex(7);
1894 initialSz = initialMm.getElementAtIndex(8);
1895 }
1896
1897 /**
1898 * Gets collection of body magnetic flux density measurements taken
1899 * at a given position with different unknown orientations and containing the
1900 * standard deviation of magnetometer measurements.
1901 *
1902 * @return collection of body magnetic flux density measurements at
1903 * a known position and timestamp with unknown orientations.
1904 */
1905 @Override
1906 public Collection<StandardDeviationBodyMagneticFluxDensity> getMeasurements() {
1907 return measurements;
1908 }
1909
1910 /**
1911 * Sets collection of body magnetic flux density measurements taken
1912 * at a given position with different unknown orientations and containing the
1913 * standard deviation of magnetometer measurements.
1914 *
1915 * @param measurements collection of body magnetic flux density
1916 * measurements at a known position and timestamp
1917 * with unknown orientations.
1918 * @throws LockedException if calibrator is currently running.
1919 */
1920 @Override
1921 public void setMeasurements(final Collection<StandardDeviationBodyMagneticFluxDensity> measurements)
1922 throws LockedException {
1923 if (running) {
1924 throw new LockedException();
1925 }
1926 this.measurements = measurements;
1927 }
1928
1929 /**
1930 * Indicates the type of measurement used by this calibrator.
1931 *
1932 * @return type of measurement used by this calibrator.
1933 */
1934 @Override
1935 public MagnetometerCalibratorMeasurementType getMeasurementType() {
1936 return MagnetometerCalibratorMeasurementType.STANDARD_DEVIATION_BODY_MAGNETIC_FLUX_DENSITY;
1937 }
1938
1939 /**
1940 * Indicates whether this calibrator requires ordered measurements in a
1941 * list or not.
1942 *
1943 * @return true if measurements must be ordered, false otherwise.
1944 */
1945 @Override
1946 public boolean isOrderedMeasurementsRequired() {
1947 return false;
1948 }
1949
1950 /**
1951 * Indicates whether this calibrator requires quality scores for each
1952 * measurement or not.
1953 *
1954 * @return true if quality scores are required, false otherwise.
1955 */
1956 @Override
1957 public boolean isQualityScoresRequired() {
1958 return false;
1959 }
1960
1961 /**
1962 * Indicates whether z-axis is assumed to be common for accelerometer,
1963 * gyroscope and magnetometer.
1964 * When enabled, this eliminates 3 variables from Mm (soft-iron) matrix.
1965 *
1966 * @return true if z-axis is assumed to be common for accelerometer,
1967 * gyroscope and magnetometer, false otherwise.
1968 */
1969 @Override
1970 public boolean isCommonAxisUsed() {
1971 return commonAxisUsed;
1972 }
1973
1974 /**
1975 * Specifies whether z-axis is assumed to be common for accelerometer and
1976 * gyroscope.
1977 * When enabled, this eliminates 3 variables from Mm matrix.
1978 *
1979 * @param commonAxisUsed true if z-axis is assumed to be common for
1980 * accelerometer, gyroscope and magnetometer, false
1981 * otherwise.
1982 * @throws LockedException if estimator is currently running.
1983 */
1984 @Override
1985 public void setCommonAxisUsed(final boolean commonAxisUsed) throws LockedException {
1986 if (running) {
1987 throw new LockedException();
1988 }
1989
1990 this.commonAxisUsed = commonAxisUsed;
1991 }
1992
1993 /**
1994 * Gets listener to handle events raised by this calibrator.
1995 *
1996 * @return listener to handle events raised by this calibrator.
1997 */
1998 public L getListener() {
1999 return listener;
2000 }
2001
2002 /**
2003 * Sets listener to handle events raised by this calibrator.
2004 *
2005 * @param listener listener to handle events raised by this calibrator.
2006 * @throws LockedException if calibrator is currently running.
2007 */
2008 public void setListener(final L listener) throws LockedException {
2009 if (running) {
2010 throw new LockedException();
2011 }
2012
2013 this.listener = listener;
2014 }
2015
2016 /**
2017 * Gets minimum number of required measurements.
2018 *
2019 * @return minimum number of required measurements.
2020 */
2021 @Override
2022 public int getMinimumRequiredMeasurements() {
2023 return commonAxisUsed ? MINIMUM_MEASUREMENTS_COMMON_Z_AXIS : MINIMUM_MEASUREMENTS_GENERAL;
2024 }
2025
2026 /**
2027 * Indicates whether calibrator is ready to start.
2028 *
2029 * @return true if calibrator is ready, false otherwise.
2030 */
2031 @Override
2032 public boolean isReady() {
2033 return measurements != null && measurements.size() >= getMinimumRequiredMeasurements();
2034 }
2035
2036 /**
2037 * Indicates whether calibrator is currently running or not.
2038 *
2039 * @return true if calibrator is running, false otherwise.
2040 */
2041 @Override
2042 public boolean isRunning() {
2043 return running;
2044 }
2045
2046 /**
2047 * Estimates magnetometer calibration parameters containing scale factors
2048 * and cross-coupling errors.
2049 *
2050 * @throws LockedException if calibrator is currently running.
2051 * @throws NotReadyException if calibrator is not ready.
2052 * @throws CalibrationException if calibration fails for numerical reasons.
2053 */
2054 @Override
2055 public void calibrate() throws LockedException, NotReadyException, CalibrationException {
2056 if (running) {
2057 throw new LockedException();
2058 }
2059
2060 if (!isReady()) {
2061 throw new NotReadyException();
2062 }
2063
2064 try {
2065 running = true;
2066
2067 onBeforeCalibrate();
2068
2069 if (listener != null) {
2070 //noinspection unchecked
2071 listener.onCalibrateStart((C) this);
2072 }
2073
2074 if (commonAxisUsed) {
2075 calibrateCommonAxis();
2076 } else {
2077 calibrateGeneral();
2078 }
2079
2080 if (listener != null) {
2081 //noinspection unchecked
2082 listener.onCalibrateEnd((C) this);
2083 }
2084
2085 } catch (final AlgebraException | FittingException | com.irurueta.numerical.NotReadyException e) {
2086 throw new CalibrationException(e);
2087 } finally {
2088 running = false;
2089 }
2090 }
2091
2092 /**
2093 * Gets estimated magnetometer soft-iron matrix containing scale factors
2094 * and cross coupling errors.
2095 * This is the product of matrix Tm containing cross coupling errors and Km
2096 * containing scaling factors.
2097 * So tat:
2098 * <pre>
2099 * Mm = [sx mxy mxz] = Tm*Km
2100 * [myx sy myz]
2101 * [mzx mzy sz ]
2102 * </pre>
2103 * Where:
2104 * <pre>
2105 * Km = [sx 0 0 ]
2106 * [0 sy 0 ]
2107 * [0 0 sz]
2108 * </pre>
2109 * and
2110 * <pre>
2111 * Tm = [1 -alphaXy alphaXz ]
2112 * [alphaYx 1 -alphaYz]
2113 * [-alphaZx alphaZy 1 ]
2114 * </pre>
2115 * Hence:
2116 * <pre>
2117 * Mm = [sx mxy mxz] = Tm*Km = [sx -sy * alphaXy sz * alphaXz ]
2118 * [myx sy myz] [sx * alphaYx sy -sz * alphaYz]
2119 * [mzx mzy sz ] [-sx * alphaZx sy * alphaZy sz ]
2120 * </pre>
2121 * This instance allows any 3x3 matrix however, typically alphaYx, alphaZx and alphaZy
2122 * are considered to be zero if the accelerometer z-axis is assumed to be the same
2123 * as the body z-axis. When this is assumed, myx = mzx = mzy = 0 and the Mm matrix
2124 * becomes upper diagonal:
2125 * <pre>
2126 * Mm = [sx mxy mxz]
2127 * [0 sy myz]
2128 * [0 0 sz ]
2129 * </pre>
2130 * Values of this matrix are unit-less.
2131 *
2132 * @return estimated magnetometer soft-iron scale factors and cross coupling errors,
2133 * or null if not available.
2134 */
2135 @Override
2136 public Matrix getEstimatedMm() {
2137 return estimatedMm;
2138 }
2139
2140 /**
2141 * Gets estimated x-axis scale factor.
2142 *
2143 * @return estimated x-axis scale factor or null if not available.
2144 */
2145 @Override
2146 public Double getEstimatedSx() {
2147 return estimatedMm != null ? estimatedMm.getElementAt(0, 0) : null;
2148 }
2149
2150 /**
2151 * Gets estimated y-axis scale factor.
2152 *
2153 * @return estimated y-axis scale factor or null if not available.
2154 */
2155 @Override
2156 public Double getEstimatedSy() {
2157 return estimatedMm != null ? estimatedMm.getElementAt(1, 1) : null;
2158 }
2159
2160 /**
2161 * Gets estimated z-axis scale factor.
2162 *
2163 * @return estimated z-axis scale factor or null if not available.
2164 */
2165 @Override
2166 public Double getEstimatedSz() {
2167 return estimatedMm != null ? estimatedMm.getElementAt(2, 2) : null;
2168 }
2169
2170 /**
2171 * Gets estimated x-y cross-coupling error.
2172 *
2173 * @return estimated x-y cross-coupling error or null if not available.
2174 */
2175 @Override
2176 public Double getEstimatedMxy() {
2177 return estimatedMm != null ? estimatedMm.getElementAt(0, 1) : null;
2178 }
2179
2180 /**
2181 * Gets estimated x-z cross-coupling error.
2182 *
2183 * @return estimated x-z cross-coupling error or null if not available.
2184 */
2185 @Override
2186 public Double getEstimatedMxz() {
2187 return estimatedMm != null ? estimatedMm.getElementAt(0, 2) : null;
2188 }
2189
2190 /**
2191 * Gets estimated y-x cross-coupling error.
2192 *
2193 * @return estimated y-x cross-coupling error or null if not available.
2194 */
2195 @Override
2196 public Double getEstimatedMyx() {
2197 return estimatedMm != null ? estimatedMm.getElementAt(1, 0) : null;
2198 }
2199
2200 /**
2201 * Gets estimated y-z cross-coupling error.
2202 *
2203 * @return estimated y-z cross-coupling error or null if not available.
2204 */
2205 @Override
2206 public Double getEstimatedMyz() {
2207 return estimatedMm != null ? estimatedMm.getElementAt(1, 2) : null;
2208 }
2209
2210 /**
2211 * Gets estimated z-x cross-coupling error.
2212 *
2213 * @return estimated z-x cross-coupling error or null if not available.
2214 */
2215 @Override
2216 public Double getEstimatedMzx() {
2217 return estimatedMm != null ? estimatedMm.getElementAt(2, 0) : null;
2218 }
2219
2220 /**
2221 * Gets estimated z-y cross-coupling error.
2222 *
2223 * @return estimated z-y cross-coupling error or null if not available.
2224 */
2225 @Override
2226 public Double getEstimatedMzy() {
2227 return estimatedMm != null ? estimatedMm.getElementAt(2, 1) : null;
2228 }
2229
2230 /**
2231 * Gets estimated covariance matrix for estimated calibration parameters.
2232 * Diagonal elements of the matrix contains variance for the following
2233 * parameters (following indicated order): sx, sy, sz, mxy, mxz, myx,
2234 * myz, mzx, mzy.
2235 *
2236 * @return estimated covariance matrix for estimated position.
2237 */
2238 @Override
2239 public Matrix getEstimatedCovariance() {
2240 return estimatedCovariance;
2241 }
2242
2243 /**
2244 * Gets estimated chi square value.
2245 *
2246 * @return estimated chi square value.
2247 */
2248 @Override
2249 public double getEstimatedChiSq() {
2250 return estimatedChiSq;
2251 }
2252
2253 /**
2254 * Gets estimated chi square degrees of freedom. Degrees of freedom is equal to the number of sampled data minus the
2255 * number of estimated parameters.
2256 *
2257 * @return estimated degrees of freedom of chi square value
2258 */
2259 @Override
2260 public int getEstimatedChiSqDegreesOfFreedom() {
2261 return estimatedChiSqDegreesOfFreedom;
2262 }
2263
2264 /**
2265 * Gets estimated reduced chi square value. This is equal to estimated chi square value divided by its degrees of
2266 * freedom. Ideally this value should be close to 1.0, indicating that fit is optimal.
2267 * A value larger than 1.0 indicates that fit is not good or noise has been underestimated, and a value smaller than
2268 * 1.0 indicates that there is overfitting or noise has been overestimated.
2269 *
2270 * @return estimated reduced chi square value
2271 */
2272 @Override
2273 public double getEstimatedReducedChiSq() {
2274 return estimatedReducedChiSq;
2275 }
2276
2277 /**
2278 * Gets estimated mean square error respect to provided measurements.
2279 *
2280 * @return estimated mean square error respect to provided measurements.
2281 */
2282 @Override
2283 public double getEstimatedMse() {
2284 return estimatedMse;
2285 }
2286
2287 /**
2288 * Gets estimated probability of finding a smaller chi square value expressed as a value between 0.0 and 1.0. The
2289 * smaller the found chi square value is, the better the fit of the estimated parameters to the actual parameter.
2290 * Thus, the smaller the chance of finding a smaller chi square value, then the better the estimated fit is.
2291 *
2292 * @return estimated probability of finding a smaller chi square value.
2293 */
2294 @Override
2295 public double getEstimatedP() {
2296 return estimatedP;
2297 }
2298
2299 /**
2300 * Gets estimated measure of quality of estimated fit as a value between 0.0 and 1.0. The larger the quality value
2301 * is, the better the fit that has been estimated.
2302 *
2303 * @return estimated measure of quality of estimated fit.
2304 */
2305 @Override
2306 public double getEstimatedQ() {
2307 return estimatedQ;
2308 }
2309
2310 /**
2311 * Called before calibration occurs.
2312 * This can be overridden by subclasses.
2313 *
2314 * @throws CalibrationException if anything fails.
2315 */
2316 protected void onBeforeCalibrate() throws CalibrationException {
2317 // no action needed
2318 }
2319
2320 /**
2321 * Internally sets ground truth magnetic flux density norm to be expected at location where
2322 * measurements have been made, expressed in Teslas (T).
2323 *
2324 * @param groundTruthMagneticFluxDensityNorm ground truth magnetic flux density norm or null if undefined.
2325 * @throws IllegalArgumentException if provided value is negative.
2326 */
2327 protected void internalSetGroundTruthMagneticFluxDensityNorm(final Double groundTruthMagneticFluxDensityNorm) {
2328 if (groundTruthMagneticFluxDensityNorm != null && groundTruthMagneticFluxDensityNorm < 0.0) {
2329 throw new IllegalArgumentException();
2330 }
2331 this.groundTruthMagneticFluxDensityNorm = groundTruthMagneticFluxDensityNorm;
2332 }
2333
2334 /**
2335 * Sets input data into Levenberg-Marquardt fitter.
2336 *
2337 * @throws WrongSizeException never happens.
2338 */
2339 private void setInputData() throws WrongSizeException {
2340 final var gtb = groundTruthMagneticFluxDensityNorm;
2341 final var gtb2 = gtb * gtb;
2342
2343 final var numMeasurements = measurements.size();
2344 final var x = new Matrix(numMeasurements, BodyMagneticFluxDensity.COMPONENTS);
2345 final var y = new double[numMeasurements];
2346 final var specificForceStandardDeviations = new double[numMeasurements];
2347 var i = 0;
2348 for (final var measurement : measurements) {
2349 final var measuredMagneticFluxDensity = measurement.getMagneticFluxDensity();
2350
2351 final var bmeasuredX = measuredMagneticFluxDensity.getBx();
2352 final var bmeasuredY = measuredMagneticFluxDensity.getBy();
2353 final var bmeasuredZ = measuredMagneticFluxDensity.getBz();
2354
2355 x.setElementAt(i, 0, bmeasuredX);
2356 x.setElementAt(i, 1, bmeasuredY);
2357 x.setElementAt(i, 2, bmeasuredZ);
2358
2359 y[i] = gtb2;
2360
2361 specificForceStandardDeviations[i] = measurement.getMagneticFluxDensityStandardDeviation();
2362
2363 i++;
2364 }
2365
2366 fitter.setInputData(x, y, specificForceStandardDeviations);
2367 }
2368
2369 /**
2370 * Internal method to perform general calibration.
2371 *
2372 * @throws FittingException if Levenberg-Marquardt fails for numerical reasons.
2373 * @throws AlgebraException if there are numerical instabilities that prevent
2374 * matrix inversion.
2375 * @throws com.irurueta.numerical.NotReadyException never happens.
2376 */
2377 private void calibrateGeneral() throws AlgebraException, FittingException,
2378 com.irurueta.numerical.NotReadyException {
2379 // The magnetometer model is:
2380 // bmeas = bm + (I + Mm) * btrue + w
2381
2382 // Ideally a least squares solution tries to minimize noise component, so:
2383 // bmeas = bm + (I + Mm) * btrue
2384
2385 // For convergence purposes of the Levenberg-Marquardt algorithm, the
2386 // magnetometer model can be better expressed as:
2387 // bmeas = T*K*(btrue + b)
2388 // bmeas = M*(btrue + b)
2389 // bmeas = M*btrue + M*b
2390
2391 // where:
2392 // M = I + Mm
2393 // bm = M*b = (I + Mm)*b --> b = M^-1*bm
2394
2395 // We know that the norm of the true body magnetic flux density
2396 // is equal to the amount of Earth magnetic flux density at provided
2397 // position and timestamp
2398 // ||btrue|| = ||bEarth|| --> from 30 µT to 60 µT
2399
2400 // Hence:
2401 // bmeas - M*b = M*btrue
2402
2403 // M^-1 * (bmeas - M*b) = btrue
2404
2405 // ||bEarth||^2 = ||btrue||^2 = (M^-1 * (bmeas - M*b))^T * (M^-1 * (bmeas - M*b))
2406 // ||bEarth||^2 = (bmeas - M*b)^T*(M^-1)^T * M^-1 * (bmeas - M*b)
2407 // ||bEarth||^2 = (bmeas - M * b)^T * ||M^-1||^2 * (bmeas - M * b)
2408 // ||bEarth||^2 = ||bmeas - M * b||^2 * ||M^-1||^2
2409
2410 // Where:
2411
2412 // b = [bx]
2413 // [by]
2414 // [bz]
2415
2416 // M = [m11 m12 m13]
2417 // [m21 m22 m23]
2418 // [m31 m32 m33]
2419
2420 final var gradientEstimator = new GradientEstimator(this::evaluateGeneral);
2421
2422 final var initialM = Matrix.identity(BodyMagneticFluxDensity.COMPONENTS, BodyMagneticFluxDensity.COMPONENTS);
2423 initialM.add(getInitialMm());
2424
2425 fitter.setFunctionEvaluator(new LevenbergMarquardtMultiDimensionFunctionEvaluator() {
2426 @Override
2427 public int getNumberOfDimensions() {
2428 // Input points are measured magnetic flux density coordinates
2429 return BodyMagneticFluxDensity.COMPONENTS;
2430 }
2431
2432 @Override
2433 public double[] createInitialParametersArray() {
2434 // cross coupling errors M
2435 return initialM.toArray();
2436 }
2437
2438 @Override
2439 public double evaluate(
2440 final int i, final double[] point, final double[] params, final double[] derivatives)
2441 throws EvaluationException {
2442
2443 bmeasX = point[0];
2444 bmeasY = point[1];
2445 bmeasZ = point[2];
2446
2447 gradientEstimator.gradient(params, derivatives);
2448
2449 return evaluateGeneral(params);
2450 }
2451 });
2452
2453 setInputData();
2454
2455 fitter.fit();
2456
2457 final var result = fitter.getA();
2458
2459 final var m11 = result[0];
2460 final var m21 = result[1];
2461 final var m31 = result[2];
2462
2463 final var m12 = result[3];
2464 final var m22 = result[4];
2465 final var m32 = result[5];
2466
2467 final var m13 = result[6];
2468 final var m23 = result[7];
2469 final var m33 = result[8];
2470
2471 final var mm = new Matrix(BodyMagneticFluxDensity.COMPONENTS, BodyMagneticFluxDensity.COMPONENTS);
2472 mm.setElementAtIndex(0, m11);
2473 mm.setElementAtIndex(1, m21);
2474 mm.setElementAtIndex(2, m31);
2475
2476 mm.setElementAtIndex(3, m12);
2477 mm.setElementAtIndex(4, m22);
2478 mm.setElementAtIndex(5, m32);
2479
2480 mm.setElementAtIndex(6, m13);
2481 mm.setElementAtIndex(7, m23);
2482 mm.setElementAtIndex(8, m33);
2483
2484 setResult(mm);
2485 }
2486
2487 /**
2488 * Internal method to perform calibration when common z-axis is assumed for both
2489 * the accelerometer and gyroscope.
2490 *
2491 * @throws FittingException if Levenberg-Marquardt fails for numerical reasons.
2492 * @throws AlgebraException if there are numerical instabilities that prevent
2493 * matrix inversion.
2494 * @throws com.irurueta.numerical.NotReadyException never happens.
2495 */
2496 private void calibrateCommonAxis() throws AlgebraException, FittingException,
2497 com.irurueta.numerical.NotReadyException {
2498 // The magnetometer model is:
2499 // bmeas = bm + (I + Mm) * btrue + w
2500
2501 // Ideally a least squares solution tries to minimize noise component, so:
2502 // bmeas = bm + (I + Mm) * btrue
2503
2504 // For convergence purposes of the Levenberg-Marquardt algorithm, the
2505 // magnetometer model can be better expressed as:
2506 // bmeas = T*K*(btrue + b)
2507 // bmeas = M*(btrue + b)
2508 // bmeas = M*btrue + M*b
2509
2510 // where:
2511 // M = I + Mm
2512 // bm = M*b = (I + Mm)*b --> b = M^-1*bm
2513
2514 // We know that the norm of the true body magnetic flux density
2515 // is equal to the amount of Earth magnetic flux density at provided
2516 // position and timestamp
2517 // ||btrue|| = ||bEarth|| --> from 30 µT to 60 µT
2518
2519 // Hence:
2520 // bmeas - M*b = M*btrue
2521
2522 // M^-1 * (bmeas - M*b) = btrue
2523
2524 // ||bEarth||^2 = ||btrue||^2 = (M^-1 * (bmeas - M*b))^T * (M^-1 * (bmeas - M*b))
2525 // ||bEarth||^2 = (bmeas - M*b)^T*(M^-1)^T * M^-1 * (bmeas - M*b)
2526 // ||bEarth||^2 = (bmeas - M * b)^T * ||M^-1||^2 * (bmeas - M * b)
2527 // ||bEarth||^2 = ||bmeas - M * b||^2 * ||M^-1||^2
2528
2529 // Where:
2530
2531 // b = [bx]
2532 // [by]
2533 // [bz]
2534
2535 // M = [m11 m12 m13]
2536 // [0 m22 m23]
2537 // [0 0 m33]
2538
2539
2540 final var gradientEstimator = new GradientEstimator(this::evaluateCommonAxis);
2541
2542 final var initialM = Matrix.identity(BodyMagneticFluxDensity.COMPONENTS, BodyMagneticFluxDensity.COMPONENTS);
2543 initialM.add(getInitialMm());
2544
2545 // Force initial M to be upper diagonal
2546 initialM.setElementAt(1, 0, 0.0);
2547 initialM.setElementAt(2, 0, 0.0);
2548 initialM.setElementAt(2, 1, 0.0);
2549
2550 fitter.setFunctionEvaluator(new LevenbergMarquardtMultiDimensionFunctionEvaluator() {
2551 @Override
2552 public int getNumberOfDimensions() {
2553 // Input points are measured magnetic flux density coordinates
2554 return BodyKinematics.COMPONENTS;
2555 }
2556
2557 @Override
2558 public double[] createInitialParametersArray() {
2559 final var initial = new double[COMMON_Z_AXIS_UNKNOWNS];
2560
2561 // upper diagonal cross coupling errors M
2562 var k = 0;
2563 for (var j = 0; j < BodyMagneticFluxDensity.COMPONENTS; j++) {
2564 for (var i = 0; i < BodyMagneticFluxDensity.COMPONENTS; i++) {
2565 if (i <= j) {
2566 initial[k] = initialM.getElementAt(i, j);
2567 k++;
2568 }
2569 }
2570 }
2571
2572 return initial;
2573 }
2574
2575 @Override
2576 public double evaluate(
2577 final int i, final double[] point, final double[] params, final double[] derivatives)
2578 throws EvaluationException {
2579
2580 bmeasX = point[0];
2581 bmeasY = point[1];
2582 bmeasZ = point[2];
2583
2584 gradientEstimator.gradient(params, derivatives);
2585
2586 return evaluateCommonAxis(params);
2587 }
2588 });
2589
2590 setInputData();
2591
2592 fitter.fit();
2593
2594 final var result = fitter.getA();
2595
2596 final var m11 = result[0];
2597
2598 final var m12 = result[1];
2599 final var m22 = result[2];
2600
2601 final var m13 = result[3];
2602 final var m23 = result[4];
2603 final var m33 = result[5];
2604
2605 final var mm = new Matrix(BodyMagneticFluxDensity.COMPONENTS, BodyMagneticFluxDensity.COMPONENTS);
2606 mm.setElementAtIndex(0, m11);
2607 mm.setElementAtIndex(1, 0.0);
2608 mm.setElementAtIndex(2, 0.0);
2609
2610 mm.setElementAtIndex(3, m12);
2611 mm.setElementAtIndex(4, m22);
2612 mm.setElementAtIndex(5, 0.0);
2613
2614 mm.setElementAtIndex(6, m13);
2615 mm.setElementAtIndex(7, m23);
2616 mm.setElementAtIndex(8, m33);
2617
2618 setResult(mm);
2619
2620 // taking into account that:
2621 // Mm = [sx mxy mxz] = [m11 m12 m13]
2622 // [myx sy myz] [m21 m22 m23]
2623 // [mzx mzy sz ] [m31 m32 m33]
2624
2625 // propagate covariance so that all parameters are taken into account
2626 // in the order: sx, sy, sz, mxy, mxz, myx, myz, mzx, mzy
2627
2628 // Since estimated values are:
2629 // (m11, m12, m22, m13, m23, m33) = (sx, mxy, sy, mxz, myz, sz)
2630
2631 // We define a lineal function mapping original parameters for the
2632 // common axis case to the general case
2633 // [sx'] = [1 0 0 0 0 0][sx ]
2634 // [sy'] [0 0 1 0 0 0][mxy]
2635 // [sz'] [0 0 0 0 0 1][sy ]
2636 // [mxy'] [0 1 0 0 0 0][mxz]
2637 // [mxz'] [0 0 0 1 0 0][myz]
2638 // [myx'] [0 0 0 0 0 0][sz ]
2639 // [myz'] [0 0 0 0 1 0]
2640 // [mzx'] [0 0 0 0 0 0]
2641 // [mzy'] [0 0 0 0 0 0]
2642
2643 // As defined in com.irurueta.statistics.MultivariateNormalDist,
2644 // if we consider the jacobian of the lineal application the matrix shown
2645 // above, then covariance can be propagated as follows
2646 final var jacobian = new Matrix(GENERAL_UNKNOWNS, COMMON_Z_AXIS_UNKNOWNS);
2647 jacobian.setElementAt(0, 0, 1.0);
2648 jacobian.setElementAt(1, 2, 1.0);
2649 jacobian.setElementAt(2, 5, 1.0);
2650 jacobian.setElementAt(3, 1, 1.0);
2651 jacobian.setElementAt(4, 3, 1.0);
2652 jacobian.setElementAt(6, 4, 1.0);
2653 // propagated covariance is J * Cov * J'
2654 final var jacobianTrans = jacobian.transposeAndReturnNew();
2655 jacobian.multiply(estimatedCovariance);
2656 jacobian.multiply(jacobianTrans);
2657 estimatedCovariance = jacobian;
2658 }
2659
2660 /**
2661 * Makes proper conversion of internal cross-coupling and bias matrices.
2662 *
2663 * @param m internal cross-coupling matrix.
2664 */
2665 private void setResult(final Matrix m) {
2666 // Because:
2667 // M = I + Mm
2668
2669 // Then:
2670 // Mm = M - I
2671
2672 if (estimatedMm == null) {
2673 estimatedMm = m;
2674 } else {
2675 estimatedMm.copyFrom(m);
2676 }
2677
2678 for (var i = 0; i < BodyMagneticFluxDensity.COMPONENTS; i++) {
2679 estimatedMm.setElementAt(i, i, estimatedMm.getElementAt(i, i) - 1.0);
2680 }
2681
2682 // since only a constant term is subtracted, covariance is preserved
2683 estimatedCovariance = fitter.getCovar();
2684 estimatedChiSq = fitter.getChisq();
2685 estimatedChiSqDegreesOfFreedom = fitter.getChisqDegreesOfFreedom();
2686 estimatedReducedChiSq = fitter.getReducedChisq();
2687 estimatedMse = fitter.getMse();
2688 try {
2689 estimatedP = fitter.getP();
2690 estimatedQ = fitter.getQ();
2691 } catch (final MaxIterationsExceededException ignore) {
2692 // if numerical instabilities arise, we assume worst case (no fit at all)
2693 // probability of finding a smaller chi square value is 1.0
2694 // quality of fit is 0.0
2695 estimatedP = 1.0;
2696 estimatedQ = 0.0;
2697 }
2698 }
2699
2700 /**
2701 * Computes estimated true magnetic flux density squared norm using current measured
2702 * body magnetic flux density and provided parameters for the general case.
2703 * This method is internally executed during gradient estimation and
2704 * Levenberg-Marquardt fitting needed for calibration computation.
2705 *
2706 * @param params array containing current parameters for the general purpose case.
2707 * Must have length 9.
2708 * @return estimated true specific force squared norm.
2709 * @throws EvaluationException if there are numerical instabilities.
2710 */
2711 private double evaluateGeneral(final double[] params) throws EvaluationException {
2712 final var m11 = params[0];
2713 final var m21 = params[1];
2714 final var m31 = params[2];
2715
2716 final var m12 = params[3];
2717 final var m22 = params[4];
2718 final var m32 = params[5];
2719
2720 final var m13 = params[6];
2721 final var m23 = params[7];
2722 final var m33 = params[8];
2723
2724 return evaluate(m11, m21, m31, m12, m22, m32, m13, m23, m33);
2725 }
2726
2727 /**
2728 * Computes estimated true magnetic flux density squared norm using current measured
2729 * body magnetic flux density and provided parameters when common z-axis is assumed.
2730 * This method is internally executed during gradient estimation and
2731 * Levenberg-Marquardt fitting needed for calibration computation.
2732 *
2733 * @param params array containing current parameters for the common z-axis case.
2734 * Must have length 6.
2735 * @return estimated true specific force squared norm.
2736 * @throws EvaluationException if there are numerical instabilities.
2737 */
2738 private double evaluateCommonAxis(final double[] params) throws EvaluationException {
2739 final var m11 = params[0];
2740
2741 final var m12 = params[1];
2742 final var m22 = params[2];
2743
2744 final var m13 = params[3];
2745 final var m23 = params[4];
2746 final var m33 = params[5];
2747
2748 return evaluate(m11, 0.0, 0.0, m12, m22, 0.0, m13, m23, m33);
2749 }
2750
2751 /**
2752 * Computes estimated true magnetic flux density squared norm using current measured
2753 * body magnetic flux density and provided parameters.
2754 * This method is internally executed during gradient estimation and
2755 * Levenberg-Marquardt fitting needed for calibration computation.
2756 *
2757 * @param m11 element 1,1 of cross-coupling error matrix.
2758 * @param m21 element 2,1 of cross-coupling error matrix.
2759 * @param m31 element 3,1 of cross-coupling error matrix.
2760 * @param m12 element 1,2 of cross-coupling error matrix.
2761 * @param m22 element 2,2 of cross-coupling error matrix.
2762 * @param m32 element 3,2 of cross-coupling error matrix.
2763 * @param m13 element 1,3 of cross-coupling error matrix.
2764 * @param m23 element 2,3 of cross-coupling error matrix.
2765 * @param m33 element 3,3 of cross-coupling error matrix.
2766 * @return estimated true specific force squared norm.
2767 * @throws EvaluationException if there are numerical instabilities.
2768 */
2769 private double evaluate(
2770 final double m11, final double m21, final double m31,
2771 final double m12, final double m22, final double m32,
2772 final double m13, final double m23, final double m33) throws EvaluationException {
2773
2774 // bmeas = M*(btrue + b)
2775
2776 // btrue = M^-1*bmeas - b
2777
2778 try {
2779 if (bmeas == null) {
2780 bmeas = new Matrix(BodyMagneticFluxDensity.COMPONENTS, 1);
2781 }
2782 if (m == null) {
2783 m = new Matrix(BodyMagneticFluxDensity.COMPONENTS, BodyMagneticFluxDensity.COMPONENTS);
2784 }
2785 if (invM == null) {
2786 invM = new Matrix(BodyMagneticFluxDensity.COMPONENTS, BodyMagneticFluxDensity.COMPONENTS);
2787 }
2788 if (b == null) {
2789 b = new Matrix(BodyMagneticFluxDensity.COMPONENTS, 1);
2790 }
2791 if (btrue == null) {
2792 btrue = new Matrix(BodyMagneticFluxDensity.COMPONENTS, 1);
2793 }
2794 if (bm == null) {
2795 bm = new Matrix(BodyMagneticFluxDensity.COMPONENTS, 1);
2796 }
2797
2798 getHardIronMatrix(bm);
2799
2800 bmeas.setElementAtIndex(0, bmeasX);
2801 bmeas.setElementAtIndex(1, bmeasY);
2802 bmeas.setElementAtIndex(2, bmeasZ);
2803
2804 m.setElementAt(0, 0, m11);
2805 m.setElementAt(1, 0, m21);
2806 m.setElementAt(2, 0, m31);
2807
2808 m.setElementAt(0, 1, m12);
2809 m.setElementAt(1, 1, m22);
2810 m.setElementAt(2, 1, m32);
2811
2812 m.setElementAt(0, 2, m13);
2813 m.setElementAt(1, 2, m23);
2814 m.setElementAt(2, 2, m33);
2815
2816 Utils.inverse(m, invM);
2817
2818 // b = m^-1 * bm
2819 invM.multiply(bm, b);
2820
2821 invM.multiply(bmeas, btrue);
2822 btrue.subtract(b);
2823
2824 final var norm = Utils.normF(btrue);
2825 return norm * norm;
2826
2827 } catch (final AlgebraException e) {
2828 throw new EvaluationException(e);
2829 }
2830 }
2831
2832 /**
2833 * Converts magnetic flux density value and unit to Teslas.
2834 *
2835 * @param value magnetic flux density value.
2836 * @param unit unit of magnetic flux density value.
2837 * @return converted value.
2838 */
2839 private static double convertMagneticFluxDensity(final double value, final MagneticFluxDensityUnit unit) {
2840 return MagneticFluxDensityConverter.convert(value, unit, MagneticFluxDensityUnit.TESLA);
2841 }
2842
2843 /**
2844 * Converts magnetic flux density instance to Teslas.
2845 *
2846 * @param magneticFluxDensity magnetic flux density instance to be converted.
2847 * @return converted value.
2848 */
2849 private static double convertMagneticFluxDensity(final MagneticFluxDensity magneticFluxDensity) {
2850 return convertMagneticFluxDensity(magneticFluxDensity.getValue().doubleValue(), magneticFluxDensity.getUnit());
2851 }
2852 }