1 /*
2 * Copyright (C) 2020 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.navigation.inertial.calibration.magnetometer;
17
18 import com.irurueta.algebra.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.frames.CoordinateTransformation;
25 import com.irurueta.navigation.frames.ECEFFrame;
26 import com.irurueta.navigation.frames.FrameType;
27 import com.irurueta.navigation.frames.NEDFrame;
28 import com.irurueta.navigation.frames.converters.ECEFtoNEDFrameConverter;
29 import com.irurueta.navigation.inertial.BodyMagneticFluxDensity;
30 import com.irurueta.navigation.inertial.calibration.CalibrationException;
31 import com.irurueta.navigation.inertial.calibration.FrameBodyMagneticFluxDensity;
32 import com.irurueta.navigation.inertial.calibration.MagneticFluxDensityTriad;
33 import com.irurueta.navigation.inertial.estimators.BodyMagneticFluxDensityEstimator;
34 import com.irurueta.navigation.inertial.wmm.NEDMagneticFluxDensity;
35 import com.irurueta.navigation.inertial.wmm.WMMEarthMagneticFluxDensityEstimator;
36 import com.irurueta.navigation.inertial.wmm.WorldMagneticModel;
37 import com.irurueta.units.MagneticFluxDensity;
38 import com.irurueta.units.MagneticFluxDensityConverter;
39 import com.irurueta.units.MagneticFluxDensityUnit;
40
41 import java.io.IOException;
42 import java.util.Collection;
43
44 /**
45 * Estimates magnetometer soft-iron cross couplings and scaling factors.
46 * <p>
47 * This calibrator uses a linear approach to find a minimum least squared error
48 * solution.
49 * <p>
50 * To use this calibrator at least 3 measurements at different known frames
51 * must be provided. In other words, magnetometer samples must be obtained at
52 * 3 different positions or orientations.
53 * Notice that frame velocities are ignored by this calibrator.
54 * <p>
55 * Measured magnetic flux density is assumed to follow the model shown below:
56 * <pre>
57 * mBmeas = bm + (I + Mm) * mBtrue + w
58 * </pre>
59 * Where:
60 * - mBmeas is the measured magnetic flux density. This is a 3x1 vector.
61 * - bm is magnetometer hard-iron bias. Ideally, on a perfect magnetometer,
62 * this should be a 3x1 zero vector.
63 * - I is the 3x3 identity matrix.
64 * - Mm is the 3x3 soft-iron matrix containing cross-couplings and scaling
65 * factors. Ideally, on a perfect magnetometer, this should be a 3x3 zero
66 * matrix.
67 * - mBtrue is ground-truth magnetic flux density. This is a 3x1 vector.
68 * - w is measurement noise. This is a 3x1 vector.
69 */
70 @SuppressWarnings("DuplicatedCode")
71 public class KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator implements
72 KnownHardIronAndFrameMagnetometerCalibrator<FrameBodyMagneticFluxDensity,
73 KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener>,
74 UnorderedFrameBodyMagneticFluxDensityMagnetometerCalibrator {
75
76 /**
77 * Indicates whether by default a common z-axis is assumed for the accelerometer,
78 * gyroscope and magnetometer.
79 */
80 public static final boolean DEFAULT_USE_COMMON_Z_AXIS = false;
81
82 /**
83 * Required minimum number of measurements.
84 */
85 public static final int MINIMUM_MEASUREMENTS = 3;
86
87 /**
88 * Number of equations generated for each measurement.
89 */
90 private static final int EQUATIONS_PER_MEASUREMENT = 3;
91
92 /**
93 * Number of unknowns when common z-axis is assumed for the accelerometer,
94 * gyroscope and magnetometer.
95 */
96 private static final int COMMON_Z_AXIS_UNKNOWNS = 6;
97
98 /**
99 * Number of unknowns for the general case.
100 */
101 private static final int GENERAL_UNKNOWNS = 9;
102
103 /**
104 * X-coordinate of known hard-iron bias.
105 * This is expressed in Teslas (T).
106 */
107 private double hardIronX;
108
109 /**
110 * Y-coordinate of known hard-iron bias.
111 * This is expressed in Teslas (T).
112 */
113 private double hardIronY;
114
115 /**
116 * Z-coordinate of known hard-iron bias.
117 * This is expressed in Teslas (T).
118 */
119 private double hardIronZ;
120
121 /**
122 * Contains a collection of body magnetic flux density measurements taken
123 * at different frames (positions and orientations).
124 * If a single device magnetometer needs to be calibrated, typically all
125 * measurements are taken at the same position, with zero velocity and
126 * multiple orientations.
127 * However, if we just want to calibrate a given magnetometer model (e.g.
128 * obtain an average and less precise calibration for the magnetometer of
129 * a given phone model), we could take measurements collected throughout
130 * the planet at multiple positions while the phone remains static (e.g.
131 * while charging), hence each measurement position will change, velocity
132 * will remain zero and orientation will be typically constant at
133 * horizontal orientation while the phone remains on a
134 * flat surface.
135 */
136 private Collection<FrameBodyMagneticFluxDensity> measurements;
137
138 /**
139 * This flag indicates whether z-axis is assumed to be common for accelerometer,
140 * gyroscope and magnetometer.
141 * When enabled, this eliminates 3 variables from Mm matrix.
142 */
143 private boolean commonAxisUsed = DEFAULT_USE_COMMON_Z_AXIS;
144
145 /**
146 * Listener to handle events raised by this calibrator.
147 */
148 private KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener;
149
150 /**
151 * Estimated magnetometer soft-iron matrix containing scale factors
152 * and cross coupling errors.
153 * This is the product of matrix Tm containing cross coupling errors and Km
154 * containing scaling factors.
155 * So tat:
156 * <pre>
157 * Mm = [sx mxy mxz] = Tm*Km
158 * [myx sy myz]
159 * [mzx mzy sz ]
160 * </pre>
161 * Where:
162 * <pre>
163 * Km = [sx 0 0 ]
164 * [0 sy 0 ]
165 * [0 0 sz]
166 * </pre>
167 * and
168 * <pre>
169 * Tm = [1 -alphaXy alphaXz ]
170 * [alphaYx 1 -alphaYz]
171 * [-alphaZx alphaZy 1 ]
172 * </pre>
173 * Hence:
174 * <pre>
175 * Mm = [sx mxy mxz] = Tm*Km = [sx -sy * alphaXy sz * alphaXz ]
176 * [myx sy myz] [sx * alphaYx sy -sz * alphaYz]
177 * [mzx mzy sz ] [-sx * alphaZx sy * alphaZy sz ]
178 * </pre>
179 * This instance allows any 3x3 matrix however, typically alphaYx, alphaZx and alphaZy
180 * are considered to be zero if the accelerometer z-axis is assumed to be the same
181 * as the body z-axis. When this is assumed, myx = mzx = mzy = 0 and the Mm matrix
182 * becomes upper diagonal:
183 * <pre>
184 * Mm = [sx mxy mxz]
185 * [0 sy myz]
186 * [0 0 sz ]
187 * </pre>
188 * Values of this matrix are unit-less.
189 */
190 private Matrix estimatedMm;
191
192 /**
193 * Indicates whether calibrator is running.
194 */
195 private boolean running;
196
197 /**
198 * Contains Earth's magnetic model.
199 */
200 private WorldMagneticModel magneticModel;
201
202 /**
203 * Constructor.
204 */
205 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator() {
206 }
207
208 /**
209 * Constructor.
210 *
211 * @param listener listener to handle events raised by this calibrator.
212 */
213 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
214 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
215 this.listener = listener;
216 }
217
218 /**
219 * Constructor.
220 *
221 * @param measurements collection of body magnetic flux density measurements
222 * taken at different frames (positions and orientations).
223 */
224 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
225 final Collection<? extends FrameBodyMagneticFluxDensity> measurements) {
226 //noinspection unchecked
227 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
228 }
229
230 /**
231 * Constructor.
232 *
233 * @param measurements collection of body magnetic flux density measurements
234 * taken at different frames (positions and orientations).
235 * @param listener listener to handle events raised by this calibrator.
236 */
237 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
238 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
239 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
240 this(measurements);
241 this.listener = listener;
242 }
243
244 /**
245 * Constructor.
246 *
247 * @param commonAxisUsed indicates whether z-axis is assumed to be common
248 * for the accelerometer, gyroscope and magnetometer.
249 */
250 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(final boolean commonAxisUsed) {
251 this.commonAxisUsed = commonAxisUsed;
252 }
253
254 /**
255 * Constructor.
256 *
257 * @param commonAxisUsed indicates whether z-axis is assumed to be common
258 * for the accelerometer, gyroscope and magnetometer.
259 * @param listener listener to handle events raised by this calibrator.
260 */
261 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
262 final boolean commonAxisUsed,
263 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
264 this(commonAxisUsed);
265 this.listener = listener;
266 }
267
268 /**
269 * Constructor.
270 *
271 * @param measurements collection of body magnetic flux density measurements
272 * taken at different frames (positions and orientations).
273 * @param commonAxisUsed indicates whether z-axis is assumed to be common
274 * for the accelerometer, gyroscope and magnetometer.
275 */
276 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
277 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed) {
278 this(measurements);
279 this.commonAxisUsed = commonAxisUsed;
280 }
281
282 /**
283 * Constructor.
284 *
285 * @param measurements collection of body magnetic flux density measurements
286 * taken at different frames (positions and orientations).
287 * @param commonAxisUsed indicates whether z-axis is assumed to be common
288 * for the accelerometer, gyroscope and magnetometer.
289 * @param listener listener to handle events raised by this calibrator.
290 */
291 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
292 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
293 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
294 this(measurements, commonAxisUsed);
295 this.listener = listener;
296 }
297
298 /**
299 * Constructor.
300 *
301 * @param magneticModel Earth's magnetic model. If null, a default model
302 * will be used instead.
303 */
304 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(final WorldMagneticModel magneticModel) {
305 this.magneticModel = magneticModel;
306 }
307
308 /**
309 * Constructor.
310 *
311 * @param magneticModel Earth's magnetic model. If null, a default model
312 * will be used instead.
313 * @param listener listener to handle events raised by this calibrator.
314 */
315 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
316 final WorldMagneticModel magneticModel,
317 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
318 this(listener);
319 this.magneticModel = magneticModel;
320 }
321
322 /**
323 * Constructor.
324 *
325 * @param measurements collection of body magnetic flux density measurements
326 * taken at different frames (positions and orientations).
327 * @param magneticModel Earth's magnetic model. If null, a default model
328 * will be used instead.
329 */
330 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
331 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
332 final WorldMagneticModel magneticModel) {
333 this(measurements);
334 this.magneticModel = magneticModel;
335 }
336
337 /**
338 * Constructor.
339 *
340 * @param measurements collection of body magnetic flux density measurements
341 * taken at different frames (positions and orientations).
342 * @param magneticModel Earth's magnetic model. If null, a default model
343 * will be used instead.
344 * @param listener listener to handle events raised by this calibrator.
345 */
346 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
347 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
348 final WorldMagneticModel magneticModel,
349 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
350 this(measurements, listener);
351 this.magneticModel = magneticModel;
352 }
353
354 /**
355 * Constructor.
356 *
357 * @param commonAxisUsed indicates whether z-axis is assumed to be common
358 * for the accelerometer, gyroscope and magnetometer.
359 * @param magneticModel Earth's magnetic model. If null, a default model
360 * will be used instead.
361 */
362 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
363 final boolean commonAxisUsed, final WorldMagneticModel magneticModel) {
364 this(commonAxisUsed);
365 this.magneticModel = magneticModel;
366 }
367
368 /**
369 * Constructor.
370 *
371 * @param commonAxisUsed indicates whether z-axis is assumed to be common
372 * for the accelerometer, gyroscope and magnetometer.
373 * @param magneticModel Earth's magnetic model. If null, a default model
374 * will be used instead.
375 * @param listener listener to handle events raised by this calibrator.
376 */
377 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
378 final boolean commonAxisUsed, final WorldMagneticModel magneticModel,
379 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
380 this(commonAxisUsed, listener);
381 this.magneticModel = magneticModel;
382 }
383
384 /**
385 * Constructor.
386 *
387 * @param measurements collection of body magnetic flux density measurements
388 * taken at different frames (positions and orientations).
389 * @param commonAxisUsed indicates whether z-axis is assumed to be common
390 * for the accelerometer, gyroscope and magnetometer.
391 * @param magneticModel Earth's magnetic model. If null, a default model
392 * will be used instead.
393 */
394 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
395 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
396 final WorldMagneticModel magneticModel) {
397 this(measurements, commonAxisUsed);
398 this.magneticModel = magneticModel;
399 }
400
401 /**
402 * Constructor.
403 *
404 * @param measurements collection of body magnetic flux density measurements
405 * taken at different frames (positions and orientations).
406 * @param commonAxisUsed indicates whether z-axis is assumed to be common
407 * for the accelerometer, gyroscope and magnetometer.
408 * @param magneticModel Earth's magnetic model. If null, a default model
409 * will be used instead.
410 * @param listener listener to handle events raised by this calibrator.
411 */
412 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
413 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
414 final WorldMagneticModel magneticModel,
415 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
416 this(measurements, commonAxisUsed, listener);
417 this.magneticModel = magneticModel;
418 }
419
420 /**
421 * Constructor.
422 *
423 * @param hardIronX x-coordinate of magnetometer hard-iron bias
424 * expressed in Teslas (T).
425 * @param hardIronY y-coordinate of magnetometer hard-iron bias
426 * expressed in Teslas (T).
427 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
428 * expressed in Teslas (T).
429 */
430 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
431 final double hardIronX, final double hardIronY, final double hardIronZ) {
432 try {
433 setHardIronCoordinates(hardIronX, hardIronY, hardIronZ);
434 } catch (final LockedException ignore) {
435 // never happens
436 }
437 }
438
439 /**
440 * Constructor.
441 *
442 * @param hardIronX x-coordinate of magnetometer hard-iron bias
443 * expressed in Teslas (T).
444 * @param hardIronY y-coordinate of magnetometer hard-iron bias
445 * expressed in Teslas (T).
446 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
447 * expressed in Teslas (T).
448 * @param listener listener to handle events raised by this calibrator.
449 */
450 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
451 final double hardIronX, final double hardIronY, final double hardIronZ,
452 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
453 this(hardIronX, hardIronY, hardIronZ);
454 this.listener = listener;
455 }
456
457 /**
458 * Constructor.
459 *
460 * @param measurements collection of body magnetic flux density measurements
461 * taken at different frames (positions and orientations).
462 * @param hardIronX x-coordinate of magnetometer hard-iron bias
463 * expressed in Teslas (T).
464 * @param hardIronY y-coordinate of magnetometer hard-iron bias
465 * expressed in Teslas (T).
466 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
467 * expressed in Teslas (T).
468 */
469 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
470 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
471 final double hardIronX, final double hardIronY, final double hardIronZ) {
472 this(hardIronX, hardIronY, hardIronZ);
473 //noinspection unchecked
474 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
475 }
476
477 /**
478 * Constructor.
479 *
480 * @param measurements collection of body magnetic flux density measurements
481 * taken at different frames (positions and orientations).
482 * @param hardIronX x-coordinate of magnetometer hard-iron bias
483 * expressed in Teslas (T).
484 * @param hardIronY y-coordinate of magnetometer hard-iron bias
485 * expressed in Teslas (T).
486 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
487 * expressed in Teslas (T).
488 * @param listener listener to handle events raised by this calibrator.
489 */
490 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
491 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
492 final double hardIronX, final double hardIronY, final double hardIronZ,
493 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
494 this(measurements, hardIronX, hardIronY, hardIronZ);
495 this.listener = listener;
496 }
497
498 /**
499 * Constructor.
500 *
501 * @param commonAxisUsed indicates whether z-axis is assumed to be common
502 * for the accelerometer, gyroscope and magnetometer.
503 * @param hardIronX x-coordinate of magnetometer hard-iron bias
504 * expressed in Teslas (T).
505 * @param hardIronY y-coordinate of magnetometer hard-iron bias
506 * expressed in Teslas (T).
507 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
508 * expressed in Teslas (T).
509 */
510 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
511 final boolean commonAxisUsed, final double hardIronX, final double hardIronY, final double hardIronZ) {
512 this(hardIronX, hardIronY, hardIronZ);
513 this.commonAxisUsed = commonAxisUsed;
514 }
515
516 /**
517 * Constructor.
518 *
519 * @param commonAxisUsed indicates whether z-axis is assumed to be common
520 * for the accelerometer, gyroscope and magnetometer.
521 * @param hardIronX x-coordinate of magnetometer hard-iron bias
522 * expressed in Teslas (T).
523 * @param hardIronY y-coordinate of magnetometer hard-iron bias
524 * expressed in Teslas (T).
525 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
526 * expressed in Teslas (T).
527 * @param listener listener to handle events raised by this calibrator.
528 */
529 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
530 final boolean commonAxisUsed, final double hardIronX, final double hardIronY, final double hardIronZ,
531 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
532 this(commonAxisUsed, hardIronX, hardIronY, hardIronZ);
533 this.listener = listener;
534 }
535
536 /**
537 * Constructor.
538 *
539 * @param measurements collection of body magnetic flux density measurements
540 * taken at different frames (positions and orientations).
541 * @param commonAxisUsed indicates whether z-axis is assumed to be common
542 * for the accelerometer, gyroscope and magnetometer.
543 * @param hardIronX x-coordinate of magnetometer hard-iron bias
544 * expressed in Teslas (T).
545 * @param hardIronY y-coordinate of magnetometer hard-iron bias
546 * expressed in Teslas (T).
547 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
548 * expressed in Teslas (T).
549 */
550 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
551 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
552 final double hardIronX, final double hardIronY, final double hardIronZ) {
553 this(commonAxisUsed, hardIronX, hardIronY, hardIronZ);
554 //noinspection unchecked
555 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
556 }
557
558 /**
559 * Constructor.
560 *
561 * @param measurements collection of body magnetic flux density measurements
562 * taken at different frames (positions and orientations).
563 * @param commonAxisUsed indicates whether z-axis is assumed to be common
564 * for the accelerometer, gyroscope and magnetometer.
565 * @param hardIronX x-coordinate of magnetometer hard-iron bias
566 * expressed in Teslas (T).
567 * @param hardIronY y-coordinate of magnetometer hard-iron bias
568 * expressed in Teslas (T).
569 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
570 * expressed in Teslas (T).
571 * @param listener listener to handle events raised by this calibrator.
572 */
573 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
574 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
575 final boolean commonAxisUsed, final double hardIronX, final double hardIronY, final double hardIronZ,
576 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
577 this(measurements, commonAxisUsed, hardIronX, hardIronY, hardIronZ);
578 this.listener = listener;
579 }
580
581 /**
582 * Constructor.
583 *
584 * @param magneticModel Earth's magnetic model. If null, a default model
585 * will be used instead.
586 * @param hardIronX x-coordinate of magnetometer hard-iron bias
587 * expressed in Teslas (T).
588 * @param hardIronY y-coordinate of magnetometer hard-iron bias
589 * expressed in Teslas (T).
590 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
591 * expressed in Teslas (T).
592 */
593 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
594 final WorldMagneticModel magneticModel,
595 final double hardIronX, final double hardIronY, final double hardIronZ) {
596 this(hardIronX, hardIronY, hardIronZ);
597 this.magneticModel = magneticModel;
598 }
599
600 /**
601 * Constructor.
602 *
603 * @param magneticModel Earth's magnetic model. If null, a default model
604 * will be used instead.
605 * @param hardIronX x-coordinate of magnetometer hard-iron bias
606 * expressed in Teslas (T).
607 * @param hardIronY y-coordinate of magnetometer hard-iron bias
608 * expressed in Teslas (T).
609 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
610 * expressed in Teslas (T).
611 * @param listener listener to handle events raised by this calibrator.
612 */
613 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
614 final WorldMagneticModel magneticModel,
615 final double hardIronX, final double hardIronY, final double hardIronZ,
616 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
617 this(magneticModel, hardIronX, hardIronY, hardIronZ);
618 this.listener = listener;
619 }
620
621 /**
622 * Constructor.
623 *
624 * @param measurements collection of body magnetic flux density measurements
625 * taken at different frames (positions and orientations).
626 * @param magneticModel Earth's magnetic model. If null, a default model
627 * will be used instead.
628 * @param hardIronX x-coordinate of magnetometer hard-iron bias
629 * expressed in Teslas (T).
630 * @param hardIronY y-coordinate of magnetometer hard-iron bias
631 * expressed in Teslas (T).
632 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
633 * expressed in Teslas (T).
634 */
635 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
636 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
637 final WorldMagneticModel magneticModel,
638 final double hardIronX, final double hardIronY, final double hardIronZ) {
639 this(hardIronX, hardIronY, hardIronZ);
640 //noinspection unchecked
641 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
642 this.magneticModel = magneticModel;
643 }
644
645 /**
646 * Constructor.
647 *
648 * @param measurements collection of body magnetic flux density measurements
649 * taken at different frames (positions and orientations).
650 * @param magneticModel Earth's magnetic model. If null, a default model
651 * will be used instead.
652 * @param hardIronX x-coordinate of magnetometer hard-iron bias
653 * expressed in Teslas (T).
654 * @param hardIronY y-coordinate of magnetometer hard-iron bias
655 * expressed in Teslas (T).
656 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
657 * expressed in Teslas (T).
658 * @param listener listener to handle events raised by this calibrator.
659 */
660 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
661 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
662 final WorldMagneticModel magneticModel,
663 final double hardIronX, final double hardIronY, final double hardIronZ,
664 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
665 this(measurements, magneticModel, hardIronX, hardIronY, hardIronZ);
666 this.listener = listener;
667 }
668
669 /**
670 * Constructor
671 *
672 * @param commonAxisUsed indicates whether z-axis is assumed to be common
673 * for the accelerometer, gyroscope and magnetometer.
674 * @param magneticModel Earth's magnetic model. If null, a default model
675 * will be used instead.
676 * @param hardIronX x-coordinate of magnetometer hard-iron bias
677 * expressed in Teslas (T).
678 * @param hardIronY y-coordinate of magnetometer hard-iron bias
679 * expressed in Teslas (T).
680 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
681 * expressed in Teslas (T).
682 */
683 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
684 final boolean commonAxisUsed, final WorldMagneticModel magneticModel,
685 final double hardIronX, final double hardIronY, final double hardIronZ) {
686 this(magneticModel, hardIronX, hardIronY, hardIronZ);
687 this.commonAxisUsed = commonAxisUsed;
688 }
689
690 /**
691 * Constructor.
692 *
693 * @param commonAxisUsed indicates whether z-axis is assumed to be common
694 * for the accelerometer, gyroscope and magnetometer.
695 * @param magneticModel Earth's magnetic model. If null, a default model
696 * will be used instead.
697 * @param hardIronX x-coordinate of magnetometer hard-iron bias
698 * expressed in Teslas (T).
699 * @param hardIronY y-coordinate of magnetometer hard-iron bias
700 * expressed in Teslas (T).
701 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
702 * expressed in Teslas (T).
703 * @param listener listener to handle events raised by this calibrator.
704 */
705 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
706 final boolean commonAxisUsed, final WorldMagneticModel magneticModel,
707 final double hardIronX, final double hardIronY, final double hardIronZ,
708 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
709 this(commonAxisUsed, magneticModel, hardIronX, hardIronY, hardIronZ);
710 this.listener = listener;
711 }
712
713 /**
714 * Constructor.
715 *
716 * @param measurements collection of body magnetic flux density measurements
717 * taken at different frames (positions and orientations).
718 * @param commonAxisUsed indicates whether z-axis is assumed to be common
719 * for the accelerometer, gyroscope and magnetometer.
720 * @param magneticModel Earth's magnetic model. If null, a default model
721 * will be used instead.
722 * @param hardIronX x-coordinate of magnetometer hard-iron bias
723 * expressed in Teslas (T).
724 * @param hardIronY y-coordinate of magnetometer hard-iron bias
725 * expressed in Teslas (T).
726 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
727 * expressed in Teslas (T).
728 */
729 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
730 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
731 final WorldMagneticModel magneticModel,
732 final double hardIronX, final double hardIronY, final double hardIronZ) {
733 this(commonAxisUsed, magneticModel, hardIronX, hardIronY, hardIronZ);
734 //noinspection unchecked
735 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
736 }
737
738 /**
739 * Constructor.
740 *
741 * @param measurements collection of body magnetic flux density measurements
742 * taken at different frames (positions and orientations).
743 * @param commonAxisUsed indicates whether z-axis is assumed to be common
744 * for the accelerometer, gyroscope and magnetometer.
745 * @param magneticModel Earth's magnetic model. If null, a default model
746 * will be used instead.
747 * @param hardIronX x-coordinate of magnetometer hard-iron bias
748 * expressed in Teslas (T).
749 * @param hardIronY y-coordinate of magnetometer hard-iron bias
750 * expressed in Teslas (T).
751 * @param hardIronZ z-coordinate of magnetometer hard-iron bias
752 * expressed in Teslas (T).
753 * @param listener listener to handle events raised by this calibrator.
754 */
755 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
756 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
757 final WorldMagneticModel magneticModel,
758 final double hardIronX, final double hardIronY, final double hardIronZ,
759 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
760 this(measurements, commonAxisUsed, magneticModel, hardIronX, hardIronY, hardIronZ);
761 this.listener = listener;
762 }
763
764 /**
765 * Constructor.
766 *
767 * @param hardIron known hard-iron bias.
768 * @throws IllegalArgumentException if provided hard-iron array does not
769 * have length 3.
770 */
771 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(final double[] hardIron) {
772 try {
773 setHardIron(hardIron);
774 } catch (final LockedException ignore) {
775 // never happens
776 }
777 }
778
779 /**
780 * Constructor.
781 *
782 * @param hardIron known hard-iron bias.
783 * @param listener listener to handle events raised by this calibrator.
784 * @throws IllegalArgumentException if provided hard-iron array does not
785 * have length 3.
786 */
787 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
788 final double[] hardIron,
789 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
790 this(hardIron);
791 this.listener = listener;
792 }
793
794 /**
795 * Constructor.
796 *
797 * @param measurements collection of body magnetic flux density measurements
798 * taken at different frames (positions and orientations).
799 * @param hardIron known hard-iron bias.
800 * @throws IllegalArgumentException if provided hard-iron array does not
801 * have length 3.
802 */
803 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
804 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final double[] hardIron) {
805 this(hardIron);
806 //noinspection unchecked
807 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
808 }
809
810 /**
811 * Constructor.
812 *
813 * @param measurements collection of body magnetic flux density measurements
814 * taken at different frames (positions and orientations).
815 * @param hardIron known hard-iron bias.
816 * @param listener listener to handle events raised by this calibrator.
817 * @throws IllegalArgumentException if provided hard-iron array does not
818 * have length 3.
819 */
820 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
821 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final double[] hardIron,
822 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
823 this(measurements, hardIron);
824 this.listener = listener;
825 }
826
827 /**
828 * Constructor.
829 *
830 * @param commonAxisUsed indicates whether z-axis is assumed to be common
831 * for the accelerometer, gyroscope and magnetometer.
832 * @param hardIron known hard-iron bias.
833 * @throws IllegalArgumentException if provided hard-iron array does not
834 * have length 3.
835 */
836 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
837 final boolean commonAxisUsed, final double[] hardIron) {
838 this(hardIron);
839 this.commonAxisUsed = commonAxisUsed;
840 }
841
842 /**
843 * Constructor.
844 *
845 * @param commonAxisUsed indicates whether z-axis is assumed to be common
846 * for the accelerometer, gyroscope and magnetometer.
847 * @param hardIron known hard-iron bias.
848 * @param listener listener to handle events raised by this calibrator.
849 * @throws IllegalArgumentException if provided hard-iron array does not
850 * have length 3.
851 */
852 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
853 final boolean commonAxisUsed, final double[] hardIron,
854 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
855 this(commonAxisUsed, hardIron);
856 this.listener = listener;
857 }
858
859 /**
860 * Constructor.
861 *
862 * @param measurements collection of body magnetic flux density measurements
863 * taken at different frames (positions and orientations).
864 * @param commonAxisUsed indicates whether z-axis is assumed to be common
865 * for the accelerometer, gyroscope and magnetometer.
866 * @param hardIron known hard-iron bias.
867 * @throws IllegalArgumentException if provided hard-iron array does not
868 * have length 3.
869 */
870 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
871 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
872 final double[] hardIron) {
873 this(commonAxisUsed, hardIron);
874 //noinspection unchecked
875 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
876 }
877
878 /**
879 * Constructor.
880 *
881 * @param measurements collection of body magnetic flux density measurements
882 * taken at different frames (positions and orientations).
883 * @param commonAxisUsed indicates whether z-axis is assumed to be common
884 * for the accelerometer, gyroscope and magnetometer.
885 * @param hardIron known hard-iron bias.
886 * @param listener listener to handle events raised by this calibrator.
887 * @throws IllegalArgumentException if provided hard-iron array does not
888 * have length 3.
889 */
890 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
891 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
892 final boolean commonAxisUsed, final double[] hardIron,
893 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
894 this(measurements, commonAxisUsed, hardIron);
895 this.listener = listener;
896 }
897
898 /**
899 * Constructor.
900 *
901 * @param magneticModel Earth's magnetic model. If null, a default model
902 * will be used instead.
903 * @param hardIron known hard-iron bias.
904 * @throws IllegalArgumentException if provided hard-iron array does not
905 * have length 3.
906 */
907 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
908 final WorldMagneticModel magneticModel, final double[] hardIron) {
909 this(hardIron);
910 this.magneticModel = magneticModel;
911 }
912
913 /**
914 * Constructor.
915 *
916 * @param magneticModel Earth's magnetic model. If null, a default model
917 * will be used instead.
918 * @param hardIron known hard-iron bias.
919 * @param listener listener to handle events raised by this calibrator.
920 * @throws IllegalArgumentException if provided hard-iron array does not
921 * have length 3.
922 */
923 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
924 final WorldMagneticModel magneticModel, final double[] hardIron,
925 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
926 this(magneticModel, hardIron);
927 this.listener = listener;
928 }
929
930 /**
931 * Constructor.
932 *
933 * @param measurements collection of body magnetic flux density measurements
934 * taken at different frames (positions and orientations).
935 * @param magneticModel Earth's magnetic model. If null, a default model
936 * will be used instead.
937 * @param hardIron known hard-iron bias.
938 * @throws IllegalArgumentException if provided hard-iron array does not
939 * have length 3.
940 */
941 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
942 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
943 final WorldMagneticModel magneticModel, final double[] hardIron) {
944 this(magneticModel, hardIron);
945 //noinspection unchecked
946 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
947 }
948
949 /**
950 * Constructor.
951 *
952 * @param measurements collection of body magnetic flux density measurements
953 * taken at different frames (positions and orientations).
954 * @param magneticModel Earth's magnetic model. If null, a default model
955 * will be used instead.
956 * @param hardIron known hard-iron bias.
957 * @param listener listener to handle events raised by this calibrator.
958 * @throws IllegalArgumentException if provided hard-iron array does not
959 * have length 3.
960 */
961 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
962 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
963 final WorldMagneticModel magneticModel, final double[] hardIron,
964 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
965 this(measurements, magneticModel, hardIron);
966 this.listener = listener;
967 }
968
969 /**
970 * Constructor.
971 *
972 * @param commonAxisUsed indicates whether z-axis is assumed to be common
973 * for the accelerometer, gyroscope and magnetometer.
974 * @param magneticModel Earth's magnetic model. If null, a default model
975 * will be used instead.
976 * @param hardIron known hard-iron bias.
977 * @throws IllegalArgumentException if provided hard-iron array does not
978 * have length 3.
979 */
980 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
981 final boolean commonAxisUsed, final WorldMagneticModel magneticModel, final double[] hardIron) {
982 this(magneticModel, hardIron);
983 this.commonAxisUsed = commonAxisUsed;
984 }
985
986 /**
987 * Constructor.
988 *
989 * @param commonAxisUsed indicates whether z-axis is assumed to be common
990 * for the accelerometer, gyroscope and magnetometer.
991 * @param magneticModel Earth's magnetic model. If null, a default model
992 * will be used instead.
993 * @param hardIron known hard-iron bias.
994 * @param listener listener to handle events raised by this calibrator.
995 * @throws IllegalArgumentException if provided hard-iron array does not
996 * have length 3.
997 */
998 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
999 final boolean commonAxisUsed, final WorldMagneticModel magneticModel, final double[] hardIron,
1000 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
1001 this(commonAxisUsed, magneticModel, hardIron);
1002 this.listener = listener;
1003 }
1004
1005 /**
1006 * Constructor.
1007 *
1008 * @param measurements collection of body magnetic flux density measurements
1009 * taken at different frames (positions and orientations).
1010 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1011 * for the accelerometer, gyroscope and magnetometer.
1012 * @param magneticModel Earth's magnetic model. If null, a default model
1013 * will be used instead.
1014 * @param hardIron known hard-iron bias.
1015 * @throws IllegalArgumentException if provided hard-iron array does not
1016 * have length 3.
1017 */
1018 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1019 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
1020 final WorldMagneticModel magneticModel, final double[] hardIron) {
1021 this(commonAxisUsed, magneticModel, hardIron);
1022 //noinspection unchecked
1023 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
1024 }
1025
1026 /**
1027 * Constructor.
1028 *
1029 * @param measurements collection of body magnetic flux density measurements
1030 * taken at different frames (positions and orientations).
1031 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1032 * for the accelerometer, gyroscope and magnetometer.
1033 * @param magneticModel Earth's magnetic model. If null, a default model
1034 * will be used instead.
1035 * @param hardIron known hard-iron bias.
1036 * @param listener listener to handle events raised by this calibrator.
1037 * @throws IllegalArgumentException if provided hard-iron array does not
1038 * have length 3.
1039 */
1040 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1041 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
1042 final WorldMagneticModel magneticModel, final double[] hardIron,
1043 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
1044 this(measurements, commonAxisUsed, magneticModel, hardIron);
1045 this.listener = listener;
1046 }
1047
1048 /**
1049 * Constructor.
1050 *
1051 * @param hardIron known hard-iron bias.
1052 * @throws IllegalArgumentException if provided hard-iron matrix is not
1053 * 3x1.
1054 */
1055 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(final Matrix hardIron) {
1056 try {
1057 setHardIron(hardIron);
1058 } catch (final LockedException ignore) {
1059 // never happens
1060 }
1061 }
1062
1063 /**
1064 * Constructor.
1065 *
1066 * @param hardIron known hard-iron bias.
1067 * @param listener listener to handle events raised by this calibrator.
1068 * @throws IllegalArgumentException if provided hard-iron matrix is not
1069 * 3x1.
1070 */
1071 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1072 final Matrix hardIron,
1073 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
1074 this(hardIron);
1075 this.listener = listener;
1076 }
1077
1078 /**
1079 * Constructor.
1080 *
1081 * @param measurements collection of body magnetic flux density measurements
1082 * taken at different frames (positions and orientations).
1083 * @param hardIron known hard-iron bias.
1084 * @throws IllegalArgumentException if provided hard-iron matrix is not
1085 * 3x1.
1086 */
1087 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1088 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final Matrix hardIron) {
1089 this(hardIron);
1090 //noinspection unchecked
1091 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
1092 }
1093
1094 /**
1095 * Constructor.
1096 *
1097 * @param measurements collection of body magnetic flux density measurements
1098 * taken at different frames (positions and orientations).
1099 * @param hardIron known hard-iron bias.
1100 * @param listener listener to handle events raised by this calibrator.
1101 * @throws IllegalArgumentException if provided hard-iron matrix is not
1102 * 3x1.
1103 */
1104 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1105 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final Matrix hardIron,
1106 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
1107 this(measurements, hardIron);
1108 this.listener = listener;
1109 }
1110
1111 /**
1112 * Constructor.
1113 *
1114 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1115 * for the accelerometer, gyroscope and magnetometer.
1116 * @param hardIron known hard-iron bias.
1117 * @throws IllegalArgumentException if provided hard-iron matrix is not
1118 * 3x1.
1119 */
1120 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1121 final boolean commonAxisUsed, final Matrix hardIron) {
1122 this(hardIron);
1123 this.commonAxisUsed = commonAxisUsed;
1124 }
1125
1126 /**
1127 * Constructor.
1128 *
1129 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1130 * for the accelerometer, gyroscope and magnetometer.
1131 * @param hardIron known hard-iron bias.
1132 * @param listener listener to handle events raised by this calibrator.
1133 * @throws IllegalArgumentException if provided hard-iron matrix is not
1134 * 3x1.
1135 */
1136 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1137 final boolean commonAxisUsed, final Matrix hardIron,
1138 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
1139 this(commonAxisUsed, hardIron);
1140 this.listener = listener;
1141 }
1142
1143 /**
1144 * Constructor.
1145 *
1146 * @param measurements collection of body magnetic flux density measurements
1147 * taken at different frames (positions and orientations).
1148 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1149 * for the accelerometer, gyroscope and magnetometer.
1150 * @param hardIron known hard-iron bias.
1151 * @throws IllegalArgumentException if provided hard-iron matrix is not
1152 * 3x1.
1153 */
1154 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1155 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
1156 final Matrix hardIron) {
1157 this(commonAxisUsed, hardIron);
1158 //noinspection unchecked
1159 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
1160 }
1161
1162 /**
1163 * Constructor.
1164 *
1165 * @param measurements collection of body magnetic flux density measurements
1166 * taken at different frames (positions and orientations).
1167 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1168 * for the accelerometer, gyroscope and magnetometer.
1169 * @param hardIron known hard-iron bias.
1170 * @param listener listener to handle events raised by this calibrator.
1171 * @throws IllegalArgumentException if provided hard-iron matrix is not
1172 * 3x1.
1173 */
1174 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1175 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
1176 final Matrix hardIron,
1177 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
1178 this(measurements, commonAxisUsed, hardIron);
1179 this.listener = listener;
1180 }
1181
1182 /**
1183 * Constructor.
1184 *
1185 * @param magneticModel Earth's magnetic model. If null, a default model
1186 * will be used instead.
1187 * @param hardIron known hard-iron bias.
1188 * @throws IllegalArgumentException if provided hard-iron matrix is not
1189 * 3x1.
1190 */
1191 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1192 final WorldMagneticModel magneticModel, final Matrix hardIron) {
1193 this(hardIron);
1194 this.magneticModel = magneticModel;
1195 }
1196
1197 /**
1198 * Constructor.
1199 *
1200 * @param magneticModel Earth's magnetic model. If null, a default model
1201 * will be used instead.
1202 * @param hardIron known hard-iron bias.
1203 * @param listener listener to handle events raised by this calibrator.
1204 * @throws IllegalArgumentException if provided hard-iron matrix is not
1205 * 3x1.
1206 */
1207 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1208 final WorldMagneticModel magneticModel, final Matrix hardIron,
1209 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
1210 this(magneticModel, hardIron);
1211 this.listener = listener;
1212 }
1213
1214 /**
1215 * Constructor.
1216 *
1217 * @param measurements collection of body magnetic flux density measurements
1218 * taken at different frames (positions and orientations).
1219 * @param magneticModel Earth's magnetic model. If null, a default model
1220 * will be used instead.
1221 * @param hardIron known hard-iron bias.
1222 * @throws IllegalArgumentException if provided hard-iron matrix is not
1223 * 3x1.
1224 */
1225 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1226 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
1227 final WorldMagneticModel magneticModel, final Matrix hardIron) {
1228 this(magneticModel, hardIron);
1229 //noinspection unchecked
1230 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
1231 }
1232
1233 /**
1234 * Constructor.
1235 *
1236 * @param measurements collection of body magnetic flux density measurements
1237 * taken at different frames (positions and orientations).
1238 * @param magneticModel Earth's magnetic model. If null, a default model
1239 * will be used instead.
1240 * @param hardIron known hard-iron bias.
1241 * @param listener listener to handle events raised by this calibrator.
1242 * @throws IllegalArgumentException if provided hard-iron matrix is not
1243 * 3x1.
1244 */
1245 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1246 final Collection<? extends FrameBodyMagneticFluxDensity> measurements,
1247 final WorldMagneticModel magneticModel, final Matrix hardIron,
1248 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
1249 this(measurements, magneticModel, hardIron);
1250 this.listener = listener;
1251 }
1252
1253 /**
1254 * Constructor.
1255 *
1256 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1257 * for the accelerometer, gyroscope and magnetometer.
1258 * @param magneticModel Earth's magnetic model. If null, a default model
1259 * will be used instead.
1260 * @param hardIron known hard-iron bias.
1261 * @throws IllegalArgumentException if provided hard-iron matrix is not
1262 * 3x1.
1263 */
1264 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1265 final boolean commonAxisUsed, final WorldMagneticModel magneticModel, final Matrix hardIron) {
1266 this(magneticModel, hardIron);
1267 this.commonAxisUsed = commonAxisUsed;
1268 }
1269
1270 /**
1271 * Constructor.
1272 *
1273 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1274 * for the accelerometer, gyroscope and magnetometer.
1275 * @param magneticModel Earth's magnetic model. If null, a default model
1276 * will be used instead.
1277 * @param hardIron known hard-iron bias.
1278 * @param listener listener to handle events raised by this calibrator.
1279 * @throws IllegalArgumentException if provided hard-iron matrix is not
1280 * 3x1.
1281 */
1282 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1283 final boolean commonAxisUsed, final WorldMagneticModel magneticModel, final Matrix hardIron,
1284 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
1285 this(commonAxisUsed, magneticModel, hardIron);
1286 this.listener = listener;
1287 }
1288
1289 /**
1290 * Constructor.
1291 *
1292 * @param measurements collection of body magnetic flux density measurements
1293 * taken at different frames (positions and orientations).
1294 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1295 * for the accelerometer, gyroscope and magnetometer.
1296 * @param magneticModel Earth's magnetic model. If null, a default model
1297 * will be used instead.
1298 * @param hardIron known hard-iron bias.
1299 * @throws IllegalArgumentException if provided hard-iron matrix is not
1300 * 3x1.
1301 */
1302 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1303 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
1304 final WorldMagneticModel magneticModel, final Matrix hardIron) {
1305 this(commonAxisUsed, magneticModel, hardIron);
1306 //noinspection unchecked
1307 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
1308 }
1309
1310 /**
1311 * Constructor.
1312 *
1313 * @param measurements collection of body magnetic flux density measurements
1314 * taken at different frames (positions and orientations).
1315 * @param commonAxisUsed indicates whether z-axis is assumed to be common
1316 * for the accelerometer, gyroscope and magnetometer.
1317 * @param magneticModel Earth's magnetic model. If null, a default model
1318 * will be used instead.
1319 * @param hardIron known hard-iron bias.
1320 * @param listener listener to handle events raised by this calibrator.
1321 * @throws IllegalArgumentException if provided hard-iron matrix is not
1322 * 3x1.
1323 */
1324 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibrator(
1325 final Collection<? extends FrameBodyMagneticFluxDensity> measurements, final boolean commonAxisUsed,
1326 final WorldMagneticModel magneticModel, final Matrix hardIron,
1327 final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener) {
1328 this(measurements, commonAxisUsed, magneticModel, hardIron);
1329 this.listener = listener;
1330 }
1331
1332 /**
1333 * Gets x-coordinate of known magnetometer hard-iron bias.
1334 * This is expressed in Teslas (T).
1335 *
1336 * @return x-coordinate of known magnetometer hard-iron bias.
1337 */
1338 @Override
1339 public double getHardIronX() {
1340 return hardIronX;
1341 }
1342
1343 /**
1344 * Sets x-coordinate of known magnetometer hard-iron bias.
1345 * This is expressed in Teslas (T).
1346 *
1347 * @param hardIronX x coordinate of magnetometer hard-iron.
1348 * @throws LockedException if calibrator is currently running.
1349 */
1350 @Override
1351 public void setHardIronX(final double hardIronX) throws LockedException {
1352 if (running) {
1353 throw new LockedException();
1354 }
1355 this.hardIronX = hardIronX;
1356 }
1357
1358 /**
1359 * Gets y-coordinate of known magnetometer hard-iron bias.
1360 * This is expressed in Teslas (T).
1361 *
1362 * @return y-coordinate of known magnetometer hard-iron bias.
1363 */
1364 @Override
1365 public double getHardIronY() {
1366 return hardIronY;
1367 }
1368
1369 /**
1370 * Sets y-coordinate of known magnetometer hard-iron bias.
1371 * This is expressed in Teslas (T).
1372 *
1373 * @param hardIronY y coordinate of magnetometer hard-iron.
1374 * @throws LockedException if calibrator is currently running.
1375 */
1376 @Override
1377 public void setHardIronY(final double hardIronY) throws LockedException {
1378 if (running) {
1379 throw new LockedException();
1380 }
1381 this.hardIronY = hardIronY;
1382 }
1383
1384 /**
1385 * Gets z-coordinate of known magnetometer hard-iron bias.
1386 * This is expressed in Teslas (T).
1387 *
1388 * @return z-coordinate of known magnetometer hard-iron bias.
1389 */
1390 @Override
1391 public double getHardIronZ() {
1392 return hardIronZ;
1393 }
1394
1395 /**
1396 * Sets z-coordinate of known magnetometer hard-iron bias.
1397 * This is expressed in Teslas (T).
1398 *
1399 * @param hardIronZ z coordinate of magnetometer hard-iron.
1400 * @throws LockedException if calibrator is currently running.
1401 */
1402 @Override
1403 public void setHardIronZ(final double hardIronZ) throws LockedException {
1404 if (running) {
1405 throw new LockedException();
1406 }
1407 this.hardIronZ = hardIronZ;
1408 }
1409
1410 /**
1411 * Gets known x coordinate of magnetometer hard-iron.
1412 *
1413 * @return x coordinate of magnetometer hard-iron.
1414 */
1415 @Override
1416 public MagneticFluxDensity getHardIronXAsMagneticFluxDensity() {
1417 return new MagneticFluxDensity(hardIronX, MagneticFluxDensityUnit.TESLA);
1418 }
1419
1420 /**
1421 * Gets known x coordinate of magnetometer hard-iron.
1422 *
1423 * @param result instance where result will be stored.
1424 */
1425 @Override
1426 public void getHardIronXAsMagneticFluxDensity(final MagneticFluxDensity result) {
1427 result.setValue(hardIronX);
1428 result.setUnit(MagneticFluxDensityUnit.TESLA);
1429 }
1430
1431 /**
1432 * Sets known x-coordinate of magnetometer hard-iron.
1433 *
1434 * @param hardIronX known x-coordinate of magnetometer hard-iron.
1435 * @throws LockedException if calibrator is currently running.
1436 */
1437 @Override
1438 public void setHardIronX(final MagneticFluxDensity hardIronX) throws LockedException {
1439 if (running) {
1440 throw new LockedException();
1441 }
1442 this.hardIronX = convertMagneticFluxDensity(hardIronX);
1443 }
1444
1445 /**
1446 * Gets known y coordinate of magnetometer hard-iron.
1447 *
1448 * @return y coordinate of magnetometer hard-iron.
1449 */
1450 @Override
1451 public MagneticFluxDensity getHardIronYAsMagneticFluxDensity() {
1452 return new MagneticFluxDensity(hardIronY, MagneticFluxDensityUnit.TESLA);
1453 }
1454
1455 /**
1456 * Gets known y coordinate of magnetometer hard-iron.
1457 *
1458 * @param result instance where result will be stored.
1459 */
1460 @Override
1461 public void getHardIronYAsMagneticFluxDensity(final MagneticFluxDensity result) {
1462 result.setValue(hardIronY);
1463 result.setUnit(MagneticFluxDensityUnit.TESLA);
1464 }
1465
1466 /**
1467 * Sets known y-coordinate of magnetometer hard-iron.
1468 *
1469 * @param hardIronY known y-coordinate of magnetometer hard-iron.
1470 * @throws LockedException if calibrator is currently running.
1471 */
1472 @Override
1473 public void setHardIronY(final MagneticFluxDensity hardIronY) throws LockedException {
1474 if (running) {
1475 throw new LockedException();
1476 }
1477 this.hardIronY = convertMagneticFluxDensity(hardIronY);
1478 }
1479
1480 /**
1481 * Gets known z coordinate of magnetometer hard-iron.
1482 *
1483 * @return z coordinate of magnetometer hard-iron.
1484 */
1485 @Override
1486 public MagneticFluxDensity getHardIronZAsMagneticFluxDensity() {
1487 return new MagneticFluxDensity(hardIronZ, MagneticFluxDensityUnit.TESLA);
1488 }
1489
1490 /**
1491 * Gets known z coordinate of magnetometer hard-iron.
1492 *
1493 * @param result instance where result will be stored.
1494 */
1495 @Override
1496 public void getHardIronZAsMagneticFluxDensity(final MagneticFluxDensity result) {
1497 result.setValue(hardIronZ);
1498 result.setUnit(MagneticFluxDensityUnit.TESLA);
1499 }
1500
1501 /**
1502 * Sets known z-coordinate of magnetometer hard-iron.
1503 *
1504 * @param hardIronZ known z-coordinate of magnetometer hard-iron.
1505 * @throws LockedException if calibrator is currently running.
1506 */
1507 @Override
1508 public void setHardIronZ(final MagneticFluxDensity hardIronZ) throws LockedException {
1509 if (running) {
1510 throw new LockedException();
1511 }
1512 this.hardIronZ = convertMagneticFluxDensity(hardIronZ);
1513 }
1514
1515 /**
1516 * Sets known hard-iron bias coordinates of magnetometer expressed
1517 * in Teslas (T).
1518 *
1519 * @param hardIronX x-coordinate of magnetometer hard-iron.
1520 * @param hardIronY y-coordinate of magnetometer hard-iron.
1521 * @param hardIronZ z-coordinate of magnetometer hard-iron.
1522 * @throws LockedException if calibrator is currently running.
1523 */
1524 @Override
1525 public void setHardIronCoordinates(
1526 final double hardIronX, final double hardIronY, final double hardIronZ) throws LockedException {
1527 if (running) {
1528 throw new LockedException();
1529 }
1530 this.hardIronX = hardIronX;
1531 this.hardIronY = hardIronY;
1532 this.hardIronZ = hardIronZ;
1533 }
1534
1535 /**
1536 * Sets known hard-iron coordinates.
1537 *
1538 * @param hardIronX x-coordinate of magnetometer hard-iron.
1539 * @param hardIronY y-coordinate of magnetometer hard-iron.
1540 * @param hardIronZ z-coordinate of magnetometer hard-iron.
1541 * @throws LockedException if calibrator is currently running.
1542 */
1543 @Override
1544 public void setHardIronCoordinates(
1545 final MagneticFluxDensity hardIronX, final MagneticFluxDensity hardIronY,
1546 final MagneticFluxDensity hardIronZ) throws LockedException {
1547 if (running) {
1548 throw new LockedException();
1549 }
1550 this.hardIronX = convertMagneticFluxDensity(hardIronX);
1551 this.hardIronY = convertMagneticFluxDensity(hardIronY);
1552 this.hardIronZ = convertMagneticFluxDensity(hardIronZ);
1553 }
1554
1555 /**
1556 * Gets known hard-iron.
1557 *
1558 * @return known hard-iron.
1559 */
1560 @Override
1561 public MagneticFluxDensityTriad getHardIronAsTriad() {
1562 return new MagneticFluxDensityTriad(MagneticFluxDensityUnit.TESLA, hardIronX, hardIronY, hardIronZ);
1563 }
1564
1565 /**
1566 * Gets known hard-iron.
1567 *
1568 * @param result instance where result will be stored.
1569 */
1570 @Override
1571 public void getHardIronAsTriad(final MagneticFluxDensityTriad result) {
1572 result.setValueCoordinatesAndUnit(hardIronX, hardIronY, hardIronZ, MagneticFluxDensityUnit.TESLA);
1573 }
1574
1575 /**
1576 * Sets known hard-iron.
1577 *
1578 * @param hardIron hard-iron to be set.
1579 * @throws LockedException if calibrator is currently running.
1580 */
1581 @Override
1582 public void setHardIron(final MagneticFluxDensityTriad hardIron) throws LockedException {
1583 if (running) {
1584 throw new LockedException();
1585 }
1586
1587 hardIronX = convertMagneticFluxDensity(hardIron.getValueX(), hardIron.getUnit());
1588 hardIronY = convertMagneticFluxDensity(hardIron.getValueY(), hardIron.getUnit());
1589 hardIronZ = convertMagneticFluxDensity(hardIron.getValueZ(), hardIron.getUnit());
1590 }
1591
1592 /**
1593 * Gets a collection of body magnetic flux density measurements taken at different
1594 * frames (positions, orientations and velocities).
1595 * If a single device IMU needs to be calibrated, typically all measurements are
1596 * taken at the same position, with zero velocity and multiple orientations.
1597 * However, if we just want to calibrate a given IMU model (e.g. obtain
1598 * an average and less precise calibration for the IMU of a given phone model),
1599 * we could take measurements collected throughout the planet at multiple positions
1600 * while the phone remains static (e.g. while charging), hence each measurement
1601 * position will change, velocity will remain zero and orientation will be
1602 * typically constant at horizontal orientation while the phone remains on a
1603 * flat surface.
1604 *
1605 * @return a collection of body magnetic flux density measurements taken at different
1606 * frames (positions, orientations and velocities).
1607 */
1608 @Override
1609 public Collection<FrameBodyMagneticFluxDensity> getMeasurements() {
1610 return measurements;
1611 }
1612
1613 /**
1614 * Sets a collection of body magnetic flux density measurements taken at different
1615 * frames (positions, orientations and velocities).
1616 * If a single device IMU needs to be calibrated, typically all measurements are
1617 * taken at the same position, with zero velocity and multiple orientations.
1618 * However, if we just want to calibrate the a given IMU model (e.g. obtain
1619 * an average and less precise calibration for the IMU of a given phone model),
1620 * we could take measurements collected throughout the planet at multiple positions
1621 * while the phone remains static (e.g. while charging), hence each measurement
1622 * position will change, velocity will remain zero and orientation will be
1623 * typically constant at horizontal orientation while the phone remains on a
1624 * flat surface.
1625 *
1626 * @param measurements collection of body magnetic flux density measurements
1627 * taken at different frames (positions, orientations
1628 * and velocities).
1629 * @throws LockedException if estimator is currently running.
1630 */
1631 @Override
1632 public void setMeasurements(
1633 final Collection<? extends FrameBodyMagneticFluxDensity> measurements) throws LockedException {
1634 if (running) {
1635 throw new LockedException();
1636 }
1637 //noinspection unchecked
1638 this.measurements = (Collection<FrameBodyMagneticFluxDensity>) measurements;
1639 }
1640
1641 /**
1642 * Indicates the type of measurement used by this calibrator.
1643 *
1644 * @return type of measurement used by this calibrator.
1645 */
1646 @Override
1647 public MagnetometerCalibratorMeasurementType getMeasurementType() {
1648 return MagnetometerCalibratorMeasurementType.FRAME_BODY_MAGNETIC_FLUX_DENSITY;
1649 }
1650
1651 /**
1652 * Indicates whether this calibrator requires ordered measurements in a
1653 * list or not.
1654 *
1655 * @return true if measurements must be ordered, false otherwise.
1656 */
1657 @Override
1658 public boolean isOrderedMeasurementsRequired() {
1659 return false;
1660 }
1661
1662 /**
1663 * Indicates whether this calibrator requires quality scores for each
1664 * measurement or not.
1665 *
1666 * @return true if quality scores are required, false otherwise.
1667 */
1668 @Override
1669 public boolean isQualityScoresRequired() {
1670 return false;
1671 }
1672
1673 /**
1674 * Indicates whether z-axis is assumed to be common for accelerometer,
1675 * gyroscope and magnetometer.
1676 * When enabled, this eliminates 3 variables from Mm (soft-iron) matrix.
1677 *
1678 * @return true if z-axis is assumed to be common for accelerometer,
1679 * gyroscope and magnetometer, false otherwise.
1680 */
1681 @Override
1682 public boolean isCommonAxisUsed() {
1683 return commonAxisUsed;
1684 }
1685
1686 /**
1687 * Specifies whether z-axis is assumed to be common for accelerometer and
1688 * gyroscope.
1689 * When enabled, this eliminates 3 variables from Mm matrix.
1690 *
1691 * @param commonAxisUsed true if z-axis is assumed to be common for
1692 * accelerometer, gyroscope and magnetometer, false
1693 * otherwise.
1694 * @throws LockedException if estimator is currently running.
1695 */
1696 @Override
1697 public void setCommonAxisUsed(final boolean commonAxisUsed) throws LockedException {
1698 if (running) {
1699 throw new LockedException();
1700 }
1701
1702 this.commonAxisUsed = commonAxisUsed;
1703 }
1704
1705 /**
1706 * Gets listener to handle events raised by this calibrator.
1707 *
1708 * @return listener to handle events raised by this calibrator.
1709 */
1710 @Override
1711 public KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener getListener() {
1712 return listener;
1713 }
1714
1715 /**
1716 * Sets listener to handle events raised by this calibrator.
1717 *
1718 * @param listener listener to handle events raised by this calibrator.
1719 * @throws LockedException if estimator is currently running.
1720 */
1721 @Override
1722 public void setListener(final KnownHardIronAndFrameMagnetometerLinearLeastSquaresCalibratorListener listener)
1723 throws LockedException {
1724 if (running) {
1725 throw new LockedException();
1726 }
1727
1728 this.listener = listener;
1729 }
1730
1731 /**
1732 * Gets minimum number of required measurements.
1733 *
1734 * @return minimum number of required measurements.
1735 */
1736 @Override
1737 public int getMinimumRequiredMeasurements() {
1738 return MINIMUM_MEASUREMENTS;
1739 }
1740
1741 /**
1742 * Indicates whether calibrator is ready to start the estimator.
1743 *
1744 * @return true if calibrator is ready, false otherwise.
1745 */
1746 @Override
1747 public boolean isReady() {
1748 return measurements != null && measurements.size() >= MINIMUM_MEASUREMENTS;
1749 }
1750
1751 /**
1752 * Indicates whether calibrator is currently running or no.
1753 *
1754 * @return true if calibrator is running, false otherwise.
1755 */
1756 @Override
1757 public boolean isRunning() {
1758 return running;
1759 }
1760
1761 /**
1762 * Gets Earth's magnetic model.
1763 *
1764 * @return Earth's magnetic model or null if not provided.
1765 */
1766 public WorldMagneticModel getMagneticModel() {
1767 return magneticModel;
1768 }
1769
1770 /**
1771 * Sets Earth's magnetic model.
1772 * If not provided a default model will be loaded internally.
1773 *
1774 * @param magneticModel Earth's magnetic model to be set.
1775 * @throws LockedException if calibrator is currently running.
1776 */
1777 public void setMagneticModel(final WorldMagneticModel magneticModel) throws LockedException {
1778 if (running) {
1779 throw new LockedException();
1780 }
1781 this.magneticModel = magneticModel;
1782 }
1783
1784 /**
1785 * Gets known hard-iron bias as an array.
1786 * Array values are expressed in Teslas (T).
1787 *
1788 * @return array containing coordinates of initial bias.
1789 */
1790 @Override
1791 public double[] getHardIron() {
1792 final var result = new double[BodyMagneticFluxDensity.COMPONENTS];
1793 getHardIron(result);
1794 return result;
1795 }
1796
1797 /**
1798 * Gets known hard-iron bias as an array.
1799 * Array values are expressed in Teslas (T).
1800 *
1801 * @param result instance where result data will be copied to.
1802 * @throws IllegalArgumentException if provided array does not have
1803 * length 3.
1804 */
1805 @Override
1806 public void getHardIron(final double[] result) {
1807 if (result.length != BodyMagneticFluxDensity.COMPONENTS) {
1808 throw new IllegalArgumentException();
1809 }
1810 result[0] = hardIronX;
1811 result[1] = hardIronY;
1812 result[2] = hardIronZ;
1813 }
1814
1815 /**
1816 * Sets known hard-iron bias as an array.
1817 * Array values are expressed in Teslas (T).
1818 *
1819 * @param hardIron known hard-iron bias.
1820 * @throws LockedException if calibrator is currently running.
1821 * @throws IllegalArgumentException if provided array does not have
1822 * length 3.
1823 */
1824 @Override
1825 public void setHardIron(final double[] hardIron) throws LockedException {
1826 if (running) {
1827 throw new LockedException();
1828 }
1829
1830 if (hardIron.length != BodyMagneticFluxDensity.COMPONENTS) {
1831 throw new IllegalArgumentException();
1832 }
1833 hardIronX = hardIron[0];
1834 hardIronY = hardIron[1];
1835 hardIronZ = hardIron[2];
1836 }
1837
1838 /**
1839 * Gets known hard-iron bias as a column matrix.
1840 *
1841 * @return hard-iron bias as a column matrix.
1842 */
1843 @Override
1844 public Matrix getHardIronMatrix() {
1845 Matrix result;
1846 try {
1847 result = new Matrix(BodyMagneticFluxDensity.COMPONENTS, 1);
1848 getHardIronMatrix(result);
1849 } catch (final WrongSizeException ignore) {
1850 // never happens
1851 result = null;
1852 }
1853 return result;
1854 }
1855
1856 /**
1857 * Gets known hard-iron bias as a column matrix.
1858 *
1859 * @param result instance where result data will be copied to.
1860 * @throws IllegalArgumentException if provided matrix is not 3x1.
1861 */
1862 @Override
1863 public void getHardIronMatrix(final Matrix result) {
1864 if (result.getRows() != BodyMagneticFluxDensity.COMPONENTS || result.getColumns() != 1) {
1865 throw new IllegalArgumentException();
1866 }
1867 result.setElementAtIndex(0, hardIronX);
1868 result.setElementAtIndex(1, hardIronY);
1869 result.setElementAtIndex(2, hardIronZ);
1870 }
1871
1872 /**
1873 * Sets known hard-iron bias.
1874 *
1875 * @param hardIron magnetometer hard-iron bias to be set.
1876 * @throws LockedException if calibrator is currently running.
1877 * @throws IllegalArgumentException if provided matrix is not 3x1.
1878 */
1879 @Override
1880 public void setHardIron(final Matrix hardIron) throws LockedException {
1881 if (running) {
1882 throw new LockedException();
1883 }
1884 if (hardIron.getRows() != BodyMagneticFluxDensity.COMPONENTS || hardIron.getColumns() != 1) {
1885 throw new IllegalArgumentException();
1886 }
1887
1888 hardIronX = hardIron.getElementAtIndex(0);
1889 hardIronY = hardIron.getElementAtIndex(1);
1890 hardIronZ = hardIron.getElementAtIndex(2);
1891 }
1892
1893 /**
1894 * Estimates magnetometer calibration parameters containing scale factors
1895 * and cross-coupling errors.
1896 *
1897 * @throws LockedException if calibrator is currently running.
1898 * @throws NotReadyException if calibrator is not ready.
1899 * @throws CalibrationException if calibration fails for numerical reasons.
1900 */
1901 @Override
1902 public void calibrate() throws LockedException, NotReadyException, CalibrationException {
1903 if (running) {
1904 throw new LockedException();
1905 }
1906
1907 if (!isReady()) {
1908 throw new NotReadyException();
1909 }
1910
1911 try {
1912 running = true;
1913
1914 if (listener != null) {
1915 listener.onCalibrateStart(this);
1916 }
1917
1918 if (commonAxisUsed) {
1919 calibrateCommonAxis();
1920 } else {
1921 calibrateGeneral();
1922 }
1923
1924 if (listener != null) {
1925 listener.onCalibrateEnd(this);
1926 }
1927
1928 } catch (final AlgebraException | IOException e) {
1929 throw new CalibrationException(e);
1930 } finally {
1931 running = false;
1932 }
1933 }
1934
1935 /**
1936 * Gets estimated magnetometer soft-iron matrix containing scale factors
1937 * and cross coupling errors.
1938 * This is the product of matrix Tm containing cross coupling errors and Km
1939 * containing scaling factors.
1940 * So tat:
1941 * <pre>
1942 * Mm = [sx mxy mxz] = Tm*Km
1943 * [myx sy myz]
1944 * [mzx mzy sz ]
1945 * </pre>
1946 * Where:
1947 * <pre>
1948 * Km = [sx 0 0 ]
1949 * [0 sy 0 ]
1950 * [0 0 sz]
1951 * </pre>
1952 * and
1953 * <pre>
1954 * Tm = [1 -alphaXy alphaXz ]
1955 * [alphaYx 1 -alphaYz]
1956 * [-alphaZx alphaZy 1 ]
1957 * </pre>
1958 * Hence:
1959 * <pre>
1960 * Mm = [sx mxy mxz] = Tm*Km = [sx -sy * alphaXy sz * alphaXz ]
1961 * [myx sy myz] [sx * alphaYx sy -sz * alphaYz]
1962 * [mzx mzy sz ] [-sx * alphaZx sy * alphaZy sz ]
1963 * </pre>
1964 * This instance allows any 3x3 matrix however, typically alphaYx, alphaZx and alphaZy
1965 * are considered to be zero if the accelerometer z-axis is assumed to be the same
1966 * as the body z-axis. When this is assumed, myx = mzx = mzy = 0 and the Mm matrix
1967 * becomes upper diagonal:
1968 * <pre>
1969 * Mm = [sx mxy mxz]
1970 * [0 sy myz]
1971 * [0 0 sz ]
1972 * </pre>
1973 * Values of this matrix are unit-less.
1974 *
1975 * @return estimated magnetometer soft-iron scale factors and cross coupling errors,
1976 * or null if not available.
1977 */
1978 @Override
1979 public Matrix getEstimatedMm() {
1980 return estimatedMm;
1981 }
1982
1983 /**
1984 * Gets estimated x-axis scale factor.
1985 *
1986 * @return estimated x-axis scale factor or null if not available.
1987 */
1988 @Override
1989 public Double getEstimatedSx() {
1990 return estimatedMm != null ? estimatedMm.getElementAt(0, 0) : null;
1991 }
1992
1993 /**
1994 * Gets estimated y-axis scale factor.
1995 *
1996 * @return estimated y-axis scale factor or null if not available.
1997 */
1998 @Override
1999 public Double getEstimatedSy() {
2000 return estimatedMm != null ? estimatedMm.getElementAt(1, 1) : null;
2001 }
2002
2003 /**
2004 * Gets estimated z-axis scale factor.
2005 *
2006 * @return estimated z-axis scale factor or null if not available.
2007 */
2008 @Override
2009 public Double getEstimatedSz() {
2010 return estimatedMm != null ? estimatedMm.getElementAt(2, 2) : null;
2011 }
2012
2013 /**
2014 * Gets estimated x-y cross-coupling error.
2015 *
2016 * @return estimated x-y cross-coupling error or null if not available.
2017 */
2018 @Override
2019 public Double getEstimatedMxy() {
2020 return estimatedMm != null ? estimatedMm.getElementAt(0, 1) : null;
2021 }
2022
2023 /**
2024 * Gets estimated x-z cross-coupling error.
2025 *
2026 * @return estimated x-z cross-coupling error or null if not available.
2027 */
2028 @Override
2029 public Double getEstimatedMxz() {
2030 return estimatedMm != null ? estimatedMm.getElementAt(0, 2) : null;
2031 }
2032
2033 /**
2034 * Gets estimated y-x cross-coupling error.
2035 *
2036 * @return estimated y-x cross-coupling error or null if not available.
2037 */
2038 @Override
2039 public Double getEstimatedMyx() {
2040 return estimatedMm != null ? estimatedMm.getElementAt(1, 0) : null;
2041 }
2042
2043 /**
2044 * Gets estimated y-z cross-coupling error.
2045 *
2046 * @return estimated y-z cross-coupling error or null if not available.
2047 */
2048 @Override
2049 public Double getEstimatedMyz() {
2050 return estimatedMm != null ? estimatedMm.getElementAt(1, 2) : null;
2051 }
2052
2053 /**
2054 * Gets estimated z-x cross-coupling error.
2055 *
2056 * @return estimated z-x cross-coupling error or null if not available.
2057 */
2058 @Override
2059 public Double getEstimatedMzx() {
2060 return estimatedMm != null ? estimatedMm.getElementAt(2, 0) : null;
2061 }
2062
2063 /**
2064 * Gets estimated z-y cross-coupling error.
2065 *
2066 * @return estimated z-y cross-coupling error or null if not available.
2067 */
2068 @Override
2069 public Double getEstimatedMzy() {
2070 return estimatedMm != null ? estimatedMm.getElementAt(2, 1) : null;
2071 }
2072
2073 /**
2074 * Internal method to perform calibration when common z-axis is assumed
2075 * for the accelerometer, gyroscope and magnetometer.
2076 *
2077 * @throws AlgebraException if there are numerical errors.
2078 * @throws IOException if world magnetic model cannot be loaded.
2079 */
2080 private void calibrateCommonAxis() throws AlgebraException, IOException {
2081 // The magnetometer model is:
2082 // mBmeas = bm + (I + Mm) * mBtrue + w
2083
2084 // Ideally a least squares solution tries to minimize noise component, so:
2085 // mBmeas = bm + (I + Mm) * mBtrue
2086
2087 // Hence:
2088 // [mBmeasx] = [bx] + ( [1 0 0] + [sx mxy mxz]) [mBtruex]
2089 // [mBmeasy] = [by] [0 1 0] [myx sy myz] [mBtruey]
2090 // [mBmeasz] = [bz] [0 0 1] [mzx mzy sz ] [mBtruez]
2091
2092 // where myx = mzx = mzy = 0
2093
2094 // Hence:
2095 // [mBmeasx] = [bx] + ( [1 0 0] + [sx mxy mxz]) [mBtruex]
2096 // [mBmeasy] = [by] [0 1 0] [0 sy myz] [mBtruey]
2097 // [mBmeasz] = [bz] [0 0 1] [0 0 sz ] [mBtruez]
2098
2099 // [mBmeasx] = [bx] + [1+sx mxy mxz ][mBtruex]
2100 // [mBmeasy] [by] [0 1+sy myz ][mBtruey]
2101 // [mBmeasz] [bz] [0 0 1+sz][mBtruez]
2102
2103 // mBmeasx = bx + (1+sx) * mBtruex + mxy * mBtruey + mxz * mBtruez
2104 // mBmeasy = by + (1+sy) * mBtruey + myz * mBtruez
2105 // mBmeasz = bz + (1+sz) * mBtruez
2106
2107 // Where the unknowns are: sx, sy, sz, mxy mxz, myz
2108 // Reordering:
2109 // mBmeasx = bx + mBtruex + sx * mBtruex + mxy * mBtruey + mxz * mBtruez
2110 // mBmeasy = by + mBtruey + sy * mBtruey + myz * mBtruez
2111 // mBmeasz = bz + mBtruez + sz * mBtruez
2112
2113 // mBmeasx - mBtruex - bx = sx * mBtruex + mxy * mBtruey + mxz * mBtruez
2114 // mBmeasy - mBtruey - by = sy * mBtruey + myz * mBtruez
2115 // mBmeasz - mBtruez - bz = sz * mBtruez
2116
2117 // [mBtruex 0 0 mBtruey mBtruez 0 ][sx ] = [mBmeasx - mBtruex - bx]
2118 // [0 mBtruey 0 0 0 mBtruez][sy ] [mBmeasy - mBtruey - by]
2119 // [0 0 mBtruez 0 0 0 ][sz ] [mBmeasz - mBtruez - bz]
2120 // [mxy]
2121 // [mxz]
2122 // [myz]
2123
2124 final WMMEarthMagneticFluxDensityEstimator wmmEstimator;
2125 if (magneticModel != null) {
2126 wmmEstimator = new WMMEarthMagneticFluxDensityEstimator(magneticModel);
2127 } else {
2128 wmmEstimator = new WMMEarthMagneticFluxDensityEstimator();
2129 }
2130
2131 final var expectedMagneticFluxDensity = new BodyMagneticFluxDensity();
2132 final var nedFrame = new NEDFrame();
2133 final var earthB = new NEDMagneticFluxDensity();
2134 final var cbn = new CoordinateTransformation(FrameType.BODY_FRAME, FrameType.LOCAL_NAVIGATION_FRAME);
2135 final var cnb = new CoordinateTransformation(FrameType.LOCAL_NAVIGATION_FRAME, FrameType.BODY_FRAME);
2136
2137 final var rows = EQUATIONS_PER_MEASUREMENT * measurements.size();
2138 final var a = new Matrix(rows, COMMON_Z_AXIS_UNKNOWNS);
2139 final var b = new Matrix(rows, 1);
2140 var i = 0;
2141 for (final var measurement : measurements) {
2142 final var measuredMagneticFluxDensity = measurement.getMagneticFluxDensity();
2143
2144 // estimate Earth magnetic flux density at frame position and
2145 // timestamp using WMM
2146 final var ecefFrame = measurement.getFrame();
2147 ECEFtoNEDFrameConverter.convertECEFtoNED(ecefFrame, nedFrame);
2148
2149 final var year = measurement.getYear();
2150
2151 final var latitude = nedFrame.getLatitude();
2152 final var longitude = nedFrame.getLongitude();
2153 final var height = nedFrame.getHeight();
2154
2155 nedFrame.getCoordinateTransformation(cbn);
2156 cbn.inverse(cnb);
2157
2158 wmmEstimator.estimate(latitude, longitude, height, year, earthB);
2159
2160 // estimate expected body magnetic flux density taking into
2161 // account body attitude (inverse of frame orientation) and
2162 // estimated Earth magnetic flux density
2163 BodyMagneticFluxDensityEstimator.estimate(earthB, cnb, expectedMagneticFluxDensity);
2164
2165 final var bMeasX = measuredMagneticFluxDensity.getBx();
2166 final var bMeasY = measuredMagneticFluxDensity.getBy();
2167 final var bMeasZ = measuredMagneticFluxDensity.getBz();
2168
2169 final var bTrueX = expectedMagneticFluxDensity.getBx();
2170 final var bTrueY = expectedMagneticFluxDensity.getBy();
2171 final var bTrueZ = expectedMagneticFluxDensity.getBz();
2172
2173 a.setElementAt(i, 0, bTrueX);
2174 a.setElementAt(i, 1, 0.0);
2175 a.setElementAt(i, 2, 0.0);
2176 a.setElementAt(i, 3, bTrueY);
2177 a.setElementAt(i, 4, bTrueZ);
2178 a.setElementAt(i, 5, 0.0);
2179
2180 b.setElementAtIndex(i, bMeasX - bTrueX - hardIronX);
2181 i++;
2182
2183 a.setElementAt(i, 0, 0.0);
2184 a.setElementAt(i, 1, bTrueY);
2185 a.setElementAt(i, 2, 0.0);
2186 a.setElementAt(i, 3, 0.0);
2187 a.setElementAt(i, 4, 0.0);
2188 a.setElementAt(i, 5, bTrueZ);
2189
2190 b.setElementAtIndex(i, bMeasY - bTrueY - hardIronY);
2191 i++;
2192
2193 a.setElementAt(i, 0, 0.0);
2194 a.setElementAt(i, 1, 0.0);
2195 a.setElementAt(i, 2, bTrueZ);
2196 a.setElementAt(i, 3, 0.0);
2197 a.setElementAt(i, 4, 0.0);
2198 a.setElementAt(i, 5, 0.0);
2199
2200 b.setElementAtIndex(i, bMeasZ - bTrueZ - hardIronZ);
2201 i++;
2202 }
2203
2204 final var unknowns = Utils.solve(a, b);
2205
2206 final var sx = unknowns.getElementAtIndex(0);
2207 final var sy = unknowns.getElementAtIndex(1);
2208 final var sz = unknowns.getElementAtIndex(2);
2209 final var mxy = unknowns.getElementAtIndex(3);
2210 final var mxz = unknowns.getElementAtIndex(4);
2211 final var myz = unknowns.getElementAtIndex(5);
2212
2213 fillMm(sx, sy, sz, mxy, mxz, 0.0, myz, 0.0, 0.0);
2214 }
2215
2216 /**
2217 * Internal method to perform general calibration.
2218 *
2219 * @throws AlgebraException if there are numerical errors.
2220 * @throws IOException if world magnetic model cannot be loaded.
2221 */
2222 private void calibrateGeneral() throws AlgebraException, IOException {
2223 // The magnetometer model is:
2224 // mBmeas = bm + (I + Mm) * mBtrue + w
2225
2226 // Ideally a least squares solution tries to minimize noise component, so:
2227 // mBmeas = bm + (I + Mm) * mBtrue
2228
2229 // Hence:
2230 // [mBmeasx] = [bx] + ( [1 0 0] + [sx mxy mxz]) [mBtruex]
2231 // [mBmeasy] = [by] [0 1 0] [myx sy myz] [mBtruey]
2232 // [mBmeasz] = [bz] [0 0 1] [mzx mzy sz ] [mBtruez]
2233
2234 // [mBmeasx] = [bx] + [1+sx mxy mxz ][mBtruex]
2235 // [mBmeasy] [by] [myx 1+sy myz ][mBtruey]
2236 // [mBmeasz] [bz] [mzx mzy 1+sz][mBtruez]
2237
2238 // mBmeasx = bx + (1+sx) * mBtruex + mxy * mBtruey + mxz * mBtruez
2239 // mBmeasy = by + myx * mBtruex + (1+sy) * mBtruey + myz * mBtruez
2240 // mBmeasz = bz + mzx * mBtruex + mzy * mBtruey + (1+sz) * mBtruez
2241
2242 // Where the unknowns are: bx, by, bz, sx, sy, sz, mxy mxz, myx, myz, mzx, mzy
2243 // Reordering:
2244 // mBmeasx = bx + mBtruex + sx * mBtruex + mxy * mBtruey + mxz * mBtruez
2245 // mBmeasy = by + myx * mBtruex + mBtruey + sy * mBtruey + myz * mBtruez
2246 // mBmeasz = bz + mzx * mBtruex + mzy * mBtruey + mBtruez + sz * mBtruez
2247
2248 // mBmeasx - mBtruex - bx = sx * mBtruex + mxy * mBtruey + mxz * mBtruez
2249 // mBmeasy - mBtruey - by = myx * mBtruex + sy * mBtruey + myz * mBtruez
2250 // mBmeasz - mBtruez - bz = mzx * mBtruex + mzy * mBtruey + sz * mBtruez
2251
2252 // [mBtruex 0 0 mBtruey mBtruez 0 0 0 0 ][sx ] = [mBmeasx - mBtruex - bx]
2253 // [0 mBtruey 0 0 0 mBtruex mBtruez 0 0 ][sy ] [mBmeasy - mBtruey - by]
2254 // [0 0 mBtruez 0 0 0 0 mBtruex mBtruey][sz ] [mBmeasz - mBtruez - bz]
2255 // [mxy]
2256 // [mxz]
2257 // [myx]
2258 // [myz]
2259 // [mzx]
2260 // [mzy]
2261
2262 final WMMEarthMagneticFluxDensityEstimator wmmEstimator;
2263 if (magneticModel != null) {
2264 wmmEstimator = new WMMEarthMagneticFluxDensityEstimator(magneticModel);
2265 } else {
2266 wmmEstimator = new WMMEarthMagneticFluxDensityEstimator();
2267 }
2268
2269 final var expectedMagneticFluxDensity = new BodyMagneticFluxDensity();
2270 final var nedFrame = new NEDFrame();
2271 final var earthB = new NEDMagneticFluxDensity();
2272 final var cbn = new CoordinateTransformation(FrameType.BODY_FRAME, FrameType.LOCAL_NAVIGATION_FRAME);
2273 final var cnb = new CoordinateTransformation(FrameType.LOCAL_NAVIGATION_FRAME, FrameType.BODY_FRAME);
2274
2275 final var rows = EQUATIONS_PER_MEASUREMENT * measurements.size();
2276 final var a = new Matrix(rows, GENERAL_UNKNOWNS);
2277 final var b = new Matrix(rows, 1);
2278 var i = 0;
2279 for (final var measurement : measurements) {
2280 final var measuredMagneticFluxDensity = measurement.getMagneticFluxDensity();
2281
2282 // estimate Earth magnetic flux density at frame position and
2283 // timestamp using WMM
2284 final var ecefFrame = measurement.getFrame();
2285 ECEFtoNEDFrameConverter.convertECEFtoNED(ecefFrame, nedFrame);
2286
2287 final var year = measurement.getYear();
2288
2289 final var latitude = nedFrame.getLatitude();
2290 final var longitude = nedFrame.getLongitude();
2291 final var height = nedFrame.getHeight();
2292
2293 nedFrame.getCoordinateTransformation(cbn);
2294 cbn.inverse(cnb);
2295
2296 wmmEstimator.estimate(latitude, longitude, height, year, earthB);
2297
2298 // estimate expected body magnetic flux density taking into
2299 // account body attitude (inverse of frame orientation) and
2300 // estimated Earth magnetic flux density
2301 BodyMagneticFluxDensityEstimator.estimate(earthB, cnb, expectedMagneticFluxDensity);
2302
2303 final var bMeasX = measuredMagneticFluxDensity.getBx();
2304 final var bMeasY = measuredMagneticFluxDensity.getBy();
2305 final var bMeasZ = measuredMagneticFluxDensity.getBz();
2306
2307 final var bTrueX = expectedMagneticFluxDensity.getBx();
2308 final var bTrueY = expectedMagneticFluxDensity.getBy();
2309 final var bTrueZ = expectedMagneticFluxDensity.getBz();
2310
2311 a.setElementAt(i, 0, bTrueX);
2312 a.setElementAt(i, 1, 0.0);
2313 a.setElementAt(i, 2, 0.0);
2314 a.setElementAt(i, 3, bTrueY);
2315 a.setElementAt(i, 4, bTrueZ);
2316 a.setElementAt(i, 5, 0.0);
2317 a.setElementAt(i, 6, 0.0);
2318 a.setElementAt(i, 7, 0.0);
2319 a.setElementAt(i, 8, 0.0);
2320
2321 b.setElementAtIndex(i, bMeasX - bTrueX - hardIronX);
2322 i++;
2323
2324 a.setElementAt(i, 0, 0.0);
2325 a.setElementAt(i, 1, bTrueY);
2326 a.setElementAt(i, 2, 0.0);
2327 a.setElementAt(i, 3, 0.0);
2328 a.setElementAt(i, 4, 0.0);
2329 a.setElementAt(i, 5, bTrueX);
2330 a.setElementAt(i, 6, bTrueZ);
2331 a.setElementAt(i, 7, 0.0);
2332 a.setElementAt(i, 8, 0.0);
2333
2334 b.setElementAtIndex(i, bMeasY - bTrueY - hardIronY);
2335 i++;
2336
2337 a.setElementAt(i, 0, 0.0);
2338 a.setElementAt(i, 1, 0.0);
2339 a.setElementAt(i, 2, bTrueZ);
2340 a.setElementAt(i, 3, 0.0);
2341 a.setElementAt(i, 4, 0.0);
2342 a.setElementAt(i, 5, 0.0);
2343 a.setElementAt(i, 6, 0.0);
2344 a.setElementAt(i, 7, bTrueX);
2345 a.setElementAt(i, 8, bTrueY);
2346
2347 b.setElementAtIndex(i, bMeasZ - bTrueZ - hardIronZ);
2348 i++;
2349 }
2350
2351 final var unknowns = Utils.solve(a, b);
2352
2353 final var sx = unknowns.getElementAtIndex(0);
2354 final var sy = unknowns.getElementAtIndex(1);
2355 final var sz = unknowns.getElementAtIndex(2);
2356 final var mxy = unknowns.getElementAtIndex(3);
2357 final var mxz = unknowns.getElementAtIndex(4);
2358 final var myx = unknowns.getElementAtIndex(5);
2359 final var myz = unknowns.getElementAtIndex(6);
2360 final var mzx = unknowns.getElementAtIndex(7);
2361 final var mzy = unknowns.getElementAtIndex(8);
2362
2363 fillMm(sx, sy, sz, mxy, mxz, myx, myz, mzx, mzy);
2364 }
2365
2366 /**
2367 * Fills scale factor and cross coupling error matrix with estimated values.
2368 *
2369 * @param sx x scale factor
2370 * @param sy y scale factor
2371 * @param sz z scale factor
2372 * @param mxy x-y cross coupling
2373 * @param mxz x-z cross coupling
2374 * @param myx y-x cross coupling
2375 * @param myz y-z cross coupling
2376 * @param mzx z-x cross coupling
2377 * @param mzy z-y cross coupling
2378 * @throws WrongSizeException never happens.
2379 */
2380 private void fillMm(final double sx, final double sy, final double sz,
2381 final double mxy, final double mxz, final double myx,
2382 final double myz, final double mzx, final double mzy) throws WrongSizeException {
2383 if (estimatedMm == null) {
2384 estimatedMm = new Matrix(BodyMagneticFluxDensity.COMPONENTS, BodyMagneticFluxDensity.COMPONENTS);
2385 }
2386
2387 estimatedMm.setElementAt(0, 0, sx);
2388 estimatedMm.setElementAt(1, 0, myx);
2389 estimatedMm.setElementAt(2, 0, mzx);
2390
2391 estimatedMm.setElementAt(0, 1, mxy);
2392 estimatedMm.setElementAt(1, 1, sy);
2393 estimatedMm.setElementAt(2, 1, mzy);
2394
2395 estimatedMm.setElementAt(0, 2, mxz);
2396 estimatedMm.setElementAt(1, 2, myz);
2397 estimatedMm.setElementAt(2, 2, sz);
2398 }
2399
2400 /**
2401 * Converts magnetic flux density value and unit to Teslas.
2402 *
2403 * @param value magnetic flux density value.
2404 * @param unit unit of magnetic flux density value.
2405 * @return converted value.
2406 */
2407 private static double convertMagneticFluxDensity(final double value, final MagneticFluxDensityUnit unit) {
2408 return MagneticFluxDensityConverter.convert(value, unit, MagneticFluxDensityUnit.TESLA);
2409 }
2410
2411 /**
2412 * Converts magnetic flux density instance to Teslas.
2413 *
2414 * @param magneticFluxDensity magnetic flux density instance to be converted.
2415 * @return converted value.
2416 */
2417 private static double convertMagneticFluxDensity(final MagneticFluxDensity magneticFluxDensity) {
2418 return convertMagneticFluxDensity(magneticFluxDensity.getValue().doubleValue(), magneticFluxDensity.getUnit());
2419 }
2420 }