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.gyroscope;
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.ECEFPosition;
26 import com.irurueta.navigation.frames.ECEFVelocity;
27 import com.irurueta.navigation.frames.FrameType;
28 import com.irurueta.navigation.frames.InvalidSourceAndDestinationFrameTypeException;
29 import com.irurueta.navigation.frames.NEDFrame;
30 import com.irurueta.navigation.frames.NEDPosition;
31 import com.irurueta.navigation.frames.NEDVelocity;
32 import com.irurueta.navigation.frames.converters.ECEFtoNEDPositionVelocityConverter;
33 import com.irurueta.navigation.frames.converters.NEDtoECEFFrameConverter;
34 import com.irurueta.navigation.frames.converters.NEDtoECEFPositionVelocityConverter;
35 import com.irurueta.navigation.geodesic.Constants;
36 import com.irurueta.navigation.inertial.BodyKinematics;
37 import com.irurueta.navigation.inertial.calibration.AccelerationFixer;
38 import com.irurueta.navigation.inertial.calibration.AngularSpeedTriad;
39 import com.irurueta.navigation.inertial.calibration.CalibrationException;
40 import com.irurueta.navigation.inertial.calibration.StandardDeviationBodyKinematics;
41 import com.irurueta.navigation.inertial.estimators.ECEFKinematicsEstimator;
42 import com.irurueta.numerical.EvaluationException;
43 import com.irurueta.numerical.GradientEstimator;
44 import com.irurueta.numerical.fitting.FittingException;
45 import com.irurueta.numerical.fitting.LevenbergMarquardtMultiDimensionFitter;
46 import com.irurueta.numerical.fitting.LevenbergMarquardtMultiDimensionFunctionEvaluator;
47 import com.irurueta.statistics.MaxIterationsExceededException;
48 import com.irurueta.units.Acceleration;
49 import com.irurueta.units.AccelerationConverter;
50 import com.irurueta.units.AccelerationUnit;
51 import com.irurueta.units.AngularSpeed;
52 import com.irurueta.units.AngularSpeedConverter;
53 import com.irurueta.units.AngularSpeedUnit;
54 import com.irurueta.units.Time;
55 import com.irurueta.units.TimeConverter;
56 import com.irurueta.units.TimeUnit;
57
58 import java.util.Collection;
59
60 /**
61 * Estimates gyroscope cross couplings and scaling factors
62 * along with G-dependent cross biases introduced on the gyroscope by the
63 * specific forces sensed by the accelerometer.
64 * <p>
65 * This calibrator assumes that the IMU is placed flat on a turntable spinning
66 * at constant speed, but absolute orientation or position of IMU is unknown.
67 * Turntable must rotate fast enough so that Earth rotation effects can be
68 * neglected, bus slow enough so that gyroscope readings can be properly made.
69 * <p>
70 * To use this calibrator at least 7 measurements are needed when common
71 * z-axis is assumed and G-dependent cross biases are ignored, otherwise
72 * at least 10 measurements are required when common z-axis is not assumed.
73 * If G-dependent cross biases are being estimated, then at least 16
74 * measurements are needed when common z-axis is assumed, otherwise at
75 * least 19 measurements are required when common z-axis is not assumed.
76 * <p>
77 * Measured gyroscope angular rates is assumed to follow the model shown below:
78 * <pre>
79 * Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue + w
80 * </pre>
81 * Where:
82 * - Ωmeas is the measured gyroscope angular rates. This is a 3x1 vector.
83 * - bg is the gyroscope bias. Ideally, on a perfect gyroscope, this should be a
84 * 3x1 zero vector.
85 * - I is the 3x3 identity matrix.
86 * - Mg is the 3x3 matrix containing cross-couplings and scaling factors. Ideally, on
87 * a perfect gyroscope, this should be a 3x3 zero matrix.
88 * - Ωtrue is ground-truth gyroscope angular rates.
89 * - Gg is the G-dependent cross biases introduced by the specific forces sensed
90 * by the accelerometer. Ideally, on a perfect gyroscope, this should be a 3x3
91 * zero matrix.
92 * - ftrue is ground-truth specific force. This is a 3x1 vector.
93 * - w is measurement noise. This is a 3x1 vector.
94 */
95 public class KnownBiasTurntableGyroscopeCalibrator implements GyroscopeNonLinearCalibrator,
96 KnownBiasGyroscopeCalibrator, UnorderedStandardDeviationBodyKinematicsGyroscopeCalibrator,
97 AccelerometerDependentGyroscopeCalibrator {
98
99 /**
100 * Indicates whether by default a common z-axis is assumed for both the accelerometer
101 * and gyroscope.
102 */
103 public static final boolean DEFAULT_USE_COMMON_Z_AXIS = true;
104
105 /**
106 * Indicates that by default G-dependent cross biases introduced
107 * by the accelerometer on the gyroscope are estimated.
108 */
109 public static final boolean DEFAULT_ESTIMATE_G_DEPENDENT_CROSS_BIASES = true;
110
111 /**
112 * Number of unknowns when common z-axis is assumed for both the accelerometer
113 * and gyroscope when G-dependent cross biases are being estimated.
114 */
115 public static final int COMMON_Z_AXIS_UNKNOWNS_AND_CROSS_BIASES = 15;
116
117 /**
118 * Number of unknowns for the general case when G-dependent cross
119 * biases are being estimated.
120 */
121 public static final int GENERAL_UNKNOWNS_AND_CROSS_BIASES = 18;
122
123 /**
124 * Number of unknowns when common z-axis is assumed for both
125 * the accelerometer and gyroscope when G-dependent cross biases
126 * are not being estimated.
127 */
128 public static final int COMMON_Z_AXIS_UNKNOWNS = 6;
129
130 /**
131 * Number of unknowns for the general case when G-dependent cross
132 * biases are not being estimated.
133 */
134 public static final int GENERAL_UNKNOWNS = 9;
135
136 /**
137 * Required minimum number of measurements when common z-axis is assumed
138 * and G-dependent cross biases are being estimated.
139 */
140 public static final int MINIMUM_MEASUREMENTS_COMMON_Z_AXIS_AND_CROSS_BIASES =
141 COMMON_Z_AXIS_UNKNOWNS_AND_CROSS_BIASES + 1;
142
143 /**
144 * Required minimum number of measurements for the general case and
145 * G-dependent cross biases are being estimated.
146 */
147 public static final int MINIMUM_MEASUREMENTS_GENERAL_AND_CROSS_BIASES =
148 GENERAL_UNKNOWNS_AND_CROSS_BIASES + 1;
149
150 /**
151 * Required minimum number of measurements when common z-axis is assumed
152 * and G-dependent cross biases are being ignored.
153 */
154 public static final int MINIMUM_MEASUREMENTS_COMMON_Z_AXIS = COMMON_Z_AXIS_UNKNOWNS + 1;
155
156 /**
157 * Required minimum number of measurements for the general case and
158 * G-dependent cross biases are being ignored.
159 */
160 public static final int MINIMUM_MEASUREMENTS_GENERAL = GENERAL_UNKNOWNS + 1;
161
162 /**
163 * Default turntable rotation rate.
164 */
165 public static final double DEFAULT_TURNTABLE_ROTATION_RATE = Constants.EARTH_ROTATION_RATE;
166
167 /**
168 * Default time interval between measurements expressed in seconds (s).
169 * This is a typical value when we have 50 samples per second.
170 */
171 public static final double DEFAULT_TIME_INTERVAL = 0.02;
172
173 /**
174 * Levenberg-Marquardt fitter to find a non-linear solution.
175 */
176 private final LevenbergMarquardtMultiDimensionFitter fitter = new LevenbergMarquardtMultiDimensionFitter();
177
178 /**
179 * Known x-coordinate of accelerometer bias to be used to fix measured
180 * specific force and find cross biases introduced by the accelerometer.
181 * This is expressed in meters per squared second (m/s^2).
182 */
183 private double accelerometerBiasX;
184
185 /**
186 * Known y-coordinate of accelerometer bias to be used to fix measured
187 * specific force and find cross biases introduced by the accelerometer.
188 * This is expressed in meters per squared second (m/s^2).
189 */
190 private double accelerometerBiasY;
191
192 /**
193 * Known z-coordinate of accelerometer bias to be used to fix measured
194 * specific force and find cross biases introduced by the accelerometer.
195 * This is expressed in meters per squared second (m/s^2).
196 */
197 private double accelerometerBiasZ;
198
199 /**
200 * Known accelerometer x scaling factor to be used to fix measured
201 * specific force and find cross biases introduced by the accelerometer.
202 */
203 private double accelerometerSx;
204
205 /**
206 * Known accelerometer y scaling factor to be used to fix measured
207 * specific force and find cross biases introduced by the accelerometer.
208 */
209 private double accelerometerSy;
210
211 /**
212 * Known accelerometer z scaling factor to be used to fix measured
213 * specific force and find cross biases introduced by the accelerometer.
214 */
215 private double accelerometerSz;
216
217 /**
218 * Known accelerometer x-y cross coupling error to be used to fix measured
219 * specific force and find cross biases introduced by the accelerometer.
220 */
221 private double accelerometerMxy;
222
223 /**
224 * Know accelerometer x-z cross coupling error to be used to fix measured
225 * specific force and find cross biases introduced by the accelerometer.
226 */
227 private double accelerometerMxz;
228
229 /**
230 * Known accelerometer y-x cross coupling error to be used to fix measured
231 * specific force and find cross biases introduced by the accelerometer.
232 */
233 private double accelerometerMyx;
234
235 /**
236 * Known accelerometer y-z cross coupling error to be used to fix measured
237 * specific force and find cross biases introduced by the accelerometer.
238 */
239 private double accelerometerMyz;
240
241 /**
242 * Known accelerometer z-x cross coupling error to be used to fix measured
243 * specific force and find cross biases introduced by the accelerometer.
244 */
245 private double accelerometerMzx;
246
247 /**
248 * Known accelerometer z-y cross coupling error to be used to fix measured
249 * specific force and find cross biases introduced by the accelerometer.
250 */
251 private double accelerometerMzy;
252
253 /**
254 * Known x-coordinate of gyroscope bias.
255 * This is expressed in radians per second (rad/s).
256 */
257 private double biasX;
258
259 /**
260 * Known y-coordinate of gyroscope bias.
261 * This is expressed in radians per second (rad/s).
262 */
263 private double biasY;
264
265 /**
266 * Known z-coordinate of gyroscope bias.
267 * This is expressed in radians per second (rad/s).
268 */
269 private double biasZ;
270
271 /**
272 * Initial gyroscope x scaling factor.
273 */
274 private double initialSx;
275
276 /**
277 * Initial gyroscope y scaling factor.
278 */
279 private double initialSy;
280
281 /**
282 * Initial gyroscope z scaling factor.
283 */
284 private double initialSz;
285
286 /**
287 * Initial gyroscope x-y cross coupling error.
288 */
289 private double initialMxy;
290
291 /**
292 * Initial gyroscope x-z cross coupling error.
293 */
294 private double initialMxz;
295
296 /**
297 * Initial gyroscope y-x cross coupling error.
298 */
299 private double initialMyx;
300
301 /**
302 * Initial gyroscope y-z cross coupling error.
303 */
304 private double initialMyz;
305
306 /**
307 * Initial gyroscope z-x cross coupling error.
308 */
309 private double initialMzx;
310
311 /**
312 * Initial gyroscope z-y cross coupling error.
313 */
314 private double initialMzy;
315
316 /**
317 * Initial G-dependent cross biases introduced on the gyroscope by the
318 * specific forces sensed by the accelerometer.
319 */
320 private Matrix initialGg;
321
322 /**
323 * Constant rotation rate at which the turntable is spinning.
324 * This is expressed in radians per second (rad/s).
325 */
326 private double turntableRotationRate = DEFAULT_TURNTABLE_ROTATION_RATE;
327
328 /**
329 * Time interval between measurements being captured expressed in
330 * second (s).
331 */
332 private double timeInterval = DEFAULT_TIME_INTERVAL;
333
334 /**
335 * Contains a collection of body kinematics measurements taken at
336 * a given position with different unknown orientations and containing
337 * the standard deviations of accelerometer and gyroscope measurements.
338 */
339 private Collection<StandardDeviationBodyKinematics> measurements;
340
341 /**
342 * Position where body kinematics measures have been taken.
343 */
344 private ECEFPosition position;
345
346 /**
347 * This flag indicates whether z-axis is assumed to be common for accelerometer
348 * and gyroscope.
349 * When enabled, this eliminates 3 variables from Mg matrix.
350 */
351 private boolean commonAxisUsed = DEFAULT_USE_COMMON_Z_AXIS;
352
353 /**
354 * This flag indicates whether G-dependent cross biases are being
355 * estimated or not.
356 * When enabled, this adds 9 variables from Gg matrix.
357 */
358 private boolean estimateGDependentCrossBiases = DEFAULT_ESTIMATE_G_DEPENDENT_CROSS_BIASES;
359
360 /**
361 * Listener to handle events raised by this calibrator.
362 */
363 private KnownBiasTurntableGyroscopeCalibratorListener listener;
364
365 /**
366 * Estimated gyroscope scale factors and cross coupling errors.
367 * This is the product of matrix Tg containing cross coupling errors and Kg
368 * containing scaling factors.
369 * So that:
370 * <pre>
371 * Mg = [sx mxy mxz] = Tg*Kg
372 * [myx sy myz]
373 * [mzx mzy sz ]
374 * </pre>
375 * Where:
376 * <pre>
377 * Kg = [sx 0 0 ]
378 * [0 sy 0 ]
379 * [0 0 sz]
380 * </pre>
381 * and
382 * <pre>
383 * Tg = [1 -alphaXy alphaXz ]
384 * [alphaYx 1 -alphaYz]
385 * [-alphaZx alphaZy 1 ]
386 * </pre>
387 * Hence:
388 * <pre>
389 * Mg = [sx mxy mxz] = Tg*Kg = [sx -sy * alphaXy sz * alphaXz ]
390 * [myx sy myz] [sx * alphaYx sy -sz * alphaYz]
391 * [mzx mzy sz ] [-sx * alphaZx sy * alphaZy sz ]
392 * </pre>
393 * This instance allows any 3x3 matrix however, typically alphaYx, alphaZx and alphaZy
394 * are considered to be zero if the gyroscope z-axis is assumed to be the same
395 * as the body z-axis. When this is assumed, myx = mzx = mzy = 0 and the Mg matrix
396 * becomes upper diagonal:
397 * <pre>
398 * Mg = [sx mxy mxz]
399 * [0 sy myz]
400 * [0 0 sz ]
401 * </pre>
402 * Values of this matrix are unit-less.
403 */
404 private Matrix estimatedMg;
405
406 /**
407 * Estimated G-dependent cross biases introduced on the gyroscope by the
408 * specific forces sensed by the accelerometer.
409 * This instance allows any 3x3 matrix.
410 */
411 private Matrix estimatedGg;
412
413 /**
414 * Estimated covariance matrix for estimated parameters.
415 */
416 private Matrix estimatedCovariance;
417
418 /**
419 * Estimated chi square value.
420 */
421 private double estimatedChiSq;
422
423 /**
424 * Estimated degrees of freedom of chi square value. Degrees of freedom is equal to the number of sampled data
425 * minus the number of estimated parameters.
426 */
427 private int estimatedChiSqDegreesOfFreedom;
428
429 /**
430 * Estimated reduced chi square value. This is equal to estimated chi square value divided by its degrees of
431 * freedom. Ideally this value should be close to 1.0.
432 */
433 private double estimatedReducedChiSq;
434
435 /**
436 * Estimated mean square error respect to provided measurements.
437 */
438 private double estimatedMse;
439
440 /**
441 * Estimated probability of finding a smaller chi square value expressed as a value between 0.0 and 1.0. The smaller
442 * the found chi square value is, the better the fit of the estimated parameters to the actual parameter. Thus, the
443 * smaller the chance of finding a smaller chi square value, then the better the estimated fit is.
444 */
445 private double estimatedP;
446
447 /**
448 * Estimated measure of quality of estimated fit as a value between 0.0 and 1.0. The larger the quality value is,
449 * the better the fit that has been estimated.
450 */
451 private double estimatedQ;
452
453 /**
454 * Indicates whether calibrator is running.
455 */
456 private boolean running;
457
458 /**
459 * Internally holds x-coordinate of measured angular rate during calibration.
460 */
461 private double measAngularRateX;
462
463 /**
464 * Internally holds y-coordinate of measured angular rate during calibration.
465 */
466 private double measAngularRateY;
467
468 /**
469 * Internally holds z-coordinate of measured angular rate during calibration.
470 */
471 private double measAngularRateZ;
472
473 /**
474 * Internally holds x-coordinate of measured specific force during calibration.
475 */
476 private double fmeasX;
477
478 /**
479 * Internally holds y-coordinate of measured specific force during calibration.
480 */
481 private double fmeasY;
482
483 /**
484 * Internally holds z-coordinate of measured specific force during calibration.
485 */
486 private double fmeasZ;
487
488 /**
489 * Internally holds measured angular rate during calibration expressed as
490 * a column matrix.
491 */
492 private Matrix measAngularRate;
493
494 /**
495 * Internally holds measured specific force during calibration expressed as
496 * a column matrix.
497 */
498 private Matrix fmeas;
499
500 /**
501 * Internally holds cross-coupling errors during calibration.
502 */
503 private Matrix m;
504
505 /**
506 * Internally holds inverse of cross-coupling errors during calibration.
507 */
508 private Matrix invM;
509
510 /**
511 * Internally holds biases during calibration.
512 */
513 private Matrix b;
514
515 /**
516 * Internally hold g-dependent cross biases during calibration.
517 */
518 private Matrix g;
519
520 /**
521 * Internally holds computed true angular rate during calibration.
522 */
523 private Matrix trueAngularRate;
524
525 /**
526 * Internally holds computed true specific force during calibration.
527 */
528 private Matrix ftrue;
529
530 /**
531 * Internally holds accelerometer bias during calibration.
532 */
533 private Matrix ba;
534
535 /**
536 * Internally holds accelerometer scaling and cross coupling errors
537 * during calibration.
538 */
539 private Matrix ma;
540
541 /**
542 * Internally holds angular rate bias due to g-dependent cross biases
543 */
544 private Matrix tmp;
545
546 /**
547 * Acceleration fixer.
548 */
549 private final AccelerationFixer accelerationFixer = new AccelerationFixer();
550
551 /**
552 * Constructor.
553 */
554 public KnownBiasTurntableGyroscopeCalibrator() {
555 try {
556 initialGg = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
557 } catch (final WrongSizeException ignore) {
558 // never happens
559 }
560 }
561
562 /**
563 * Constructor.
564 *
565 * @param position position where body kinematics measures
566 * have been taken.
567 * @param turntableRotationRate constant rotation rate at which the
568 * turntable is spinning. Must be
569 * expressed in radians per second (rad/s).
570 * @param timeInterval time interval between measurements being
571 * captured expressed in seconds (s).
572 * @param measurements collection of body kinematics
573 * measurements with standard deviations
574 * taken at the same position with zero
575 * velocity and unknown different
576 * orientations.
577 * @param bias known gyroscope bias. This must be 3x1
578 * and is expressed in radians per second
579 * (rad/s).
580 * @param initialMg initial gyroscope scale factors and
581 * cross coupling errors matrix. Must
582 * be 3x3.
583 * @param initialGg initial gyroscope G-dependent cross
584 * biases introduced on the gyroscope by
585 * the specific forces sensed by the
586 * accelerometer. Must be 3x3.
587 * @throws IllegalArgumentException if any of the provided values does
588 * not have proper size or if either
589 * turntable rotation rate or
590 * time interval is zero or negative.
591 */
592 public KnownBiasTurntableGyroscopeCalibrator(
593 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
594 final Collection<StandardDeviationBodyKinematics> measurements, final Matrix bias, final Matrix initialMg,
595 final Matrix initialGg) {
596 this();
597 this.position = position;
598 this.measurements = measurements;
599 try {
600 setTurntableRotationRate(turntableRotationRate);
601 setTimeInterval(timeInterval);
602 setBias(bias);
603 setInitialMg(initialMg);
604 setInitialGg(initialGg);
605 } catch (final LockedException ignore) {
606 // never happens
607 }
608 }
609
610 /**
611 * Constructor.
612 *
613 * @param position position where body kinematics measures
614 * have been taken.
615 * @param turntableRotationRate constant rotation rate at which the
616 * turntable is spinning. Must be
617 * expressed in radians per second (rad/s).
618 * @param timeInterval time interval between measurements being
619 * captured expressed in seconds (s).
620 * @param measurements collection of body kinematics
621 * measurements with standard deviations
622 * taken at the same position with zero
623 * velocity and unknown different
624 * orientations.
625 * @param bias known gyroscope bias. This must be 3x1
626 * and is expressed in radians per second
627 * (rad/s).
628 * @param initialMg initial gyroscope scale factors and
629 * cross coupling errors matrix. Must
630 * be 3x3.
631 * @param initialGg initial gyroscope G-dependent cross
632 * biases introduced on the gyroscope by
633 * the specific forces sensed by the
634 * accelerometer. Must be 3x3.
635 * @param listener listener to handle events raised by this
636 * calibrator.
637 * @throws IllegalArgumentException if any of the provided values does
638 * not have proper size or if either
639 * turntable rotation rate or
640 * time interval is zero or negative.
641 */
642 public KnownBiasTurntableGyroscopeCalibrator(
643 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
644 final Collection<StandardDeviationBodyKinematics> measurements, final Matrix bias, final Matrix initialMg,
645 final Matrix initialGg, final KnownBiasTurntableGyroscopeCalibratorListener listener) {
646 this(position, turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg);
647 this.listener = listener;
648 }
649
650 /**
651 * Constructor.
652 *
653 * @param position position where body kinematics measures
654 * have been taken.
655 * @param turntableRotationRate constant rotation rate at which the
656 * turntable is spinning. Must be
657 * expressed in radians per second (rad/s).
658 * @param timeInterval time interval between measurements being
659 * captured expressed in seconds (s).
660 * @param measurements collection of body kinematics
661 * measurements with standard deviations
662 * taken at the same position with zero
663 * velocity and unknown different
664 * orientations.
665 * @param bias known gyroscope bias. This must have
666 * length 3 and is expressed in radians
667 * per second (rad/s).
668 * @param initialMg initial gyroscope scale factors and
669 * cross coupling errors matrix. Must
670 * be 3x3.
671 * @param initialGg initial gyroscope G-dependent cross
672 * biases introduced on the gyroscope by
673 * the specific forces sensed by the
674 * accelerometer. Must be 3x3.
675 * @throws IllegalArgumentException if any of the provided values does
676 * not have proper size or if either
677 * turntable rotation rate or
678 * time interval is zero or negative.
679 */
680 public KnownBiasTurntableGyroscopeCalibrator(
681 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
682 final Collection<StandardDeviationBodyKinematics> measurements, final double[] bias, final Matrix initialMg,
683 final Matrix initialGg) {
684 this();
685 this.position = position;
686 this.measurements = measurements;
687 try {
688 setTurntableRotationRate(turntableRotationRate);
689 setTimeInterval(timeInterval);
690 setBias(bias);
691 setInitialMg(initialMg);
692 setInitialGg(initialGg);
693 } catch (final LockedException ignore) {
694 // never happens
695 }
696 }
697
698 /**
699 * Constructor.
700 *
701 * @param position position where body kinematics measures
702 * have been taken.
703 * @param turntableRotationRate constant rotation rate at which the
704 * turntable is spinning. Must be
705 * expressed in radians per second (rad/s).
706 * @param timeInterval time interval between measurements being
707 * captured expressed in seconds (s).
708 * @param measurements collection of body kinematics
709 * measurements with standard deviations
710 * taken at the same position with zero
711 * velocity and unknown different
712 * orientations.
713 * @param bias known gyroscope bias. This must have
714 * length 3 and is expressed in radians
715 * per second (rad/s).
716 * @param initialMg initial gyroscope scale factors and
717 * cross coupling errors matrix. Must
718 * be 3x3.
719 * @param initialGg initial gyroscope G-dependent cross
720 * biases introduced on the gyroscope by
721 * the specific forces sensed by the
722 * accelerometer. Must be 3x3.
723 * @param listener listener to handle events raised by
724 * this calibrator.
725 * @throws IllegalArgumentException if any of the provided values does
726 * not have proper size or if either
727 * turntable rotation rate or
728 * time interval is zero or negative.
729 */
730 public KnownBiasTurntableGyroscopeCalibrator(
731 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
732 final Collection<StandardDeviationBodyKinematics> measurements, final double[] bias, final Matrix initialMg,
733 final Matrix initialGg, final KnownBiasTurntableGyroscopeCalibratorListener listener) {
734 this(position, turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg);
735 this.listener = listener;
736 }
737
738 /**
739 * Constructor.
740 *
741 * @param position position where body kinematics measures
742 * have been taken.
743 * @param turntableRotationRate constant rotation rate at which the
744 * turntable is spinning. Must be
745 * expressed in radians per second (rad/s).
746 * @param timeInterval time interval between measurements being
747 * captured expressed in seconds (s).
748 * @param measurements collection of body kinematics
749 * measurements with standard deviations
750 * taken at the same position with zero
751 * velocity and unknown different
752 * orientations.
753 * @param bias known gyroscope bias. This must have
754 * length 3 and is expressed in radians per
755 * second (rad/s).
756 * @param initialMg initial gyroscope scale factors and
757 * cross coupling errors matrix. Must
758 * be 3x3.
759 * @param initialGg initial gyroscope G-dependent cross
760 * biases introduced on the gyroscope by
761 * the specific forces sensed by the
762 * accelerometer. Must be 3x3.
763 * @param accelerometerBias known accelerometer bias. This must
764 * have length 3 and is expressed in
765 * meters per squared second
766 * (m/s^2).
767 * @param accelerometerMa known accelerometer scale factors and
768 * cross coupling matrix. Must be 3x3.
769 * @throws IllegalArgumentException if any of the provided values does
770 * not have proper size or if either
771 * turntable rotation rate or
772 * time interval is zero or negative.
773 */
774 public KnownBiasTurntableGyroscopeCalibrator(
775 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
776 final Collection<StandardDeviationBodyKinematics> measurements, final double[] bias, final Matrix initialMg,
777 final Matrix initialGg, final double[] accelerometerBias, final Matrix accelerometerMa) {
778 this(position, turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg);
779 try {
780 setAccelerometerBias(accelerometerBias);
781 setAccelerometerMa(accelerometerMa);
782 } catch (final LockedException ignore) {
783 // never happens
784 }
785 }
786
787 /**
788 * Constructor.
789 *
790 * @param position position where body kinematics measures
791 * have been taken.
792 * @param turntableRotationRate constant rotation rate at which the
793 * turntable is spinning. Must be
794 * expressed in radians per second (rad/s).
795 * @param timeInterval time interval between measurements being
796 * captured expressed in seconds (s).
797 * @param measurements collection of body kinematics
798 * measurements with standard deviations
799 * taken at the same position with zero
800 * velocity and unknown different
801 * orientations.
802 * @param bias known gyroscope bias. This must have
803 * length 3 and is expressed in radians per
804 * second (rad/s).
805 * @param initialMg initial gyroscope scale factors and
806 * cross coupling errors matrix. Must
807 * be 3x3.
808 * @param initialGg initial gyroscope G-dependent cross
809 * biases introduced on the gyroscope by
810 * the specific forces sensed by the
811 * accelerometer. Must be 3x3.
812 * @param accelerometerBias known accelerometer bias. This must
813 * have length 3 and is expressed in
814 * meters per squared second (m/s^2).
815 * @param accelerometerMa known accelerometer scale factors and
816 * cross coupling matrix. Must be 3x3.
817 * @param listener listener to handle events raised by
818 * this calibrator.
819 * @throws IllegalArgumentException if any of the provided values does
820 * not have proper size or if either
821 * turntable rotation rate or
822 * time interval is zero or negative.
823 */
824 public KnownBiasTurntableGyroscopeCalibrator(
825 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
826 final Collection<StandardDeviationBodyKinematics> measurements, final double[] bias, final Matrix initialMg,
827 final Matrix initialGg, final double[] accelerometerBias, final Matrix accelerometerMa,
828 final KnownBiasTurntableGyroscopeCalibratorListener listener) {
829 this(position, turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg, accelerometerBias,
830 accelerometerMa);
831 this.listener = listener;
832 }
833
834 /**
835 * Constructor.
836 *
837 * @param position position where body kinematics measures
838 * have been taken.
839 * @param turntableRotationRate constant rotation rate at which the
840 * turntable is spinning. Must be
841 * expressed in radians per second (rad/s).
842 * @param timeInterval time interval between measurements being
843 * captured expressed in seconds (s).
844 * @param measurements collection of body kinematics
845 * measurements with standard deviations
846 * taken at the same position with zero
847 * velocity and unknown different
848 * orientations.
849 * @param bias known gyroscope bias. This must be 3x1
850 * and is expressed in radians per second
851 * (rad/s).
852 * @param initialMg initial gyroscope scale factors and
853 * cross coupling errors matrix. Must
854 * be 3x3.
855 * @param initialGg initial gyroscope G-dependent cross
856 * biases introduced on the gyroscope by
857 * the specific forces sensed by the
858 * accelerometer. Must be 3x3.
859 * @param accelerometerBias known accelerometer bias. This must
860 * have length 3 and is expressed in
861 * meters per squared second
862 * (m/s^2).
863 * @param accelerometerMa known accelerometer scale factors and
864 * cross coupling matrix. Must be 3x3.
865 * @throws IllegalArgumentException if any of the provided values does
866 * not have proper size or if either
867 * turntable rotation rate or
868 * time interval is zero or negative.
869 */
870 public KnownBiasTurntableGyroscopeCalibrator(
871 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
872 final Collection<StandardDeviationBodyKinematics> measurements, final Matrix bias, final Matrix initialMg,
873 final Matrix initialGg, final Matrix accelerometerBias, final Matrix accelerometerMa) {
874 this(position, turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg);
875 try {
876 setAccelerometerBias(accelerometerBias);
877 setAccelerometerMa(accelerometerMa);
878 } catch (final LockedException ignore) {
879 // never happens
880 }
881 }
882
883 /**
884 * Constructor.
885 *
886 * @param position position where body kinematics measures
887 * have been taken.
888 * @param turntableRotationRate constant rotation rate at which the
889 * turntable is spinning. Must be
890 * expressed in radians per second (rad/s).
891 * @param timeInterval time interval between measurements being
892 * captured expressed in seconds (s).
893 * @param measurements collection of body kinematics
894 * measurements with standard deviations
895 * taken at the same position with zero
896 * velocity and unknown different
897 * orientations.
898 * @param bias known gyroscope bias. This must be 3x1
899 * and is expressed in radians per second
900 * (rad/s).
901 * @param initialMg initial gyroscope scale factors and
902 * cross coupling errors matrix. Must
903 * be 3x3.
904 * @param initialGg initial gyroscope G-dependent cross
905 * biases introduced on the gyroscope by
906 * the specific forces sensed by the
907 * accelerometer. Must be 3x3.
908 * @param accelerometerBias known accelerometer bias. This must
909 * have length 3 and is expressed in
910 * meters per squared second (m/s^2).
911 * @param accelerometerMa known accelerometer scale factors and
912 * cross coupling matrix. Must be 3x3.
913 * @param listener listener to handle events raised by
914 * this calibrator.
915 * @throws IllegalArgumentException if any of the provided values does
916 * not have proper size or if either
917 * turntable rotation rate or
918 * time interval is zero or negative.
919 */
920 public KnownBiasTurntableGyroscopeCalibrator(
921 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
922 final Collection<StandardDeviationBodyKinematics> measurements, final Matrix bias, final Matrix initialMg,
923 final Matrix initialGg, final Matrix accelerometerBias, final Matrix accelerometerMa,
924 final KnownBiasTurntableGyroscopeCalibratorListener listener) {
925 this(position, turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg, accelerometerBias,
926 accelerometerMa);
927 this.listener = listener;
928 }
929
930 /**
931 * Constructor.
932 *
933 * @param position position where body kinematics
934 * measures have been taken.
935 * @param turntableRotationRate constant rotation rate at which
936 * the turntable is spinning. Must
937 * be expressed in radians per
938 * second (rad/s).
939 * @param timeInterval time interval between measurements
940 * being captured expressed in
941 * seconds (s).
942 * @param measurements collection of body kinematics
943 * measurements with standard
944 * deviations taken at the same
945 * position with zero velocity
946 * and unknown different
947 * orientations.
948 * @param commonAxisUsed indicates whether z-axis is
949 * assumed to be common for
950 * accelerometer and gyroscope.
951 * @param estimateGDependentCrossBiases true if G-dependent cross biases
952 * will be estimated, false
953 * otherwise.
954 * @param bias known gyroscope bias. This must
955 * be 3x1 and is expressed in
956 * radians per second (rad/s).
957 * @param initialMg initial gyroscope scale factors
958 * and cross coupling errors matrix.
959 * Must be 3x3.
960 * @param initialGg initial gyroscope G-dependent
961 * cross biases introduced on the
962 * gyroscope by the specific
963 * forces sensed by the
964 * accelerometer. Must be 3x3.
965 * @throws IllegalArgumentException if any of the provided values does
966 * not have proper size or if either
967 * turntable rotation rate or
968 * time interval is zero or negative.
969 */
970 public KnownBiasTurntableGyroscopeCalibrator(
971 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
972 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
973 final boolean estimateGDependentCrossBiases, final Matrix bias, final Matrix initialMg,
974 final Matrix initialGg) {
975 this(position, turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg);
976 this.commonAxisUsed = commonAxisUsed;
977 this.estimateGDependentCrossBiases = estimateGDependentCrossBiases;
978 }
979
980 /**
981 * Constructor.
982 *
983 * @param position position where body kinematics
984 * measures have been taken.
985 * @param turntableRotationRate constant rotation rate at which
986 * the turntable is spinning. Must
987 * be expressed in radians per
988 * second (rad/s).
989 * @param timeInterval time interval between measurements
990 * being captured expressed in
991 * seconds (s).
992 * @param measurements collection of body kinematics
993 * measurements with standard
994 * deviations taken at the same
995 * position with zero velocity and
996 * unknown different orientations.
997 * @param commonAxisUsed indicates whether z-axis is
998 * assumed to be common for
999 * accelerometer and gyroscope.
1000 * @param estimateGDependentCrossBiases true if G-dependent cross
1001 * biases will be estimated, false
1002 * otherwise.
1003 * @param bias known gyroscope bias. This must
1004 * be 3x1 and is expressed in
1005 * radians per second (rad/s).
1006 * @param initialMg initial gyroscope scale factors
1007 * and cross coupling errors
1008 * matrix. Must be 3x3.
1009 * @param initialGg initial gyroscope G-dependent
1010 * cross biases introduced on the
1011 * gyroscope by the specific
1012 * forces sensed by the
1013 * accelerometer. Must be 3x3.
1014 * @param listener listener to handle events
1015 * raised by this calibrator.
1016 * @throws IllegalArgumentException if any of the provided values does
1017 * not have proper size or if either
1018 * turntable rotation rate or
1019 * time interval is zero or negative.
1020 */
1021 public KnownBiasTurntableGyroscopeCalibrator(
1022 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
1023 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1024 final boolean estimateGDependentCrossBiases, final Matrix bias, final Matrix initialMg,
1025 final Matrix initialGg, final KnownBiasTurntableGyroscopeCalibratorListener listener) {
1026 this(position, turntableRotationRate, timeInterval, measurements, commonAxisUsed, estimateGDependentCrossBiases,
1027 bias, initialMg, initialGg);
1028 this.listener = listener;
1029 }
1030
1031 /**
1032 * Constructor.
1033 *
1034 * @param position position where body kinematics
1035 * measures have been taken.
1036 * @param turntableRotationRate constant rotation rate at which
1037 * the turntable is spinning. Must
1038 * be expressed in radians per
1039 * second (rad/s).
1040 * @param timeInterval time interval between measurements
1041 * being captured expressed in
1042 * seconds (s).
1043 * @param measurements collection of body kinematics
1044 * measurements with standard
1045 * deviations taken at the same
1046 * position with zero velocity
1047 * and unknown different
1048 * orientations.
1049 * @param commonAxisUsed indicates whether z-axis is
1050 * assumed to be common for
1051 * accelerometer and gyroscope.
1052 * @param estimateGDependentCrossBiases true if G-dependent cross biases
1053 * will be estimated, false
1054 * otherwise.
1055 * @param bias known gyroscope bias. This must
1056 * have length 3 and is expressed
1057 * in radians per second (rad/s).
1058 * @param initialMg initial gyroscope scale factors
1059 * and cross coupling errors matrix.
1060 * Must be 3x3.
1061 * @param initialGg initial gyroscope G-dependent
1062 * cross biases introduced on the
1063 * gyroscope by the specific forces
1064 * sensed by the accelerometer.
1065 * Must be 3x3.
1066 * @throws IllegalArgumentException if any of the provided values does
1067 * not have proper size or if either
1068 * turntable rotation rate or
1069 * time interval is zero or negative.
1070 */
1071 public KnownBiasTurntableGyroscopeCalibrator(
1072 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
1073 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1074 final boolean estimateGDependentCrossBiases, final double[] bias, final Matrix initialMg,
1075 final Matrix initialGg) {
1076 this(position, turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg);
1077 this.commonAxisUsed = commonAxisUsed;
1078 this.estimateGDependentCrossBiases = estimateGDependentCrossBiases;
1079 }
1080
1081 /**
1082 * Constructor.
1083 *
1084 * @param position position where body kinematics
1085 * measures have been taken.
1086 * @param turntableRotationRate constant rotation rate at which
1087 * the turntable is spinning. Must
1088 * be expressed in radians per
1089 * second (rad/s).
1090 * @param timeInterval time interval between measurements
1091 * being captured expressed in
1092 * seconds (s).
1093 * @param measurements collection of body kinematics
1094 * measurements with standard
1095 * deviations taken at the same
1096 * position with zero velocity
1097 * and unknown different
1098 * orientations.
1099 * @param commonAxisUsed indicates whether z-axis is
1100 * assumed to be common for
1101 * accelerometer and gyroscope.
1102 * @param estimateGDependentCrossBiases true if G-dependent cross biases
1103 * will be estimated, false
1104 * otherwise.
1105 * @param bias known gyroscope bias. This must
1106 * have length 3 and is expressed
1107 * in radians per second (rad/s).
1108 * @param initialMg initial gyroscope scale factors
1109 * and cross coupling errors
1110 * matrix. Must be 3x3.
1111 * @param initialGg initial gyroscope G-dependent
1112 * cross biases introduced on the
1113 * gyroscope by the specific forces
1114 * sensed by the accelerometer.
1115 * Must be 3x3.
1116 * @param listener listener to handle events raised
1117 * by this calibrator.
1118 * @throws IllegalArgumentException if any of the provided values does
1119 * not have proper size or if either
1120 * turntable rotation rate or
1121 * time interval is zero or negative.
1122 */
1123 public KnownBiasTurntableGyroscopeCalibrator(
1124 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
1125 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1126 final boolean estimateGDependentCrossBiases, final double[] bias, final Matrix initialMg,
1127 final Matrix initialGg, final KnownBiasTurntableGyroscopeCalibratorListener listener) {
1128 this(position, turntableRotationRate, timeInterval, measurements, commonAxisUsed, estimateGDependentCrossBiases,
1129 bias, initialMg, initialGg);
1130 this.listener = listener;
1131 }
1132
1133 /**
1134 * Constructor.
1135 *
1136 * @param position position where body kinematics
1137 * measures have been taken.
1138 * @param turntableRotationRate constant rotation rate at which
1139 * the turntable is spinning. Must
1140 * be expressed in radians per
1141 * second (rad/s).
1142 * @param timeInterval time interval between measurements
1143 * being captured expressed in
1144 * seconds (s).
1145 * @param measurements collection of body kinematics
1146 * measurements with standard
1147 * deviations taken at the same
1148 * position with zero velocity
1149 * and unknown different
1150 * orientations.
1151 * @param commonAxisUsed indicates whether z-axis is
1152 * assumed to be common for
1153 * accelerometer and gyroscope.
1154 * @param estimateGDependentCrossBiases true if G-dependent cross
1155 * biases will be estimated,
1156 * false otherwise.
1157 * @param bias known gyroscope bias. This must
1158 * have length 3 and is expressed
1159 * in radians per second (rad/s).
1160 * @param initialMg initial gyroscope scale factors
1161 * and cross coupling errors
1162 * matrix. Must be 3x3.
1163 * @param initialGg initial gyroscope G-dependent
1164 * cross biases introduced on the
1165 * gyroscope by the specific forces
1166 * sensed by the accelerometer.
1167 * Must be 3x3.
1168 * @param accelerometerBias known accelerometer bias. This
1169 * must have length 3 and is
1170 * expressed in meters per squared
1171 * second (m/s^2).
1172 * @param accelerometerMa known accelerometer scale factors
1173 * and cross coupling matrix. Must
1174 * be 3x3.
1175 * @throws IllegalArgumentException if any of the provided values does
1176 * not have proper size or if either
1177 * turntable rotation rate or
1178 * time interval is zero or negative.
1179 */
1180 public KnownBiasTurntableGyroscopeCalibrator(
1181 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
1182 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1183 final boolean estimateGDependentCrossBiases, final double[] bias, final Matrix initialMg,
1184 final Matrix initialGg, final double[] accelerometerBias, final Matrix accelerometerMa) {
1185 this(position, turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg, accelerometerBias,
1186 accelerometerMa);
1187 this.commonAxisUsed = commonAxisUsed;
1188 this.estimateGDependentCrossBiases = estimateGDependentCrossBiases;
1189 }
1190
1191 /**
1192 * Constructor.
1193 *
1194 * @param position position where body kinematics
1195 * measures have been taken.
1196 * @param turntableRotationRate constant rotation rate at which
1197 * the turntable is spinning. Must
1198 * be expressed in radians per
1199 * second (rad/s).
1200 * @param timeInterval time interval between measurements
1201 * being captured expressed in
1202 * seconds (s).
1203 * @param measurements collection of body kinematics
1204 * measurements with standard
1205 * deviations taken at the same
1206 * position with zero velocity
1207 * and unknown different
1208 * orientations.
1209 * @param commonAxisUsed indicates whether z-axis is
1210 * assumed to be common for
1211 * accelerometer and gyroscope.
1212 * @param estimateGDependentCrossBiases true if G-dependent cross biases
1213 * will be estimated, false
1214 * otherwise.
1215 * @param bias known gyroscope bias. This must
1216 * have length 3 and is expressed
1217 * in radians per second (rad/s).
1218 * @param initialMg initial gyroscope scale factors
1219 * and cross coupling errors matrix.
1220 * Must be 3x3.
1221 * @param initialGg initial gyroscope G-dependent
1222 * cross biases introduced on the
1223 * gyroscope by the specific forces
1224 * sensed by the accelerometer. Must
1225 * be 3x3.
1226 * @param accelerometerBias known accelerometer bias. This
1227 * must have length 3 and is
1228 * expressed in meters per squared
1229 * second (m/s^2).
1230 * @param accelerometerMa known accelerometer scale factors
1231 * and cross coupling matrix. Must
1232 * be 3x3.
1233 * @param listener listener to handle events raised
1234 * by this calibrator.
1235 * @throws IllegalArgumentException if any of the provided values does
1236 * not have proper size or if either
1237 * turntable rotation rate or
1238 * time interval is zero or negative.
1239 */
1240 public KnownBiasTurntableGyroscopeCalibrator(
1241 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
1242 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1243 final boolean estimateGDependentCrossBiases, final double[] bias, final Matrix initialMg,
1244 final Matrix initialGg, final double[] accelerometerBias, final Matrix accelerometerMa,
1245 final KnownBiasTurntableGyroscopeCalibratorListener listener) {
1246 this(position, turntableRotationRate, timeInterval, measurements, commonAxisUsed, estimateGDependentCrossBiases,
1247 bias, initialMg, initialGg, accelerometerBias, accelerometerMa);
1248 this.listener = listener;
1249 }
1250
1251 /**
1252 * Constructor.
1253 *
1254 * @param position position where body kinematics
1255 * measures have been taken.
1256 * @param turntableRotationRate constant rotation rate at which
1257 * the turntable is spinning. Must
1258 * be expressed in radians per
1259 * second (rad/s).
1260 * @param timeInterval time interval between measurements
1261 * being captured expressed in
1262 * seconds (s).
1263 * @param measurements collection of body kinematics
1264 * measurements with standard
1265 * deviations taken at the same
1266 * position with zero velocity and
1267 * unknown different orientations.
1268 * @param commonAxisUsed indicates whether z-axis is
1269 * assumed to be common for
1270 * accelerometer and gyroscope.
1271 * @param estimateGDependentCrossBiases true if G-dependent cross biases
1272 * will be estimated, false
1273 * otherwise.
1274 * @param bias known gyroscope bias. This must
1275 * be 3x1 and is expressed in
1276 * radians per second (rad/s).
1277 * @param initialMg initial gyroscope scale factors
1278 * and cross coupling errors matrix.
1279 * Must be 3x3.
1280 * @param initialGg initial gyroscope G-dependent
1281 * cross biases introduced on the
1282 * gyroscope by the specific forces
1283 * sensed by the accelerometer. Must
1284 * be 3x3.
1285 * @param accelerometerBias known accelerometer bias. This
1286 * must have length 3 and is
1287 * expressed in meters per squared
1288 * second (m/s^2).
1289 * @param accelerometerMa known accelerometer scale factors
1290 * and cross coupling matrix. Must
1291 * be 3x3.
1292 * @throws IllegalArgumentException if any of the provided values does
1293 * not have proper size or if either
1294 * turntable rotation rate or
1295 * time interval is zero or negative.
1296 */
1297 public KnownBiasTurntableGyroscopeCalibrator(
1298 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
1299 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1300 final boolean estimateGDependentCrossBiases, final Matrix bias, final Matrix initialMg,
1301 final Matrix initialGg, final Matrix accelerometerBias, final Matrix accelerometerMa) {
1302 this(position, turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg, accelerometerBias,
1303 accelerometerMa);
1304 this.commonAxisUsed = commonAxisUsed;
1305 this.estimateGDependentCrossBiases = estimateGDependentCrossBiases;
1306 }
1307
1308 /**
1309 * Constructor.
1310 *
1311 * @param position position where body kinematics
1312 * measures have been taken.
1313 * @param turntableRotationRate constant rotation rate at which
1314 * the turntable is spinning. Must
1315 * be expressed in radians per
1316 * second (rad/s).
1317 * @param timeInterval time interval between measurements
1318 * being captured expressed in
1319 * seconds (s).
1320 * @param measurements collection of body kinematics
1321 * measurements with standard
1322 * deviations taken at the same
1323 * position with zero velocity and
1324 * unknown different orientations.
1325 * @param commonAxisUsed indicates whether z-axis is
1326 * assumed to be common for
1327 * accelerometer and gyroscope.
1328 * @param estimateGDependentCrossBiases true if G-dependent cross biases
1329 * will be estimated, false
1330 * otherwise.
1331 * @param bias known gyroscope bias. This must
1332 * be 3x1 and is expressed in
1333 * radians per second (rad/s).
1334 * @param initialMg initial gyroscope scale factors
1335 * and cross coupling errors matrix.
1336 * Must be 3x3.
1337 * @param initialGg initial gyroscope G-dependent
1338 * cross biases introduced on the
1339 * gyroscope by the specific forces
1340 * sensed by the accelerometer. Must
1341 * be 3x3.
1342 * @param accelerometerBias known accelerometer bias. This
1343 * must have length 3 and is
1344 * expressed in meters per squared
1345 * second (m/s^2).
1346 * @param accelerometerMa known accelerometer scale factors
1347 * and cross coupling matrix. Must
1348 * be 3x3.
1349 * @param listener listener to handle events raised
1350 * by this calibrator.
1351 * @throws IllegalArgumentException if any of the provided values does
1352 * not have proper size or if either
1353 * turntable rotation rate or
1354 * time interval is zero or negative.
1355 */
1356 public KnownBiasTurntableGyroscopeCalibrator(
1357 final ECEFPosition position, final double turntableRotationRate, final double timeInterval,
1358 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1359 final boolean estimateGDependentCrossBiases, final Matrix bias, final Matrix initialMg,
1360 final Matrix initialGg, final Matrix accelerometerBias, final Matrix accelerometerMa,
1361 final KnownBiasTurntableGyroscopeCalibratorListener listener) {
1362 this(position, turntableRotationRate, timeInterval, measurements, commonAxisUsed, estimateGDependentCrossBiases,
1363 bias, initialMg, initialGg, accelerometerBias, accelerometerMa);
1364 this.listener = listener;
1365 }
1366
1367 /**
1368 * Constructor.
1369 *
1370 * @param position position where body kinematics measures
1371 * have been taken.
1372 * @param turntableRotationRate constant rotation rate at which the
1373 * turntable is spinning. Must be
1374 * expressed in radians per second (rad/s).
1375 * @param timeInterval time interval between measurements being
1376 * captured expressed in seconds (s).
1377 * @param measurements collection of body kinematics
1378 * measurements with standard deviations
1379 * taken at the same position with zero
1380 * velocity and unknown different
1381 * orientations.
1382 * @param bias known gyroscope bias. This must be 3x1
1383 * and is expressed in radians per second
1384 * (rad/s).
1385 * @param initialMg initial gyroscope scale factors and
1386 * cross coupling errors matrix. Must
1387 * be 3x3.
1388 * @param initialGg initial gyroscope G-dependent cross
1389 * biases introduced on the gyroscope by
1390 * the specific forces sensed by the
1391 * accelerometer. Must be 3x3.
1392 * @throws IllegalArgumentException if any of the provided values does
1393 * not have proper size or if either
1394 * turntable rotation rate or
1395 * time interval is zero or negative.
1396 */
1397 public KnownBiasTurntableGyroscopeCalibrator(
1398 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1399 final Collection<StandardDeviationBodyKinematics> measurements, final Matrix bias, final Matrix initialMg,
1400 final Matrix initialGg) {
1401 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg);
1402 }
1403
1404 /**
1405 * Constructor.
1406 *
1407 * @param position position where body kinematics measures
1408 * have been taken.
1409 * @param turntableRotationRate constant rotation rate at which the
1410 * turntable is spinning. Must be
1411 * expressed in radians per second (rad/s).
1412 * @param timeInterval time interval between measurements being
1413 * captured expressed in seconds (s).
1414 * @param measurements collection of body kinematics
1415 * measurements with standard deviations
1416 * taken at the same position with zero
1417 * velocity and unknown different
1418 * orientations.
1419 * @param bias known gyroscope bias. This must be 3x1
1420 * and is expressed in radians per second
1421 * (rad/s).
1422 * @param initialMg initial gyroscope scale factors and
1423 * cross coupling errors matrix. Must
1424 * be 3x3.
1425 * @param initialGg initial gyroscope G-dependent cross
1426 * biases introduced on the gyroscope by
1427 * the specific forces sensed by the
1428 * accelerometer. Must be 3x3.
1429 * @param listener listener to handle events raised by this
1430 * calibrator.
1431 * @throws IllegalArgumentException if any of the provided values does
1432 * not have proper size or if either
1433 * turntable rotation rate or
1434 * time interval is zero or negative.
1435 */
1436 public KnownBiasTurntableGyroscopeCalibrator(
1437 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1438 final Collection<StandardDeviationBodyKinematics> measurements, final Matrix bias, final Matrix initialMg,
1439 final Matrix initialGg, final KnownBiasTurntableGyroscopeCalibratorListener listener) {
1440 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg,
1441 listener);
1442 }
1443
1444 /**
1445 * Constructor.
1446 *
1447 * @param position position where body kinematics measures
1448 * have been taken.
1449 * @param turntableRotationRate constant rotation rate at which the
1450 * turntable is spinning. Must be
1451 * expressed in radians per second (rad/s).
1452 * @param timeInterval time interval between measurements being
1453 * captured expressed in seconds (s).
1454 * @param measurements collection of body kinematics
1455 * measurements with standard deviations
1456 * taken at the same position with zero
1457 * velocity and unknown different
1458 * orientations.
1459 * @param bias known gyroscope bias. This must have
1460 * length 3 and is expressed in radians
1461 * per second (rad/s).
1462 * @param initialMg initial gyroscope scale factors and
1463 * cross coupling errors matrix. Must
1464 * be 3x3.
1465 * @param initialGg initial gyroscope G-dependent cross
1466 * biases introduced on the gyroscope by
1467 * the specific forces sensed by the
1468 * accelerometer. Must be 3x3.
1469 * @throws IllegalArgumentException if any of the provided values does
1470 * not have proper size or if either
1471 * turntable rotation rate or
1472 * time interval is zero or negative.
1473 */
1474 public KnownBiasTurntableGyroscopeCalibrator(
1475 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1476 final Collection<StandardDeviationBodyKinematics> measurements, final double[] bias, final Matrix initialMg,
1477 final Matrix initialGg) {
1478 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg);
1479 }
1480
1481 /**
1482 * Constructor.
1483 *
1484 * @param position position where body kinematics measures
1485 * have been taken.
1486 * @param turntableRotationRate constant rotation rate at which the
1487 * turntable is spinning. Must be
1488 * expressed in radians per second (rad/s).
1489 * @param timeInterval time interval between measurements being
1490 * captured expressed in seconds (s).
1491 * @param measurements collection of body kinematics
1492 * measurements with standard deviations
1493 * taken at the same position with zero
1494 * velocity and unknown different
1495 * orientations.
1496 * @param bias known gyroscope bias. This must have
1497 * length 3 and is expressed in radians
1498 * per second (rad/s).
1499 * @param initialMg initial gyroscope scale factors and
1500 * cross coupling errors matrix. Must
1501 * be 3x3.
1502 * @param initialGg initial gyroscope G-dependent cross
1503 * biases introduced on the gyroscope by
1504 * the specific forces sensed by the
1505 * accelerometer. Must be 3x3.
1506 * @param listener listener to handle events raised by
1507 * this calibrator.
1508 * @throws IllegalArgumentException if any of the provided values does
1509 * not have proper size or if either
1510 * turntable rotation rate or
1511 * time interval is zero or negative.
1512 */
1513 public KnownBiasTurntableGyroscopeCalibrator(
1514 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1515 final Collection<StandardDeviationBodyKinematics> measurements, final double[] bias,
1516 final Matrix initialMg, final Matrix initialGg,
1517 final KnownBiasTurntableGyroscopeCalibratorListener listener) {
1518 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg,
1519 listener);
1520 }
1521
1522 /**
1523 * Constructor.
1524 *
1525 * @param position position where body kinematics measures
1526 * have been taken.
1527 * @param turntableRotationRate constant rotation rate at which the
1528 * turntable is spinning. Must be
1529 * expressed in radians per second (rad/s).
1530 * @param timeInterval time interval between measurements being
1531 * captured expressed in seconds (s).
1532 * @param measurements collection of body kinematics
1533 * measurements with standard deviations
1534 * taken at the same position with zero
1535 * velocity and unknown different
1536 * orientations.
1537 * @param bias known gyroscope bias. This must have
1538 * length 3 and is expressed in radians per
1539 * second (rad/s).
1540 * @param initialMg initial gyroscope scale factors and
1541 * cross coupling errors matrix. Must
1542 * be 3x3.
1543 * @param initialGg initial gyroscope G-dependent cross
1544 * biases introduced on the gyroscope by
1545 * the specific forces sensed by the
1546 * accelerometer. Must be 3x3.
1547 * @param accelerometerBias known accelerometer bias. This must
1548 * have length 3 and is expressed in
1549 * meters per squared second
1550 * (m/s^2).
1551 * @param accelerometerMa known accelerometer scale factors and
1552 * cross coupling matrix. Must be 3x3.
1553 * @throws IllegalArgumentException if any of the provided values does
1554 * not have proper size or if either
1555 * turntable rotation rate or
1556 * time interval is zero or negative.
1557 */
1558 public KnownBiasTurntableGyroscopeCalibrator(
1559 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1560 final Collection<StandardDeviationBodyKinematics> measurements, final double[] bias, final Matrix initialMg,
1561 final Matrix initialGg, final double[] accelerometerBias, final Matrix accelerometerMa) {
1562 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg,
1563 accelerometerBias, accelerometerMa);
1564 }
1565
1566 /**
1567 * Constructor.
1568 *
1569 * @param position position where body kinematics measures
1570 * have been taken.
1571 * @param turntableRotationRate constant rotation rate at which the
1572 * turntable is spinning. Must be
1573 * expressed in radians per second (rad/s).
1574 * @param timeInterval time interval between measurements being
1575 * captured expressed in seconds (s).
1576 * @param measurements collection of body kinematics
1577 * measurements with standard deviations
1578 * taken at the same position with zero
1579 * velocity and unknown different
1580 * orientations.
1581 * @param bias known gyroscope bias. This must have
1582 * length 3 and is expressed in radians per
1583 * second (rad/s).
1584 * @param initialMg initial gyroscope scale factors and
1585 * cross coupling errors matrix. Must
1586 * be 3x3.
1587 * @param initialGg initial gyroscope G-dependent cross
1588 * biases introduced on the gyroscope by
1589 * the specific forces sensed by the
1590 * accelerometer. Must be 3x3.
1591 * @param accelerometerBias known accelerometer bias. This must
1592 * have length 3 and is expressed in
1593 * meters per squared second (m/s^2).
1594 * @param accelerometerMa known accelerometer scale factors and
1595 * cross coupling matrix. Must be 3x3.
1596 * @param listener listener to handle events raised by
1597 * this calibrator.
1598 * @throws IllegalArgumentException if any of the provided values does
1599 * not have proper size or if either
1600 * turntable rotation rate or
1601 * time interval is zero or negative.
1602 */
1603 public KnownBiasTurntableGyroscopeCalibrator(
1604 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1605 final Collection<StandardDeviationBodyKinematics> measurements, final double[] bias, final Matrix initialMg,
1606 final Matrix initialGg, final double[] accelerometerBias, final Matrix accelerometerMa,
1607 final KnownBiasTurntableGyroscopeCalibratorListener listener) {
1608 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg,
1609 accelerometerBias, accelerometerMa, listener);
1610 }
1611
1612 /**
1613 * Constructor.
1614 *
1615 * @param position position where body kinematics measures
1616 * have been taken.
1617 * @param turntableRotationRate constant rotation rate at which the
1618 * turntable is spinning. Must be
1619 * expressed in radians per second (rad/s).
1620 * @param timeInterval time interval between measurements being
1621 * captured expressed in seconds (s).
1622 * @param measurements collection of body kinematics
1623 * measurements with standard deviations
1624 * taken at the same position with zero
1625 * velocity and unknown different
1626 * orientations.
1627 * @param bias known gyroscope bias. This must be 3x1
1628 * and is expressed in radians per second
1629 * (rad/s).
1630 * @param initialMg initial gyroscope scale factors and
1631 * cross coupling errors matrix. Must
1632 * be 3x3.
1633 * @param initialGg initial gyroscope G-dependent cross
1634 * biases introduced on the gyroscope by
1635 * the specific forces sensed by the
1636 * accelerometer. Must be 3x3.
1637 * @param accelerometerBias known accelerometer bias. This must
1638 * have length 3 and is expressed in
1639 * meters per squared second
1640 * (m/s^2).
1641 * @param accelerometerMa known accelerometer scale factors and
1642 * cross coupling matrix. Must be 3x3.
1643 * @throws IllegalArgumentException if any of the provided values does
1644 * not have proper size or if either
1645 * turntable rotation rate or
1646 * time interval is zero or negative.
1647 */
1648 public KnownBiasTurntableGyroscopeCalibrator(
1649 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1650 final Collection<StandardDeviationBodyKinematics> measurements, final Matrix bias, final Matrix initialMg,
1651 final Matrix initialGg, final Matrix accelerometerBias, final Matrix accelerometerMa) {
1652 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg,
1653 accelerometerBias, accelerometerMa);
1654 }
1655
1656 /**
1657 * Constructor.
1658 *
1659 * @param position position where body kinematics measures
1660 * have been taken.
1661 * @param turntableRotationRate constant rotation rate at which the
1662 * turntable is spinning. Must be
1663 * expressed in radians per second (rad/s).
1664 * @param timeInterval time interval between measurements being
1665 * captured expressed in seconds (s).
1666 * @param measurements collection of body kinematics
1667 * measurements with standard deviations
1668 * taken at the same position with zero
1669 * velocity and unknown different
1670 * orientations.
1671 * @param bias known gyroscope bias. This must be 3x1
1672 * and is expressed in radians per second
1673 * (rad/s).
1674 * @param initialMg initial gyroscope scale factors and
1675 * cross coupling errors matrix. Must
1676 * be 3x3.
1677 * @param initialGg initial gyroscope G-dependent cross
1678 * biases introduced on the gyroscope by
1679 * the specific forces sensed by the
1680 * accelerometer. Must be 3x3.
1681 * @param accelerometerBias known accelerometer bias. This must
1682 * have length 3 and is expressed in
1683 * meters per squared second (m/s^2).
1684 * @param accelerometerMa known accelerometer scale factors and
1685 * cross coupling matrix. Must be 3x3.
1686 * @param listener listener to handle events raised by
1687 * this calibrator.
1688 * @throws IllegalArgumentException if any of the provided values does
1689 * not have proper size or if either
1690 * turntable rotation rate or
1691 * time interval is zero or negative.
1692 */
1693 public KnownBiasTurntableGyroscopeCalibrator(
1694 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1695 final Collection<StandardDeviationBodyKinematics> measurements, final Matrix bias, final Matrix initialMg,
1696 final Matrix initialGg, final Matrix accelerometerBias, final Matrix accelerometerMa,
1697 final KnownBiasTurntableGyroscopeCalibratorListener listener) {
1698 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, bias, initialMg, initialGg,
1699 accelerometerBias, accelerometerMa, listener);
1700 }
1701
1702 /**
1703 * Constructor.
1704 *
1705 * @param position position where body kinematics
1706 * measures have been taken.
1707 * @param turntableRotationRate constant rotation rate at which
1708 * the turntable is spinning. Must
1709 * be expressed in radians per
1710 * second (rad/s).
1711 * @param timeInterval time interval between measurements
1712 * being captured expressed in
1713 * seconds (s).
1714 * @param measurements collection of body kinematics
1715 * measurements with standard
1716 * deviations taken at the same
1717 * position with zero velocity
1718 * and unknown different
1719 * orientations.
1720 * @param commonAxisUsed indicates whether z-axis is
1721 * assumed to be common for
1722 * accelerometer and gyroscope.
1723 * @param estimateGDependentCrossBiases true if G-dependent cross biases
1724 * will be estimated, false
1725 * otherwise.
1726 * @param bias known gyroscope bias. This must
1727 * be 3x1 and is expressed in
1728 * radians per second (rad/s).
1729 * @param initialMg initial gyroscope scale factors
1730 * and cross coupling errors matrix.
1731 * Must be 3x3.
1732 * @param initialGg initial gyroscope G-dependent
1733 * cross biases introduced on the
1734 * gyroscope by the specific
1735 * forces sensed by the
1736 * accelerometer. Must be 3x3.
1737 * @throws IllegalArgumentException if any of the provided values does
1738 * not have proper size or if either
1739 * turntable rotation rate or
1740 * time interval is zero or negative.
1741 */
1742 public KnownBiasTurntableGyroscopeCalibrator(
1743 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1744 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1745 final boolean estimateGDependentCrossBiases, final Matrix bias, final Matrix initialMg,
1746 final Matrix initialGg) {
1747 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, commonAxisUsed,
1748 estimateGDependentCrossBiases, bias, initialMg, initialGg);
1749 }
1750
1751 /**
1752 * Constructor.
1753 *
1754 * @param position position where body kinematics
1755 * measures have been taken.
1756 * @param turntableRotationRate constant rotation rate at which
1757 * the turntable is spinning. Must
1758 * be expressed in radians per
1759 * second (rad/s).
1760 * @param timeInterval time interval between measurements
1761 * being captured expressed in
1762 * seconds (s).
1763 * @param measurements collection of body kinematics
1764 * measurements with standard
1765 * deviations taken at the same
1766 * position with zero velocity and
1767 * unknown different orientations.
1768 * @param commonAxisUsed indicates whether z-axis is
1769 * assumed to be common for
1770 * accelerometer and gyroscope.
1771 * @param estimateGDependentCrossBiases true if G-dependent cross
1772 * biases will be estimated, false
1773 * otherwise.
1774 * @param bias known gyroscope bias. This must
1775 * be 3x1 and is expressed in
1776 * radians per second (rad/s).
1777 * @param initialMg initial gyroscope scale factors
1778 * and cross coupling errors
1779 * matrix. Must be 3x3.
1780 * @param initialGg initial gyroscope G-dependent
1781 * cross biases introduced on the
1782 * gyroscope by the specific
1783 * forces sensed by the
1784 * accelerometer. Must be 3x3.
1785 * @param listener listener to handle events
1786 * raised by this calibrator.
1787 * @throws IllegalArgumentException if any of the provided values does
1788 * not have proper size or if either
1789 * turntable rotation rate or
1790 * time interval is zero or negative.
1791 */
1792 public KnownBiasTurntableGyroscopeCalibrator(
1793 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1794 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1795 final boolean estimateGDependentCrossBiases, final Matrix bias, final Matrix initialMg,
1796 final Matrix initialGg, final KnownBiasTurntableGyroscopeCalibratorListener listener) {
1797 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, commonAxisUsed,
1798 estimateGDependentCrossBiases, bias, initialMg, initialGg, listener);
1799 }
1800
1801 /**
1802 * Constructor.
1803 *
1804 * @param position position where body kinematics
1805 * measures have been taken.
1806 * @param turntableRotationRate constant rotation rate at which
1807 * the turntable is spinning. Must
1808 * be expressed in radians per
1809 * second (rad/s).
1810 * @param timeInterval time interval between measurements
1811 * being captured expressed in
1812 * seconds (s).
1813 * @param measurements collection of body kinematics
1814 * measurements with standard
1815 * deviations taken at the same
1816 * position with zero velocity
1817 * and unknown different
1818 * orientations.
1819 * @param commonAxisUsed indicates whether z-axis is
1820 * assumed to be common for
1821 * accelerometer and gyroscope.
1822 * @param estimateGDependentCrossBiases true if G-dependent cross biases
1823 * will be estimated, false
1824 * otherwise.
1825 * @param bias known gyroscope bias. This must
1826 * have length 3 and is expressed
1827 * in radians per second (rad/s).
1828 * @param initialMg initial gyroscope scale factors
1829 * and cross coupling errors matrix.
1830 * Must be 3x3.
1831 * @param initialGg initial gyroscope G-dependent
1832 * cross biases introduced on the
1833 * gyroscope by the specific forces
1834 * sensed by the accelerometer.
1835 * Must be 3x3.
1836 * @throws IllegalArgumentException if any of the provided values does
1837 * not have proper size or if either
1838 * turntable rotation rate or
1839 * time interval is zero or negative.
1840 */
1841 public KnownBiasTurntableGyroscopeCalibrator(
1842 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1843 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1844 final boolean estimateGDependentCrossBiases, final double[] bias, final Matrix initialMg,
1845 final Matrix initialGg) {
1846 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, commonAxisUsed,
1847 estimateGDependentCrossBiases, bias, initialMg, initialGg);
1848 }
1849
1850 /**
1851 * Constructor.
1852 *
1853 * @param position position where body kinematics
1854 * measures have been taken.
1855 * @param turntableRotationRate constant rotation rate at which
1856 * the turntable is spinning. Must
1857 * be expressed in radians per
1858 * second (rad/s).
1859 * @param timeInterval time interval between measurements
1860 * being captured expressed in
1861 * seconds (s).
1862 * @param measurements collection of body kinematics
1863 * measurements with standard
1864 * deviations taken at the same
1865 * position with zero velocity
1866 * and unknown different
1867 * orientations.
1868 * @param commonAxisUsed indicates whether z-axis is
1869 * assumed to be common for
1870 * accelerometer and gyroscope.
1871 * @param estimateGDependentCrossBiases true if G-dependent cross biases
1872 * will be estimated, false
1873 * otherwise.
1874 * @param bias known gyroscope bias. This must
1875 * have length 3 and is expressed
1876 * in radians per second (rad/s).
1877 * @param initialMg initial gyroscope scale factors
1878 * and cross coupling errors
1879 * matrix. Must be 3x3.
1880 * @param initialGg initial gyroscope G-dependent
1881 * cross biases introduced on the
1882 * gyroscope by the specific forces
1883 * sensed by the accelerometer.
1884 * Must be 3x3.
1885 * @param listener listener to handle events raised
1886 * by this calibrator.
1887 * @throws IllegalArgumentException if any of the provided values does
1888 * not have proper size or if either
1889 * turntable rotation rate or
1890 * time interval is zero or negative.
1891 */
1892 public KnownBiasTurntableGyroscopeCalibrator(
1893 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1894 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1895 final boolean estimateGDependentCrossBiases, final double[] bias, final Matrix initialMg,
1896 final Matrix initialGg, final KnownBiasTurntableGyroscopeCalibratorListener listener) {
1897 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, commonAxisUsed,
1898 estimateGDependentCrossBiases, bias, initialMg, initialGg, listener);
1899 }
1900
1901 /**
1902 * Constructor.
1903 *
1904 * @param position position where body kinematics
1905 * measures have been taken.
1906 * @param turntableRotationRate constant rotation rate at which
1907 * the turntable is spinning. Must
1908 * be expressed in radians per
1909 * second (rad/s).
1910 * @param timeInterval time interval between measurements
1911 * being captured expressed in
1912 * seconds (s).
1913 * @param measurements collection of body kinematics
1914 * measurements with standard
1915 * deviations taken at the same
1916 * position with zero velocity
1917 * and unknown different
1918 * orientations.
1919 * @param commonAxisUsed indicates whether z-axis is
1920 * assumed to be common for
1921 * accelerometer and gyroscope.
1922 * @param estimateGDependentCrossBiases true if G-dependent cross
1923 * biases will be estimated,
1924 * false otherwise.
1925 * @param bias known gyroscope bias. This must
1926 * have length 3 and is expressed
1927 * in radians per second (rad/s).
1928 * @param initialMg initial gyroscope scale factors
1929 * and cross coupling errors
1930 * matrix. Must be 3x3.
1931 * @param initialGg initial gyroscope G-dependent
1932 * cross biases introduced on the
1933 * gyroscope by the specific forces
1934 * sensed by the accelerometer.
1935 * Must be 3x3.
1936 * @param accelerometerBias known accelerometer bias. This
1937 * must have length 3 and is
1938 * expressed in meters per squared
1939 * second (m/s^2).
1940 * @param accelerometerMa known accelerometer scale factors
1941 * and cross coupling matrix. Must
1942 * be 3x3.
1943 * @throws IllegalArgumentException if any of the provided values does
1944 * not have proper size or if either
1945 * turntable rotation rate or
1946 * time interval is zero or negative.
1947 */
1948 public KnownBiasTurntableGyroscopeCalibrator(
1949 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
1950 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
1951 final boolean estimateGDependentCrossBiases, final double[] bias, final Matrix initialMg,
1952 final Matrix initialGg, final double[] accelerometerBias, final Matrix accelerometerMa) {
1953 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, commonAxisUsed,
1954 estimateGDependentCrossBiases, bias, initialMg, initialGg, accelerometerBias, accelerometerMa);
1955 }
1956
1957 /**
1958 * Constructor.
1959 *
1960 * @param position position where body kinematics
1961 * measures have been taken.
1962 * @param turntableRotationRate constant rotation rate at which
1963 * the turntable is spinning. Must
1964 * be expressed in radians per
1965 * second (rad/s).
1966 * @param timeInterval time interval between measurements
1967 * being captured expressed in
1968 * seconds (s).
1969 * @param measurements collection of body kinematics
1970 * measurements with standard
1971 * deviations taken at the same
1972 * position with zero velocity
1973 * and unknown different
1974 * orientations.
1975 * @param commonAxisUsed indicates whether z-axis is
1976 * assumed to be common for
1977 * accelerometer and gyroscope.
1978 * @param estimateGDependentCrossBiases true if G-dependent cross biases
1979 * will be estimated, false
1980 * otherwise.
1981 * @param bias known gyroscope bias. This must
1982 * have length 3 and is expressed
1983 * in radians per second (rad/s).
1984 * @param initialMg initial gyroscope scale factors
1985 * and cross coupling errors matrix.
1986 * Must be 3x3.
1987 * @param initialGg initial gyroscope G-dependent
1988 * cross biases introduced on the
1989 * gyroscope by the specific forces
1990 * sensed by the accelerometer. Must
1991 * be 3x3.
1992 * @param accelerometerBias known accelerometer bias. This
1993 * must have length 3 and is
1994 * expressed in meters per squared
1995 * second (m/s^2).
1996 * @param accelerometerMa known accelerometer scale factors
1997 * and cross coupling matrix. Must
1998 * be 3x3.
1999 * @param listener listener to handle events raised
2000 * by this calibrator.
2001 * @throws IllegalArgumentException if any of the provided values does
2002 * not have proper size or if either
2003 * turntable rotation rate or
2004 * time interval is zero or negative.
2005 */
2006 public KnownBiasTurntableGyroscopeCalibrator(
2007 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
2008 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
2009 final boolean estimateGDependentCrossBiases, final double[] bias, final Matrix initialMg,
2010 final Matrix initialGg, final double[] accelerometerBias, final Matrix accelerometerMa,
2011 final KnownBiasTurntableGyroscopeCalibratorListener listener) {
2012 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, commonAxisUsed,
2013 estimateGDependentCrossBiases, bias, initialMg, initialGg, accelerometerBias, accelerometerMa,
2014 listener);
2015 }
2016
2017 /**
2018 * Constructor.
2019 *
2020 * @param position position where body kinematics
2021 * measures have been taken.
2022 * @param turntableRotationRate constant rotation rate at which
2023 * the turntable is spinning. Must
2024 * be expressed in radians per
2025 * second (rad/s).
2026 * @param timeInterval time interval between measurements
2027 * being captured expressed in
2028 * seconds (s).
2029 * @param measurements collection of body kinematics
2030 * measurements with standard
2031 * deviations taken at the same
2032 * position with zero velocity and
2033 * unknown different orientations.
2034 * @param commonAxisUsed indicates whether z-axis is
2035 * assumed to be common for
2036 * accelerometer and gyroscope.
2037 * @param estimateGDependentCrossBiases true if G-dependent cross biases
2038 * will be estimated, false
2039 * otherwise.
2040 * @param bias known gyroscope bias. This must
2041 * be 3x1 and is expressed in
2042 * radians per second (rad/s).
2043 * @param initialMg initial gyroscope scale factors
2044 * and cross coupling errors matrix.
2045 * Must be 3x3.
2046 * @param initialGg initial gyroscope G-dependent
2047 * cross biases introduced on the
2048 * gyroscope by the specific forces
2049 * sensed by the accelerometer. Must
2050 * be 3x3.
2051 * @param accelerometerBias known accelerometer bias. This
2052 * must have length 3 and is
2053 * expressed in meters per squared
2054 * second (m/s^2).
2055 * @param accelerometerMa known accelerometer scale factors
2056 * and cross coupling matrix. Must
2057 * be 3x3.
2058 * @throws IllegalArgumentException if any of the provided values does
2059 * not have proper size or if either
2060 * turntable rotation rate or
2061 * time interval is zero or negative.
2062 */
2063 public KnownBiasTurntableGyroscopeCalibrator(
2064 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
2065 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
2066 final boolean estimateGDependentCrossBiases, final Matrix bias, final Matrix initialMg,
2067 final Matrix initialGg, final Matrix accelerometerBias, final Matrix accelerometerMa) {
2068 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, commonAxisUsed,
2069 estimateGDependentCrossBiases, bias, initialMg, initialGg, accelerometerBias, accelerometerMa);
2070 }
2071
2072 /**
2073 * Constructor.
2074 *
2075 * @param position position where body kinematics
2076 * measures have been taken.
2077 * @param turntableRotationRate constant rotation rate at which
2078 * the turntable is spinning. Must
2079 * be expressed in radians per
2080 * second (rad/s).
2081 * @param timeInterval time interval between measurements
2082 * being captured expressed in
2083 * seconds (s).
2084 * @param measurements collection of body kinematics
2085 * measurements with standard
2086 * deviations taken at the same
2087 * position with zero velocity and
2088 * unknown different orientations.
2089 * @param commonAxisUsed indicates whether z-axis is
2090 * assumed to be common for
2091 * accelerometer and gyroscope.
2092 * @param estimateGDependentCrossBiases true if G-dependent cross biases
2093 * will be estimated, false
2094 * otherwise.
2095 * @param bias known gyroscope bias. This must
2096 * be 3x1 and is expressed in
2097 * radians per second (rad/s).
2098 * @param initialMg initial gyroscope scale factors
2099 * and cross coupling errors matrix.
2100 * Must be 3x3.
2101 * @param initialGg initial gyroscope G-dependent
2102 * cross biases introduced on the
2103 * gyroscope by the specific forces
2104 * sensed by the accelerometer. Must
2105 * be 3x3.
2106 * @param accelerometerBias known accelerometer bias. This
2107 * must have length 3 and is
2108 * expressed in meters per squared
2109 * second (m/s^2).
2110 * @param accelerometerMa known accelerometer scale factors
2111 * and cross coupling matrix. Must
2112 * be 3x3.
2113 * @param listener listener to handle events raised
2114 * by this calibrator.
2115 * @throws IllegalArgumentException if any of the provided values does
2116 * not have proper size or if either
2117 * turntable rotation rate or
2118 * time interval is zero or negative.
2119 */
2120 public KnownBiasTurntableGyroscopeCalibrator(
2121 final NEDPosition position, final double turntableRotationRate, final double timeInterval,
2122 final Collection<StandardDeviationBodyKinematics> measurements, final boolean commonAxisUsed,
2123 final boolean estimateGDependentCrossBiases, final Matrix bias, final Matrix initialMg,
2124 final Matrix initialGg, final Matrix accelerometerBias, final Matrix accelerometerMa,
2125 final KnownBiasTurntableGyroscopeCalibratorListener listener) {
2126 this(convertPosition(position), turntableRotationRate, timeInterval, measurements, commonAxisUsed,
2127 estimateGDependentCrossBiases, bias, initialMg, initialGg, accelerometerBias, accelerometerMa,
2128 listener);
2129 }
2130
2131 /**
2132 * Gets known x-coordinate of accelerometer bias to be used to fix
2133 * measured specific force and find cross biases introduced by the
2134 * accelerometer.
2135 * This is expressed in meters per squared second (m/s^2).
2136 *
2137 * @return known x-coordinate of accelerometer bias.
2138 */
2139 @Override
2140 public double getAccelerometerBiasX() {
2141 return accelerometerBiasX;
2142 }
2143
2144 /**
2145 * Sets known x-coordinate of accelerometer bias to be used to fix
2146 * measured specific force and find cross biases introduced by the
2147 * accelerometer.
2148 * This is expressed in meters per squared second (m/s^2).
2149 *
2150 * @param accelerometerBiasX known x-coordinate of accelerometer bias.
2151 * @throws LockedException if calibrator is currently running.
2152 */
2153 @Override
2154 public void setAccelerometerBiasX(final double accelerometerBiasX) throws LockedException {
2155 if (running) {
2156 throw new LockedException();
2157 }
2158 this.accelerometerBiasX = accelerometerBiasX;
2159 }
2160
2161 /**
2162 * Gets known y-coordinate of accelerometer bias to be used to fix
2163 * measured specific force and find cross biases introduced by the
2164 * accelerometer.
2165 * This is expressed in meters per squared second (m/s^2).
2166 *
2167 * @return known y-coordinate of accelerometer bias.
2168 */
2169 @Override
2170 public double getAccelerometerBiasY() {
2171 return accelerometerBiasY;
2172 }
2173
2174 /**
2175 * Sets known y-coordinate of accelerometer bias to be used to fix
2176 * measured specific force and find cross biases introduced by the
2177 * accelerometer.
2178 * This is expressed in meters per squared second (m/s^2).
2179 *
2180 * @param accelerometerBiasY known y-coordinate of accelerometer bias.
2181 * @throws LockedException if calibrator is currently running.
2182 */
2183 @Override
2184 public void setAccelerometerBiasY(final double accelerometerBiasY) throws LockedException {
2185 if (running) {
2186 throw new LockedException();
2187 }
2188 this.accelerometerBiasY = accelerometerBiasY;
2189 }
2190
2191 /**
2192 * Gets known z-coordinate of accelerometer bias to be used to fix
2193 * measured specific force and find cross biases introduced by the
2194 * accelerometer.
2195 * This is expressed in meters per squared second (m/s^2).
2196 *
2197 * @return known z-coordinate of accelerometer bias.
2198 */
2199 @Override
2200 public double getAccelerometerBiasZ() {
2201 return accelerometerBiasZ;
2202 }
2203
2204 /**
2205 * Sets known z-coordinate of accelerometer bias to be used to fix
2206 * measured specific force and find cross biases introduced by the
2207 * accelerometer.
2208 * This is expressed in meters per squared second (m/s^2).
2209 *
2210 * @param accelerometerBiasZ known z-coordinate of accelerometer bias.
2211 * @throws LockedException if calibrator is currently running.
2212 */
2213 @Override
2214 public void setAccelerometerBiasZ(final double accelerometerBiasZ) throws LockedException {
2215 if (running) {
2216 throw new LockedException();
2217 }
2218 this.accelerometerBiasZ = accelerometerBiasZ;
2219 }
2220
2221 /**
2222 * Gets known x-coordinate of accelerometer bias to be used to fix
2223 * measured specific force and find cross biases introduced by the
2224 * accelerometer.
2225 *
2226 * @return known x-coordinate of accelerometer bias.
2227 */
2228 @Override
2229 public Acceleration getAccelerometerBiasXAsAcceleration() {
2230 return new Acceleration(accelerometerBiasX, AccelerationUnit.METERS_PER_SQUARED_SECOND);
2231 }
2232
2233 /**
2234 * Gets known x-coordinate of accelerometer bias to be used to fix
2235 * measured specific force and find cross biases introduced by the
2236 * accelerometer.
2237 *
2238 * @param result instance where result data will be stored.
2239 */
2240 @Override
2241 public void getAccelerometerBiasXAsAcceleration(final Acceleration result) {
2242 result.setValue(accelerometerBiasX);
2243 result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
2244 }
2245
2246 /**
2247 * Sets known x-coordinate of accelerometer bias to be used to fix
2248 * measured specific force and find cross biases introduced by the
2249 * accelerometer.
2250 *
2251 * @param accelerometerBiasX x-coordinate of accelerometer bias.
2252 * @throws LockedException if calibrator is currently running.
2253 */
2254 @Override
2255 public void setAccelerometerBiasX(final Acceleration accelerometerBiasX) throws LockedException {
2256 if (running) {
2257 throw new LockedException();
2258 }
2259 this.accelerometerBiasX = convertAcceleration(accelerometerBiasX);
2260 }
2261
2262 /**
2263 * Gets known y-coordinate of accelerometer bias to be used to fix
2264 * measured specific force and find cross biases introduced by the
2265 * accelerometer.
2266 *
2267 * @return known y-coordinate of accelerometer bias.
2268 */
2269 @Override
2270 public Acceleration getAccelerometerBiasYAsAcceleration() {
2271 return new Acceleration(accelerometerBiasY, AccelerationUnit.METERS_PER_SQUARED_SECOND);
2272 }
2273
2274 /**
2275 * Gets known y-coordinate of accelerometer bias to be used to fix
2276 * measured specific force and find cross biases introduced by the
2277 * accelerometer.
2278 *
2279 * @param result instance where result data will be stored.
2280 */
2281 @Override
2282 public void getAccelerometerBiasYAsAcceleration(final Acceleration result) {
2283 result.setValue(accelerometerBiasY);
2284 result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
2285 }
2286
2287 /**
2288 * Sets known y-coordinate of accelerometer bias to be used to fix
2289 * measured specific force and find cross biases introduced by the
2290 * accelerometer.
2291 *
2292 * @param accelerometerBiasY y-coordinate of accelerometer bias.
2293 * @throws LockedException if calibrator is currently running.
2294 */
2295 @Override
2296 public void setAccelerometerBiasY(final Acceleration accelerometerBiasY) throws LockedException {
2297 if (running) {
2298 throw new LockedException();
2299 }
2300 this.accelerometerBiasY = convertAcceleration(accelerometerBiasY);
2301 }
2302
2303 /**
2304 * Gets known z-coordinate of accelerometer bias to be used to fix
2305 * measured specific force and find cross biases introduced by the
2306 * accelerometer.
2307 *
2308 * @return known z-coordinate of accelerometer bias.
2309 */
2310 @Override
2311 public Acceleration getAccelerometerBiasZAsAcceleration() {
2312 return new Acceleration(accelerometerBiasZ, AccelerationUnit.METERS_PER_SQUARED_SECOND);
2313 }
2314
2315 /**
2316 * Gets known z-coordinate of accelerometer bias to be used to fix
2317 * measured specific force and find cross biases introduced by the
2318 * accelerometer.
2319 *
2320 * @param result instance where result data will be stored.
2321 */
2322 @Override
2323 public void getAccelerometerBiasZAsAcceleration(final Acceleration result) {
2324 result.setValue(accelerometerBiasZ);
2325 result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
2326 }
2327
2328 /**
2329 * Sets known z-coordinate of accelerometer bias to be used to fix
2330 * measured specific force and find cross biases introduced by the
2331 * accelerometer.
2332 *
2333 * @param accelerometerBiasZ z-coordinate of accelerometer bias.
2334 * @throws LockedException if calibrator is currently running.
2335 */
2336 @Override
2337 public void setAccelerometerBiasZ(final Acceleration accelerometerBiasZ) throws LockedException {
2338 if (running) {
2339 throw new LockedException();
2340 }
2341 this.accelerometerBiasZ = convertAcceleration(accelerometerBiasZ);
2342 }
2343
2344 /**
2345 * Sets known accelerometer bias to be used to fix measured specific
2346 * force and find cross biases introduced by the accelerometer.
2347 * This is expressed in meters per squared second (m/s^2).
2348 *
2349 * @param accelerometerBiasX x-coordinate of accelerometer bias.
2350 * @param accelerometerBiasY y-coordinate of accelerometer bias.
2351 * @param accelerometerBiasZ z-coordinate of accelerometer bias.
2352 * @throws LockedException if calibrator is currently running.
2353 */
2354 @Override
2355 public void setAccelerometerBias(
2356 final double accelerometerBiasX, final double accelerometerBiasY, final double accelerometerBiasZ)
2357 throws LockedException {
2358 if (running) {
2359 throw new LockedException();
2360 }
2361
2362 this.accelerometerBiasX = accelerometerBiasX;
2363 this.accelerometerBiasY = accelerometerBiasY;
2364 this.accelerometerBiasZ = accelerometerBiasZ;
2365 }
2366
2367 /**
2368 * Sets known accelerometer bias to be used to fix measured specific
2369 * force and find cross biases introduced by the accelerometer.
2370 *
2371 * @param accelerometerBiasX x-coordinate of accelerometer bias.
2372 * @param accelerometerBiasY y-coordinate of accelerometer bias.
2373 * @param accelerometerBiasZ z-coordinate of accelerometer bias.
2374 * @throws LockedException if calibrator is currently running.
2375 */
2376 @Override
2377 public void setAccelerometerBias(
2378 final Acceleration accelerometerBiasX, final Acceleration accelerometerBiasY,
2379 final Acceleration accelerometerBiasZ) throws LockedException {
2380 if (running) {
2381 throw new LockedException();
2382 }
2383
2384 this.accelerometerBiasX = convertAcceleration(accelerometerBiasX);
2385 this.accelerometerBiasY = convertAcceleration(accelerometerBiasY);
2386 this.accelerometerBiasZ = convertAcceleration(accelerometerBiasZ);
2387 }
2388
2389 /**
2390 * Gets known accelerometer bias to be used to fix measured specific
2391 * force and find cross biases introduced by the accelerometer.
2392 * This is expressed in meters per squared second (m/s^2).
2393 *
2394 * @return known accelerometer bias.
2395 */
2396 @Override
2397 public double[] getAccelerometerBias() {
2398 final var result = new double[BodyKinematics.COMPONENTS];
2399 getAccelerometerBias(result);
2400 return result;
2401 }
2402
2403 /**
2404 * Gets known accelerometer bias to be used to fix measured specific
2405 * force and find cross biases introduced by the accelerometer.
2406 * This is expressed in meters per squared second (m/s^2).
2407 *
2408 * @param result instance where result data will be copied to.
2409 * @throws IllegalArgumentException if provided array does not have
2410 * length 3.
2411 */
2412 @Override
2413 public void getAccelerometerBias(final double[] result) {
2414 if (result.length != BodyKinematics.COMPONENTS) {
2415 throw new IllegalArgumentException();
2416 }
2417
2418 result[0] = accelerometerBiasX;
2419 result[1] = accelerometerBiasY;
2420 result[2] = accelerometerBiasZ;
2421 }
2422
2423 /**
2424 * Sets known accelerometer bias to be used to fix measured specific
2425 * force and find cross biases introduced by the accelerometer.
2426 * This is expressed in meters per squared second (m/s^2).
2427 *
2428 * @param accelerometerBias known accelerometer bias.
2429 * @throws LockedException if calibrator is currently running.
2430 * @throws IllegalArgumentException if provided array does not have
2431 * length 3.
2432 */
2433 @Override
2434 public void setAccelerometerBias(final double[] accelerometerBias) throws LockedException {
2435 if (running) {
2436 throw new LockedException();
2437 }
2438
2439 if (accelerometerBias.length != BodyKinematics.COMPONENTS) {
2440 throw new IllegalArgumentException();
2441 }
2442
2443 accelerometerBiasX = accelerometerBias[0];
2444 accelerometerBiasY = accelerometerBias[1];
2445 accelerometerBiasZ = accelerometerBias[2];
2446 }
2447
2448 /**
2449 * Gets known accelerometer bias to be used to fix measured specific
2450 * force and find cross biases introduced by the accelerometer.
2451 * This is expressed in meters per squared second (m/s^2).
2452 *
2453 * @return known accelerometer bias.
2454 */
2455 @Override
2456 public Matrix getAccelerometerBiasAsMatrix() {
2457 Matrix result;
2458 try {
2459 result = new Matrix(BodyKinematics.COMPONENTS, 1);
2460 getAccelerometerBiasAsMatrix(result);
2461 } catch (final WrongSizeException ignore) {
2462 // never happens
2463 result = null;
2464 }
2465 return result;
2466 }
2467
2468 /**
2469 * Gets known accelerometer bias to be used to fix measured specific
2470 * force and find cross biases introduced by the accelerometer.
2471 * This is expressed in meters per squared second (m/s^2).
2472 *
2473 * @param result instance where result data will be copied to.
2474 * @throws IllegalArgumentException if provided matrix is not 3x1.
2475 */
2476 @Override
2477 public void getAccelerometerBiasAsMatrix(final Matrix result) {
2478 if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != 1) {
2479 throw new IllegalArgumentException();
2480 }
2481 result.setElementAtIndex(0, accelerometerBiasX);
2482 result.setElementAtIndex(1, accelerometerBiasY);
2483 result.setElementAtIndex(2, accelerometerBiasZ);
2484 }
2485
2486 /**
2487 * Sets known accelerometer bias to be used to fix measured specific
2488 * force and find cross biases introduced by the accelerometer.
2489 * This is expressed in meters per squared second (m/s^2).
2490 *
2491 * @param accelerometerBias known accelerometer bias. Must be 3x1.
2492 * @throws LockedException if calibrator is currently running.
2493 * @throws IllegalArgumentException if provided matrix is not 3x1.
2494 */
2495 @Override
2496 public void setAccelerometerBias(final Matrix accelerometerBias) throws LockedException {
2497 if (running) {
2498 throw new LockedException();
2499 }
2500 if (accelerometerBias.getRows() != BodyKinematics.COMPONENTS || accelerometerBias.getColumns() != 1) {
2501 throw new IllegalArgumentException();
2502 }
2503
2504 accelerometerBiasX = accelerometerBias.getElementAtIndex(0);
2505 accelerometerBiasY = accelerometerBias.getElementAtIndex(1);
2506 accelerometerBiasZ = accelerometerBias.getElementAtIndex(2);
2507 }
2508
2509 /**
2510 * Gets known accelerometer x scaling factor to be used to fix measured
2511 * specific force and find cross biases introduced by the accelerometer.
2512 *
2513 * @return known accelerometer x scaling factor.
2514 */
2515 @Override
2516 public double getAccelerometerSx() {
2517 return accelerometerSx;
2518 }
2519
2520 /**
2521 * Sets known accelerometer x scaling factor to be used to fix measured
2522 * specific force and find cross biases introduced by the accelerometer.
2523 *
2524 * @param accelerometerSx known accelerometer x scaling factor.
2525 * @throws LockedException if calibrator is currently running.
2526 */
2527 @Override
2528 public void setAccelerometerSx(final double accelerometerSx) throws LockedException {
2529 if (running) {
2530 throw new LockedException();
2531 }
2532 this.accelerometerSx = accelerometerSx;
2533 }
2534
2535 /**
2536 * Gets known accelerometer y scaling factor to be used to fix measured
2537 * specific force and find cross biases introduced by the accelerometer.
2538 *
2539 * @return known accelerometer y scaling factor.
2540 */
2541 @Override
2542 public double getAccelerometerSy() {
2543 return accelerometerSy;
2544 }
2545
2546 /**
2547 * Sets known accelerometer y scaling factor to be used to fix measured
2548 * specific force and find cross biases introduced by the accelerometer.
2549 *
2550 * @param accelerometerSy known accelerometer y scaling factor.
2551 * @throws LockedException if calibrator is currently running.
2552 */
2553 @Override
2554 public void setAccelerometerSy(final double accelerometerSy) throws LockedException {
2555 if (running) {
2556 throw new LockedException();
2557 }
2558 this.accelerometerSy = accelerometerSy;
2559 }
2560
2561 /**
2562 * Gets known accelerometer z scaling factor to be used to fix measured
2563 * specific force and find cross biases introduced by the accelerometer.
2564 *
2565 * @return known accelerometer z scaling factor.
2566 */
2567 @Override
2568 public double getAccelerometerSz() {
2569 return accelerometerSz;
2570 }
2571
2572 /**
2573 * Sets known accelerometer z scaling factor to be used to fix measured
2574 * specific force and find cross biases introduced by the accelerometer.
2575 *
2576 * @param accelerometerSz known accelerometer z scaling factor.
2577 * @throws LockedException if calibrator is currently running.
2578 */
2579 @Override
2580 public void setAccelerometerSz(final double accelerometerSz) throws LockedException {
2581 if (running) {
2582 throw new LockedException();
2583 }
2584 this.accelerometerSz = accelerometerSz;
2585 }
2586
2587 /**
2588 * Gets known accelerometer x-y cross coupling error to be used to fix
2589 * measured specific force and find cross biases introduced by the
2590 * accelerometer.
2591 *
2592 * @return known accelerometer x-y cross coupling error.
2593 */
2594 @Override
2595 public double getAccelerometerMxy() {
2596 return accelerometerMxy;
2597 }
2598
2599 /**
2600 * Sets known accelerometer x-y cross coupling error to be used to fix
2601 * measured specific force and find cross biases introduced by the
2602 * accelerometer.
2603 *
2604 * @param accelerometerMxy known accelerometer x-y cross coupling error.
2605 * @throws LockedException if calibrator is currently running.
2606 */
2607 @Override
2608 public void setAccelerometerMxy(final double accelerometerMxy) throws LockedException {
2609 if (running) {
2610 throw new LockedException();
2611 }
2612 this.accelerometerMxy = accelerometerMxy;
2613 }
2614
2615 /**
2616 * Gets known accelerometer x-z cross coupling error to be used to fix
2617 * measured specific force and find cross biases introduced by the
2618 * accelerometer.
2619 *
2620 * @return known accelerometer x-z cross coupling error.
2621 */
2622 @Override
2623 public double getAccelerometerMxz() {
2624 return accelerometerMxz;
2625 }
2626
2627 /**
2628 * Sets known accelerometer x-z cross coupling error to be used to fix
2629 * measured specific force and find cross biases introduced by the
2630 * accelerometer.
2631 *
2632 * @param accelerometerMxz known accelerometer x-z cross coupling error.
2633 * @throws LockedException if calibrator is currently running.
2634 */
2635 @Override
2636 public void setAccelerometerMxz(final double accelerometerMxz) throws LockedException {
2637 if (running) {
2638 throw new LockedException();
2639 }
2640 this.accelerometerMxz = accelerometerMxz;
2641 }
2642
2643 /**
2644 * Gets known accelerometer y-x cross coupling error to be used to fix
2645 * measured specific force and find cross biases introduced by the
2646 * accelerometer.
2647 *
2648 * @return known accelerometer y-x cross coupling error.
2649 */
2650 @Override
2651 public double getAccelerometerMyx() {
2652 return accelerometerMyx;
2653 }
2654
2655 /**
2656 * Sets known accelerometer y-x cross coupling error to be used to fix
2657 * measured specific force and find cross biases introduced by the
2658 * accelerometer.
2659 *
2660 * @param accelerometerMyx known accelerometer y-x cross coupling
2661 * error.
2662 * @throws LockedException if calibrator is currently running.
2663 */
2664 @Override
2665 public void setAccelerometerMyx(final double accelerometerMyx) throws LockedException {
2666 if (running) {
2667 throw new LockedException();
2668 }
2669 this.accelerometerMyx = accelerometerMyx;
2670 }
2671
2672 /**
2673 * Gets known accelerometer y-z cross coupling error to be used to fix
2674 * measured specific force and find cross biases introduced by the
2675 * accelerometer.
2676 *
2677 * @return known accelerometer y-z cross coupling error.
2678 */
2679 @Override
2680 public double getAccelerometerMyz() {
2681 return accelerometerMyz;
2682 }
2683
2684 /**
2685 * Sets known accelerometer y-z cross coupling error to be used to fix
2686 * measured specific force and find cross biases introduced by the
2687 * accelerometer.
2688 *
2689 * @param accelerometerMyz known accelerometer y-z cross coupling
2690 * error.
2691 * @throws LockedException if calibrator is currently running.
2692 */
2693 @Override
2694 public void setAccelerometerMyz(final double accelerometerMyz) throws LockedException {
2695 if (running) {
2696 throw new LockedException();
2697 }
2698 this.accelerometerMyz = accelerometerMyz;
2699 }
2700
2701 /**
2702 * Gets known accelerometer z-x cross coupling error to be used to fix
2703 * measured specific force and find cross biases introduced by the
2704 * accelerometer.
2705 *
2706 * @return known accelerometer z-x cross coupling error.
2707 */
2708 @Override
2709 public double getAccelerometerMzx() {
2710 return accelerometerMzx;
2711 }
2712
2713 /**
2714 * Sets known accelerometer z-x cross coupling error to be used to fix
2715 * measured specific force and find cross biases introduced by the
2716 * accelerometer.
2717 *
2718 * @param accelerometerMzx known accelerometer z-x cross coupling
2719 * error.
2720 * @throws LockedException if calibrator is currently running.
2721 */
2722 @Override
2723 public void setAccelerometerMzx(final double accelerometerMzx) throws LockedException {
2724 if (running) {
2725 throw new LockedException();
2726 }
2727 this.accelerometerMzx = accelerometerMzx;
2728 }
2729
2730 /**
2731 * Gets known accelerometer z-y cross coupling error to be used to fix
2732 * measured specific force and find cross biases introduced by the
2733 * accelerometer.
2734 *
2735 * @return known accelerometer z-y cross coupling error.
2736 */
2737 @Override
2738 public double getAccelerometerMzy() {
2739 return accelerometerMzy;
2740 }
2741
2742 /**
2743 * Sets known accelerometer z-y cross coupling error to be used to fix
2744 * measured specific force and find cross biases introduced by the
2745 * accelerometer.
2746 *
2747 * @param accelerometerMzy known accelerometer z-y cross coupling
2748 * error.
2749 * @throws LockedException if calibrator is currently running.
2750 */
2751 @Override
2752 public void setAccelerometerMzy(final double accelerometerMzy) throws LockedException {
2753 if (running) {
2754 throw new LockedException();
2755 }
2756 this.accelerometerMzy = accelerometerMzy;
2757 }
2758
2759 /**
2760 * Sets known accelerometer scaling factors to be used to fix measured
2761 * specific force and find cross biases introduced by the
2762 * accelerometer.
2763 *
2764 * @param accelerometerSx known accelerometer x scaling factor.
2765 * @param accelerometerSy known accelerometer y scaling factor.
2766 * @param accelerometerSz known accelerometer z scaling factor.
2767 * @throws LockedException if calibrator is currently running.
2768 */
2769 @Override
2770 public void setAccelerometerScalingFactors(
2771 final double accelerometerSx, final double accelerometerSy, final double accelerometerSz)
2772 throws LockedException {
2773 if (running) {
2774 throw new LockedException();
2775 }
2776 this.accelerometerSx = accelerometerSx;
2777 this.accelerometerSy = accelerometerSy;
2778 this.accelerometerSz = accelerometerSz;
2779 }
2780
2781 /**
2782 * Sets known accelerometer cross coupling errors to be used to fix
2783 * measured specific force and find cross biases introduced by the
2784 * accelerometer.
2785 *
2786 * @param accelerometerMxy known accelerometer x-y cross coupling
2787 * error.
2788 * @param accelerometerMxz known accelerometer x-z cross coupling
2789 * error.
2790 * @param accelerometerMyx known accelerometer y-x cross coupling
2791 * error.
2792 * @param accelerometerMyz known accelerometer y-z cross coupling
2793 * error.
2794 * @param accelerometerMzx known accelerometer z-x cross coupling
2795 * error.
2796 * @param accelerometerMzy known accelerometer z-y cross coupling
2797 * error.
2798 * @throws LockedException if calibrator is currently running.
2799 */
2800 @Override
2801 public void setAccelerometerCrossCouplingErrors(
2802 final double accelerometerMxy, final double accelerometerMxz,
2803 final double accelerometerMyx, final double accelerometerMyz,
2804 final double accelerometerMzx, final double accelerometerMzy) throws LockedException {
2805 if (running) {
2806 throw new LockedException();
2807 }
2808 this.accelerometerMxy = accelerometerMxy;
2809 this.accelerometerMxz = accelerometerMxz;
2810 this.accelerometerMyx = accelerometerMyx;
2811 this.accelerometerMyz = accelerometerMyz;
2812 this.accelerometerMzx = accelerometerMzx;
2813 this.accelerometerMzy = accelerometerMzy;
2814 }
2815
2816 /**
2817 * Sets known accelerometer scaling factors and cross coupling errors
2818 * to be used to fix measured specific force and find cross biases
2819 * introduced by the accelerometer.
2820 *
2821 * @param accelerometerSx known accelerometer x scaling factor.
2822 * @param accelerometerSy known accelerometer y scaling factor.
2823 * @param accelerometerSz known accelerometer z scaling factor.
2824 * @param accelerometerMxy known accelerometer x-y cross coupling
2825 * error.
2826 * @param accelerometerMxz known accelerometer x-z cross coupling
2827 * error.
2828 * @param accelerometerMyx known accelerometer y-x cross coupling
2829 * error.
2830 * @param accelerometerMyz known accelerometer y-z cross coupling
2831 * error.
2832 * @param accelerometerMzx known accelerometer z-x cross coupling
2833 * error.
2834 * @param accelerometerMzy known accelerometer z-y cross coupling
2835 * error.
2836 * @throws LockedException if calibrator is currently running.
2837 */
2838 @Override
2839 public void setAccelerometerScalingFactorsAndCrossCouplingErrors(
2840 final double accelerometerSx, final double accelerometerSy, final double accelerometerSz,
2841 final double accelerometerMxy, final double accelerometerMxz, final double accelerometerMyx,
2842 final double accelerometerMyz, final double accelerometerMzx, final double accelerometerMzy)
2843 throws LockedException {
2844 if (running) {
2845 throw new LockedException();
2846 }
2847 setAccelerometerScalingFactors(accelerometerSx, accelerometerSy, accelerometerSz);
2848 setAccelerometerCrossCouplingErrors(accelerometerMxy, accelerometerMxz, accelerometerMyx,
2849 accelerometerMyz, accelerometerMzx, accelerometerMzy);
2850 }
2851
2852 /**
2853 * Gets known accelerometer scale factors and cross coupling
2854 * errors matrix.
2855 *
2856 * @return known accelerometer scale factors and cross coupling
2857 * errors matrix.
2858 */
2859 @Override
2860 public Matrix getAccelerometerMa() {
2861 Matrix result;
2862 try {
2863 result = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
2864 getAccelerometerMa(result);
2865 } catch (final WrongSizeException ignore) {
2866 // never happens
2867 result = null;
2868 }
2869 return result;
2870 }
2871
2872 /**
2873 * Gets known accelerometer scale factors and cross coupling
2874 * errors matrix.
2875 *
2876 * @param result instance where data will be stored.
2877 * @throws IllegalArgumentException if provided matrix is not 3x3.
2878 */
2879 @Override
2880 public void getAccelerometerMa(final Matrix result) {
2881 if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != BodyKinematics.COMPONENTS) {
2882 throw new IllegalArgumentException();
2883 }
2884 result.setElementAtIndex(0, accelerometerSx);
2885 result.setElementAtIndex(1, accelerometerMyx);
2886 result.setElementAtIndex(2, accelerometerMzx);
2887
2888 result.setElementAtIndex(3, accelerometerMxy);
2889 result.setElementAtIndex(4, accelerometerSy);
2890 result.setElementAtIndex(5, accelerometerMzy);
2891
2892 result.setElementAtIndex(6, accelerometerMxz);
2893 result.setElementAtIndex(7, accelerometerMyz);
2894 result.setElementAtIndex(8, accelerometerSz);
2895 }
2896
2897 /**
2898 * Sets known accelerometer scale factors and cross coupling
2899 * errors matrix.
2900 *
2901 * @param accelerometerMa known accelerometer scale factors and
2902 * cross coupling errors matrix. Must be 3x3.
2903 * @throws LockedException if calibrator is currently running.
2904 * @throws IllegalArgumentException if provided matrix is not 3x3.
2905 */
2906 @Override
2907 public void setAccelerometerMa(final Matrix accelerometerMa) throws LockedException {
2908 if (running) {
2909 throw new LockedException();
2910 }
2911 if (accelerometerMa.getRows() != BodyKinematics.COMPONENTS
2912 || accelerometerMa.getColumns() != BodyKinematics.COMPONENTS) {
2913 throw new IllegalArgumentException();
2914 }
2915
2916 accelerometerSx = accelerometerMa.getElementAtIndex(0);
2917 accelerometerMyx = accelerometerMa.getElementAtIndex(1);
2918 accelerometerMzx = accelerometerMa.getElementAtIndex(2);
2919
2920 accelerometerMxy = accelerometerMa.getElementAtIndex(3);
2921 accelerometerSy = accelerometerMa.getElementAtIndex(4);
2922 accelerometerMzy = accelerometerMa.getElementAtIndex(5);
2923
2924 accelerometerMxz = accelerometerMa.getElementAtIndex(6);
2925 accelerometerMyz = accelerometerMa.getElementAtIndex(7);
2926 accelerometerSz = accelerometerMa.getElementAtIndex(8);
2927 }
2928
2929 /**
2930 * Gets known x-coordinate of gyroscope bias.
2931 * This is expressed in radians per second (rad/s).
2932 *
2933 * @return known x-coordinate of gyroscope bias.
2934 */
2935 @Override
2936 public double getBiasX() {
2937 return biasX;
2938 }
2939
2940 /**
2941 * Sets known x-coordinate of gyroscope bias.
2942 * This is expressed in radians per second (rad/s).
2943 *
2944 * @param biasX known x-coordinate of gyroscope bias.
2945 * @throws LockedException if calibrator is currently running.
2946 */
2947 @Override
2948 public void setBiasX(final double biasX) throws LockedException {
2949 if (running) {
2950 throw new LockedException();
2951 }
2952 this.biasX = biasX;
2953 }
2954
2955 /**
2956 * Gets known y-coordinate of gyroscope bias.
2957 * This is expressed in radians per second (rad/s).
2958 *
2959 * @return known y-coordinate of gyroscope bias.
2960 */
2961 @Override
2962 public double getBiasY() {
2963 return biasY;
2964 }
2965
2966 /**
2967 * Sets known y-coordinate of gyroscope bias.
2968 * This is expressed in radians per second (rad/s).
2969 *
2970 * @param biasY known y-coordinate of gyroscope bias.
2971 * @throws LockedException if calibrator is currently running.
2972 */
2973 @Override
2974 public void setBiasY(final double biasY) throws LockedException {
2975 if (running) {
2976 throw new LockedException();
2977 }
2978 this.biasY = biasY;
2979 }
2980
2981 /**
2982 * Gets known z-coordinate of gyroscope bias.
2983 * This is expressed in radians per second (rad/s).
2984 *
2985 * @return known z-coordinate of gyroscope bias.
2986 */
2987 @Override
2988 public double getBiasZ() {
2989 return biasZ;
2990 }
2991
2992 /**
2993 * Sets known z-coordinate of gyroscope bias.
2994 * This is expressed in radians per second (rad/s).
2995 *
2996 * @param biasZ known z-coordinate of gyroscope bias.
2997 * @throws LockedException if calibrator is currently running.
2998 */
2999 @Override
3000 public void setBiasZ(final double biasZ) throws LockedException {
3001 if (running) {
3002 throw new LockedException();
3003 }
3004 this.biasZ = biasZ;
3005 }
3006
3007 /**
3008 * Gets known x-coordinate of gyroscope bias.
3009 *
3010 * @return known x-coordinate of gyroscope bias.
3011 */
3012 @Override
3013 public AngularSpeed getBiasAngularSpeedX() {
3014 return new AngularSpeed(biasX, AngularSpeedUnit.RADIANS_PER_SECOND);
3015 }
3016
3017 /**
3018 * Gets known x-coordinate of gyroscope bias.
3019 *
3020 * @param result instance where result data will be stored.
3021 */
3022 @Override
3023 public void getBiasAngularSpeedX(final AngularSpeed result) {
3024 result.setValue(biasX);
3025 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
3026 }
3027
3028 /**
3029 * Sets known x-coordinate of gyroscope bias.
3030 *
3031 * @param biasX known x-coordinate of gyroscope bias.
3032 * @throws LockedException if calibrator is currently running.
3033 */
3034 @Override
3035 public void setBiasX(final AngularSpeed biasX) throws LockedException {
3036 if (running) {
3037 throw new LockedException();
3038 }
3039 this.biasX = convertAngularSpeed(biasX);
3040 }
3041
3042 /**
3043 * Gets known y-coordinate of gyroscope bias.
3044 *
3045 * @return known y-coordinate of gyroscope bias.
3046 */
3047 @Override
3048 public AngularSpeed getBiasAngularSpeedY() {
3049 return new AngularSpeed(biasY, AngularSpeedUnit.RADIANS_PER_SECOND);
3050 }
3051
3052 /**
3053 * Gets known y-coordinate of gyroscope bias.
3054 *
3055 * @param result instance where result data will be stored.
3056 */
3057 @Override
3058 public void getBiasAngularSpeedY(final AngularSpeed result) {
3059 result.setValue(biasY);
3060 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
3061 }
3062
3063 /**
3064 * Sets known y-coordinate of gyroscope bias to be used to find a
3065 * solution.
3066 *
3067 * @param biasY known y-coordinate of gyroscope bias.
3068 * @throws LockedException if calibrator is currently running.
3069 */
3070 @Override
3071 public void setBiasY(final AngularSpeed biasY) throws LockedException {
3072 if (running) {
3073 throw new LockedException();
3074 }
3075 this.biasY = convertAngularSpeed(biasY);
3076 }
3077
3078 /**
3079 * Gets known z-coordinate of gyroscope bias to be used to find a
3080 * solution.
3081 *
3082 * @return known z-coordinate of gyroscope bias.
3083 */
3084 @Override
3085 public AngularSpeed getBiasAngularSpeedZ() {
3086 return new AngularSpeed(biasZ, AngularSpeedUnit.RADIANS_PER_SECOND);
3087 }
3088
3089 /**
3090 * Gets known z-coordinate of gyroscope bias.
3091 *
3092 * @param result instance where result data will be stored.
3093 */
3094 @Override
3095 public void getBiasAngularSpeedZ(final AngularSpeed result) {
3096 result.setValue(biasZ);
3097 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
3098 }
3099
3100 /**
3101 * Sets known z-coordinate of gyroscope bias.
3102 *
3103 * @param biasZ known z-coordinate of gyroscope bias.
3104 * @throws LockedException if calibrator is currently running.
3105 */
3106 @Override
3107 public void setBiasZ(final AngularSpeed biasZ) throws LockedException {
3108 if (running) {
3109 throw new LockedException();
3110 }
3111 this.biasZ = convertAngularSpeed(biasZ);
3112 }
3113
3114 /**
3115 * Sets known bias coordinates of gyroscope expressed in radians
3116 * per second (rad/s).
3117 *
3118 * @param biasX known x-coordinate of gyroscope bias.
3119 * @param biasY known y-coordinate of gyroscope bias.
3120 * @param biasZ known z-coordinate of gyroscope bias.
3121 * @throws LockedException if calibrator is currently running.
3122 */
3123 @Override
3124 public void setBiasCoordinates(
3125 final double biasX, final double biasY, final double biasZ) throws LockedException {
3126 if (running) {
3127 throw new LockedException();
3128 }
3129 this.biasX = biasX;
3130 this.biasY = biasY;
3131 this.biasZ = biasZ;
3132 }
3133
3134 /**
3135 * Sets known bias coordinates of gyroscope.
3136 *
3137 * @param biasX known x-coordinate of gyroscope bias.
3138 * @param biasY known y-coordinate of gyroscope bias.
3139 * @param biasZ known z-coordinate of gyroscope bias.
3140 * @throws LockedException if calibrator is currently running.
3141 */
3142 @Override
3143 public void setBiasCoordinates(
3144 final AngularSpeed biasX, final AngularSpeed biasY, final AngularSpeed biasZ) throws LockedException {
3145 if (running) {
3146 throw new LockedException();
3147 }
3148 this.biasX = convertAngularSpeed(biasX);
3149 this.biasY = convertAngularSpeed(biasY);
3150 this.biasZ = convertAngularSpeed(biasZ);
3151 }
3152
3153 /**
3154 * Gets known gyroscope bias.
3155 *
3156 * @return known gyroscope bias.
3157 */
3158 public AngularSpeedTriad getBiasAsTriad() {
3159 return new AngularSpeedTriad(AngularSpeedUnit.RADIANS_PER_SECOND, biasX, biasY, biasZ);
3160 }
3161
3162 /**
3163 * Gets known gyroscope bias.
3164 *
3165 * @param result instance where result will be stored.
3166 */
3167 public void getBiasAsTriad(final AngularSpeedTriad result) {
3168 result.setValueCoordinatesAndUnit(biasX, biasY, biasZ, AngularSpeedUnit.RADIANS_PER_SECOND);
3169 }
3170
3171 /**
3172 * Sets known gyroscope bias.
3173 *
3174 * @param bias gyroscope bias to be set.
3175 * @throws LockedException if calibrator is currently running.
3176 */
3177 public void setBias(final AngularSpeedTriad bias) throws LockedException {
3178 if (running) {
3179 throw new LockedException();
3180 }
3181
3182 biasX = convertAngularSpeed(bias.getValueX(), bias.getUnit());
3183 biasY = convertAngularSpeed(bias.getValueY(), bias.getUnit());
3184 biasZ = convertAngularSpeed(bias.getValueZ(), bias.getUnit());
3185 }
3186
3187 /**
3188 * Gets initial x scaling factor of gyroscope.
3189 *
3190 * @return initial x scaling factor of gyroscope.
3191 */
3192 @Override
3193 public double getInitialSx() {
3194 return initialSx;
3195 }
3196
3197 /**
3198 * Sets initial x scaling factor of gyroscope.
3199 *
3200 * @param initialSx initial x scaling factor of gyroscope.
3201 * @throws LockedException if calibrator is currently running.
3202 */
3203 @Override
3204 public void setInitialSx(final double initialSx) throws LockedException {
3205 if (running) {
3206 throw new LockedException();
3207 }
3208 this.initialSx = initialSx;
3209 }
3210
3211 /**
3212 * Gets initial y scaling factor of gyroscope.
3213 *
3214 * @return initial y scaling factor of gyroscope.
3215 */
3216 @Override
3217 public double getInitialSy() {
3218 return initialSy;
3219 }
3220
3221 /**
3222 * Sets initial y scaling factor of gyroscope.
3223 *
3224 * @param initialSy initial y scaling factor of gyroscope.
3225 * @throws LockedException if calibrator is currently running.
3226 */
3227 @Override
3228 public void setInitialSy(final double initialSy) throws LockedException {
3229 if (running) {
3230 throw new LockedException();
3231 }
3232 this.initialSy = initialSy;
3233 }
3234
3235 /**
3236 * Gets initial z scaling factor of gyroscope.
3237 *
3238 * @return initial z scaling factor of gyroscope.
3239 */
3240 @Override
3241 public double getInitialSz() {
3242 return initialSz;
3243 }
3244
3245 /**
3246 * Sets initial z scaling factor of gyroscope.
3247 *
3248 * @param initialSz initial z scaling factor of gyroscope.
3249 * @throws LockedException if calibrator is currently running.
3250 */
3251 @Override
3252 public void setInitialSz(final double initialSz) throws LockedException {
3253 if (running) {
3254 throw new LockedException();
3255 }
3256 this.initialSz = initialSz;
3257 }
3258
3259 /**
3260 * Gets initial x-y cross coupling error of gyroscope.
3261 *
3262 * @return initial x-y cross coupling error of gyroscope.
3263 */
3264 @Override
3265 public double getInitialMxy() {
3266 return initialMxy;
3267 }
3268
3269 /**
3270 * Sets initial x-y cross coupling error of gyroscope.
3271 *
3272 * @param initialMxy initial x-y cross coupling error of gyroscope.
3273 * @throws LockedException if calibrator is currently running.
3274 */
3275 @Override
3276 public void setInitialMxy(final double initialMxy) throws LockedException {
3277 if (running) {
3278 throw new LockedException();
3279 }
3280 this.initialMxy = initialMxy;
3281 }
3282
3283 /**
3284 * Gets initial x-z cross coupling error of gyroscope.
3285 *
3286 * @return initial x-z cross coupling error of gyroscope.
3287 */
3288 @Override
3289 public double getInitialMxz() {
3290 return initialMxz;
3291 }
3292
3293 /**
3294 * Sets initial x-z cross coupling error of gyroscope.
3295 *
3296 * @param initialMxz initial x-z cross coupling error of gyroscope.
3297 * @throws LockedException if calibrator is currently running.
3298 */
3299 @Override
3300 public void setInitialMxz(final double initialMxz) throws LockedException {
3301 if (running) {
3302 throw new LockedException();
3303 }
3304 this.initialMxz = initialMxz;
3305 }
3306
3307 /**
3308 * Gets initial y-x cross coupling error of gyroscope.
3309 *
3310 * @return initial y-x cross coupling error of gyroscope.
3311 */
3312 @Override
3313 public double getInitialMyx() {
3314 return initialMyx;
3315 }
3316
3317 /**
3318 * Sets initial y-x cross coupling error of gyroscope.
3319 *
3320 * @param initialMyx initial y-x cross coupling error of gyroscope.
3321 * @throws LockedException if calibrator is currently running.
3322 */
3323 @Override
3324 public void setInitialMyx(final double initialMyx) throws LockedException {
3325 if (running) {
3326 throw new LockedException();
3327 }
3328 this.initialMyx = initialMyx;
3329 }
3330
3331 /**
3332 * Gets initial y-z cross coupling error of gyroscope.
3333 *
3334 * @return initial y-z cross coupling error of gyroscope.
3335 */
3336 @Override
3337 public double getInitialMyz() {
3338 return initialMyz;
3339 }
3340
3341 /**
3342 * Sets initial y-z cross coupling error of gyroscope.
3343 *
3344 * @param initialMyz initial y-z cross coupling error of gyroscope.
3345 * @throws LockedException if calibrator is currently running.
3346 */
3347 @Override
3348 public void setInitialMyz(final double initialMyz) throws LockedException {
3349 if (running) {
3350 throw new LockedException();
3351 }
3352 this.initialMyz = initialMyz;
3353 }
3354
3355 /**
3356 * Gets initial z-x cross coupling error of gyroscope.
3357 *
3358 * @return initial z-x cross coupling error of gyroscope.
3359 */
3360 @Override
3361 public double getInitialMzx() {
3362 return initialMzx;
3363 }
3364
3365 /**
3366 * Sets initial z-x cross coupling error of gyroscope.
3367 *
3368 * @param initialMzx initial z-x cross coupling error of gyroscope.
3369 * @throws LockedException if calibrator is currently running.
3370 */
3371 @Override
3372 public void setInitialMzx(final double initialMzx) throws LockedException {
3373 if (running) {
3374 throw new LockedException();
3375 }
3376 this.initialMzx = initialMzx;
3377 }
3378
3379 /**
3380 * Gets initial z-y cross coupling error of gyroscope.
3381 *
3382 * @return initial z-y cross coupling error of gyroscope.
3383 */
3384 @Override
3385 public double getInitialMzy() {
3386 return initialMzy;
3387 }
3388
3389 /**
3390 * Sets initial z-y cross coupling error of gyroscope.
3391 *
3392 * @param initialMzy initial z-y cross coupling error of gyroscope.
3393 * @throws LockedException if calibrator is currently running.
3394 */
3395 @Override
3396 public void setInitialMzy(final double initialMzy) throws LockedException {
3397 if (running) {
3398 throw new LockedException();
3399 }
3400 this.initialMzy = initialMzy;
3401 }
3402
3403 /**
3404 * Sets initial scaling factors of gyroscope.
3405 *
3406 * @param initialSx initial x scaling factor of gyroscope.
3407 * @param initialSy initial y scaling factor of gyroscope.
3408 * @param initialSz initial z scaling factor of gyroscope.
3409 * @throws LockedException if calibrator is currently running.
3410 */
3411 @Override
3412 public void setInitialScalingFactors(
3413 final double initialSx, final double initialSy, final double initialSz) throws LockedException {
3414 if (running) {
3415 throw new LockedException();
3416 }
3417 this.initialSx = initialSx;
3418 this.initialSy = initialSy;
3419 this.initialSz = initialSz;
3420 }
3421
3422 /**
3423 * Sets initial cross coupling errors of gyroscope.
3424 *
3425 * @param initialMxy initial x-y cross coupling error of gyroscope.
3426 * @param initialMxz initial x-z cross coupling error of gyroscope.
3427 * @param initialMyx initial y-x cross coupling error of gyroscope.
3428 * @param initialMyz initial y-z cross coupling error of gyroscope.
3429 * @param initialMzx initial z-x cross coupling error of gyroscope.
3430 * @param initialMzy initial z-y cross coupling error of gyroscope.
3431 * @throws LockedException if calibrator is currently running.
3432 */
3433 @Override
3434 public void setInitialCrossCouplingErrors(
3435 final double initialMxy, final double initialMxz, final double initialMyx,
3436 final double initialMyz, final double initialMzx, final double initialMzy) throws LockedException {
3437 if (running) {
3438 throw new LockedException();
3439 }
3440 this.initialMxy = initialMxy;
3441 this.initialMxz = initialMxz;
3442 this.initialMyx = initialMyx;
3443 this.initialMyz = initialMyz;
3444 this.initialMzx = initialMzx;
3445 this.initialMzy = initialMzy;
3446 }
3447
3448 /**
3449 * Sets initial scaling factors and cross coupling errors of
3450 * gyroscope.
3451 *
3452 * @param initialSx initial x scaling factor of gyroscope.
3453 * @param initialSy initial y scaling factor of gyroscope.
3454 * @param initialSz initial z scaling factor of gyroscope.
3455 * @param initialMxy initial x-y cross coupling error of gyroscope.
3456 * @param initialMxz initial x-z cross coupling error of gyroscope.
3457 * @param initialMyx initial y-x cross coupling error of gyroscope.
3458 * @param initialMyz initial y-z cross coupling error of gyroscope.
3459 * @param initialMzx initial z-x cross coupling error of gyroscope.
3460 * @param initialMzy initial z-y cross coupling error of gyroscope.
3461 * @throws LockedException if calibrator is currently running.
3462 */
3463 @Override
3464 public void setInitialScalingFactorsAndCrossCouplingErrors(
3465 final double initialSx, final double initialSy, final double initialSz,
3466 final double initialMxy, final double initialMxz, final double initialMyx,
3467 final double initialMyz, final double initialMzx, final double initialMzy) throws LockedException {
3468 if (running) {
3469 throw new LockedException();
3470 }
3471 setInitialScalingFactors(initialSx, initialSy, initialSz);
3472 setInitialCrossCouplingErrors(initialMxy, initialMxz, initialMyx, initialMyz, initialMzx, initialMzy);
3473 }
3474
3475 /**
3476 * Gets known gyroscope bias as an array.
3477 * Array values are expressed in radians per second (rad/s).
3478 *
3479 * @return array containing coordinates of gyroscope bias.
3480 */
3481 @Override
3482 public double[] getBias() {
3483 final var result = new double[BodyKinematics.COMPONENTS];
3484 getBias(result);
3485 return result;
3486 }
3487
3488 /**
3489 * Gets known gyroscope bias as an array.
3490 * Array values are expressed in radians per second (rad/s).
3491 *
3492 * @param result instance where result data will be copied to.
3493 * @throws IllegalArgumentException if provided array does not have length 3.
3494 */
3495 @Override
3496 public void getBias(final double[] result) {
3497 if (result.length != BodyKinematics.COMPONENTS) {
3498 throw new IllegalArgumentException();
3499 }
3500 result[0] = biasX;
3501 result[1] = biasY;
3502 result[2] = biasZ;
3503 }
3504
3505 /**
3506 * Sets known gyroscope bias as an array.
3507 * Array values are expressed in radians per second (rad/s).
3508 *
3509 * @param bias known bias.
3510 * @throws LockedException if calibrator is currently running.
3511 * @throws IllegalArgumentException if provided array does not have length 3.
3512 */
3513 @Override
3514 public void setBias(final double[] bias) throws LockedException {
3515 if (running) {
3516 throw new LockedException();
3517 }
3518
3519 if (bias.length != BodyKinematics.COMPONENTS) {
3520 throw new IllegalArgumentException();
3521 }
3522 biasX = bias[0];
3523 biasY = bias[1];
3524 biasZ = bias[2];
3525 }
3526
3527 /**
3528 * Gets known gyroscope bias as a column matrix.
3529 * Matrix values are expressed in radians per second (rad/s).
3530 *
3531 * @return known gyroscope bias as a column matrix.
3532 */
3533 @Override
3534 public Matrix getBiasAsMatrix() {
3535 Matrix result;
3536 try {
3537 result = new Matrix(BodyKinematics.COMPONENTS, 1);
3538 getBiasAsMatrix(result);
3539 } catch (final WrongSizeException ignore) {
3540 // never happens
3541 result = null;
3542 }
3543 return result;
3544 }
3545
3546 /**
3547 * Gets known gyroscope bias as a column matrix.
3548 * Matrix values are expressed in radians per second (rad/s).
3549 *
3550 * @param result instance where result data will be copied to.
3551 * @throws IllegalArgumentException if provided matrix is not 3x1.
3552 */
3553 @Override
3554 public void getBiasAsMatrix(final Matrix result) {
3555 if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != 1) {
3556 throw new IllegalArgumentException();
3557 }
3558 result.setElementAtIndex(0, biasX);
3559 result.setElementAtIndex(1, biasY);
3560 result.setElementAtIndex(2, biasZ);
3561 }
3562
3563 /**
3564 * Sets known gyroscope bias as a column matrix.
3565 * Matrix values are expressed in radians per second (rad/s).
3566 *
3567 * @param bias known gyroscope bias.
3568 * @throws LockedException if calibrator is currently running.
3569 * @throws IllegalArgumentException if provided matrix is not 3x1.
3570 */
3571 @Override
3572 public void setBias(final Matrix bias) throws LockedException {
3573 if (running) {
3574 throw new LockedException();
3575 }
3576 if (bias.getRows() != BodyKinematics.COMPONENTS || bias.getColumns() != 1) {
3577 throw new IllegalArgumentException();
3578 }
3579
3580 biasX = bias.getElementAtIndex(0);
3581 biasY = bias.getElementAtIndex(1);
3582 biasZ = bias.getElementAtIndex(2);
3583 }
3584
3585 /**
3586 * Gets initial gyroscope scale factors and cross coupling errors
3587 * matrix.
3588 *
3589 * @return initial gyroscope scale factors and cross coupling errors
3590 * matrix.
3591 */
3592 @Override
3593 public Matrix getInitialMg() {
3594 Matrix result;
3595 try {
3596 result = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
3597 getInitialMg(result);
3598 } catch (final WrongSizeException ignore) {
3599 // never happens
3600 result = null;
3601 }
3602 return result;
3603 }
3604
3605 /**
3606 * Gets initial gyroscope scale factors and cross coupling errors
3607 * matrix.
3608 *
3609 * @param result instance where data will be stored.
3610 * @throws IllegalArgumentException if provided matrix is not 3x3.
3611 */
3612 @Override
3613 public void getInitialMg(final Matrix result) {
3614 if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != BodyKinematics.COMPONENTS) {
3615 throw new IllegalArgumentException();
3616 }
3617 result.setElementAtIndex(0, initialSx);
3618 result.setElementAtIndex(1, initialMyx);
3619 result.setElementAtIndex(2, initialMzx);
3620
3621 result.setElementAtIndex(3, initialMxy);
3622 result.setElementAtIndex(4, initialSy);
3623 result.setElementAtIndex(5, initialMzy);
3624
3625 result.setElementAtIndex(6, initialMxz);
3626 result.setElementAtIndex(7, initialMyz);
3627 result.setElementAtIndex(8, initialSz);
3628 }
3629
3630 /**
3631 * Sets initial gyroscope scale factors and cross coupling errors matrix.
3632 *
3633 * @param initialMg initial scale factors and cross coupling errors matrix.
3634 * @throws IllegalArgumentException if provided matrix is not 3x3.
3635 * @throws LockedException if calibrator is currently running.
3636 */
3637 @Override
3638 public void setInitialMg(final Matrix initialMg) throws LockedException {
3639 if (running) {
3640 throw new LockedException();
3641 }
3642 if (initialMg.getRows() != BodyKinematics.COMPONENTS || initialMg.getColumns() != BodyKinematics.COMPONENTS) {
3643 throw new IllegalArgumentException();
3644 }
3645
3646 initialSx = initialMg.getElementAtIndex(0);
3647 initialMyx = initialMg.getElementAtIndex(1);
3648 initialMzx = initialMg.getElementAtIndex(2);
3649
3650 initialMxy = initialMg.getElementAtIndex(3);
3651 initialSy = initialMg.getElementAtIndex(4);
3652 initialMzy = initialMg.getElementAtIndex(5);
3653
3654 initialMxz = initialMg.getElementAtIndex(6);
3655 initialMyz = initialMg.getElementAtIndex(7);
3656 initialSz = initialMg.getElementAtIndex(8);
3657 }
3658
3659 /**
3660 * Gets initial G-dependent cross biases introduced on the gyroscope by the
3661 * specific forces sensed by the accelerometer.
3662 *
3663 * @return a 3x3 matrix containing initial g-dependent cross biases.
3664 */
3665 @Override
3666 public Matrix getInitialGg() {
3667 return new Matrix(initialGg);
3668 }
3669
3670 /**
3671 * Gets initial G-dependent cross biases introduced on the gyroscope by the
3672 * specific forces sensed by the accelerometer.
3673 *
3674 * @param result instance where data will be stored.
3675 * @throws IllegalArgumentException if provided matrix is not 3x3.
3676 */
3677 @Override
3678 public void getInitialGg(final Matrix result) {
3679 if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != BodyKinematics.COMPONENTS) {
3680 throw new IllegalArgumentException();
3681 }
3682
3683 result.copyFrom(initialGg);
3684 }
3685
3686 /**
3687 * Sets initial G-dependent cross biases introduced on the gyroscope by the
3688 * specific forces sensed by the accelerometer.
3689 *
3690 * @param initialGg g-dependent cross biases.
3691 * @throws LockedException if calibrator is currently running.
3692 * @throws IllegalArgumentException if provided matrix is not 3x3.
3693 */
3694 @Override
3695 public void setInitialGg(final Matrix initialGg) throws LockedException {
3696 if (running) {
3697 throw new LockedException();
3698 }
3699
3700 if (initialGg.getRows() != BodyKinematics.COMPONENTS || initialGg.getColumns() != BodyKinematics.COMPONENTS) {
3701 throw new IllegalArgumentException();
3702 }
3703
3704 initialGg.copyTo(this.initialGg);
3705 }
3706
3707 /**
3708 * Gets constant rotation rate at which the turntable is spinning.
3709 * This is expressed in radians per second (rad/s).
3710 *
3711 * @return constant rotation rate of turntable.
3712 */
3713 public double getTurntableRotationRate() {
3714 return turntableRotationRate;
3715 }
3716
3717 /**
3718 * Sets constant rotation rate at which the turntable is spinning.
3719 * This is expressed in radians per second (rad/s).
3720 *
3721 * @param turntableRotationRate constant rotation rate of turntable.
3722 * @throws LockedException if calibrator is currently running
3723 * @throws IllegalArgumentException if provided value is zero or
3724 * negative.
3725 */
3726 public void setTurntableRotationRate(final double turntableRotationRate) throws LockedException {
3727 if (running) {
3728 throw new LockedException();
3729 }
3730 if (turntableRotationRate <= 0.0) {
3731 throw new IllegalArgumentException();
3732 }
3733
3734 this.turntableRotationRate = turntableRotationRate;
3735 }
3736
3737 /**
3738 * Gets constant rotation rate at which the turntable is spinning.
3739 *
3740 * @return constant rotation rate of turntable.
3741 */
3742 public AngularSpeed getTurntableRotationRateAsAngularSpeed() {
3743 return new AngularSpeed(turntableRotationRate, AngularSpeedUnit.RADIANS_PER_SECOND);
3744 }
3745
3746 /**
3747 * Gets constant rotation rate at which the turntable is spinning.
3748 *
3749 * @param result instance where result will be stored.
3750 */
3751 public void getTurntableRotationRateAsAngularSpeed(final AngularSpeed result) {
3752 result.setValue(turntableRotationRate);
3753 result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
3754 }
3755
3756 /**
3757 * Sets constant rotation rate at which the turntable is spinning.
3758 *
3759 * @param turntableRotationRate constant rotation rate of turntable.
3760 * @throws LockedException if calibrator is currently running.
3761 * @throws IllegalArgumentException if provided value is zero or
3762 * negative.
3763 */
3764 public void setTurntableRotationRate(final AngularSpeed turntableRotationRate) throws LockedException {
3765 if (running) {
3766 throw new LockedException();
3767 }
3768 setTurntableRotationRate(convertAngularSpeed(turntableRotationRate));
3769 }
3770
3771 /**
3772 * Gets time interval between measurements being captured expressed in
3773 * seconds (s).
3774 *
3775 * @return time interval between measurements.
3776 */
3777 public double getTimeInterval() {
3778 return timeInterval;
3779 }
3780
3781 /**
3782 * Sets time interval between measurements being captured expressed in
3783 * seconds (s).
3784 *
3785 * @param timeInterval time interval between measurements.
3786 * @throws LockedException if calibrator is currently running.
3787 * @throws IllegalArgumentException if provided value is zero or
3788 * negative.
3789 */
3790 public void setTimeInterval(final double timeInterval) throws LockedException {
3791 if (running) {
3792 throw new LockedException();
3793 }
3794
3795 if (timeInterval <= 0.0) {
3796 throw new IllegalArgumentException();
3797 }
3798 this.timeInterval = timeInterval;
3799 }
3800
3801 /**
3802 * Gets time interval between measurements being captured.
3803 *
3804 * @return time interval between measurements.
3805 */
3806 public Time getTimeIntervalAsTime() {
3807 return new Time(timeInterval, TimeUnit.SECOND);
3808 }
3809
3810 /**
3811 * Gets time interval between measurements being captured.
3812 *
3813 * @param result instance where result will be stored.
3814 */
3815 public void getTimeIntervalAsTime(final Time result) {
3816 result.setValue(timeInterval);
3817 result.setUnit(TimeUnit.SECOND);
3818 }
3819
3820 /**
3821 * Sets time interval between measurements being captured.
3822 *
3823 * @param timeInterval time interval between measurements.
3824 * @throws LockedException if calibrator is currently running.
3825 */
3826 public void setTimeInterval(final Time timeInterval) throws LockedException {
3827 if (running) {
3828 throw new LockedException();
3829 }
3830 setTimeInterval(convertTime(timeInterval));
3831 }
3832
3833 /**
3834 * Gets a collection of body kinematics measurements taken at
3835 * a given position with different unknown orientations and containing
3836 * the standard deviations of accelerometer and gyroscope measurements.
3837 *
3838 * @return collection of body kinematics measurements at a known position
3839 * with unknown orientations.
3840 */
3841 @Override
3842 public Collection<StandardDeviationBodyKinematics> getMeasurements() {
3843 return measurements;
3844 }
3845
3846 /**
3847 * Sets a collection of body kinematics measurements taken at
3848 * a given position with different unknown orientations and containing
3849 * the standard deviations of accelerometer and gyroscope measurements.
3850 *
3851 * @param measurements collection of body kinematics measurements at a
3852 * known position with unknown orientations.
3853 * @throws LockedException if calibrator is currently running.
3854 */
3855 @Override
3856 public void setMeasurements(final Collection<StandardDeviationBodyKinematics> measurements) throws LockedException {
3857 if (running) {
3858 throw new LockedException();
3859 }
3860 this.measurements = measurements;
3861 }
3862
3863 /**
3864 * Gets position where body kinematics measures have been taken expressed in
3865 * ECEF coordinates.
3866 *
3867 * @return position where body kinematics measures have been taken.
3868 */
3869 public ECEFPosition getEcefPosition() {
3870 return position;
3871 }
3872
3873 /**
3874 * Gets position where body kinematics measures have been taken expressed in
3875 * ECEF coordinates.
3876 *
3877 * @param position position where body kinematics measures have been taken.
3878 * @throws LockedException if calibrator is currently running.
3879 */
3880 public void setPosition(final ECEFPosition position) throws LockedException {
3881 if (running) {
3882 throw new LockedException();
3883 }
3884
3885 this.position = position;
3886 }
3887
3888 /**
3889 * Gets position where body kinematics measures have been taken expressed in
3890 * NED coordinates.
3891 *
3892 * @return position where body kinematics measures have been taken or null if
3893 * not available.
3894 */
3895 public NEDPosition getNedPosition() {
3896 final var result = new NEDPosition();
3897 return getNedPosition(result) ? result : null;
3898 }
3899
3900 /**
3901 * Gets position where body kinematics measures have been taken expressed in
3902 * NED coordinates.
3903 *
3904 * @param result instance where result will be stored.
3905 * @return true if NED position could be computed, false otherwise.
3906 */
3907 public boolean getNedPosition(final NEDPosition result) {
3908 if (position != null) {
3909 final var velocity = new NEDVelocity();
3910 ECEFtoNEDPositionVelocityConverter.convertECEFtoNED(
3911 position.getX(), position.getY(), position.getZ(), 0.0, 0.0, 0.0, result, velocity);
3912 return true;
3913 } else {
3914 return false;
3915 }
3916 }
3917
3918 /**
3919 * Sets position where body kinematics measures have been taken expressed in
3920 * NED coordinates.
3921 *
3922 * @param position position where body kinematics measures have been taken.
3923 * @throws LockedException if calibrator is currently running.
3924 */
3925 public void setPosition(final NEDPosition position) throws LockedException {
3926 if (running) {
3927 throw new LockedException();
3928 }
3929
3930 this.position = convertPosition(position);
3931 }
3932
3933 /**
3934 * Indicates the type of measurement or sequence used by this calibrator.
3935 *
3936 * @return type of measurement or sequence used by this calibrator.
3937 */
3938 @Override
3939 public GyroscopeCalibratorMeasurementOrSequenceType getMeasurementOrSequenceType() {
3940 return GyroscopeCalibratorMeasurementOrSequenceType.STANDARD_DEVIATION_BODY_KINEMATICS_MEASUREMENT;
3941 }
3942
3943 /**
3944 * Indicates whether this calibrator requires ordered measurements or sequences
3945 * in a list or not.
3946 *
3947 * @return true if measurements or sequences must be ordered, false otherwise.
3948 */
3949 @Override
3950 public boolean isOrderedMeasurementsOrSequencesRequired() {
3951 return false;
3952 }
3953
3954 /**
3955 * Indicates whether this calibrator requires quality scores for each
3956 * measurement/sequence or not.
3957 *
3958 * @return true if quality scores are required, false otherwise.
3959 */
3960 @Override
3961 public boolean isQualityScoresRequired() {
3962 return false;
3963 }
3964
3965 /**
3966 * Indicates whether z-axis is assumed to be common for accelerometer and
3967 * gyroscope.
3968 * When enabled, this eliminates 3 variables from Ma matrix.
3969 *
3970 * @return true if z-axis is assumed to be common for accelerometer and gyroscope,
3971 * false otherwise.
3972 */
3973 @Override
3974 public boolean isCommonAxisUsed() {
3975 return commonAxisUsed;
3976 }
3977
3978 /**
3979 * Specifies whether z-axis is assumed to be common for accelerometer and
3980 * gyroscope.
3981 * When enabled, this eliminates 3 variables from Ma matrix.
3982 *
3983 * @param commonAxisUsed true if z-axis is assumed to be common for accelerometer
3984 * and gyroscope, false otherwise.
3985 * @throws LockedException if calibrator is currently running.
3986 */
3987 @Override
3988 public void setCommonAxisUsed(final boolean commonAxisUsed) throws LockedException {
3989 if (running) {
3990 throw new LockedException();
3991 }
3992
3993 this.commonAxisUsed = commonAxisUsed;
3994 }
3995
3996 /**
3997 * Indicates whether G-dependent cross biases are being estimated
3998 * or not.
3999 * When enabled, this adds 9 variables from Gg matrix.
4000 *
4001 * @return true if G-dependent cross biases will be estimated,
4002 * false otherwise.
4003 */
4004 public boolean isGDependentCrossBiasesEstimated() {
4005 return estimateGDependentCrossBiases;
4006 }
4007
4008 /**
4009 * Specifies whether G-dependent cross biases are being estimated
4010 * or not.
4011 * When enabled, this adds 9 variables from Gg matrix.
4012 *
4013 * @param estimateGDependentCrossBiases true if G-dependent cross
4014 * biases will be estimated,
4015 * false otherwise.
4016 * @throws LockedException if calibrator is currently running.
4017 */
4018 public void setGDependentCrossBiasesEstimated(final boolean estimateGDependentCrossBiases) throws LockedException {
4019 if (running) {
4020 throw new LockedException();
4021 }
4022
4023 this.estimateGDependentCrossBiases = estimateGDependentCrossBiases;
4024 }
4025
4026 /**
4027 * Gets listener to handle events raised by this estimator.
4028 *
4029 * @return listener to handle events raised by this estimator.
4030 */
4031 public KnownBiasTurntableGyroscopeCalibratorListener getListener() {
4032 return listener;
4033 }
4034
4035 /**
4036 * Sets listener to handle events raised by this estimator.
4037 *
4038 * @param listener listener to handle events raised by this estimator.
4039 * @throws LockedException if calibrator is currently running.
4040 */
4041 public void setListener(
4042 final KnownBiasTurntableGyroscopeCalibratorListener listener) throws LockedException {
4043 if (running) {
4044 throw new LockedException();
4045 }
4046
4047 this.listener = listener;
4048 }
4049
4050 /**
4051 * Gets minimum number of required measurements.
4052 *
4053 * @return minimum number of required measurements.
4054 */
4055 @Override
4056 public int getMinimumRequiredMeasurementsOrSequences() {
4057 if (commonAxisUsed) {
4058 if (estimateGDependentCrossBiases) {
4059 return MINIMUM_MEASUREMENTS_COMMON_Z_AXIS_AND_CROSS_BIASES;
4060 } else {
4061 return MINIMUM_MEASUREMENTS_COMMON_Z_AXIS;
4062 }
4063 } else {
4064 if (estimateGDependentCrossBiases) {
4065 return MINIMUM_MEASUREMENTS_GENERAL_AND_CROSS_BIASES;
4066 } else {
4067 return MINIMUM_MEASUREMENTS_GENERAL;
4068 }
4069 }
4070 }
4071
4072 /**
4073 * Indicates whether calibrator is ready to start.
4074 *
4075 * @return true if calibrator is ready, false otherwise.
4076 */
4077 @Override
4078 public boolean isReady() {
4079 return measurements != null && measurements.size() >= getMinimumRequiredMeasurementsOrSequences();
4080 }
4081
4082 /**
4083 * Indicates whether calibrator is currently running or not.
4084 *
4085 * @return true if calibrator is running, false otherwise.
4086 */
4087 @Override
4088 public boolean isRunning() {
4089 return running;
4090 }
4091
4092 /**
4093 * Estimates gyroscope calibration parameters containing bias, scale factors,
4094 * cross-coupling errors and G-dependent coupling.
4095 *
4096 * @throws LockedException if calibrator is currently running.
4097 * @throws NotReadyException if calibrator is not ready.
4098 * @throws CalibrationException if estimation fails for numerical reasons.
4099 */
4100 @Override
4101 public void calibrate() throws LockedException, NotReadyException, CalibrationException {
4102 if (running) {
4103 throw new LockedException();
4104 }
4105
4106 if (!isReady()) {
4107 throw new NotReadyException();
4108 }
4109
4110 try {
4111 running = true;
4112
4113 if (listener != null) {
4114 listener.onCalibrateStart(this);
4115 }
4116
4117 if (commonAxisUsed) {
4118 if (estimateGDependentCrossBiases) {
4119 calibrateCommonAxisAndGDependentCrossBiases();
4120 } else {
4121 calibrateCommonAxis();
4122 }
4123 } else {
4124 if (estimateGDependentCrossBiases) {
4125 calibrateGeneralAndGDependentCrossBiases();
4126 } else {
4127 calibrateGeneral();
4128 }
4129 }
4130
4131 if (listener != null) {
4132 listener.onCalibrateEnd(this);
4133 }
4134
4135 } catch (final AlgebraException | FittingException | com.irurueta.numerical.NotReadyException
4136 | InvalidSourceAndDestinationFrameTypeException e) {
4137 throw new CalibrationException(e);
4138 } finally {
4139 running = false;
4140 }
4141 }
4142
4143 /**
4144 * Gets estimated gyroscope scale factors and cross coupling errors.
4145 * This is the product of matrix Tg containing cross coupling errors and Kg
4146 * containing scaling factors.
4147 * So that:
4148 * <pre>
4149 * Mg = [sx mxy mxz] = Tg*Kg
4150 * [myx sy myz]
4151 * [mzx mzy sz ]
4152 * </pre>
4153 * Where:
4154 * <pre>
4155 * Kg = [sx 0 0 ]
4156 * [0 sy 0 ]
4157 * [0 0 sz]
4158 * </pre>
4159 * and
4160 * <pre>
4161 * Tg = [1 -alphaXy alphaXz ]
4162 * [alphaYx 1 -alphaYz]
4163 * [-alphaZx alphaZy 1 ]
4164 * </pre>
4165 * Hence:
4166 * <pre>
4167 * Mg = [sx mxy mxz] = Tg*Kg = [sx -sy * alphaXy sz * alphaXz ]
4168 * [myx sy myz] [sx * alphaYx sy -sz * alphaYz]
4169 * [mzx mzy sz ] [-sx * alphaZx sy * alphaZy sz ]
4170 * </pre>
4171 * This instance allows any 3x3 matrix however, typically alphaYx, alphaZx and alphaZy
4172 * are considered to be zero if the gyroscope z-axis is assumed to be the same
4173 * as the body z-axis. When this is assumed, myx = mzx = mzy = 0 and the Mg matrix
4174 * becomes upper diagonal:
4175 * <pre>
4176 * Mg = [sx mxy mxz]
4177 * [0 sy myz]
4178 * [0 0 sz ]
4179 * </pre>
4180 * Values of this matrix are unit-less.
4181 *
4182 * @return estimated gyroscope scale factors and cross coupling errors, or null
4183 * if not available.
4184 */
4185 @Override
4186 public Matrix getEstimatedMg() {
4187 return estimatedMg;
4188 }
4189
4190 /**
4191 * Gets estimated gyroscope x-axis scale factor.
4192 *
4193 * @return estimated gyroscope x-axis scale factor or null
4194 * if not available.
4195 */
4196 @Override
4197 public Double getEstimatedSx() {
4198 return estimatedMg != null ? estimatedMg.getElementAt(0, 0) : null;
4199 }
4200
4201 /**
4202 * Gets estimated gyroscope y-axis scale factor.
4203 *
4204 * @return estimated gyroscope y-axis scale factor or null
4205 * if not available.
4206 */
4207 @Override
4208 public Double getEstimatedSy() {
4209 return estimatedMg != null ? estimatedMg.getElementAt(1, 1) : null;
4210 }
4211
4212 /**
4213 * Gets estimated gyroscope z-axis scale factor.
4214 *
4215 * @return estimated gyroscope z-axis scale factor or null
4216 * if not available.
4217 */
4218 @Override
4219 public Double getEstimatedSz() {
4220 return estimatedMg != null ? estimatedMg.getElementAt(2, 2) : null;
4221 }
4222
4223 /**
4224 * Gets estimated gyroscope x-y cross-coupling error.
4225 *
4226 * @return estimated gyroscope x-y cross-coupling error or null
4227 * if not available.
4228 */
4229 @Override
4230 public Double getEstimatedMxy() {
4231 return estimatedMg != null ? estimatedMg.getElementAt(0, 1) : null;
4232 }
4233
4234 /**
4235 * Gets estimated gyroscope x-z cross-coupling error.
4236 *
4237 * @return estimated gyroscope x-z cross-coupling error or null
4238 * if not available.
4239 */
4240 @Override
4241 public Double getEstimatedMxz() {
4242 return estimatedMg != null ? estimatedMg.getElementAt(0, 2) : null;
4243 }
4244
4245 /**
4246 * Gets estimated gyroscope y-x cross-coupling error.
4247 *
4248 * @return estimated gyroscope y-x cross-coupling error or null
4249 * if not available.
4250 */
4251 @Override
4252 public Double getEstimatedMyx() {
4253 return estimatedMg != null ? estimatedMg.getElementAt(1, 0) : null;
4254 }
4255
4256 /**
4257 * Gets estimated gyroscope y-z cross-coupling error.
4258 *
4259 * @return estimated gyroscope y-z cross-coupling error or null
4260 * if not available.
4261 */
4262 @Override
4263 public Double getEstimatedMyz() {
4264 return estimatedMg != null ? estimatedMg.getElementAt(1, 2) : null;
4265 }
4266
4267 /**
4268 * Gets estimated gyroscope z-x cross-coupling error.
4269 *
4270 * @return estimated gyroscope z-x cross-coupling error or null
4271 * if not available.
4272 */
4273 @Override
4274 public Double getEstimatedMzx() {
4275 return estimatedMg != null ? estimatedMg.getElementAt(2, 0) : null;
4276 }
4277
4278 /**
4279 * Gets estimated gyroscope z-y cross-coupling error.
4280 *
4281 * @return estimated gyroscope z-y cross-coupling error or null
4282 * if not available.
4283 */
4284 @Override
4285 public Double getEstimatedMzy() {
4286 return estimatedMg != null ? estimatedMg.getElementAt(2, 1) : null;
4287 }
4288
4289 /**
4290 * Gets estimated G-dependent cross biases introduced on the gyroscope by the
4291 * specific forces sensed by the accelerometer.
4292 * This instance allows any 3x3 matrix.
4293 *
4294 * @return estimated G-dependent cross biases.
4295 */
4296 @Override
4297 public Matrix getEstimatedGg() {
4298 return estimatedGg;
4299 }
4300
4301 /**
4302 * Gets estimated covariance matrix for estimated parameters.
4303 * Diagonal elements of the matrix contains variance for the following
4304 * parameters (following indicated order): sx, sy, sz, mxy, mxz, myx,
4305 * myz, mzx, mzy, gg11, gg21, gg31, gg12, gg22, gg32, gg13, gg23, gg33.
4306 *
4307 * @return estimated covariance matrix for estimated parameters.
4308 */
4309 @Override
4310 public Matrix getEstimatedCovariance() {
4311 return estimatedCovariance;
4312 }
4313
4314 /**
4315 * Gets estimated chi square value.
4316 *
4317 * @return estimated chi square value.
4318 */
4319 @Override
4320 public double getEstimatedChiSq() {
4321 return estimatedChiSq;
4322 }
4323
4324 /**
4325 * Gets estimated chi square degrees of freedom. Degrees of freedom is equal to the number of sampled data minus the
4326 * number of estimated parameters.
4327 *
4328 * @return estimated degrees of freedom of chi square value
4329 */
4330 @Override
4331 public int getEstimatedChiSqDegreesOfFreedom() {
4332 return estimatedChiSqDegreesOfFreedom;
4333 }
4334
4335 /**
4336 * Gets estimated reduced chi square value. This is equal to estimated chi square value divided by its degrees of
4337 * freedom. Ideally this value should be close to 1.0, indicating that fit is optimal.
4338 * A value larger than 1.0 indicates that fit is not good or noise has been underestimated, and a value smaller than
4339 * 1.0 indicates that there is overfitting or noise has been overestimated.
4340 *
4341 * @return estimated reduced chi square value
4342 */
4343 @Override
4344 public double getEstimatedReducedChiSq() {
4345 return estimatedReducedChiSq;
4346 }
4347
4348 /**
4349 * Gets estimated mean square error respect to provided measurements.
4350 *
4351 * @return estimated mean square error respect to provided measurements.
4352 */
4353 @Override
4354 public double getEstimatedMse() {
4355 return estimatedMse;
4356 }
4357
4358 /**
4359 * Gets estimated probability of finding a smaller chi square value expressed as a value between 0.0 and 1.0. The
4360 * smaller the found chi square value is, the better the fit of the estimated parameters to the actual parameter.
4361 * Thus, the smaller the chance of finding a smaller chi square value, then the better the estimated fit is.
4362 *
4363 * @return estimated probability of finding a smaller chi square value.
4364 */
4365 @Override
4366 public double getEstimatedP() {
4367 return estimatedP;
4368 }
4369
4370 /**
4371 * Gets estimated measure of quality of estimated fit as a value between 0.0 and 1.0. The larger the quality value
4372 * is, the better the fit that has been estimated.
4373 *
4374 * @return estimated measure of quality of estimated fit.
4375 */
4376 @Override
4377 public double getEstimatedQ() {
4378 return estimatedQ;
4379 }
4380
4381 /**
4382 * Internal method to perform calibration when common z-axis is assumed
4383 * for both the accelerometer and gyroscope and when G-dependent cross
4384 * biases are being estimated.
4385 *
4386 * @throws AlgebraException if there are numerical errors.
4387 * @throws FittingException if no convergence to solution is found.
4388 * @throws com.irurueta.numerical.NotReadyException if fitter is not ready.
4389 * @throws InvalidSourceAndDestinationFrameTypeException never happens
4390 */
4391 private void calibrateCommonAxisAndGDependentCrossBiases() throws AlgebraException, FittingException,
4392 com.irurueta.numerical.NotReadyException, InvalidSourceAndDestinationFrameTypeException {
4393
4394 // The gyroscope model is
4395 // Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue + w
4396
4397 // Ideally a least squares solution tries to minimize noise component, so:
4398 // Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue
4399
4400 // For convergence purposes of the Levenberg-Marquardt algorithm, we
4401 // take common factor M = I + Mg
4402
4403 // and the gyroscope model can be better expressed as:
4404
4405 // Ωmeas = M*(Ωtrue + b + G * ftrue)
4406
4407 // where:
4408 // bg = M*b --> b = M^-1*bg
4409 // Gg = M*G --> G = M^-1*Gg
4410
4411 // We know that the norm of the true angular rate when the device is in a pixed
4412 // and unknown position and orientation is equal to the Earth rotation rate.
4413 // ||Ωtrue|| = 7.292115E-5 rad/s
4414
4415 // Hence
4416 // Ωmeas - M*b - M*G*ftrue = M*Ωtrue
4417 // M^-1 * (Ωmeas - M*b - M*G*ftrue) = Ωtrue
4418
4419 // ||Ωtrue||^2 = (M^-1 * (Ωmeas - M*b - M*G*ftrue))^T*(M^-1 * (Ωmeas - M*b - M*G*ftrue))
4420 // ||Ωtrue||^2 = (Ωmeas - M*b - M*G*ftrue)^T * (M^-1)^T * M^-1 * (Ωmeas - M*b - M*G*ftrue)
4421 // ||Ωtrue||^2 = (Ωmeas - M*b - M*G*ftrue)^T * ||M^-1||^2 * (Ωmeas - M*b - M*G*ftrue)
4422 // ||Ωtrue||^2 = ||Ωmeas - M*b - M*G*ftrue||^2 * ||M^-1||^2
4423
4424 // Where:
4425
4426 // b = [bx]
4427 // [by]
4428 // [bz]
4429
4430 // M = [m11 m12 m13]
4431 // [0 m22 m23]
4432 // [0 0 m33]
4433
4434 // G = [g11 g12 g13]
4435 // [g21 g22 g23]
4436 // [g31 g32 g33]
4437
4438 // ftrue = [ftruex]
4439 // [ftruey]
4440 // [fturez]
4441
4442 final var gradientEstimator = new GradientEstimator(this::evaluateCommonAxisWitGDependentCrossBiases);
4443
4444 final var initialM = Matrix.identity(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
4445 initialM.add(getInitialMg());
4446
4447 // Force initial M to be upper diagonal
4448 initialM.setElementAt(1, 0, 0.0);
4449 initialM.setElementAt(2, 0, 0.0);
4450 initialM.setElementAt(2, 1, 0.0);
4451
4452 final var invInitM = Utils.inverse(initialM);
4453 final var initGg = getInitialGg();
4454 final var initG = invInitM.multiplyAndReturnNew(initGg);
4455
4456 fitter.setFunctionEvaluator(new LevenbergMarquardtMultiDimensionFunctionEvaluator() {
4457 @Override
4458 public int getNumberOfDimensions() {
4459 // Input points are measured angular rate coordinates +
4460 // measured specific force coordinates
4461 return 2 * BodyKinematics.COMPONENTS;
4462 }
4463
4464 @Override
4465 public double[] createInitialParametersArray() {
4466 final var initial = new double[COMMON_Z_AXIS_UNKNOWNS_AND_CROSS_BIASES];
4467
4468 // upper diagonal cross coupling errors M
4469 var k = 0;
4470 for (var j = 0; j < BodyKinematics.COMPONENTS; j++) {
4471 for (var i = 0; i < BodyKinematics.COMPONENTS; i++) {
4472 if (i <= j) {
4473 initial[k] = initialM.getElementAt(i, j);
4474 k++;
4475 }
4476 }
4477 }
4478
4479 // g-dependent cross biases G
4480 final var num = BodyKinematics.COMPONENTS * BodyKinematics.COMPONENTS;
4481 for (int i = 0, j = k; i < num; i++, j++) {
4482 initial[j] = initG.getElementAtIndex(i);
4483 }
4484
4485 return initial;
4486 }
4487
4488 @Override
4489 public double evaluate(
4490 final int i, final double[] point, final double[] params, final double[] derivatives)
4491 throws EvaluationException {
4492
4493 measAngularRateX = point[0];
4494 measAngularRateY = point[1];
4495 measAngularRateZ = point[2];
4496
4497 fmeasX = point[3];
4498 fmeasY = point[4];
4499 fmeasZ = point[5];
4500
4501 gradientEstimator.gradient(params, derivatives);
4502
4503 return evaluateCommonAxisWitGDependentCrossBiases(params);
4504 }
4505 });
4506
4507 setInputDataWithGDependentCrossBiases();
4508
4509 fitter.fit();
4510
4511 final var result = fitter.getA();
4512
4513 final var m11 = result[0];
4514
4515 final var m12 = result[1];
4516 final var m22 = result[2];
4517
4518 final var m13 = result[3];
4519 final var m23 = result[4];
4520 final var m33 = result[5];
4521
4522 final var g11 = result[6];
4523 final var g21 = result[7];
4524 final var g31 = result[8];
4525
4526 final var g12 = result[9];
4527 final var g22 = result[10];
4528 final var g32 = result[11];
4529
4530 final var g13 = result[12];
4531 final var g23 = result[13];
4532 final var g33 = result[14];
4533
4534 final var mm = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
4535 mm.setElementAtIndex(0, m11);
4536 mm.setElementAtIndex(1, 0.0);
4537 mm.setElementAtIndex(2, 0.0);
4538
4539 mm.setElementAtIndex(3, m12);
4540 mm.setElementAtIndex(4, m22);
4541 mm.setElementAtIndex(5, 0.0);
4542
4543 mm.setElementAtIndex(6, m13);
4544 mm.setElementAtIndex(7, m23);
4545 mm.setElementAtIndex(8, m33);
4546
4547 final var mg = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
4548 mg.setElementAtIndex(0, g11);
4549 mg.setElementAtIndex(1, g21);
4550 mg.setElementAtIndex(2, g31);
4551
4552 mg.setElementAtIndex(3, g12);
4553 mg.setElementAtIndex(4, g22);
4554 mg.setElementAtIndex(5, g32);
4555
4556 mg.setElementAtIndex(6, g13);
4557 mg.setElementAtIndex(7, g23);
4558 mg.setElementAtIndex(8, g33);
4559
4560 setResult(mm, mg);
4561
4562 // at this point covariance is expressed in terms of M and G, and must
4563 // be expressed in terms of Mg and Gg.
4564 // We know that:
4565 // Mg = M - I
4566 // Gg = M * G
4567
4568 // M = [m11 m12 m13]
4569 // [0 m22 m23]
4570 // [0 0 m33]
4571
4572 // G = [g11 g12 g13]
4573 // [g21 g22 g23]
4574 // [g31 g32 g33]
4575
4576 // Mg = [sx mxy mxz] = [m11 - 1 m12 m13 ]
4577 // [myx sy myz] [0 m22 - 1 m23 ]
4578 // [mzx mzy sz ] [0 0 m33 - 1 ]
4579
4580 // Gg = [gg11 gg12 gg13] = [m11 m12 m13][g11 g12 g13]
4581 // [gg21 gg22 gg23] [0 m22 m23][g21 g22 g23]
4582 // [gg31 gg32 gg33] [0 0 m33][g31 g32 g33]
4583
4584 // Defining the linear application:
4585 // F(M, G) = F(m11, m12, m22, m32=0, m13, m23, m33, g11, g21, g31, g12, g22, g32, g13, g23, g33)
4586 // as:
4587 // [sx] = [m11 - 1]
4588 // [sy] [m22 - 1]
4589 // [sz] [m33 - 1]
4590 // [mxy] [m12]
4591 // [mxz] [m13]
4592 // [myx] [0]
4593 // [myz] [m23]
4594 // [mzx] [0]
4595 // [mzy] [0]
4596 // [gg11] [m11 * g11 + m12 * g21 + m13 * g31]
4597 // [gg21] [ m22 * g21 + m23 * g31]
4598 // [gg31] [ m33 * g31]
4599 // [gg12] [m11 * g12 + m12 * g22 + m13 * g32]
4600 // [gg22] [ m22 * g22 + n23 * g32]
4601 // [gg32] [ m33 * g32]
4602 // [gg13] [m11 * g13 + m12 * g23 + m13 * g33]
4603 // [gg23] [ m22 * g23 + m23 * g33]
4604 // [gg33] [ m33 * g33]
4605
4606 // Then the Jacobian of F(M, G) is:
4607 // J = [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
4608 // [0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 ]
4609 // [0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 ]
4610 // [0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
4611 // [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 ]
4612 // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
4613 // [0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 ]
4614 // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
4615 // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
4616 // [g11 g21 0 g31 0 0 m11 m12 m13 0 0 0 0 0 0 ]
4617 // [0 0 g21 0 g31 0 0 m22 m23 0 0 0 0 0 0 ]
4618 // [0 0 0 0 0 g31 0 0 m33 0 0 0 0 0 0 ]
4619 // [g12 g22 0 g32 0 0 0 0 0 m11 m12 m13 0 0 0 ]
4620 // [0 0 g22 0 g32 0 0 0 0 0 m22 m23 0 0 0 ]
4621 // [0 0 0 0 0 g32 0 0 0 0 0 m33 0 0 0 ]
4622 // [g13 g23 0 g33 0 0 0 0 0 0 0 0 m11 m12 m13]
4623 // [0 0 g23 0 g33 0 0 0 0 0 0 0 0 m22 m23]
4624 // [0 0 0 0 0 g33 0 0 0 0 0 0 0 0 m33]
4625
4626 // We know that the propagated covariance is J * Cov * J', hence:
4627 final var jacobian = new Matrix(GENERAL_UNKNOWNS_AND_CROSS_BIASES, COMMON_Z_AXIS_UNKNOWNS_AND_CROSS_BIASES);
4628
4629 jacobian.setElementAt(0, 0, 1.0);
4630 jacobian.setElementAt(1, 2, 1.0);
4631 jacobian.setElementAt(2, 5, 1.0);
4632
4633 jacobian.setElementAt(3, 1, 1.0);
4634 jacobian.setElementAt(4, 3, 1.0);
4635
4636 jacobian.setElementAt(6, 4, 1.0);
4637
4638 jacobian.setElementAt(9, 0, g11);
4639 jacobian.setElementAt(9, 1, g21);
4640 jacobian.setElementAt(9, 3, g31);
4641 jacobian.setElementAt(9, 6, m11);
4642 jacobian.setElementAt(9, 7, m12);
4643 jacobian.setElementAt(9, 8, m13);
4644
4645 jacobian.setElementAt(10, 2, g21);
4646 jacobian.setElementAt(10, 4, g31);
4647 jacobian.setElementAt(10, 7, m22);
4648 jacobian.setElementAt(10, 8, m23);
4649
4650 jacobian.setElementAt(11, 5, g31);
4651 jacobian.setElementAt(11, 8, m33);
4652
4653 jacobian.setElementAt(12, 0, g12);
4654 jacobian.setElementAt(12, 1, g22);
4655 jacobian.setElementAt(12, 3, g32);
4656 jacobian.setElementAt(12, 9, m11);
4657 jacobian.setElementAt(12, 10, m12);
4658 jacobian.setElementAt(12, 11, m13);
4659
4660 jacobian.setElementAt(13, 2, g22);
4661 jacobian.setElementAt(13, 4, g32);
4662 jacobian.setElementAt(13, 10, m22);
4663 jacobian.setElementAt(13, 11, m23);
4664
4665 jacobian.setElementAt(14, 5, g32);
4666 jacobian.setElementAt(14, 11, m33);
4667
4668 jacobian.setElementAt(15, 0, g13);
4669 jacobian.setElementAt(15, 1, g23);
4670 jacobian.setElementAt(15, 3, g33);
4671 jacobian.setElementAt(15, 12, m11);
4672 jacobian.setElementAt(15, 13, m12);
4673 jacobian.setElementAt(15, 14, m13);
4674
4675 jacobian.setElementAt(16, 2, g23);
4676 jacobian.setElementAt(16, 4, g33);
4677 jacobian.setElementAt(16, 13, m22);
4678 jacobian.setElementAt(16, 14, m23);
4679
4680 jacobian.setElementAt(17, 5, g33);
4681 jacobian.setElementAt(17, 14, m33);
4682
4683 final var jacobianTrans = jacobian.transposeAndReturnNew();
4684 jacobian.multiply(estimatedCovariance);
4685 jacobian.multiply(jacobianTrans);
4686 estimatedCovariance = jacobian;
4687 }
4688
4689 /**
4690 * Internal method to perform general calibration when G-dependent cross
4691 * biases are being estimated.
4692 *
4693 * @throws AlgebraException if there are numerical errors.
4694 * @throws FittingException if no convergence to solution is found.
4695 * @throws com.irurueta.numerical.NotReadyException if fitter is not ready.
4696 * @throws InvalidSourceAndDestinationFrameTypeException never happens
4697 */
4698 private void calibrateGeneralAndGDependentCrossBiases() throws AlgebraException, FittingException,
4699 com.irurueta.numerical.NotReadyException, InvalidSourceAndDestinationFrameTypeException {
4700
4701 // The gyroscope model is
4702 // Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue + w
4703
4704 // Ideally a least squares solution tries to minimize noise component, so:
4705 // Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue
4706
4707 // For convergence purposes of the Levenberg-Marquardt algorithm, we
4708 // take common factor M = I + Mg
4709
4710 // and the gyroscope model can be better expressed as:
4711
4712 // Ωmeas = M*(Ωtrue + b + G * ftrue)
4713
4714 // where:
4715 // bg = M*b --> b = M^-1*bg
4716 // Gg = M*G --> G = M^-1*Gg
4717
4718 // We know that the norm of the true angular rate when the device is in a pixed
4719 // and unknown position and orientation is equal to the Earth rotation rate.
4720 // ||Ωtrue|| = 7.292115E-5 rad/s
4721
4722 // Hence
4723 // Ωmeas - M*b - M*G*ftrue = M*Ωtrue
4724 // M^-1 * (Ωmeas - M*b - M*G*ftrue) = Ωtrue
4725
4726 // ||Ωtrue||^2 = (M^-1 * (Ωmeas - M*b - M*G*ftrue))^T*(M^-1 * (Ωmeas - M*b - M*G*ftrue))
4727 // ||Ωtrue||^2 = (Ωmeas - M*b - M*G*ftrue)^T * (M^-1)^T * M^-1 * (Ωmeas - M*b - M*G*ftrue)
4728 // ||Ωtrue||^2 = (Ωmeas - M*b - M*G*ftrue)^T * ||M^-1||^2 * (Ωmeas - M*b - M*G*ftrue)
4729 // ||Ωtrue||^2 = ||Ωmeas - M*b - M*G*ftrue||^2 * ||M^-1||^2
4730
4731 // Where:
4732
4733 // b = [bx]
4734 // [by]
4735 // [bz]
4736
4737 // M = [m11 m12 m13]
4738 // [m21 m22 m23]
4739 // [m31 m32 m33]
4740
4741 // G = [g11 g12 g13]
4742 // [g21 g22 g23]
4743 // [g31 g32 g33]
4744
4745 // ftrue = [ftruex]
4746 // [ftruey]
4747 // [fturez]
4748
4749 final var gradientEstimator = new GradientEstimator(this::evaluateGeneralWitGDependentCrossBiases);
4750
4751 final var initialM = Matrix.identity(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
4752 initialM.add(getInitialMg());
4753
4754 final var invInitM = Utils.inverse(initialM);
4755 final var initGg = getInitialGg();
4756 final var initG = invInitM.multiplyAndReturnNew(initGg);
4757
4758 fitter.setFunctionEvaluator(new LevenbergMarquardtMultiDimensionFunctionEvaluator() {
4759 @Override
4760 public int getNumberOfDimensions() {
4761 // Input points are measured angular rate coordinates +
4762 // measured specific force coordinates
4763 return 2 * BodyKinematics.COMPONENTS;
4764 }
4765
4766 @Override
4767 public double[] createInitialParametersArray() {
4768 final var initial = new double[GENERAL_UNKNOWNS_AND_CROSS_BIASES];
4769
4770 // cross coupling errors M
4771 final var num = BodyKinematics.COMPONENTS * BodyKinematics.COMPONENTS;
4772 for (var i = 0; i < num; i++) {
4773 initial[i] = initialM.getElementAtIndex(i);
4774 }
4775
4776 // g-dependent cross biases G
4777 for (int i = 0, j = num; i < num; i++, j++) {
4778 initial[j] = initG.getElementAtIndex(i);
4779 }
4780
4781 return initial;
4782 }
4783
4784 @Override
4785 public double evaluate(
4786 final int i, final double[] point, final double[] params, final double[] derivatives)
4787 throws EvaluationException {
4788
4789 measAngularRateX = point[0];
4790 measAngularRateY = point[1];
4791 measAngularRateZ = point[2];
4792
4793 fmeasX = point[3];
4794 fmeasY = point[4];
4795 fmeasZ = point[5];
4796
4797 gradientEstimator.gradient(params, derivatives);
4798
4799 return evaluateGeneralWitGDependentCrossBiases(params);
4800 }
4801 });
4802
4803 setInputDataWithGDependentCrossBiases();
4804
4805 fitter.fit();
4806
4807 final var result = fitter.getA();
4808
4809 final var m11 = result[0];
4810 final var m21 = result[1];
4811 final var m31 = result[2];
4812
4813 final var m12 = result[3];
4814 final var m22 = result[4];
4815 final var m32 = result[5];
4816
4817 final var m13 = result[6];
4818 final var m23 = result[7];
4819 final var m33 = result[8];
4820
4821 final var g11 = result[9];
4822 final var g21 = result[10];
4823 final var g31 = result[11];
4824
4825 final var g12 = result[12];
4826 final var g22 = result[13];
4827 final var g32 = result[14];
4828
4829 final var g13 = result[15];
4830 final var g23 = result[16];
4831 final var g33 = result[17];
4832
4833 final var mm = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
4834 mm.setElementAtIndex(0, m11);
4835 mm.setElementAtIndex(1, m21);
4836 mm.setElementAtIndex(2, m31);
4837
4838 mm.setElementAtIndex(3, m12);
4839 mm.setElementAtIndex(4, m22);
4840 mm.setElementAtIndex(5, m32);
4841
4842 mm.setElementAtIndex(6, m13);
4843 mm.setElementAtIndex(7, m23);
4844 mm.setElementAtIndex(8, m33);
4845
4846 final var mg = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
4847 mg.setElementAtIndex(0, g11);
4848 mg.setElementAtIndex(1, g21);
4849 mg.setElementAtIndex(2, g31);
4850
4851 mg.setElementAtIndex(3, g12);
4852 mg.setElementAtIndex(4, g22);
4853 mg.setElementAtIndex(5, g32);
4854
4855 mg.setElementAtIndex(6, g13);
4856 mg.setElementAtIndex(7, g23);
4857 mg.setElementAtIndex(8, g33);
4858
4859 setResult(mm, mg);
4860
4861 // at this point covariance is expressed in terms of M and G, and must
4862 // be expressed in terms of Mg and Gg.
4863 // We know that:
4864 // Mg = M - I
4865 // Gg = M * G
4866
4867 // M = [m11 m12 m13]
4868 // [m21 m22 m23]
4869 // [m31 m32 m33]
4870
4871 // G = [g11 g12 g13]
4872 // [g21 g22 g23]
4873 // [g31 g32 g33]
4874
4875 // Mg = [sx mxy mxz] = [m11 - 1 m12 m13 ]
4876 // [myx sy myz] [m21 m22 - 1 m23 ]
4877 // [mzx mzy sz ] [m31 m32 m33 - 1 ]
4878
4879 // Gg = [gg11 gg12 gg13] = [m11 m12 m13][g11 g12 g13]
4880 // [gg21 gg22 gg23] [m21 m22 m23][g21 g22 g23]
4881 // [gg31 gg32 gg33] [m31 m32 m33][g31 g32 g33]
4882
4883 // Defining the linear application:
4884 // F(M, G) = F(m11, m21, m31, m12, m22, m32, m13, m23, m33, g11, g21, g31, g12, g22, g32, g13, g23, g33)
4885 // as:
4886 // [sx] = [m11 - 1]
4887 // [sy] [m22 - 1]
4888 // [sz] [m33 - 1]
4889 // [mxy] [m12]
4890 // [mxz] [m13]
4891 // [myx] [m21]
4892 // [myz] [m23]
4893 // [mzx] [m31]
4894 // [mzy] [m32]
4895 // [gg11] [m11 * g11 + m12 * g21 + m13 * g31]
4896 // [gg21] [m21 * g11 + m22 * g21 + m23 * g31]
4897 // [gg31] [m31 * g11 + m32 * g21 + m33 * g31]
4898 // [gg12] [m11 * g12 + m12 * g22 + m13 * g32]
4899 // [gg22] [m21 * g12 + m22 * g22 + n23 * g32]
4900 // [gg32] [m31 * g12 + m32 * g22 + m33 * g32]
4901 // [gg13] [m11 * g13 + m12 * g23 + m13 * g33]
4902 // [gg23] [m21 * g13 + m22 * g23 + m23 * g33]
4903 // [gg33] [m31 * g13 + m32 * g23 + m33 * g33]
4904
4905 // Then the Jacobian of F(M, G) is:
4906 // J = [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
4907 // [0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
4908 // [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 ]
4909 // [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
4910 // [0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 ]
4911 // [0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
4912 // [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 ]
4913 // [0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
4914 // [0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 ]
4915 // [g11 0 0 g21 0 0 g31 0 0 m11 m12 m13 0 0 0 0 0 0 ]
4916 // [0 g11 0 0 g21 0 0 g31 0 m21 m22 m23 0 0 0 0 0 0 ]
4917 // [0 0 g11 0 0 g21 0 0 g31 m31 m32 m33 0 0 0 0 0 0 ]
4918 // [g12 0 0 g22 0 0 g32 0 0 0 0 0 m11 m12 m13 0 0 0 ]
4919 // [0 g12 0 0 g22 0 0 g32 0 0 0 0 m21 m22 m23 0 0 0 ]
4920 // [0 0 g12 0 0 g22 0 0 g32 0 0 0 m31 m32 m33 0 0 0 ]
4921 // [g13 0 0 g23 0 0 g33 0 0 0 0 0 0 0 0 m11 m12 m13]
4922 // [0 g13 0 0 g23 0 0 g33 0 0 0 0 0 0 0 m21 m22 m23]
4923 // [0 0 g13 0 0 g23 0 0 g33 0 0 0 0 0 0 m31 m32 m33]
4924
4925 // We know that the propagated covariance is J * Cov * J', hence:
4926 final var jacobian = new Matrix(GENERAL_UNKNOWNS_AND_CROSS_BIASES, GENERAL_UNKNOWNS_AND_CROSS_BIASES);
4927
4928 jacobian.setElementAt(0, 0, 1.0);
4929 jacobian.setElementAt(1, 4, 1.0);
4930 jacobian.setElementAt(2, 8, 1.0);
4931
4932 jacobian.setElementAt(3, 3, 1.0);
4933 jacobian.setElementAt(4, 6, 1.0);
4934 jacobian.setElementAt(5, 1, 1.0);
4935
4936 jacobian.setElementAt(6, 7, 1.0);
4937 jacobian.setElementAt(7, 2, 1.0);
4938 jacobian.setElementAt(8, 5, 1.9);
4939
4940 jacobian.setElementAt(9, 0, g11);
4941 jacobian.setElementAt(9, 3, g21);
4942 jacobian.setElementAt(9, 6, g31);
4943 jacobian.setElementAt(9, 9, m11);
4944 jacobian.setElementAt(9, 10, m12);
4945 jacobian.setElementAt(9, 11, m13);
4946
4947 jacobian.setElementAt(10, 1, g11);
4948 jacobian.setElementAt(10, 4, g21);
4949 jacobian.setElementAt(10, 7, g31);
4950 jacobian.setElementAt(10, 9, m21);
4951 jacobian.setElementAt(10, 10, m22);
4952 jacobian.setElementAt(10, 11, m23);
4953
4954 jacobian.setElementAt(11, 2, g11);
4955 jacobian.setElementAt(11, 5, g21);
4956 jacobian.setElementAt(11, 8, g31);
4957 jacobian.setElementAt(11, 9, m31);
4958 jacobian.setElementAt(11, 10, m32);
4959 jacobian.setElementAt(11, 11, m33);
4960
4961 jacobian.setElementAt(12, 0, g12);
4962 jacobian.setElementAt(12, 3, g22);
4963 jacobian.setElementAt(12, 6, g32);
4964 jacobian.setElementAt(12, 12, m11);
4965 jacobian.setElementAt(12, 13, m12);
4966 jacobian.setElementAt(12, 14, m13);
4967
4968 jacobian.setElementAt(13, 1, g12);
4969 jacobian.setElementAt(13, 4, g22);
4970 jacobian.setElementAt(13, 7, g32);
4971 jacobian.setElementAt(13, 12, m21);
4972 jacobian.setElementAt(13, 13, m22);
4973 jacobian.setElementAt(13, 14, m23);
4974
4975 jacobian.setElementAt(14, 2, g12);
4976 jacobian.setElementAt(14, 5, g22);
4977 jacobian.setElementAt(14, 8, g32);
4978 jacobian.setElementAt(14, 12, m31);
4979 jacobian.setElementAt(14, 13, m32);
4980 jacobian.setElementAt(14, 14, m33);
4981
4982 jacobian.setElementAt(15, 0, g13);
4983 jacobian.setElementAt(15, 3, g23);
4984 jacobian.setElementAt(15, 6, g33);
4985 jacobian.setElementAt(15, 15, m11);
4986 jacobian.setElementAt(15, 16, m12);
4987 jacobian.setElementAt(15, 17, m13);
4988
4989 jacobian.setElementAt(16, 1, g13);
4990 jacobian.setElementAt(16, 4, g23);
4991 jacobian.setElementAt(16, 7, g33);
4992 jacobian.setElementAt(16, 15, m21);
4993 jacobian.setElementAt(16, 16, m22);
4994 jacobian.setElementAt(16, 17, m23);
4995
4996 jacobian.setElementAt(17, 2, g13);
4997 jacobian.setElementAt(17, 5, g23);
4998 jacobian.setElementAt(17, 8, g33);
4999 jacobian.setElementAt(17, 15, m31);
5000 jacobian.setElementAt(17, 16, m32);
5001 jacobian.setElementAt(17, 17, m33);
5002
5003 final var jacobianTrans = jacobian.transposeAndReturnNew();
5004 jacobian.multiply(estimatedCovariance);
5005 jacobian.multiply(jacobianTrans);
5006 estimatedCovariance = jacobian;
5007 }
5008
5009 /**
5010 * Internal method to perform calibration when common z-axis is assumed for both
5011 * the accelerometer and gyroscope and G-dependent cross biases are ignored.
5012 *
5013 * @throws AlgebraException if there are numerical errors.
5014 * @throws FittingException if no convergence to solution is found.
5015 * @throws com.irurueta.numerical.NotReadyException if fitter is not ready.
5016 * @throws InvalidSourceAndDestinationFrameTypeException never happens.
5017 */
5018 private void calibrateCommonAxis() throws AlgebraException, FittingException,
5019 com.irurueta.numerical.NotReadyException, InvalidSourceAndDestinationFrameTypeException {
5020
5021 // The gyroscope model is
5022 // Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue + w
5023
5024 // Ideally a least squares solution tries to minimize noise component, so:
5025 // Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue
5026
5027 // Since G-dependent cross biases are ignored, we can assume that Gg = 0
5028
5029 // Hence:
5030 // Ωmeas = bg + (I + Mg) * Ωtrue
5031
5032 // For convergence purposes of the Levenberg-Marquardt algorithm, the
5033 // gyroscope model can be better expressed as:
5034 // Ωmeas = T*K*(Ωtrue + b)
5035 // Ωmeas = M*(Ωtrue + b)
5036 // Ωmeas = M*Ωtrue + M*b
5037
5038 // where:
5039 // M = I + Mg
5040 // bg = M*b = (I + Mg)*b --> b = M^-1*bg
5041
5042 // We know that the norm of the true angular rate when the device is in a pixed
5043 // and unknown position and orientation is equal to the Earth rotation rate.
5044 // ||Ωtrue|| = 7.292115E-5 rad/s
5045
5046 // Hence
5047 // Ωmeas - M*b = M*Ωtrue
5048
5049 // M^-1 * (Ωmeas - M*b) = Ωtrue
5050
5051 // ||Ωtrue||^2 = (M^-1 * (Ωmeas - M*b))^T*(M^-1 * (Ωmeas - M*b))
5052 // ||Ωtrue||^2 = (Ωmeas - M*b)^T * (M^-1)^T * M^-1 * (Ωmeas - M*b)
5053 // ||Ωtrue||^2 = (Ωmeas - M*b)^T * ||M^-1||^2 * (Ωmeas - M*b)
5054 // ||Ωtrue||^2 = ||Ωmeas - M*b||^2 * ||M^-1||^2
5055
5056 // Where:
5057
5058 // b = [bx]
5059 // [by]
5060 // [bz]
5061
5062 // M = [m11 m12 m13]
5063 // [0 m22 m23]
5064 // [0 0 m33]
5065
5066 final var gradientEstimator = new GradientEstimator(this::evaluateCommonAxis);
5067
5068 final var initialM = Matrix.identity(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
5069 initialM.add(getInitialMg());
5070
5071 // Force initial M to be upper diagonal
5072 initialM.setElementAt(1, 0, 0.0);
5073 initialM.setElementAt(2, 0, 0.0);
5074 initialM.setElementAt(2, 1, 0.0);
5075
5076 fitter.setFunctionEvaluator(new LevenbergMarquardtMultiDimensionFunctionEvaluator() {
5077 @Override
5078 public int getNumberOfDimensions() {
5079 // Input points are measured angular rate coordinates
5080 return BodyKinematics.COMPONENTS;
5081 }
5082
5083 @Override
5084 public double[] createInitialParametersArray() {
5085 final var initial = new double[COMMON_Z_AXIS_UNKNOWNS];
5086
5087 // upper diagonal cross coupling errors M
5088 var k = 0;
5089 for (var j = 0; j < BodyKinematics.COMPONENTS; j++) {
5090 for (var i = 0; i < BodyKinematics.COMPONENTS; i++) {
5091 if (i <= j) {
5092 initial[k] = initialM.getElementAt(i, j);
5093 k++;
5094 }
5095 }
5096 }
5097
5098 return initial;
5099 }
5100
5101 @Override
5102 public double evaluate(
5103 final int i, final double[] point, final double[] params, final double[] derivatives)
5104 throws EvaluationException {
5105
5106 measAngularRateX = point[0];
5107 measAngularRateY = point[1];
5108 measAngularRateZ = point[2];
5109
5110 gradientEstimator.gradient(params, derivatives);
5111
5112 return evaluateCommonAxis(params);
5113 }
5114 });
5115
5116 setInputData();
5117
5118 fitter.fit();
5119
5120 final var result = fitter.getA();
5121
5122 final var m11 = result[0];
5123
5124 final var m12 = result[1];
5125 final var m22 = result[2];
5126
5127 final var m13 = result[3];
5128 final var m23 = result[4];
5129 final var m33 = result[5];
5130
5131 final var mm = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
5132 mm.setElementAtIndex(0, m11);
5133 mm.setElementAtIndex(1, 0.0);
5134 mm.setElementAtIndex(2, 0.0);
5135
5136 mm.setElementAtIndex(3, m12);
5137 mm.setElementAtIndex(4, m22);
5138 mm.setElementAtIndex(5, 0.0);
5139
5140 mm.setElementAtIndex(6, m13);
5141 mm.setElementAtIndex(7, m23);
5142 mm.setElementAtIndex(8, m33);
5143
5144 setResult(mm);
5145
5146 // at this point covariance is expressed in terms of M, and must
5147 // be expressed in terms of Mg.
5148 // We know that:
5149 // Mg = M - I
5150 // Gg = M * G
5151
5152 // M = [m11 m12 m13]
5153 // [0 m22 m23]
5154 // [0 0 m33]
5155
5156 // G = [g11 g12 g13] = 0
5157 // [g21 g22 g23]
5158 // [g31 g32 g33]
5159
5160 // Mg = [sx mxy mxz] = [m11 - 1 m12 m13 ]
5161 // [myx sy myz] [0 m22 - 1 m23 ]
5162 // [mzx mzy sz ] [0 0 m33 - 1 ]
5163
5164 // Gg = [gg11 gg12 gg13] = [m11 m12 m13][g11 g12 g13]
5165 // [gg21 gg22 gg23] [0 m22 m23][g21 g22 g23]
5166 // [gg31 gg32 gg33] [0 0 m33][g31 g32 g33]
5167
5168 // Defining the linear application:
5169 // F(M) = F(m11, m12, m22, m32=0, m13, m23, m33)
5170 // as:
5171 // [sx] = [m11 - 1]
5172 // [sy] [m22 - 1]
5173 // [sz] [m33 - 1]
5174 // [mxy] [m12]
5175 // [mxz] [m13]
5176 // [myx] [0]
5177 // [myz] [m23]
5178 // [mzx] [0]
5179 // [mzy] [0]
5180 // [gg11] [0]
5181 // [gg21] [0]
5182 // [gg31] [0]
5183 // [gg12] [0]
5184 // [gg22] [0]
5185 // [gg32] [0]
5186 // [gg13] [0]
5187 // [gg23] [0]
5188 // [gg33] [0]
5189
5190 // Then the Jacobian of F(M) is:
5191 // J = [1 0 0 0 0 0]
5192 // [0 0 1 0 0 0]
5193 // [0 0 0 0 0 1]
5194 // [0 1 0 0 0 0]
5195 // [0 0 0 1 0 0]
5196 // [0 0 0 0 0 0]
5197 // [0 0 0 0 1 0]
5198 // [0 0 0 0 0 0]
5199 // [0 0 0 0 0 0]
5200 // [0 0 0 0 0 0]
5201 // [0 0 0 0 0 0]
5202 // [0 0 0 0 0 0]
5203 // [0 0 0 0 0 0]
5204 // [0 0 0 0 0 0]
5205 // [0 0 0 0 0 0]
5206 // [0 0 0 0 0 0]
5207 // [0 0 0 0 0 0]
5208 // [0 0 0 0 0 0]
5209
5210 // We know that the propagated covariance is J * Cov * J', hence:
5211 final var jacobian = new Matrix(GENERAL_UNKNOWNS_AND_CROSS_BIASES, COMMON_Z_AXIS_UNKNOWNS);
5212
5213 jacobian.setElementAt(0, 0, 1.0);
5214 jacobian.setElementAt(1, 2, 1.0);
5215 jacobian.setElementAt(2, 5, 1.0);
5216
5217 jacobian.setElementAt(3, 1, 1.0);
5218 jacobian.setElementAt(4, 3, 1.0);
5219
5220 jacobian.setElementAt(6, 4, 1.0);
5221
5222 final var jacobianTrans = jacobian.transposeAndReturnNew();
5223 jacobian.multiply(estimatedCovariance);
5224 jacobian.multiply(jacobianTrans);
5225 estimatedCovariance = jacobian;
5226 }
5227
5228 /**
5229 * Internal method to perform general calibration when G-dependent cross biases
5230 * are ignored.
5231 *
5232 * @throws AlgebraException if there are numerical errors.
5233 * @throws FittingException if no convergence to solution is found.
5234 * @throws com.irurueta.numerical.NotReadyException if fitter is not ready.
5235 * @throws InvalidSourceAndDestinationFrameTypeException never happens.
5236 */
5237 private void calibrateGeneral() throws AlgebraException, FittingException, com.irurueta.numerical.NotReadyException,
5238 InvalidSourceAndDestinationFrameTypeException {
5239
5240 // The gyroscope model is
5241 // Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue + w
5242
5243 // Ideally a least squares solution tries to minimize noise component, so:
5244 // Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue
5245
5246 // Since G-dependent cross giases are ignored, we can assume that Gg = 0
5247
5248 // Hence:
5249 // Ωmeas = bg + (I + Mg) * Ωtrue
5250
5251 // For convergence purposes of the Levenberg-Marquardt algorithm, the
5252 // gyroscope model can be better expressed as:
5253 // Ωmeas = T*K*(Ωtrue + b)
5254 // Ωmeas = M*(Ωtrue + b)
5255 // Ωmeas = M*Ωtrue + M*b
5256
5257 // where:
5258 // M = I + Mg
5259 // bg = M*b = (I + Mg)*b --> b = M^-1*bg
5260
5261 // We know that the norm of the true angular rate when the device is in a pixed
5262 // and unknown position and orientation is equal to the Earth rotation rate.
5263 // ||Ωtrue|| = 7.292115E-5 rad/s
5264
5265 // Hence
5266 // Ωmeas - M*b = M*Ωtrue
5267
5268 // M^-1 * (Ωmeas - M*b) = Ωtrue
5269
5270 // ||Ωtrue||^2 = (M^-1 * (Ωmeas - M*b))^T*(M^-1 * (Ωmeas - M*b))
5271 // ||Ωtrue||^2 = (Ωmeas - M*b)^T * (M^-1)^T * M^-1 * (Ωmeas - M*b)
5272 // ||Ωtrue||^2 = (Ωmeas - M*b)^T * ||M^-1||^2 * (Ωmeas - M*b)
5273 // ||Ωtrue||^2 = ||Ωmeas - M*b||^2 * ||M^-1||^2
5274
5275 // Where:
5276
5277 // b = [bx]
5278 // [by]
5279 // [bz]
5280
5281 // M = [m11 m12 m13]
5282 // [m21 m22 m23]
5283 // [m31 m32 m33]
5284
5285 final var gradientEstimator = new GradientEstimator(this::evaluateGeneral);
5286
5287 final var initialM = Matrix.identity(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
5288 initialM.add(getInitialMg());
5289
5290 fitter.setFunctionEvaluator(new LevenbergMarquardtMultiDimensionFunctionEvaluator() {
5291 @Override
5292 public int getNumberOfDimensions() {
5293 // Input points are measured angular rate coordinates
5294 return BodyKinematics.COMPONENTS;
5295 }
5296
5297 @Override
5298 public double[] createInitialParametersArray() {
5299 return initialM.toArray();
5300 }
5301
5302 @Override
5303 public double evaluate(
5304 final int i, final double[] point, final double[] params, final double[] derivatives)
5305 throws EvaluationException {
5306
5307 measAngularRateX = point[0];
5308 measAngularRateY = point[1];
5309 measAngularRateZ = point[2];
5310
5311 gradientEstimator.gradient(params, derivatives);
5312
5313 return evaluateGeneral(params);
5314 }
5315 });
5316
5317 setInputData();
5318
5319 fitter.fit();
5320
5321 final var result = fitter.getA();
5322
5323 final var m11 = result[0];
5324 final var m21 = result[1];
5325 final var m31 = result[2];
5326
5327 final var m12 = result[3];
5328 final var m22 = result[4];
5329 final var m32 = result[5];
5330
5331 final var m13 = result[6];
5332 final var m23 = result[7];
5333 final var m33 = result[8];
5334
5335 final var mm = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
5336 mm.setElementAtIndex(0, m11);
5337 mm.setElementAtIndex(1, m21);
5338 mm.setElementAtIndex(2, m31);
5339
5340 mm.setElementAtIndex(3, m12);
5341 mm.setElementAtIndex(4, m22);
5342 mm.setElementAtIndex(5, m32);
5343
5344 mm.setElementAtIndex(6, m13);
5345 mm.setElementAtIndex(7, m23);
5346 mm.setElementAtIndex(8, m33);
5347
5348 setResult(mm);
5349
5350 // at this point covariance is expressed in terms of M, and must
5351 // be expressed in terms of Mg.
5352 // We know that:
5353 // Mg = M - I
5354 // Gg = M * G
5355
5356 // M = [m11 m12 m13]
5357 // [m21 m22 m23]
5358 // [m31 m32 m33]
5359
5360 // G = [g11 g12 g13] = 0
5361 // [g21 g22 g23]
5362 // [g31 g32 g33]
5363
5364 // Mg = [sx mxy mxz] = [m11 - 1 m12 m13 ]
5365 // [myx sy myz] [m21 m22 - 1 m23 ]
5366 // [mzx mzy sz ] [m31 m32 m33 - 1 ]
5367
5368 // Gg = [gg11 gg12 gg13] = [m11 m12 m13][g11 g12 g13]
5369 // [gg21 gg22 gg23] [m21 m22 m23][g21 g22 g23]
5370 // [gg31 gg32 gg33] [m31 m32 m33][g31 g32 g33]
5371
5372 // Defining the linear application:
5373 // F(M) = F(m11, m21, m31, m12, m22, m32, m13, m23, m33)
5374 // as:
5375 // [sx] = [m11 - 1]
5376 // [sy] [m22 - 1]
5377 // [sz] [m33 - 1]
5378 // [mxy] [m12]
5379 // [mxz] [m13]
5380 // [myx] [m21]
5381 // [myz] [m23]
5382 // [mzx] [m31]
5383 // [mzy] [m32]
5384 // [gg11] [0]
5385 // [gg21] [0]
5386 // [gg31] [0]
5387 // [gg12] [0]
5388 // [gg22] [0]
5389 // [gg32] [0]
5390 // [gg13] [0]
5391 // [gg23] [0]
5392 // [gg33] [0]
5393
5394 // Then the Jacobian of F(M) is:
5395 // J = [1 0 0 0 0 0 0 0 0]
5396 // [0 0 0 0 1 0 0 0 0]
5397 // [0 0 0 0 0 0 0 0 1]
5398 // [0 0 0 1 0 0 0 0 0]
5399 // [0 0 0 0 0 0 1 0 0]
5400 // [0 1 0 0 0 0 0 0 0]
5401 // [0 0 0 0 0 0 0 1 0]
5402 // [0 0 1 0 0 0 0 0 0]
5403 // [0 0 0 0 0 1 0 0 0]
5404 // [0 0 0 0 0 0 0 0 0]
5405 // [0 0 0 0 0 0 0 0 0]
5406 // [0 0 0 0 0 0 0 0 0]
5407 // [0 0 0 0 0 0 0 0 0]
5408 // [0 0 0 0 0 0 0 0 0]
5409 // [0 0 0 0 0 0 0 0 0]
5410 // [0 0 0 0 0 0 0 0 0]
5411 // [0 0 0 0 0 0 0 0 0]
5412 // [0 0 0 0 0 0 0 0 0]
5413
5414 // We know that the propagated covariance is J * Cov * J', hence:
5415 final var jacobian = new Matrix(GENERAL_UNKNOWNS_AND_CROSS_BIASES, GENERAL_UNKNOWNS);
5416
5417 jacobian.setElementAt(0, 0, 1.0);
5418 jacobian.setElementAt(1, 4, 1.0);
5419 jacobian.setElementAt(2, 8, 1.0);
5420
5421 jacobian.setElementAt(3, 3, 1.0);
5422 jacobian.setElementAt(4, 6, 1.0);
5423 jacobian.setElementAt(5, 1, 1.0);
5424
5425 jacobian.setElementAt(6, 7, 1.0);
5426 jacobian.setElementAt(7, 2, 1.0);
5427 jacobian.setElementAt(8, 5, 1.9);
5428
5429 final var jacobianTrans = jacobian.transposeAndReturnNew();
5430 jacobian.multiply(estimatedCovariance);
5431 jacobian.multiply(jacobianTrans);
5432 estimatedCovariance = jacobian;
5433 }
5434
5435 /**
5436 * Sets input data into Levenberg-Marquardt fitter when G-dependent cross biases
5437 * are taken into account.
5438 *
5439 * @throws AlgebraException if provided accelerometer cross coupling
5440 * errors are not valid.
5441 * @throws InvalidSourceAndDestinationFrameTypeException never happens
5442 */
5443 private void setInputDataWithGDependentCrossBiases()
5444 throws AlgebraException, InvalidSourceAndDestinationFrameTypeException {
5445 // compute reference frame at current position
5446 final var nedPosition = getNedPosition();
5447 final var nedC = new CoordinateTransformation(FrameType.BODY_FRAME, FrameType.LOCAL_NAVIGATION_FRAME);
5448 final var nedFrame = new NEDFrame(nedPosition, nedC);
5449 final var ecefFrame = NEDtoECEFFrameConverter.convertNEDtoECEFAndReturnNew(nedFrame);
5450 final var refKinematics = ECEFKinematicsEstimator.estimateKinematicsAndReturnNew(timeInterval, ecefFrame,
5451 ecefFrame);
5452
5453 final var refAngularRateX = refKinematics.getAngularRateX();
5454 final var refAngularRateY = refKinematics.getAngularRateY();
5455 final var refAngularRateZ = refKinematics.getAngularRateZ();
5456
5457 final var w2 = turntableRotationRate * turntableRotationRate;
5458
5459 final var numMeasurements = measurements.size();
5460 final var x = new Matrix(numMeasurements, 2 * BodyKinematics.COMPONENTS);
5461 final var y = new double[numMeasurements];
5462 final var angularRateStandardDeviations = new double[numMeasurements];
5463 var i = 0;
5464 for (final var measurement : measurements) {
5465 final var measuredKinematics = measurement.getKinematics();
5466
5467 final var angularRateX = measuredKinematics.getAngularRateX();
5468 final var angularRateY = measuredKinematics.getAngularRateY();
5469 final var angularRateZ = measuredKinematics.getAngularRateZ();
5470
5471 final var fX = measuredKinematics.getFx();
5472 final var fY = measuredKinematics.getFy();
5473 final var fZ = measuredKinematics.getFz();
5474
5475 x.setElementAt(i, 0, angularRateX - refAngularRateX);
5476 x.setElementAt(i, 1, angularRateY - refAngularRateY);
5477 x.setElementAt(i, 2, angularRateZ - refAngularRateZ);
5478
5479 x.setElementAt(i, 3, fX);
5480 x.setElementAt(i, 4, fY);
5481 x.setElementAt(i, 5, fZ);
5482
5483 y[i] = w2;
5484
5485 angularRateStandardDeviations[i] = measurement.getAngularRateStandardDeviation();
5486
5487 i++;
5488 }
5489
5490 fitter.setInputData(x, y, angularRateStandardDeviations);
5491
5492 ba = getAccelerometerBiasAsMatrix();
5493 ma = getAccelerometerMa();
5494 accelerationFixer.setBias(ba);
5495 accelerationFixer.setCrossCouplingErrors(ma);
5496 }
5497
5498 /**
5499 * Sets input data into Levenberg-Marquardt fitter when G-dependent cross biases
5500 * are ignored.
5501 *
5502 * @throws AlgebraException if provided accelerometer cross coupling
5503 * errors are not valid.
5504 * @throws InvalidSourceAndDestinationFrameTypeException never happens.
5505 */
5506 private void setInputData() throws AlgebraException, InvalidSourceAndDestinationFrameTypeException {
5507
5508 // compute reference frame at current position
5509 final var nedPosition = getNedPosition();
5510 final var nedC = new CoordinateTransformation(FrameType.BODY_FRAME, FrameType.LOCAL_NAVIGATION_FRAME);
5511 final var nedFrame = new NEDFrame(nedPosition, nedC);
5512 final var ecefFrame = NEDtoECEFFrameConverter.convertNEDtoECEFAndReturnNew(nedFrame);
5513 final var refKinematics = ECEFKinematicsEstimator.estimateKinematicsAndReturnNew(timeInterval, ecefFrame,
5514 ecefFrame);
5515
5516 final var refAngularRateX = refKinematics.getAngularRateX();
5517 final var refAngularRateY = refKinematics.getAngularRateY();
5518 final var refAngularRateZ = refKinematics.getAngularRateZ();
5519
5520 final var w2 = turntableRotationRate * turntableRotationRate;
5521
5522 final var numMeasurements = measurements.size();
5523 final var x = new Matrix(numMeasurements, BodyKinematics.COMPONENTS);
5524 final var y = new double[numMeasurements];
5525 final var angularRateStandardDeviations = new double[numMeasurements];
5526 var i = 0;
5527 for (final var measurement : measurements) {
5528 final var measuredKinematics = measurement.getKinematics();
5529
5530 final var angularRateX = measuredKinematics.getAngularRateX();
5531 final var angularRateY = measuredKinematics.getAngularRateY();
5532 final var angularRateZ = measuredKinematics.getAngularRateZ();
5533
5534 x.setElementAt(i, 0, angularRateX - refAngularRateX);
5535 x.setElementAt(i, 1, angularRateY - refAngularRateY);
5536 x.setElementAt(i, 2, angularRateZ - refAngularRateZ);
5537
5538 y[i] = w2;
5539
5540 angularRateStandardDeviations[i] = measurement.getAngularRateStandardDeviation();
5541
5542 i++;
5543 }
5544
5545 fitter.setInputData(x, y, angularRateStandardDeviations);
5546
5547 ba = getAccelerometerBiasAsMatrix();
5548 ma = getAccelerometerMa();
5549 accelerationFixer.setBias(ba);
5550 accelerationFixer.setCrossCouplingErrors(ma);
5551 }
5552
5553 /**
5554 * Converts provided NED position expressed in terms of latitude, longitude and height respect
5555 * mean Earth surface, to position expressed in ECEF coordinates.
5556 *
5557 * @param position NED position to be converted.
5558 * @return converted position expressed in ECEF coordinates.
5559 */
5560 private static ECEFPosition convertPosition(final NEDPosition position) {
5561 final var velocity = new ECEFVelocity();
5562 final var result = new ECEFPosition();
5563 NEDtoECEFPositionVelocityConverter.convertNEDtoECEF(
5564 position.getLatitude(), position.getLongitude(), position.getHeight(), 0.0, 0.0, 0.0,
5565 result, velocity);
5566 return result;
5567 }
5568
5569 /**
5570 * Converts acceleration instance to meters per squared second.
5571 *
5572 * @param acceleration acceleration instance to be converted.
5573 * @return converted value.
5574 */
5575 private static double convertAcceleration(final Acceleration acceleration) {
5576 return AccelerationConverter.convert(acceleration.getValue().doubleValue(), acceleration.getUnit(),
5577 AccelerationUnit.METERS_PER_SQUARED_SECOND);
5578 }
5579
5580 /**
5581 * Converts angular speed instance to radians per second (rad/s).
5582 *
5583 * @param value angular speed value.
5584 * @param unit unit of angular speed value.
5585 * @return converted value.
5586 */
5587 private static double convertAngularSpeed(final double value, final AngularSpeedUnit unit) {
5588 return AngularSpeedConverter.convert(value, unit, AngularSpeedUnit.RADIANS_PER_SECOND);
5589 }
5590
5591 /**
5592 * Converts angular speed instance to radians per second (rad/s).
5593 *
5594 * @param angularSpeed angular speed instance to be converted.
5595 * @return converted value.
5596 */
5597 private static double convertAngularSpeed(final AngularSpeed angularSpeed) {
5598 return convertAngularSpeed(angularSpeed.getValue().doubleValue(), angularSpeed.getUnit());
5599 }
5600
5601 /**
5602 * Converts time instance to seconds.
5603 *
5604 * @param time time instance to be converted.
5605 * @return converted value.
5606 */
5607 private static double convertTime(final Time time) {
5608 return TimeConverter.convert(time.getValue().doubleValue(), time.getUnit(), TimeUnit.SECOND);
5609 }
5610
5611 /**
5612 * Makes proper conversion of internal cross-coupling and g-dependent
5613 * cross bias matrices.
5614 *
5615 * @param m internal scaling and cross-coupling matrix.
5616 * @param g internal g-dependent cross bias matrix.
5617 * @throws AlgebraException if a numerical instability occurs.
5618 */
5619 private void setResult(final Matrix m, final Matrix g) throws AlgebraException {
5620 setResult(m);
5621
5622 // Gg = M*G
5623 m.multiply(g, estimatedGg);
5624 }
5625
5626 /**
5627 * Makes proper conversion of internal cross-coupling matrix.
5628 *
5629 * @param m internal scaling and cross-coupling matrix.
5630 * @throws AlgebraException if a numerical instability occurs.
5631 */
5632 private void setResult(final Matrix m) throws AlgebraException {
5633 // Because:
5634 // M = I + Mg
5635
5636 // Then:
5637 // Mg = M - I
5638
5639 if (estimatedMg == null) {
5640 estimatedMg = m;
5641 } else {
5642 estimatedMg.copyFrom(m);
5643 }
5644
5645 for (var i = 0; i < BodyKinematics.COMPONENTS; i++) {
5646 estimatedMg.setElementAt(i, i, estimatedMg.getElementAt(i, i) - 1.0);
5647 }
5648
5649 if (estimatedGg == null) {
5650 estimatedGg = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
5651 } else {
5652 estimatedGg.initialize(0.0);
5653 }
5654
5655 estimatedCovariance = fitter.getCovar();
5656 estimatedChiSq = fitter.getChisq();
5657 estimatedChiSqDegreesOfFreedom = fitter.getChisqDegreesOfFreedom();
5658 estimatedReducedChiSq = fitter.getReducedChisq();
5659 estimatedMse = fitter.getMse();
5660 try {
5661 estimatedP = fitter.getP();
5662 estimatedQ = fitter.getQ();
5663 } catch (final MaxIterationsExceededException ignore) {
5664 // if numerical instabilities arise, we assume worst case (no fit at all)
5665 // probability of finding a smaller chi square value is 1.0
5666 // quality of fit is 0.0
5667 estimatedP = 1.0;
5668 estimatedQ = 0.0;
5669 }
5670 }
5671
5672 /**
5673 * Computes estimated true angular rate squared norm using current measured
5674 * angular rate and specific force along with provided parameters for the
5675 * general case when G-dependent cross biases are taken into account.
5676 * This method is internally executed during gradient estimation and
5677 * Levenberg-Marquardt fitting needed for calibration computation.
5678 *
5679 * @param params array containing parameters for the general purpose case
5680 * when G-dependent cross biases are taken into account. Must
5681 * have length 18.
5682 * @return estimated true angular rate squared norm.
5683 * @throws EvaluationException if there are numerical instabilities.
5684 */
5685 private double evaluateGeneralWitGDependentCrossBiases(final double[] params) throws EvaluationException {
5686 final var m11 = params[0];
5687 final var m21 = params[1];
5688 final var m31 = params[2];
5689
5690 final var m12 = params[3];
5691 final var m22 = params[4];
5692 final var m32 = params[5];
5693
5694 final var m13 = params[6];
5695 final var m23 = params[7];
5696 final var m33 = params[8];
5697
5698 final var g11 = params[9];
5699 final var g21 = params[10];
5700 final var g31 = params[11];
5701
5702 final var g12 = params[12];
5703 final var g22 = params[13];
5704 final var g32 = params[14];
5705
5706 final var g13 = params[15];
5707 final var g23 = params[16];
5708 final var g33 = params[17];
5709
5710 return evaluate(m11, m21, m31, m12, m22, m32, m13, m23, m33, g11, g21, g31, g12, g22, g32, g13, g23, g33);
5711 }
5712
5713 /**
5714 * Computes estimated true angular rate squared norm using current measured
5715 * angular rate and specific force along with provided parameters when
5716 * common z-axis is assumed and G-dependent cross biases are taken into
5717 * account.
5718 * This method is internally executed during gradient estimation and
5719 * Levenberg-Marquardt fitting needed for calibration computation.
5720 *
5721 * @param params array containing parameters for the general purpose case
5722 * when G-dependent cross biases are taken into account. Must
5723 * have length 15.
5724 * @return estimated true angular rate squared norm.
5725 * @throws EvaluationException if there are numerical instabilities.
5726 */
5727 private double evaluateCommonAxisWitGDependentCrossBiases(final double[] params) throws EvaluationException {
5728 final var m11 = params[0];
5729
5730 final var m12 = params[1];
5731 final var m22 = params[2];
5732
5733 final var m13 = params[3];
5734 final var m23 = params[4];
5735 final var m33 = params[5];
5736
5737 final var g11 = params[6];
5738 final var g21 = params[7];
5739 final var g31 = params[8];
5740
5741 final var g12 = params[9];
5742 final var g22 = params[10];
5743 final var g32 = params[11];
5744
5745 final var g13 = params[12];
5746 final var g23 = params[13];
5747 final var g33 = params[14];
5748
5749 return evaluate(m11, 0.0, 0.0, m12, m22, 0.0, m13, m23, m33,
5750 g11, g21, g31, g12, g22, g32, g13, g23, g33);
5751 }
5752
5753 /**
5754 * Computes estimated true angular rate squared norm using current measured
5755 * angular rate and provided parameters for the general case when G-dependent
5756 * cross biases are ignored.
5757 * This method is internally executed during gradient estimation and
5758 * Levenberg-Marquardt fitting needed for calibration computation.
5759 *
5760 * @param params array containing current parameters for the general purpose case
5761 * when G-dependent cross biases are ignored. Must have length 9.
5762 * @return estimated true angular rate squared norm.
5763 * @throws EvaluationException if there are numerical instabilities.
5764 */
5765 private double evaluateGeneral(final double[] params) throws EvaluationException {
5766 final var m11 = params[0];
5767 final var m21 = params[1];
5768 final var m31 = params[2];
5769
5770 final var m12 = params[3];
5771 final var m22 = params[4];
5772 final var m32 = params[5];
5773
5774 final var m13 = params[6];
5775 final var m23 = params[7];
5776 final var m33 = params[8];
5777
5778 return evaluate(m11, m21, m31, m12, m22, m32, m13, m23, m33);
5779 }
5780
5781 /**
5782 * Computes estimated true angular rate squared norm using current measured
5783 * angular rate and provided parameters when common z-axis is assumed and
5784 * G-dependent cross biases are ignored.
5785 * This method is internally executed during gradient estimation and
5786 * Levenberg-Marquardt fitting needed for calibration computation.
5787 *
5788 * @param params array containing current parameters for the common z-axis case
5789 * when G-dependent cross biases are ignored. Must have length 6.
5790 * @return estimated true angular rate squared norm.
5791 * @throws EvaluationException if there are numerical instabilities.
5792 */
5793 private double evaluateCommonAxis(final double[] params) throws EvaluationException {
5794 final var m11 = params[0];
5795
5796 final var m12 = params[1];
5797 final var m22 = params[2];
5798
5799 final var m13 = params[3];
5800 final var m23 = params[4];
5801 final var m33 = params[5];
5802
5803 return evaluate(m11, 0.0, 0.0, m12, m22, 0.0, m13, m23, m33);
5804 }
5805
5806 /**
5807 * Computes estimated true angular rate squared norm using current measured
5808 * angular rate and provided parameters.
5809 * This method is internally executed during gradient estimation and
5810 * Levenberg-Marquardt fitting needed for calibration computation.
5811 *
5812 * @param m11 element 1,1 of cross-coupling error matrix.
5813 * @param m21 element 2,1 of cross-coupling error matrix.
5814 * @param m31 element 3,1 of cross-coupling error matrix.
5815 * @param m12 element 1,2 of cross-coupling error matrix.
5816 * @param m22 element 2,2 of cross-coupling error matrix.
5817 * @param m32 element 3,2 of cross-coupling error matrix.
5818 * @param m13 element 1,3 of cross-coupling error matrix.
5819 * @param m23 element 2,3 of cross-coupling error matrix.
5820 * @param m33 element 3,3 of cross-coupling error matrix.
5821 * @param g11 element 1,1 of g-dependent cross bias matrix.
5822 * @param g21 element 2,1 of g-dependent cross bias matrix.
5823 * @param g31 element 3,1 of g-dependent cross bias matrix.
5824 * @param g12 element 1,2 of g-dependent cross bias matrix.
5825 * @param g22 element 2,2 of g-dependent cross bias matrix.
5826 * @param g32 element 3,2 of g-dependent cross bias matrix.
5827 * @param g13 element 1,3 of g-dependent cross bias matrix.
5828 * @param g23 element 2,3 of g-dependent cross bias matrix.
5829 * @param g33 element 3,3 of g-dependent cross bias matrix.
5830 * @return estimated true angular rate squared norm.
5831 * @throws EvaluationException if there are numerical instabilities.
5832 */
5833 private double evaluate(final double m11, final double m21, final double m31,
5834 final double m12, final double m22, final double m32,
5835 final double m13, final double m23, final double m33,
5836 final double g11, final double g21, final double g31,
5837 final double g12, final double g22, final double g32,
5838 final double g13, final double g23, final double g33) throws EvaluationException {
5839
5840 // Ωmeas = bg + (I + Mg) * Ωtrue + Gg * ftrue
5841 // Ωmeas = M*(Ωtrue + b + G * ftrue)
5842
5843 // M = I + Mg
5844 // bg = M*b --> b = M^-1*bg
5845 // Gg = M*G --> G = M^-1*Gg
5846
5847 // Ωtrue = M^-1 * Ωmeas - b - G*ftrue
5848
5849 try {
5850 if (measAngularRate == null) {
5851 measAngularRate = new Matrix(BodyKinematics.COMPONENTS, 1);
5852 }
5853 if (fmeas == null) {
5854 fmeas = new Matrix(BodyKinematics.COMPONENTS, 1);
5855 }
5856 if (m == null) {
5857 m = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
5858 }
5859 if (invM == null) {
5860 invM = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
5861 }
5862 if (b == null) {
5863 b = new Matrix(BodyKinematics.COMPONENTS, 1);
5864 }
5865 if (g == null) {
5866 g = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
5867 }
5868 if (trueAngularRate == null) {
5869 trueAngularRate = new Matrix(BodyKinematics.COMPONENTS, 1);
5870 }
5871 if (ftrue == null) {
5872 ftrue = new Matrix(BodyKinematics.COMPONENTS, 1);
5873 }
5874 if (tmp == null) {
5875 tmp = new Matrix(BodyKinematics.COMPONENTS, 1);
5876 }
5877
5878 measAngularRate.setElementAtIndex(0, measAngularRateX);
5879 measAngularRate.setElementAtIndex(1, measAngularRateY);
5880 measAngularRate.setElementAtIndex(2, measAngularRateZ);
5881
5882 fmeas.setElementAtIndex(0, fmeasX);
5883 fmeas.setElementAtIndex(1, fmeasY);
5884 fmeas.setElementAtIndex(2, fmeasZ);
5885
5886 m.setElementAt(0, 0, m11);
5887 m.setElementAt(1, 0, m21);
5888 m.setElementAt(2, 0, m31);
5889
5890 m.setElementAt(0, 1, m12);
5891 m.setElementAt(1, 1, m22);
5892 m.setElementAt(2, 1, m32);
5893
5894 m.setElementAt(0, 2, m13);
5895 m.setElementAt(1, 2, m23);
5896 m.setElementAt(2, 2, m33);
5897
5898 Utils.inverse(m, invM);
5899
5900 b.setElementAtIndex(0, biasX);
5901 b.setElementAtIndex(1, biasY);
5902 b.setElementAtIndex(2, biasZ);
5903
5904 g.setElementAt(0, 0, g11);
5905 g.setElementAt(1, 0, g21);
5906 g.setElementAt(2, 0, g31);
5907
5908 g.setElementAt(0, 1, g12);
5909 g.setElementAt(1, 1, g22);
5910 g.setElementAt(2, 1, g32);
5911
5912 g.setElementAt(0, 2, g13);
5913 g.setElementAt(1, 2, g23);
5914 g.setElementAt(2, 2, g33);
5915
5916 getAccelerometerBiasAsMatrix(ba);
5917 getAccelerometerMa(ma);
5918
5919 // fix measured accelerometer value to obtain true
5920 // specific force
5921 accelerationFixer.fix(fmeas, ftrue);
5922 g.multiply(ftrue, tmp);
5923
5924 invM.multiply(measAngularRate, trueAngularRate);
5925 trueAngularRate.subtract(b);
5926 trueAngularRate.subtract(tmp);
5927
5928 final var norm = Utils.normF(trueAngularRate);
5929 return norm * norm;
5930
5931 } catch (final AlgebraException e) {
5932 throw new EvaluationException(e);
5933 }
5934 }
5935
5936 /**
5937 * Computes estimated true angular rate squared norm using current measured
5938 * angular rate and provided parameters.
5939 * This method is internally executed during gradient estimation and
5940 * Levenberg-Marquardt fittin needed for calibration computation.
5941 *
5942 * @param m11 element 1,1 of cross-coupling error matrix.
5943 * @param m21 element 2,1 of cross-coupling error matrix.
5944 * @param m31 element 3,1 of cross-coupling error matrix.
5945 * @param m12 element 1,2 of cross-coupling error matrix.
5946 * @param m22 element 2,2 of cross-coupling error matrix.
5947 * @param m32 element 3,2 of cross-coupling error matrix.
5948 * @param m13 element 1,3 of cross-coupling error matrix.
5949 * @param m23 element 2,3 of cross-coupling error matrix.
5950 * @param m33 element 3,3 of cross-coupling error matrix.
5951 * @return estimated true angular rate squared norm.
5952 * @throws EvaluationException if there are numerical instabilities.
5953 */
5954 private double evaluate(final double m11, final double m21, final double m31,
5955 final double m12, final double m22, final double m32,
5956 final double m13, final double m23, final double m33) throws EvaluationException {
5957
5958 // Ωmeas = M*(Ωtrue + b)
5959 // Ωtrue = M^-1 * Ωmeas - b
5960
5961 try {
5962 if (measAngularRate == null) {
5963 measAngularRate = new Matrix(BodyKinematics.COMPONENTS, 1);
5964 }
5965 if (m == null) {
5966 m = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
5967 }
5968 if (invM == null) {
5969 invM = new Matrix(BodyKinematics.COMPONENTS, BodyKinematics.COMPONENTS);
5970 }
5971 if (b == null) {
5972 b = new Matrix(BodyKinematics.COMPONENTS, 1);
5973 }
5974 if (trueAngularRate == null) {
5975 trueAngularRate = new Matrix(BodyKinematics.COMPONENTS, 1);
5976 }
5977
5978 measAngularRate.setElementAtIndex(0, measAngularRateX);
5979 measAngularRate.setElementAtIndex(1, measAngularRateY);
5980 measAngularRate.setElementAtIndex(2, measAngularRateZ);
5981
5982 m.setElementAt(0, 0, m11);
5983 m.setElementAt(1, 0, m21);
5984 m.setElementAt(2, 0, m31);
5985
5986 m.setElementAt(0, 1, m12);
5987 m.setElementAt(1, 1, m22);
5988 m.setElementAt(2, 1, m32);
5989
5990 m.setElementAt(0, 2, m13);
5991 m.setElementAt(1, 2, m23);
5992 m.setElementAt(2, 2, m33);
5993
5994 Utils.inverse(m, invM);
5995
5996 b.setElementAtIndex(0, biasX);
5997 b.setElementAtIndex(1, biasY);
5998 b.setElementAtIndex(2, biasZ);
5999
6000 invM.multiply(measAngularRate, trueAngularRate);
6001 trueAngularRate.subtract(b);
6002
6003 final var norm = Utils.normF(trueAngularRate);
6004 return norm * norm;
6005
6006 } catch (final AlgebraException e) {
6007 throw new EvaluationException(e);
6008 }
6009 }
6010 }